/*
Script: Core.js
	MooTools - My Object Oriented JavaScript Tools.

License:
	MIT-style license.

Copyright:
	Copyright (c) 2006-2008 [Valerio Proietti](http://mad4milk.net/).

Code & Documentation:
	[The MooTools production team](http://mootools.net/developers/).

Inspiration:
	- Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
	- Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
*/

var MooTools = {
	'version': '1.2.1',
	'build': '0d4845aab3d9a4fdee2f0d4a6dd59210e4b697cf'
};

var Native = function(options){
	options = options || {};
	var name = options.name;
	var legacy = options.legacy;
	var protect = options.protect;
	var methods = options.implement;
	var generics = options.generics;
	var initialize = options.initialize;
	var afterImplement = options.afterImplement || function(){};
	var object = initialize || legacy;
	generics = generics !== false;

	object.constructor = Native;
	object.$family = {name: 'native'};
	if (legacy && initialize) object.prototype = legacy.prototype;
	object.prototype.constructor = object;

	if (name){
		var family = name.toLowerCase();
		object.prototype.$family = {name: family};
		Native.typize(object, family);
	}

	var add = function(obj, name, method, force){
		if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
		if (generics) Native.genericize(obj, name, protect);
		afterImplement.call(obj, name, method);
		return obj;
	};

	object.alias = function(a1, a2, a3){
		if (typeof a1 == 'string'){
			if ((a1 = this.prototype[a1])) return add(this, a2, a1, a3);
		}
		for (var a in a1) this.alias(a, a1[a], a2);
		return this;
	};

	object.implement = function(a1, a2, a3){
		if (typeof a1 == 'string') return add(this, a1, a2, a3);
		for (var p in a1) add(this, p, a1[p], a2);
		return this;
	};

	if (methods) object.implement(methods);

	return object;
};

Native.genericize = function(object, property, check){
	if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
		var args = Array.prototype.slice.call(arguments);
		return object.prototype[property].apply(args.shift(), args);
	};
};

Native.implement = function(objects, properties){
	for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
};

Native.typize = function(object, family){
	if (!object.type) object.type = function(item){
		return ($type(item) === family);
	};
};

(function(){
	var natives = {'Array': Array, 'Date': Date, 'Function': Function, 'Number': Number, 'RegExp': RegExp, 'String': String};
	for (var n in natives) new Native({name: n, initialize: natives[n], protect: true});

	var types = {'boolean': Boolean, 'native': Native, 'object': Object};
	for (var t in types) Native.typize(types[t], t);

	var generics = {
		'Array': ["concat", "indexOf", "join", "lastIndexOf", "pop", "push", "reverse", "shift", "slice", "sort", "splice", "toString", "unshift", "valueOf"],
		'String': ["charAt", "charCodeAt", "concat", "indexOf", "lastIndexOf", "match", "replace", "search", "slice", "split", "substr", "substring", "toLowerCase", "toUpperCase", "valueOf"]
	};
	for (var g in generics){
		for (var i = generics[g].length; i--;) Native.genericize(window[g], generics[g][i], true);
	};
})();

var Hash = new Native({

	name: 'Hash',

	initialize: function(object){
		if ($type(object) == 'hash') object = $unlink(object.getClean());
		for (var key in object) this[key] = object[key];
		return this;
	}

});

Hash.implement({

	forEach: function(fn, bind){
		for (var key in this){
			if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
		}
	},

	getClean: function(){
		var clean = {};
		for (var key in this){
			if (this.hasOwnProperty(key)) clean[key] = this[key];
		}
		return clean;
	},

	getLength: function(){
		var length = 0;
		for (var key in this){
			if (this.hasOwnProperty(key)) length++;
		}
		return length;
	}

});

Hash.alias('forEach', 'each');

Array.implement({

	forEach: function(fn, bind){
		for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
	}

});

Array.alias('forEach', 'each');

function $A(iterable){
	if (iterable.item){
		var array = [];
		for (var i = 0, l = iterable.length; i < l; i++) array[i] = iterable[i];
		return array;
	}
	return Array.prototype.slice.call(iterable);
};

function $arguments(i){
	return function(){
		return arguments[i];
	};
};

function $chk(obj){
	return !!(obj || obj === 0);
};

function $clear(timer){
	clearTimeout(timer);
	clearInterval(timer);
	return null;
};

function $defined(obj){
	return (obj != undefined);
};

function $each(iterable, fn, bind){
	var type = $type(iterable);
	((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
};

function $empty(){};

function $extend(original, extended){
	for (var key in (extended || {})) original[key] = extended[key];
	return original;
};

function $H(object){
	return new Hash(object);
};

function $lambda(value){
	return (typeof value == 'function') ? value : function(){
		return value;
	};
};

function $merge(){
	var mix = {};
	for (var i = 0, l = arguments.length; i < l; i++){
		var object = arguments[i];
		if ($type(object) != 'object') continue;
		for (var key in object){
			var op = object[key], mp = mix[key];
			mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $merge(mp, op) : $unlink(op);
		}
	}
	return mix;
};

function $pick(){
	for (var i = 0, l = arguments.length; i < l; i++){
		if (arguments[i] != undefined) return arguments[i];
	}
	return null;
};

function $random(min, max){
	return Math.floor(Math.random() * (max - min + 1) + min);
};

function $splat(obj){
	var type = $type(obj);
	return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
};

var $time = Date.now || function(){
	return +new Date;
};

function $try(){
	for (var i = 0, l = arguments.length; i < l; i++){
		try {
			return arguments[i]();
		} catch(e){}
	}
	return null;
};

function $type(obj){
	if (obj == undefined) return false;
	if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
	if (obj.nodeName){
		switch (obj.nodeType){
			case 1: return 'element';
			case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
		}
	} else if (typeof obj.length == 'number'){
		if (obj.callee) return 'arguments';
		else if (obj.item) return 'collection';
	}
	return typeof obj;
};

function $unlink(object){
	var unlinked;
	switch ($type(object)){
		case 'object':
			unlinked = {};
			for (var p in object) unlinked[p] = $unlink(object[p]);
		break;
		case 'hash':
			unlinked = new Hash(object);
		break;
		case 'array':
			unlinked = [];
			for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
		break;
		default: return object;
	}
	return unlinked;
};


/*
Script: Browser.js
	The Browser Core. Contains Browser initialization, Window and Document, and the Browser Hash.

License:
	MIT-style license.
*/

var Browser = $merge({

	Engine: {name: 'unknown', version: 0},

	Platform: {name: (window.orientation != undefined) ? 'ipod' : (navigator.platform.match(/mac|win|linux/i) || ['other'])[0].toLowerCase()},

	Features: {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)},

	Plugins: {},

	Engines: {

		presto: function(){
			return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925));
		},

		trident: function(){
			return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? 5 : 4);
		},

		webkit: function(){
			return (navigator.taintEnabled) ? false : ((Browser.Features.xpath) ? ((Browser.Features.query) ? 525 : 420) : 419);
		},

		gecko: function(){
			return (document.getBoxObjectFor == undefined) ? false : ((document.getElementsByClassName) ? 19 : 18);
		}

	}

}, Browser || {});

Browser.Platform[Browser.Platform.name] = true;

Browser.detect = function(){

	for (var engine in this.Engines){
		var version = this.Engines[engine]();
		if (version){
			this.Engine = {name: engine, version: version};
			this.Engine[engine] = this.Engine[engine + version] = true;
			break;
		}
	}

	return {name: engine, version: version};

};

Browser.detect();

Browser.Request = function(){
	return $try(function(){
		return new XMLHttpRequest();
	}, function(){
		return new ActiveXObject('MSXML2.XMLHTTP');
	});
};

Browser.Features.xhr = !!(Browser.Request());

Browser.Plugins.Flash = (function(){
	var version = ($try(function(){
		return navigator.plugins['Shockwave Flash'].description;
	}, function(){
		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
	}) || '0 r0').match(/\d+/g);
	return {version: parseInt(version[0] || 0 + '.' + version[1] || 0), build: parseInt(version[2] || 0)};
})();

function $exec(text){
	if (!text) return text;
	if (window.execScript){
		window.execScript(text);
	} else {
		var script = document.createElement('script');
		script.setAttribute('type', 'text/javascript');
		script[(Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerText' : 'text'] = text;
		document.head.appendChild(script);
		document.head.removeChild(script);
	}
	return text;
};

Native.UID = 1;

var $uid = (Browser.Engine.trident) ? function(item){
	return (item.uid || (item.uid = [Native.UID++]))[0];
} : function(item){
	return item.uid || (item.uid = Native.UID++);
};

var Window = new Native({

	name: 'Window',

	legacy: (Browser.Engine.trident) ? null: window.Window,

	initialize: function(win){
		$uid(win);
		if (!win.Element){
			win.Element = $empty;
			if (Browser.Engine.webkit) win.document.createElement("iframe"); //fixes safari 2
			win.Element.prototype = (Browser.Engine.webkit) ? window["[[DOMElement.prototype]]"] : {};
		}
		win.document.window = win;
		return $extend(win, Window.Prototype);
	},

	afterImplement: function(property, value){
		window[property] = Window.Prototype[property] = value;
	}

});

Window.Prototype = {$family: {name: 'window'}};

new Window(window);

var Document = new Native({

	name: 'Document',

	legacy: (Browser.Engine.trident) ? null: window.Document,

	initialize: function(doc){
		$uid(doc);
		doc.head = doc.getElementsByTagName('head')[0];
		doc.html = doc.getElementsByTagName('html')[0];
		if (Browser.Engine.trident && Browser.Engine.version <= 4) $try(function(){
			doc.execCommand("BackgroundImageCache", false, true);
		});
		if (Browser.Engine.trident) doc.window.attachEvent('onunload', function() {
			doc.window.detachEvent('onunload', arguments.callee);
			doc.head = doc.html = doc.window = null;
		});
		return $extend(doc, Document.Prototype);
	},

	afterImplement: function(property, value){
		document[property] = Document.Prototype[property] = value;
	}

});

Document.Prototype = {$family: {name: 'document'}};

new Document(document);


/*
Script: Array.js
	Contains Array Prototypes like each, contains, and erase.

License:
	MIT-style license.
*/

Array.implement({

	every: function(fn, bind){
		for (var i = 0, l = this.length; i < l; i++){
			if (!fn.call(bind, this[i], i, this)) return false;
		}
		return true;
	},

	filter: function(fn, bind){
		var results = [];
		for (var i = 0, l = this.length; i < l; i++){
			if (fn.call(bind, this[i], i, this)) results.push(this[i]);
		}
		return results;
	},

	clean: function() {
		return this.filter($defined);
	},

	indexOf: function(item, from){
		var len = this.length;
		for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
			if (this[i] === item) return i;
		}
		return -1;
	},

	map: function(fn, bind){
		var results = [];
		for (var i = 0, l = this.length; i < l; i++) results[i] = fn.call(bind, this[i], i, this);
		return results;
	},

	some: function(fn, bind){
		for (var i = 0, l = this.length; i < l; i++){
			if (fn.call(bind, this[i], i, this)) return true;
		}
		return false;
	},

	associate: function(keys){
		var obj = {}, length = Math.min(this.length, keys.length);
		for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
		return obj;
	},

	link: function(object){
		var result = {};
		for (var i = 0, l = this.length; i < l; i++){
			for (var key in object){
				if (object[key](this[i])){
					result[key] = this[i];
					delete object[key];
					break;
				}
			}
		}
		return result;
	},

	contains: function(item, from){
		return this.indexOf(item, from) != -1;
	},

	extend: function(array){
		for (var i = 0, j = array.length; i < j; i++) this.push(array[i]);
		return this;
	},

	getLast: function(){
		return (this.length) ? this[this.length - 1] : null;
	},

	getRandom: function(){
		return (this.length) ? this[$random(0, this.length - 1)] : null;
	},

	include: function(item){
		if (!this.contains(item)) this.push(item);
		return this;
	},

	combine: function(array){
		for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
		return this;
	},

	erase: function(item){
		for (var i = this.length; i--; i){
			if (this[i] === item) this.splice(i, 1);
		}
		return this;
	},

	empty: function(){
		this.length = 0;
		return this;
	},

	flatten: function(){
		var array = [];
		for (var i = 0, l = this.length; i < l; i++){
			var type = $type(this[i]);
			if (!type) continue;
			array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]);
		}
		return array;
	},

	hexToRgb: function(array){
		if (this.length != 3) return null;
		var rgb = this.map(function(value){
			if (value.length == 1) value += value;
			return value.toInt(16);
		});
		return (array) ? rgb : 'rgb(' + rgb + ')';
	},

	rgbToHex: function(array){
		if (this.length < 3) return null;
		if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
		var hex = [];
		for (var i = 0; i < 3; i++){
			var bit = (this[i] - 0).toString(16);
			hex.push((bit.length == 1) ? '0' + bit : bit);
		}
		return (array) ? hex : '#' + hex.join('');
	}

});


/*
Script: Function.js
	Contains Function Prototypes like create, bind, pass, and delay.

License:
	MIT-style license.
*/

Function.implement({

	extend: function(properties){
		for (var property in properties) this[property] = properties[property];
		return this;
	},

	create: function(options){
		var self = this;
		options = options || {};
		return function(event){
			var args = options.arguments;
			args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
			if (options.event) args = [event || window.event].extend(args);
			var returns = function(){
				return self.apply(options.bind || null, args);
			};
			if (options.delay) return setTimeout(returns, options.delay);
			if (options.periodical) return setInterval(returns, options.periodical);
			if (options.attempt) return $try(returns);
			return returns();
		};
	},

	run: function(args, bind){
		return this.apply(bind, $splat(args));
	},

	pass: function(args, bind){
		return this.create({bind: bind, arguments: args});
	},

	bind: function(bind, args){
		return this.create({bind: bind, arguments: args});
	},

	bindWithEvent: function(bind, args){
		return this.create({bind: bind, arguments: args, event: true});
	},

	attempt: function(args, bind){
		return this.create({bind: bind, arguments: args, attempt: true})();
	},

	delay: function(delay, bind, args){
		return this.create({bind: bind, arguments: args, delay: delay})();
	},

	periodical: function(periodical, bind, args){
		return this.create({bind: bind, arguments: args, periodical: periodical})();
	}

});


/*
Script: Number.js
	Contains Number Prototypes like limit, round, times, and ceil.

License:
	MIT-style license.
*/

Number.implement({

	limit: function(min, max){
		return Math.min(max, Math.max(min, this));
	},

	round: function(precision){
		precision = Math.pow(10, precision || 0);
		return Math.round(this * precision) / precision;
	},

	times: function(fn, bind){
		for (var i = 0; i < this; i++) fn.call(bind, i, this);
	},

	toFloat: function(){
		return parseFloat(this);
	},

	toInt: function(base){
		return parseInt(this, base || 10);
	}

});

Number.alias('times', 'each');

(function(math){
	var methods = {};
	math.each(function(name){
		if (!Number[name]) methods[name] = function(){
			return Math[name].apply(null, [this].concat($A(arguments)));
		};
	});
	Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);


/*
Script: String.js
	Contains String Prototypes like camelCase, capitalize, test, and toInt.

License:
	MIT-style license.
*/

String.implement({

	test: function(regex, params){
		return ((typeof regex == 'string') ? new RegExp(regex, params) : regex).test(this);
	},

	contains: function(string, separator){
		return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
	},

	trim: function(){
		return this.replace(/^\s+|\s+$/g, '');
	},

	clean: function(){
		return this.replace(/\s+/g, ' ').trim();
	},

	camelCase: function(){
		return this.replace(/-\D/g, function(match){
			return match.charAt(1).toUpperCase();
		});
	},

	hyphenate: function(){
		return this.replace(/[A-Z]/g, function(match){
			return ('-' + match.charAt(0).toLowerCase());
		});
	},

	capitalize: function(){
		return this.replace(/\b[a-z]/g, function(match){
			return match.toUpperCase();
		});
	},

	escapeRegExp: function(){
		return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
	},

	toInt: function(base){
		return parseInt(this, base || 10);
	},

	toFloat: function(){
		return parseFloat(this);
	},

	hexToRgb: function(array){
		var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
		return (hex) ? hex.slice(1).hexToRgb(array) : null;
	},

	rgbToHex: function(array){
		var rgb = this.match(/\d{1,3}/g);
		return (rgb) ? rgb.rgbToHex(array) : null;
	},

	stripScripts: function(option){
		var scripts = '';
		var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(){
			scripts += arguments[1] + '\n';
			return '';
		});
		if (option === true) $exec(scripts);
		else if ($type(option) == 'function') option(scripts, text);
		return text;
	},

	substitute: function(object, regexp){
		return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
			if (match.charAt(0) == '\\') return match.slice(1);
			return (object[name] != undefined) ? object[name] : '';
		});
	}

});


/*
Script: Hash.js
	Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.

License:
	MIT-style license.
*/

Hash.implement({

	has: Object.prototype.hasOwnProperty,

	keyOf: function(value){
		for (var key in this){
			if (this.hasOwnProperty(key) && this[key] === value) return key;
		}
		return null;
	},

	hasValue: function(value){
		return (Hash.keyOf(this, value) !== null);
	},

	extend: function(properties){
		Hash.each(properties, function(value, key){
			Hash.set(this, key, value);
		}, this);
		return this;
	},

	combine: function(properties){
		Hash.each(properties, function(value, key){
			Hash.include(this, key, value);
		}, this);
		return this;
	},

	erase: function(key){
		if (this.hasOwnProperty(key)) delete this[key];
		return this;
	},

	get: function(key){
		return (this.hasOwnProperty(key)) ? this[key] : null;
	},

	set: function(key, value){
		if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
		return this;
	},

	empty: function(){
		Hash.each(this, function(value, key){
			delete this[key];
		}, this);
		return this;
	},

	include: function(key, value){
		var k = this[key];
		if (k == undefined) this[key] = value;
		return this;
	},

	map: function(fn, bind){
		var results = new Hash;
		Hash.each(this, function(value, key){
			results.set(key, fn.call(bind, value, key, this));
		}, this);
		return results;
	},

	filter: function(fn, bind){
		var results = new Hash;
		Hash.each(this, function(value, key){
			if (fn.call(bind, value, key, this)) results.set(key, value);
		}, this);
		return results;
	},

	every: function(fn, bind){
		for (var key in this){
			if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
		}
		return true;
	},

	some: function(fn, bind){
		for (var key in this){
			if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
		}
		return false;
	},

	getKeys: function(){
		var keys = [];
		Hash.each(this, function(value, key){
			keys.push(key);
		});
		return keys;
	},

	getValues: function(){
		var values = [];
		Hash.each(this, function(value){
			values.push(value);
		});
		return values;
	},

	toQueryString: function(base){
		var queryString = [];
		Hash.each(this, function(value, key){
			if (base) key = base + '[' + key + ']';
			var result;
			switch ($type(value)){
				case 'object': result = Hash.toQueryString(value, key); break;
				case 'array':
					var qs = {};
					value.each(function(val, i){
						qs[i] = val;
					});
					result = Hash.toQueryString(qs, key);
				break;
				default: result = key + '=' + encodeURIComponent(value);
			}
			if (value != undefined) queryString.push(result);
		});

		return queryString.join('&');
	}

});

Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});


/*
Script: Event.js
	Contains the Event Native, to make the event object completely crossbrowser.

License:
	MIT-style license.
*/

var Event = new Native({

	name: 'Event',

	initialize: function(event, win){
		win = win || window;
		var doc = win.document;
		event = event || win.event;
		if (event.$extended) return event;
		this.$extended = true;
		var type = event.type;
		var target = event.target || event.srcElement;
		while (target && target.nodeType == 3) target = target.parentNode;

		if (type.test(/key/)){
			var code = event.which || event.keyCode;
			var key = Event.Keys.keyOf(code);
			if (type == 'keydown'){
				var fKey = code - 111;
				if (fKey > 0 && fKey < 13) key = 'f' + fKey;
			}
			key = key || String.fromCharCode(code).toLowerCase();
		} else if (type.match(/(click|mouse|menu)/i)){
			doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
			var page = {
				x: event.pageX || event.clientX + doc.scrollLeft,
				y: event.pageY || event.clientY + doc.scrollTop
			};
			var client = {
				x: (event.pageX) ? event.pageX - win.pageXOffset : event.clientX,
				y: (event.pageY) ? event.pageY - win.pageYOffset : event.clientY
			};
			if (type.match(/DOMMouseScroll|mousewheel/)){
				var wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
			}
			var rightClick = (event.which == 3) || (event.button == 2);
			var related = null;
			if (type.match(/over|out/)){
				switch (type){
					case 'mouseover': related = event.relatedTarget || event.fromElement; break;
					case 'mouseout': related = event.relatedTarget || event.toElement;
				}
				if (!(function(){
					while (related && related.nodeType == 3) related = related.parentNode;
					return true;
				}).create({attempt: Browser.Engine.gecko})()) related = false;
			}
		}

		return $extend(this, {
			event: event,
			type: type,

			page: page,
			client: client,
			rightClick: rightClick,

			wheel: wheel,

			relatedTarget: related,
			target: target,

			code: code,
			key: key,

			shift: event.shiftKey,
			control: event.ctrlKey,
			alt: event.altKey,
			meta: event.metaKey
		});
	}

});

Event.Keys = new Hash({
	'enter': 13,
	'up': 38,
	'down': 40,
	'left': 37,
	'right': 39,
	'esc': 27,
	'space': 32,
	'backspace': 8,
	'tab': 9,
	'delete': 46
});

Event.implement({

	stop: function(){
		return this.stopPropagation().preventDefault();
	},

	stopPropagation: function(){
		if (this.event.stopPropagation) this.event.stopPropagation();
		else this.event.cancelBubble = true;
		return this;
	},

	preventDefault: function(){
		if (this.event.preventDefault) this.event.preventDefault();
		else this.event.returnValue = false;
		return this;
	}

});


/*
Script: Class.js
	Contains the Class Function for easily creating, extending, and implementing reusable Classes.

License:
	MIT-style license.
*/

var Class = new Native({

	name: 'Class',

	initialize: function(properties){
		properties = properties || {};
		var klass = function(){
			for (var key in this){
				if ($type(this[key]) != 'function') this[key] = $unlink(this[key]);
			}
			this.constructor = klass;
			if (Class.prototyping) return this;
			var instance = (this.initialize) ? this.initialize.apply(this, arguments) : this;
			if (this.options && this.options.initialize) this.options.initialize.call(this);
			return instance;
		};

		for (var mutator in Class.Mutators){
			if (!properties[mutator]) continue;
			properties = Class.Mutators[mutator](properties, properties[mutator]);
			delete properties[mutator];
		}

		$extend(klass, this);
		klass.constructor = Class;
		klass.prototype = properties;
		return klass;
	}

});

Class.Mutators = {

	Extends: function(self, klass){
		Class.prototyping = klass.prototype;
		var subclass = new klass;
		delete subclass.parent;
		subclass = Class.inherit(subclass, self);
		delete Class.prototyping;
		return subclass;
	},

	Implements: function(self, klasses){
		$splat(klasses).each(function(klass){
			Class.prototying = klass;
			$extend(self, ($type(klass) == 'class') ? new klass : klass);
			delete Class.prototyping;
		});
		return self;
	}

};

Class.extend({

	inherit: function(object, properties){
		var caller = arguments.callee.caller;
		for (var key in properties){
			var override = properties[key];
			var previous = object[key];
			var type = $type(override);
			if (previous && type == 'function'){
				if (override != previous){
					if (caller){
						override.__parent = previous;
						object[key] = override;
					} else {
						Class.override(object, key, override);
					}
				}
			} else if(type == 'object'){
				object[key] = $merge(previous, override);
			} else {
				object[key] = override;
			}
		}

		if (caller) object.parent = function(){
			return arguments.callee.caller.__parent.apply(this, arguments);
		};

		return object;
	},

	override: function(object, name, method){
		var parent = Class.prototyping;
		if (parent && object[name] != parent[name]) parent = null;
		var override = function(){
			var previous = this.parent;
			this.parent = parent ? parent[name] : object[name];
			var value = method.apply(this, arguments);
			this.parent = previous;
			return value;
		};
		object[name] = override;
	}

});

Class.implement({

	implement: function(){
		var proto = this.prototype;
		$each(arguments, function(properties){
			Class.inherit(proto, properties);
		});
		return this;
	}

});


/*
Script: Class.Extras.js
	Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.

License:
	MIT-style license.
*/

var Chain = new Class({

	$chain: [],

	chain: function(){
		this.$chain.extend(Array.flatten(arguments));
		return this;
	},

	callChain: function(){
		return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
	},

	clearChain: function(){
		this.$chain.empty();
		return this;
	}

});

var Events = new Class({

	$events: {},

	addEvent: function(type, fn, internal){
		type = Events.removeOn(type);
		if (fn != $empty){
			this.$events[type] = this.$events[type] || [];
			this.$events[type].include(fn);
			if (internal) fn.internal = true;
		}
		return this;
	},

	addEvents: function(events){
		for (var type in events) this.addEvent(type, events[type]);
		return this;
	},

	fireEvent: function(type, args, delay){
		type = Events.removeOn(type);
		if (!this.$events || !this.$events[type]) return this;
		this.$events[type].each(function(fn){
			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
		}, this);
		return this;
	},

	removeEvent: function(type, fn){
		type = Events.removeOn(type);
		if (!this.$events[type]) return this;
		if (!fn.internal) this.$events[type].erase(fn);
		return this;
	},

	removeEvents: function(events){
		if ($type(events) == 'object'){
			for (var type in events) this.removeEvent(type, events[type]);
			return this;
		}
		if (events) events = Events.removeOn(events);
		for (var type in this.$events){
			if (events && events != type) continue;
			var fns = this.$events[type];
			for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
		}
		return this;
	}

});

Events.removeOn = function(string){
	return string.replace(/^on([A-Z])/, function(full, first) {
		return first.toLowerCase();
	});
};

var Options = new Class({

	setOptions: function(){
		this.options = $merge.run([this.options].extend(arguments));
		if (!this.addEvent) return this;
		for (var option in this.options){
			if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
			this.addEvent(option, this.options[option]);
			delete this.options[option];
		}
		return this;
	}

});


/*
Script: Element.js
	One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser,
	time-saver methods to let you easily work with HTML Elements.

License:
	MIT-style license.
*/

var Element = new Native({

	name: 'Element',

	legacy: window.Element,

	initialize: function(tag, props){
		var konstructor = Element.Constructors.get(tag);
		if (konstructor) return konstructor(props);
		if (typeof tag == 'string') return document.newElement(tag, props);
		return $(tag).set(props);
	},

	afterImplement: function(key, value){
		Element.Prototype[key] = value;
		if (Array[key]) return;
		Elements.implement(key, function(){
			var items = [], elements = true;
			for (var i = 0, j = this.length; i < j; i++){
				var returns = this[i][key].apply(this[i], arguments);
				items.push(returns);
				if (elements) elements = ($type(returns) == 'element');
			}
			return (elements) ? new Elements(items) : items;
		});
	}

});

Element.Prototype = {$family: {name: 'element'}};

Element.Constructors = new Hash;

var IFrame = new Native({

	name: 'IFrame',

	generics: false,

	initialize: function(){
		var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
		var props = params.properties || {};
		var iframe = $(params.iframe) || false;
		var onload = props.onload || $empty;
		delete props.onload;
		props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name, 'IFrame_' + $time());
		iframe = new Element(iframe || 'iframe', props);
		var onFrameLoad = function(){
			var host = $try(function(){
				return iframe.contentWindow.location.host;
			});
			if (host && host == window.location.host){
				var win = new Window(iframe.contentWindow);
				new Document(iframe.contentWindow.document);
				$extend(win.Element.prototype, Element.Prototype);
			}
			onload.call(iframe.contentWindow, iframe.contentWindow.document);
		};
		(window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
		return iframe;
	}

});

var Elements = new Native({

	initialize: function(elements, options){
		options = $extend({ddup: true, cash: true}, options);
		elements = elements || [];
		if (options.ddup || options.cash){
			var uniques = {}, returned = [];
			for (var i = 0, l = elements.length; i < l; i++){
				var el = $.element(elements[i], !options.cash);
				if (options.ddup){
					if (uniques[el.uid]) continue;
					uniques[el.uid] = true;
				}
				returned.push(el);
			}
			elements = returned;
		}
		return (options.cash) ? $extend(elements, this) : elements;
	}

});

Elements.implement({

	filter: function(filter, bind){
		if (!filter) return this;
		return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
			return item.match(filter);
		} : filter, bind));
	}

});

Document.implement({

	newElement: function(tag, props){
		if (Browser.Engine.trident && props){
			['name', 'type', 'checked'].each(function(attribute){
				if (!props[attribute]) return;
				tag += ' ' + attribute + '="' + props[attribute] + '"';
				if (attribute != 'checked') delete props[attribute];
			});
			tag = '<' + tag + '>';
		}
		return $.element(this.createElement(tag)).set(props);
	},

	newTextNode: function(text){
		return this.createTextNode(text);
	},

	getDocument: function(){
		return this;
	},

	getWindow: function(){
		return this.window;
	}

});

Window.implement({

	$: function(el, nocash){
		if (el && el.$family && el.uid) return el;
		var type = $type(el);
		return ($[type]) ? $[type](el, nocash, this.document) : null;
	},

	$$: function(selector){
		if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
		var elements = [];
		var args = Array.flatten(arguments);
		for (var i = 0, l = args.length; i < l; i++){
			var item = args[i];
			switch ($type(item)){
				case 'element': elements.push(item); break;
				case 'string': elements.extend(this.document.getElements(item, true));
			}
		}
		return new Elements(elements);
	},

	getDocument: function(){
		return this.document;
	},

	getWindow: function(){
		return this;
	}

});

$.string = function(id, nocash, doc){
	id = doc.getElementById(id);
	return (id) ? $.element(id, nocash) : null;
};

$.element = function(el, nocash){
	$uid(el);
	if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
		var proto = Element.Prototype;
		for (var p in proto) el[p] = proto[p];
	};
	return el;
};

$.object = function(obj, nocash, doc){
	if (obj.toElement) return $.element(obj.toElement(doc), nocash);
	return null;
};

$.textnode = $.whitespace = $.window = $.document = $arguments(0);

Native.implement([Element, Document], {

	getElement: function(selector, nocash){
		return $(this.getElements(selector, true)[0] || null, nocash);
	},

	getElements: function(tags, nocash){
		tags = tags.split(',');
		var elements = [];
		var ddup = (tags.length > 1);
		tags.each(function(tag){
			var partial = this.getElementsByTagName(tag.trim());
			(ddup) ? elements.extend(partial) : elements = partial;
		}, this);
		return new Elements(elements, {ddup: ddup, cash: !nocash});
	}

});

(function(){

var collected = {}, storage = {};
var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};

var get = function(uid){
	return (storage[uid] || (storage[uid] = {}));
};

var clean = function(item, retain){
	if (!item) return;
	var uid = item.uid;
	if (Browser.Engine.trident){
		if (item.clearAttributes){
			var clone = retain && item.cloneNode(false);
			item.clearAttributes();
			if (clone) item.mergeAttributes(clone);
		} else if (item.removeEvents){
			item.removeEvents();
		}
		if ((/object/i).test(item.tagName)){
			for (var p in item){
				if (typeof item[p] == 'function') item[p] = $empty;
			}
			Element.dispose(item);
		}
	}	
	if (!uid) return;
	collected[uid] = storage[uid] = null;
};

var purge = function(){
	Hash.each(collected, clean);
	if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
	if (window.CollectGarbage) CollectGarbage();
	collected = storage = null;
};

var walk = function(element, walk, start, match, all, nocash){
	var el = element[start || walk];
	var elements = [];
	while (el){
		if (el.nodeType == 1 && (!match || Element.match(el, match))){
			if (!all) return $(el, nocash);
			elements.push(el);
		}
		el = el[walk];
	}
	return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
};

var attributes = {
	'html': 'innerHTML',
	'class': 'className',
	'for': 'htmlFor',
	'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
};
var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
var camels = ['value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];

Hash.extend(attributes, bools.associate(bools));
Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));

var inserters = {

	before: function(context, element){
		if (element.parentNode) element.parentNode.insertBefore(context, element);
	},

	after: function(context, element){
		if (!element.parentNode) return;
		var next = element.nextSibling;
		(next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
	},

	bottom: function(context, element){
		element.appendChild(context);
	},

	top: function(context, element){
		var first = element.firstChild;
		(first) ? element.insertBefore(context, first) : element.appendChild(context);
	}

};

inserters.inside = inserters.bottom;

Hash.each(inserters, function(inserter, where){

	where = where.capitalize();

	Element.implement('inject' + where, function(el){
		inserter(this, $(el, true));
		return this;
	});

	Element.implement('grab' + where, function(el){
		inserter($(el, true), this);
		return this;
	});

});

Element.implement({

	set: function(prop, value){
		switch ($type(prop)){
			case 'object':
				for (var p in prop) this.set(p, prop[p]);
				break;
			case 'string':
				var property = Element.Properties.get(prop);
				(property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
		}
		return this;
	},

	get: function(prop){
		var property = Element.Properties.get(prop);
		return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
	},

	erase: function(prop){
		var property = Element.Properties.get(prop);
		(property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
		return this;
	},

	setProperty: function(attribute, value){
		var key = attributes[attribute];
		if (value == undefined) return this.removeProperty(attribute);
		if (key && bools[attribute]) value = !!value;
		(key) ? this[key] = value : this.setAttribute(attribute, '' + value);
		return this;
	},

	setProperties: function(attributes){
		for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
		return this;
	},

	getProperty: function(attribute){
		var key = attributes[attribute];
		var value = (key) ? this[key] : this.getAttribute(attribute, 2);
		return (bools[attribute]) ? !!value : (key) ? value : value || null;
	},

	getProperties: function(){
		var args = $A(arguments);
		return args.map(this.getProperty, this).associate(args);
	},

	removeProperty: function(attribute){
		var key = attributes[attribute];
		(key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
		return this;
	},

	removeProperties: function(){
		Array.each(arguments, this.removeProperty, this);
		return this;
	},

	hasClass: function(className){
		return this.className.contains(className, ' ');
	},

	addClass: function(className){
		if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
		return this;
	},

	removeClass: function(className){
		this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
		return this;
	},

	toggleClass: function(className){
		return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
	},

	adopt: function(){
		Array.flatten(arguments).each(function(element){
			element = $(element, true);
			if (element) this.appendChild(element);
		}, this);
		return this;
	},

	appendText: function(text, where){
		return this.grab(this.getDocument().newTextNode(text), where);
	},

	grab: function(el, where){
		inserters[where || 'bottom']($(el, true), this);
		return this;
	},

	inject: function(el, where){
		inserters[where || 'bottom'](this, $(el, true));
		return this;
	},

	replaces: function(el){
		el = $(el, true);
		el.parentNode.replaceChild(this, el);
		return this;
	},

	wraps: function(el, where){
		el = $(el, true);
		return this.replaces(el).grab(el, where);
	},

	getPrevious: function(match, nocash){
		return walk(this, 'previousSibling', null, match, false, nocash);
	},

	getAllPrevious: function(match, nocash){
		return walk(this, 'previousSibling', null, match, true, nocash);
	},

	getNext: function(match, nocash){
		return walk(this, 'nextSibling', null, match, false, nocash);
	},

	getAllNext: function(match, nocash){
		return walk(this, 'nextSibling', null, match, true, nocash);
	},

	getFirst: function(match, nocash){
		return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
	},

	getLast: function(match, nocash){
		return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
	},

	getParent: function(match, nocash){
		return walk(this, 'parentNode', null, match, false, nocash);
	},

	getParents: function(match, nocash){
		return walk(this, 'parentNode', null, match, true, nocash);
	},

	getChildren: function(match, nocash){
		return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
	},

	getWindow: function(){
		return this.ownerDocument.window;
	},

	getDocument: function(){
		return this.ownerDocument;
	},

	getElementById: function(id, nocash){
		var el = this.ownerDocument.getElementById(id);
		if (!el) return null;
		for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
			if (!parent) return null;
		}
		return $.element(el, nocash);
	},

	getSelected: function(){
		return new Elements($A(this.options).filter(function(option){
			return option.selected;
		}));
	},

	getComputedStyle: function(property){
		if (this.currentStyle) return this.currentStyle[property.camelCase()];
		var computed = this.getDocument().defaultView.getComputedStyle(this, null);
		return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
	},

	toQueryString: function(){
		var queryString = [];
		this.getElements('input, select, textarea', true).each(function(el){
			if (!el.name || el.disabled) return;
			var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
				return opt.value;
			}) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
			$splat(value).each(function(val){
				if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
			});
		});
		return queryString.join('&');
	},

	clone: function(contents, keepid){
		contents = contents !== false;
		var clone = this.cloneNode(contents);
		var clean = function(node, element){
			if (!keepid) node.removeAttribute('id');
			if (Browser.Engine.trident){
				node.clearAttributes();
				node.mergeAttributes(element);
				node.removeAttribute('uid');
				if (node.options){
					var no = node.options, eo = element.options;
					for (var j = no.length; j--;) no[j].selected = eo[j].selected;
				}
			}
			var prop = props[element.tagName.toLowerCase()];
			if (prop && element[prop]) node[prop] = element[prop];
		};

		if (contents){
			var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
			for (var i = ce.length; i--;) clean(ce[i], te[i]);
		}

		clean(clone, this);
		return $(clone);
	},

	destroy: function(){
		Element.empty(this);
		Element.dispose(this);
		clean(this, true);
		return null;
	},

	empty: function(){
		$A(this.childNodes).each(function(node){
			Element.destroy(node);
		});
		return this;
	},

	dispose: function(){
		return (this.parentNode) ? this.parentNode.removeChild(this) : this;
	},

	hasChild: function(el){
		el = $(el, true);
		if (!el) return false;
		if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
		return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
	},

	match: function(tag){
		return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
	}

});

Native.implement([Element, Window, Document], {

	addListener: function(type, fn){
		if (type == 'unload'){
			var old = fn, self = this;
			fn = function(){
				self.removeListener('unload', fn);
				old();
			};
		} else {
			collected[this.uid] = this;
		}
		if (this.addEventListener) this.addEventListener(type, fn, false);
		else this.attachEvent('on' + type, fn);
		return this;
	},

	removeListener: function(type, fn){
		if (this.removeEventListener) this.removeEventListener(type, fn, false);
		else this.detachEvent('on' + type, fn);
		return this;
	},

	retrieve: function(property, dflt){
		var storage = get(this.uid), prop = storage[property];
		if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
		return $pick(prop);
	},

	store: function(property, value){
		var storage = get(this.uid);
		storage[property] = value;
		return this;
	},

	eliminate: function(property){
		var storage = get(this.uid);
		delete storage[property];
		return this;
	}

});

window.addListener('unload', purge);

})();

Element.Properties = new Hash;

Element.Properties.style = {

	set: function(style){
		this.style.cssText = style;
	},

	get: function(){
		return this.style.cssText;
	},

	erase: function(){
		this.style.cssText = '';
	}

};

Element.Properties.tag = {

	get: function(){
		return this.tagName.toLowerCase();
	}

};

Element.Properties.html = (function(){
	var wrapper = document.createElement('div');

	var translations = {
		table: [1, '<table>', '</table>'],
		select: [1, '<select>', '</select>'],
		tbody: [2, '<table><tbody>', '</tbody></table>'],
		tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
	};
	translations.thead = translations.tfoot = translations.tbody;

	var html = {
		set: function(){
			var html = Array.flatten(arguments).join('');
			var wrap = Browser.Engine.trident && translations[this.get('tag')];
			if (wrap){
				var first = wrapper;
				first.innerHTML = wrap[1] + html + wrap[2];
				for (var i = wrap[0]; i--;) first = first.firstChild;
				this.empty().adopt(first.childNodes);
			} else {
				this.innerHTML = html;
			}
		}
	};

	html.erase = html.set;

	return html;
})();

if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
	get: function(){
		if (this.innerText) return this.innerText;
		var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
		var text = temp.innerText;
		temp.destroy();
		return text;
	}
};


/*
Script: Element.Event.js
	Contains Element methods for dealing with events, and custom Events.

License:
	MIT-style license.
*/

Element.Properties.events = {set: function(events){
	this.addEvents(events);
}};

Native.implement([Element, Window, Document], {

	addEvent: function(type, fn){
		var events = this.retrieve('events', {});
		events[type] = events[type] || {'keys': [], 'values': []};
		if (events[type].keys.contains(fn)) return this;
		events[type].keys.push(fn);
		var realType = type, custom = Element.Events.get(type), condition = fn, self = this;
		if (custom){
			if (custom.onAdd) custom.onAdd.call(this, fn);
			if (custom.condition){
				condition = function(event){
					if (custom.condition.call(this, event)) return fn.call(this, event);
					return true;
				};
			}
			realType = custom.base || realType;
		}
		var defn = function(){
			return fn.call(self);
		};
		var nativeEvent = Element.NativeEvents[realType];
		if (nativeEvent){
			if (nativeEvent == 2){
				defn = function(event){
					event = new Event(event, self.getWindow());
					if (condition.call(self, event) === false) event.stop();
				};
			}
			this.addListener(realType, defn);
		}
		events[type].values.push(defn);
		return this;
	},

	removeEvent: function(type, fn){
		var events = this.retrieve('events');
		if (!events || !events[type]) return this;
		var pos = events[type].keys.indexOf(fn);
		if (pos == -1) return this;
		events[type].keys.splice(pos, 1);
		var value = events[type].values.splice(pos, 1)[0];
		var custom = Element.Events.get(type);
		if (custom){
			if (custom.onRemove) custom.onRemove.call(this, fn);
			type = custom.base || type;
		}
		return (Element.NativeEvents[type]) ? this.removeListener(type, value) : this;
	},

	addEvents: function(events){
		for (var event in events) this.addEvent(event, events[event]);
		return this;
	},

	removeEvents: function(events){
		if ($type(events) == 'object'){
			for (var type in events) this.removeEvent(type, events[type]);
			return this;
		}
		var attached = this.retrieve('events');
		if (!attached) return this;
		if (!events){
			for (var type in attached) this.removeEvents(type);
			this.eliminate('events');
		} else if (attached[events]){
			while (attached[events].keys[0]) this.removeEvent(events, attached[events].keys[0]);
			attached[events] = null;
		}
		return this;
	},

	fireEvent: function(type, args, delay){
		var events = this.retrieve('events');
		if (!events || !events[type]) return this;
		events[type].keys.each(function(fn){
			fn.create({'bind': this, 'delay': delay, 'arguments': args})();
		}, this);
		return this;
	},

	cloneEvents: function(from, type){
		from = $(from);
		var fevents = from.retrieve('events');
		if (!fevents) return this;
		if (!type){
			for (var evType in fevents) this.cloneEvents(from, evType);
		} else if (fevents[type]){
			fevents[type].keys.each(function(fn){
				this.addEvent(type, fn);
			}, this);
		}
		return this;
	}

});

Element.NativeEvents = {
	click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons
	mousewheel: 2, DOMMouseScroll: 2, //mouse wheel
	mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement
	keydown: 2, keypress: 2, keyup: 2, //keyboard
	focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements
	load: 1, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window
	error: 1, abort: 1, scroll: 1 //misc
};

(function(){

var $check = function(event){
	var related = event.relatedTarget;
	if (related == undefined) return true;
	if (related === false) return false;
	return ($type(this) != 'document' && related != this && related.prefix != 'xul' && !this.hasChild(related));
};

Element.Events = new Hash({

	mouseenter: {
		base: 'mouseover',
		condition: $check
	},

	mouseleave: {
		base: 'mouseout',
		condition: $check
	},

	mousewheel: {
		base: (Browser.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel'
	}

});

})();


/*
Script: Element.Style.js
	Contains methods for interacting with the styles of Elements in a fashionable way.

License:
	MIT-style license.
*/

Element.Properties.styles = {set: function(styles){
	this.setStyles(styles);
}};

Element.Properties.opacity = {

	set: function(opacity, novisibility){
		if (!novisibility){
			if (opacity == 0){
				if (this.style.visibility != 'hidden') this.style.visibility = 'hidden';
			} else {
				if (this.style.visibility != 'visible') this.style.visibility = 'visible';
			}
		}
		if (!this.currentStyle || !this.currentStyle.hasLayout) this.style.zoom = 1;
		if (Browser.Engine.trident) this.style.filter = (opacity == 1) ? '' : 'alpha(opacity=' + opacity * 100 + ')';
		this.style.opacity = opacity;
		this.store('opacity', opacity);
	},

	get: function(){
		return this.retrieve('opacity', 1);
	}

};

Element.implement({

	setOpacity: function(value){
		return this.set('opacity', value, true);
	},

	getOpacity: function(){
		return this.get('opacity');
	},

	setStyle: function(property, value){
		switch (property){
			case 'opacity': return this.set('opacity', parseFloat(value));
			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
		}
		property = property.camelCase();
		if ($type(value) != 'string'){
			var map = (Element.Styles.get(property) || '@').split(' ');
			value = $splat(value).map(function(val, i){
				if (!map[i]) return '';
				return ($type(val) == 'number') ? map[i].replace('@', Math.round(val)) : val;
			}).join(' ');
		} else if (value == String(Number(value))){
			value = Math.round(value);
		}
		try {
			this.style[property] = value;
		} catch (e) {
			//
		}
		return this;
	},

	getStyle: function(property){
		switch (property){
			case 'opacity': return this.get('opacity');
			case 'float': property = (Browser.Engine.trident) ? 'styleFloat' : 'cssFloat';
		}
		property = property.camelCase();
		var result = this.style[property];
		if (!$chk(result)){
			result = [];
			for (var style in Element.ShortStyles){
				if (property != style) continue;
				for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s));
				return result.join(' ');
			}
			result = this.getComputedStyle(property);
		}
		if (result){
			result = String(result);
			var color = result.match(/rgba?\([\d\s,]+\)/);
			if (color) result = result.replace(color[0], color[0].rgbToHex());
		}
		if (Browser.Engine.presto || (Browser.Engine.trident && !$chk(parseInt(result)))){
			if (property.test(/^(height|width)$/)){
				var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0;
				values.each(function(value){
					size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt();
				}, this);
				return this['offset' + property.capitalize()] - size + 'px';
			}
			if ((Browser.Engine.presto) && String(result).test('px')) return result;
			if (property.test(/(border(.+)Width|margin|padding)/)) return '0px';
		}
		return result;
	},

	setStyles: function(styles){
		for (var style in styles) this.setStyle(style, styles[style]);
		return this;
	},

	getStyles: function(){
		var result = {};
		Array.each(arguments, function(key){
			result[key] = this.getStyle(key);
		}, this);
		return result;
	}

});

Element.Styles = new Hash({
	left: '@px', top: '@px', bottom: '@px', right: '@px',
	width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px',
	backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)',
	fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)',
	margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)',
	borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)',
	zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@'
});

Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}};

['Top', 'Right', 'Bottom', 'Left'].each(function(direction){
	var Short = Element.ShortStyles;
	var All = Element.Styles;
	['margin', 'padding'].each(function(style){
		var sd = style + direction;
		Short[style][sd] = All[sd] = '@px';
	});
	var bd = 'border' + direction;
	Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)';
	var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color';
	Short[bd] = {};
	Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px';
	Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@';
	Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)';
});


/*
Script: Element.Dimensions.js
	Contains methods to work with size, scroll, or positioning of Elements and the window object.

License:
	MIT-style license.

Credits:
	- Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
	- Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
*/

(function(){

Element.implement({

	scrollTo: function(x, y){
		if (isBody(this)){
			this.getWindow().scrollTo(x, y);
		} else {
			this.scrollLeft = x;
			this.scrollTop = y;
		}
		return this;
	},

	getSize: function(){
		if (isBody(this)) return this.getWindow().getSize();
		return {x: this.offsetWidth, y: this.offsetHeight};
	},

	getScrollSize: function(){
		if (isBody(this)) return this.getWindow().getScrollSize();
		return {x: this.scrollWidth, y: this.scrollHeight};
	},

	getScroll: function(){
		if (isBody(this)) return this.getWindow().getScroll();
		return {x: this.scrollLeft, y: this.scrollTop};
	},

	getScrolls: function(){
		var element = this, position = {x: 0, y: 0};
		while (element && !isBody(element)){
			position.x += element.scrollLeft;
			position.y += element.scrollTop;
			element = element.parentNode;
		}
		return position;
	},

	getOffsetParent: function(){
		var element = this;
		if (isBody(element)) return null;
		if (!Browser.Engine.trident) return element.offsetParent;
		while ((element = element.parentNode) && !isBody(element)){
			if (styleString(element, 'position') != 'static') return element;
		}
		return null;
	},

	getOffsets: function(){
		if (Browser.Engine.trident){
			var bound = this.getBoundingClientRect(), html = this.getDocument().documentElement;
			return {
				x: bound.left + html.scrollLeft - html.clientLeft,
				y: bound.top + html.scrollTop - html.clientTop
			};
		}

		var element = this, position = {x: 0, y: 0};
		if (isBody(this)) return position;

		while (element && !isBody(element)){
			position.x += element.offsetLeft;
			position.y += element.offsetTop;

			if (Browser.Engine.gecko){
				if (!borderBox(element)){
					position.x += leftBorder(element);
					position.y += topBorder(element);
				}
				var parent = element.parentNode;
				if (parent && styleString(parent, 'overflow') != 'visible'){
					position.x += leftBorder(parent);
					position.y += topBorder(parent);
				}
			} else if (element != this && Browser.Engine.webkit){
				position.x += leftBorder(element);
				position.y += topBorder(element);
			}

			element = element.offsetParent;
		}
		if (Browser.Engine.gecko && !borderBox(this)){
			position.x -= leftBorder(this);
			position.y -= topBorder(this);
		}
		return position;
	},

	getPosition: function(relative){
		if (isBody(this)) return {x: 0, y: 0};
		var offset = this.getOffsets(), scroll = this.getScrolls();
		var position = {x: offset.x - scroll.x, y: offset.y - scroll.y};
		var relativePosition = (relative && (relative = $(relative))) ? relative.getPosition() : {x: 0, y: 0};
		return {x: position.x - relativePosition.x, y: position.y - relativePosition.y};
	},

	getCoordinates: function(element){
		if (isBody(this)) return this.getWindow().getCoordinates();
		var position = this.getPosition(element), size = this.getSize();
		var obj = {left: position.x, top: position.y, width: size.x, height: size.y};
		obj.right = obj.left + obj.width;
		obj.bottom = obj.top + obj.height;
		return obj;
	},

	computePosition: function(obj){
		return {left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top')};
	},

	position: function(obj){
		return this.setStyles(this.computePosition(obj));
	}

});

Native.implement([Document, Window], {

	getSize: function(){
		var win = this.getWindow();
		if (Browser.Engine.presto || Browser.Engine.webkit) return {x: win.innerWidth, y: win.innerHeight};
		var doc = getCompatElement(this);
		return {x: doc.clientWidth, y: doc.clientHeight};
	},

	getScroll: function(){
		var win = this.getWindow();
		var doc = getCompatElement(this);
		return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
	},

	getScrollSize: function(){
		var doc = getCompatElement(this);
		var min = this.getSize();
		return {x: Math.max(doc.scrollWidth, min.x), y: Math.max(doc.scrollHeight, min.y)};
	},

	getPosition: function(){
		return {x: 0, y: 0};
	},

	getCoordinates: function(){
		var size = this.getSize();
		return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
	}

});

// private methods

var styleString = Element.getComputedStyle;

function styleNumber(element, style){
	return styleString(element, style).toInt() || 0;
};

function borderBox(element){
	return styleString(element, '-moz-box-sizing') == 'border-box';
};

function topBorder(element){
	return styleNumber(element, 'border-top-width');
};

function leftBorder(element){
	return styleNumber(element, 'border-left-width');
};

function isBody(element){
	return (/^(?:body|html)$/i).test(element.tagName);
};

function getCompatElement(element){
	var doc = element.getDocument();
	return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
};

})();

//aliases

Native.implement([Window, Document, Element], {

	getHeight: function(){
		return this.getSize().y;
	},

	getWidth: function(){
		return this.getSize().x;
	},

	getScrollTop: function(){
		return this.getScroll().y;
	},

	getScrollLeft: function(){
		return this.getScroll().x;
	},

	getScrollHeight: function(){
		return this.getScrollSize().y;
	},

	getScrollWidth: function(){
		return this.getScrollSize().x;
	},

	getTop: function(){
		return this.getPosition().y;
	},

	getLeft: function(){
		return this.getPosition().x;
	}

});


/*
Script: Selectors.js
	Adds advanced CSS Querying capabilities for targeting elements. Also includes pseudoselectors support.

License:
	MIT-style license.
*/

Native.implement([Document, Element], {

	getElements: function(expression, nocash){
		expression = expression.split(',');
		var items, local = {};
		for (var i = 0, l = expression.length; i < l; i++){
			var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
			if (i != 0 && elements.item) elements = $A(elements);
			items = (i == 0) ? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
		}
		return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
	}

});

Element.implement({

	match: function(selector){
		if (!selector || (selector == this)) return true;
		var tagid = Selectors.Utils.parseTagAndID(selector);
		var tag = tagid[0], id = tagid[1];
		if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
		var parsed = Selectors.Utils.parseSelector(selector);
		return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
	}

});

var Selectors = {Cache: {nth: {}, parsed: {}}};

Selectors.RegExps = {
	id: (/#([\w-]+)/),
	tag: (/^(\w+|\*)/),
	quick: (/^(\w+|\*)$/),
	splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
	combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)(["']?)([^\4]*?)\4)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
};

Selectors.Utils = {

	chk: function(item, uniques){
		if (!uniques) return true;
		var uid = $uid(item);
		if (!uniques[uid]) return uniques[uid] = true;
		return false;
	},

	parseNthArgument: function(argument){
		if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
		var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
		if (!parsed) return false;
		var inta = parseInt(parsed[1]);
		var a = (inta || inta === 0) ? inta : 1;
		var special = parsed[2] || false;
		var b = parseInt(parsed[3]) || 0;
		if (a != 0){
			b--;
			while (b < 1) b += a;
			while (b >= a) b -= a;
		} else {
			a = b;
			special = 'index';
		}
		switch (special){
			case 'n': parsed = {a: a, b: b, special: 'n'}; break;
			case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
			case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
			case 'first': parsed = {a: 0, special: 'index'}; break;
			case 'last': parsed = {special: 'last-child'}; break;
			case 'only': parsed = {special: 'only-child'}; break;
			default: parsed = {a: (a - 1), special: 'index'};
		}

		return Selectors.Cache.nth[argument] = parsed;
	},

	parseSelector: function(selector){
		if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
		var m, parsed = {classes: [], pseudos: [], attributes: []};
		while ((m = Selectors.RegExps.combined.exec(selector))){
			var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
			if (cn){
				parsed.classes.push(cn);
			} else if (pn){
				var parser = Selectors.Pseudo.get(pn);
				if (parser) parsed.pseudos.push({parser: parser, argument: pa});
				else parsed.attributes.push({name: pn, operator: '=', value: pa});
			} else if (an){
				parsed.attributes.push({name: an, operator: ao, value: av});
			}
		}
		if (!parsed.classes.length) delete parsed.classes;
		if (!parsed.attributes.length) delete parsed.attributes;
		if (!parsed.pseudos.length) delete parsed.pseudos;
		if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
		return Selectors.Cache.parsed[selector] = parsed;
	},

	parseTagAndID: function(selector){
		var tag = selector.match(Selectors.RegExps.tag);
		var id = selector.match(Selectors.RegExps.id);
		return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
	},

	filter: function(item, parsed, local){
		var i;
		if (parsed.classes){
			for (i = parsed.classes.length; i--; i){
				var cn = parsed.classes[i];
				if (!Selectors.Filters.byClass(item, cn)) return false;
			}
		}
		if (parsed.attributes){
			for (i = parsed.attributes.length; i--; i){
				var att = parsed.attributes[i];
				if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
			}
		}
		if (parsed.pseudos){
			for (i = parsed.pseudos.length; i--; i){
				var psd = parsed.pseudos[i];
				if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
			}
		}
		return true;
	},

	getByTagAndID: function(ctx, tag, id){
		if (id){
			var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
			return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
		} else {
			return ctx.getElementsByTagName(tag);
		}
	},

	search: function(self, expression, local){
		var splitters = [];

		var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
			splitters.push(m1);
			return ':)' + m2;
		}).split(':)');

		var items, filtered, item;

		for (var i = 0, l = selectors.length; i < l; i++){

			var selector = selectors[i];

			if (i == 0 && Selectors.RegExps.quick.test(selector)){
				items = self.getElementsByTagName(selector);
				continue;
			}

			var splitter = splitters[i - 1];

			var tagid = Selectors.Utils.parseTagAndID(selector);
			var tag = tagid[0], id = tagid[1];

			if (i == 0){
				items = Selectors.Utils.getByTagAndID(self, tag, id);
			} else {
				var uniques = {}, found = [];
				for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
				items = found;
			}

			var parsed = Selectors.Utils.parseSelector(selector);

			if (parsed){
				filtered = [];
				for (var m = 0, n = items.length; m < n; m++){
					item = items[m];
					if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
				}
				items = filtered;
			}

		}

		return items;

	}

};

Selectors.Getters = {

	' ': function(found, self, tag, id, uniques){
		var items = Selectors.Utils.getByTagAndID(self, tag, id);
		for (var i = 0, l = items.length; i < l; i++){
			var item = items[i];
			if (Selectors.Utils.chk(item, uniques)) found.push(item);
		}
		return found;
	},

	'>': function(found, self, tag, id, uniques){
		var children = Selectors.Utils.getByTagAndID(self, tag, id);
		for (var i = 0, l = children.length; i < l; i++){
			var child = children[i];
			if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
		}
		return found;
	},

	'+': function(found, self, tag, id, uniques){
		while ((self = self.nextSibling)){
			if (self.nodeType == 1){
				if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
				break;
			}
		}
		return found;
	},

	'~': function(found, self, tag, id, uniques){
		while ((self = self.nextSibling)){
			if (self.nodeType == 1){
				if (!Selectors.Utils.chk(self, uniques)) break;
				if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
			}
		}
		return found;
	}

};

Selectors.Filters = {

	byTag: function(self, tag){
		return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
	},

	byID: function(self, id){
		return (!id || (self.id && self.id == id));
	},

	byClass: function(self, klass){
		return (self.className && self.className.contains(klass, ' '));
	},

	byPseudo: function(self, parser, argument, local){
		return parser.call(self, argument, local);
	},

	byAttribute: function(self, name, operator, value){
		var result = Element.prototype.getProperty.call(self, name);
		if (!result) return (operator == '!=');
		if (!operator || value == undefined) return true;
		switch (operator){
			case '=': return (result == value);
			case '*=': return (result.contains(value));
			case '^=': return (result.substr(0, value.length) == value);
			case '$=': return (result.substr(result.length - value.length) == value);
			case '!=': return (result != value);
			case '~=': return result.contains(value, ' ');
			case '|=': return result.contains(value, '-');
		}
		return false;
	}

};

Selectors.Pseudo = new Hash({

	// w3c pseudo selectors

	checked: function(){
		return this.checked;
	},

	empty: function(){
		return !(this.innerText || this.textContent || '').length;
	},

	not: function(selector){
		return !Element.match(this, selector);
	},

	contains: function(text){
		return (this.innerText || this.textContent || '').contains(text);
	},

	'first-child': function(){
		return Selectors.Pseudo.index.call(this, 0);
	},

	'last-child': function(){
		var element = this;
		while ((element = element.nextSibling)){
			if (element.nodeType == 1) return false;
		}
		return true;
	},

	'only-child': function(){
		var prev = this;
		while ((prev = prev.previousSibling)){
			if (prev.nodeType == 1) return false;
		}
		var next = this;
		while ((next = next.nextSibling)){
			if (next.nodeType == 1) return false;
		}
		return true;
	},

	'nth-child': function(argument, local){
		argument = (argument == undefined) ? 'n' : argument;
		var parsed = Selectors.Utils.parseNthArgument(argument);
		if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
		var count = 0;
		local.positions = local.positions || {};
		var uid = $uid(this);
		if (!local.positions[uid]){
			var self = this;
			while ((self = self.previousSibling)){
				if (self.nodeType != 1) continue;
				count ++;
				var position = local.positions[$uid(self)];
				if (position != undefined){
					count = position + count;
					break;
				}
			}
			local.positions[uid] = count;
		}
		return (local.positions[uid] % parsed.a == parsed.b);
	},

	// custom pseudo selectors

	index: function(index){
		var element = this, count = 0;
		while ((element = element.previousSibling)){
			if (element.nodeType == 1 && ++count > index) return false;
		}
		return (count == index);
	},

	even: function(argument, local){
		return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
	},

	odd: function(argument, local){
		return Selectors.Pseudo['nth-child'].call(this, '2n', local);
	}

});


/*
Script: Domready.js
	Contains the domready custom event.

License:
	MIT-style license.
*/

Element.Events.domready = {

	onAdd: function(fn){
		if (Browser.loaded) fn.call(this);
	}

};

(function(){

	var domready = function(){
		if (Browser.loaded) return;
		Browser.loaded = true;
		window.fireEvent('domready');
		document.fireEvent('domready');
	};

	if (Browser.Engine.trident){
		var temp = document.createElement('div');
		(function(){
			($try(function(){
				temp.doScroll('left');
				return $(temp).inject(document.body).set('html', 'temp').dispose();
			})) ? domready() : arguments.callee.delay(50);
		})();
	} else if (Browser.Engine.webkit && Browser.Engine.version < 525){
		(function(){
			(['loaded', 'complete'].contains(document.readyState)) ? domready() : arguments.callee.delay(50);
		})();
	} else {
		window.addEvent('load', domready);
		document.addEvent('DOMContentLoaded', domready);
	}

})();


/*
Script: JSON.js
	JSON encoder and decoder.

License:
	MIT-style license.

See Also:
	<http://www.json.org/>
*/

var JSON = new Hash({

	$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},

	$replaceChars: function(chr){
		return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
	},

	encode: function(obj){
		switch ($type(obj)){
			case 'string':
				return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
			case 'array':
				return '[' + String(obj.map(JSON.encode).filter($defined)) + ']';
			case 'object': case 'hash':
				var string = [];
				Hash.each(obj, function(value, key){
					var json = JSON.encode(value);
					if (json) string.push(JSON.encode(key) + ':' + json);
				});
				return '{' + string + '}';
			case 'number': case 'boolean': return String(obj);
			case false: return 'null';
		}
		return null;
	},

	decode: function(string, secure){
		if ($type(string) != 'string' || !string.length) return null;
		if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
		return eval('(' + string + ')');
	}

});

Native.implement([Hash, Array, String, Number], {

	toJSON: function(){
		return JSON.encode(this);
	}

});


/*
Script: Cookie.js
	Class for creating, loading, and saving browser Cookies.

License:
	MIT-style license.

Credits:
	Based on the functions by Peter-Paul Koch (http://quirksmode.org).
*/

var Cookie = new Class({

	Implements: Options,

	options: {
		path: false,
		domain: false,
		duration: false,
		secure: false,
		document: document
	},

	initialize: function(key, options){
		this.key = key;
		this.setOptions(options);
	},

	write: function(value){
		value = encodeURIComponent(value);
		if (this.options.domain) value += '; domain=' + this.options.domain;
		if (this.options.path) value += '; path=' + this.options.path;
		if (this.options.duration){
			var date = new Date();
			date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);
			value += '; expires=' + date.toGMTString();
		}
		if (this.options.secure) value += '; secure';
		this.options.document.cookie = this.key + '=' + value;
		return this;
	},

	read: function(){
		var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)');
		return (value) ? decodeURIComponent(value[1]) : null;
	},

	dispose: function(){
		new Cookie(this.key, $merge(this.options, {duration: -1})).write('');
		return this;
	}

});

Cookie.write = function(key, value, options){
	return new Cookie(key, options).write(value);
};

Cookie.read = function(key){
	return new Cookie(key).read();
};

Cookie.dispose = function(key, options){
	return new Cookie(key, options).dispose();
};


/*
Script: Swiff.js
	Wrapper for embedding SWF movies. Supports (and fixes) External Interface Communication.

License:
	MIT-style license.

Credits:
	Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
*/

var Swiff = new Class({

	Implements: [Options],

	options: {
		id: null,
		height: 1,
		width: 1,
		container: null,
		properties: {},
		params: {
			quality: 'high',
			allowScriptAccess: 'always',
			wMode: 'transparent',
			swLiveConnect: true
		},
		callBacks: {},
		vars: {}
	},

	toElement: function(){
		return this.object;
	},

	initialize: function(path, options){
		this.instance = 'Swiff_' + $time();

		this.setOptions(options);
		options = this.options;
		var id = this.id = options.id || this.instance;
		var container = $(options.container);

		Swiff.CallBacks[this.instance] = {};

		var params = options.params, vars = options.vars, callBacks = options.callBacks;
		var properties = $extend({height: options.height, width: options.width}, options.properties);

		var self = this;

		for (var callBack in callBacks){
			Swiff.CallBacks[this.instance][callBack] = (function(option){
				return function(){
					return option.apply(self.object, arguments);
				};
			})(callBacks[callBack]);
			vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
		}

		params.flashVars = Hash.toQueryString(vars);
		if (Browser.Engine.trident){
			properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
			params.movie = path;
		} else {
			properties.type = 'application/x-shockwave-flash';
			properties.data = path;
		}
		var build = '<object id="' + id + '"';
		for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
		build += '>';
		for (var param in params){
			if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
		}
		build += '</object>';
		this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
	},

	replaces: function(element){
		element = $(element, true);
		element.parentNode.replaceChild(this.toElement(), element);
		return this;
	},

	inject: function(element){
		$(element, true).appendChild(this.toElement());
		return this;
	},

	remote: function(){
		return Swiff.remote.apply(Swiff, [this.toElement()].extend(arguments));
	}

});

Swiff.CallBacks = {};

Swiff.remote = function(obj, fn){
	var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
	return eval(rs);
};


/*
Script: Fx.js
	Contains the basic animation logic to be extended by all other Fx Classes.

License:
	MIT-style license.
*/

var Fx = new Class({

	Implements: [Chain, Events, Options],

	options: {
		/*
		onStart: $empty,
		onCancel: $empty,
		onComplete: $empty,
		*/
		fps: 50,
		unit: false,
		duration: 500,
		link: 'ignore'
	},

	initialize: function(options){
		this.subject = this.subject || this;
		this.setOptions(options);
		this.options.duration = Fx.Durations[this.options.duration] || this.options.duration.toInt();
		var wait = this.options.wait;
		if (wait === false) this.options.link = 'cancel';
	},

	getTransition: function(){
		return function(p){
			return -(Math.cos(Math.PI * p) - 1) / 2;
		};
	},

	step: function(){
		var time = $time();
		if (time < this.time + this.options.duration){
			var delta = this.transition((time - this.time) / this.options.duration);
			this.set(this.compute(this.from, this.to, delta));
		} else {
			this.set(this.compute(this.from, this.to, 1));
			this.complete();
		}
	},

	set: function(now){
		return now;
	},

	compute: function(from, to, delta){
		return Fx.compute(from, to, delta);
	},

	check: function(caller){
		if (!this.timer) return true;
		switch (this.options.link){
			case 'cancel': this.cancel(); return true;
			case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
		}
		return false;
	},

	start: function(from, to){
		if (!this.check(arguments.callee, from, to)) return this;
		this.from = from;
		this.to = to;
		this.time = 0;
		this.transition = this.getTransition();
		this.startTimer();
		this.onStart();
		return this;
	},

	complete: function(){
		if (this.stopTimer()) this.onComplete();
		return this;
	},

	cancel: function(){
		if (this.stopTimer()) this.onCancel();
		return this;
	},

	onStart: function(){
		this.fireEvent('start', this.subject);
	},

	onComplete: function(){
		this.fireEvent('complete', this.subject);
		if (!this.callChain()) this.fireEvent('chainComplete', this.subject);
	},

	onCancel: function(){
		this.fireEvent('cancel', this.subject).clearChain();
	},

	pause: function(){
		this.stopTimer();
		return this;
	},

	resume: function(){
		this.startTimer();
		return this;
	},

	stopTimer: function(){
		if (!this.timer) return false;
		this.time = $time() - this.time;
		this.timer = $clear(this.timer);
		return true;
	},

	startTimer: function(){
		if (this.timer) return false;
		this.time = $time() - this.time;
		this.timer = this.step.periodical(Math.round(1000 / this.options.fps), this);
		return true;
	}

});

Fx.compute = function(from, to, delta){
	return (to - from) * delta + from;
};

Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000};


/*
Script: Fx.CSS.js
	Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.

License:
	MIT-style license.
*/

Fx.CSS = new Class({

	Extends: Fx,

	//prepares the base from/to object

	prepare: function(element, property, values){
		values = $splat(values);
		var values1 = values[1];
		if (!$chk(values1)){
			values[1] = values[0];
			values[0] = element.getStyle(property);
		}
		var parsed = values.map(this.parse);
		return {from: parsed[0], to: parsed[1]};
	},

	//parses a value into an array

	parse: function(value){
		value = $lambda(value)();
		value = (typeof value == 'string') ? value.split(' ') : $splat(value);
		return value.map(function(val){
			val = String(val);
			var found = false;
			Fx.CSS.Parsers.each(function(parser, key){
				if (found) return;
				var parsed = parser.parse(val);
				if ($chk(parsed)) found = {value: parsed, parser: parser};
			});
			found = found || {value: val, parser: Fx.CSS.Parsers.String};
			return found;
		});
	},

	//computes by a from and to prepared objects, using their parsers.

	compute: function(from, to, delta){
		var computed = [];
		(Math.min(from.length, to.length)).times(function(i){
			computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
		});
		computed.$family = {name: 'fx:css:value'};
		return computed;
	},

	//serves the value as settable

	serve: function(value, unit){
		if ($type(value) != 'fx:css:value') value = this.parse(value);
		var returned = [];
		value.each(function(bit){
			returned = returned.concat(bit.parser.serve(bit.value, unit));
		});
		return returned;
	},

	//renders the change to an element

	render: function(element, property, value, unit){
		element.setStyle(property, this.serve(value, unit));
	},

	//searches inside the page css to find the values for a selector

	search: function(selector){
		if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
		var to = {};
		Array.each(document.styleSheets, function(sheet, j){
			var href = sheet.href;
			if (href && href.contains('://') && !href.contains(document.domain)) return;
			var rules = sheet.rules || sheet.cssRules;
			Array.each(rules, function(rule, i){
				if (!rule.style) return;
				var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
					return m.toLowerCase();
				}) : null;
				if (!selectorText || !selectorText.test('^' + selector + '$')) return;
				Element.Styles.each(function(value, style){
					if (!rule.style[style] || Element.ShortStyles[style]) return;
					value = String(rule.style[style]);
					to[style] = (value.test(/^rgb/)) ? value.rgbToHex() : value;
				});
			});
		});
		return Fx.CSS.Cache[selector] = to;
	}

});

Fx.CSS.Cache = {};

Fx.CSS.Parsers = new Hash({

	Color: {
		parse: function(value){
			if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
			return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
		},
		compute: function(from, to, delta){
			return from.map(function(value, i){
				return Math.round(Fx.compute(from[i], to[i], delta));
			});
		},
		serve: function(value){
			return value.map(Number);
		}
	},

	Number: {
		parse: parseFloat,
		compute: Fx.compute,
		serve: function(value, unit){
			return (unit) ? value + unit : value;
		}
	},

	String: {
		parse: $lambda(false),
		compute: $arguments(1),
		serve: $arguments(0)
	}

});


/*
Script: Fx.Tween.js
	Formerly Fx.Style, effect to transition any CSS property for an element.

License:
	MIT-style license.
*/

Fx.Tween = new Class({

	Extends: Fx.CSS,

	initialize: function(element, options){
		this.element = this.subject = $(element);
		this.parent(options);
	},

	set: function(property, now){
		if (arguments.length == 1){
			now = property;
			property = this.property || this.options.property;
		}
		this.render(this.element, property, now, this.options.unit);
		return this;
	},

	start: function(property, from, to){
		if (!this.check(arguments.callee, property, from, to)) return this;
		var args = Array.flatten(arguments);
		this.property = this.options.property || args.shift();
		var parsed = this.prepare(this.element, this.property, args);
		return this.parent(parsed.from, parsed.to);
	}

});

Element.Properties.tween = {

	set: function(options){
		var tween = this.retrieve('tween');
		if (tween) tween.cancel();
		return this.eliminate('tween').store('tween:options', $extend({link: 'cancel'}, options));
	},

	get: function(options){
		if (options || !this.retrieve('tween')){
			if (options || !this.retrieve('tween:options')) this.set('tween', options);
			this.store('tween', new Fx.Tween(this, this.retrieve('tween:options')));
		}
		return this.retrieve('tween');
	}

};

Element.implement({

	tween: function(property, from, to){
		this.get('tween').start(arguments);
		return this;
	},

	fade: function(how){
		var fade = this.get('tween'), o = 'opacity', toggle;
		how = $pick(how, 'toggle');
		switch (how){
			case 'in': fade.start(o, 1); break;
			case 'out': fade.start(o, 0); break;
			case 'show': fade.set(o, 1); break;
			case 'hide': fade.set(o, 0); break;
			case 'toggle':
				var flag = this.retrieve('fade:flag', this.get('opacity') == 1);
				fade.start(o, (flag) ? 0 : 1);
				this.store('fade:flag', !flag);
				toggle = true;
			break;
			default: fade.start(o, arguments);
		}
		if (!toggle) this.eliminate('fade:flag');
		return this;
	},

	highlight: function(start, end){
		if (!end){
			end = this.retrieve('highlight:original', this.getStyle('background-color'));
			end = (end == 'transparent') ? '#fff' : end;
		}
		var tween = this.get('tween');
		tween.start('background-color', start || '#ffff88', end).chain(function(){
			this.setStyle('background-color', this.retrieve('highlight:original'));
			tween.callChain();
		}.bind(this));
		return this;
	}

});


/*
Script: Fx.Morph.js
	Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules.

License:
	MIT-style license.
*/

Fx.Morph = new Class({

	Extends: Fx.CSS,

	initialize: function(element, options){
		this.element = this.subject = $(element);
		this.parent(options);
	},

	set: function(now){
		if (typeof now == 'string') now = this.search(now);
		for (var p in now) this.render(this.element, p, now[p], this.options.unit);
		return this;
	},

	compute: function(from, to, delta){
		var now = {};
		for (var p in from) now[p] = this.parent(from[p], to[p], delta);
		return now;
	},

	start: function(properties){
		if (!this.check(arguments.callee, properties)) return this;
		if (typeof properties == 'string') properties = this.search(properties);
		var from = {}, to = {};
		for (var p in properties){
			var parsed = this.prepare(this.element, p, properties[p]);
			from[p] = parsed.from;
			to[p] = parsed.to;
		}
		return this.parent(from, to);
	}

});

Element.Properties.morph = {

	set: function(options){
		var morph = this.retrieve('morph');
		if (morph) morph.cancel();
		return this.eliminate('morph').store('morph:options', $extend({link: 'cancel'}, options));
	},

	get: function(options){
		if (options || !this.retrieve('morph')){
			if (options || !this.retrieve('morph:options')) this.set('morph', options);
			this.store('morph', new Fx.Morph(this, this.retrieve('morph:options')));
		}
		return this.retrieve('morph');
	}

};

Element.implement({

	morph: function(props){
		this.get('morph').start(props);
		return this;
	}

});


/*
Script: Fx.Transitions.js
	Contains a set of advanced transitions to be used with any of the Fx Classes.

License:
	MIT-style license.

Credits:
	Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>, modified and optimized to be used with MooTools.
*/

Fx.implement({

	getTransition: function(){
		var trans = this.options.transition || Fx.Transitions.Sine.easeInOut;
		if (typeof trans == 'string'){
			var data = trans.split(':');
			trans = Fx.Transitions;
			trans = trans[data[0]] || trans[data[0].capitalize()];
			if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')];
		}
		return trans;
	}

});

Fx.Transition = function(transition, params){
	params = $splat(params);
	return $extend(transition, {
		easeIn: function(pos){
			return transition(pos, params);
		},
		easeOut: function(pos){
			return 1 - transition(1 - pos, params);
		},
		easeInOut: function(pos){
			return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
		}
	});
};

Fx.Transitions = new Hash({

	linear: $arguments(0)

});

Fx.Transitions.extend = function(transitions){
	for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]);
};

Fx.Transitions.extend({

	Pow: function(p, x){
		return Math.pow(p, x[0] || 6);
	},

	Expo: function(p){
		return Math.pow(2, 8 * (p - 1));
	},

	Circ: function(p){
		return 1 - Math.sin(Math.acos(p));
	},

	Sine: function(p){
		return 1 - Math.sin((1 - p) * Math.PI / 2);
	},

	Back: function(p, x){
		x = x[0] || 1.618;
		return Math.pow(p, 2) * ((x + 1) * p - x);
	},

	Bounce: function(p){
		var value;
		for (var a = 0, b = 1; 1; a += b, b /= 2){
			if (p >= (7 - 4 * a) / 11){
				value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
				break;
			}
		}
		return value;
	},

	Elastic: function(p, x){
		return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
	}

});

['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
	Fx.Transitions[transition] = new Fx.Transition(function(p){
		return Math.pow(p, [i + 2]);
	});
});


/*
Script: Request.js
	Powerful all purpose Request Class. Uses XMLHTTPRequest.

License:
	MIT-style license.
*/

var Request = new Class({

	Implements: [Chain, Events, Options],

	options: {/*
		onRequest: $empty,
		onComplete: $empty,
		onCancel: $empty,
		onSuccess: $empty,
		onFailure: $empty,
		onException: $empty,*/
		url: '',
		data: '',
		headers: {
			'X-Requested-With': 'XMLHttpRequest',
			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
		},
		async: true,
		format: false,
		method: 'post',
		link: 'ignore',
		isSuccess: null,
		emulation: true,
		urlEncoded: true,
		encoding: 'utf-8',
		evalScripts: false,
		evalResponse: false
	},

	initialize: function(options){
		this.xhr = new Browser.Request();
		this.setOptions(options);
		this.options.isSuccess = this.options.isSuccess || this.isSuccess;
		this.headers = new Hash(this.options.headers);
	},

	onStateChange: function(){
		if (this.xhr.readyState != 4 || !this.running) return;
		this.running = false;
		this.status = 0;
		$try(function(){
			this.status = this.xhr.status;
		}.bind(this));
		if (this.options.isSuccess.call(this, this.status)){
			this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
			this.success(this.response.text, this.response.xml);
		} else {
			this.response = {text: null, xml: null};
			this.failure();
		}
		this.xhr.onreadystatechange = $empty;
	},

	isSuccess: function(){
		return ((this.status >= 200) && (this.status < 300));
	},

	processScripts: function(text){
		if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
		return text.stripScripts(this.options.evalScripts);
	},

	success: function(text, xml){
		this.onSuccess(this.processScripts(text), xml);
	},

	onSuccess: function(){
		this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
	},

	failure: function(){
		this.onFailure();
	},

	onFailure: function(){
		this.fireEvent('complete').fireEvent('failure', this.xhr);
	},

	setHeader: function(name, value){
		this.headers.set(name, value);
		return this;
	},

	getHeader: function(name){
		return $try(function(){
			return this.xhr.getResponseHeader(name);
		}.bind(this));
	},

	check: function(caller){
		if (!this.running) return true;
		switch (this.options.link){
			case 'cancel': this.cancel(); return true;
			case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
		}
		return false;
	},

	send: function(options){
		if (!this.check(arguments.callee, options)) return this;
		this.running = true;

		var type = $type(options);
		if (type == 'string' || type == 'element') options = {data: options};

		var old = this.options;
		options = $extend({data: old.data, url: old.url, method: old.method}, options);
		var data = options.data, url = options.url, method = options.method;

		switch ($type(data)){
			case 'element': data = $(data).toQueryString(); break;
			case 'object': case 'hash': data = Hash.toQueryString(data);
		}

		if (this.options.format){
			var format = 'format=' + this.options.format;
			data = (data) ? format + '&' + data : format;
		}

		if (this.options.emulation && ['put', 'delete'].contains(method)){
			var _method = '_method=' + method;
			data = (data) ? _method + '&' + data : _method;
			method = 'post';
		}

		if (this.options.urlEncoded && method == 'post'){
			var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
			this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
		}

		if (data && method == 'get'){
			url = url + (url.contains('?') ? '&' : '?') + data;
			data = null;
		}

		this.xhr.open(method.toUpperCase(), url, this.options.async);

		this.xhr.onreadystatechange = this.onStateChange.bind(this);

		this.headers.each(function(value, key){
			try {
				this.xhr.setRequestHeader(key, value);
			} catch (e){
				this.fireEvent('exception', [key, value]);
			}
		}, this);

		this.fireEvent('request');
		this.xhr.send(data);
		if (!this.options.async) this.onStateChange();
		return this;
	},

	cancel: function(){
		if (!this.running) return this;
		this.running = false;
		this.xhr.abort();
		this.xhr.onreadystatechange = $empty;
		this.xhr = new Browser.Request();
		this.fireEvent('cancel');
		return this;
	}

});

(function(){

var methods = {};
['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){
	methods[method] = function(){
		var params = Array.link(arguments, {url: String.type, data: $defined});
		return this.send($extend(params, {method: method.toLowerCase()}));
	};
});

Request.implement(methods);

})();

Element.Properties.send = {

	set: function(options){
		var send = this.retrieve('send');
		if (send) send.cancel();
		return this.eliminate('send').store('send:options', $extend({
			data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action')
		}, options));
	},

	get: function(options){
		if (options || !this.retrieve('send')){
			if (options || !this.retrieve('send:options')) this.set('send', options);
			this.store('send', new Request(this.retrieve('send:options')));
		}
		return this.retrieve('send');
	}

};

Element.implement({

	send: function(url){
		var sender = this.get('send');
		sender.send({data: this, url: url || sender.options.url});
		return this;
	}

});


/*
Script: Request.HTML.js
	Extends the basic Request Class with additional methods for interacting with HTML responses.

License:
	MIT-style license.
*/

Request.HTML = new Class({

	Extends: Request,

	options: {
		update: false,
		evalScripts: true,
		filter: false
	},

	processHTML: function(text){
		var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
		text = (match) ? match[1] : text;

		var container = new Element('div');

		return $try(function(){
			var root = '<root>' + text + '</root>', doc;
			if (Browser.Engine.trident){
				doc = new ActiveXObject('Microsoft.XMLDOM');
				doc.async = false;
				doc.loadXML(root);
			} else {
				doc = new DOMParser().parseFromString(root, 'text/xml');
			}
			root = doc.getElementsByTagName('root')[0];
			for (var i = 0, k = root.childNodes.length; i < k; i++){
				var child = Element.clone(root.childNodes[i], true, true);
				if (child) container.grab(child);
			}
			return container;
		}) || container.set('html', text);
	},

	success: function(text){
		var options = this.options, response = this.response;

		response.html = text.stripScripts(function(script){
			response.javascript = script;
		});

		var temp = this.processHTML(response.html);

		response.tree = temp.childNodes;
		response.elements = temp.getElements('*');

		if (options.filter) response.tree = response.elements.filter(options.filter);
		if (options.update) $(options.update).empty().set('html', response.html);
		if (options.evalScripts) $exec(response.javascript);

		this.onSuccess(response.tree, response.elements, response.html, response.javascript);
	}

});

Element.Properties.load = {

	set: function(options){
		var load = this.retrieve('load');
		if (load) load.cancel();
		return this.eliminate('load').store('load:options', $extend({data: this, link: 'cancel', update: this, method: 'get'}, options));
	},

	get: function(options){
		if (options || ! this.retrieve('load')){
			if (options || !this.retrieve('load:options')) this.set('load', options);
			this.store('load', new Request.HTML(this.retrieve('load:options')));
		}
		return this.retrieve('load');
	}

};

Element.implement({

	load: function(){
		this.get('load').send(Array.link(arguments, {data: Object.type, url: String.type}));
		return this;
	}

});


/*
Script: Request.JSON.js
	Extends the basic Request Class with additional methods for sending and receiving JSON data.

License:
	MIT-style license.
*/

Request.JSON = new Class({

	Extends: Request,

	options: {
		secure: true
	},

	initialize: function(options){
		this.parent(options);
		this.headers.extend({'Accept': 'application/json', 'X-Request': 'JSON'});
	},

	success: function(text){
		this.response.json = JSON.decode(text, this.options.secure);
		this.onSuccess(this.response.json, text);
	}

});
//MooTools More, <http://mootools.net/more>. Copyright (c) 2006-2008 Valerio Proietti, <http://mad4milk.net>, MIT Style License.

/*
Script: Fx.Slide.js
	Effect to slide an element in and out of view.

License:
	MIT-style license.
*/

Fx.Slide = new Class({

	Extends: Fx,

	options: {
		mode: 'vertical'
	},

	initialize: function(element, options){
		this.addEvent('complete', function(){
			this.open = (this.wrapper['offset' + this.layout.capitalize()] != 0);
			if (this.open && Browser.Engine.webkit419) this.element.dispose().inject(this.wrapper);
		}, true);
		this.element = this.subject = $(element);
		this.parent(options);
		var wrapper = this.element.retrieve('wrapper');
		this.wrapper = wrapper || new Element('div', {
			styles: $extend(this.element.getStyles('margin', 'position'), {'overflow': 'hidden'})
		}).wraps(this.element);
		this.element.store('wrapper', this.wrapper).setStyle('margin', 0);
		this.now = [];
		this.open = true;
	},

	vertical: function(){
		this.margin = 'margin-top';
		this.layout = 'height';
		this.offset = this.element.offsetHeight;
	},

	horizontal: function(){
		this.margin = 'margin-left';
		this.layout = 'width';
		this.offset = this.element.offsetWidth;
	},

	set: function(now){
		this.element.setStyle(this.margin, now[0]);
		this.wrapper.setStyle(this.layout, now[1]);
		return this;
	},

	compute: function(from, to, delta){
		var now = [];
		var x = 2;
		x.times(function(i){
			now[i] = Fx.compute(from[i], to[i], delta);
		});
		return now;
	},

	start: function(how, mode){
		if (!this.check(arguments.callee, how, mode)) return this;
		this[mode || this.options.mode]();
		var margin = this.element.getStyle(this.margin).toInt();
		var layout = this.wrapper.getStyle(this.layout).toInt();
		var caseIn = [[margin, layout], [0, this.offset]];
		var caseOut = [[margin, layout], [-this.offset, 0]];
		var start;
		switch (how){
			case 'in': start = caseIn; break;
			case 'out': start = caseOut; break;
			case 'toggle': start = (this.wrapper['offset' + this.layout.capitalize()] == 0) ? caseIn : caseOut;
		}
		return this.parent(start[0], start[1]);
	},

	slideIn: function(mode){
		return this.start('in', mode);
	},

	slideOut: function(mode){
		return this.start('out', mode);
	},

	hide: function(mode){
		this[mode || this.options.mode]();
		this.open = false;
		return this.set([-this.offset, 0]);
	},

	show: function(mode){
		this[mode || this.options.mode]();
		this.open = true;
		return this.set([0, this.offset]);
	},

	toggle: function(mode){
		return this.start('toggle', mode);
	}

});

Element.Properties.slide = {

	set: function(options){
		var slide = this.retrieve('slide');
		if (slide) slide.cancel();
		return this.eliminate('slide').store('slide:options', $extend({link: 'cancel'}, options));
	},
	
	get: function(options){
		if (options || !this.retrieve('slide')){
			if (options || !this.retrieve('slide:options')) this.set('slide', options);
			this.store('slide', new Fx.Slide(this, this.retrieve('slide:options')));
		}
		return this.retrieve('slide');
	}

};

Element.implement({

	slide: function(how, mode){
		how = how || 'toggle';
		var slide = this.get('slide'), toggle;
		switch (how){
			case 'hide': slide.hide(mode); break;
			case 'show': slide.show(mode); break;
			case 'toggle':
				var flag = this.retrieve('slide:flag', slide.open);
				slide[(flag) ? 'slideOut' : 'slideIn'](mode);
				this.store('slide:flag', !flag);
				toggle = true;
			break;
			default: slide.start(how, mode);
		}
		if (!toggle) this.eliminate('slide:flag');
		return this;
	}

});


/*
Script: Fx.Scroll.js
	Effect to smoothly scroll any element, including the window.

License:
	MIT-style license.
*/

Fx.Scroll = new Class({

	Extends: Fx,

	options: {
		offset: {'x': 0, 'y': 0},
		wheelStops: true
	},

	initialize: function(element, options){
		this.element = this.subject = $(element);
		this.parent(options);
		var cancel = this.cancel.bind(this, false);

		if ($type(this.element) != 'element') this.element = $(this.element.getDocument().body);

		var stopper = this.element;

		if (this.options.wheelStops){
			this.addEvent('start', function(){
				stopper.addEvent('mousewheel', cancel);
			}, true);
			this.addEvent('complete', function(){
				stopper.removeEvent('mousewheel', cancel);
			}, true);
		}
	},

	set: function(){
		var now = Array.flatten(arguments);
		this.element.scrollTo(now[0], now[1]);
	},

	compute: function(from, to, delta){
		var now = [];
		var x = 2;
		x.times(function(i){
			now.push(Fx.compute(from[i], to[i], delta));
		});
		return now;
	},

	start: function(x, y){
		if (!this.check(arguments.callee, x, y)) return this;
		var offsetSize = this.element.getSize(), scrollSize = this.element.getScrollSize();
		var scroll = this.element.getScroll(), values = {x: x, y: y};
		for (var z in values){
			var max = scrollSize[z] - offsetSize[z];
			if ($chk(values[z])) values[z] = ($type(values[z]) == 'number') ? values[z].limit(0, max) : max;
			else values[z] = scroll[z];
			values[z] += this.options.offset[z];
		}
		return this.parent([scroll.x, scroll.y], [values.x, values.y]);
	},

	toTop: function(){
		return this.start(false, 0);
	},

	toLeft: function(){
		return this.start(0, false);
	},

	toRight: function(){
		return this.start('right', false);
	},

	toBottom: function(){
		return this.start(false, 'bottom');
	},

	toElement: function(el){
		var position = $(el).getPosition(this.element);
		return this.start(position.x, position.y);
	}

});


/*
Script: Fx.Elements.js
	Effect to change any number of CSS properties of any number of Elements.

License:
	MIT-style license.
*/

Fx.Elements = new Class({

	Extends: Fx.CSS,

	initialize: function(elements, options){
		this.elements = this.subject = $$(elements);
		this.parent(options);
	},

	compute: function(from, to, delta){
		var now = {};
		for (var i in from){
			var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
			for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
		}
		return now;
	},

	set: function(now){
		for (var i in now){
			var iNow = now[i];
			for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
		}
		return this;
	},

	start: function(obj){
		if (!this.check(arguments.callee, obj)) return this;
		var from = {}, to = {};
		for (var i in obj){
			var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
			for (var p in iProps){
				var parsed = this.prepare(this.elements[i], p, iProps[p]);
				iFrom[p] = parsed.from;
				iTo[p] = parsed.to;
			}
		}
		return this.parent(from, to);
	}

});

/*
Script: Drag.js
	The base Drag Class. Can be used to drag and resize Elements using mouse events.

License:
	MIT-style license.
*/

var Drag = new Class({

	Implements: [Events, Options],

	options: {/*
		onBeforeStart: $empty,
		onStart: $empty,
		onDrag: $empty,
		onCancel: $empty,
		onComplete: $empty,*/
		snap: 6,
		unit: 'px',
		grid: false,
		style: true,
		limit: false,
		handle: false,
		invert: false,
		preventDefault: false,
		modifiers: {x: 'left', y: 'top'}
	},

	initialize: function(){
		var params = Array.link(arguments, {'options': Object.type, 'element': $defined});
		this.element = $(params.element);
		this.document = this.element.getDocument();
		this.setOptions(params.options || {});
		var htype = $type(this.options.handle);
		this.handles = (htype == 'array' || htype == 'collection') ? $$(this.options.handle) : $(this.options.handle) || this.element;
		this.mouse = {'now': {}, 'pos': {}};
		this.value = {'start': {}, 'now': {}};
		
		this.selection = (Browser.Engine.trident) ? 'selectstart' : 'mousedown';
		
		this.bound = {
			start: this.start.bind(this),
			check: this.check.bind(this),
			drag: this.drag.bind(this),
			stop: this.stop.bind(this),
			cancel: this.cancel.bind(this),
			eventStop: $lambda(false)
		};
		this.attach();
	},

	attach: function(){
		this.handles.addEvent('mousedown', this.bound.start);
		return this;
	},

	detach: function(){
		this.handles.removeEvent('mousedown', this.bound.start);
		return this;
	},

	start: function(event){
		if (this.options.preventDefault) event.preventDefault();
		this.fireEvent('beforeStart', this.element);
		this.mouse.start = event.page;
		var limit = this.options.limit;
		this.limit = {'x': [], 'y': []};
		for (var z in this.options.modifiers){
			if (!this.options.modifiers[z]) continue;
			if (this.options.style) this.value.now[z] = this.element.getStyle(this.options.modifiers[z]).toInt();
			else this.value.now[z] = this.element[this.options.modifiers[z]];
			if (this.options.invert) this.value.now[z] *= -1;
			this.mouse.pos[z] = event.page[z] - this.value.now[z];
			if (limit && limit[z]){
				for (var i = 2; i--; i){
					if ($chk(limit[z][i])) this.limit[z][i] = $lambda(limit[z][i])();
				}
			}
		}
		if ($type(this.options.grid) == 'number') this.options.grid = {'x': this.options.grid, 'y': this.options.grid};
		this.document.addEvents({mousemove: this.bound.check, mouseup: this.bound.cancel});
		this.document.addEvent(this.selection, this.bound.eventStop);
	},

	check: function(event){
		if (this.options.preventDefault) event.preventDefault();
		var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
		if (distance > this.options.snap){
			this.cancel();
			this.document.addEvents({
				mousemove: this.bound.drag,
				mouseup: this.bound.stop
			});
			this.fireEvent('start', this.element).fireEvent('snap', this.element);
		}
	},

	drag: function(event){
		if (this.options.preventDefault) event.preventDefault();
		this.mouse.now = event.page;
		for (var z in this.options.modifiers){
			if (!this.options.modifiers[z]) continue;
			this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
			if (this.options.invert) this.value.now[z] *= -1;
			if (this.options.limit && this.limit[z]){
				if ($chk(this.limit[z][1]) && (this.value.now[z] > this.limit[z][1])){
					this.value.now[z] = this.limit[z][1];
				} else if ($chk(this.limit[z][0]) && (this.value.now[z] < this.limit[z][0])){
					this.value.now[z] = this.limit[z][0];
				}
			}
			if (this.options.grid[z]) this.value.now[z] -= (this.value.now[z] % this.options.grid[z]);
			if (this.options.style) this.element.setStyle(this.options.modifiers[z], this.value.now[z] + this.options.unit);
			else this.element[this.options.modifiers[z]] = this.value.now[z];
		}
		this.fireEvent('drag', this.element);
	},

	cancel: function(event){
		this.document.removeEvent('mousemove', this.bound.check);
		this.document.removeEvent('mouseup', this.bound.cancel);
		if (event){
			this.document.removeEvent(this.selection, this.bound.eventStop);
			this.fireEvent('cancel', this.element);
		}
	},

	stop: function(event){
		this.document.removeEvent(this.selection, this.bound.eventStop);
		this.document.removeEvent('mousemove', this.bound.drag);
		this.document.removeEvent('mouseup', this.bound.stop);
		if (event) this.fireEvent('complete', this.element);
	}

});

Element.implement({
	
	makeResizable: function(options){
		return new Drag(this, $merge({modifiers: {'x': 'width', 'y': 'height'}}, options));
	}

});

/*
Script: Drag.Move.js
	A Drag extension that provides support for the constraining of draggables to containers and droppables.

License:
	MIT-style license.
*/

Drag.Move = new Class({

	Extends: Drag,

	options: {
		droppables: [],
		container: false
	},

	initialize: function(element, options){
		this.parent(element, options);
		this.droppables = $$(this.options.droppables);
		this.container = $(this.options.container);
		if (this.container && $type(this.container) != 'element') this.container = $(this.container.getDocument().body);
		element = this.element;
		
		var current = element.getStyle('position');
		var position = (current != 'static') ? current : 'absolute';
		if (element.getStyle('left') == 'auto' || element.getStyle('top') == 'auto') element.position(element.getPosition(element.offsetParent));
		
		element.setStyle('position', position);
		
		this.addEvent('start', function(){
			this.checkDroppables();
		}, true);
	},

	start: function(event){
		if (this.container){
			var el = this.element, cont = this.container, ccoo = cont.getCoordinates(el.offsetParent), cps = {}, ems = {};

			['top', 'right', 'bottom', 'left'].each(function(pad){
				cps[pad] = cont.getStyle('padding-' + pad).toInt();
				ems[pad] = el.getStyle('margin-' + pad).toInt();
			}, this);

			var width = el.offsetWidth + ems.left + ems.right, height = el.offsetHeight + ems.top + ems.bottom;
			var x = [ccoo.left + cps.left, ccoo.right - cps.right - width];
			var y = [ccoo.top + cps.top, ccoo.bottom - cps.bottom - height];

			this.options.limit = {x: x, y: y};
		}
		this.parent(event);
	},

	checkAgainst: function(el){
		el = el.getCoordinates();
		var now = this.mouse.now;
		return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
	},

	checkDroppables: function(){
		var overed = this.droppables.filter(this.checkAgainst, this).getLast();
		if (this.overed != overed){
			if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
			if (overed){
				this.overed = overed;
				this.fireEvent('enter', [this.element, overed]);
			} else {
				this.overed = null;
			}
		}
	},

	drag: function(event){
		this.parent(event);
		if (this.droppables.length) this.checkDroppables();
	},

	stop: function(event){
		this.checkDroppables();
		this.fireEvent('drop', [this.element, this.overed]);
		this.overed = null;
		return this.parent(event);
	}

});

Element.implement({

	makeDraggable: function(options){
		return new Drag.Move(this, options);
	}

});


/*
Script: Hash.Cookie.js
	Class for creating, reading, and deleting Cookies in JSON format.

License:
	MIT-style license.
*/

Hash.Cookie = new Class({

	Extends: Cookie,

	options: {
		autoSave: true
	},

	initialize: function(name, options){
		this.parent(name, options);
		this.load();
	},

	save: function(){
		var value = JSON.encode(this.hash);
		if (!value || value.length > 4096) return false; //cookie would be truncated!
		if (value == '{}') this.dispose();
		else this.write(value);
		return true;
	},

	load: function(){
		this.hash = new Hash(JSON.decode(this.read(), true));
		return this;
	}

});

Hash.Cookie.implement((function(){
	
	var methods = {};
	
	Hash.each(Hash.prototype, function(method, name){
		methods[name] = function(){
			var value = method.apply(this.hash, arguments);
			if (this.options.autoSave) this.save();
			return value;
		};
	});
	
	return methods;
	
})());

/*
Script: Color.js
	Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa.

License:
	MIT-style license.
*/

var Color = new Native({
  
	initialize: function(color, type){
		if (arguments.length >= 3){
			type = "rgb"; color = Array.slice(arguments, 0, 3);
		} else if (typeof color == 'string'){
			if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true);
			else if (color.match(/hsb/)) color = color.hsbToRgb();
			else color = color.hexToRgb(true);
		}
		type = type || 'rgb';
		switch (type){
			case 'hsb':
				var old = color;
				color = color.hsbToRgb();
				color.hsb = old;
			break;
			case 'hex': color = color.hexToRgb(true); break;
		}
		color.rgb = color.slice(0, 3);
		color.hsb = color.hsb || color.rgbToHsb();
		color.hex = color.rgbToHex();
		return $extend(color, this);
	}

});

Color.implement({

	mix: function(){
		var colors = Array.slice(arguments);
		var alpha = ($type(colors.getLast()) == 'number') ? colors.pop() : 50;
		var rgb = this.slice();
		colors.each(function(color){
			color = new Color(color);
			for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha));
		});
		return new Color(rgb, 'rgb');
	},

	invert: function(){
		return new Color(this.map(function(value){
			return 255 - value;
		}));
	},

	setHue: function(value){
		return new Color([value, this.hsb[1], this.hsb[2]], 'hsb');
	},

	setSaturation: function(percent){
		return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb');
	},

	setBrightness: function(percent){
		return new Color([this.hsb[0], this.hsb[1], percent], 'hsb');
	}

});

function $RGB(r, g, b){
	return new Color([r, g, b], 'rgb');
};

function $HSB(h, s, b){
	return new Color([h, s, b], 'hsb');
};

function $HEX(hex){
	return new Color(hex, 'hex');
};

Array.implement({

	rgbToHsb: function(){
		var red = this[0], green = this[1], blue = this[2];
		var hue, saturation, brightness;
		var max = Math.max(red, green, blue), min = Math.min(red, green, blue);
		var delta = max - min;
		brightness = max / 255;
		saturation = (max != 0) ? delta / max : 0;
		if (saturation == 0){
			hue = 0;
		} else {
			var rr = (max - red) / delta;
			var gr = (max - green) / delta;
			var br = (max - blue) / delta;
			if (red == max) hue = br - gr;
			else if (green == max) hue = 2 + rr - br;
			else hue = 4 + gr - rr;
			hue /= 6;
			if (hue < 0) hue++;
		}
		return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)];
	},

	hsbToRgb: function(){
		var br = Math.round(this[2] / 100 * 255);
		if (this[1] == 0){
			return [br, br, br];
		} else {
			var hue = this[0] % 360;
			var f = hue % 60;
			var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255);
			var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255);
			var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255);
			switch (Math.floor(hue / 60)){
				case 0: return [br, t, p];
				case 1: return [q, br, p];
				case 2: return [p, br, t];
				case 3: return [p, q, br];
				case 4: return [t, p, br];
				case 5: return [br, p, q];
			}
		}
		return false;
	}

});

String.implement({

	rgbToHsb: function(){
		var rgb = this.match(/\d{1,3}/g);
		return (rgb) ? hsb.rgbToHsb() : null;
	},
	
	hsbToRgb: function(){
		var hsb = this.match(/\d{1,3}/g);
		return (hsb) ? hsb.hsbToRgb() : null;
	}

});


/*
Script: Group.js
	Class for monitoring collections of events

License:
	MIT-style license.
*/

var Group = new Class({

	initialize: function(){
		this.instances = Array.flatten(arguments);
		this.events = {};
		this.checker = {};
	},

	addEvent: function(type, fn){
		this.checker[type] = this.checker[type] || {};
		this.events[type] = this.events[type] || [];
		if (this.events[type].contains(fn)) return false;
		else this.events[type].push(fn);
		this.instances.each(function(instance, i){
			instance.addEvent(type, this.check.bind(this, [type, instance, i]));
		}, this);
		return this;
	},

	check: function(type, instance, i){
		this.checker[type][i] = true;
		var every = this.instances.every(function(current, j){
			return this.checker[type][j] || false;
		}, this);
		if (!every) return;
		this.checker[type] = {};
		this.events[type].each(function(event){
			event.call(this, this.instances, instance);
		}, this);
	}

});


/*
Script: Assets.js
	Provides methods to dynamically load JavaScript, CSS, and Image files into the document.

License:
	MIT-style license.
*/

var Asset = new Hash({

	javascript: function(source, properties){
		properties = $extend({
			onload: $empty,
			document: document,
			check: $lambda(true)
		}, properties);
		
		var script = new Element('script', {'src': source, 'type': 'text/javascript'});

		var load = properties.onload.bind(script), check = properties.check, doc = properties.document;
		delete properties.onload; delete properties.check; delete properties.document;

		script.addEvents({
			load: load,
			readystatechange: function(){
				if (['loaded', 'complete'].contains(this.readyState)) load();
			}
		}).setProperties(properties);
		

		if (Browser.Engine.webkit419) var checker = (function(){
			if (!$try(check)) return;
			$clear(checker);
			load();
		}).periodical(50);

		return script.inject(doc.head);
	},

	css: function(source, properties){
		return new Element('link', $merge({
			'rel': 'stylesheet', 'media': 'screen', 'type': 'text/css', 'href': source
		}, properties)).inject(document.head);
	},

	image: function(source, properties){
		properties = $merge({
			'onload': $empty,
			'onabort': $empty,
			'onerror': $empty
		}, properties);
		var image = new Image();
		var element = $(image) || new Element('img');
		['load', 'abort', 'error'].each(function(name){
			var type = 'on' + name;
			var event = properties[type];
			delete properties[type];
			image[type] = function(){
				if (!image) return;
				if (!element.parentNode){
					element.width = image.width;
					element.height = image.height;
				}
				image = image.onload = image.onabort = image.onerror = null;
				event.delay(1, element, element);
				element.fireEvent(name, element, 1);
			};
		});
		image.src = element.src = source;
		if (image && image.complete) image.onload.delay(1);
		return element.setProperties(properties);
	},

	images: function(sources, options){
		options = $merge({
			onComplete: $empty,
			onProgress: $empty
		}, options);
		if (!sources.push) sources = [sources];
		var images = [];
		var counter = 0;
		sources.each(function(source){
			var img = new Asset.image(source, {
				'onload': function(){
					options.onProgress.call(this, counter, sources.indexOf(source));
					counter++;
					if (counter == sources.length) options.onComplete();
				}
			});
			images.push(img);
		});
		return new Elements(images);
	}

});

/*
Script: Sortables.js
	Class for creating a drag and drop sorting interface for lists of items.

License:
	MIT-style license.
*/

var Sortables = new Class({

	Implements: [Events, Options],

	options: {/*
		onSort: $empty,
		onStart: $empty,
		onComplete: $empty,*/
		snap: 4,
		opacity: 1,
		clone: false,
		revert: false,
		handle: false,
		constrain: false
	},

	initialize: function(lists, options){
		this.setOptions(options);
		this.elements = [];
		this.lists = [];
		this.idle = true;
		
		this.addLists($$($(lists) || lists));
		if (!this.options.clone) this.options.revert = false;
		if (this.options.revert) this.effect = new Fx.Morph(null, $merge({duration: 250, link: 'cancel'}, this.options.revert));
	},

	attach: function(){
		this.addLists(this.lists);
		return this;
	},

	detach: function(){
		this.lists = this.removeLists(this.lists);
		return this;
	},

	addItems: function(){
		Array.flatten(arguments).each(function(element){
			this.elements.push(element);
			var start = element.retrieve('sortables:start', this.start.bindWithEvent(this, element));
			(this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start);
		}, this);
		return this;
	},

	addLists: function(){
		Array.flatten(arguments).each(function(list){
			this.lists.push(list);
			this.addItems(list.getChildren());
		}, this);
		return this;
	},

	removeItems: function(){
		var elements = [];
		Array.flatten(arguments).each(function(element){
			elements.push(element);
			this.elements.erase(element);
			var start = element.retrieve('sortables:start');
			(this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start);
		}, this);
		return $$(elements);
	},

	removeLists: function(){
		var lists = [];
		Array.flatten(arguments).each(function(list){
			lists.push(list);
			this.lists.erase(list);
			this.removeItems(list.getChildren());
		}, this);
		return $$(lists);
	},

	getClone: function(event, element){
		if (!this.options.clone) return new Element('div').inject(document.body);
		if ($type(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list);
		return element.clone(true).setStyles({
			'margin': '0px',
			'position': 'absolute',
			'visibility': 'hidden',
			'width': element.getStyle('width')
		}).inject(this.list).position(element.getPosition(element.getOffsetParent()));
	},

	getDroppables: function(){
		var droppables = this.list.getChildren();
		if (!this.options.constrain) droppables = this.lists.concat(droppables).erase(this.list);
		return droppables.erase(this.clone).erase(this.element);
	},

	insert: function(dragging, element){
		var where = 'inside';
		if (this.lists.contains(element)){
			this.list = element;
			this.drag.droppables = this.getDroppables();
		} else {
			where = this.element.getAllPrevious().contains(element) ? 'before' : 'after';
		}
		this.element.inject(element, where);
		this.fireEvent('sort', [this.element, this.clone]);
	},

	start: function(event, element){
		if (!this.idle) return;
		this.idle = false;
		this.element = element;
		this.opacity = element.get('opacity');
		this.list = element.getParent();
		this.clone = this.getClone(event, element);
		
		this.drag = new Drag.Move(this.clone, {
			snap: this.options.snap,
			container: this.options.constrain && this.element.getParent(),
			droppables: this.getDroppables(),
			onSnap: function(){
				event.stop();
				this.clone.setStyle('visibility', 'visible');
				this.element.set('opacity', this.options.opacity || 0);
				this.fireEvent('start', [this.element, this.clone]);
			}.bind(this),
			onEnter: this.insert.bind(this),
			onCancel: this.reset.bind(this),
			onComplete: this.end.bind(this)
		});
		
		this.clone.inject(this.element, 'before');
		this.drag.start(event);
	},

	end: function(){
		this.drag.detach();
		this.element.set('opacity', this.opacity);
		if (this.effect){
			var dim = this.element.getStyles('width', 'height');
			var pos = this.clone.computePosition(this.element.getPosition(this.clone.offsetParent));
			this.effect.element = this.clone;
			this.effect.start({
				top: pos.top,
				left: pos.left,
				width: dim.width,
				height: dim.height,
				opacity: 0.25
			}).chain(this.reset.bind(this));
		} else {
			this.reset();
		}
	},

	reset: function(){
		this.idle = true;
		this.clone.destroy();
		this.fireEvent('complete', this.element);
	},

	serialize: function(){
		var params = Array.link(arguments, {modifier: Function.type, index: $defined});
		var serial = this.lists.map(function(list){
			return list.getChildren().map(params.modifier || function(element){
				return element.get('id');
			}, this);
		}, this);
		
		var index = params.index;
		if (this.lists.length == 1) index = 0;
		return $chk(index) && index >= 0 && index < this.lists.length ? serial[index] : serial;
	}

});

/*
Script: Tips.js
	Class for creating nice tips that follow the mouse cursor when hovering an element.

License:
	MIT-style license.
*/

var Tips = new Class({

	Implements: [Events, Options],

	options: {
		onShow: function(tip){
			tip.setStyle('visibility', 'visible');
		},
		onHide: function(tip){
			tip.setStyle('visibility', 'hidden');
		},
		showDelay: 100,
		hideDelay: 100,
		className: null,
		offsets: {x: 16, y: 16},
		fixed: false
	},

	initialize: function(){
		var params = Array.link(arguments, {options: Object.type, elements: $defined});
		this.setOptions(params.options || null);
		
		this.tip = new Element('div').inject(document.body);
		
		if (this.options.className) this.tip.addClass(this.options.className);
		
		var top = new Element('div', {'class': 'tip-top'}).inject(this.tip);
		this.container = new Element('div', {'class': 'tip'}).inject(this.tip);
		var bottom = new Element('div', {'class': 'tip-bottom'}).inject(this.tip);

		this.tip.setStyles({position: 'absolute', top: 0, left: 0, visibility: 'hidden'});
		
		if (params.elements) this.attach(params.elements);
	},
	
	attach: function(elements){
		$$(elements).each(function(element){
			var title = element.retrieve('tip:title', element.get('title'));
			var text = element.retrieve('tip:text', element.get('rel') || element.get('href'));
			var enter = element.retrieve('tip:enter', this.elementEnter.bindWithEvent(this, element));
			var leave = element.retrieve('tip:leave', this.elementLeave.bindWithEvent(this, element));
			element.addEvents({mouseenter: enter, mouseleave: leave});
			if (!this.options.fixed){
				var move = element.retrieve('tip:move', this.elementMove.bindWithEvent(this, element));
				element.addEvent('mousemove', move);
			}
			element.store('tip:native', element.get('title'));
			element.erase('title');
		}, this);
		return this;
	},
	
	detach: function(elements){
		$$(elements).each(function(element){
			element.removeEvent('mouseenter', element.retrieve('tip:enter') || $empty);
			element.removeEvent('mouseleave', element.retrieve('tip:leave') || $empty);
			element.removeEvent('mousemove', element.retrieve('tip:move') || $empty);
			element.eliminate('tip:enter').eliminate('tip:leave').eliminate('tip:move');
			var original = element.retrieve('tip:native');
			if (original) element.set('title', original);
		});
		return this;
	},
	
	elementEnter: function(event, element){
		
		$A(this.container.childNodes).each(Element.dispose);
		
		var title = element.retrieve('tip:title');
		
		if (title){
			this.titleElement = new Element('div', {'class': 'tip-title'}).inject(this.container);
			this.fill(this.titleElement, title);
		}
		
		var text = element.retrieve('tip:text');
		if (text){
			this.textElement = new Element('div', {'class': 'tip-text'}).inject(this.container);
			this.fill(this.textElement, text);
		}
		
		this.timer = $clear(this.timer);
		this.timer = this.show.delay(this.options.showDelay, this);

		this.position((!this.options.fixed) ? event : {page: element.getPosition()});
	},
	
	elementLeave: function(event){
		$clear(this.timer);
		this.timer = this.hide.delay(this.options.hideDelay, this);
	},
	
	elementMove: function(event){
		this.position(event);
	},
	
	position: function(event){
		var size = window.getSize(), scroll = window.getScroll();
		var tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight};
		var props = {x: 'left', y: 'top'};
		for (var z in props){
			var pos = event.page[z] + this.options.offsets[z];
			if ((pos + tip[z] - scroll[z]) > size[z]) pos = event.page[z] - this.options.offsets[z] - tip[z];
			this.tip.setStyle(props[z], pos);
		}
	},
	
	fill: function(element, contents){
		(typeof contents == 'string') ? element.set('html', contents) : element.adopt(contents);
	},

	show: function(){
		this.fireEvent('show', this.tip);
	},

	hide: function(){
		this.fireEvent('hide', this.tip);
	}

});

/*
Script: SmoothScroll.js
	Class for creating a smooth scrolling effect to all internal links on the page.

License:
	MIT-style license.
*/

var SmoothScroll = new Class({

	Extends: Fx.Scroll,

	initialize: function(options, context){
		context = context || document;
		var doc = context.getDocument(), win = context.getWindow();
		this.parent(doc, options);
		this.links = (this.options.links) ? $$(this.options.links) : $$(doc.links);
		var location = win.location.href.match(/^[^#]*/)[0] + '#';
		this.links.each(function(link){
			if (link.href.indexOf(location) != 0) return;
			var anchor = link.href.substr(location.length);
			if (anchor && $(anchor)) this.useLink(link, anchor);
		}, this);
		if (!Browser.Engine.webkit419) this.addEvent('complete', function(){
			win.location.hash = this.anchor;
		}, true);
	},

	useLink: function(link, anchor){
		link.addEvent('click', function(event){
			this.anchor = anchor;
			this.toElement(anchor);
			event.stop();
		}.bind(this));
	}

});

/*
Script: Slider.js
	Class for creating horizontal and vertical slider controls.

License:
	MIT-style license.
*/

var Slider = new Class({

	Implements: [Events, Options],

	options: {/*
		onChange: $empty,
		onComplete: $empty,*/
		onTick: function(position){
			if(this.options.snap) position = this.toPosition(this.step);
			this.knob.setStyle(this.property, position);
		},
		snap: false,
		offset: 0,
		range: false,
		wheel: false,
		steps: 100,
		mode: 'horizontal'
	},

	initialize: function(element, knob, options){
		this.setOptions(options);
		this.element = $(element);
		this.knob = $(knob);
		this.previousChange = this.previousEnd = this.step = -1;
		this.element.addEvent('mousedown', this.clickedElement.bind(this));
		if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement.bindWithEvent(this));
		var offset, limit = {}, modifiers = {'x': false, 'y': false};
		switch (this.options.mode){
			case 'vertical':
				this.axis = 'y';
				this.property = 'top';
				offset = 'offsetHeight';
				break;
			case 'horizontal':
				this.axis = 'x';
				this.property = 'left';
				offset = 'offsetWidth';
		}
		this.half = this.knob[offset] / 2;
		this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
		this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
		this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
		this.range = this.max - this.min;
		this.steps = this.options.steps || this.full;
		this.stepSize = Math.abs(this.range) / this.steps;
		this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
		
		this.knob.setStyle('position', 'relative').setStyle(this.property, - this.options.offset);
		modifiers[this.axis] = this.property;
		limit[this.axis] = [- this.options.offset, this.full - this.options.offset];
		this.drag = new Drag(this.knob, {
			snap: 0,
			limit: limit,
			modifiers: modifiers,
			onDrag: this.draggedKnob.bind(this),
			onStart: this.draggedKnob.bind(this),
			onComplete: function(){
				this.draggedKnob();
				this.end();
			}.bind(this)
		});
		if (this.options.snap) {
			this.drag.options.grid = Math.ceil(this.stepWidth);
			this.drag.options.limit[this.axis][1] = this.full;
		}
	},

	set: function(step){
		if (!((this.range > 0) ^ (step < this.min))) step = this.min;
		if (!((this.range > 0) ^ (step > this.max))) step = this.max;
		
		this.step = Math.round(step);
		this.checkStep();
		this.end();
		this.fireEvent('tick', this.toPosition(this.step));
		return this;
	},

	clickedElement: function(event){
		var dir = this.range < 0 ? -1 : 1;
		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
		position = position.limit(-this.options.offset, this.full -this.options.offset);
		
		this.step = Math.round(this.min + dir * this.toStep(position));
		this.checkStep();
		this.end();
		this.fireEvent('tick', position);
	},
	
	scrolledElement: function(event){
		var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
		this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
		event.stop();
	},

	draggedKnob: function(){
		var dir = this.range < 0 ? -1 : 1;
		var position = this.drag.value.now[this.axis];
		position = position.limit(-this.options.offset, this.full -this.options.offset);
		this.step = Math.round(this.min + dir * this.toStep(position));
		this.checkStep();
	},

	checkStep: function(){
		if (this.previousChange != this.step){
			this.previousChange = this.step;
			this.fireEvent('change', this.step);
		}
	},

	end: function(){
		if (this.previousEnd !== this.step){
			this.previousEnd = this.step;
			this.fireEvent('complete', this.step + '');
		}
	},

	toStep: function(position){
		var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
		return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
	},

	toPosition: function(step){
		return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
	}

});

/*
Script: Scroller.js
	Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries.

License:
	MIT-style license.
*/

var Scroller = new Class({

	Implements: [Events, Options],

	options: {
		area: 20,
		velocity: 1,
		onChange: function(x, y){
			this.element.scrollTo(x, y);
		}
	},

	initialize: function(element, options){
		this.setOptions(options);
		this.element = $(element);
		this.listener = ($type(this.element) != 'element') ? $(this.element.getDocument().body) : this.element;
		this.timer = null;
		this.coord = this.getCoords.bind(this);
	},

	start: function(){
		this.listener.addEvent('mousemove', this.coord);
	},

	stop: function(){
		this.listener.removeEvent('mousemove', this.coord);
		this.timer = $clear(this.timer);
	},

	getCoords: function(event){
		this.page = (this.listener.get('tag') == 'body') ? event.client : event.page;
		if (!this.timer) this.timer = this.scroll.periodical(50, this);
	},

	scroll: function(){
		var size = this.element.getSize(), scroll = this.element.getScroll(), pos = this.element.getPosition(), change = {'x': 0, 'y': 0};
		for (var z in this.page){
			if (this.page[z] < (this.options.area + pos[z]) && scroll[z] != 0)
				change[z] = (this.page[z] - this.options.area - pos[z]) * this.options.velocity;
			else if (this.page[z] + this.options.area > (size[z] + pos[z]) && size[z] + size[z] != scroll[z])
				change[z] = (this.page[z] - size[z] + this.options.area - pos[z]) * this.options.velocity;
		}
		if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]);
	}

});

/*
Script: Accordion.js
	An Fx.Elements extension which allows you to easily create accordion type controls.

License:
	MIT-style license.
*/

var Accordion = new Class({

	Extends: Fx.Elements,

	options: {/*
		onActive: $empty,
		onBackground: $empty,*/
		display: 0,
		show: false,
		height: true,
		width: false,
		opacity: true,
		fixedHeight: false,
		fixedWidth: false,
		wait: false,
		alwaysHide: false
	},

	initialize: function(){
		var params = Array.link(arguments, {'container': Element.type, 'options': Object.type, 'togglers': $defined, 'elements': $defined});
		this.parent(params.elements, params.options);
		this.togglers = $$(params.togglers);
		this.container = $(params.container);
		this.previous = -1;
		if (this.options.alwaysHide) this.options.wait = true;
		if ($chk(this.options.show)){
			this.options.display = false;
			this.previous = this.options.show;
		}
		if (this.options.start){
			this.options.display = false;
			this.options.show = false;
		}
		this.effects = {};
		if (this.options.opacity) this.effects.opacity = 'fullOpacity';
		if (this.options.width) this.effects.width = this.options.fixedWidth ? 'fullWidth' : 'offsetWidth';
		if (this.options.height) this.effects.height = this.options.fixedHeight ? 'fullHeight' : 'scrollHeight';
		for (var i = 0, l = this.togglers.length; i < l; i++) this.addSection(this.togglers[i], this.elements[i]);
		this.elements.each(function(el, i){
			if (this.options.show === i){
				this.fireEvent('active', [this.togglers[i], el]);
			} else {
				for (var fx in this.effects) el.setStyle(fx, 0);
			}
		}, this);
		if ($chk(this.options.display)) this.display(this.options.display);
	},

	addSection: function(toggler, element, pos){
		toggler = $(toggler);
		element = $(element);
		var test = this.togglers.contains(toggler);
		var len = this.togglers.length;
		this.togglers.include(toggler);
		this.elements.include(element);
		if (len && (!test || pos)){
			pos = $pick(pos, len - 1);
			toggler.inject(this.togglers[pos], 'before');
			element.inject(toggler, 'after');
		} else if (this.container && !test){
			toggler.inject(this.container);
			element.inject(this.container);
		}
		var idx = this.togglers.indexOf(toggler);
		toggler.addEvent('click', this.display.bind(this, idx));
		if (this.options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
		if (this.options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
		element.fullOpacity = 1;
		if (this.options.fixedWidth) element.fullWidth = this.options.fixedWidth;
		if (this.options.fixedHeight) element.fullHeight = this.options.fixedHeight;
		element.setStyle('overflow', 'hidden');
		if (!test){
			for (var fx in this.effects) element.setStyle(fx, 0);
		}
		return this;
	},

	display: function(index){
		index = ($type(index) == 'element') ? this.elements.indexOf(index) : index;
		if ((this.timer && this.options.wait) || (index === this.previous && !this.options.alwaysHide)) return this;
		this.previous = index;
		var obj = {};
		this.elements.each(function(el, i){
			obj[i] = {};
			var hide = (i != index) || (this.options.alwaysHide && (el.offsetHeight > 0));
			this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
			for (var fx in this.effects) obj[i][fx] = hide ? 0 : el[this.effects[fx]];
		}, this);
		return this.start(obj);
	}

});
Function.implement({
	safeBind: function(bind, args){
		return this.create({bind: bind, arguments: args});
	}

});

Event.Keys.shift = 16;
Event.Keys.ctrl = 17;
Event.Keys.alt = 18;
Event.Keys.capslock = 20;
Event.Keys.pageup = 33;
Event.Keys.pagedown = 34;
Element.Events.set('rightclick', {
	base: 'click',
	condition: function(event) {
		return event.rightClick;
	}
});

String.implement({
	trunc: function(maxLength) {
		maxLength = maxLength || 50;
		maxLength = (maxLength >= 10 ? maxLength : 10);
		return (this.length > maxLength) ? this.substring(0, maxLength - 3) + '...' : this.toString();
	},
	escapeRegExp: function() {
		return this.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
	},
	escape: function() {
		return this.replace(/('|\\)/g, '\\$1');
	},
	replaceAll: function(find, replace) {
		return this.replace(new RegExp(find, 'g'), replace);
	},
	format: function() {
		var self = this;
		if ($type(arguments[0]) == 'object') {
			var obj = arguments[0];
			for (var p in obj) {
				self = self.replace(new RegExp('\\{' + (p) + '\\}', 'g'), obj[p]);
			}
		} else {
			for (var i = 0; i < arguments.length; i++) {
				self = self.replace(new RegExp('\\{' + (i) + '\\}', 'g'), arguments[i]);
			}
		}
		return self;
	},
	startsWith: function(str) {
		return ($defined(str) && this.substring(0, str.length) == str);
	},
	endsWith: function(str) {
		return ($defined(str) && this.substring(this.length - str.length) == str);
	},
	repeat: function(times) {
		return new Array(times + 1).join(this);
	},
	substringTo: function(pattern, emptyNoMatch) {
		var index = this.indexOf(pattern);
		return (index == -1 ? (!emptyNoMatch ? '' : '' + this) : this.substring(0, index));
	},
	substringFrom: function(pattern, emptyNoMatch) {
		var index = this.indexOf(pattern);
		return (index == -1 ? (!emptyNoMatch ? '' : '' + this) : this.substring(index + pattern.length));
	}
});

Element.implement({
	hasEvent: function(type) {
		var events = this.retrieve('events');
		return (events && $defined(events[type]) && events[type].keys.length > 0);
	},
	trimValue: function() {
		var val = this.get('value');
		if (val) this.set('value', val.trim());
		return this;
	},
	setDisplay: function(display) {
		return this.setStyle('display', display);
	},
	show: function() {
		return this.setStyle('display', 'block');
	},
	hide: function() {
		return this.setStyle('display', 'none');
	},
	emptyClass: function() {
		return this.set('class', '');
	},
	addClasses: function() {
		Array.flatten(arguments).each(this.addClass, this);
		return this;
	},
	hasClasses: function() {
		var hasCls = true;
		Array.flatten(arguments).each(function(cls) {
			hasCls = hasCls && this.hasClass(cls);
		}, this);
		return hasCls;
	},
	removeClasses: function() {
		for (var i = 0; i < arguments.length; i++) this.removeClass(arguments[i]);
		return this;
	},
	replaceClass: function(what, by) {
		return this.removeClass(what).addClass(by);
	},
	addClassOn: function(eventType, cls) {
		switch (eventType) {
			case 'enter': case 'mouseenter':
				this.addEvents({
					mouseenter: function() {
						this.addClass(cls);
					},
					mouseleave: function() {
						this.removeClass(cls);
					}
				});
				break;
			case 'over': case 'mouseover':
				this.addEvents({
					mouseover: function() {
						this.addClass(cls);
					},
					mouseout: function() {
						this.removeClass(cls);
					}
				});
				break;
			case 'focus':
				this.addEvents({
					focus: function() {
						this.addClass(cls);
					},
					blur: function() {
						this.removeClass(cls);
					}
				});
				break;
			default:
				break;
		}
		return this;
	},
	descendantOf: function(el) {
		if (this.compareDocumentPosition) return ((this.compareDocumentPosition(el) & 8) === 8);
		else if (el.contains) return (el.contains(this) && this != el);
		else {
			var parent = this;
			while (parent = parent.elNode) if (parent == el) return true;
		}
		return false;
	},
	ascendantOf: function(el) {
		return el.descendantOf(this);
	},
	isInBody: function() {
		return this.desendantOf(document.body);
	},
	deportChilds: function(el) {
		el = $(el);
		$A(this.childNodes).each(function(child) {
			el.appendChild(child);
		});
		return this;
	},
	grabChilds: function(el) {
		$(el).deportChilds(this);
		return this;
	},
	getXBorders: function() {
		return this.getStyle('border-left-width').toInt() + this.getStyle('border-right-width').toInt();
	},
	getYBorders: function() {
		return this.getStyle('border-top-width').toInt() + this.getStyle('border-bottom-width').toInt();
	},
	getSizeWithoutBorders: function() {
		var size = this.getSize();
		size.x = size.x - this.getXBorders();
		size.y = size.y - this.getYBorders();
		return size;
	},
	getCoordinatesWithoutBorders: function() {
		var coord = this.getCoordinates();
		coord.width = coord.width - this.getXBorders();
		coord.height = coord.height - this.getYBorders();
		return coord;
	},
	setSize: function(param, morphProps) {
		var size = null;
		switch ($type(param)) {
			case 'string': case 'element':
				var el = $(param);
				if (el) size = el.getSizeWithoutBorders();
				break;
			case 'object':
				size = param;
			default: break;
		}
		if (size != null) {
			var styles = {};
			if ($defined(size.x)) styles.width = size.x;
			if ($defined(size.y)) styles.height = size.y;
			morphProps = morphProps || {duration: 0};
			this.set('morph', morphProps);
			this.morph(styles);
		}
		return this;
	},
	setPosition: function(param, morphProps) {
		var pos = null;
		switch ($type(param)) {
			case 'string': case 'element':
				var el = $(param);
				if (el) pos = el.getPosition();
				break;
			case 'object':
				pos = param;
			default: break;
		}
		if (pos != null) {
			var styles = {position: 'absolute'};
			if ($defined(pos.x)) styles.left = pos.x;
			if ($defined(pos.y)) styles.top = pos.y;
			morphProps = morphProps || {duration: 0};
			this.set('morph', morphProps);
			this.morph(styles);
		}
		return this;
	},
	setCoordinates: function(param, morphProps) {
		var coord = null;
		switch ($type(param)) {
			case 'string': case 'element':
				var el = $(param);
				if (el) {
					coord = el.getCoordinatesWithoutBorders();
					delete coord.right;
					delete coord.bottom;
				}
				break;
			case 'object':
				coord = param;
			default: break;
		}
		if (coord != null) {
			var styles = {position: 'absolute'};
			if ($defined(coord.left)) styles.left = coord.left;
			if ($defined(coord.top)) styles.top = coord.top;
			if ($defined(coord.width)) styles.width = coord.width;
			if ($defined(coord.height)) styles.height = coord.height;
			morphProps = morphProps || {duration: 0};
			this.set('morph', morphProps);
			this.morph(styles);
		}
		return this;
	}
});

Date.implement({
	getWeek: function() {
		var ms1d = 864e5;
		var ms7d = 7 * ms1d;
		var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d;
		var AWN = Math.floor(DC3 / 7);
		var Wyr = new Date(AWN * ms7d).getUTCFullYear();
		return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
	},
	toJson: function() {
		return {
			year: this.getFullYear(),
			month: this.getMonth() + 1,
			day: this.getDate(),
			hours: this.getHours(),
			minutes: this.getMinutes(),
			seconds: this.getSeconds(),
			week: this.getWeek()
		};
	},
	getFirstDayOfWeek: function() {
		var currentDay = this.getDay(), firstDay = this.getDate();
		if (currentDay == 0) firstDay -= 6;
		else if (currentDay != 1) firstDay = firstDay - currentDay + 1;
		return new Date(this.getFullYear(), this.getMonth(), firstDay);
	},
	getLastDayOfWeek: function() {
		var currentDay = this.getDay(), lastDay = this.getDate();
		if (currentDay != 0) lastDay = lastDay + (7 - currentDay);
		return new Date(this.getFullYear(), this.getMonth(), lastDay);
	},
	getElapsed: function(date) {
		return ((date || new Date()).getTime() - this.getTime());
	}
});

Array.implement({
	trunc: function(maxLength) {
		maxLength = maxLength || 3;
		var result = '';
		if (this.length <= maxLength) {
			return this.join(', ');
		} else {
			return this.filter(function(item, index) {
				return index < maxLength;
			}).join(', ') + ', ...';
		}
	},
	insert: function(what, index) {
		if (index == -1 || index == this.length - 1) {
			return this.push(what);
		} else if (index == 0) {
			return this.unshift(what);
		} else if (0 < index && index < this.length) {
			var rest = this.slice(index + 1);
			this.length = index + 1;
			rest.unshift(what);
			return this.push.apply(this, rest);
		}
		return this.length;
	},
	remove: function(from, to) {
		var rest = this.slice((to || from) + 1 || this.length);
		this.length = from < 0 ? this.length + from : from;
		return this.push.apply(this, rest);
	},
	capture: function(fn, bind) {
		var result = this.filter(fn, bind);
		for (var i = 0, l = result.length; i < l; i++) {
			this.erase(result[i]);
		}
		return result;
	}
});

(function() {
	var ua = navigator.userAgent.toLowerCase();
	Browser.name = null;
	Browser.version = null;
	Browser.isFirefox = /firefox[\/\s](\d+\.\d+)/.test(ua) || false;
	if (Browser.isFirefox) {
		Browser.version = parseFloat(RegExp.$1);
		Browser.name = 'firefox';
	}
	Browser.isChrome = /chrome[\/\s](\d+\.\d+)/.test(ua) || false;
	if (Browser.isChrome) {
		Browser.version = parseFloat(RegExp.$1);
		Browser.name = 'chrome';
	}
	Browser.isSafari = (/safari[\/\s](\d+\.\d+)/.test(ua) && !Browser.isChrome) || false;
	if (Browser.isSafari) {
		var version = /version[\/\s](\d+\.\d+)/.test(ua);
		Browser.version = parseFloat(RegExp.$1);
		Browser.name = 'safari';
	}
	Browser.isIE = /msie (\d+\.\d+)/.test(ua) || false;
	if (Browser.isIE) {
		Browser.version = (ua.contains('trident') ? 8 : parseFloat(RegExp.$1));
		Browser.name = 'ie';
	}
	Browser.isOpera = /opera[\/\s](\d+\.\d+)/.test(ua) || false;
	if (Browser.isOpera) {
		Browser.version = parseFloat(RegExp.$1);
		if (/version[\/\s](\d+\.\d+)/.test(ua)) Browser.version = parseFloat(RegExp.$1);
		Browser.name = 'opera';
	}
})();
Browser.Features.strict = (document.compatMode == 'CSS1Compat');
Browser.Features.secure = (window.location.href.toLowerCase().startsWith('https'));
Browser.Features.console = $defined(window.console);
Browser.Plugins.firebug = (Browser.Features.console && $defined(window.console.firebug));

Hash.implement({
	toArray: function() {
		var array = [];
		this.each(function(value, key) {
			array.push(value);
		});
		return array;
	}
});

Number.implement({
	isBetween: function(n1, n2) {
		return (this >= n1 && this <= n2);
	},
	isBetweenStrict: function(n1, n2) {
		return (this > n1 && this < n2);
	}
});

Function.implement({
	intercept: function(fn, bind) {
		var self = this;
		return function() {
			if (fn.apply(bind || this, arguments) === false) return false;
			return self.apply(this, arguments);
		};
	},
	callBefore: function(fn, bind) {
		var self = this;
		return function() {
			fn.apply(bind || this, arguments);
			return self.apply(this, arguments);
		};
	},
	callAfter: function(fn, bind) {
		var self = this;
		return function() {
			var result = self.apply(this, arguments);
			fn.apply(bind || this, arguments);
			return result;
		};
	},
	addArguments: function() {
		var self = this, args = $A(arguments).flatten();
		return function() {
			return self.run($A(arguments).extend(args));
		};
	}
});

Boolean.prototype.toValue = Boolean.prototype.toValue || function(trueValue, falseValue) {
	return (this == true ? trueValue : falseValue);
};

Events.implement({
	hasEvent: function(type) {
		type = Events.removeOn(type);
		return ($defined(this.$events[type]) && this.$events[type].length > 0);
	}
});

Options.implement({
	initializeOptions: function(options) {
		if (options) this.setOptions(options);
	}
});

var Moo = {
	version: '1.0',
	build: 'r0.0.22',
	mootoolsCore: '1.2.1',
	mootoolsMore: '1.2',
	pixelImgSrc: 'moo/resources/images/pixel.gif',
	emptyPageSrc: '',
	stopEvent: function(event) {
		event.stop();
	},
	onReady: function(fn, bind) {
		window.addEvent('domready', (bind ? fn.safeBind(bind) : fn));
		return this;
	},
	namespace: function() {
		$each(arguments, function(a) {
			var ps = a.split('.'), root = window[ps[0]];
			if (!root) root = window[ps[0]] = {};
			for (var i = 1, l = ps.length; i < l; i++) {
				if (!root[ps[i]]) root[ps[i]] = {};
				root = root[ps[i]];
			}
		});
		return this;
	},
	toQueryString: function() {
		var result = [], query = null;
		for (var i = 0, l = arguments.length; i < l; i++) {
			query = '';
			switch ($type(arguments[i])) {
				case 'string':
					query = arguments[i];
					break;
				case 'element':
					query = arguments[i].toQueryString();
					break;
				case 'object':
					query = Hash.toQueryString(arguments[i]);
					break;
				case 'array':
					var innerQuery = '', aQuery = [];
					for (var p = 0, l2 = arguments[i].length; p < l2; p++) {
						innerQuery = this.toQueryString(arguments[i][p]);
						if (innerQuery != '') aQuery.push(innerQuery);
					}
					if (aQuery.length != 0) result.push(aQuery.join('&'));
				default:
					break;
			}
			if (query != '') result.push(query);
		}
		return result.join('&');
	},
	encodeUrl: function(url, toQueryString) {
		toQueryString = this.toQueryString(toQueryString);
		return url + (toQueryString == '' ? '' : '?' + toQueryString);
	},
	decodeParams: function(params, decode) {
		var result = null;
		if (typeof params == 'string') {
			result = {};
			var pairs = params.split('&'), pair = null;
			for (var i = 0, l = pairs.length; i < l; i++) {
				pair = pairs[i].split('=');
				result[pair[0]] = (this.toBoolean(decode, true) ? decodeURIComponent(pair[1]) : pair[1]);
			}
		}
		return result;
	},
	decodeUrl: function(url) {
		url = url || window.location.href;
		var result = {}, anchor = null, params = null, anchorIndex = url.indexOf('#'), sepIndex = url.indexOf('?');
		if (anchorIndex != -1) {
			anchor = url.substring(anchorIndex + 1);
			url = url.substring(0, anchorIndex);
		}
		if (sepIndex != -1) {
			params = this.decodeParams(url.substring(sepIndex + 1));
			url = url.substring(0, sepIndex);
		}
		result.url = url;
		result.params = params;
		result.anchor = anchor;
		return result;
	},
	createLoader: function(content, options) {
		var container = new Element('span', {
			html: '<img src="' + this.pixelImgSrc + '" class="moo-icon-loader" style="float: left; margin-right: 5px;" />'
		});
		var contentContainer = new Element('div').inject(container);
		Moo.update(content, contentContainer);
		return container;
	},
	toLoader: function(el, content, options) {
		el = $(el);
		var loader = this.createLoader(content, options);
		if (el) this.update(loader, el);
		return loader;
	},
	injectHTML: function(html, el, where) {
		el = $(el);
		where = where || 'bottom';
		if ($(el).insertAdjacentHTML) {
			switch(where) {
				case 'bottom': case 'beforeend':
					el.insertAdjacentHTML('BeforeEnd', html);
					return el.lastChild;
				case 'top': case 'afterbegin':
					el.insertAdjacentHTML('AfterBegin', html);
					return el.firstChild;
				case 'before': case 'beforebegin':
					el.insertAdjacentHTML('BeforeBegin', html);
					return el.previousSibling;
				case 'after': case 'afterend':
					el.insertAdjacentHTML('AfterEnd', html);
					return el.nextSibling;
			}
		}
		var range = el.ownerDocument.createRange(), fragment = null;
		switch(where) {
			case 'bottom': case 'beforeend':
				if(el.lastChild) {
					range.setStartAfter(el.lastChild);
					fragment = range.createContextualFragment(html);
					el.appendChild(fragment);
					return el.lastChild;
				} else {
					el.innerHTML = html;
					return el.lastChild;
				}
			case 'top': case 'afterbegin':
				if (el.firstChild) {
					range.setStartBefore(el.firstChild);
					fragment = range.createContextualFragment(html);
					el.insertBefore(fragment, el.firstChild);
					return el.firstChild;
				} else {
					el.innerHTML = html;
					return el.firstChild;
				}
			case 'before': case 'beforebegin':
				range.setStartBefore(el);
				fragment = range.createContextualFragment(html);
				el.parentNode.insertBefore(fragment, el);
				return el.previousSibling;
			case 'after': case 'afterend':
				range.setStartAfter(el);
				fragment = range.createContextualFragment(html);
				el.parentNode.insertBefore(fragment, el.nextSibling);
				return el.nextSibling;
		}
	},
	getHTML: function(el, destroy) {
		el = $(el);
		var result = el.get('html');
		if (Moo.toBoolean(destroy, true)) el.destroy();
		return result;
	},
	log: (Browser.Features.console ? function() {
		console.log.apply(console, arguments);
	} : $empty),
	$timers: new Hash(),
	startTimer: function(key) {
		var start = $time();
		if (key) this.$timers.set(key, start);
		return start;
	},
	endTimer: function(key) {
		var end = $time();
		if (key) {
			var start = this.$timers.get(key);
			if (start) end = end - start;
		}
		return end;
	},
	toBoolean: function(bool, defaultValue) {
		return (typeof bool == 'boolean' ? bool : defaultValue);
	},
	toNumber: function(num, defaultValue) {
		return (isNaN(num) ? defaultValue : parseFloat(num));
	},
	toNumberMin: function(num, min, defaultValue) {
		num = this.toNumber(num, defaultValue);
		return (min <= num ? num : defaultValue);
	},
	toNumberMax: function(num, max, defaultValue) {
		num = this.toNumber(num, defaultValue);
		return (num <= max ? num : defaultValue);
	},
	toNumberMinMax: function(num, min, max, defaultValue) {
		num = this.toNumber(num, defaultValue);
		return (min <= num && num <= max ? num : defaultValue);
	},
	copyObject: function(obj) {
		return $merge({}, obj);
	},
	isEmptyObject: function(obj) {
		var empty = true;
		for (var p in obj) {
			empty = false;
			break;
		}
		return empty;
	},
	getAnchorCoordinates: function(what, el, options) {
		what = $(what);
		el = $(el);
		options = $merge({
			mode: 'tl-bl',
			offsets: {
				x: 0,
				y: 0
			}
		}, options);
		var whatCoord = what.getCoordinates(),
			elCoord = el.getCoordinates(),
			splitted = options.mode.split('-'),
			whatMode = splitted[0],
			elMode = splitted[1],
			top = elCoord.top,
			left = elCoord.left;
		if (whatMode.substring(0, 1) == 'b') top = top - whatCoord.height;
		if (whatMode.substring(1) == 'r') left = left - whatCoord.width;
		if (elMode.substring(0, 1) == 'b') top = top + elCoord.height;
		if (elMode.substring(1) == 'r') left = left + elCoord.width;
		top += options.offsets.y;
		left += options.offsets.x;
		return {
			top: top,
			left: left,
			height: whatCoord.height,
			width: whatCoord.width,
			bottom: top + whatCoord.height,
			right: left + whatCoord.width
		};
	},
	applyAnchor: function(what, el, coords) {
		what = $(what);
		el = $(el);
		what.setStyles({
			top: coords.top,
			left: coords.left
		});
		return this;
	},
	prepareAnchor: function(what) {
		what.store('moo:opacity', what.getStyle('opacity'));
		what.setStyles({
			top: 0,
			left: 0,
			opacity: 0.01
		});
		what.show();
	},
	finishAnchor: function(what) {
		what.setOpacity(what.retrieve('moo:opacity'));
	},
	anchor: function(what, el, options) {
		what = $(what);
		el = $(el);
		this.prepareAnchor(what);
		var coords = this.getAnchorCoordinates(what, el, options);
		what.setStyles({
			top: coords.top,
			left: coords.left
		});
		this.finishAnchor(what);
		return this;
	},
	autoAnchor: function(what, el, options) {
		what = $(what);
		el = $(el);
		options = $merge({
			modes: [{
				mode: 'tl-bl',
				offsets: {
					x: 0,
					y: 0
				}
			}, {
				mode: 'tr-br',
				offsets: {
					x: 0,
					y: 0
				}
			}, {
				mode: 'bl-tl',
				offsets: {
					x: 0,
					y: 0
				}
			}, {
				mode: 'br-tr',
				offsets: {
					x: 0,
					y: 0
				}
			}]
		}, options);
		this.prepareAnchor(what);
		var viewSize = this.getViewSize(),
			coords = null,
			anchored = false;
		for (var m in options.modes) {
			coords = this.getAnchorCoordinates(what, el, options.modes[m]);
			if (coords.top >= 0 && coords.right <= viewSize.width && coords.bottom <= viewSize.height) {
				this.applyAnchor(what, el, coords);
				anchored = true;
				break;
			}
		}
		if (!anchored) {
			this.applyAnchor(what, el, this.getAnchorCoordinates(what, el, options.modes[0]));
		}
		this.finishAnchor(what);
		return this;
	},
	concat: function(arr, sep) {
		return Array.flatten(arr).join(sep || '');
	},
	implement: function(src, dest, scope) {
		switch($type(src)) {
			case 'class': case 'native':
				src.implement(dest);
				break;
			case 'object':
				if (!$defined(scope) || scope == true) scope = src;
				for (var p in dest) if (!src[p]) {
					var d = dest[p];
					if (typeof d == 'function') src[p] = (scope ? d.safeBind(scope) : d);
					else src[p] = d;
				}
				break;
			default:
				break;
		}
		return this;
	},
	override: function(src, dest, scope) {
		switch($type(src)) {
			case 'class':
				src = src.prototype;
			case 'object':
				if (!$defined(scope) || scope == true) scope = src;
				for (var p in dest) {
					var d = dest[p];
					if (typeof d == 'function') src[p] = (scope ? d.safeBind(scope) : d);
					else src[p] = d;
					src[p] = dest[p];
				}
				break;
			default:
				break;
		}
		return this;
	},
	$storage: new Hash(),
	store: function(key, value) {
		this.$storage.set(key, value);
		return this;
	},
	retrieve: function(key, dflt) {
		var result = this.$storage.get(key);
		if (result == null) {
			result = dflt;
			this.$storage.set(key, result);
		}
		return result;
	},
	eliminate: function(key) {
		this.$storage.erase(key);
		return this;
	},
	capture: function(what, obj) {
		var result = obj[what];
		delete obj[what];
		return result;
	},
	deport: function(props, src, dest) {
		Array.flatten([props]).each(function(prop) {
			dest[prop] = src[prop];
			delete src[prop];
		});
		return this;
	},
	alias: function(src, name, alias) {
		var origin = src[name];
		if (typeof origin == 'function') src[alias] = origin.safeBind(src);
		else src[alias] = origin;
		return this;
	},
	open: function(url, options) {
		options = $merge({
			name: '',
			width: null,
			height: null,
			directories: true,
			location: true,
			menubar: true,
			resizable: true,
			scrollbars: true,
			status: true,
			toolbar: true
		}, options);
		var name = this.capture('name', options), opts = [], booleanToWord = function(b) {
			return (b ? 'yes' : 'no');
		};
		if (options.width) opts.push('width=' + this.capture('width', options));
		if (options.height) opts.push('height=' + this.capture('height', options));
		for (var p in options) {
			opts.push(p + '=' + (options[p] ? 'yes' : 'no'));
		}
		return window.open(url, name, opts.join(','));
	},
	implementEvent: function(name, doc) {
		doc = doc || document;
		var cName = name.capitalize() ,impl = {};
		impl['add' + cName] = function(fn) {
			doc.addEvent(name, fn);
			return this;
		}.safeBind(this);
		impl['remove' + cName] = function(fn) {
			doc.removeEvent(name, fn);
			return this;
		}.safeBind(this);
		impl['clear' + cName] = function() {
			doc.removeEvents(name);
			return this;
		}.safeBind(this);
		this.implement(Moo, impl);
		return this;
	},
	doDownload: function(url, toQuerystring) {
		if (!this.$downloadIFrame) {
			this.$downloadIFrame = new Element('iframe', {
				src: this.emptyPageSrc,
				styles: {
					display: 'none',
					position: 'absolute',
					top: 0,
					left: 0
				}
			}).inject(document.body);
		}
		this.$downloadIFrame.set('src', this.encodeUrl(url, toQuerystring));
	},
	getRamdomColor: function(options) {
		options = $merge({
			rMin: 0, rMax: 255,
			gMin: 0, gMax: 255,
			bMin: 0, bMax: 255
		}, options);
		this.log(options.alpha);
		return [$random(options.rMin, options.rMax), $random(options.gMin, options.gMax), $random(options.bMin, options.bMax)].rgbToHex();
    },
	setTitle: function(title) {
		document.title = title;
	},
	getResolution: function() {
		return {
			width: screen.width,
			height: screen.height
		};
	},
	getViewSize: function() {
		var scrollSize = $(document.body).getScrollSize();
		return {
			width: scrollSize.x,
			height: scrollSize.y
		};
	},
	getViewCoordinates: function() {
		var size = $(document.body).getSize(), scroll = $(document.body).getScroll();
		return {
			left: scroll.x,
			width: size.x,
			right: scroll.x + size.x,
			top: scroll.y,
			height: size.y,
			bottom: scroll.y + size.y
		};
	}
};
['mousedown', 'mouseup', 'keydown', 'keyup', 'click', 'dblclick'].each(function(name) {
	Moo.implementEvent(name);
});
['resize', 'scroll'].each(function(name) {
	Moo.implementEvent(name, window);
});

Moo.Features = {
	fxDuration: 'normal',
	useTooltip: true
};

Moo.Sequence = new Class({
	Implements: Options,
	options: {
		start: 0,
		step: 1,
		prefix: ''
	},
	initialize: function(options) {
		this.setOptions(options);
		this.$value = this.options.start;
		this.$step = this.options.step;
		this.$prefix = this.options.prefix;
	},
	next: function() {
		this.$value = this.$value + this.$step;
		return this.$prefix + this.$value;
	}
});
Moo.implement(Moo, {
	$uidSequence: new Moo.Sequence({
		prefix: 0
	}),
	getUID: function() {
		return this.$uidSequence.next();
	}
});

Moo.Selectors = {
	cache: new Hash(),
	set: function() {
		switch ($type(arguments[0])) {
			case 'string':
				this.cache.set(arguments[0], arguments[1]);
				break;
			case 'object':
				for (var prop in arguments[0]) this.set(prop, arguments[0][prop]);
				break;
		}
	},
	get: function(key, toCls) {
		return ((toCls === true ? '.' : '') + (this.cache.get(key) || ''));
	}
};
Moo.implement(Moo, {
    getSelector: Moo.Selectors.get,
    setSelector: Moo.Selectors.set
}, Moo.Selectors);

Moo.setSelector({
	'Moo.Fx.Overflowable': 'moo-x-fx-overflowable',
	'Moo.Fx.Layoutable': 'moo-x-fx-layoutable'
});
Moo.namespace('Moo.Fx');
Moo.Fx = {
	overflowable: (Browser.isFirefox && Browser.version < 3.6),
	prepareOverflow: function(el) {
		if (this.overflowable) $(el).addClass(Moo.getSelector('Moo.Fx.Overflowable'));
	},
	fixOverflow: function(el) {
		if (this.overflowable) {
			el = $(el);
			var els = el.getElements('.' + Moo.getSelector('Moo.Fx.Overflowable'));
			if (el.hasClass(Moo.getSelector('Moo.Fx.Overflowable'))) els.push(el);
			for (var i = 0, l = els.length; i < l; i++) els[i].store('moo.fx.overflow', els[i].getStyle('overflow')).setStyle('overflow', 'hidden');
		}
	},
	unfixOverflow: function(el) {
		if (this.overflowable) {
			el = $(el);
			var els = el.getElements('.' + Moo.getSelector('Moo.Fx.Overflowable'));
			if (el.hasClass(Moo.getSelector('Moo.Fx.Overflowable'))) els.push(el);
			for (var i = 0, l = els.length; i < l; i++) els[i].setStyle('overflow', els[i].retrieve('moo.fx.overflow') || '');
		}
	},
	prepareLayout: function(el) {
		$(el).addClass(Moo.getSelector('Moo.Fx.Layoutable'));
	},
	doLayout: function(el) {
		el = $(el);
		var cmps = Moo.getComponents('.' + Moo.getSelector('Moo.Fx.Layoutable'), el);
		if (el.hasClass(Moo.getSelector('Moo.Fx.Layoutable'))) cmps.push(el.get('mooComponent'));
		for (var i = 0, l = cmps.length; i < l; i++) if (cmps[i] && cmps[i].$doLayout) cmps[i].$doLayout();
	}
};

Moo.Dom = {
	focus: function(something) {
		var focused = false;
		switch($type(something)) {
			case 'element': case 'string':
				something = $(something);
				try {
					something.focus();
					focused = true;
				} catch (e) {}
				break;
			case 'array':
				for (var i = 0, l = something.length; i < l && !focused; i++) {
					focused = focused || this.focus(something[i]);
				}
				break;
			default:
				break;
		}
		return focused;
	},
	setText: function(text, container) {
		if (typeof text == 'string' && text.trim() != '') container.set('text', text);
		else container.set('html', '&nbsp;');
	},
	getCenteredPosition: function(container) {
		var winSize = window.getSize();
		var winScroll = window.getScroll();
		var size = container.getSize();
		var pos = {
			left: winScroll.x + (winSize.x - size.x) / 2,
			top: winScroll.y + (winSize.y - size.y) / 2
		};
		if (pos.left < 0) pos.left = 0;
		if (pos.top < 0) pos.top = 0; 
		return pos;
	},
	center: function(container) {
		container.setStyles(this.getCenteredPosition(container));
	}
};

Moo.Controls = {
	emptyRe: /[^.*]/,
	isEmpty: function(str) {
		return !(this.emptyRe.test(str));
	},
	alphaRe: /^[a-z ._-]+$/i,
	isAlpha: function(str) {
		return (this.alphaRe.test(str));
	},
	alphanumRe: /^[a-z0-9 ._-]+$/i,
	isAlphanum: function(str) {
		return (this.alphanumRe.test(str));
	},
	integerRe: /^[-+]?\d+$/,
	isInteger: function(str) {
		return (this.integerRe.test(str));
	},
	numberRe: /^[-+]?\d*\.?\d+$/,
	isNumber: function(str) {
		return (this.numberRe.test(str));
	},
	emailRe: /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,
	isEmail: function(str) {
		return (this.emailRe.test(str));
	},
	colorRe: /\#+[a-f0-9]{6}$/i,
	isColor: function(str) {
		return (this.colorRe.test(str));
	}
};

Moo.Lang = {
	cache: new Hash(),
	set: function() {
		switch ($type(arguments[0])) {
			case 'string':
				this.cache.set(arguments[0], arguments[1]);
				break;
			case 'object':
				for (var prop in arguments[0]) this.set(prop, arguments[0][prop]);
				break;
		}
	},
	get: function(key) {
		return this.cache.get(key) || key;
	}
};
Moo.Lang.set({
	'moo.commons.ok': 'OK',
	'moo.commons.cancel': 'Cancel',
	'moo.commons.search': 'Search',
	'moo.commons.clear': 'Clear',
	'moo.commons.validate': 'Validate',
	'moo.commons.loading': 'Loading in progress, please wait...',
	'moo.commons.no_data': 'No data found.',
	'moo.commons.too_many_data': 'Too many data found. Please refine your search.',
	'moo.commons.max_elements': 'You can\'t select more than {0} element(s).'
});
Moo.setLang = Moo.Lang.set.safeBind(Moo.Lang);
Moo.getLang = Moo.Lang.get.safeBind(Moo.Lang);

Moo.DateHelper = {
	isLeapYear: function(year) {
		year = parseInt(year, 10);
		return (((year % 4) == 0) && ((year % 100) != 0) || ((year % 400) == 0));
	},
	daysInMonth: function(month, year) {
		month = parseInt(month, 10);
		year = parseInt(year, 10);
		if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
			return 31;
		} else if (month == 4 || month == 6 || month == 9 || month == 11) {
			return 30;
		} else if (month == 2) {
			return (this.isLeapYear(year) ? 29 : 28);
		}
	},
	firstDayInMonth: function(month, year) {
		month = parseInt(month, 10);
		year = parseInt(year, 10);
		var date = new Date(year, month - 1, 1);
		var day = date.getDay() - 1;
		if (day < 0) {
			day = 6;
		}
		return day;
	},
	stringToJson: function(str) {
		if (str.trim() == '' || !this.isDate(str)) return null;
		return {
			year: str.substr(6).toInt(),
			month: str.substr(3, 2).toInt(),
			day: str.substr(0, 2).toInt()
		};
	},
	jsonToString: function(json) {
		return (('' + json.day).length == 1 ? '0' + json.day : json.day) + '/' + (('' + json.month).length == 1 ? '0' + json.month : json.month) + '/' + json.year;
	},
	jsonToDate : function(json) {
		  return new Date(json.year || 0,
		   (json.month || 1) - 1,
		   json.day || 1,
		   json.hours || json.h || 0,
		   json.minutes || json.m || 0,
		   json.seconds || 0);
	},
	$isDate: function(year, month, day) {
		if (isNaN(day) || isNaN(month) || isNaN(year)) {
			return false;
		} else if ((day <= 0) || (day > 31) || (month <= 0) || (month > 12) || (year < 0)) {
			return false;
		} else if (day > this.daysInMonth(month, year)) {
			return false;
		}
		return true;
	},
	isDate: function(date) {
		date = date || '';
		if (date == '') return true;
		if (date.length != 10) return false;
		var day = date.substring(0, 2);
		var month = date.substring(3, 5);
		var year = date.substring(6, 10);
		return this.$isDate(year, month, day);
	},
	isJsonDate: function(date) {
		if (!date) return false;
		return this.$isDate(date.year, date.month, date.day);
	},
	formatDate: function(date) {
		return this.formatDateWithoutYear(date) + ' ' + date.getFullYear() ;
	},
	formatDateWithoutYear: function(date) {
		var result = [], dayOfWeek = date.getDay();
		result.push(Moo.getLang('moo.calendar.days')[(dayOfWeek == 0 ? 6 : dayOfWeek - 1)]);
		result.push(' ');
		result.push(date.getDate());
		result.push(' ');
		result.push(Moo.getLang('moo.calendar.months')[date.getMonth()]);
		return result.join('');
	}
};

Moo.TimeHelper = {
	$timeSep: ':',
	$isTime: function(h, m) {
		if (isNaN(h) || isNaN(m)) return false;
		if (h < 0 || h > 23 || m < 0 || m > 59) return false;
		return true;
	},
	isTime: function(time) {
		time = time || '';
		if (time == '') return true;
		if (time.length != 5) return false;
		var h = time.substring(0, 2), sep = time.substring(2, 3), m = time.substring(3, 6);
		if (isNaN(h) || isNaN(m) || sep != this.$timeSep) return false;
		if (h < 0 || h > 23 || m < 0 || m > 59) return false;
		return true;
	},
	stringToJson: function(time) {
		var result = null;
		if (time != '' && this.isTime(time)) {
			var times = time.split(this.$timeSep), h = times[0].toInt(), m = times[1].toInt();
			result = {h: h, m: m};
		}
		return result;
	},
	jsonToString: function(json) {
	    return ((json.h < 10 ? '0' : '') + json.h + this.$timeSep + (json.m < 10 ? '0' : '') + json.m);
	},
	completeTime: function(time) {
		var result = null;
		if (!this.isTime(time) && Moo.Controls.isInteger(time)) {
			var l = time.length, h = null, m = null;
			switch (l) {
				case 1: case 2:
					h = time.toInt();
					m = 0;
					break;
				case 3:
					h = time.substring(0, 1).toInt();
					m = time.substring(1).toInt();
					break;
				case 4:
					h = time.substring(0, 2).toInt();
					m = time.substring(2).toInt();
					break;
				default:
					break;
			}
			if (h != null && m != null) {
				if (this.$isTime(h, m)) result = this.jsonToString({h: h, m: m});
			}
		} else {
			result = time;
		}
		return result;
	},
	isBefore: function(time1, time2) {
		var t1 = this.stringToJson(time1), t2 = this.stringToJson(time2);
		if (!t1 || !t2) return false;
		return this.isJsonBefore(t1, t2);
	},
	isJsonBefore: function(time1, time2) {
		return (time1.h <= time2.h && time1.m < time2.m);
	},
	isAfter: function(time1, time2) {
		var t1 = this.stringToJson(time1), t2 = this.stringToJson(time2);
		if (!t1 || !t2) return false;
		return this.isJsonAfter(t1, t2);
	},
	isJsonAfter: function(time1, time2) {
		return (time1.h >= time2.h && time1.m > time2.m);
	},
	dateToTime: function(date) {
		var jsonDate = date.toJson();
		return this.jsonToString({h: jsonDate.hours, m: jsonDate.minutes});
	}
};

Moo.ClickRepeater = new Class({
	Implements: Options,
	options: {
		handle: null,
		delay: 500
	},
	initialize: function(options) {
		this.setOptions(options);
		this.handle = this.options.handle;
		switch($type(this.handle)) {
			case 'string': case 'element':
				this.handle = $(this.handle);
				break;
			case 'moo.component':
				this.handle = this.handle.container;
				break;
		}
		this.handle.addEvents({
			mousedown: this.startTimer.safeBind(this),
			mouseup: this.stop.safeBind(this),
			mouseout: this.stop.safeBind(this)
		});
	},
	start: function() {
		this.handle.fireEvent('click');
		this.timer = this.start.delay(this.options.delay, this);
	},
	stop: function() {
		$clear(this.timer);
	},
	startTimer: function() {
		this.timer = this.start.delay(this.options.delay, this);
	}
});

Moo.KeyListener = {
	$disabled: false,
	$cache: [],
	listen: function(event) {
		if (!this.$disabled) {
			var listener;
			for (var i = 0, l = this.$cache.length; i < l; i++) {
				listener = this.$cache[i];
				this.process(listener, event);
			}
		}
	},
	add: function(listener) {
		switch($type(listener)) {
			case 'object':
				listener = $merge({
					key: null,
					fn: $empty,
					alt: false,
					ctrl: false,
					shift: false,
					stopEvent: false
				}, listener);
				this.$cache.push(listener);
				break;
			case 'array':
				for (var i = 0, l = listener.length; i < l; i++) this.add(listener[i]);
				break;
		}
	},
	remove: function(listener) {
		switch($type(listener)) {
			case 'object':
				this.$cache.erase(listener);
				break;
			case 'array':
				for (var i = 0, l = listener.length; i < l; i++) this.remove(listener[i]);
				break;
		}
	},
	process: function(listener, event) {
		if (listener.key != event.key) return false;
		if (listener.ctrl === true && !event.control) return false;
		if (listener.shift === true && !event.shift) return false;
		if (listener.alt === true && !event.alt) return false;
		if (listener.stopEvent === true) event.stop();
		listener.fn.run(event);
		return true;
	},
	setDisabled: function(disabled) {
		this.$disabled = Moo.toBoolean(disabled, false);
	}
};
Moo.addKeydown(Moo.KeyListener.listen.safeBind(Moo.KeyListener));

Moo.Themeable = new Class({
	theme: {},
	initializeTheme: function() {
		if (!this.$theme) return this;
		for (var t in this.$theme) this.theme[t] = this.options.containerCls + this.$theme[t];
		if (this.options.theme) this.setTheme(this.options.theme);
		return this;
	},
	setTheme: function() {
		switch($type(arguments[0])) {
			case 'string':
				this.theme[arguments[0]] = arguments[1];
				break;
			case 'object':
				this.theme = $merge(this.theme, arguments[0]);
				break;
		}
		return this;
	}
});

Moo.Langable = new Class({
	lang: {},
	initializeLang: function() {
		if (!this.$lang) return this;
		if (!this.lang) this.lang = {};
		var langs = $merge(this.$lang, this.options.lang);
		for (var l in langs) this.lang[l] = Moo.getLang(langs[l]);
		return this;
	},
	setLang: function() {
		switch($type(arguments[0])) {
			case 'string':
				this.lang[arguments[0]] = arguments[1];
				break;
			case 'object':
				this.lang = $merge(this.lang, arguments[0]);
				break;
		}
		return this;
	}
});

Moo.setSelector({
	'Moo.Destroyable': 'moo-x-destroyable'
});
Moo.Destroyable = new Class({
	makeDestroyable: function(addCls) {
		if (Moo.toBoolean(addCls, true)) this.container.addClass(Moo.getSelector('Moo.Destroyable'));
		else this.container.removeClass(Moo.getSelector('Moo.Destroyable'));
		return this;
	}
});

Moo.implement(Moo, {
	$componentSequence: new Moo.Sequence({
		prefix: 'moo-component-'
	})
});
Element.Properties.mooComponent = {
	set: function(component) {
		return this.eliminate('mooComponent').store('mooComponent', component);
	},
	get: function() {
		return this.retrieve('mooComponent');
	}
};
Moo.setSelector({
	'Moo.Component': 'moo-x-component'
});
Moo.Component = new Class({
	Implements: [Events, Options],
	$mooComponent: true,
	$componentType: 'component',
	$componentSelector: '',
	$containerType: 'div',
	options: {
		id: null,
		containerCls: '',
		containerExtraCls: '',
		properties: {},
		styles: {},
		absolute: false,
		top: null,
		bottom: null,
		left: null,
		rigth: null,
		tooltip: null
	},
	initialize: function(options) {
		this.$family = {name: 'moo.component'};
		this.initializeOptions(options);
		this.container = new Element(this.$containerType, this.options.properties);
		this.id = this.options.id || Moo.$componentSequence.next();
		this.container.set('id', this.id);
		this.container.set('mooComponent', this);
		delete this.options.id;
		this.container.addClasses(Moo.getSelector('Moo.Component'), this.$componentSelector, this.options.containerCls, this.options.containerExtraCls);
		this.setStyles(this.options.styles);
		if (this.options.absolute === true) {
			var styles = {position: 'absolute'};
			if (this.options.bottom != null) styles.bottom = this.options.bottom;
			else styles.top = this.options.top || 0;
			if (this.options.right != null) styles.right = this.options.right;
			else styles.left = this.options.left || 0;
			this.container.setStyles(styles);
		}
		if (this.options.tooltip) this.makeTooltip(this.options.tooltip);
	},
	inject: function(container, where) {
		this.fireEvent('onBeforeInject', container);
		this.container.inject(container, where);
		this.fireEvent('onInject', container);
		return this;
	},
	getComponent: function(el) {
		return Moo.getComponent(el);
	},
	getComponents: function(selectors) {
		return Moo.getComponents(selectors, this.container);
	},
	empty: function() {
		Moo.empty(this.container);
		return this;
	},
	update: function(what) {
		Moo.update(what, this.container);
		return this;
	},
	destroy: function() {
		this.fireEvent('beforeDestroy');
		this.empty();
		this.container.destroy();
		this.fireEvent('destroy');
	},
	prepareLayout: function() {
		Moo.Fx.prepareLayout(this.container);
		return this;
	},
	doLayout: function() {
		Moo.Fx.doLayout(this.container);
		return this;
	},
	toQueryString: function() {
		return this.container.toQueryString();
	},
	anchor: function(el, options) {
		Moo.anchor(this.container, el, options);
		return this;
	},
	injectTop: function(container) {
		return this.inject(container, 'top');
	},
	injectBottom: function(container) {
		return this.inject(container, 'bottom');
	},
	injectBefore: function(container) {
		return this.inject(container, 'before');
	},
	injectAfter: function(container) {
		return this.inject(container, 'after');
	},
	add: function(something, where) {
		var result = Moo.inject(something, this.container, where || 'bottom');
		return result;
	},
	addTop: function(something) {
		return this.add(something, 'top');
	},
	addBottom: function(something) {
		return this.add(something, 'bottom');
	},
	getElement: function(selector) {
		return this.container.getElement(selector);
	},
	getElements: function(selectors) {
		return this.container.getElements(selectors);
	},
	getStyle: function(property) {
		return this.container.getStyle(property);
	},
	getStyles: function() {
		return this.container.getStyles.run(arguments, this.container);
	},
	setStyle: function(name, value) {
		this.container.setStyle(name, value);
		return this;
	},
	setStyles: function(styles) {
		this.container.setStyles(styles);
		return this;
	},
	setOpacity: function(opacity) {
		this.container.setOpacity(opacity);
		return this;
	},
	show: function() {
		this.container.show();
		return this;
	},
	hide: function() {
		this.container.hide();
		return this;
	},
	setWidth: function(width) {
		return this.setStyle('width', width || 'auto');
	},
	setHeight: function(height) {
		return this.setStyle('height', height || 'auto');
	},
	addClass: function(cls) {
		this.container.addClass(cls);
		return this;
	},
	addClasses: function() {
		this.container.addClasses.run(arguments, this.container);
		return this;
	},
	hasClass: function(cls) {
		return this.container.hasClass(cls);
	},
	hasClasses: function() {
		return this.container.hasClasses.run(arguments, this.container);
	},
	removeClass: function(cls) {
		this.container.removeClass(cls);
		return this;
	},
	removeClasses: function() {
		this.container.removeClasses.run(arguments, this.container);
		return this;
	},
	replaceClass: function(what, by) {
		this.container.replaceClass(what, by);
		return this;
	},
	getParent: function() {
		return this.container.getParent();
	},
	dispose: function() {
		this.container.dispose();
		return this;
	},
	descendantOf: function(el) {
		return this.container.descendantOf(el);
	},
	ascendantOf: function(el) {
		return this.container.ascendantOf(el);
	},
	isInBody: function() {
		return this.container.isInBody();
	},
	getSize: function() {
		return this.container.getSize();
	},
	getScrollSize: function() {
		return this.container.getScrollSize();
	},
	getScroll: function() {
		return this.container.getScroll();
	},
	getPosition: function() {
		return this.container.getPosition();
	},
	getCoordinates: function() {
		return this.container.getCoordinates();
	},
	fade: function(how) {
		this.container.fade(how);
		return this;
	},
	highlight: function(start, end) {
		this.container.highlight(start, end);
		return this;
	},
	store: function(property, value) {
		this.container.store(property, value);
		return this;
	},
	retrieve: function(property, dflt) {
		return this.container.retrieve(property, dflt);
	},
	eliminate: function(property){
		this.container.eliminate(property);
		return this;
	},
	set: function(prop, value) {
		this.container.set(prop, value);
		return this;
	},
	get: function(prop) {
		return this.container.get(prop);
	},
	erase: function(prop) {
		this.container.erase(prop);
		return this;
	}
});
Moo.implement(Moo, {
	isComponent: function(what) {
		return ($type(what) == 'moo.component');
	},
	getComponent: function(el) {
		return ((el = $(el)) ? el.get('mooComponent') : null);
	},
	getComponents: function(selectors, el) {
		el = $(el);
		var els = el.getElements(selectors);
		var cmps = els.map(function(el) {
			return el.get('mooComponent');
		});
		return cmps;
	},
	$types: new Hash(),
	registerType: function(name, componentClass) {
		this.$types.set(name, componentClass);
	},
	create: function(obj) {
		var result = null;
		if (obj) {
			var mooType = obj.mooType;
			if (mooType) {
				var cmp = this.$types.get(mooType);
				delete obj.mooType;
				if (cmp) result = new cmp(obj);
			}
		}
		return result; 
	},
	inject: function(what, el, where) {
		el = $(el);
		where = where || 'bottom';
		var result = null;
		switch($type(what)) {
			case 'element':
				result = $(what);
				result.inject(el, where);
				break;
			case 'string': case 'number':
				result = Moo.injectHTML(what, el, where);
				break;
			case 'moo.component':
				result = what;
				result.inject(el, where);
				break;
			case 'object':
				var result = this.create(what);
				if (result != null) {
					result.inject(el, where);
				}
				break;
			case 'array':
				for (var i = 0, l = what.length; i < l; i++) this.inject(what[i], el);
				break;
			case 'function':
				result = this.inject(what.run(el), el, where);
			default:
				result = null;
				break;
		}
		return result;
	},
	empty: function(el) {
		el = $(el);
		this.getComponents(Moo.getSelector('Moo.Destroyable', true), el).each(function(cmp) {
			cmp.destroy();
		});
		el.empty();
		return this;
	},
	update: function(what, el) {
		return this.empty(el).inject(what, el);
	},
	destroy: function(what) {
		switch ($type(what)) {
			case 'element': case 'string':
				what = $(what);
				Moo.empty(what);
				what.destroy();
				break;
			case 'moo.component':
				what.destroy();
				break;
			default:
				break;
		}
		return this;
	}
});
Moo.alias(Moo, 'getComponent', '$').alias(Moo, 'getComponents', '$$');

Moo.setSelector({
	'Moo.Icon': 'moo-x-icon'
});
Moo.Icon = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$componentType: 'icon',
	$componentSelector: Moo.getSelector('Moo.Icon'),
	$containerType: 'img',
	$theme: {
		over: '-over',
		disabled: '-disabled'
	},
	options: {
		containerCls: 'moo-icon',
		overable: true,
		disabled: false,
		stopEvent: false,
		destroyOnClick: false,
		hideOnClick: false,
		disableOnClick: false,
		onClick: $empty,
		onMouseOver: $empty,
		onMouseOut: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.initializeTheme();
		this.container.set('src', Moo.pixelImgSrc);
		this.container.addEvent('click', this.onClick.safeBind(this));
		this.setDisabled(this.options.disabled);
		this.container.addEvents({
			mouseover: this.onMouseOver.safeBind(this),
			mouseout: this.onMouseOut.safeBind(this)
		});
	},
	onClick: function(event) {
		if (!this.$disabled) {
			if (this.options.stopEvent === true && event) event.stop();
			this.fireEvent('onClick', this);
			if (this.options.destroyOnClick) this.destroy();
			else if (this.options.hideOnClick) this.hide();
			else if (this.options.disableOnClick) {
				this.setDisabled(true);
				this.container.removeClass(this.theme.over);
			}
		}
		return this;
	},
	onMouseOver: function() {
		if (!this.$disabled && this.options.overable === true) this.container.addClass(this.theme.over);
		this.fireEvent('onMouseOver', this);
		return this;
	},
	onMouseOut: function() {
		if (this.options.overable === true) this.container.removeClass(this.theme.over);
		this.fireEvent('onMouseOut', this);
		return this;
	},
	setDisabled: function(disabled) {
		this.$disabled = Moo.toBoolean(disabled, false);
		if (this.$disabled) this.container.addClass(this.theme.disabled);
		else this.container.removeClass(this.theme.disabled);
	},
	isDisabled: function() {
		return this.$disabled;
	}
});
Moo.registerType('icon', Moo.Icon);
Moo.implement(Moo, {
	getHTMLIcon: function(cls, style) {
		return '<img src="' + this.pixelImgSrc + '" class="' + Array.flatten([cls]).join('') + '" style="' + (style || '') + '" />';
	}
});

Moo.Icons = {};
Moo.implement(Moo, {
	setIcon: function() {
		switch ($type(arguments[0])) {
			case 'string':
				Moo.Icons[arguments[0]] = arguments[1];
				break;
			case 'object':
				for (var prop in arguments[0]) this.setIcon(prop, arguments[0][prop]);
				break;
		}
	}
});
['loader',
'right', 'dblright',
'left', 'dblleft',
'down', 'dbldown',
'up', 'dblup',
'close', 'mini-close',
'combo-down', 'combo-up',
'calendar',
'search',
'save',
'trash',
'erase',
'folder',
'file',
'valid', 'invalid',
'error', 'warning', 'information', 'question',
'plus', 'minus'].each(function(name) {
	Moo.setIcon(name.replaceAll('-', ''), 'moo-icon-' + name);
});

Moo.setSelector({
	'Moo.Link': 'moo-x-link'
});
Moo.Link = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$componentType: 'link',
	$componentSelector: Moo.getSelector('Moo.Link'),
	$containerType: 'a',
	$theme: {
		over: '-over'
	},
	options: {
		containerCls: 'moo-link',
		text: null,
		href: 'javascript:void(0);',
		iconCls: null,
		stopEvent: false,
		destroyOnClick: false,
		hideOnClick: false,
		disableOnClick: false,
		onClick: $empty,
		onMouseOver: $empty,
		onMouseOut: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.initializeTheme();
		this.container.set('href', this.options.href);
		this.textContainer = new Element('span').inject(this.container);
		this.setText(this.options.text);
		if (this.options.iconCls != null) {
			this.icon = new Moo.Icon({
				containerCls: this.options.iconCls,
				overable: false
			}).inject(this.container, 'top');
		}
		this.container.addEvents({
			click: this.onClick.safeBind(this),
			mouseover: this.onMouseOver.safeBind(this),
			mouseout: this.onMouseOut.safeBind(this)
		});
	},
	setText: function(text) {
		Moo.update(text, this.textContainer);
		return this;
	},
	onClick: function(event) {
		if (this.options.stopEvent === true && event) event.stop();
		this.fireEvent('onClick', this);
		if (this.options.destroyOnClick) this.destroy();
		else if (this.options.hideOnClick) this.hide();
		else if (this.options.disableOnClick) {
			this.setDisabled(true);
			this.container.removeClass(this.theme.over);
		}
		return this;
	},
	onMouseOver: function() {
		this.container.addClass(this.theme.over);
		this.fireEvent('onMouseOver', this);
		return this;
	},
	onMouseOut: function() {
		this.container.removeClass(this.theme.over);
		this.fireEvent('onMouseOut', this);
		return this;
	}
});
Moo.registerType('link', Moo.Link);

Moo.setSelector({
	'Moo.Button': 'moo-x-button'
});
Moo.Button = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$componentType: 'button',
	$componentSelector: Moo.getSelector('Moo.Button'),
	$theme: {
		left: '-left',
		center: '-center',
		right: '-right',
		over: '-over',
		disabled: '-disabled'
	},
	$containerType: 'table',
	options: {
		containerCls: 'moo-button',
		iconCls: null,
		text: null,
		disabled: false,
		stopEvent: false,
		destroyOnClick: false,
		hideOnClick: false,
		disableOnClick: false,
		onClick: $empty,
		onMouseOver: $empty,
		onMouseOut: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.initializeTheme();
		this.container.set({
			cellpadding: 0,
			cellspacing: 0
		});
		this.tBody = new Element('tbody').inject(this.container);
		this.row = new Element('tr').inject(this.tBody);
		this.leftCell = new Element('td', {html: '<em> </em>'}).addClass(this.theme.left).inject(this.row);
		this.centerCell = new Element('td').addClass(this.theme.center).inject(this.row);
		this.rightCell = new Element('td', {html: '<em> </em>'}).addClass(this.theme.right).inject(this.row);
		this.buttonEl = new Element('button').inject(this.centerCell);
		this.setText(this.options.text);
		if (this.options.iconCls != null) {
			this.icon = new Moo.Icon({
				containerCls: this.options.iconCls,
				overable: false
			}).inject(this.centerCell, 'top');
		}
		this.container.addEvent('click', this.onClick.safeBind(this));
		this.setDisabled(this.options.disabled);
		this.container.addEvents({
			mouseover: this.onMouseOver.safeBind(this),
			mouseout: this.onMouseOut.safeBind(this)
		});
	},
	setText: function(text) {
		Moo.update(text, this.buttonEl);
		return this;
	},
	focus: function() {
		Moo.Dom.focus(this.buttonEl);
		return this;
	},
	onClick: function(event) {
		if (!this.$disabled) {
			if (this.options.stopEvent === true && event) event.stop();
			this.fireEvent('onClick', this);
			if (this.options.destroyOnClick) this.destroy();
			else if (this.options.hideOnClick) this.hide();
			else if (this.options.disableOnClick) {
				this.setDisabled(true);
				this.container.removeClass(this.theme.over);
			}
		}
		return this;
	},
	onMouseOver: function() {
		if (!this.$disabled) this.container.addClass(this.theme.over);
		this.fireEvent('onMouseOver', this);
		return this;
	},
	onMouseOut: function() {
		this.container.removeClass(this.theme.over);
		this.fireEvent('onMouseOut', this);
		return this;
	},
	setDisabled: function(disabled) {
		this.$disabled = Moo.toBoolean(disabled, false);
		if (this.$disabled) this.container.addClass(this.theme.disabled);
		else this.container.removeClass(this.theme.disabled);
		if (this.icon) this.icon.setDisabled(this.$disabled);
	},
	isDisabled: function() {
		return this.$disabled;
	}
});
Moo.registerType('button', Moo.Button);

Moo.setSelector({
	'Moo.Box': 'moo-x-box'
});
Moo.Box = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$componentType: 'box',
	$componentSelector: Moo.getSelector('Moo.Box'),
	$theme: {
		topLeft: '-top-left',
		topCenter: '-top-center',
		topRight: '-top-right',
		middleLeft: '-middle-left',
		middleCenter: '-middle-center',
		middleRight: '-middle-right',
		bottomLeft: '-bottom-left',
		bottomCenter: '-bottom-center',
		bottomRight: '-bottom-right'
	},
	options: {
		containerCls: 'moo-box',
		content: null,
		width: null,
		height: (Browser.isIE ? 'auto' : null)
	},
	initialize: function(options) {
		this.parent(options);
		this.initializeTheme();
		this.boxTopLeft = new Element('div').addClass(this.theme.topLeft).inject(this.container);
		this.boxTopRight = new Element('div').addClass(this.theme.topRight).inject(this.boxTopLeft);
		this.boxTopCenter = new Element('div').addClass(this.theme.topCenter).inject(this.boxTopRight);
		this.boxMiddleLeft = new Element('div').addClass(this.theme.middleLeft).inject(this.container);
		this.boxMiddleRight = new Element('div').addClass(this.theme.middleRight).inject(this.boxMiddleLeft);
		this.boxMiddleCenter = new Element('div').addClass(this.theme.middleCenter).inject(this.boxMiddleRight);
		this.boxBottomLeft = new Element('div').addClass(this.theme.bottomLeft).inject(this.container);
		this.boxBottomRight = new Element('div').addClass(this.theme.bottomRight).inject(this.boxBottomLeft);
		this.boxBottomCenter = new Element('div').addClass(this.theme.bottomCenter).inject(this.boxBottomRight);
		this.mainContainer = this.boxMiddleCenter;
		if (this.options.width) this.setWidth(this.options.width);
		if (this.options.height) this.setHeight(this.options.height);
		if (this.options.content) this.add(this.options.content);
		delete this.options.content;
	},
	add: function(something, where) {
		var result = Moo.inject(something, this.mainContainer, where || 'bottom');
		return result;
	},
	empty: function() {
		Moo.empty(this.mainContainer);
		return this;
	},
	update: function(what) {
		Moo.update(what, this.mainContainer);
		return this;
	},
	getElement: function(selector) {
		return this.mainContainer.getElement(selector);
	},
	getElements: function(selectors) {
		return this.mainContainer.getElements(selectors);
	},
	setHeight: function(height) {
		this.mainContainer.setStyles({
			overflow: (height ? 'auto' : ''),
			height: height
		});
		return this;
	}
});
Moo.registerType('box', Moo.Box);

Moo.setSelector({
	'Moo.Cards': 'moo-x-cards'
});
Moo.Cards = new Class({
	Extends: Moo.Box,
	$componentType: 'Cards',
	$componentSelector: Moo.getSelector('Moo.Cards'),
	options: {
		containerCls: 'moo-cards',
		cards: [],
		cardCls: null,
		display: null
	},
	initialize: function(options) {
		this.$cards = new Hash();
		this.$displayedKey = null;
		this.parent(options);
		this.options.cards.each(this.add.safeBind(this));
		delete this.options.cards;
		if (this.options.display) this.display(this.options.display);
		delete this.options.display;
	},
	add: function(options) {
		options = options || {};
		var cardKey = options.key || 'moo-card-' + Moo.getUID(),
			cardCls = options.cls || this.options.cardCls;
		var card = new Element('div').hide().inject(this.mainContainer);
		if (cardCls) card.addClass(cardCls);
		Moo.inject(options.content, card);
		this.$cards.set(cardKey, card);
		return {
			key: cardKey,
			card: card
		};
	},
	display: function(key) {
		if (this.$displayedKey) {
			if (key == this.$displayedKey) return this;
			this.getCard(this.$displayedKey).hide();
		}
		var card = this.getCard(key);
		if (card) {
			card.show();
			this.$displayedKey = key;
		}
		return this;
	},
	getDisplayedKey: function() {
		return this.$displayedKey;
	},
	getDisplayedCard: function() {
		return this.$cards.get(this.$displayedKey);
	},
	getCard: function(key) {
		return this.$cards.get(key);
	},
	getKeys: function() {
		return this.$cards.getKeys();
	},
	getCards: function() {
		return this.$cards.getValues();
	},
	remove: function(key) {
		var card = this.getCard(key);
		if (card) {
			this.$cards.erase(key);
			card.destroy();
			if (key == this.$displayedKey) this.$displayedKey = null;
		}
	},
	update: function(key, what) {
		var card = this.getCard(key);
		if (card) Moo.update(what, card);
		return this;
	},
	hasKey: function(key) {
		return this.$cards.has(key);
	},
	count: function() {
		return this.$cards.getLength();
	}
});
Moo.registerType('Cards', Moo.Cards);

Moo.namespace('Moo.layout');

Moo.setSelector({
	'Moo.Layout': 'moo-x-layout',
	'Moo.Layout.Row': 'moo-x-layout-row',
	'Moo.Layout.Cell': 'moo-x-layout-cell'
});
Moo.layout.Layout = new Class({
	Extends: Moo.Component,
	$componentType: 'layout',
	$componentSelector: Moo.getSelector('Moo.Layout'),
	options: {
		tableCls: 'moo-layout',
		cellpadding: 0,
		cellspacing: 0,
		width: null,
		height: null,
		rows: []
	},
	initialize: function(options) {
		this.parent(options);
		this.table = new Element('table', {
			cellpadding: this.options.cellpadding,
			cellspacing: this.options.cellspacing
		}).addClass(this.options.tableCls).inject(this.container);
		if (this.options.width != null) this.table.set('width', this.options.width);
		if (this.options.height != null) this.table.set('height', this.options.height);
		this.tBody = new Element('tbody').inject(this.table);
		this.options.rows.each(this.newRow.safeBind(this));
	},
	newRow: function(options) {
		options = $merge({
			cls: 'moo-row',
			cells: [],
			id: null
		}, options);
		var row = new Element('tr', {
			id: options.id || ('moo-layout-row-' + Moo.getUID())
		}).addClasses(Moo.getSelector('Moo.Layout.Row'), options.cls).inject(this.tBody);
		options.cells.each(function(c) {
			c = $merge({
				cls: 'moo-cell',
				align: 'left',
				valign: 'top',
				content: '&nbsp;',
				width: 'auto',
				extraCls: '',
				height: 'auto'
			}, c);
			var cell = new Element('td', {
				id: c.id || Moo.getUID(),
				align: c.align,
				vAlign: c.valign,
				width: c.width,
				height: c.height
			}).addClasses(Moo.getSelector('Moo.Layout.Cell'), c.cls, c.extraCls);
			if (c.colspan) cell.set('colspan', c.colspan);
			else if (c.rowspan) cell.set('rowspan', c.rowspan);
			Moo.inject(c.content, cell);
			cell.inject(row);
		});
		return row;
	},
	getRow: function(what) {
		var row = null;
		switch ($type(what)) {
			case 'string':
				row = $(what);
				break;
			case 'number':
				row = this.table.getElements(Moo.getSelector('Moo.Layout.Row'))[what];
				break;
			case 'element':
				row = element;
				break;
			default:
				break;
		}
		return row;
	},
	removeRow: function(what) {
		var row = this.getRow(what);
		if (row != null) Moo.destroy(row);
		return this;
	}
});
Moo.registerType('layout', Moo.layout.Layout);
Moo.Layout = Moo.layout.Layout;

Moo.setSelector({
	'Moo.ColumnLayout': 'moo-x-columnlayout'
});
Moo.layout.ColumnLayout = new Class({
	Extends: Moo.Component,
	$componentType: 'column-layout',
	$componentSelector: Moo.getSelector('Moo.ColumnLayout'),
	options: {
		tableCls: 'moo-layout',
		cellpadding: 0,
		cellspacing: 0,
		width: null,
		columns: []
	},
	initialize: function(options) {
		this.parent(options);
		this.table = new Element('table', {
			cellpadding: this.options.cellpadding,
			cellspacing: this.options.cellspacing
		}).addClass(this.options.tableCls).inject(this.container);
		if (this.options.width != null) this.table.set('width', this.options.width);
		this.tBody = new Element('tbody').inject(this.table);
		this.row = new Element('tr').addClass('TODO').inject(this.tBody);
		this.options.columns.each(this.newColumn.safeBind(this));
	},
	newColumn: function(options) {
		options = $merge({
			cls: 'moo-column',
			align: 'left',
			valign: 'top',
			cells: []
		}, options);
		var column = new Element('td', {
			options: options.id || Moo.getUID(),
			align: options.align,
			valign: options.valign
		}).addClass(options.cls).inject(this.row);
		options.cells.each(function(c) {
			c = $merge({
				cls: 'moo-cell',
				content: '&nbsp;'
			}, c);
			var cell = new Element('div', {
				id: c.id || Moo.getUID()
			}).addClass(c.cls);
			if (c.colspan) cell.set('colspan', c.colspan);
			Moo.inject(c.content, cell);
			cell.inject(column);
		});
		return column;
	}
});
Moo.registerType('column-layout', Moo.layout.ColumnLayout);
Moo.ColumnLayout = Moo.layout.ColumnLayout;

Moo.Mask = new Class({
	Implements: Options,
	options: {
		containerCls: 'moo-mask',
		opacity: 0.4,
		hideOnClick: false
	},
	initialize: function(element, options) {
		this.applyDimensions = this.applyDimensions.safeBind(this);
		this.initializeOptions(options);
		this.element = $(element) || $(document.body);
		this.container = new Element('div', {
			html: '&nbsp;'
		}).addClass(this.options.containerCls).setOpacity(this.options.opacity).hide();
		if (this.options.hideOnClick == true) this.container.addEvent('click', this.hide.safeBind(this));
		this.isOpen = false;
		Moo.addResize(this.applyDimensions);
	},
	inject: function() {
		this.container.inject(document.body);
		return this;
	},
	applyDimensions: function() {
		if (this.isOpen == true) {
			if (this.element == document.body) {
				this.container.setStyles(Moo.getViewSize());
			} else {
				var coords = this.element.getCoordinates();
				this.container.setStyles({
					top: coords.top,
					left: coords.left,
					width: coords.width,
					height: coords.height
				});
			}
		}
		return this;
	},
	show: function() {
		if (this.isOpen === false) {
			this.container.show();
			this.isOpen = true;
			this.applyDimensions();
		}
		return this;
	},
	hide: function() {
		if (this.isOpen === true) {
			this.container.setStyles({
				width: 0,
				height: 0
			}).hide();
			this.isOpen = false;
		}
		return this;
	},
	destroy: function() {
		this.container.destroy();
		Moo.removeResize(this.applyDimensions);
		return this;
	},
	setZIndex: function(zIndex) {
		this.container.setStyle('z-index', zIndex);
		return this;
	}
});

Moo.Loader = {
	options: {
		cursorTransform: true,
		zIndex: 100000,
		containerCls: 'moo-loader'
	},
	$initialized: false,
	initialize: function() {
		if (!this.$initialized) {
			this.center = this.center.safeBind(this);
			this.mask = new Moo.Mask().setZIndex(this.options.zIndex).inject(document.body);
			this.container = new Element('div').setStyles({
				position: 'absolute',
				'z-index': this.options.zIndex
			}).addClass(this.options.containerCls).hide().inject(document.body);
			this.isOpen = false;
			this.$initialized = true;
		}
	},
	show: function(what) {
		this.initialize();
		Moo.update(what, this.container);
		if (this.isOpen == false) {
			this.mask.show.delay(1, this.mask);
			this.container.show.delay(1, this.container);
			this.center.delay(1, this);
			Moo.addResize(this.center);
			Moo.addScroll(this.center);
			if (this.options.cursorTransform == true) $(document.body).setStyle('cursor', 'wait');
			this.isOpen = true;
		}
		return this;
	},
	hide: function() {
		if (this.isOpen == true) {
			this.container.hide.delay(1, this.container);
			this.mask.hide.delay(1, this.mask);
			Moo.removeResize(this.center);
			Moo.removeScroll(this.center);
			if (this.options.cursorTransform == true) $(document.body).setStyle('cursor', 'auto');
			this.isOpen = false;
		}
		return this;
	},
	center: function() {
		this.initialize();
		if (this.isOpen == true) Moo.Dom.center(this.container);
	}
};
Moo.implement(Moo, {
	showLoader: Moo.Loader.show.safeBind(Moo.Loader),
	hideLoader: Moo.Loader.hide.safeBind(Moo.Loader)
});

Moo.TooltipManager = {
	initialize: function() {
		this.container = new Element('div').addClass('moo-tooltip').setStyles({
			position: 'absolute',
			top: 0,
			left: 0,
			'z-index': 10000
		}).hide().inject(document.body);
		this.headerContainer = new Element('div').addClass('moo-tooltip-header').inject(this.container);
		this.bodyContainer = new Element('div').addClass('moo-tooltip-body').inject(this.container);
		this.footerContainer = new Element('div').addClass('moo-tooltip-footer').inject(this.container);
		this.container.addEvent('mouseenter', this.hide.safeBind(this));
	},
	setHeader: function(header) {
		this.headerContainer.empty();
		if (header == null) this.headerContainer.hide();
		else {
			Moo.inject(header, this.headerContainer);
			this.headerContainer.show();
		}
	},
	setBody: function(body) {
		this.bodyContainer.empty();
		if (body == null) this.bodyContainer.hide();
		else {
			Moo.inject(body, this.bodyContainer);
			this.bodyContainer.show();
		}
	},
	setFooter: function(footer) {
		this.footerContainer.empty();
		if (footer == null) this.footerContainer.hide();
		else {
			Moo.inject(footer, this.footerContainer);
			this.footerContainer.show();
		}
	},
	show: function() {
		this.container.show();
	},
	hide: function() {
		this.headerContainer.empty().hide();
		this.bodyContainer.empty().hide();
		this.footerContainer.empty().hide();
		this.container.hide();
	},
	addClass: function(cls) {
		this.container.addClass(cls);
	},
	emptyClass: function() {
		this.container.emptyClass();
	}
};
Moo.onReady(Moo.TooltipManager.initialize, Moo.TooltipManager);
Moo.Tooltip = new Class({
	Implements: Options,
	options: {
		containerCls: 'moo-tooltip',
		width: 'auto',
		header: null,
		body: null,
		footer: null,
		offsets: {x: 16, y: 16},
		load: $empty,
		loadText: '',
		loadDelay: 0,
		loadDelayText: null
	},
	initialize: function(element, options) {
		this.setOptions(options);
		this.element = $(element);
		this.$mouseIn = false;
		this.element.addEvents({
			mouseenter: this.onMouseEnter.safeBind(this),
			mouseleave: this.onMouseLeave.safeBind(this),
			mousemove: this.locate.safeBind(this)
		});
		this.$doLoad = ($type(this.options.load) == 'function' && this.options.load != $empty);
		var loadDelayText = this.options.loadDelayText;
		if (this.$doLoad && loadDelayText != null) {
			if ($type(loadDelayText) == 'string') loadDelayText = loadDelayText.format({
				ms: this.options.loadDelay,
				s: this.options.loadDelay / 1000
			});
			this.options.body = loadDelayText;
		}
	},
	onMouseEnter: function() {
		this.$mouseIn = true;
		if (this.$doLoad) this.load();
		else this.apply();
	},
	onMouseLeave: function() {
		this.$mouseIn = false;
		$clear(this.delayedLoad);
		Moo.TooltipManager.emptyClass();
		Moo.TooltipManager.hide();
	},
	load: function() {
		this.delayedLoad = function() {
			this.$doLoad = false;
			this.options.header = this.options.footer = null;
			this.options.body = Moo.createLoader(this.options.loadText);
			this.apply();
			this.options.load(this);
		}.delay(this.options.loadDelay, this);
		if (this.options.loadDelayText != null) this.apply();
	},
	locate: function(event) {
		var size = window.getSize(), scroll = window.getScroll();
		var tip = {x: Moo.TooltipManager.container.offsetWidth, y: Moo.TooltipManager.container.offsetHeight};
		var props = {x: 'left', y: 'top'};
		for (var z in props){
			var pos = event.page[z] + this.options.offsets[z];
			if ((pos + tip[z] - scroll[z]) > size[z]) pos = event.page[z] - this.options.offsets[z] - tip[z];
			Moo.TooltipManager.container.setStyle(props[z], pos);
		}
	},
	update: function(options) {
		this.options = $merge({
			containerCls: this.options.containerCls,
			width: 'auto',
			header: null,
			body: null,
			footer: null,
			offsets: {x: 16, y: 16}
		}, options);
		this.apply();
	},
	apply: function() {
		if (this.$mouseIn) {
			var mustShow = (this.options.header != null) || (this.options.body != null) || (this.options.footer != null);
			if (mustShow) {
					Moo.TooltipManager.container.setStyle('width', this.options.width);
					Moo.TooltipManager.setHeader($type(this.options.header) == 'element' ? this.options.header.clone() : this.options.header);
					Moo.TooltipManager.setBody($type(this.options.body) == 'element' ? this.options.body.clone() : this.options.body);
					Moo.TooltipManager.setFooter($type(this.options.footer) == 'element' ? this.options.footer.clone() : this.options.footer);
					Moo.TooltipManager.addClass(this.options.containerCls);
					Moo.TooltipManager.show();
			} else {
				Moo.TooltipManager.hide();
			}
		}
	}
});
Element.implement({
	makeTooltip: function(options) {
		if (Moo.Features.useTooltip === true) {
			switch($type(options)) {
				case 'string':
				case 'number':
					options = {body: '' + options};
					break;
				case 'object':
					break;
				default:
					options = null;
					break;
			}
			var tooltip = this.retrieve('moo.tooltip');
			if (tooltip == null) {
				tooltip = new Moo.Tooltip(this, options);
				this.store('moo.tooltip', tooltip);
			} else {
				tooltip.update(options);
			}
		} else {
			this.set('title', (typeof options == 'string' ? options : ''));
		}
		return this;
	}
});
Moo.Component.implement({
	makeTooltip: function(options) {
		this.container.makeTooltip(options);
		return this;
	}
});
Moo.implement(Moo, {
	fixInnerTooltip: function(innerEl, parentEl, innerCls) {
		innerEl.addEvents({
			mouseenter: function(e) {
				Moo.TooltipManager.emptyClass();
				Moo.TooltipManager.addClass(innerCls || 'moo-tooltip');
			},
			mouseleave: function(e) {
				parentEl.fireEvent('mouseenter', parent, e);
			}
		});
	}
});

Moo.setSelector({
	'Moo.Panel': 'moo-x-panel'
});
Moo.Panel = new Class({
	Extends: Moo.Box,
	$componentType: 'panel',
	$componentSelector: Moo.getSelector('Moo.Panel'),
	$theme: {
		topLeft: '-header-left',
		topCenter: '-header-center',
		topRight: '-header-right',
		middleLeft: '-body-left',
		middleCenter: '-body-center',
		middleRight: '-body-right',
		bottomLeft: '-footer-left',
		bottomCenter: '-footer-center',
		bottomRight: '-footer-right',
		collapsed: '-collapsed',
		noHeader: '-noheader',
		title: '-title',
		bodyWrapper: '-body-wrapper'
	},
	options: {
		containerCls: 'moo-panel',
		width: 'auto',
		height: 'auto',
		showHeader: true,
		title: null,
		iconCls: '',
		collapsible: false,
		collapser: 'icon',
		collapsed: false,
		collapseDuration: null,
		collapseTransition: null,
		collapseUpCls: Moo.Icons.up,
		collapseDownCls: Moo.Icons.down,
		body: null,
		bodyCls: '',
		headerTools: null,
		onBeforeCollapse: $empty,
		onBeforeExpand: $empty,
		onCollapse: $empty,
		onExpand: $empty
	},
	initialize: function(options) {
		this.parent(options);
		if (this.options.collapseDuration == null) this.options.collapseDuration = Moo.Features.fxDuration;
		this.container.setStyle('width', this.options.width);
		if (this.options.showHeader === true) {
			this.headerContainer = this.boxTopCenter;
			this.titleContainer = new Element('div').addClass(this.theme.title).inject(this.headerContainer);
			this.setTitle(this.options.title);
			if (this.options.iconCls != null) Moo.inject(new Moo.Icon({
				containerCls: this.options.iconCls
			}), this.titleContainer, 'top');
			delete this.options.iconCls;
		}
		this.bodyWrapper = new Element('div').addClass(this.theme.bodyWrapper);
		this.bodyWrapper.adopt(this.boxMiddleLeft, this.boxBottomLeft);
		this.bodyWrapper.inject(this.container);
		Moo.Fx.prepareOverflow(this.mainContainer);
		if (this.options.body) this.add(this.options.body);
		delete this.options.body;
		if (this.options.showHeader === true) {
			if (this.options.collapsible === true) {
				this.collapseFx = new Fx.Slide(this.bodyWrapper, {
					duration: this.options.collapseDuration,
					transition: this.options.collapseTransition,
					link: 'cancel',
					onStart: function() {
						if (this.collapseFx.open) {
							this.fireEvent('onBeforeCollapse', this);
						} else {
							this.container.removeClass(this.theme.collapsed);
							this.fireEvent('onBeforeExpand', this);
						}
					}.safeBind(this),
					onComplete: function() {
						if (this.options.collapser != 'title') {
							if (this.collapseFx.open) {
								this.collapseEl.replaceClass(this.options.collapseDownCls, this.options.collapseUpCls);
								if (this.collapseEl.retrieve('mousein') === true) this.collapseEl.replaceClass(this.options.collapseDownCls + '-over', this.options.collapseUpCls + '-over');
							} else {
								this.collapseEl.replaceClass(this.options.collapseUpCls, this.options.collapseDownCls);
								if (this.collapseEl.retrieve('mousein') === true) this.collapseEl.replaceClass(this.options.collapseUpCls + '-over', this.options.collapseDownCls + '-over');
							}
						}
						if (this.collapseFx.open) {
							this.fireEvent('onExpand', this);
						} else {
							this.container.addClass(this.theme.collapsed);
							this.fireEvent('onCollapse', this);
						}
					}.safeBind(this)
				});
				if (Moo.Fx.overflowable) {
					this.addEvent('onBeforeCollapse', function() {
						Moo.Fx.fixOverflow(this.mainContainer);
					}.safeBind(this));
					this.addEvent('onExpand', function() {
						Moo.Fx.unfixOverflow(this.mainContainer);
					});
				}
				switch (this.options.collapser) {
					case 'title':
						this.collapseEl = this.titleContainer;
						this.collapseEl.addClass('moo-clickable').addEvent('click', function() {
							this.toggleSlide();
						}.safeBind(this));
						break;
					default:
						this.collapseIcon = this.addHeaderTool({
							mooType: 'icon',
							containerCls: this.options.collapseUpCls,
							containerExtraCls: 'moo-icon',
							overable: false,
							onClick: this.toggleSlide.safeBind(this)
						});
						this.collapseEl = this.collapseIcon.container;
						this.collapseEl.addEvents({
							mouseover: function() {
								this.collapseEl.store('mousein', true);
								if (this.collapseFx.open) {
									this.collapseEl.addClass(this.options.collapseUpCls + '-over');
								} else {
									this.collapseEl.addClass(this.options.collapseDownCls + '-over');
								}
							}.safeBind(this),
							mouseout: function() {
								this.collapseEl.store('mousein', false);
								if (this.collapseFx.open) {
									this.collapseEl.removeClass(this.options.collapseUpCls + '-over');
								} else {
									this.collapseEl.removeClass(this.options.collapseDownCls + '-over');
								}
							}.safeBind(this)
						});
						break;
				}
				if (this.collapseFx) this.collapseFx.addEvent('complete', function() {
					if (this.open) this.wrapper.setStyle('height', 'auto');
				});
			}
		} else {
			this.addClass(this.theme.noHeader);
		}
		switch ($type(this.options.headerTools)) {
			case 'array':
				this.options.headerTools.each(this.addHeaderTool, this);
				break;
			case 'object':
				this.addHeaderTool(this.options.headerTools);
				break;
			default:
				break;
		}
	},
	inject: function(container, where) {
		this.parent(container, where);
		if (!this.$collapseFix && this.options.collapsible === true && this.options.collapsed === true) {
			if (Moo.Fx.overflowable && this.collapseFx.open) {
				Moo.Fx.fixOverflow(this.mainContainer);
			}
			this.collapseFx.hide();
			this.container.addClass(this.theme.collapsed);
			this.collapseEl.replaceClass(this.options.collapseUpCls, this.options.collapseDownCls);
			this.$collapseFix = true;
		}
		return this;
	},
	setTitle: function(title) {
		if (this.options.showHeader === true) {
			Moo.Dom.setText(title, this.titleContainer);
		}
		return this;
	},
	setBody: function(body) {
		return this.update(body);
	},
	toggleSlide: function() {
		if (this.collapseFx) this.collapseFx.toggle();
		return this;
	},
	collapse: function() {
		if (this.collapseFx && this.collapseFx.open) this.collapseFx.slideOut();
		return this;
	},
	expand: function() {
		if (this.collapseFx && !this.collapseFx.open) this.collapseFx.slideIn();
		return this;
	},
	isCollapsible: function() {
		return this.options.collapsible;
	},
	isCollapsed: function() {
		return (this.collapseFx && !this.collapseFx.open);
	},
	addHeaderTool: function(options) {
		var cmp = null;
		if (this.titleContainer) {
			if (options.onClick) options.onClick = options.onClick.addArguments(this);
			cmp = Moo.create(options);
			cmp.setStyle('float', 'right');
			cmp.inject(this.titleContainer, 'before');
		}
		return cmp;
	}
});
Moo.registerType('panel', Moo.Panel);

Moo.PanelGroup = new Class({
	$panels: [],
	initialize: function() {
		this.onBeforeExpand = function(panel) {
			this.$panels.each(function(p) {
				if (p != panel) p.collapse();
			});
		}.safeBind(this);
		Array.flatten(arguments).each(this.add, this);
	},
	add: function(panel) {
		panel.addEvent('onBeforeExpand', this.onBeforeExpand);
		this.$panels.include(panel);
		return this;
	},
	remove: function(panel) {
		panel.removeEvent('onBeforeExpand', this.onBeforeExpand);
		this.$panels.erase(panel);
		return this;
	}
});

Moo.setSelector({
	'Moo.ProgressBar': 'moo-x-progressbar'
});
Moo.ProgressBar = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$componentType: 'progressbar',
	$componentSelector: Moo.getSelector('Moo.ProgressBar'),
	$theme: {
		progress: '-progress',
		text: '-text'
	},
	options: {
		containerCls: 'moo-progressbar',
		value: 0,
		showText: true,
		getText: function(value, instance) {
			return (value * 100) + '%';
		},
		onUpdate: $empty
	},
	$value: null,
	initialize: function(options) {
		this.parent(options);
		this.initializeTheme();
		this.setWidth(this.options.width);
		this.progressEl = new Element('div').inject(this.container).addClass(this.theme.progress).set('html', '&nbsp;');
		this.textEl = new Element('span').injectTop(this.container).addClass(this.theme.text);
		this.showText(this.options.showText);
		this.setValue(this.options.value, false);
	},
	getValue: function() {
		return this.$value;
	},
	setValue: function(value, fireUpdate) {
		this.$value = parseFloat(Moo.toNumberMinMax(value, 0, 1, 0).toFixed(2), 10);
		this.progressEl.setStyle('width', (this.$value * 100) + '%');
		if (this.$showText) this.setText(this.options.getText(this.$value, this));
		if (Moo.toBoolean(fireUpdate, true)) this.fireEvent('onUpdate', [this.$value, this]);
		return this;
	},
	showText: function(show) {
		this.$showText = Moo.toBoolean(show, true);
		this.textEl[this.$showText ? 'show' : 'hide']();
		return this;
	},
	setText: function(text) {
		Moo.update(text, this.textEl);
		return this;
	},
	reset: function(fireUpdate) {
		this.setValue(0, fireUpdate);
		return this;
	},
	startAnim: function(options) {
		this.stopAnim();
		this.$animOptions = $merge({
			value: 0,
			increment: 0.1,
			delay: 300,
			fireUpdate: true,
			onStop: $empty
		}, options);
		this.$animTimer = this.autoUpdate.delay(this.$animOptions.delay, this);
		return this;
	},
	autoUpdate: function() {
		if (this.$animOptions) {
			this.setValue(this.$animOptions.value, this.$animOptions.fireUpdate);
			this.$animOptions.value += this.$animOptions.increment;
			if (this.$animOptions.value > 1) this.$animOptions.value = 0;
			this.$animTimer = this.autoUpdate.delay(this.$animOptions.delay, this);
		}
		return this;
	},
	stopAnim: function() {
		if (this.$animOptions) {
			$clear(this.$animTimer);
			this.$animOptions.onStop(this.$value, this);
			this.$animOptions = null;
		}
		return this;
	}
});
Moo.registerType('progressbar', Moo.ProgressBar);

Moo.namespace('Moo.window');

Moo.window.WindowManager = {
    $cache : new Hash(),
    $zIndex : 1000,
    ordered : [],
    ok : false,
    
    windowKeyboardListenerFn : function(event) {
	    if (event.code == 27) {
		    if (Moo.window.WindowManager.ordered && Moo.window.WindowManager.ordered.length > 0) {
			    Moo.window.WindowManager.ordered[Moo.window.WindowManager.ordered.length - 1].close();
		    }
		    event.stop();
	    }
    },
    
    add : function(window) {
	    if (!Moo.window.WindowManager.ok) {
		    Moo.addKeydown(Moo.window.WindowManager.windowKeyboardListenerFn.bind(Moo.window.WindowManager));
		    Moo.window.WindowManager.ok = true;
	    }
	    window.$winUID = '' + $time();
	    window.setZIndex(this.getZIndexMax() + 1);
	    this.$cache.set(window.$winUID, window);
    },
    getZIndexMax : function() {
	    var zIndex = this.$zIndex;
	    this.$cache.each(function(window) {
		    zIndex = Math.max(zIndex, window.getZIndex());
	    });
	    return zIndex;
    },
    remove : function(window) {
	    var newOrdered = [];
	    for ( var i = 0; i < Moo.window.WindowManager.ordered.length; i++) {
		    if (Moo.window.WindowManager.ordered[i].$winUID != window.$winUID) {
			    newOrdered.push(Moo.window.WindowManager.ordered[i]);
		    }
	    }
	    Moo.window.WindowManager.ordered = newOrdered;
	    if (Moo.window.WindowManager.ordered.length ==0) {
	    	 Moo.removeKeydown(Moo.window.WindowManager.windowKeyboardListenerFn.bind(Moo.window.WindowManager));
	    }
	    this.$cache.erase(window.$winUID);
    },
    bringToFront : function(window) {
	    var max = this.getZIndexMax();
	    if (window.getZIndex() < max)
		    window.setZIndex(max + 1);
	    if (Moo.window.WindowManager.ordered && Moo.window.WindowManager.ordered.length > 0) {
		    if (Moo.window.WindowManager.ordered[Moo.window.WindowManager.ordered.length - 1].$winUID != window.$winUID) {
			    this.ordered.push(window);
		    }
	    } else {
		    this.ordered.push(window);
	    }
    },
    getWindows : function(groupName) {
	    return (groupName ? this.$cache.filter(function(window) {
		    return window.options.groupName == groupName;
	    }) : this.$cache);
    },
    closeAll : function(groupName) {
	    this.getWindows(groupName).each(function(window) {
		    window.close();
	    });
    },
    organize : function(groupName) {
	    var i = 0;
	    this.getWindows(groupName).each(function(window) {
		    window.container.setStyles({
		        top : 100 + (i * 30),
		        left : 150 + (i * 30)
		    });
		    window.setZIndex(this.$zIndex + i);
		    i++;
	    }, this);
    },
    showAll : function(groupName) {
	    this.getWindows(groupName).each(function(window) {
		    window.show();
	    });
    },
    hideAll : function(groupName) {
	    this.getWindows(groupName).each(function(window) {
		    window.hide();
	    });
    },
    fadeAll : function(how, groupName) {
	    this.getWindows(groupName).each(function(window) {
		    window.fade(how);
	    });
    }
};

Moo.setSelector({
	'Moo.Window': 'moo-x-window'
});
Moo.window.Window = new Class({
	Extends: Moo.Panel,
	$componentType: 'window',
	$componentSelector: Moo.getSelector('Moo.Window'),
	options: {
		containerCls: 'moo-window',
		width: 400,
		animateEl: null,
		draggable: true,
		closable: true,
		destroyable: true,
		modal: false,
		autoCenter: false,
		openDuration: null,
		openTransition: null,
		closeDuration: null,
		closeTransition: null,
		openOnInitialize: false,
		zIndexMax: null,
		groupName: null,
		onBeforeOpen: $empty,
		onOpen: $empty,
		onBeforeClose: $empty,
		onClose: $empty,
		onBeforeDrag: $empty,
		onDrag: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.options.collapser = 'icon';
		if (this.options.openDuration == null) this.options.openDuration = Moo.Features.fxDuration;
		if (this.options.closeDuration == null) this.options.closeDuration = Moo.Features.fxDuration;
    	this.container.setStyles({
    		position: 'absolute',
    		top: 0,
    		left: 0
    	}).hide();
		this.setAnimateEl(this.options.animateEl);
		delete this.options.animateEl;
		if (this.options.showHeader === true) {
			if (this.options.closable === true) {
				this.closeIcon = this.addHeaderTool({
					mooType: 'icon',
					containerCls: Moo.Icons.close,
					containerExtraCls: 'moo-icon',
					onClick: this.close.safeBind(this)
				});
				this.closeEl = this.closeIcon.container;
			}
		}
		if (this.options.modal === true) {
			this.mask = new Moo.Mask();
			this.addEvents({
				beforeInject: this.mask.inject.safeBind(this.mask),
				beforeOpen: this.mask.show.safeBind(this.mask),
				close: this.mask.hide.safeBind(this.mask),
				destroy: this.mask.destroy.safeBind(this.mask)
			});
		}
		if (this.options.autoCenter === true) {
			this.center = this.center.safeBind(this);
			Moo.addResize(this.center);
			this.addEvent('destroy', function() {
				Moo.removeResize(this.center);
			}.safeBind(this));
		}
		Moo.window.WindowManager.add(this);
		this.container.addEvent('mousedown', function() {
			this.toFront();
		}.safeBind(this));
		this.inject(document.body);
		this.isOpen = false;
		if (this.options.openOnInitialize == true) this.open();
	},
	inject: function(container, where) {
		this.parent(container, where);
		if (!this.dragFix && this.options.draggable === true) {
			this.headerContainer.setStyle('cursor', 'move');
			this.container.makeDraggable({
				handle: this.headerContainer,
				onStart: function() {
					Moo.Fx.fixOverflow(this.mainContainer);
					this.fireEvent('onBeforeDrag');
					this.setBodyVisible(false);
					this.toFront();
					this.setOpacity(0.7);
				}.safeBind(this),
				onComplete: function() {
					Moo.Fx.unfixOverflow(this.mainContainer);
					this.fireEvent('onDrag');
					this.setBodyVisible(true);
					this.setOpacity(1);
				}.safeBind(this),
				limit: {
					x: [0],
					y: [0]
				}
			});
			this.dragFix = true;
		}
		return this;
	},
	setAnimateEl: function(animateEl) {
		switch($type(animateEl)) {
			case 'string':
				if ($(animateEl)) this.animateEl = $(animateEl);
				else this.animateEl = $(document.body);
				break;
			case 'element':
				this.animateEl = $(animateEl);
				break;
			default:
				this.animateEl = $(document.body);
				break;
		}
		return this;
	},
	open: function(options) {
		options = $merge({
			duration: null,
			transition: null,
			animateEl: null,
			start: null,
			end: null
		}, options);
		if (options.duration != null) this.options.openDuration = options.duration;
		if (options.transition != null) this.options.openTransition = options.openTransition;
		this.setStyles({
			opacity: 0.001,
			top: 0,
			left: 0
		}).show();
		this.toFront();
		this.doLayout();
		var selfCoord = this.container.getCoordinates();
		var animateElCoord = null;
		var styles = {left: 10, top: 10, width: 10, height: 10};
		if (this.animateEl === document.body) animateElCoord = styles;
		else animateElCoord = this.animateEl.getCoordinates();
		var centeredPos = Moo.Dom.getCenteredPosition(this.container);
		if (options.start != null) {
			options.start = $extend({
				left: options.start.left || animateElCoord.left,
				top: options.start.top || animateElCoord.top,
				height: 10,
				width: 10
			}, options.start);
		} else {
			options.start = $extend({
				left: animateElCoord.left,
				top: animateElCoord.top,
				height: animateElCoord.height,
				width: animateElCoord.width
			}, options.start);
		}
		if (options.end != null) {
			options.end = $extend({
				left: options.end.left || centeredPos.left,
				top: options.end.top || centeredPos.top,
				height: selfCoord.height,
				width: selfCoord.width
			}, options.end);
		} else {
			options.end = $extend({
				left: centeredPos.left,
				top: centeredPos.top,
				height: selfCoord.height,
				width: selfCoord.width
			}, options.end);
		}
		var morphOptions = {
			left: [options.start.left, options.end.left],
			top: [options.start.top, options.end.top],
			height: [options.start.height, options.end.height],
			width: [options.start.width, options.end.width]
		};
		this.container.setStyles({
			height: options.start.height,
			width: options.start.width,
			left: options.start.left,
			top: options.start.top
		});
		var onMorphStart = function() {
			if (this.options.openDuration != 0) this.container.setStyle('opacity', 1);
			this.fireEvent('onBeforeOpen');
			Moo.Fx.fixOverflow(this.mainContainer);
			this.setBodyVisible(false);
			this.showHeaderIcons(false);
			this.focus();
			this.container.setStyle('overflow', 'hidden');
			this.bodyWrapper.setStyle('height', '100%');
		}.safeBind(this);
		var onMorphComplete = function() {
			if (this.options.openDuration == 0) this.container.setStyle('opacity', 1);
			Moo.Fx.unfixOverflow(this.mainContainer);
			this.setBodyVisible(true);
			this.showHeaderIcons(true);
			this.setStyles({
				width: this.options.width,
				height: 'auto'
			});
			this.isOpen = true;
			this.fireEvent('onOpen');
			this.container.setStyle('overflow', '');
			this.bodyWrapper.setStyle('height', '');
		}.safeBind(this);
		this.container.set('morph', {
			duration: this.options.openDuration,
			transition: this.options.openTransition,
			onStart: onMorphStart,
			onComplete: onMorphComplete
		}).morph(morphOptions);
		return this;
	},
	close: function() {
		if (this.options.closable === true) {
			var animateElCoord = null;
			var selfCoord = this.container.getCoordinates();
			this.container.setStyles({
				height: selfCoord.height,
				overflow: 'hidden'
			});
			if (this.animateEl === document.body) animateElCoord = {left: 10, top: 10, width: 10, height: 10};
			else animateElCoord = this.animateEl.getCoordinates();
			var morphOptions = {
				left: animateElCoord.left,
				top: animateElCoord.top,
				width: [selfCoord.width, animateElCoord.width],
				height: [selfCoord.height, animateElCoord.height]
			};
			var onMorphStart = function() {
				this.fireEvent('onBeforeClose');
				Moo.Fx.fixOverflow(this.mainContainer);
				this.setBodyVisible(false);
				this.showHeaderIcons(false);
				this.container.setStyle('overflow', 'hidden');
				this.bodyWrapper.setStyle('height', '100%');
			}.safeBind(this);
			var onMorphComplete = function() {
				this.hide();
				this.setBodyVisible(true);
				this.showHeaderIcons(true);
				this.container.setStyle('overflow', '');
				this.bodyWrapper.setStyle('height', '');
				Moo.Fx.unfixOverflow(this.mainContainer);
				this.setStyles({
					width: this.options.width,
					height: 'auto'
				});
				if (this.options.destroyable === true) {
					Moo.window.WindowManager.remove(this);
					this.destroy();
				}
				this.isOpen = false;
				this.fireEvent('onClose');
			}.safeBind(this);
			this.container.set('morph', {
				duration: this.options.closeDuration,
				transition: this.options.closeTransition,
				onStart: onMorphStart,
				onComplete: onMorphComplete
			}).morph(morphOptions);
		}
		return this;
	},
	setZIndex: function(zIndex) {
		if (this.options.zIndexMax != null) zIndex = Math.min(zIndex, this.options.zIndexMax);
		if (this.mask) this.mask.setZIndex(zIndex);
		this.container.setStyle('z-index', zIndex);
		return this;
	},
	getZIndex: function() {
		return this.container.getStyle('z-index');
	},
	focus: function() {
		var els = this.mainContainer.getElements('input, textarea, select, a');
		return Moo.Dom.focus(els);
	},
	setBodyVisible: function(visibility) {
		this.mainContainer.setStyle('visibility', (visibility === true ? '' : 'hidden'));
		return this;
	},
	showHeaderIcons: function(mode) {
		this.headerContainer.getElements('a, img').setStyle('display', (mode === false ? 'none' : ''));
		return this;
	},
	center: function() {
		Moo.Dom.center(this.container);
		return this;
	},
	toFront: function() {
		Moo.window.WindowManager.bringToFront(this);
		return this;
	}
});
Moo.Window = Moo.window.Window;

Moo.window.Alert = new Class({
	Extends: Moo.Window,
	options: {
		title: 'Alert',
		iconCls: Moo.Icons.warning,
		openDuration: 0,
		closeDuration: 0,
		okText: 'moo.commons.ok',
		okCls: 'moo-button'
	},
	initialize: function(options) {
		if (typeof options == 'string') options = {body: options};
		else if (!options) options = {};
		options = $merge(options || {}, {
			modal: true,
			destroyable: true,
			closable: true
		});
		this.parent(options);
		this.okButton = new Moo.Button({
			text: Moo.getLang(this.options.okText),
			containerCls: this.options.okCls,
			onClick: this.onOkClick.safeBind(this)
		});
		this.buttonsToolbar = new Moo.Toolbar({
			autoFit: true,
			items: [this.okButton]
		});
		this.add(this.buttonsToolbar);
		this.addEvent('onOpen', this.onOpen.safeBind(this));
		this.open();
	},
	onOkClick: function() {
		this.close();
		return this;
	},
	onOpen: function() {
		this.okButton.focus();
	}
});
Moo.implement(Moo, {
	alert: function(options) {
		return new Moo.window.Alert(options);
	}
});

Moo.window.Confirm = new Class({
	Extends: Moo.window.Alert,
	options: {
		title: 'Confirm',
		cancelText: 'moo.commons.cancel',
		cancelCls: 'moo-button',
		confirmFn: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.cancelButton = new Moo.Button({
			text: Moo.getLang(this.options.cancelText),
			containerCls: this.options.cancelCls,
			onClick: this.onCancelClick.safeBind(this)
		});
		this.buttonsToolbar.add(this.cancelButton);
	},
	onOkClick: function() {
		this.options.confirmFn(this);
		this.close();
		return this;
	},
	onOpen: function() {
		this.cancelButton.focus();
	},
	onCancelClick: function() {
		this.close();
		return this;
	}
});
Moo.implement(Moo, {
	confirm: function(options) {
		return new Moo.window.Confirm(options);
	}
});

Moo.window.Selector = new Class({
	Extends: Moo.Window,
	Implements: Moo.Langable,
	$componentType: 'window-selector',
	$lang: {
		search: 'moo.commons.search',
		clear: 'moo.commons.clear',
		loading: 'moo.commons.loading',
		noData: 'moo.commons.no_data',
		tooManyData: 'moo.commons.too_many_data'
	},
	options: {
		modal: true,
		url: null,
		data: [],
		params: {},
		fields: [],
		startSearchOnOpen: true,
		startSearchOnEnter: true,
		showClearButton: false,
		onSelect: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.initializeLang();
		this.fieldset = new Moo.form.Fieldset({
			items: this.options.fields
		});
		this.add(this.fieldset);
		this.searchButton = new Moo.Button({
			text: this.lang.search,
			onClick: this.startSearch.safeBind(this)
		});
		var toolbarItems = ['>', this.searchButton];
		if (this.options.showClearButton == true) {
			this.clearButton = new Moo.Button({
				text: this.lang.clear,
				onClick: this.clearFields.safeBind(this)
			});
			toolbarItems.push('', this.clearButton);
		}
		this.toolbar = new Moo.Toolbar({
			items: toolbarItems
		});
		this.add(this.toolbar);
		this.resultContainer = new Element('div').addClass('moo-selector-result');
		this.add(this.resultContainer);
		Moo.Fx.prepareOverflow(this.resultContainer);
		if (this.options.startSearchOnOpen == true) this.addEvent('onOpen', this.startSearch.safeBind(this));
		if (this.options.startSearchOnEnter == true) {
			var els = $$(this.fieldset.container.getElements('input[type=text]'));
			els.addEvent('keyup', function(event) {
				if (event.key == 'enter') this.startSearch();
			}.bindWithEvent(this));
		}
	},
	clearFields: function() {
		var fields = this.fieldset.getComponents('.' + Moo.getSelector('Moo.Field'));
		for (var i = 0, l = fields.length; i < l; i++) {
			if (fields[i].clear) fields[i].clear();
		}
		return this;
	},
	hideMessage: function() {
		if (this.msgContainer) this.msgContainer.hide();
		return this;
	},
	showMessage: function(msg) {
		if (!this.msgContainer) this.msgContainer = new Element('div').addClass('moo-selector-message').injectBefore(this.resultContainer);
		this.msgContainer.empty().set('html', msg).show();
		return this;
	},
	startSearch: function() {
		this.showMessage(this.lang.loading);
		Moo.inject(new Moo.Icon({
			containerCls: Moo.Icons.loader,
			overable: false
		}), this.msgContainer, 'top');
		if (this.options.url == null) {
			this.createResult(this.options.datas);
			this.hideMessage();
		} else {
			if (!this.request) this.request = new Request.JSON({
				url: this.options.url,
				onComplete: function(jsonResponse) {
					switch (jsonResponse.statut) {
						case 'NO_DATA':
							this.showMessage(this.lang.noData);
							Moo.empty(this.resultContainer);
							break;
						case 'TOO_MANY_DATA':
							this.showMessage(this.lang.tooManyData);
							Moo.empty(this.resultContainer);
							break;
						default:
							this.createResult(jsonResponse.datas);
							this.hideMessage(this.resultContainer);
							break;
					}
				}.safeBind(this)
			});
			this.request.send(Moo.toQueryString(this.options.params, this.fieldset.container));
		}
	},
	createResult: function(datas) {
		var data = null, items = [], item = null;
		for (var i = 0, l = datas.length; i < l; i++) {
			data = datas[i];
			item = new Element('div', {text: data.label}).addEvent('click', function(e, d) {
				this.fireEvent('select', d);
				this.close();
			}.bindWithEvent(this, data)).addClass('moo-item');
			if (i % 2 == 0) item.addClass('moo-item-even');
			item.addClassOn('over', 'moo-item-over');
			items.push(item);
		}
		Moo.update(items, this.resultContainer);
	}
});

Moo.window.ListSelector = new Class({
	Extends: Moo.Window,
	Implements: Moo.Langable,
	$componentType: 'window-listselector',
	$lang: {
		maxElements: 'moo.commons.max_elements',
		search: 'moo.commons.search',
		clear: 'moo.commons.clear',
		validate: 'moo.commons.validate',
		loading: 'moo.commons.loading',
		noData: 'moo.commons.no_data',
		tooManyData: 'moo.commons.too_many_data'
	},
	options: {
		modal: true,
		url: null,
		datas: [],
		selectedDatas: [],
		params: {},
		fields: [],
		startSearchOnOpen: true,
		startSearchOnEnter: true,
		showClearButton: false,
		maxElements: 200,
		allLeftToRightCls: Moo.Icons.dblright,
		leftToRightCls: Moo.Icons.right,
		allRightToLeftCls: Moo.Icons.dblleft,
		rightToLeftCls: Moo.Icons.left,
		onValidate: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.initializeLang();
		this.fieldset = new Moo.form.Fieldset({
			items: this.options.fields
		});
		this.add(this.fieldset);
		this.searchButton = new Moo.Button({
			text: this.lang.search,
			onClick: this.startSearch.safeBind(this)
		});
		var toolbarItems = ['>', this.searchButton];
		if (this.options.showClearButton == true) {
			this.clearButton = new Moo.Button({
				text: this.lang.clear,
				onClick: this.clearFields.safeBind(this)
			});
			toolbarItems.push('', this.clearButton);
		}
		this.toolbar = new Moo.Toolbar({
			items: toolbarItems
		});
		this.add(this.toolbar);
		this.resultContainer = new Element('div').addClass('moo-listselector-result');
		this.add(this.resultContainer);
		var selectExt = {
			addOptions: function(options) {
				options.each(function(option) {
					new Element('option', {value: option.value, text: option.label}).inject(this);
				}.safeBind(this));
			}
		};
		this.leftSelect = new Element('select', {multiple: true});
		this.leftSelect = $extend(this.leftSelect, selectExt);
		this.rightSelect = new Element('select', {multiple: true});
		this.rightSelect = $extend(this.rightSelect, selectExt);
		this.allLeftToRightButton = new Moo.Icon({
			containerCls: this.options.allLeftToRightCls,
			containerExtraCls: ['moo-icon', 'moo-listselector-button'],
			onClick: this.allLeftToRight.safeBind(this)
		});
		this.leftToRightButton = new Moo.Icon({
			containerCls: this.options.leftToRightCls,
			containerExtraCls: ['moo-icon', 'moo-listselector-button'],
			onClick: this.leftToRight.safeBind(this)
		});
		this.allRightToLeftButton = new Moo.Icon({
			containerCls: this.options.allRightToLeftCls,
			containerExtraCls: ['moo-icon', 'moo-listselector-button'],
			onClick: this.allRightToLeft.safeBind(this)
		});
		this.rightToLeftButton = new Moo.Icon({
			containerCls: this.options.rightToLeftCls,
			containerExtraCls: ['moo-icon', 'moo-listselector-button'],
			onClick: this.rightToLeft.safeBind(this)
		});
		this.selectsLayout = new Moo.Layout({
			width: '100%',
			cellspacing: 3,
			tableCls: '',
			rows: [{
				cells: [{
					rowspan: 4,
					content: this.leftSelect
				}, {
					align: 'center',
					cls: '',
					content: this.allLeftToRightButton
				}, {
					rowspan: 4,
					content: this.rightSelect
				}]
			}, {
				cells: [{align: 'center', cls: '', content: this.leftToRightButton}]
			}, {
				cells: [{align: 'center', cls: '', content: this.rightToLeftButton}]
			}, {
				cells: [{align: 'center', cls: '', content: this.allRightToLeftButton}]
			}]
		}).inject(this.resultContainer);
		this.validateButton = new Moo.Button({
			text: this.lang.validate,
			onClick: this.validate.safeBind(this)
		});
		this.bottomToolbar = new Moo.Toolbar({
			items: ['>', this.validateButton]
		});
		this.add(this.bottomToolbar);
		if (this.options.startSearchOnOpen == true) this.addEvent('onOpen', this.startSearch.safeBind(this));
		if (this.options.startSearchOnEnter == true) {
			var els = $$(this.fieldset.container.getElements('input[type=text]'));
			els.addEvent('keyup', function(event) {
				if (event.key == 'enter') this.startSearch();
			}.bindWithEvent(this));
		}
		this.lang.maxElements = this.lang.maxElements.format(this.options.maxElements);
		var datas = this.options.selectedDatas, data = null, items = [], item = null;
		for (var i = 0, l = datas.length; i < l; i++) {
			data = datas[i];
			item = new Element('option', {text: data.label, value: data.value});
			items.push(item);
		}
		Moo.update(items, this.rightSelect);
	},
	clearFields: function() {
		var fields = this.fieldset.getComponents('.' + Moo.getSelector('Moo.Field'));
		for (var i = 0, l = fields.length; i < l; i++) {
			if (fields[i].clear) fields[i].clear();
		}
		return this;
	},
	hideMessage: function() {
		if (this.msgContainer) this.msgContainer.hide();
		return this;
	},
	showMessage: function(msg) {
		if (!this.msgContainer) this.msgContainer = new Element('div').addClass('moo-selector-message').injectBefore(this.resultContainer);
		this.msgContainer.empty().set('html', msg).show();
		return this;
	},
	startSearch: function() {
		this.showMessage(this.lang.loading);
		Moo.inject(new Moo.Icon({
			containerCls: Moo.Icons.loader,
			overable: false
		}), this.msgContainer, 'top');
		if (this.options.url == null) {
			this.createResult(this.options.datas);
			this.hideMessage();
		} else {
			if (!this.request) this.request = new Request.JSON({
				url: this.options.url,
				onComplete: function(jsonResponse) {
					switch (jsonResponse.statut) {
						case 'NO_DATA':
							this.showMessage(this.lang.noData);
							Moo.empty(this.leftSelect);
							break;
						case 'TOO_MANY_DATA':
							this.showMessage(this.options.tooManyData);
							Moo.empty(this.leftSelect);
							break;
						default:
							this.createResult(jsonResponse.datas);
							this.hideMessage();
							break;
					}
				}.safeBind(this)
			});
			this.request.send(Moo.toQueryString(this.options.params, this.fieldset.container));
		}
	},
	validate: function() {
		var selected = [];
		$each($A(this.rightSelect.options), function(item) {
			selected.push({label: item.text, value: item.value});
		});
		this.fireEvent('validate', [selected]);
		this.close();
	},
	createResult: function(datas) {
		var data = null, items = [], item = null;
		for (var i = 0, l = datas.length; i < l; i++) {
			data = datas[i];
			item = new Element('option', {text: data.label, value: data.value});
			items.push(item);
		}
		Moo.update(items, this.leftSelect);
	},
	allLeftToRight: function() {
		$each($A(this.leftSelect.options), function(option) {
			option.selected = true;
		});
		this.leftToRight();
	},
	leftToRight: function() {
		if (this.leftSelect.getSelected().length + this.rightSelect.options.length <= this.options.maxElements) {
			var selectedOptions = [];
			var notSelectedOptions = [];
			$each($A(this.leftSelect.options), function(item) {
				if (item.selected) {
					selectedOptions.push({label: item.text, value: item.value});
				} else {
					notSelectedOptions.push({label: item.text, value: item.value});
				}
			});
			this.leftSelect.empty().addOptions(notSelectedOptions);
			this.rightSelect.addOptions(selectedOptions);
			this.addDblClick();
			this.hideMessage();
		} else {
			this.showMessage(this.lang.maxElements);
		}
	},
	allRightToLeft: function() {
		$each($A(this.rightSelect.options), function(option) {
			option.selected = true;
		});
		this.rightToLeft();
	},
	rightToLeft: function() {
		var selectedOptions = [];
		var notSelectedOptions = [];
		$each($A(this.rightSelect.options), function(item) {
			if (item.selected) {
				selectedOptions.push({label: item.text, value: item.value});
			} else {
				notSelectedOptions.push({label: item.text, value: item.value});
			}
		});
		this.rightSelect.empty().addOptions(notSelectedOptions);
		this.leftSelect.addOptions(selectedOptions);
		this.addDblClick();
	},
	addDblClick: function() {
		var options = this.leftSelect.getElements('option');
		options.removeEvent('dblclick').addEvent('dblclick', this.leftToRight.safeBind(this));
		options = this.rightSelect.getElements('option');
		options.removeEvent('dblclick').addEvent('dblclick', this.rightToLeft.safeBind(this));
	}
});

Moo.namespace('Moo.tabs');
Moo.setSelector({
	'Moo.Tabs': 'moo-x-tabs',
	'Moo.Tabs.Toggler': 'moo-x-tabs-toggler',
	'Moo.Tabs.TogglerActive': 'moo-x-tabs-toggler-active'
});

Moo.tabs.Tabs = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$theme: {
		togglers: '-togglers',
		cards: '-cards'
	},
	$componentType: 'tabs',
	$componentSelector: Moo.getSelector('Moo.Tabs'),
	options: {
		containerCls: 'moo-tabs',
		display: 0,
		items: [],
		width: null,
		togglerCls: null,
		togglersStyles: null,
		cardCls: null,
		cardsStyles: null
	},
	initialize: function(options) {
		this.$activeToggler = null;
		this.$cards = null;
		this.parent(options);
		this.initializeTheme();
		this.togglersContainer = new Element('div').addClass(this.theme.togglers).inject(this.container);
		if (this.options.togglersStyles != null) this.togglersContainer.setStyles(this.options.togglersStyles);
		this.togglersList = new Element('ul').inject(this.togglersContainer);
		new Element('div').setStyle('clear', 'both').inject(this.togglersContainer);
		this.$cards = new Moo.Cards({
			containerCls: this.theme.cards,
			cardCls: this.options.cardCls,
			styles: this.options.cardsStyles
		}).inject(this.container);
		this.options.items.each(this.add.safeBind(this));
		delete this.options.items;
		if (this.options.width != null) this.container.setStyle('width', this.options.width);
		this.display(this.options.display);
		delete this.options.display;
	},
	add: function(options) {
		options.tabs = this;
		if (!options.containerCls && this.options.togglerCls) options.containerCls = this.options.togglerCls;
		var toggler = new Moo.tabs.Toggler(options);
		return toggler;
	},
	getTogglers: function() {
		return Moo.$$(Moo.getSelector('Moo.Tabs.Toggler', true), this.togglersList);
	},
	getToggler: function(arg) {
		var toggler = null;
		switch($type(arg)) {
			case 'moo.component':
				toggler = arg;
				break;
			case 'number':
				toggler = this.getTogglers()[arg];
				break;
			case 'string':
				toggler = Moo.$(arg, this.togglersList);
				break;
			default:
				break;
		}
		return toggler;
	},
	getTogglerByKey: function(key) {
		var togglers = this.getTogglers(), toggler = null;
		for (var i = 0, l = togglers.length; i < l && toggler == null; i++) if (togglers[i].getCardKey() == key) toggler = togglers[i];
		return toggler;
	},
	getActiveToggler: function() {
		return Moo.$$('.' + Moo.getSelector('Moo.Tabs.TogglerActive'))[0];
	},
	getActiveIndex: function() {
		var togglers = this.getTogglers(), cls = Moo.getSelector('Moo.Tabs.TogglerActive'), index = -1;
		for (var i = 0, l = togglers.length; i < l && index == -1; i++) if (togglers[i].hasClass(cls)) index = i;
		return index;
	},
	getActiveCard: function() {
		return this.getActiveToggler().$card;
	},
	getCard: function(arg) {
		return this.getToggler(arg).$card;
	},
	getCardByKey: function(key) {
		return this.$cards.getCard(key);
	},
	display: function(arg) {
		var toggler = this.getToggler(arg);
		if (toggler && toggler != this.$activeToggler) {
			if (this.$activeToggler != null) this.$activeToggler.hide();
			toggler.display();
			this.$activeToggler = toggler;
		}
	},
	displayByKey: function(key) {
		this.display(this.getTogglerByKey(key));
		return this;
	},
	close: function(arg, forceClose) {
		var toggler = this.getToggler(arg);
		if (toggler) {
			var displayFirst = (toggler == this.$activeToggler);
			toggler.$close(forceClose);
			if (displayFirst) this.display(0);
		}
	},
	closeByKey: function(key, force) {
		this.close(this.getTogglerByKey(key), force);
		return this;
	},
	contains: function(id) {
		return (this.togglersList.getElement('#' + id) != null);
	},
	containsKey: function(key) {
		return (this.getTogglerByKey(key) != null);
	},
	count: function() {
		return this.getTogglers().length;
	},
	closeAll: function(forceClose) {
		var togglers = this.getTogglers();
		for (var i = 0, l = togglers.length; i < l; i++) this.close(togglers[i], forceClose);
		return this;
	}
});
Moo.registerType('tabs', Moo.tabs.Tabs);
Moo.Tabs = Moo.tabs.Tabs;

Moo.tabs.Toggler = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$theme: {
		left: '-left',
		center: '-center',
		right: '-right',
		active: '-active',
		over: '-over',
		card: '-card'
	},
	$componentType: 'tabs-toggler',
	$componentSelector: Moo.getSelector('Moo.Tabs.Toggler'),
	$containerType: 'li',
	options: {
		tabs: null,
		containerCls: 'moo-tabs-toggler',
		iconCls: null,
		title: '',
		content: null,
		closable: false,
		cardKey: null,
		onDisplay: $empty,
		onHide: $empty,
		onClose: $empty
	},
	initialize: function(options) {
		this.$tabs = options.tabs;
		delete options.tabs;
		this.$cards = this.$tabs.$cards;
		this.$cardKey = null;
		this.$card = null;
		this.parent(options);
		this.initializeTheme();
		this.table = new Element('table', {
			cellpadding: 0,
			cellspacing: 0
		}).inject(this.container);
		this.tBody = new Element('tbody').inject(this.table);
		this.row = new Element('tr').inject(this.tBody);
		this.leftCell = new Element('td', {html: '<em> </em>'}).addClass(this.theme.left).inject(this.row);
		this.centerCell = new Element('td').addClass(this.theme.center).inject(this.row);
		this.rightCell = new Element('td', {html: '<em> </em>'}).addClass(this.theme.right).inject(this.row);
		this.textContainer = new Element('span').inject(this.centerCell);
		this.textContainer.set('text', this.options.title);
		if (this.options.iconCls != null) {
			this.icon = new Moo.Icon({
				containerCls: this.options.iconCls,
				overable: false
			}).inject(this.centerCell, 'top');
		}
		this.container.addEvents({
			click: function() {
				this.$tabs.display(this);
			}.safeBind(this),
			mouseenter: function() {
				if (this != this.$tabs.$activeToggler) this.addClass(this.theme.over);
			}.safeBind(this),
			mouseleave: function() {
				if (this != this.$tabs.$activeToggler) this.removeClass(this.theme.over);
			}.safeBind(this)
		});
		if (this.options.closable === true) {
			this.closeIcon = new Moo.Icon({
				containerCls: 'moo-tabs-close',
				containerExtraCls: 'moo-icon',
				stopEvent: true,
				onClick: this.close.safeBind(this)
			}).inject(this.textContainer);
		}
		if (this.options.titleTooltip) this.container.makeTooltip(this.options.titleTooltip);
		var card = this.$cards.add({
			content: this.options.content,
			cls: this.theme.card,
            key: this.options.cardKey
		});
		delete this.options.content;
		this.$cardKey = card.key;
		this.$card = card.card;
		this.inject(this.$tabs.togglersList);
	},
	hide: function() {
		this.removeClasses(this.theme.over, this.theme.active, Moo.getSelector('Moo.Tabs.TogglerActive'));
		this.fireEvent('onHide', [this, this.$tabs]);
	},
	display: function() {
		this.addClasses(this.theme.active, Moo.getSelector('Moo.Tabs.TogglerActive'));
		this.$cards.display(this.$cardKey);
		this.fireEvent('onDisplay', [this, this.$tabs]);
	},
	$close: function(forceClose) {
		if (this.options.closable || (forceClose)) {
			this.$cards.remove(this.$cardKey);
			this.fireEvent('onClose', [this, this.$tabs]);
			this.destroy();
		}
	},
	close: function(forceClose) {
		this.$tabs.close(this, forceClose);
	},
	setTitle: function(title) {
		Moo.update(title, this.textContainer);
		return this;
	},
	setContent: function(content) {
		Moo.update(content, this.$card);
		return this;
	},
	getCardKey: function() {
		return this.$cardKey;
	}
});

Moo.setSelector({
	'Moo.Menu': 'moo-x-menu'
});
Moo.Menu = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$theme: {
		item: '-item',
		itemOver: '-item-over'
	},
	$componentType: 'menu',
	$componentSelector: Moo.getSelector('Moo.Menu'),
	options: {
		containerCls: 'moo-menu',
		offsets: {x: 0, y: 0},
		closeDelay: 300,
		zIndex: 10000,
		items: [],
		onOpen: $empty,
		onClose: $empty
	},
	initialize: function(opener, options) {
		this.opener = $(opener).addEvents({
			mouseenter: this.open.safeBind(this),
			mouseleave: this.delayedClose.safeBind(this)
		});
		this.parent(options);
		this.initializeTheme();
		this.container.hide().setStyles({
			position: 'absolute',
			'z-index': this.options.zIndex,
			top: 0,
			left: 0
		}).addEvents({
			mouseenter: this.open.safeBind(this),
			mouseleave: this.delayedClose.safeBind(this)
		});
		this.listContainer = new Element('ul').inject(this.container);
		this.setItems(this.options.items);
		delete this.options.items;
		this.inject(document.body);
	},
	open: function() {
		this.cancelDelayedClose();
		var openerCoords = this.opener.getCoordinates();
		this.container.setStyles({
			top: openerCoords.bottom + this.options.offsets.y,
			left: openerCoords.left + this.options.offsets.x
		}).show();
		this.fireEvent('open');
		return this;
	},
	close: function() {
		this.cancelDelayedClose();
		this.container.hide();
		this.fireEvent('close');
		return this;
	},
	delayedClose: function() {
		this.closeTimer = function() {
			this.close();
		}.delay(this.options.closeDelay, this);
		return this;
	},
	cancelDelayedClose: function() {
		$clear(this.closeTimer);
		return this;
	},
	addItem: function(item) {
		item = $merge({
			id: null,
			text: null,
			href: 'javascript:void(0);',
			closeOnClick: true,
			click: null
		},item);
		var li = new Element('li').inject(this.listContainer);
		var link = new Moo.Link({
			id: item.id,
			text: item.text,
			href: item.href,
			containerCls: this.theme.item
		});
		link.container.addClassOn('over', this.theme.itemOver).inject(li);
		if (typeof item.click == 'function') link.container.addEvent('click', item.click);
		if (item.closeOnClick === true) link.container.addEvent('click', this.close.safeBind(this));
	},
	addItems: function(items) {
		items = items || [];
		items.each(function(item) {
			this.addItem(item);
		}, this);
	},
	setItems: function(items) {
		this.listContainer.empty();
		this.addItems(items);
	}
});

Moo.setLang({
	'moo.calendar.today': 'Today',
	'moo.calendar.days': ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
	'moo.calendar.months': ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
});
Moo.setSelector({
	'Moo.Calendar': 'moo-x-calendar'
});
Moo.Calendar = new Class({
	Extends: Moo.Component,
	Implements: [Moo.Langable, Moo.Themeable],
	$componentType: 'calendar',
	$componentSelector: Moo.getSelector('Moo.Calendar'),
	$lang: {
		days: 'moo.calendar.days',
		today: 'moo.calendar.today',
		months: 'moo.calendar.months'
	},
	$theme: {
		header: '-header',
		headerButton: '-header-button',
		title: '-title',
		weekHeader: '-week-header',
		week: '-week',
		weekSelected: '-week-selected',
		dayHeader: '-day-header',
		day: '-day',
		dayActive: '-day-active',
		dayInactive: '-day-inactive',
		daySelected: '-day-selected',
		dayOver: '-day-over',
		today: '-today'
	},
	options: {
		containerCls: 'moo-calendar',
		width: 200,
		date: null,
		showToday: true,
		today: null,
		showWeeks: false,
		week: null,
		weekTooltip: null,
		weekTooltipCls: null,
		previousYearIconCls: Moo.Icons.dblleft,
		previousMonthIconCls: Moo.Icons.left,
		nextYearIconCls: Moo.Icons.dblright,
		nextMonthIconCls: Moo.Icons.right,
		onNextMonth: $empty,
		onPreviousMonth: $empty,
		onNextYear: $empty,
		onPreviousYear: $empty,
		onDayClick: $empty,
		onWeekClick: $empty,
		onTodayClick: $empty
	},
	initialize: function(options) {
		this.$today = null;
		this.$date = null;
		this.$week = null;
		this.$currentYear = null;
		this.$currentMonth = null;
		this.parent(options);
		this.initializeLang();
		this.initializeTheme();
		this.container.setStyle('width', this.options.width);
		var headerTable = new Element('table', {
			cellspacing: 0,
			width: '100%'
		});
		var headerTBody = new Element('tbody').inject(headerTable);
		var headerRow = new Element('tr').addClass(this.theme.header).inject(headerTBody);
		var previousYearCell = new Element('td').addClasses('moo-clickable', this.theme.headerButton);
		this.previousYearIcon = new Moo.Icon({
			containerCls: this.options.previousYearIconCls,
			containerExtraCls: 'moo-icon',
			onClick: this.previousYear.safeBind(this)
		}).inject(previousYearCell);
		previousYearCell.inject(headerRow);
		var previousMonthCell = new Element('td').addClasses('moo-clickable', this.theme.headerButton);
		this.previousMonthIcon = new Moo.Icon({
			containerCls: this.options.previousMonthIconCls,
			containerExtraCls: 'moo-icon',
			onClick: this.previousMonth.safeBind(this)
		}).inject(previousMonthCell);
		previousMonthCell.inject(headerRow);
		this.title = new Element('td').addClass(this.theme.title).inject(headerRow);
		var nextMonthCell = new Element('td').addClasses('moo-clickable', this.theme.headerButton);
		this.nextMonthIcon = new Moo.Icon({
			containerCls: this.options.nextMonthIconCls,
			containerExtraCls: 'moo-icon',
			onClick: this.nextMonth.safeBind(this)
		}).inject(nextMonthCell);
		nextMonthCell.inject(headerRow);
		var nextYearCell = new Element('td').addClasses('moo-clickable', this.theme.headerButton);
		this.nextYearIcon = new Moo.Icon({
			containerCls: this.options.nextYearIconCls,
			containerExtraCls: 'moo-icon',
			onClick: this.nextYear.safeBind(this)
		}).inject(nextYearCell);
		nextYearCell.inject(headerRow);
		headerTable.inject(this.container);
		var daysTable = new Element('table', {width: '100%'}).set('cellspacing', 0);
		var daysTHead = new Element('thead').inject(daysTable);
		var daysHeadRow = new Element('tr').inject(daysTHead);
		if (this.options.showWeeks == true) {
			var td = new Element('td').set('html', '&nbsp;').addClass(this.theme.weekHeader).inject(daysHeadRow);
			td.setStyle('width', this.container.getWidth() / 8);
		}
		this.lang.days.each(function(day, index) {
			var td = new Element('td').set('text', day.substr(0, 1).toUpperCase()).addClass(this.theme.dayHeader).inject(daysHeadRow);
			td.setStyle('width', this.container.getWidth() / (this.options.showWeeks == true ? 8 : 7));
		}, this);
		this.daysTBody = new Element('tbody').inject(daysTable);
		daysTable.inject(this.container);
		if (this.options.showToday == true) {
			switch ($type(this.options.today)) {
				case 'string': case 'number':
					this.$today = new Date(this.options.today).toJson();
					break;
				case 'date':
					this.$today = this.options.today.toJson();
					break;
				case 'object':
					this.$today = this.options.today;
					break;
				default:
					this.$today = new Date().toJson();
					break;
			}
			delete this.options.today;
			new Element('div').addClass(this.theme.today).set('text', this.lang.today).addEvent('click', function(event) {
				this.setDate(this.$today);
				this.fireEvent('onTodayClick', this);
			}.bindWithEvent(this)).inject(this.container);
		}
		this.setDate(this.options.date);
		this.setWeek(this.options.week);
	},
	$setDate: function(date) {
		if (!Moo.DateHelper.isJsonDate(date)) date = new Date().toJson();
		this.$date = date;
		this.$currentYear = this.$date.year;
		this.$currentMonth = this.$date.month;
		return this;
	},
	setDate: function(date) {
		var result = null;
		switch ($type(date)) {
			case 'string':
				this.$dateType = 'string';
				result = Moo.DateHelper.stringToJson(date);
				break;
			case 'number':
				this.$dateType = 'number';
				result = new Date(date).toJson();
				break;
			case 'date':
				this.$dateType = 'date';
				result = date.toJson();
				break;
			case 'object':
				this.$dateType = 'object';
				result = date;
				break;
			default:
				this.$dateType = 'object';
				result = new Date().toJson();
				break;
		}
		this.$setDate(result);
		this.$week = null;
		this.update();
		return this;
	},
	getDate: function(type) {
		var result = null, type = type || this.$dateType;
		switch (type) {
			case 'date':
				result = new Date(this.$date.year, this.$date.month - 1, this.$date.day);
				break;
			case 'string':
				result = Moo.DateHelper.jsonToString(this.$date);
				break;
			case 'number':
				result = new Date(this.$date.year, this.$date.month - 1, this.$date.day).getTime();
				break;
			default:
				result = this.$date;
				break;
		}
		return result;
	},
	setWeek: function(week) {
		if (this.options.showWeeks === true) {
			switch ($type(week)) {
				case 'object':
					this.$week = week;
					if (!this.$week.num) this.$week.num = new Date(this.$week.firstDay.year, this.$week.firstDay.month - 1, this.$week.firstDay.day).getWeek();
					break;
				default:
					this.$week = null;
					break;
			}
			if (this.$week != null) {
				this.$setDate(week.firstDay);
				this.update();
			}
		}
		return this;
	},
	getWeek: function() {
		var week = null;
		if (this.options.showWeeks === true && this.$week) week = this.$week;
		return week;
	},
	update: function() {
		this.title.set('text', this.lang.months[this.$currentMonth - 1] + ' ' + this.$currentYear);
		this.daysTBody.empty();
		this.$previousYear = this.$currentYear;
		this.$previousMonth = this.$currentMonth - 1;
		if (this.$previousMonth <= 0) {
			this.$previousYear--;
			this.$previousMonth = 12;
		}
		this.$nextYear = this.$currentYear;
		this.$nextMonth = this.$currentMonth + 1;
		if (this.$nextMonth > 12) {
			this.$nextYear++;
			this.$nextMonth = 1;
		}
		this.$firstDayInMonth = Moo.DateHelper.firstDayInMonth(this.$currentMonth, this.$currentYear);
		this.$daysInMonth = Moo.DateHelper.daysInMonth(this.$currentMonth, this.$currentYear);
		this.$daysInPreviousMonth = Moo.DateHelper.daysInMonth(this.$previousMonth, this.$previousYear);
		var curDayIndex = 0;
		var curDayInNextMonthIndex = 0;
		var started = false;
		for (var w = 0; w < 6; w++) {
			var tr = new Element('tr').inject(this.daysTBody),
				weekObj = {},
				showWeeks = (this.options.showWeeks == true);
			for (var dayIndex = 0; dayIndex < 7; dayIndex++) {
				var cell = new Element('td').set('html', '&nbsp;').addClass(this.theme.day).inject(tr);
				if (!started) {
					if (dayIndex == this.$firstDayInMonth) {
						curDayIndex++;
						this.makeDayCell(cell, curDayIndex);
						started = true;
						if (showWeeks) {
							if (dayIndex == 0) {
								weekObj.firstDay = {
									day: curDayIndex,
									month: this.$currentMonth,
									year: this.$currentYear
								};
								weekObj.num = new Date(this.$currentYear, this.$currentMonth - 1, curDayIndex).getWeek();
							} else if (dayIndex == 6) {
								weekObj.lastDay = {
									day: curDayIndex,
									month: this.$currentMonth,
									year: this.$currentYear
								};
							}
						}
					} else {
						cell.set('text', this.$daysInPreviousMonth - (this.$firstDayInMonth - dayIndex - 1)).addClass(this.theme.dayInactive);
						if (showWeeks) {
							if (dayIndex == 0) {
								weekObj.firstDay = {
									day: this.$daysInPreviousMonth - (this.$firstDayInMonth - dayIndex - 1),
									month: this.$previousMonth,
									year: this.$previousYear
								};
								weekObj.num = new Date(this.$previousYear, this.$previousMonth - 1, this.$daysInPreviousMonth - (this.$firstDayInMonth - dayIndex - 1)).getWeek();
							} else if (dayIndex == 6) {
								weekObj.lastDay = {
									day: this.$daysInPreviousMonth - (this.$firstDayInMonth - dayIndex - 1),
									month: this.$previousMonth,
									year: this.$previousYear
								};
							}
						}
					}
				} else {
					if (curDayIndex < this.$daysInMonth) {
						curDayIndex++;
						this.makeDayCell(cell, curDayIndex);
						if (showWeeks) {
							if (dayIndex == 0) {
								weekObj.firstDay = {
									day: curDayIndex,
									month: this.$currentMonth,
									year: this.$currentYear
								};
								weekObj.num = new Date(this.$currentYear, this.$currentMonth - 1, curDayIndex).getWeek();
							} else if (dayIndex == 6) {
								weekObj.lastDay = {
									day: curDayIndex,
									month: this.$currentMonth,
									year: this.$currentYear
								};
							}
						}
					} else {
						curDayInNextMonthIndex++;
						cell.set('text', curDayInNextMonthIndex).addClass(this.theme.dayInactive);
						if (showWeeks) {
							if (dayIndex == 0) {
								weekObj.firstDay = {
									day: curDayInNextMonthIndex,
									month: this.$nextMonth,
									year: this.$nextYear
								};
								weekObj.num = new Date(this.$nextYear, this.$nextMonth - 1, curDayInNextMonthIndex).getWeek();
							} else if (dayIndex == 6) {
								weekObj.lastDay = {
									day: curDayInNextMonthIndex,
									month: this.$nextMonth,
									year: this.$nextYear
								};
							}
						}
					}
				}
			}
			if (showWeeks) {
				var weekEl = new Element('td').set('text', '>').addClass(this.theme.week).addEvent('click', this.weekClick.safeBind(this, weekObj)).injectTop(tr);
				if (this.options.weekTooltip != null) {
					var tooltip = this.options.weekTooltip;
					if (this.options.weekTooltipCls != null) tooltip = {
						containerCls: this.options.weekTooltipCls,
						body: this.options.weekTooltip
					};
					weekEl.makeTooltip(tooltip);
				}
				if (this.$week && this.$week.firstDay.day == weekObj.firstDay.day && this.$week.firstDay.month == weekObj.firstDay.month && this.$week.firstDay.year == weekObj.firstDay.year) {
					this.clearSelectedWeek();
					tr.addClass(this.theme.weekSelected);
				}
			}
		}
		return this;
	},
	updateYear: function(add) {
		this.$currentYear = this.$currentYear + (add === true ? 1 : -1);
		return this.update();
	},
	nextYear: function() {
		this.updateYear(true);
		this.fireEvent('onNextYear', this);
		return this;
	},
	previousYear: function() {
		this.updateYear(false);
		this.fireEvent('onPreviousYear', this);
		return this;
	},
	updateMonth: function(add) {
		if (add === true) {
			if (this.$currentMonth === 12) {
				this.$currentMonth = 1;
				return this.updateYear(true);
			} else {
				this.$currentMonth++;
				return this.update();
			}
		} else {
			if (this.$currentMonth === 1) {
				this.$currentMonth = 12;
				return this.updateYear(false);
			} else {
				this.$currentMonth--;
				return this.update();
			}
		}
	},
	nextMonth: function() {
		this.updateMonth(true);
		this.fireEvent('onNextMonth', this);
		return this;
	},
	previousMonth: function() {
		this.updateMonth(false);
		this.fireEvent('onPreviousMonth', this);
		return this;
	},
	makeDayCell: function(cell, dayIndex) {
		cell.set('text', dayIndex).addClass(this.theme.dayActive);
		if (this.options.onDayClick != $empty) {
			cell.addEvent('click', this.dayClick.bindWithEvent(this, [cell, dayIndex]));
			cell.addClassOn('over', this.theme.dayOver);
		} else {
			cell.setStyle('cursor', 'default');
		}
		if (this.$date.day == dayIndex && this.$currentMonth == this.$date.month && this.$currentYear == this.$date.year) {
			cell.addClass(this.theme.daySelected);
		}
	},
	clearSelectedDay: function() {
		var daySelected = this.container.getElement('.' + this.theme.daySelected);
		if (daySelected) daySelected.removeClass(this.theme.daySelected);
	},
	dayClick: function(event, aCell, dayIndex) {
		this.clearSelectedDay();
		this.clearSelectedWeek();
		aCell.addClass(this.theme.daySelected);
		this.$week = null;
		this.$setDate({
			year: this.$currentYear,
			month: this.$currentMonth,
			day: dayIndex
		});
		this.update();
		this.fireEvent('onDayClick', this);
	},
	clearSelectedWeek: function() {
		var weekSelected = this.container.getElement('.' + this.theme.weekSelected);
		if (weekSelected) weekSelected.removeClass(this.theme.weekSelected);
	},
	weekClick: function(weekObj) {
		this.setWeek(weekObj);
		this.fireEvent('onWeekClick', [weekObj, this]);
	}
});
Moo.registerType('calendar', Moo.Calendar);

Moo.DatePickerManager = {
	options: {
		closeDelay: 500
	},
	$initialized: false,
	initialize: function() {
		if (!this.$initialized) {
			this.close = this.close.safeBind(this);
			this.calendar = new Moo.Calendar();
			this.calendar.container.setStyles({
				position: 'absolute',
				'z-index': 10000
			}).hide().addEvent('mousedown', function(e) {
				e.stop(); // prevent closing the calendar
			});
			this.calendar.inject(document.body);
			this.$initialized = true;
		}
	},
	manage: function(datePicker) {
		this.initialize();
		this.datePicker = datePicker;
		this.calendar.setOptions($merge(this.datePicker.calendarOptions, {
			onDayClick: this.choose.safeBind(this),
			onTodayClick: this.choose.safeBind(this)
		}));
		this.open();
	},
	open: function() {
		Moo.addMousedown(this.close);
		this.calendar.$setDate(Moo.DateHelper.stringToJson(this.datePicker.input.get('value')));
		this.calendar.update();
		if (this.datePicker.options.autoAnchor) {
			Moo.autoAnchor(this.calendar.container, this.datePicker.opener, this.datePicker.options.autoAnchorOptions);
		} else {
			Moo.anchor(this.calendar.container, this.datePicker.opener, this.datePicker.options.anchorOptions);
		}
	},
	close: function() {
		Moo.removeMousedown(this.close);
		this.calendar.removeEvents();
		this.calendar.container.hide();
		this.datePicker = null;
	},
	choose: function() {
		this.datePicker.input.set('value', this.calendar.getDate('string'));
		this.datePicker.fireEvent('choose');
		this.close();
	}
};
Moo.DatePicker = new Class({
	Implements: [Events, Options],
	options: {
		handle: null,
		autoAnchor: true,
		autoAnchorOptions: null,
		anchorOptions: null,
		onChoose: $empty
	},
	initialize: function(input, options, calendarOptions) {
		this.setOptions(options);
		this.input = $(input);
		this.opener = $(this.options.handle) || this.input;
		this.calendarOptions = calendarOptions;
		this.opener.addEvent('click', function(event) {
			Moo.DatePickerManager.manage(this);
		}.bindWithEvent(this));
	}
});

Moo.setSelector({
	'Moo.ColorPicker': 'moo-x-colorpicker'
});
Moo.ColorPicker = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$componentType: 'color-picker',
	$componentSelector: Moo.getSelector('Moo.ColorPicker'),
	$theme: {
		color: '-color',
		colorOver: '-color-over'
	},
	options: {
		containerCls: 'moo-colorpicker',
		width: 183,
		height: 104,
		colors: [
			'#000000', '#FF0000', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#FF00FF',
			'#555555', '#FF5555', '#FFFF55', '#55FF55', '#55FFFF', '#5555FF', '#FF55FF',
			'#AAAAAA', '#FFAAAA', '#FFFFAA', '#AAFFAA', '#AAFFFF', '#AAAAFF', '#FFAAFF',
			'#FFFFFF', '#FFDDDD', '#FFFFDD', '#DDFFDD', '#DDFFFF', '#DDDDFF', '#FFDDFF'
		],
		extraColors: [],
		onPick: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.container.setStyle('width', this.options.width);
		if (this.options.height) this.container.setStyle('height', this.options.height);
		this.initializeTheme();
		this.options.colors.each(this.addColor, this);
		this.options.extraColors.each(this.addColor, this);
	},
	addColor: function(color) {
		var colorContainer = new Element('div').inject(this.container);
		colorContainer.set('html', '&nbsp;');
		colorContainer.setStyle('background-color', color);
		colorContainer.addClass(this.theme.color);
		colorContainer.addClassOn('over', this.theme.colorOver);
		colorContainer.addEvent('click', this.pick.safeBind(this, color));
	},
	pick: function(color) {
		this.fireEvent('onPick', color, this);
	},
	hasColor: function(color) {
		return this.options.colors.contains(color) || this.options.extraColors.contains(color);
	}
});
Moo.registerType('color-picker', Moo.ColorPicker);

Moo.setSelector({
	'Moo.Toolbar': 'moo-x-toolbar',
	'Moo.Toolbar.Cell': 'moo-x-toolbar-cell'
});
Moo.Toolbar = new Class({
	Extends: Moo.Component,
	$componentType: 'toolbar',
	$componentSelector: Moo.getSelector('Moo.Toolbar'),
	options: {
		containerCls: 'moo-toolbar',
		autoFit: false,
		spacerWidth: 5,
		cellpadding: 0,
		cellspacing: 3,
		width: null,
		items: []
	},
	initialize: function(options) {
		this.parent(options);
		if (this.options.width != null) this.container.setStyle('width', this.options.width);
		this.table = new Element('table', {
			border: 0,
			cellpadding: this.options.cellpadding,
			cellspacing: this.options.cellspacing
		}).inject(this.container);
		if (this.options.autoFit === true) this.table.set('width', '100%');
		this.tBody = new Element('tbody').inject(this.table);
		this.row = new Element('tr').inject(this.tBody);
		this.options.items.each(this.add.safeBind(this));
	},
	add: function(item) {
		switch($type(item)) {
			case 'string':
				switch(item) {
					case ' ':
						new Element('td', {
							html: '&nbsp;',
							width: this.options.spacerWidth
						}).inject(this.row);
						break;
					case '>':
						new Element('td', {
							html: '&nbsp;',
							width: '100%'
						}).inject(this.row);
						break;
					default:
						cell = new Element('td',{
							html: item
						}).addClasses('moo-toolbar-cell', Moo.getSelector('Moo.Toolbar.Cell')).inject(this.row);
						break;
				}
				break;
			default:
				var cell = new Element('td');
				Moo.inject(item, cell);
				cell.addClasses('moo-toolbar-cell', Moo.getSelector('Moo.Toolbar.Cell')).inject(this.row);
				break;
		}
		this.autoFit();
		return this;
	},
	autoFit: function() {
		if (this.options.autoFit === true) {
			var cells = this.container.getElements('.' + Moo.getSelector('Moo.Toolbar.Cell'));
			switch(cells.length) {
				case 0:
					break;
				case 1:
					cells[0].set({
						align: 'center',
						width: '100%'
					});
					break;
				case 2:
					cells[0].set({
						align: 'right',
						width: '50%'
					});
					cells[1].set({
						align: 'left',
						width: '50%'
					});
					break;
				default:
					cells[0].set({
						align: 'right',
						width: '50%'
					});
					for (var i = 1; i < cells.length - 1; i++) {
						cells[i].set({
							align: 'center',
							width: ''
						});
					}
					cells[cells.length - 1].set({
						align: 'left',
						width: '50%'
					});
					break;
			}
		}
	}
});
Moo.registerType('toolbar', Moo.Toolbar);

Moo.namespace('Moo.tree');

Moo.setSelector({
	'Moo.Tree': 'moo-x-tree'
});
Moo.tree.Tree = new Class({
	Extends: Moo.Component,
	$componentType: 'tree',
	$componentSelector: Moo.getSelector('Moo.Tree'),
	options: {
		width: 'auto',
		height: 'auto',
		containerCls: 'moo-tree',
		items: [],
		title: null
	},
	initialize: function(options) {
		this.parent(options);
		this.container.setStyle('width', this.options.width);
		if (this.options.title != null) {
			var title = new Element('div').addClass('moo-title').inject(this.container);
			Moo.Dom.setText(this.options.title, title);
		}
		this.itemsContainer = new Element('div').addClass('moo-items').inject(this.container);
		this.itemsContainer.setStyle('height', this.options.height);
		this.options.items.each(this.addItem.safeBind(this));
		delete this.options.items;
	},
	addItem: function(item) {
		var cmp = null;
		if (Moo.isComponent(item)) {
			cmp = item.inject(this.container);
		} else {
			cmp = new Moo.tree.Item(item).inject(this.itemsContainer);
		}
		cmp.node.addClass('moo-tree-root');
		return this;
	}
});
Moo.registerType('tree', Moo.tree.Tree);
Moo.Tree = Moo.tree.Tree;

Moo.setSelector({
	'Moo.Tree.Item': 'moo-x-treeitem'
});
Moo.tree.Item = new Class({
	Extends: Moo.Component,
	$componentType: 'tree-item',
	$componentSelector: Moo.getSelector('Moo.Tree.Item'),
	options: {
		containerCls: '',
		text: null,
		closed: false,
		childs: [],
		href: null,
		tooltip: null,
		iconCls: null,
		onClick: $empty,
		onOpen: $empty,
		onClose: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.node = new Moo.Link({
			text: this.options.text,
			containerCls: 'moo-tree-item',
			href: this.options.href,
			onClick: this.click.safeBind(this)
		}).inject(this.container);
		this.node.container.addClassOn('over', 'moo-tree-item-over');
		this.isOpen = false;
		this.setTooltip(this.options.tooltip);
		delete this.options.tooltip;
		if (this.options.iconCls != null && this.options.iconCls != '') this.iconCmp = new Moo.Icon({
			containerCls: this.options.iconCls,
			overable: false
		}).inject(this.node.container, 'top');		
		if (this.options.childs.length != 0) {
			this.createChildsContainer();
			this.options.childs.each(function(item) {
				this.addItem(item);
			}, this);
			delete this.options.childs;
		} else {
			this.node.container.addClass('moo-tree-leaf');
		}
		if (this.options.closed === true) this.close();
	},
	createChildsContainer: function() {
		if (!this.childsContainer) {
			this.node.container.addClass('moo-tree-node');
			this.childsContainer = new Element('div').addClass('moo-tree-childs').inject(this.container);
			if (this.options.closed === false) {
				this.childsContainer.show();
				this.isOpen = true;
			} else {
				this.childsContainer.hide();
				this.isOpen = false;
			}
			this.node.container.addEvent('click', this.toggleClose.safeBind(this));
		} else {
			this.node.container.removeClass('moo-tree-leaf');
		}
	},
	addItem: function(item) {
		this.createChildsContainer();
		if (Moo.isComponent(item)) {
			item.inject(this.childsContainer);
		} else {
			new Moo.tree.Item(item).inject(this.childsContainer);
		}
		return this;
	},
	open: function() {
		if (this.childsContainer) {
			this.childsContainer.show();
			this.node.container.removeClass('moo-tree-node-closed');
			this.fireEvent('open');
			this.isOpen = true;
		}
		return this;
	},
	close: function() {
		if (this.childsContainer) {
			this.childsContainer.hide();
			this.node.container.addClass('moo-tree-node-closed');
			this.fireEvent('close');
			this.isOpen = false;
		}
		return this;
	},
	toggleClose: function(e) {
		if (this.isOpen === true) this.close();
		else this.open();
		return this;
	},
	setTooltip: function(options) {
		this.node.container.makeTooltip(options);
		return this;
	},
	click: function() {
		this.fireEvent('onClick', this);
	}
});

Moo.namespace('Moo.form');
Moo.setLang({
	'moo.field.empty': 'This field is required.',
	'moo.field.alpha': 'This field is not alpha (ex: abc).',
	'moo.field.alphanum': 'This field is not alphanumeric (ex: abc123).',
	'moo.field.integer': 'This field is not an integer (ex: 123).',
	'moo.field.number': 'This field is not a number (ex: 123 or 123.45).',
	'moo.field.email': 'This field is not a valid email (ex: moo@moo.com).',
	'moo.field.date': 'This field is not a valid date (format: DD/MM/YYYY).',
	'moo.field.time': 'This field is not a valid time (format: hh:mm).',
	'moo.field.minLength': 'This field required at least {0} characters.',
	'moo.field.dualtime': 'Start time must be less than the end of time.',
	'moo.field.dualtime.timemin': 'Minimum time is {0}.',
	'moo.field.dualtime.timemax': 'Maximum time is {0}.',
	'moo.field.dualtime.delta': 'Because this duration is fixed to {0}, start time sould be less than {1}.',
	'moo.field.color': 'This field is not a valid color (ex: #A1B2C3).',
	'moo.field.erase': 'Erase'
});

Moo.setSelector({
	'Moo.Input': 'moo-x-input'
});
Moo.form.Input = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$componentType: 'input',
	$componentSelector: Moo.getSelector('Moo.Input'),
	$containerType: 'table',
	$theme: {
		left: '-left',
		right: '-right',
		center: '-center',
		focused: '-focused',
		invalid: '-invalid'
	},
	options: {
		containerCls: 'moo-input',
		type: 'text',
		name: '',
		value: '',
		width: null,
		onFocus: $empty,
		onBlur: $empty,
		onChange: $empty
	},
	initialize: function(options) {
		this.initializeOptions(options);
		this.inputId = this.options.id;
		if (this.inputId === null) this.inputId = Moo.$componentSequence.next();
		delete this.options.id;
		this.parent();
		this.initializeTheme();
		this.container.set({
			cellpadding: 0,
			cellspacing: 0
		});
		this.tBody = new Element('tbody').inject(this.container);
		this.row = new Element('tr').inject(this.tBody);
		this.leftCell = new Element('td', {html: '<em> </em>'}).addClass(this.theme.left).inject(this.row);
		this.centerCell = new Element('td').addClass(this.theme.center).inject(this.row);
		this.input = new Element('input', {
			id: this.inputId,
			type: this.options.type,
			name: this.options.name,
			value: this.options.value
		}).addEvents({
			focus: this.focus.safeBind(this),
			blur: this.blur.safeBind(this),
			change: this.change.safeBind(this)
		}).inject(this.centerCell);
		this.input.set('mooComponent', this);
		this.rightCell = new Element('td', {html: '<em> </em>'}).addClass(this.theme.right).inject(this.row);
		this.setWidth(this.options.width);
	},
	getValue: function() {
		return this.input.get('value');
	},
	setValue: function(value) {
		this.input.set('value', value);
		return this;
	},
	focus: function() {
		if (this.options.containerCls != null && this.options.containerCls != '') this.container.addClass(this.theme.focused);
		this.fireEvent('focus');
	},
	blur: function() {
		if (this.options.containerCls != null && this.options.containerCls != '') this.container.removeClass(this.theme.focused);
		this.fireEvent('blur');
	},
	change: function() {
		this.fireEvent('change');
	},
	toInvalid: function(mode) {
		if (mode == true) this.container.addClass(this.theme.invalid);
		else this.container.removeClass(this.theme.invalid);
		return this;
	},
	setWidth: function(width) {
		width = width || 'auto';
		this.input.setStyle('width', width);
		return this;
	}
});
Moo.registerType('input', Moo.form.Input);

Moo.setSelector({
	'Moo.Fieldset': 'moo-x-fieldset'
});
Moo.form.Fieldset = new Class({
	Extends: Moo.Component,
	$componentType: 'fieldset',
	$containerType: 'fieldset',
	$componentSelector: Moo.getSelector('Moo.Fieldset'),
	options: {
		containerCls: 'moo-fieldset',
		hideIcons: true,
		labelsAlign: 'right',
		labelsWidth: null,
		items: []
	},
	initialize: function(options) {
		this.parent(options);
		$each(this.options.items, this.add, this);
	},
	add: function(something) {
		var result = Moo.inject(something, this.container);
		if (result != null) {
			if (this.options.hideIcons === true) {
				if (result.hideIcon) result.hideIcon();
			} else {
				if (result.showIcon) result.showIcon();
			}
			if (result.setLabelAlign) result.setLabelAlign(this.options.labelsAlign);
			if (this.options.labelsWidth != null && result.setLabelWidth) result.setLabelWidth(this.options.labelsWidth);
		}
		return result;
	},
	control: function() {
		var isValid = true;
		this.getComponents('.moo-controlable').each(function(cmp) {
			isValid = cmp.control() && isValid;
		});
		return isValid;
	}
});
Moo.registerType('fieldset', Moo.form.Fieldset);

Moo.form.FormPanel = new Class({
	Extends: Moo.Panel,
	$componentType: 'form-panel',
	options: {
		containerCls: 'moo-form-panel',
		name: '',
		method: 'post',
		hideIcons: true,
		labelsAlign: 'right',
		labelsWidth: 130,
		items: []
	},
	initialize: function(options) {
		this.initializeOptions(options);
		delete this.options.body; // we do not want some stuff in the bodyContainer
		this.form = new Element('form', {
			name: this.options.name,
			method: this.options.method
		});
		this.parent();
		this.form.inject(this.mainContainer);
		this.mainContainer = this.form;
		$each(this.options.items, this.add, this);
	},
	add: function(something) {
		var result = Moo.inject(something, this.form);
		if (result != null) {
			if (this.options.hideIcons === true) {
				if (result.hideIcon) result.hideIcon();
			} else {
				if (result.showIcon) result.showIcon();
			}
			if (result.setLabelAlign) result.setLabelAlign(this.options.labelsAlign);
			if (result.setLabelWidth) result.setLabelWidth(this.options.labelsWidth);
		}
		return result;
	},
	control: function() {
		var isValid = true;
		this.getComponents('.moo-controlable').each(function(cmp) {
			isValid = cmp.control() && isValid;
		});
		return isValid;
	}
});
Moo.registerType('form-panel', Moo.form.FormPanel);

Moo.setSelector({
	'Moo.Field': 'moo-x-field'
});
Moo.form.Field = new Class({
	Extends: Moo.Component,
	$componentType: 'field',
	$componentSelector: Moo.getSelector('Moo.Field'),
	$field: true,
	options: {
		containerCls: 'moo-field',
		hideIcon: true,
		iconCls: '',
		hideField: false,
		hideLabel: false,
		labelCls: 'moo-label',
		labelText: '',
		labelAlign: 'right',
		labelWidth: 130,
		readonly: false,
		control: null,
		triggersInjection: 'field'
	},
	initialize: function(options) {
		this.initializeOptions(options);
		this.fieldId = this.options.id;
		delete this.options.id;
		if (this.fieldId === null) this.fieldId = Moo.$componentSequence.next();
		this.parent();
		this.icon = new Element('div', {html: '&nbsp;'}).inject(this.container);
		this.icon.addClasses('moo-field-icon', this.options.iconCls);
		if (this.options.hideIcon === true) this.hideIcon();
		delete this.options.hideIcon;
		this.label = new Element('label', {'for': this.fieldId}).inject(this.container);
		this.label.addClass(this.options.labelCls);
		this.setLabelAlign(this.options.labelAlign).setLabelWidth(this.options.labelWidth);
		Moo.Dom.setText(this.options.labelText, this.label);
		if (this.options.hideLabel === true) this.hideLabel();
		delete this.options.hideLabel;
		this.field = this.buildField().inject(this.container);
		delete this.buildField;
		this.field.set('mooComponent', this);
		if (this.options.hideField === true) this.hideField();
		delete this.options.hideField;
		this.setReadonly(this.options.readonly);
		if (this.options.control) this.addControl(this.options.control, this.options.control);
	},
	buildField: function() {
		return new Element('div', {text: 'not implemented', id: this.fieldId});
	},
	showLabel: function() {
		this.label.show();
		return this;
	},
	hideLabel: function() {
		this.label.hide();
		return this;
	},
	setLabelAlign: function(align) {
		this.label.setStyle('text-align', align || 'left');
		return this;
	},
	setLabelWidth: function(width) {
		this.label.setStyle('width', width || 'auto');
		return this;
	},
	showField: function() {
		this.field.show();
		return this;
	},
	hideField: function() {
		this.field.hide();
		return this;
	},
	showIcon: function() {
		this.icon.show();
		return this;
	},
	hideIcon: function() {
		this.icon.hide();
		return this;
	},
	addTrigger: function(options) {
		var cmp = null;
		var oldClick = options.onClick || $empty, self = this;
		var newClick = function(c) {
			oldClick.run([c, self]);
		};
		options.onClick = newClick;
		cmp = Moo.create(options);
		cmp.addClass('moo-trigger');
		cmp.setStyle('float', (['left', 'right'].contains(options['float']) ? options['float'] : 'left'));
		var el = this.field;
		if (this.options.triggersInjection == 'display' && this.displayTextEl) el = this.displayTextEl;
		cmp.injectAfter(el);
		return cmp;
	},
	setReadonly: function(readonly) {
		this.field.set('readonly', readonly);
		return this;
	},
	getValue: function() {
		return this.field.get('value');
	},
	setValue: function(value) {
		this.field.set('value', value);
		return this;
	},
	addControl: function(name, control) {
		if (!this.$controls) this.$controls = new Hash();
		switch($type(control)) {
			case 'object':
				if (typeof control.fn != 'function') control = null;
				break;
			case 'string':
				var fnName = 'is' + control.capitalize();
				var labelName = 'moo.field.' + control;
				if (Moo.Controls[fnName]) {
					control = {};
					control.fn = Moo.Controls[fnName].safeBind(Moo.Controls);
					control.title = Moo.getLang(labelName);
				} else control = null;
				break;
			case 'function':
				control = {fn: control};
				control.title = title;
				break;
			default:
				break;
		}
		if (control != null) this.$controls.set(name, control);
		return this;
	},
	removeControl: function(name) {
		if (this.$controls) this.$controls.remove(name);
		return this;
	},
	control: function() {
		this.isValid = true;
		this.invalidTitle = '';
		if (this.$controls) this.$controls.each(function(control, name) {
			if (this.isValid) {
				this.isValid = control.fn.run(this.getValue());
				if (!this.isValid) this.invalidTitle = control.title;
			}
		}, this);
		return this.isValid;
	}
});

Moo.setSelector({
	'Moo.HiddenField': 'moo-x-hiddenfield'
});
Moo.form.HiddenField = new Class({
	Extends: Moo.Component,
	$componentType: 'hidden-field',
	$componentSelector: Moo.getSelector('Moo.HiddenField'),
	$field: true,
	$containerType: 'input',
	options: {
		name: '',
		value: ''
	},
	initialize: function(options) {
		this.initializeOptions(options);
		this.options = $extend(this.options, {
			properties: {
				type: 'hidden',
				name: this.options.name,
				value: this.options.value
			}
		});
		this.parent();
	},
	getValue: function() {
		return this.container.get('value');
	},
	setValue: function(value) {
		this.container.set('value', value);
		return this;
	}
});
Moo.registerType('hidden-field', Moo.form.HiddenField);

Moo.form.TextField = new Class({
	Extends: Moo.form.Field,
	Implements: Moo.Langable,
	$componentType: 'text-field',
	$lang: {
		required: 'moo.field.empty',
		minLength: 'moo.field.minLength',
		erase: 'moo.field.erase'
	},
	options: {
		name: '',
		value: '',
		fieldCls: 'moo-input',
		displayText: '',
		displayTextCls: 'moo-display-text',
		required: false,
		minLength: null,
		maxLength: null,
		control: null,
		triggersPosition: 'left',
		showEraser: false,
		eraseTriggerCls: Moo.Icons.erase,
		eraseTriggerTooltip: true,
		eraseTriggerTooltipOptions: null,
		invalidIconCls: Moo.Icons.invalid,
		enterFn: $empty,
		onErase: $empty,
		onFocus: $empty,
		onBlur: $empty,
		onChange: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.initializeLang();
		this.container.addClass('moo-controlable');
		this.field.addClass(this.options.fieldCls).addEvents({
			focus: this.onFocus.safeBind(this),
			blur: this.onBlur.safeBind(this),
			change: this.onChange.safeBind(this),
			keydown: this.onKeydown.safeBind(this),
			keyup: this.onKeyup.safeBind(this)
		});
		if (this.options.maxLength != null) this.field.set('maxLength', this.options.maxLength);
		this.displayTextEl = new Element('div').addClass(this.options.displayTextCls).inject(this.container);
		this.setDisplayText(this.options.displayText);
		if (this.options.showEraser) {
			this.erase = this.erase.safeBind(this);
			this.eraserTrigger = this.addTrigger({
				mooType: 'icon',
				containerCls: this.options.eraseTriggerCls,
				'float': this.options.triggersPosition,
				onClick: this.erase
			});
			if (this.options.eraseTriggerTooltip) {
				var tooltipOptions = this.options.eraseTriggerTooltipOptions || {};
				if (!tooltipOptions.body) tooltipOptions.body = this.lang.erase;
				this.eraserTrigger.makeTooltip(tooltipOptions);
				Moo.fixInnerTooltip(this.eraserTrigger.container, this.container, tooltipOptions.containerCls);
			}
		}
	},
	buildField: function() {
		return new Element('input', {
			type: 'text',
			name: this.options.name,
			value: this.options.value,
			id: this.fieldId
		});
	},
	setValue: function(value) {
		this.field.set('value', value);
		this.control();
		return this;
	},
	setDisplayText: function(text) {
		Moo.Dom.setText(text, this.displayTextEl);
		return this;
	},
	setValueAndDisplayText: function(value, text) {
		this.setValue(value);
		this.setDisplayText(text);
		return this;
	},
	setTooltip: function(title) {
		if (title == '') title = null;
		var tooltipOptions = {body: null};
		if (title != null) tooltipOptions = {
			containerCls: 'moo-invalid-tooltip',
			body: '<img src="' + Moo.pixelImgSrc + '" class="' + this.options.invalidIconCls + '" />' + title
		};
		this.container.makeTooltip(tooltipOptions);
		return this;
	},
	control: function() {
		this.isValid = true;
		this.invalidTitle = '';
		var fieldValue = this.field.get('value');
		if (this.options.required === true && Moo.Controls.isEmpty(fieldValue)) {
			this.invalidTitle = this.lang.required;
			this.isValid = false;
		}
		var minLength = this.options.minLength;
		if (minLength != null && this.isValid && fieldValue.length < minLength) {
			this.invalidTitle = this.lang.minLength.format(minLength);
			this.isValid = false;
		}
		if (this.isValid && fieldValue != '') this.parent();
		this.setValid(this.isValid, this.invalidTitle);
		return this.isValid;
	},
	setValid: function(valid, title) {
		valid = Moo.toBoolean(valid, true);
		this.isValid = valid;
		if (this.isValid) {
			this.removeClass(this.options.containerCls + '-invalid');
			this.setTooltip('');
		} else {
			this.addClass(this.options.containerCls + '-invalid');
			this.invalidTitle = title || '';
			this.setTooltip(this.invalidTitle);
		}
	},
	clear: function() {
		this.setValueAndDisplayText('', '');
		return this;
	},
	onFocus: function() {
		this.container.addClass(this.options.containerCls + '-focused');
		this.control();
		this.fireEvent('onFocus');
	},
	onBlur: function() {
		this.container.removeClass(this.options.containerCls + '-focused');
		this.control();
		this.fireEvent('onBlur');
	},
	onChange: function() {
		this.fireEvent('onChange');
	},
	onKeydown: function(event) {
	},
	onKeyup: function(event) {
		if (this.options.enterFn != $empty && event.key == 'enter') this.options.enterFn.run(this);
		else this.control();
	},
	erase: function() {
		this.field.set('value', '');
		this.setDisplayText();
		this.control();
		Moo.Dom.focus(this.field);
		this.fireEvent('onErase', this);
	}
});
Moo.registerType('text-field', Moo.form.TextField);

Moo.setSelector({
	'Moo.TextareaField': 'moo-x-textareafield'
});
Moo.form.TextareaField = new Class({
	Extends: Moo.form.TextField,
	$componentType: 'textarea-field',
	$componentSelector: Moo.getSelector('Moo.TextareaField'),
	options: {
		fieldCls: 'moo-textarea' // redefine the class
	},
	initialize: function(options) {
		this.parent(options);
		this.container.addClass('moo-textarea-field');
	},
	buildField: function() {
		return new Element('textarea', {
			name: this.options.name,
			value: this.options.value,
			id: this.fieldId
		});
	}
});
Moo.registerType('textarea-field', Moo.form.TextareaField);

Moo.setSelector({
	'Moo.DateField': 'moo-x-datefield'
});
Moo.form.DateField = new Class({
	Extends: Moo.form.TextField,
	Implements: Moo.Langable,
	$componentType: 'date-field',
	$componentSelector: Moo.getSelector('Moo.DateField'),
	$lang: {
		date: 'moo.field.date'
	},
	options: {
		calendarTriggerCls: Moo.Icons.calendar,
		fieldWidth: 75
	},
	initialize: function(options) {
		this.addControl('moo.date', {
			fn: Moo.DateHelper.isDate.safeBind(Moo.DateHelper),
			title: this.lang.date
		});
		this.parent(options);
		this.field.setStyle('width', this.options.fieldWidth);
		this.field.set('maxlength', 10);
		this.dateTrigger = this.addTrigger({
			mooType: 'icon',
			containerCls: this.options.calendarTriggerCls,
			'float': this.options.triggersPosition
		});
		new Moo.DatePicker(this.field, {
			handle: this.dateTrigger.container,
			onChoose: function() {
				this.control();
				Moo.Dom.focus(this.field);
				this.fireEvent('onChange', this);
			}.safeBind(this)
		});
	}
});
Moo.registerType('date-field', Moo.form.DateField);

Moo.setSelector({
	'Moo.TimeField': 'moo-x-timefield'
});
Moo.form.TimeField = new Class({
	Extends: Moo.form.TextField,
	Implements: Moo.Langable,
	$componentType: 'time-field',
	$componentSelector: Moo.getSelector('Moo.TimeField'),
	$lang: {
		time: 'moo.field.time'
	},
	options: {
		fieldWidth: 40,
		autoComplete: true
	},
	initialize: function(options) {
		this.addControl('moo.time', {
			fn: Moo.TimeHelper.isTime.safeBind(Moo.TimeHelper),
			title: this.lang.time
		});
		this.parent(options);
		this.field.setStyle('width', this.options.fieldWidth);
		this.field.set('maxlength', 5);
	},
	complete: function() {
		var value = this.field.get('value'), completed = Moo.TimeHelper.completeTime(value);
		if (completed != null && value != completed) this.field.set('value', completed);
		return this;
	},
	onBlur: function() {
		if (this.options.autoComplete === true && !this.isValid) this.complete();
		this.parent();
	}
});
Moo.registerType('time-field', Moo.form.TimeField);

Moo.setSelector({
	'Moo.PasswordField': 'moo-x-passwordfield'
});
Moo.form.PasswordField = new Class({
	Extends: Moo.form.TextField,
	$componentType: 'password-field',
	$componentSelector: Moo.getSelector('Moo.PasswordField'),
	buildField: function() {
		return new Element('input', {
			type: 'password',
			name: this.options.name,
			value: this.options.value,
			id: this.fieldId
		});
	}
});
Moo.registerType('password-field', Moo.form.PasswordField);

Moo.setSelector({
	'Moo.SearchField': 'moo-x-searchfield'
});
Moo.form.SearchField = new Class({
	Extends: Moo.form.TextField,
	$componentType: 'search-field',
	$componentSelector: Moo.getSelector('Moo.SearchField'),
	options: {
		fieldCls: '',
		searchTriggerCls: Moo.Icons.search,
		searchFn: $empty
	},
	initialize: function(options) {
		this.parent(options);
		this.field.addClasses('moo-input', this.options.fieldCls);
		this.searchTrigger = this.addTrigger({
			mooType: 'icon',
			containerCls: this.options.searchTriggerCls,
			'float': this.options.triggersPosition,
			onClick: function() {
				this.options.searchFn.run(this);
			}.safeBind(this)
		});
	}
});
Moo.registerType('search-field', Moo.form.SearchField);

Moo.setSelector({
	'Moo.CheckboxField': 'moo-x-checkboxfield'
});
Moo.form.CheckboxField = new Class({
	Extends: Moo.form.Field,
	$componentType: 'checkbox-field',
	$componentSelector: Moo.getSelector('Moo.CheckboxField'),
	options: {
		name: '',
		checked: false,
		fieldCls: '',
		synchroValue: true,
		checkedValue: true,
		uncheckedValue: false,
		onClick: $empty
	},
	buildField: function() {
		return new Element('input', {
			type: 'checkbox',
			name: this.options.name,
			value: this.options.value,
			id: this.fieldId,
			checked: this.options.checked,
			events: {
				click: this.onClick.safeBind(this)
			}
		});
	},
	onClick: function(event) {
		if (this.options.synchroValue == true) this.field.value = (this.field.checked ? this.options.checkedValue : this.options.uncheckedValue);
		this.fireEvent('onClick', [this.getValue(), this]);
	}
});
Moo.registerType('checkbox-field', Moo.form.CheckboxField);

Moo.setSelector({
	'Moo.SelectField': 'moo-x-selectfield'
});
Moo.form.SelectField = new Class({
	Extends: Moo.form.Field,
	$componentType: 'select-field',
	$componentSelector: Moo.getSelector('Moo.SelectField'),
	options: {
		fieldCls: 'moo-select',
		selected: null,
		options: []
	},
	initialize: function(options) {
		this.parent(options);
		this.field.addClass(this.options.fieldCls);
		this.addOpts(this.options.options);
		delete this.options.options;
		this.setSelected(this.options.selected);
		this.field.set('value', this.options.value);
	},
	buildField: function() {
		return new Element('select', {
			name: this.options.name,
			id: this.fieldId
		});
	},
	addOpt: function(obj) {
		var optionEl = document.createElement('option');
		optionEl.value = '' + obj.value;
		optionEl.appendChild(document.createTextNode('' + obj.label));
		this.field.appendChild(optionEl);
		return this;
	},
	addOpts: function() {
		Array.flatten(arguments).each(function(obj) {
			this.addOpt(obj);
		}, this);
		return this;
	},
	setOpts: function() {
		this.field.empty();
		return this.addOpts(arguments);
	},
	setSelected: function(value) {
		if ($defined(value)) this.field.value = value;
		return this;
	}
});
Moo.registerType('select-field', Moo.form.SelectField);

Moo.setSelector({
	'Moo.ComboField': 'moo-x-combofield'
});
Moo.form.ComboField = new Class({
	Extends: Moo.form.Field,
	Implements: [Moo.Destroyable, Moo.Langable],
	$componentType: 'combo-field',
	$componentSelector: Moo.getSelector('Moo.ComboField'),
	$lang: {
		erase: 'moo.field.erase'
	},
	options: {
		inputCls: 'moo-combo-input',
		triggerCls: Moo.Icons.combodown,
		listCls: 'moo-combo-list',
		name: '',
		value: '',
		displayValue: null,
		readonly: true,
		width: 'auto',
		maxWidth: 400,
		height: 'auto',
		maxHeight: 200,
		items: [],
		itemValueMatch: 'value',
		itemLabelMatch: 'label',
		addEmpty: false,
		emptyValue: '',
		emptyLabel: '',
		showEraser: false,
		eraseTiggerFloat: 'left',
		eraseTriggerCls: Moo.Icons.erase,
		eraseTriggerTooltip: true,
		eraseTriggerTooltipOptions: null,
		autoAnchor: true,
		autoAnchorOptions: null,
		anchorOptions: null,
		onChange: $empty,
		onErase: $empty
	},
	initialize: function(options) {
		this.isComboOpen = false;
		this.emptyItem = null;
		this.closeCombo = this.closeCombo.bindWithEvent(this);
		var displayId = Moo.$componentSequence.next();
		this.displayField = new Element('input', {type: 'text', id: displayId});
		this.parent(options);
		this.initializeLang();
		this.makeDestroyable();
		this.displayField.addClass(this.options.inputCls).injectAfter(this.field);
		this.setReadonly(this.options.readonly);
		this.displayField.addEvents({
			click: this.openCombo.safeBind(this),
			focus: function() {
				this.container.addClass(this.options.containerCls + '-focused');
				this.openCombo();
			}.safeBind(this),
			blur: function() {
				this.container.removeClass(this.options.containerCls + '-focused');
			}.safeBind(this)
		});
		this.label.set('for', displayId);
		this.comboTriggerEl = new Element('div').setStyle('float', 'left').addEvent('click', function() {
			this.displayField.focus();
		}.safeBind(this)).addClasses('moo-combo-trigger', this.options.triggerCls).injectAfter(this.displayField);
		this.comboTriggerEl.addClassOn('over', this.options.triggerCls + '-over');
		this.listContainer = new Moo.Component({
			containerCls: this.options.listCls,
			styles: {
				position: 'absolute',
				top: 0,
				left: 0,
				display: 'none'
			}
		}).inject(document.body);
		this.listContainer.container.addEvent('mousedown', function(event) {
			event.stop();
		});
		this.setComboItems(this.options.items);
		delete this.options.items;
		this.setComboWidth(this.options.width);
		this.setComboHeight(this.options.height);
		this.setValue(this.options.value);
		if (this.options.displayValue) this.setDisplayValue(this.options.displayValue);
		if (this.options.showEraser) {
			this.erase = this.erase.safeBind(this);
			this.eraserTrigger = new Moo.Icon({
				containerCls: this.options.eraseTriggerCls,
				onClick: this.erase
			}).injectAfter(this.comboTriggerEl);
			if (this.options.eraseTriggerTooltip) {
				var tooltipOptions = this.options.eraseTriggerTooltipOptions || {};
				if (!tooltipOptions.body) tooltipOptions.body = this.lang.erase;
				this.eraserTrigger.makeTooltip(tooltipOptions);
			}
			this.eraserTrigger.setStyle('float', this.options.eraseTiggerFloat);
			this.eraserTrigger.addClass('moo-trigger');
		}
	},
	buildField: function() {
		return new Element('input', {
			type: 'hidden',
			name: this.options.name,
			value: this.options.value,
			id: this.fieldId
		});
	},
	destroy: function() {
		Moo.destroy(this.listContainer);
		this.container.destroy();
	},
	setReadonly: function(readonly) {
		this.displayField.set('readonly', readonly);
		this.displayField.setStyle('cursor', (readonly === true ? 'pointer' : 'text'));
		return this;
	},
	setValue: function(value, fireChange) {
		var previous = this.getValue();
		this.field.set('value', value);
		var label = null, items = this.items, item = null;
		for (var i = 0, l = items.length; i < l && label == null; i++) {
			item = items[i];
			if (item[this.options.itemValueMatch] == value) label = item[this.options.itemLabelMatch];
		} if (label == null && value == this.options.emptyValue) {
			label = this.options.emptyLabel;
		}
		this.setDisplayValue(label || '');
		if (Moo.toBoolean(fireChange, false) && previous != this.getValue()) this.fireEvent('onChange');
		return this;
	},
	setComboItems: function(items, fireChange) {
		this.items = items || [];
		this.listContainer.empty();
		if (this.options.addEmpty == true) {
			if (!this.emptyItem) {
				this.emptyItem = {};
				this.emptyItem[this.options.itemValueMatch] = this.options.emptyValue;
				this.emptyItem[this.options.itemLabelMatch] = this.options.emptyLabel;
			}
			items.unshift(this.emptyItem);
		}
		this.items.each(function(it) {
			new Element('div', {
				text: it[this.options.itemLabelMatch]
			}).addClass('moo-item').addClassOn('over', 'moo-item-over').addEvents({
				click: function(event, item) {
					this.setValue(item[this.options.itemValueMatch], true);
					this.closeCombo();
				}.bindWithEvent(this, it)
			}).inject(this.listContainer.container);
		}, this);
		this.erase(Moo.toBoolean(fireChange, false));
	},
	isEmpty: function() {
		return (this.items.length == 0);
	},
	getDisplayValue: function() {
		return this.displayField.get('value');
	},
	setDisplayValue: function(value) {
		this.displayField.set('value', value);
		return this;
	},
	openCombo: function() {
		if (!this.isEmpty() && !this.isComboOpen) {
			Moo.addMousedown(this.closeCombo);
			if (this.options.autoAnchor) {
				Moo.autoAnchor(this.listContainer.container, this.displayField, this.options.autoAnchorOptions);
			} else {
				Moo.anchor(this.listContainer.container, this.displayField, this.options.anchorOptions);
			}
			this.listContainer.setStyles({
				width: 'auto',
				height: 'auto'
			});
			var coords = this.displayField.getCoordinatesWithoutBorders(),
				listCoords = this.listContainer.container.getCoordinatesWithoutBorders();
			var width = listCoords.width;
			if (this.options.width == 'auto') width = Math.max(width, coords.width);
			if (width > this.options.maxWidth) width = this.options.maxWidth;
			this.setComboWidth(width);
			var height = listCoords.height;
			if (height > this.options.maxHeight) height = this.options.maxHeight;
			this.setComboHeight(height);
			this.isComboOpen = true;
		}
		return this;
	},
	closeCombo: function(event) {
		if (event && event.target == this.comboTriggerEl) {
			event.stop();
		}
		Moo.removeMousedown(this.closeCombo);
		this.listContainer.hide();
		this.isComboOpen = false;
		return this;
	},
	setComboWidth: function(width) {
		this.listContainer.setStyle('width', width);
		return this;
	},
	setComboHeight: function(height) {
		this.listContainer.setStyle('height', height);
		return this;
	},
	erase: function(fireChange) {
		this.setValue(this.options.emptyValue, Moo.toBoolean(fireChange, true));
		this.fireEvent('onErase', this);
		return this;
	}
});
Moo.registerType('combo-field', Moo.form.ComboField);

Moo.setSelector({
	'Moo.DualTimeField': 'moo-x-dualtimefield'
});
Moo.form.DualTimeField = new Class({
	Extends: Moo.form.TimeField,
	Implements: Moo.Langable,
	$componentType: 'dualtime-field',
	$componentSelector: Moo.getSelector('Moo.DualTimeField'),
	$lang: {
		dualTime: 'moo.field.dualtime',
		timeMin: 'moo.field.dualtime.timemin',
		timeMax: 'moo.field.dualtime.timemax',
		delta: 'moo.field.dualtime.delta'
	},
	options: {
		field2Cls: 'moo-input',
		field2Width: 40,
		label2Cls: 'moo-label',
		label2Text: '',
		label2Width: '',
		autoComplete2: true,
		delta: null,
		initDelta: null,
		timeMin: null,
		timeMax: null
	},
	initialize: function(options) {
		this.parent(options);
		var field2id = 'moo-dualtimefield-2-' + Moo.getUID();
		this.label2 = new Element('label').set({
			text: this.options.label2Text,
			'for': field2id
		}).addClass(this.options.label2Cls).setStyle('width', this.options.label2Width).injectAfter(this.field);
		this.field2 = new Element('input', {
			type: 'text',
			id: field2id,
			maxlength: 5,
			value: this.options.value2
		}).setStyle('width', this.options.field2Width).injectAfter(this.label2);
		this.field2.addClass(this.options.field2Cls).addEvents({
			focus: this.onFocus2.safeBind(this),
			blur: this.onBlur2.safeBind(this),
			change: this.onChange2.safeBind(this),
			keydown: this.onKeydown2.safeBind(this),
			keyup: this.onKeyup2.safeBind(this)
		});
		if (this.options.delta != null) this.setDelta(this.options.delta);
		delete this.options.delta;
		if (this.options.timeMin != null) this.setTimeMin(this.options.timeMin);
		delete this.options.timeMin;
		if (this.options.timeMax != null) this.setTimeMax(this.options.timeMax);
		delete this.options.timeMax;
		this.addDelta(this.options.initDelta);
		delete this.options.initDelta;
	},
	control: function() {
		this.parent();
		var value1 = this.field.get('value').trim();
		if (this.isValid) {
			if (value1 == '') {
				this.setValue2('');
			} else {
				var value2 = this.field2.get('value').trim(), time1 = Moo.TimeHelper.stringToJson(value1), time2 = Moo.TimeHelper.stringToJson(value2);
				if (this.delta != null) {
					var min = time1.m + this.delta.m, hour = time1.h + this.delta.h;
					var m = (min % 60).toInt(), h = hour + (min / 60).toInt();
					this.setValue2(Moo.TimeHelper.jsonToString({h: h, m: m}));
					this.isValid = Moo.TimeHelper.isTime(this.field2.get('value').trim());
					this.invalidTitle = this.deltaText;
					this.setValid(this.isValid, this.invalidTitle);
					return this.isValid;
				}
				this.isValid = Moo.TimeHelper.isTime(value2);
				if (!this.isValid) this.invalidTitle = this.lang.time;
				if (this.isValid && value2 != '') { 
					var diff = ((time2.h - time1.h) * 60) + (time2.m - time1.m);
					this.isValid = (diff >= 0);
					if (!this.isValid) this.invalidTitle = this.lang.dualTime;
				}
				if (this.isValid && this.timeMin != null && value2 != '') { 
					var diff1 = ((time1.h - this.timeMin.h) * 60) + (time1.m - this.timeMin.m);
					this.isValid = (diff1 >= 0);
					if (!this.isValid) this.invalidTitle = this.timeMinText;
					if (this.isValid) {
						var diff2 = ((time2.h - this.timeMin.h) * 60) + (time2.m - this.timeMin.m);
						this.isValid = (diff2 >= 0);
						if (!this.isValid) this.invalidTitle = this.timeMinText;
					}
				}
				if (this.isValid && this.timeMax != null && value2 != '') { 
					var diff1 = ((time1.h - this.timeMax.h) * 60) + (time1.m - this.timeMax.m);
					this.isValid = (diff1 <= 0);
					if (!this.isValid) this.invalidTitle = this.timeMaxText;
					if (this.isValid) {
						var diff2 = ((time2.h - this.timeMax.h) * 60) + (time2.m - this.timeMax.m);
						this.isValid = (diff2 <= 0);
						if (!this.isValid) this.invalidTitle = this.timeMaxText;
					}
				}
			}
		}
		this.setValid(this.isValid, this.invalidTitle);
		return this.isValid;
	},
	setDelta: function(delta) {
		this.delta = Moo.TimeHelper.stringToJson(delta);
		if (this.delta != null) {
			this.setReadonly2(true);
			var time1Max = {h: 23 - this.delta.h, m: 59 - this.delta.m};
			this.deltaText = this.lang.delta.format(Moo.TimeHelper.jsonToString(this.delta), Moo.TimeHelper.jsonToString(time1Max));
		}
		return this.delta;
	},
	setTimeMin: function(timeMin) {
		this.timeMin = Moo.TimeHelper.stringToJson(timeMin);
		if (this.timeMin != null) this.timeMinText = this.lang.timeMin.format(this.timeMin.h + Moo.TimeHelper.$timeSep + this.timeMin.m);
		return this.timeMin;
	},
	setTimeMax: function(timeMax) {
		this.timeMax = Moo.TimeHelper.stringToJson(timeMax);
		if (this.timeMax != null) this.timeMaxText = this.lang.timeMax.format(this.timeMax.h + Moo.TimeHelper.$timeSep + this.timeMax.m);
		return this.timeMax;
	},
	getValue2: function() {
		return this.field2.get('value');
	},
	setValue2: function(value) {
		this.field2.set('value', value);
		return this;
	},
	setReadonly2: function(readonly) {
		this.field2.set('readonly', readonly);
		return this;
	},
	addDelta: function(delta) {
		if (!delta) return this;
		delta = Moo.TimeHelper.stringToJson(delta);
		if (delta != null) {
			var time1 = Moo.TimeHelper.stringToJson(this.field.get('value').trim());
			if (time1 != null) {
				var min = time1.m + delta.m, hour = time1.h + delta.h;
				var m = (min % 60).toInt(), h = hour + (min / 60).toInt();
				this.setValue2(Moo.TimeHelper.jsonToString({h: h, m: m}));
			}
		}
		return this;
	},
	complete2: function() {
		var value = this.field2.get('value'), completed = Moo.TimeHelper.completeTime(value);
		if (completed != null && value != completed) this.field2.set('value', completed);
		return this;
	},
	onFocus2: function() {
		if (this.options.autoComplete === true && !this.isValid) this.complete();
		this.container.addClass(this.options.containerCls + '-focused');
		this.control();
		this.fireEvent('onFocus');
	},
	onBlur2: function() {
		if (this.options.autoComplete2 === true && !this.isValid) this.complete2();
		this.container.removeClass(this.options.containerCls + '-focused');
		this.control();
		this.fireEvent('onBlur');
	},
	onChange2: function() {
		this.fireEvent('onChange');
	},
	onKeydown2: function(event) {
	},
	onKeyup2: function(event) {
		if (this.options.enterFn != $empty && event.key == 'enter') this.options.enterFn.run(this);
		else this.control();
	}
});
Moo.registerType('dualtime-field', Moo.form.DualTimeField);

Moo.setSelector({
	'Moo.RadioField': 'moo-x-radiofield'
});
Moo.form.RadioField = new Class({
	Extends: Moo.form.Field,
	$componentType: 'radio-field',
	$componentSelector: Moo.getSelector('Moo.RadioField'),
	options: {
		name: '',
		fieldCls: '',
		value: null,
		radios: [],
		defaultLabelCls: 'moo-radio-label',
		onClick: $empty,
		onChange: $empty
	},
	initialize: function(options) {
		this.$value = null;
		this.onClick = this.onClick.safeBind(this);
		this.parent(options);
		new Element('label', {
			text: this.options.radios[0].label,
			'for': this.fieldId
		}).addClass(this.options.defaultLabelCls || this.options.radios[0].labelCls).inject(this.container);
		this.options.radios.shift();
		this.options.radios.each(function(radio) {
			var uid = 'moo-radio-' + Moo.getUID();
			new Element('input', {
				type: 'radio',
				name: this.options.name,
				value: radio.value,
				checked: ((radio.checked || radio.selected) === true),
				id: uid
			}).addEvent('click', this.onClick).inject(this.container);
			new Element('label', {
				text: radio.label,
				'for': uid
			}).addClass(this.options.defaultLabelCls || radio.labelCls).inject(this.container);
		}, this);
		this.setValue(this.options.value);
		delete this.options.value;
	},
	buildField: function() {
		return new Element('input', {
			type: 'radio',
			name: this.options.name,
			value: this.options.radios[0].value,
			id: this.fieldId,
			checked: ((this.options.radios[0].checked || this.options.radios[0].selected) === true)
		}).addEvent('click', this.onClick);
	},
	getValue: function() {
		var value = null, input = this.getChecked();
		if (input) value = input.get('value');
		return value;
	},
	setValue: function(value) {
		if (this.getValue() == value) return this;
		var inputs = this.getInputs();
		for (var i = 0, l = inputs.length; i < l; i++) {
			if (inputs[i].value == value) {
				this.$value = value;
				inputs[i].checked = true;
				this.onChange();
				break;
			}
		}
		return this;
	},
	getInputs: function() {
		return this.getElements('input');
	},
	getChecked: function() {
		var input = null, inputs = this.getInputs();
		for (var i = 0, l = inputs.length; i < l; i++) {
			if (inputs[i].checked) {
				input = inputs[i];
				break;
			}
		}
		return input;
	},
	onClick: function() {
		var value = this.getValue();
		this.fireEvent('onClick', this);
		if (this.$value != value) {
			this.$value = value;
			this.onChange();
		}
	},
	onChange: function() {
		this.fireEvent('onChange', [this.getValue(), this]);
	}
});
Moo.registerType('radio-field', Moo.form.RadioField);

Moo.setSelector({
	'Moo.ColorField': 'moo-x-colorfield'
});
Moo.form.ColorField = new Class({
	Extends: Moo.form.TextField,
	Implements: Moo.Destroyable,
	$componentType: 'color-field',
	$componentSelector: Moo.getSelector('Moo.ColorField'),
	options: {
		control: 'color',
		pickerOptions: {},
		autoAnchor: true,
		autoAnchorOptions: null,
		anchorOptions: null
	},
	initialize: function(options) {
		this.$isOpen = false;
		this.$picker = null;
		this.parent(options);
		this.makeDestroyable();
		this.open = this.open.safeBind(this);
		this.close = this.close.safeBind(this);
		this.pick = this.pick.safeBind(this);
		this.field.addEvent('blur', function() {
			this.setColor(this.field.get('value'));
		}.safeBind(this));
		this.pickerTrigger = new Element('div').injectAfter(this.field);
		this.pickerTrigger.set('html', '&nbsp;');
		this.pickerTrigger.addEvent('click', this.open);
		this.pickerTrigger.setStyles({
			border: '1px solid #97A3EB',
			'margin-left': 1,
			'float': 'left',
			width: 18,
			height: 18,
			cursor: 'pointer'
		});
		if (this.options.value) this.setColor(this.options.value);
	},
	destroy: function() {
		Moo.removeMousedown(this.close);
		if (this.$picker) this.$picker.container.destroy();
		this.container.destroy();
	},
	getPicker: function() {
		if (!this.$picker) {
			var pickerOptions = this.options.pickerOptions;
			pickerOptions.onPick = this.pick;
			this.$picker = new Moo.ColorPicker(pickerOptions);
			this.$picker.container.setStyles({
				position: 'absolute',
				'z-index': 10000
			}).addEvent('mousedown', Moo.stopEvent);
			this.$picker.hide().inject(document.body);
		}
		return this.$picker;
	},
	pick: function(color, pickerInstance) {
		this.close();
		this.field.set('value', color);
		this.setColor(color);
		this.control();
		this.fireEvent('onChoose', color, this);
	},
	open: function() {
		if (!this.$isOpen) {
			var picker = this.getPicker();
			if (this.options.autoAnchor) {
				Moo.autoAnchor(picker.container, this.pickerTrigger, this.options.autoAnchorOptions);
			} else {
				Moo.anchor(picker.container, this.pickerTrigger, this.options.anchorOptions);
			}
			Moo.addMousedown(this.close);
			this.$isOpen = true;
		}
	},
	close: function() {
		if (this.$isOpen) {
			this.getPicker().hide();
			Moo.removeMousedown(this.close);
			this.$isOpen = false;
		}
	},
	setColor: function(color) {
		this.pickerTrigger.setStyle('background-color', color);
	}
});
Moo.registerType('color-field', Moo.form.ColorField);

Moo.namespace('Moo.data');

Moo.data.Renderers = {
	string: function(cellContex) {
		return (!cellContex.value || cellContex.value == '' ? '&nbsp;' : cellContex.value);
	},
	integer: function(cellContex) {
		return (!$defined(cellContex.value) || cellContex.value === '' ? '&nbsp;' : '<div style="text-align: right;">' + cellContex.value + '</div>');
	},
	date: function(cellContex) {
		return (!cellContex.value || cellContex.value == '' ? '&nbsp;' : '<div style="text-align: right;">' + cellContex.value + '</div>');
	},
	object: function(cellContext) {
		var html = '';
		if (cellContext.data) {
			for (var p in cellContext.data) {
				html += cellContext.data[p];
			}
		}
		return (html == '' ? '&nbsp;' : html);
	}
};

Moo.data.Sorters = {
	sortBy: function(i, asc) {
		return function(s1, s2) {
			return (s1[i] < s2[i] ? (asc ? -1 : 1) : (asc ? 1 : -1));
		};
	}
};

Moo.data.DataSet = new Class({
	Implements: [Events, Options],
	options: {
		columns: [],
		datas: [],
		onAdd: $empty,
		onInclude: $empty,
		onEmpty: $empty,
		onSort: $empty
	},
	initialize: function(options) {
		this.setOptions(options);
		var columns = [], column = null, cols = this.options.columns;
		for (var i = 0, l = cols.length; i < l; i++) {
			switch ($type(cols[i])) {
				case 'string': column = {name: cols[i]}; break;
				case 'object': column = cols[i]; break;
				default: column = null; break;
			}
			if (column != null) columns.push($merge({
				type: 'string'
			}, column));
		}
		delete this.options.columns;
		this.columns = columns;
		this.setDatas(this.options.datas);
		delete this.options;
	},
	setDatas: function(datas) {
		this.datas = datas;
	},
	getRenderer: function(columnName) {
		var renderer = null;
		for (var i = 0, l = this.columns.length; i < l && renderer == null; i++) if (this.columns[i].name == columnName) renderer = Moo.data.Renderers[this.columns[i].type] || Moo.data.Renderers.string;
		return renderer;
	},
	sort: function(columnName, asc) {
		this.datas = this.datas.sort(Moo.data.Sorters.sortBy(columnName, asc));
		this.fireEvent('onSort');
	}
});
Moo.DataSet = Moo.data.DataSet;

Moo.setSelector({
	'Moo.DataGrid': 'moo-x-datagrid',
	'Moo.DataGrid.Header': 'moo-x-datagrid-header',
	'Moo.DataGrid.Row': 'moo-x-datagrid-row',
	'Moo.DataGrid.Cell': 'moo-x-datagrid-cell'
});
Moo.data.DataGrid = new Class({
	Extends: Moo.Component,
	Implements: Moo.Themeable,
	$componentType: 'datagrid',
	$componentSelector: Moo.getSelector('Moo.DataGrid'),
	$selectors: {
		header: Moo.getSelector('Moo.DataGrid.Header'),
		row: Moo.getSelector('Moo.DataGrid.Row'),
		cell: Moo.getSelector('Moo.DataGrid.Cell')
	},
	$theme: {
		headers: '-headers',
		header: '-header',
		headerLast: '-header-last',
		headerWrapper: '-header-wrapper',
		headerOver: '-header-over',
		headerSortable: '-header-sortable',
		rows: '-rows',
		row: '-row',
		rowOver: '-row-over',
		rowEven: '-row-even',
		cell: '-cell',
		cellWrapper: '-cell-wrapper'
	},
	options: {
		containerCls: 'moo-datagrid',
		dataSet: null,
		headers: [],
		columnsWidth: 80,
		width: 'auto',
		height: 'auto',
		vScrollOffset: 19,
		alternateStyles: true,
		rowOver: true,
		processRow: $empty
	},
	initialize: function(options) {
		this.cellsStyles = [];
		this.renderers = [];
		this.aligns = [];
		this.vAligns = [];
		this.parent(options);
		this.processRow = this.options.processRow;
		delete this.options.processRow;
		this.prepareLayout();
		this.initializeTheme();
		this.dataSet = this.options.dataSet;
		delete this.options.dataSet;
		this.container.setStyle('width', this.options.width);
		this.headersContainer = new Element('div').addClass(this.theme.headers).inject(this.container);
		this.headersTable = new Element('table');
		this.headersTable.set({
			cellpadding: 0,
			cellspacing: 0
		});
		this.headersTBody = new Element('tbody');
		this.headersTable.appendChild(this.headersTBody);
		var row = new Element('tr'), cell = null, cellWrapper = null, sortable = false;
		this.options.headers.each(function(header, columnIndex) {
			this.options.headers[columnIndex] = header = $merge({
				text: '',
				width: null,
				renderer: null,
				columnName: '' + columnIndex,
				sortable: false,
				align: 'left',
				vAlign: 'center'
			}, header);
			cell = new Element('td').addClasses(this.$selectors.header, this.theme.header).inject(row);
			cellWrapper = new Element('div').addClass(this.theme.headerWrapper).inject(cell);
			cellWrapper.set('text', header.text);
			cell.addClass(this.getHeaderCellCls(header.columnName));
			this.cellsStyles[columnIndex] = {width: header.width || this.options.columnsWidth};
			$$(cell, cellWrapper).setStyles(this.cellsStyles[columnIndex]);
			this.renderers[columnIndex] = header.renderer || this.dataSet.getRenderer(header.columnName);
			this.aligns[columnIndex] = header.align;
			this.vAligns[columnIndex] = header.vAlign;
			if (header.sortable === true) {
				cell.addClassOn('enter', this.theme.headerOver);
				sortable = true;
				cellWrapper.addClass(this.theme.headerSortable).addEvent('click', function(e, colName) {
					this.sort(colName);
				}.bindWithEvent(this, header.columnName));
			}
		}, this);
		new Element('td', {
			html: '<div style="width:' + this.options.vScrollOffset + 'px;">&nbsp;</div>'
		}).setStyle('width', this.options.vScrollOffset).addClasses(this.theme.headerLast).inject(row);
		this.headersTBody.appendChild(row);
		this.headersTable.inject(this.headersContainer);
		if (sortable == true) {
			this.build = this.build.safeBind(this);
			this.dataSet.addEvent('onSort', this.build);
			this.addEvent('destroy', function() {
				this.dataSet.removeEvent('onSort', this.build);
			}.safeBind(this));
		}
		this.rowsContainer = new Element('div').inject(this.container).addClass(this.theme.rows);
		this.rowsContainer.addEvent('scroll', function() {
			this.headersContainer.scrollTo(this.rowsContainer.getScroll().x, 0);
		}.safeBind(this));
		Moo.Fx.prepareOverflow(this.rowsContainer);
		this.rowsContainer.setStyle('height', this.options.height);
		this.addEvent('inject', this.build.safeBind(this));
	},
	build: function() {
		this.rowsContainer.empty();
		this.rowsTable = new Element('table');
		this.rowsTable.set({
			cellpadding: 0,
			cellspacing: 0
		});
		this.rowsTBody = new Element('tbody');
		this.rowsTable.appendChild(this.rowsTBody);
		this.rowsTable.inject(this.rowsContainer);
		var datas = this.getDatas();
		for (var i = 0, l = datas.length; i < l; i++) {
			this.addRow(datas[i], false, false);
		}
		if (this.options.alternateStyles === true) this.alternateStyles();
		this.$doLayout();
	},
	addRow: function(data, first, alternateStyles) {
		var row = null, cell = null, content = null, cellWrapper = null, cellContex = null;
		row = new Element('tr').addClasses(this.$selectors.row, this.theme.row);
		if (this.options.rowOver === true) row.addClassOn('over', this.theme.rowOver);
		this.options.headers.each(function(header, columnIndex) {
			cell = new Element('td', {
				align: this.aligns[columnIndex],
				valign: this.vAligns[columnIndex]
			}).addClasses(this.$selectors.cell, this.theme.cell).setStyles(this.cellsStyles[columnIndex]).inject(row);
			cell.addClass(this.getCellsCls(header.columnName));
			cellContex = {
				value: data[header.columnName],
				data: data,
				rowEl: row,
				cellEl: cell,
				gridInstance: this
			};
			content = this.renderers[columnIndex](cellContex);
			cellWrapper = new Element('div').addClass(this.theme.cellWrapper).inject(cell);
			cellWrapper.setStyles(this.cellsStyles[columnIndex]);
			switch ($type(content)) {
				case 'string':
					cellWrapper.set('html', content);
					break;
				case 'element':
					cellWrapper.appendChild(content);
					break;
				case 'moo.component':
					cellWrapper.appendChild(content.container);
					break;
				default:
					cellWrapper.set('html', '&nbsp;');
					break;
			}
		}, this);
		first = first || false;
		if (first == true) row.injectTop(this.rowsTBody);
		else this.rowsTBody.appendChild(row);
		this.processRow({
			rowEl: row,
			data: data,
			gridInstance: this
		});
		if (Moo.toBoolean(alternateStyles, true)) this.alternateStyles();
		return row;
	},
	$doLayout: function() {
		if (this.dataSet.datas && this.dataSet.datas.length != 0) {
			var rowsTableCoord = this.rowsTable.getCoordinates();
			this.container.setStyle('width', rowsTableCoord.width + this.options.vScrollOffset);
		}
	},
	sort: function(columnName) {
		if (!this.sortOrders) this.sortOrders = [];
		this.sortOrders[columnName] = (this.sortOrders[columnName] === true ? false : true);
		this.dataSet.sort(columnName, this.sortOrders[columnName]);
	},
	getDatas: function() {
		return this.dataSet.datas;
	},
	getRows: function() {
		return this.rowsTBody.getElements('tr.' + this.$selectors.row);
	},
	getHeaderCellCls: function(columnName) {
		return this.$selectors.header + '-' + columnName;
	},
	getHeaderCell: function(columnName) {
		return this.headersContainer.getElement('td.' + this.getHeaderCellCls(columnName));
	},
	getCellsCls: function(columnName) {
		return this.$selectors.cell + '-' + columnName;
	},
	getCells: function(columnName) {
		return this.rowsTBody.getElements('td.' + this.getCellsCls(columnName));
	},
	getColumnCells: function(columnName) {
		var cells = this.getCells(columnName), headerCell = this.getHeaderCell(columnName);
		if (headerCell) cells.unshift(headerCell);
		return cells;
	},
	getColumnIndex: function(columnName) {
		var index = -1, headers = this.options.headers;
		for (var i = 0, l = headers.length; i < l; i++) {
			if (headers[i].columnName == columnName) {
				index = i;
				break;
			}
		}
		return index;
	},
	showColumn: function(columnName) {
		var cells = this.getColumnCells(columnName);
		cells.setDisplay('');
		return this;
	},
	hideColumn: function(columnName) {
		var cells = this.getColumnCells(columnName);
		cells.setDisplay('none');
		return this;
	},
	setColumnWidth: function(columnName, width) {
		var index = this.getColumnIndex(columnName);
		if (index != -1) this.cellsStyles[index].width = width;
		var cells = this.getColumnCells(columnName);
		cells.unshift($$(cells.map(function(cell) {
			return cell.getElement(':first-child');
		})));
		cells.setStyle('width', width);
		return this;
	},
	alternateStyles: function() {
		this.getRows().each(function(row, index) {
			row.removeClass(this.theme.rowEven);
			if (index % 2 == 0) row.addClass(this.theme.rowEven);
		}, this);
		return this;
	}
});
Moo.registerType('datagrid', Moo.data.DataGrid);
Moo.DataGrid = Moo.data.DataGrid;

Moo.StyleSwitcher = {
	styles: [],
	initialize: function(styles) {
		this.styles = styles || this.styles || [];
		return this;
	},
	createSwitchers: function(options) {
		options = $merge({
			addDelimitors: true,
			delimitor: ' | ',
			createSwitcher: function(style, styleIndex) {
				var switcher = new Moo.Link({
					text: style.label,
					onClick: function() {
						this.switchStyle(styleIndex);
					}.safeBind(this)
				});
				return switcher;
			}.safeBind(this)
		}, options);
		var container = new Element('div');
		var nbStyles = this.styles.length;
		this.styles.each(function(style, styleIndex) {
			var switcher = options.createSwitcher(style, styleIndex);
			switcher.inject(container);
			if (options.addDelimitors && styleIndex != nbStyles - 1) {
				switch ($type(options.delimitor)) {
					case 'string': container.appendText(options.delimitor); break;
					case 'element': container.appendChild(options.delimitor.clone()); break;
					default: break;
				}
			}
		}, this);
		return container;
	},
	switchStyle: function(styleIndex) {
		var style = this.styles[styleIndex];
		style.files.each(function(fileItem, fileIndex) {
			if (fileItem.asset) fileItem.asset.destroy();
			fileItem.asset = new Asset.css(fileItem.src, {id: 'style' + styleIndex + 'file' + fileIndex});
		});
		return this;
	}
};

Moo.setLang('moo.notifier.title', 'Notification');
Moo.Notifier = {
	options: {
		hideDelay: 4000,
		opacity: 1,
		iconCls: Moo.Icons.information,
		parent: document.body,
		styles: {
			'z-index': 10000,
			position: 'fixed',
			width: 300,
			top: 10,
			right: 10
		},
		draggable: true,
		dragOpacity: 0.7,
		dragCursor: 'move',
		highlight: true,
		highlightStart: null,
		highlightEnd: null
	},
	$initialized: false,
	$index: 0,
	setOptions: function(options) {
		if (!this.$initialized) this.options = $merge(this.options, options);
		return this;
	},
	initialize: function(options) {
		if (!this.$initialized) {
			this.setOptions(options);
			if (!$(this.options.parent)) this.options.parent = document.body;
			this.container = new Element('div').setStyles(this.options.styles).hide().addClass('moo-notifier').setOpacity(this.options.opacity);
			this.container.inject(this.options.parent);
			this.titleContainer = new Element('div', {text: Moo.getLang('moo.notifier.title')}).addClass('moo-title').inject(this.container);
			if (this.options.iconCls != null && this.iconCls != '') this.iconCmp = new Moo.Icon({
				containerCls: this.options.iconCls
			}).inject(this.titleContainer, 'top');
			if (this.options.draggable) {
				this.titleContainer.setStyle('cursor', this.options.dragCursor);
				this.container.makeDraggable({
					handle: this.titleContainer,
					onStart: function() {
						this.container.set('opacity', this.options.dragOpacity);
					}.safeBind(this),
					onComplete: function() {
						this.container.set('opacity', this.options.opacity);
					}.safeBind(this)
				});
			}
			this.notifiersContainer = new Element('div').inject(this.container);
			this.$initialized = true;
		}
	},
	clean: function() {
		if (this.notifiersContainer.getChildren().length == 0) this.container.hide();
	},
	doDestroy: function(container) {
		container.destroy();
		this.clean();
	},
	notify: function(something) {
		this.initialize();
		this.container.show();
		var container = new Element('div').addClass('moo-notify');
		if (++this.$index % 2 == 0) container.addClass('moo-notify-even');
		something = '' + something;
		Moo.inject(something, container);
		container.inject(this.notifiersContainer);
		if (this.options.highlight) container.highlight(this.options.highlightStart, this.options.highlightEnd);
		this.doDestroy.delay(this.options.hideDelay, this, container);
	}
};
Moo.notify = Moo.Notifier.notify.safeBind(Moo.Notifier);

Moo.namespace("Moo.Css");

Moo.Css.StyleSheet = new Class({
    
    Implements : [ Options ],
    
    options : {
	    autoWrite : true
    
    },
    
    initialize : function(options) {
	    
	    this.setOptions(options);
	    this.styles = {};
	    this.renderNode = new Element('div');
	    this.node = new Element('style', {
	        type : 'text/css',
	        id : 'x-moo-css-stylesheet-' + Moo.getUID()
	    });
	    this.node.inject(document.head);
	    
	    if (this.options.autoWrite) {
		    this.write();
	    }
    },
    
    setStyle : function(selector, style) {
	    this.styles[selector] = style;
	    if (this.options.autoWrite) {
		    this.write();
	    }
    },
    
    getStyle : function(selector) {
	    return this.styles[selector];
    },
    
    write : function() {
	    var cssText = "";
	    for ( var selector in this.styles) {
		    cssText += this.toCss(selector, this.styles[selector]);
	    }
	    
	    this.node.set('text', cssText);
	    
    },
    
    toCss : function(selector, style) {
	    var str = selector + '{';
	    switch ($type(style)) {
		    case 'object':
		    case 'hash':
			    Hash.each(style, function(value, key) {
				    str += key + ":" + value + ";";
			    });
			    break;
		    case 'string':
			    str += style;
			    break;
		    
	    }
	    str += '}';
	    return str;
    }
});
Moo.pixelImgSrc = '../static/resources/images/pixel.gif';
Moo.emptyPageSrc = 'Empty.html';

if (Browser.isIE) {
	Moo.Features.fxDuration = 0;
}

Moo.form.TextField.prototype.options.triggersInjection = 'display';
Moo.form.TextareaField.prototype.options.triggersInjection = 'display';
Moo.form.SearchField.prototype.options.triggersInjection = 'display';

Moo.ColorPicker.prototype.options.height = null;
/*

French.

*/
Moo.setLang({
	'moo.commons.ok': 'OK',
	'moo.commons.cancel': 'Annuler',
	'moo.commons.search': 'Rechercher',
	'moo.commons.clear': 'Vider',
	'moo.commons.validate': 'Valider',
	'moo.commons.loading': 'Chargement en cours, veuillez patienter...',
	'moo.commons.no_data': 'Aucune donnée trouvée.',
	'moo.commons.too_many_data': 'Trop de données.',
	'moo.commons.max_elements': 'Vous ne pouvez pas sélectionner plus de {0} élément(s).',
	'moo.calendar.today': 'Aujourd\'hui',
	'moo.calendar.days': ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'],
	'moo.calendar.months': ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'],
	'moo.notifier.title': 'Notification',
	'moo.field.empty': 'Ce champ est requis.',
	'moo.field.alpha': 'Ce champ n\'est pas alpha (ex: abc).',
	'moo.field.alphanum': 'Ce champ n\'est pas alphanumérique (ex: abc123).',
	'moo.field.integer': 'Ce champ n\'est pas un entier (ex: 123).',
	'moo.field.number': 'Ce champ n\'est pas un nombre (ex: 123 ou 123.45).',
	'moo.field.email': 'Ce champ n\'est pas un email valide (ex: moo@moo.com).',
	'moo.field.date': 'Ce champ n\'est pas une date valide (format: JJ/MM/AAAA).',
	'moo.field.time': 'Ce champ n\'est pas un horaire valide (format: hh:mm).',
	'moo.field.minLength': 'Ce champ requiert au moins {0} caractères.',
	'moo.field.dualtime': 'L\'heure de début doit être avant l\'heure de fin.',
	'moo.field.dualtime.timemin': 'L\'heure minimale est {0}.',
	'moo.field.dualtime.timemax': 'L\'heure maximale est {0}.',
	'moo.field.dualtime.delta': 'La durée étant fixée à {0}, l\'heure de début devrait être inférieure à {1}.',
	'moo.field.color': 'Ce champ n\'est pas une couleur valide (ex: #A1B2C3).',
	'moo.field.erase': 'Effacer'
});
// Intégration de l'option notrigger dans le SearchField
// Si activé, alors les triggers sont desactivés
// On ne peut pas travailler sur le readonly => gestab est basé dessus (niveau css, pour ne pas afficher la zone input)
Moo.form.SearchField.prototype.initialize = Moo.form.SearchField.prototype.initialize.callAfter(function() {
	if ($defined(this.options.hidetrigger) && (this.options.hidetrigger == true)) {
		this.searchTrigger.hide();
		this.eraserTrigger.hide();
	}
});

// Création du composant Moo.form.DateAndTime
Moo.Selectors.set({
	'DateAndTimeSelector' : 'moo-x-dateandtimefield'
});
Moo.form.DateAndTime = new Class({
    Extends : Moo.form.DateField,
    $componentType : 'dateandtime-field',
    $componentSelector : Moo.Selectors.get('DateAndTimeSelector'),
    options : {
        field2Cls : 'moo-input',
        field2Width : 40,
        field2Value : '',
        label2Cls : 'moo-label',
        label2Text : '',
        label2Width : '',
        autoComplete : true
    },
    
    Implements : Moo.Langable,
    $lang : {},
    
    initialize : function(options) {
	    this.setOptions(options);
	    this.parent(this.options);
	    
	    var field2id = 'moo-dateandtimefield-2-' + Moo.getUID();
	    this.label2 = new Element('label').set({
	        text : this.options.label2Text,
	        'for' : field2id
	    }).addClass(this.options.label2Cls).setStyle('width', this.options.label2Width).injectAfter(this.field);
	    this.field2 = new Element('input', {
	        type : 'text',
	        id : field2id,
	        maxlength : 5,
	        value : this.options.field2Value
	    }).setStyle('width', this.options.field2Width).injectAfter(this.label2);
	    this.field2.addClass(this.options.field2Cls).addEvents({
	        focus : function() {
		        this.container.addClass(this.options.containerCls + '-focused');
		        this.control();
		        this.fireEvent('onFocus');
	        }.safeBind(this),
	        blur : function() {
		        this.container.removeClass(this.options.containerCls + '-focused');
		        if (this.options.autoComplete === true && !this.isValid) {
			        this.complete();
		        }
		        this.control();
		        this.fireEvent('onBlur');
	        }.safeBind(this),
	        change : function() {
		        this.fireEvent('onChange');
	        }.safeBind(this),
	        keyup : this.control.safeBind(this)
	    });
	    this.dateTrigger.injectAfter(this.field);
    },
    control : function() {
	    this.parent();
	    var value1 = this.field.get('value').trim();
	    if (this.isValid && (value1 != '')) {
		    var value2 = this.field2.get('value').trim(), time1 = Moo.TimeHelper.stringToJson(value1), time2 = Moo.TimeHelper.stringToJson(value2);
		    this.isValid = Moo.TimeHelper.isTime(value2);
		    if (!this.isValid) {
			    this.invalidTitle = this.options.invalidTimeText;
		    }
	    }
	    if (this.isValid) {
		    this.removeClass(this.options.containerCls + '-invalid');
		    this.setTooltip('');
	    } else {
		    this.addClass(this.options.containerCls + '-invalid');
		    this.setTooltip(this.invalidTitle);
	    }
	    return this.isValid;
    },
    getValue2 : function() {
	    return this.field2.get('value');
    },
    setValue2 : function(value) {
	    this.field2.set('value', value);
	    return this;
    },
    setReadonly2 : function(readonly) {
	    this.field2.set('readonly', readonly);
	    return this;
    },
    
    complete : function() {
	    var value = this.field2.get('value'), completed = Moo.TimeHelper.completeTime(value);
	    if ((completed != null) && (value != completed)) {
		    this.field2.set('value', completed);
	    }
	    return this;
    }
});
Moo.registerType('dateandtime-field', Moo.form.DateAndTime);

// Stuff for the menu
Moo.HTML = {};
Moo.pixelImgSrc = '../../../' + Moo.pixelImgSrc;
Moo.Icons.noicon = 'moo-icon-noicon';
Moo.Icons.submenuright = 'moo-icon-submenu-right';
Moo.getEl = $;
Moo.emptyFn = $empty;
Moo.stopEventFn = function(event) {
	event.stop();
};
Moo.implement(Moo, {
    makeDestroyable : function(el, addCls) {
	    if (this.toBoolean(addCls, true)) {
		    el.addClass(Moo.getSelector('Moo.Destroyable'));
	    } else {
		    el.removeClass(Moo.getSelector('Moo.Destroyable'));
	    }
	    return this;
    },
    storeDestroyable : function(el, cmp) {
	    var arr = el.retrieve('moo:destroy', []);
	    arr.push(cmp);
	    return this;
    },
    eliminateDestroyable : function(el, cmp) {
	    var arr = el.retrieve('moo:destroy', []);
	    arr.erase(cmp);
	    return this;
    },
    destroyDestroyable : function(el) {
	    el.retrieve('moo:destroy', []).each(function(cmp) {
		    cmp.destroy();
	    });
	    el.store('moo:destroy', []);
	    return this;
    }
});
Moo.empty = function(el) {
	el = Moo.getEl(el);
	this.getComponents(Moo.getSelector('Moo.Destroyable', true), el).each(function(cmp) {
		Moo.destroyDestroyable(cmp.container);
		cmp.destroy();
	});
	if (el.hasClass(Moo.getSelector('Moo.Destroyable'))) {
		Moo.destroyDestroyable(el);
	}
	el.empty();
	return this;
}.safeBind(Moo);
Moo.implement(Moo.HTML, {
    $icon : '<img src="{src}" class="{containerCls}" style="{style}" />',
    icon : function(options) {
	    var obj = {
		    src : Moo.pixelImgSrc
	    };
	    if (typeof options == 'string') {
		    obj.containerCls = options;
		    obj.style = '';
	    } else {
		    obj.containerCls = options.containerCls;
		    obj.style = options.style || '';
	    }
	    return this.$icon.format(obj);
    }
});
Moo.setSelector({
	'Moo.Menu' : 'moo-x-menu'
});
Moo.Menu = new Class({
    Extends : Moo.Component,
    Binds : [ 'open', 'close' ],
    $componentType : 'menu',
    $componentSelector : Moo.getSelector('Moo.Menu'),
    options : {
        containerCls : 'moo-menu',
        zIndex : 10000,
        items : [],
        autoAnchor : true,
        autoAnchorOptions : null,
        anchorOptions : null,
        openEvent : 'click',
        parentMenuCmp : null,
        onOpen : Moo.emptyFn,
        onClose : Moo.emptyFn
    },
    initialize : function(opener, options) {
	    this.$open = false;
	    this.$mouseIn = false;
	    this.$parentMenu = null;
	    this.$lastSubMenu = null;
	    this.open = this.open.safeBind(this);
	    this.close = this.close.safeBind(this);
	    this.parent(options);
	    this.$parentMenu = this.options.parentMenuCmp;
	    this.container.hide().setStyles({
	        position : 'absolute',
	        'z-index' : this.options.zIndex,
	        top : 0,
	        left : 0
	    }).addEvent('mousedown', Moo.stopEventFn).inject(document.body);
	    this.listContainer = new Element('ul').inject(this.container);
	    this.setItems(this.options.items);
	    delete this.options.items;
	    this.attach(opener);
    },
    empty : function() {
	    var items = this.getItemsWithSubMenu();
	    for ( var i = 0, l = items.length; i < l; i++) {
		    items[i].getSubMenu().destroy();
	    }
	    Moo.empty(this.listContainer);
	    return this;
    },
    destroy : function() {
	    this.detach();
	    this.empty();
	    this.container.destroy();
	    return this;
    },
    open : function() {
	    if (!this.$open) {
		    Moo.addMousedown(this.close);
		    if (this.options.autoAnchor) {
			    Moo.autoAnchor(this.container, this.opener, this.options.autoAnchorOptions);
		    } else {
			    Moo.anchor(this.container, this.opener, this.options.anchorOptions);
		    }
		    this.$open = true;
		    this.fireEvent('onOpen', this);
	    }
	    return this;
    },
    close : function() {
	    if (this.$open) {
		    Moo.removeMousedown(this.close);
		    this.container.hide();
		    this.$open = false;
		    this.fireEvent('onClose', this);
		    if (this.$lastSubMenu) {
			    this.$lastSubMenu.close();
		    }
	    }
	    return this;
    },
    openSubMenu : function(menuItem) {
	    var menu = menuItem.getSubMenu();
	    if (this.$lastSubMenu && (this.$lastSubMenu != menu)) {
		    this.$lastSubMenu.close();
	    }
	    this.$lastSubMenu = menu;
	    menu.open();
	    return this;
    },
    attach : function(opener) {
	    this.detach();
	    this.opener = Moo.getEl(opener);
	    if (!this.$parentMenu) {
		    Moo.makeDestroyable(this.opener).storeDestroyable(this.opener, this);
		    this.opener.addEvent(this.options.openEvent == 'click' ? 'click' : 'mouseenter', this.open);
	    } else {
		    this.openSubMenu = this.$parentMenu.openSubMenu.pass(this.opener.get('mooComponent'), this.$parentMenu);
		    this.opener.addEvent(this.options.openEvent == 'click' ? 'click' : 'mouseenter', this.openSubMenu);
	    }
	    return this;
    },
    detach : function() {
	    if (this.opener) {
		    if (!this.$parentMenu) {
			    Moo.makeDestroyable(this.opener, false).eliminateDestroyable(this.opener, this);
			    this.opener.removeEvent(this.options.openEvent == 'click' ? 'click' : 'mouseenter', this.open);
		    } else {
			    this.opener.removeEvent(this.options.openEvent == 'click' ? 'click' : 'mouseenter', this.openSubMenu);
		    }
	    }
	    this.opener = null;
	    return this;
    },
    addItem : function(item) {
	    item = item || {};
	    item.menuCmp = this;
	    item.parentMenuCmp = this.$parentMenu;
	    new Moo.MenuItem(item).inject(this.listContainer);
	    return this;
    },
    addItems : function(items) {
	    items = items || [];
	    items.each(this.addItem, this);
	    return this;
    },
    setItems : function(items) {
	    this.empty();
	    this.addItems(items);
	    return this;
    },
    getItems : function() {
	    return Moo.getComponents(Moo.getSelector('Moo.MenuItem', true), this.container);
    },
    getItemsWithSubMenu : function() {
	    return this.getItems().filter(function(item) {
		    return item.getSubMenu() != null;
	    });
    },
    getRootMenu : function() {
	    var parent = this.$parentMenu;
	    return (parent ? parent.getRootMenu() : this);
    },
    closeRootMenu : function() {
	    this.getRootMenu().close();
	    return this;
    }
});
Moo.setSelector({
	'Moo.MenuItem' : 'moo-x-menuitem'
});
Moo.MenuItem = new Class({
    Extends : Moo.Component,
    Binds : [ 'onClick' ],
    Implements : Moo.Themeable,
    $theme : {
	    over : '-over'
    },
    $componentType : 'menuitem',
    $componentSelector : Moo.getSelector('Moo.MenuItem'),
    $containerType : 'li',
    options : {
        containerCls : 'moo-menu-item',
        text : null,
        href : 'javascript:void(0);',
        closeOnClick : true,
        iconCls : Moo.Icons.noicon,
        menuOptions : null,
        subMenuIcon : Moo.Icons.submenuright,
        menuCmp : null,
        onClick : Moo.emptyFn
    },
    initialize : function(options) {
	    this.$menuCmp = null;
	    this.$subMenuCmp = null;
	    this.onClick = this.onClick.safeBind(this);
	    this.parent(options);
	    this.$menuCmp = this.options.menuCmp;
	    this.initializeTheme();
	    this.container.addClassOn('enter', this.theme.over);
	    this.linkEl = new Element('a', {
	        text : this.options.text,
	        href : this.options.href
	    }).addEvent('click', this.onClick).inject(this.container);
	    Moo.injectHTML(Moo.HTML.icon(this.options.iconCls), this.linkEl, 'top');
	    this.setSubMenu(this.options.menuOptions);
    },
    onClick : function() {
	    this.fireEvent('onClick', this);
	    if (this.options.closeOnClick === true) {
		    this.$menuCmp.closeRootMenu();
	    }
    },
    getSubMenu : function() {
	    return this.$subMenuCmp;
    },
    setSubMenu : function(options) {
	    if (!options) {
		    return this;
	    }
	    options.zIndex = this.$menuCmp.options.zIndex + 1;
	    options.autoAnchorOptions = options.autoAnchorOptions || {
		    modes : [ {
		        mode : 'tl-tr',
		        offsets : {
			        x : (Browser.isFirefox ? 3 : 2)
		        }
		    } ]
	    };
	    options.parentMenuCmp = this.$menuCmp;
	    options.openEvent = options.openEvent || 'hover';
	    if (!this.$subMenuCmp) {
		    this.$subMenuCmp = new Moo.Menu(this.container, options);
	    }
	    this.subMenuIcon = new Moo.Icon({
	        containerCls : this.options.subMenuIcon,
	        styles : {
	            position : 'absolute',
	            top : 0,
	            right : 0
	        }
	    }).inject(this.container);
	    return this;
    }
});

Moo.implement(Moo.DateHelper, {
	completeDate : function(str) {
		if (this.isDate(str)) {
			return str;
		}
		if (!str || str.contains('/')) {
			return null;
		}
		var res = null;
		switch (str.length) {
			case 6:
				var d = str.substring(0, 2).toInt(), m = str.substring(2, 4).toInt(), y = str.substring(4, 6).toInt();
				res = d.zeroPad(2) + '/' + m.zeroPad(2) + '/2' + y.zeroPad(3);
				break;
			case 8:
				var d = str.substring(0, 2).toInt(), m = str.substring(2, 4).toInt(), y = str.substring(4, 8).toInt();
				res = d.zeroPad(2) + '/' + m.zeroPad(2) + '/' + y.zeroPad(4);
				break;
			default:
				break;
		}
		if (!this.isDate(res)) {
			res = null;
		}
		return res;
	}
});

String.implement({
    leftPad : function(car, maxLength) {
	    var result = '' + this, delta = maxLength - result.length;
	    return car.repeat(delta < 0 ? 0 : delta) + result;
    },
    rightPad : function(car, maxLength) {
	    var result = '' + this, delta = maxLength - result.length;
	    return result + car.repeat(delta < 0 ? 0 : delta);
    },
    zeroPad : function(maxLength) {
	    return this.leftPad('0', maxLength);
    }
});

Number.implement({
	zeroPad : function(maxLength) {
		return ('' + this).zeroPad(maxLength);
	}
});

Moo.getFromPath = function(obj, path) {
	var spath = path.split('.'), result = obj[spath[0]];
	for ( var i = 1, l = spath.length; $defined(result) && (i < l); i++) {
		result = result[spath[i]];
	}
	return result;
}.safeBind(Moo);

Moo.implement(String, {
    capitalize : function() {
	    return this.substring(0, 1).toUpperCase() + this.substring(1);
    },
    
    uncapitalize : function() {
	    return this.substring(0, 1).toLowerCase() + this.substring(1);
    }
});

Element.implement({
	setOpacityZero : function() {
		this.setStyle('opacity', 0);
		this.setStyle('visibility', '');
		return this;
	}
});

Date.implement({
	getLastDayInMonth : function() {
		return new Date(this.getFullYear(), this.getMonth(), Moo.DateHelper.daysInMonth(this.getMonth() + 1, this.getFullYear()));
	}
});

Event.implement({
    isNavKey : function() {
	    var c = this.code, k = this.key;
	    return ((c >= 33) && (c <= 40)); // Page Up/Down, End, Home, Left,
	    // Up,
	    // Right, Down;
    },
    
    isSpecialKey : function() {
	    var c = this.code;
	    return ((c >= 16) && (c <= 20)) || // Shift, Ctrl, Alt, Pause, Caps
	    // Lock
	    ((c >= 44) && (c <= 46)) || // Print Screen, Insert, Delete
	    ((c >= 112) && (c <= 123)) || // F1 -> F12
	    (c == 9) || // Tab
	    (c == 27); // escape
    }

});
Array.prototype.unique = function() {
	var a = [];
	var l = this.length;
	for ( var i = 0; i < l; i++) {
		for ( var j = i + 1; j < l; j++) {
			// If this[i] is found later in the array
			if (this[i] === this[j]) {
				j = ++i;
			}
		}
		a.push(this[i]);
	}
	return a;
};
Array.prototype.intersect = function() {
	if (!arguments.length) {
		return [];
	}
	var a1 = this;
	var a = a2 = null;
	var n = 0;
	while (n < arguments.length) {
		a = [];
		a2 = arguments[n];
		var l = a1.length;
		var l2 = a2.length;
		for ( var i = 0; i < l; i++) {
			for ( var j = 0; j < l2; j++) {
				if (a1[i] === a2[j]) {
					a.push(a1[i]);
				}
			}
		}
		a1 = a;
		n++;
	}
	return a.unique();
};

Document.implement({
	newElement : function(tag, props) {
		var createElementAcceptsHTML;
		try {
			var x = document.createElement('<input name=x>');
			createElementAcceptsHTML = (x.name == 'x');
		} catch (e) {
		}
		
		if (createElementAcceptsHTML && props) {
			[ 'name', 'type', 'checked' ].each(function(attribute) {
				if (!props[attribute]) {
					return;
				}
				tag += ' ' + attribute + '="' + props[attribute] + '"';
				if (attribute != 'checked') {
					delete props[attribute];
				}
			});
			tag = '<' + tag + '>';
		}
		return $.element(this.createElement(tag)).set(props);
	}
});

Moo.window.Selector.prototype.createResult = function(datas) {
	var filterText = '';
	if ($defined($("GDF_COMBOFIELD_FILTER_VALUE"))) {
		if ($("GDF_COMBOFIELD_FILTER_VALUE").value != null) {
			filterText = $("GDF_COMBOFIELD_FILTER_VALUE").value;
		}
	}
	
	var data = null, items = [], item = null;
	for ( var i = 0, l = datas.length; i < l; i++) {
		data = datas[i];
		if (data.label.toUpperCase().contains(filterText.toUpperCase())) {
			item = new Element('div', {
				text : data.label
			}).addEvent('click', function(e, d) {
				this.fireEvent('select', d);
				this.close();
			}.bindWithEvent(this, data)).addClass('moo-item');
			if (i % 2 == 0) {
				item.addClass('moo-item-even');
			}
			item.addClassOn('over', 'moo-item-over');
			items.push(item);
		}
	}
	Moo.update(items, this.resultContainer);
	
};

Moo.window.ListSelector.prototype.createResult = function(datas) {
	var rightItems = [];
	if (this.rightSelect && this.rightSelect.options) {
		for ( var i = 0; i < this.rightSelect.options.length; i++) {
			rightItems.push({
			    text : this.rightSelect.options[i].label,
			    value : this.rightSelect.options[i].value
			});
		}
	}
	var filterText = '';
	if ($defined($("GDF_LISTFIELD_FILTER_VALUE"))) {
		if ($("GDF_LISTFIELD_FILTER_VALUE").value != null) {
			filterText = $("GDF_LISTFIELD_FILTER_VALUE").value;
		}
	}
	
	var data = null, items = [], item = null;
	for ( var i = 0, l = datas.length; i < l; i++) {
		data = datas[i];
		if (data.label.toUpperCase().contains(filterText.toUpperCase())) {
			var found = false;
			for ( var j = 0, l2 = rightItems.length; j < l2; j++) {
				if (rightItems[j].value == data.value) {
					found = true;
					break;
				}
			}
			if (!found) {
				item = new Element('option', {
				    text : data.label,
				    value : data.value
				});
				items.push(item);
			}
		}
	}
	Moo.update(items, this.leftSelect);
	
};

Moo.DateHelper.formatDateMonthAndYear = function(date) {
	var result = [];
	result.push(Moo.getLang('moo.calendar.months')[date.getMonth()]);
	result.push(' ');
	result.push(date.getFullYear());
	return result.join('');
};

String.implement({
	capitaliseFirstLetter : function() {
		return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
	}
});
(function(){var rsplit=function(string,regex){var result=regex.exec(string),retArr=new Array(),first_idx,last_idx,first_bit;while(result!=null){first_idx=result.index;last_idx=regex.lastIndex;if((first_idx)!=0){first_bit=string.substring(0,first_idx);retArr.push(string.substring(0,first_idx));string=string.slice(first_idx)}retArr.push(result[0]);string=string.slice(result[0].length);result=regex.exec(string)}if(!string==""){retArr.push(string)}return retArr},chop=function(string){return string.substr(0,string.length-1)},extend=function(d,s){for(var n in s){if(s.hasOwnProperty(n)){d[n]=s[n]}}};EJS=function(options){options=typeof options=="string"?{view:options}:options;this.set_options(options);if(options.precompiled){this.template={};this.template.process=options.precompiled;EJS.update(this.name,this);return }if(options.element){if(typeof options.element=="string"){var name=options.element;options.element=document.getElementById(options.element);if(options.element==null){throw name+"does not exist!"}}if(options.element.value){this.text=options.element.value}else{this.text=options.element.innerHTML}this.name=options.element.id;this.type="["}else{if(options.url){options.url=EJS.endExt(options.url,this.extMatch);this.name=this.name?this.name:options.url;var url=options.url;var template=EJS.get(this.name,this.cache);if(template){return template}if(template==EJS.INVALID_PATH){return null}try{this.text=EJS.request(url+(this.cache?"":"?"+Math.random()))}catch(e){}if(this.text==null){throw ({type:"EJS",message:"There is no template at "+url})}}}var template=new EJS.Compiler(this.text,this.type);template.compile(options,this.name);EJS.update(this.name,this);this.template=template};EJS.prototype={render:function(object,extra_helpers){object=object||{};this._extra_helpers=extra_helpers;var v=new EJS.Helpers(object,extra_helpers||{});return this.template.process.call(object,object,v)},update:function(element,options){if(typeof element=="string"){element=document.getElementById(element)}if(options==null){_template=this;return function(object){EJS.prototype.update.call(_template,element,object)}}if(typeof options=="string"){params={};params.url=options;_template=this;params.onComplete=function(request){var object=eval(request.responseText);EJS.prototype.update.call(_template,element,object)};EJS.ajax_request(params)}else{element.innerHTML=this.render(options)}},out:function(){return this.template.out},set_options:function(options){this.type=options.type||EJS.type;this.cache=options.cache!=null?options.cache:EJS.cache;this.text=options.text||null;this.name=options.name||null;this.ext=options.ext||EJS.ext;this.extMatch=new RegExp(this.ext.replace(/\./,"."))}};EJS.endExt=function(path,match){if(!path){return null}match.lastIndex=0;return path+(match.test(path)?"":this.ext)};EJS.Scanner=function(source,left,right){extend(this,{left_delimiter:left+"%",right_delimiter:"%"+right,double_left:left+"%%",double_right:"%%"+right,left_equal:left+"%=",left_comment:left+"%#"});this.SplitRegexp=left=="["?/(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/:new RegExp("("+this.double_left+")|(%%"+this.double_right+")|("+this.left_equal+")|("+this.left_comment+")|("+this.left_delimiter+")|("+this.right_delimiter+"\n)|("+this.right_delimiter+")|(\n)");this.source=source;this.stag=null;this.lines=0};EJS.Scanner.to_text=function(input){if(input==null||input===undefined){return""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString()}return""};EJS.Scanner.prototype={scan:function(block){scanline=this.scanline;regex=this.SplitRegexp;if(!this.source==""){var source_split=rsplit(this.source,/\n/);for(var i=0;i<source_split.length;i++){var item=source_split[i];this.scanline(item,regex,block)}}},scanline:function(line,regex,block){this.lines++;var line_split=rsplit(line,regex);for(var i=0;i<line_split.length;i++){var token=line_split[i];if(token!=null){try{block(token,this)}catch(e){throw {type:"EJS.Scanner",line:this.lines}}}}}};EJS.Buffer=function(pre_cmd,post_cmd){this.line=new Array();this.script="";this.pre_cmd=pre_cmd;this.post_cmd=post_cmd;for(var i=0;i<this.pre_cmd.length;i++){this.push(pre_cmd[i])}};EJS.Buffer.prototype={push:function(cmd){this.line.push(cmd)},cr:function(){this.script=this.script+this.line.join("; ");this.line=new Array();this.script=this.script+"\n"},close:function(){if(this.line.length>0){for(var i=0;i<this.post_cmd.length;i++){this.push(pre_cmd[i])}this.script=this.script+this.line.join("; ");line=null}}};EJS.Compiler=function(source,left){this.pre_cmd=["var ___ViewO = [];"];this.post_cmd=new Array();this.source=" ";if(source!=null){if(typeof source=="string"){source=source.replace(/\r\n/g,"\n");source=source.replace(/\r/g,"\n");this.source=source}else{if(source.innerHTML){this.source=source.innerHTML}}if(typeof this.source!="string"){this.source=""}}left=left||"<";var right=">";switch(left){case"[":right="]";break;case"<":break;default:throw left+" is not a supported deliminator";break}this.scanner=new EJS.Scanner(this.source,left,right);this.out=""};EJS.Compiler.prototype={compile:function(options,name){options=options||{};this.out="";var put_cmd="___ViewO.push(";var insert_cmd=put_cmd;var buff=new EJS.Buffer(this.pre_cmd,this.post_cmd);var content="";var clean=function(content){content=content.replace(/\\/g,"\\\\");content=content.replace(/\n/g,"\\n");content=content.replace(/"/g,'\\"');return content};this.scanner.scan(function(token,scanner){if(scanner.stag==null){switch(token){case"\n":content=content+"\n";buff.push(put_cmd+'"'+clean(content)+'");');buff.cr();content="";break;case scanner.left_delimiter:case scanner.left_equal:case scanner.left_comment:scanner.stag=token;if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}content="";break;case scanner.double_left:content=content+scanner.left_delimiter;break;default:content=content+token;break}}else{switch(token){case scanner.right_delimiter:switch(scanner.stag){case scanner.left_delimiter:if(content[content.length-1]=="\n"){content=chop(content);buff.push(content);buff.cr()}else{buff.push(content)}break;case scanner.left_equal:buff.push(insert_cmd+"(EJS.Scanner.to_text("+content+")))");break}scanner.stag=null;content="";break;case scanner.double_right:content=content+scanner.right_delimiter;break;default:content=content+token;break}}});if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}buff.close();this.out=buff.script+";";var to_be_evaled="/*"+name+"*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {"+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";try{eval(to_be_evaled)}catch(e){if(typeof JSLINT!="undefined"){JSLINT(this.out);for(var i=0;i<JSLINT.errors.length;i++){var error=JSLINT.errors[i];if(error.reason!="Unnecessary semicolon."){error.line++;var e=new Error();e.lineNumber=error.line;e.message=error.reason;if(options.view){e.fileName=options.view}throw e}}}else{throw e}}}};EJS.config=function(options){EJS.cache=options.cache!=null?options.cache:EJS.cache;EJS.type=options.type!=null?options.type:EJS.type;EJS.ext=options.ext!=null?options.ext:EJS.ext;var templates_directory=EJS.templates_directory||{};EJS.templates_directory=templates_directory;EJS.get=function(path,cache){if(cache==false){return null}if(templates_directory[path]){return templates_directory[path]}return null};EJS.update=function(path,template){if(path==null){return }templates_directory[path]=template};EJS.INVALID_PATH=-1};EJS.config({cache:true,type:"<",ext:".ejs"});EJS.Helpers=function(data,extras){this._data=data;this._extras=extras;extend(this,extras)};EJS.Helpers.prototype={view:function(options,data,helpers){if(!helpers){helpers=this._extras}if(!data){data=this._data}return new EJS(options).render(data,helpers)},to_text:function(input,null_text){if(input==null||input===undefined){return null_text||""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString().replace(/\n/g,"<br />").replace(/''/g,"'")}return""}};EJS.newRequest=function(){var factories=[function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];for(var i=0;i<factories.length;i++){try{var request=factories[i]();if(request!=null){return request}}catch(e){continue}}};EJS.request=function(path){var request=new EJS.newRequest();request.open("GET",path,false);try{request.send(null)}catch(e){return null}if(request.status==404||request.status==2||(request.status==0&&request.responseText=="")){return null}return request.responseText};EJS.ajax_request=function(params){params.method=(params.method?params.method:"GET");var request=new EJS.newRequest();request.onreadystatechange=function(){if(request.readyState==4){if(request.status==200){params.onComplete(request)}else{params.onComplete(request)}}};request.open(params.method,params.url);request.send(null)}})();EJS.Helpers.prototype.date_tag=function(C,O,A){if(!(O instanceof Date)){O=new Date()}var B=["January","February","March","April","May","June","July","August","September","October","November","December"];var G=[],D=[],P=[];var J=O.getFullYear();var H=O.getMonth();var N=O.getDate();for(var M=J-15;M<J+15;M++){G.push({value:M,text:M})}for(var E=0;E<12;E++){D.push({value:(E),text:B[E]})}for(var I=0;I<31;I++){P.push({value:(I+1),text:(I+1)})}var L=this.select_tag(C+"[year]",J,G,{id:C+"[year]"});var F=this.select_tag(C+"[month]",H,D,{id:C+"[month]"});var K=this.select_tag(C+"[day]",N,P,{id:C+"[day]"});return L+F+K};EJS.Helpers.prototype.form_tag=function(B,A){A=A||{};A.action=B;if(A.multipart==true){A.method="post";A.enctype="multipart/form-data"}return this.start_tag_for("form",A)};EJS.Helpers.prototype.form_tag_end=function(){return this.tag_end("form")};EJS.Helpers.prototype.hidden_field_tag=function(A,C,B){return this.input_field_tag(A,C,"hidden",B)};EJS.Helpers.prototype.input_field_tag=function(A,D,C,B){B=B||{};B.id=B.id||A;B.value=D||"";B.type=C||"text";B.name=A;return this.single_tag_for("input",B)};EJS.Helpers.prototype.is_current_page=function(A){return(window.location.href==A||window.location.pathname==A?true:false)};EJS.Helpers.prototype.link_to=function(B,A,C){if(!B){var B="null"}if(!C){var C={}}if(C.confirm){C.onclick=' var ret_confirm = confirm("'+C.confirm+'"); if(!ret_confirm){ return false;} ';C.confirm=null}C.href=A;return this.start_tag_for("a",C)+B+this.tag_end("a")};EJS.Helpers.prototype.submit_link_to=function(B,A,C){if(!B){var B="null"}if(!C){var C={}}C.onclick=C.onclick||"";if(C.confirm){C.onclick=' var ret_confirm = confirm("'+C.confirm+'"); if(!ret_confirm){ return false;} ';C.confirm=null}C.value=B;C.type="submit";C.onclick=C.onclick+(A?this.url_for(A):"")+"return false;";return this.start_tag_for("input",C)};EJS.Helpers.prototype.link_to_if=function(F,B,A,D,C,E){return this.link_to_unless((F==false),B,A,D,C,E)};EJS.Helpers.prototype.link_to_unless=function(E,B,A,C,D){C=C||{};if(E){if(D&&typeof D=="function"){return D(B,A,C,D)}else{return B}}else{return this.link_to(B,A,C)}};EJS.Helpers.prototype.link_to_unless_current=function(B,A,C,D){C=C||{};return this.link_to_unless(this.is_current_page(A),B,A,C,D)};EJS.Helpers.prototype.password_field_tag=function(A,C,B){return this.input_field_tag(A,C,"password",B)};EJS.Helpers.prototype.select_tag=function(D,G,H,F){F=F||{};F.id=F.id||D;F.value=G;F.name=D;var B="";B+=this.start_tag_for("select",F);for(var E=0;E<H.length;E++){var C=H[E];var A={value:C.value};if(C.value==G){A.selected="selected"}B+=this.start_tag_for("option",A)+C.text+this.tag_end("option")}B+=this.tag_end("select");return B};EJS.Helpers.prototype.single_tag_for=function(A,B){return this.tag(A,B,"/>")};EJS.Helpers.prototype.start_tag_for=function(A,B){return this.tag(A,B)};EJS.Helpers.prototype.submit_tag=function(A,B){B=B||{};B.type=B.type||"submit";B.value=A||"Submit";return this.single_tag_for("input",B)};EJS.Helpers.prototype.tag=function(C,E,D){if(!D){var D=">"}var B=" ";for(var A in E){if(E[A]!=null){var F=E[A].toString()}else{var F=""}if(A=="Class"){A="class"}if(F.indexOf("'")!=-1){B+=A+'="'+F+'" '}else{B+=A+"='"+F+"' "}}return"<"+C+B+D};EJS.Helpers.prototype.tag_end=function(A){return"</"+A+">"};EJS.Helpers.prototype.text_area_tag=function(A,C,B){B=B||{};B.id=B.id||A;B.name=B.name||A;C=C||"";if(B.size){B.cols=B.size.split("x")[0];B.rows=B.size.split("x")[1];delete B.size}B.cols=B.cols||50;B.rows=B.rows||4;return this.start_tag_for("textarea",B)+C+this.tag_end("textarea")};EJS.Helpers.prototype.text_tag=EJS.Helpers.prototype.text_area_tag;EJS.Helpers.prototype.text_field_tag=function(A,C,B){return this.input_field_tag(A,C,"text",B)};EJS.Helpers.prototype.url_for=function(A){return'window.location="'+A+'";'};EJS.Helpers.prototype.img_tag=function(B,C,A){A=A||{};A.src=B;A.alt=C;return this.single_tag_for("img",A)}
/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
/*
Ext JS - JavaScript Library
Copyright (c) 2006-2011, Sencha Inc.
All rights reserved.
licensing@sencha.com
*/


(function() {
    var global = this,
        objectPrototype = Object.prototype,
        toString = Object.prototype.toString,
        enumerables = true,
        enumerablesTest = { toString: 1 },
        i;

    if (typeof Ext === 'undefined') {
        global.Ext = {};
    }

    Ext.global = global;

    for (i in enumerablesTest) {
        enumerables = null;
    }

    if (enumerables) {
        enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
                       'toLocaleString', 'toString', 'constructor'];
    }

    
    Ext.enumerables = enumerables;

    
    Ext.apply = function(object, config, defaults) {
        if (defaults) {
            Ext.apply(object, defaults);
        }

        if (object && config && typeof config === 'object') {
            var i, j, k;

            for (i in config) {
                object[i] = config[i];
            }

            if (enumerables) {
                for (j = enumerables.length; j--;) {
                    k = enumerables[j];
                    if (config.hasOwnProperty(k)) {
                        object[k] = config[k];
                    }
                }
            }
        }

        return object;
    };

    Ext.buildSettings = Ext.apply({
        baseCSSPrefix: 'x-',
        scopeResetCSS: false
    }, Ext.buildSettings || {});

    Ext.apply(Ext, {
        
        emptyFn: function() {},

        baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,

        
        applyIf: function(object, config) {
            var property;

            if (object) {
                for (property in config) {
                    if (object[property] === undefined) {
                        object[property] = config[property];
                    }
                }
            }

            return object;
        },

        
        iterate: function(object, fn, scope) {
            if (Ext.isEmpty(object)) {
                return;
            }

            if (scope === undefined) {
                scope = object;
            }

            if (Ext.isIterable(object)) {
                Ext.Array.each.call(Ext.Array, object, fn, scope);
            }
            else {
                Ext.Object.each.call(Ext.Object, object, fn, scope);
            }
        }
    });

    Ext.apply(Ext, {

        
        extend: function() {
            
            var objectConstructor = objectPrototype.constructor,
                inlineOverrides = function(o) {
                for (var m in o) {
                    if (!o.hasOwnProperty(m)) {
                        continue;
                    }
                    this[m] = o[m];
                }
            };

            return function(subclass, superclass, overrides) {
                
                if (Ext.isObject(superclass)) {
                    overrides = superclass;
                    superclass = subclass;
                    subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
                        superclass.apply(this, arguments);
                    };
                }

                if (!superclass) {
                    Ext.Error.raise({
                        sourceClass: 'Ext',
                        sourceMethod: 'extend',
                        msg: 'Attempting to extend from a class which has not been loaded on the page.'
                    });
                }

                
                var F = function() {},
                    subclassProto, superclassProto = superclass.prototype;

                F.prototype = superclassProto;
                subclassProto = subclass.prototype = new F();
                subclassProto.constructor = subclass;
                subclass.superclass = superclassProto;

                if (superclassProto.constructor === objectConstructor) {
                    superclassProto.constructor = superclass;
                }

                subclass.override = function(overrides) {
                    Ext.override(subclass, overrides);
                };

                subclassProto.override = inlineOverrides;
                subclassProto.proto = subclassProto;

                subclass.override(overrides);
                subclass.extend = function(o) {
                    return Ext.extend(subclass, o);
                };

                return subclass;
            };
        }(),

        
        override: function(cls, overrides) {
            if (cls.prototype.$className) {
                return cls.override(overrides);
            }
            else {
                Ext.apply(cls.prototype, overrides);
            }
        }
    });

    
    Ext.apply(Ext, {

        
        valueFrom: function(value, defaultValue, allowBlank){
            return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
        },

        
        typeOf: function(value) {
            if (value === null) {
                return 'null';
            }

            var type = typeof value;

            if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
                return type;
            }

            var typeToString = toString.call(value);

            switch(typeToString) {
                case '[object Array]':
                    return 'array';
                case '[object Date]':
                    return 'date';
                case '[object Boolean]':
                    return 'boolean';
                case '[object Number]':
                    return 'number';
                case '[object RegExp]':
                    return 'regexp';
            }

            if (type === 'function') {
                return 'function';
            }

            if (type === 'object') {
                if (value.nodeType !== undefined) {
                    if (value.nodeType === 3) {
                        return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
                    }
                    else {
                        return 'element';
                    }
                }

                return 'object';
            }

            Ext.Error.raise({
                sourceClass: 'Ext',
                sourceMethod: 'typeOf',
                msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
            });
        },

        
        isEmpty: function(value, allowEmptyString) {
            return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
        },

        
        isArray: ('isArray' in Array) ? Array.isArray : function(value) {
            return toString.call(value) === '[object Array]';
        },

        
        isDate: function(value) {
            return toString.call(value) === '[object Date]';
        },

        
        isObject: (toString.call(null) === '[object Object]') ?
        function(value) {
            return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.nodeType === undefined;
        } :
        function(value) {
            return toString.call(value) === '[object Object]';
        },

        
        isPrimitive: function(value) {
            var type = typeof value;

            return type === 'string' || type === 'number' || type === 'boolean';
        },

        
        isFunction:
        
        
        (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
            return toString.call(value) === '[object Function]';
        } : function(value) {
            return typeof value === 'function';
        },

        
        isNumber: function(value) {
            return typeof value === 'number' && isFinite(value);
        },

        
        isNumeric: function(value) {
            return !isNaN(parseFloat(value)) && isFinite(value);
        },

        
        isString: function(value) {
            return typeof value === 'string';
        },

        
        isBoolean: function(value) {
            return typeof value === 'boolean';
        },

        
        isElement: function(value) {
            return value ? value.nodeType === 1 : false;
        },

        
        isTextNode: function(value) {
            return value ? value.nodeName === "#text" : false;
        },

        
        isDefined: function(value) {
            return typeof value !== 'undefined';
        },

        
        isIterable: function(value) {
            return (value && typeof value !== 'string') ? value.length !== undefined : false;
        }
    });

    Ext.apply(Ext, {

        
        clone: function(item) {
            if (item === null || item === undefined) {
                return item;
            }

            
            
            
            if (item.nodeType && item.cloneNode) {
                return item.cloneNode(true);
            }

            var type = toString.call(item);

            
            if (type === '[object Date]') {
                return new Date(item.getTime());
            }

            var i, j, k, clone, key;

            
            if (type === '[object Array]') {
                i = item.length;

                clone = [];

                while (i--) {
                    clone[i] = Ext.clone(item[i]);
                }
            }
            
            else if (type === '[object Object]' && item.constructor === Object) {
                clone = {};

                for (key in item) {
                    clone[key] = Ext.clone(item[key]);
                }

                if (enumerables) {
                    for (j = enumerables.length; j--;) {
                        k = enumerables[j];
                        clone[k] = item[k];
                    }
                }
            }

            return clone || item;
        },

        
        getUniqueGlobalNamespace: function() {
            var uniqueGlobalNamespace = this.uniqueGlobalNamespace;

            if (uniqueGlobalNamespace === undefined) {
                var i = 0;

                do {
                    uniqueGlobalNamespace = 'ExtSandbox' + (++i);
                } while (Ext.global[uniqueGlobalNamespace] !== undefined);

                Ext.global[uniqueGlobalNamespace] = Ext;
                this.uniqueGlobalNamespace = uniqueGlobalNamespace;
            }

            return uniqueGlobalNamespace;
        },

        
        functionFactory: function() {
            var args = Array.prototype.slice.call(arguments);

            if (args.length > 0) {
                args[args.length - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' +
                    args[args.length - 1];
            }

            return Function.prototype.constructor.apply(Function.prototype, args);
        }
    });

    
    Ext.type = Ext.typeOf;

})();


(function() {


var version = '4.0.1', Version;
    Ext.Version = Version = Ext.extend(Object, {

        
        constructor: function(version) {
            var parts, releaseStartIndex;

            if (version instanceof Version) {
                return version;
            }

            this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');

            releaseStartIndex = this.version.search(/([^\d\.])/);

            if (releaseStartIndex !== -1) {
                this.release = this.version.substr(releaseStartIndex, version.length);
                this.shortVersion = this.version.substr(0, releaseStartIndex);
            }

            this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');

            parts = this.version.split('.');

            this.major = parseInt(parts.shift() || 0, 10);
            this.minor = parseInt(parts.shift() || 0, 10);
            this.patch = parseInt(parts.shift() || 0, 10);
            this.build = parseInt(parts.shift() || 0, 10);

            return this;
        },

        
        toString: function() {
            return this.version;
        },

        
        valueOf: function() {
            return this.version;
        },

        
        getMajor: function() {
            return this.major || 0;
        },

        
        getMinor: function() {
            return this.minor || 0;
        },

        
        getPatch: function() {
            return this.patch || 0;
        },

        
        getBuild: function() {
            return this.build || 0;
        },

        
        getRelease: function() {
            return this.release || '';
        },

        
        isGreaterThan: function(target) {
            return Version.compare(this.version, target) === 1;
        },

        
        isLessThan: function(target) {
            return Version.compare(this.version, target) === -1;
        },

        
        equals: function(target) {
            return Version.compare(this.version, target) === 0;
        },

        
        match: function(target) {
            target = String(target);
            return this.version.substr(0, target.length) === target;
        },

        
        toArray: function() {
            return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
        },

        
        getShortVersion: function() {
            return this.shortVersion;
        }
    });

    Ext.apply(Version, {
        
        releaseValueMap: {
            'dev': -6,
            'alpha': -5,
            'a': -5,
            'beta': -4,
            'b': -4,
            'rc': -3,
            '#': -2,
            'p': -1,
            'pl': -1
        },

        
        getComponentValue: function(value) {
            return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
        },

        
        compare: function(current, target) {
            var currentValue, targetValue, i;

            current = new Version(current).toArray();
            target = new Version(target).toArray();

            for (i = 0; i < Math.max(current.length, target.length); i++) {
                currentValue = this.getComponentValue(current[i]);
                targetValue = this.getComponentValue(target[i]);

                if (currentValue < targetValue) {
                    return -1;
                } else if (currentValue > targetValue) {
                    return 1;
                }
            }

            return 0;
        }
    });

    Ext.apply(Ext, {
        
        versions: {},

        
        lastRegisteredVersion: null,

        
        setVersion: function(packageName, version) {
            Ext.versions[packageName] = new Version(version);
            Ext.lastRegisteredVersion = Ext.versions[packageName];

            return this;
        },

        
        getVersion: function(packageName) {
            if (packageName === undefined) {
                return Ext.lastRegisteredVersion;
            }

            return Ext.versions[packageName];
        },

        
        deprecate: function(packageName, since, closure, scope) {
            if (Version.compare(Ext.getVersion(packageName), since) < 1) {
                closure.call(scope);
            }
        }
    }); 

    Ext.setVersion('core', version);

})();



Ext.String = {
    trimRegex: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
    escapeRe: /('|\\)/g,
    formatRe: /\{(\d+)\}/g,
    escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,

    /**
     * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
     * @param {String} value The string to encode
     * @return {String} The encoded text
     * @method
     */
    htmlEncode: (function() {
        var entities = {
            '&': '&amp;',
            '>': '&gt;',
            '<': '&lt;',
            '"': '&quot;'
        }, keys = [], p, regex;
        
        for (p in entities) {
            keys.push(p);
        }
        
        regex = new RegExp('(' + keys.join('|') + ')', 'g');
        
        return function(value) {
            return (!value) ? value : String(value).replace(regex, function(match, capture) {
                return entities[capture];    
            });
        };
    })(),

    
    htmlDecode: (function() {
        var entities = {
            '&amp;': '&',
            '&gt;': '>',
            '&lt;': '<',
            '&quot;': '"'
        }, keys = [], p, regex;
        
        for (p in entities) {
            keys.push(p);
        }
        
        regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
        
        return function(value) {
            return (!value) ? value : String(value).replace(regex, function(match, capture) {
                if (capture in entities) {
                    return entities[capture];
                } else {
                    return String.fromCharCode(parseInt(capture.substr(2), 10));
                }
            });
        };
    })(),

    
    urlAppend : function(url, string) {
        if (!Ext.isEmpty(string)) {
            return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
        }

        return url;
    },

    
    trim: function(string) {
        return string.replace(Ext.String.trimRegex, "");
    },

    
    capitalize: function(string) {
        return string.charAt(0).toUpperCase() + string.substr(1);
    },

    
    ellipsis: function(value, len, word) {
        if (value && value.length > len) {
            if (word) {
                var vs = value.substr(0, len - 2),
                index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                if (index !== -1 && index >= (len - 15)) {
                    return vs.substr(0, index) + "...";
                }
            }
            return value.substr(0, len - 3) + "...";
        }
        return value;
    },

    
    escapeRegex: function(string) {
        return string.replace(Ext.String.escapeRegexRe, "\\$1");
    },

    
    escape: function(string) {
        return string.replace(Ext.String.escapeRe, "\\$1");
    },

    
    toggle: function(string, value, other) {
        return string === value ? other : value;
    },

    
    leftPad: function(string, size, character) {
        var result = String(string);
        character = character || " ";
        while (result.length < size) {
            result = character + result;
        }
        return result;
    },

    
    format: function(format) {
        var args = Ext.Array.toArray(arguments, 1);
        return format.replace(Ext.String.formatRe, function(m, i) {
            return args[i];
        });
    }
};



(function() {

var isToFixedBroken = (0.9).toFixed() !== '1';

Ext.Number = {
    
    constrain: function(number, min, max) {
        number = parseFloat(number);

        if (!isNaN(min)) {
            number = Math.max(number, min);
        }
        if (!isNaN(max)) {
            number = Math.min(number, max);
        }
        return number;
    },

    
    toFixed: function(value, precision) {
        if (isToFixedBroken) {
            precision = precision || 0;
            var pow = Math.pow(10, precision);
            return (Math.round(value * pow) / pow).toFixed(precision);
        }

        return value.toFixed(precision);
    },

    
    from: function(value, defaultValue) {
        if (isFinite(value)) {
            value = parseFloat(value);
        }

        return !isNaN(value) ? value : defaultValue;
    }
};

})();


Ext.num = function() {
    return Ext.Number.from.apply(this, arguments);
};

(function() {

    var arrayPrototype = Array.prototype,
        slice = arrayPrototype.slice,
        supportsForEach = 'forEach' in arrayPrototype,
        supportsMap = 'map' in arrayPrototype,
        supportsIndexOf = 'indexOf' in arrayPrototype,
        supportsEvery = 'every' in arrayPrototype,
        supportsSome = 'some' in arrayPrototype,
        supportsFilter = 'filter' in arrayPrototype,
        supportsSort = function() {
            var a = [1,2,3,4,5].sort(function(){ return 0; });
            return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
        }(),
        supportsSliceOnNodeList = true,
        ExtArray;
    try {
        
        if (typeof document !== 'undefined') {
            slice.call(document.getElementsByTagName('body'));
        }
    } catch (e) {
        supportsSliceOnNodeList = false;
    }

    ExtArray = Ext.Array = {
        
        each: function(array, fn, scope, reverse) {
            array = ExtArray.from(array);

            var i,
                ln = array.length;

            if (reverse !== true) {
                for (i = 0; i < ln; i++) {
                    if (fn.call(scope || array[i], array[i], i, array) === false) {
                        return i;
                    }
                }
            }
            else {
                for (i = ln - 1; i > -1; i--) {
                    if (fn.call(scope || array[i], array[i], i, array) === false) {
                        return i;
                    }
                }
            }

            return true;
        },

        
        forEach: function(array, fn, scope) {
            if (supportsForEach) {
                return array.forEach(fn, scope);
            }

            var i = 0,
                ln = array.length;

            for (; i < ln; i++) {
                fn.call(scope, array[i], i, array);
            }
        },

        
        indexOf: function(array, item, from) {
            if (supportsIndexOf) {
                return array.indexOf(item, from);
            }

            var i, length = array.length;

            for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
                if (array[i] === item) {
                    return i;
                }
            }

            return -1;
        },

        
        contains: function(array, item) {
            if (supportsIndexOf) {
                return array.indexOf(item) !== -1;
            }

            var i, ln;

            for (i = 0, ln = array.length; i < ln; i++) {
                if (array[i] === item) {
                    return true;
                }
            }

            return false;
        },

        
        toArray: function(iterable, start, end){
            if (!iterable || !iterable.length) {
                return [];
            }

            if (typeof iterable === 'string') {
                iterable = iterable.split('');
            }

            if (supportsSliceOnNodeList) {
                return slice.call(iterable, start || 0, end || iterable.length);
            }

            var array = [],
                i;

            start = start || 0;
            end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;

            for (i = start; i < end; i++) {
                array.push(iterable[i]);
            }

            return array;
        },

        
        pluck: function(array, propertyName) {
            var ret = [],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                ret.push(item[propertyName]);
            }

            return ret;
        },

        
        map: function(array, fn, scope) {
            if (supportsMap) {
                return array.map(fn, scope);
            }

            var results = [],
                i = 0,
                len = array.length;

            for (; i < len; i++) {
                results[i] = fn.call(scope, array[i], i, array);
            }

            return results;
        },

        
        every: function(array, fn, scope) {
            if (!fn) {
                Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
            }
            if (supportsEvery) {
                return array.every(fn, scope);
            }

            var i = 0,
                ln = array.length;

            for (; i < ln; ++i) {
                if (!fn.call(scope, array[i], i, array)) {
                    return false;
                }
            }

            return true;
        },

        
        some: function(array, fn, scope) {
            if (!fn) {
                Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
            }
            if (supportsSome) {
                return array.some(fn, scope);
            }

            var i = 0,
                ln = array.length;

            for (; i < ln; ++i) {
                if (fn.call(scope, array[i], i, array)) {
                    return true;
                }
            }

            return false;
        },

        
        clean: function(array) {
            var results = [],
                i = 0,
                ln = array.length,
                item;

            for (; i < ln; i++) {
                item = array[i];

                if (!Ext.isEmpty(item)) {
                    results.push(item);
                }
            }

            return results;
        },

        
        unique: function(array) {
            var clone = [],
                i = 0,
                ln = array.length,
                item;

            for (; i < ln; i++) {
                item = array[i];

                if (ExtArray.indexOf(clone, item) === -1) {
                    clone.push(item);
                }
            }

            return clone;
        },

        
        filter: function(array, fn, scope) {
            if (supportsFilter) {
                return array.filter(fn, scope);
            }

            var results = [],
                i = 0,
                ln = array.length;

            for (; i < ln; i++) {
                if (fn.call(scope, array[i], i, array)) {
                    results.push(array[i]);
                }
            }

            return results;
        },

        
        from: function(value, newReference) {
            if (value === undefined || value === null) {
                return [];
            }

            if (Ext.isArray(value)) {
                return (newReference) ? slice.call(value) : value;
            }

            if (value && value.length !== undefined && typeof value !== 'string') {
                return Ext.toArray(value);
            }

            return [value];
        },

        
        remove: function(array, item) {
            var index = ExtArray.indexOf(array, item);

            if (index !== -1) {
                array.splice(index, 1);
            }

            return array;
        },

        
        include: function(array, item) {
            if (!ExtArray.contains(array, item)) {
                array.push(item);
            }
        },

        
        clone: function(array) {
            return slice.call(array);
        },

        
        merge: function() {
            var args = slice.call(arguments),
                array = [],
                i, ln;

            for (i = 0, ln = args.length; i < ln; i++) {
                array = array.concat(args[i]);
            }

            return ExtArray.unique(array);
        },

        
        intersect: function() {
            var intersect = [],
                arrays = slice.call(arguments),
                i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;

            if (!arrays.length) {
                return intersect;
            }

            
            for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
                if (!minArray || array.length < minArray.length) {
                    minArray = array;
                    x = i;
                }
            }

            minArray = Ext.Array.unique(minArray);
            arrays.splice(x, 1);

            
            
            
            for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
                var count = 0;

                for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
                    for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
                        if (x === y) {
                            count++;
                            break;
                        }
                    }
                }

                if (count === arraysLn) {
                    intersect.push(x);
                }
            }

            return intersect;
        },

        
        difference: function(arrayA, arrayB) {
            var clone = slice.call(arrayA),
                ln = clone.length,
                i, j, lnB;

            for (i = 0,lnB = arrayB.length; i < lnB; i++) {
                for (j = 0; j < ln; j++) {
                    if (clone[j] === arrayB[i]) {
                        clone.splice(j, 1);
                        j--;
                        ln--;
                    }
                }
            }

            return clone;
        },

        
        sort: function(array, sortFn) {
            if (supportsSort) {
                if (sortFn) {
                    return array.sort(sortFn);
                } else {
                    return array.sort();
                }
            }

            var length = array.length,
                i = 0,
                comparison,
                j, min, tmp;

            for (; i < length; i++) {
                min = i;
                for (j = i + 1; j < length; j++) {
                    if (sortFn) {
                        comparison = sortFn(array[j], array[min]);
                        if (comparison < 0) {
                            min = j;
                        }
                    } else if (array[j] < array[min]) {
                        min = j;
                    }
                }
                if (min !== i) {
                    tmp = array[i];
                    array[i] = array[min];
                    array[min] = tmp;
                }
            }

            return array;
        },

        
        flatten: function(array) {
            var worker = [];

            function rFlatten(a) {
                var i, ln, v;

                for (i = 0, ln = a.length; i < ln; i++) {
                    v = a[i];

                    if (Ext.isArray(v)) {
                        rFlatten(v);
                    } else {
                        worker.push(v);
                    }
                }

                return worker;
            }

            return rFlatten(array);
        },

        
        min: function(array, comparisonFn) {
            var min = array[0],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                if (comparisonFn) {
                    if (comparisonFn(min, item) === 1) {
                        min = item;
                    }
                }
                else {
                    if (item < min) {
                        min = item;
                    }
                }
            }

            return min;
        },

        
        max: function(array, comparisonFn) {
            var max = array[0],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                if (comparisonFn) {
                    if (comparisonFn(max, item) === -1) {
                        max = item;
                    }
                }
                else {
                    if (item > max) {
                        max = item;
                    }
                }
            }

            return max;
        },

        
        mean: function(array) {
            return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
        },

        
        sum: function(array) {
            var sum = 0,
                i, ln, item;

            for (i = 0,ln = array.length; i < ln; i++) {
                item = array[i];

                sum += item;
            }

            return sum;
        }

    };

    
    Ext.each = Ext.Array.each;

    
    Ext.Array.union = Ext.Array.merge;

    
    Ext.min = Ext.Array.min;

    
    Ext.max = Ext.Array.max;

    
    Ext.sum = Ext.Array.sum;

    
    Ext.mean = Ext.Array.mean;

    
    Ext.flatten = Ext.Array.flatten;

    
    Ext.clean = Ext.Array.clean;

    
    Ext.unique = Ext.Array.unique;

    
    Ext.pluck = Ext.Array.pluck;

    
    Ext.toArray = function() {
        return ExtArray.toArray.apply(ExtArray, arguments);
    }
})();



Ext.Function = {

    
    flexSetter: function(fn) {
        return function(a, b) {
            var k, i;

            if (a === null) {
                return this;
            }

            if (typeof a !== 'string') {
                for (k in a) {
                    if (a.hasOwnProperty(k)) {
                        fn.call(this, k, a[k]);
                    }
                }

                if (Ext.enumerables) {
                    for (i = Ext.enumerables.length; i--;) {
                        k = Ext.enumerables[i];
                        if (a.hasOwnProperty(k)) {
                            fn.call(this, k, a[k]);
                        }
                    }
                }
            } else {
                fn.call(this, a, b);
            }

            return this;
        };
    },

   
    bind: function(fn, scope, args, appendArgs) {
        var method = fn,
            applyArgs;

        return function() {
            var callArgs = args || arguments;

            if (appendArgs === true) {
                callArgs = Array.prototype.slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }
            else if (Ext.isNumber(appendArgs)) {
                callArgs = Array.prototype.slice.call(arguments, 0); 
                applyArgs = [appendArgs, 0].concat(args); 
                Array.prototype.splice.apply(callArgs, applyArgs); 
            }

            return method.apply(scope || window, callArgs);
        };
    },

    
    pass: function(fn, args, scope) {
        if (args) {
            args = Ext.Array.from(args);
        }

        return function() {
            return fn.apply(scope, args.concat(Ext.Array.toArray(arguments)));
        };
    },

    
    alias: function(object, methodName) {
        return function() {
            return object[methodName].apply(object, arguments);
        };
    },

    
    createInterceptor: function(origFn, newFn, scope, returnValue) {
        var method = origFn;
        if (!Ext.isFunction(newFn)) {
            return origFn;
        }
        else {
            return function() {
                var me = this,
                    args = arguments;
                newFn.target = me;
                newFn.method = origFn;
                return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
            };
        }
    },

    
    createDelayed: function(fn, delay, scope, args, appendArgs) {
        if (scope || args) {
            fn = Ext.Function.bind(fn, scope, args, appendArgs);
        }
        return function() {
            var me = this;
            setTimeout(function() {
                fn.apply(me, arguments);
            }, delay);
        };
    },

    
    defer: function(fn, millis, obj, args, appendArgs) {
        fn = Ext.Function.bind(fn, obj, args, appendArgs);
        if (millis > 0) {
            return setTimeout(fn, millis);
        }
        fn();
        return 0;
    },

    
    createSequence: function(origFn, newFn, scope) {
        if (!Ext.isFunction(newFn)) {
            return origFn;
        }
        else {
            return function() {
                var retval = origFn.apply(this || window, arguments);
                newFn.apply(scope || this || window, arguments);
                return retval;
            };
        }
    },

    
    createBuffered: function(fn, buffer, scope, args) {
        return function(){
            var timerId;
            return function() {
                var me = this;
                if (timerId) {
                    clearInterval(timerId);
                    timerId = null;
                }
                timerId = setTimeout(function(){
                    fn.apply(scope || me, args || arguments);
                }, buffer);
            };
        }();
    },

    
    createThrottled: function(fn, interval, scope) {
        var lastCallTime, elapsed, lastArgs, timer, execute = function() {
            fn.apply(scope || this, lastArgs);
            lastCallTime = new Date().getTime();
        };

        return function() {
            elapsed = new Date().getTime() - lastCallTime;
            lastArgs = arguments;

            clearTimeout(timer);
            if (!lastCallTime || (elapsed >= interval)) {
                execute();
            } else {
                timer = setTimeout(execute, interval - elapsed);
            }
        };
    }
};


Ext.defer = Ext.Function.alias(Ext.Function, 'defer');


Ext.pass = Ext.Function.alias(Ext.Function, 'pass');


Ext.bind = Ext.Function.alias(Ext.Function, 'bind');



(function() {

var ExtObject = Ext.Object = {

    
    toQueryObjects: function(name, value, recursive) {
        var self = ExtObject.toQueryObjects,
            objects = [],
            i, ln;

        if (Ext.isArray(value)) {
            for (i = 0, ln = value.length; i < ln; i++) {
                if (recursive) {
                    objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                }
                else {
                    objects.push({
                        name: name,
                        value: value[i]
                    });
                }
            }
        }
        else if (Ext.isObject(value)) {
            for (i in value) {
                if (value.hasOwnProperty(i)) {
                    if (recursive) {
                        objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                    }
                    else {
                        objects.push({
                            name: name,
                            value: value[i]
                        });
                    }
                }
            }
        }
        else {
            objects.push({
                name: name,
                value: value
            });
        }

        return objects;
    },

    
    toQueryString: function(object, recursive) {
        var paramObjects = [],
            params = [],
            i, j, ln, paramObject, value;

        for (i in object) {
            if (object.hasOwnProperty(i)) {
                paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
            }
        }

        for (j = 0, ln = paramObjects.length; j < ln; j++) {
            paramObject = paramObjects[j];
            value = paramObject.value;

            if (Ext.isEmpty(value)) {
                value = '';
            }
            else if (Ext.isDate(value)) {
                value = Ext.Date.toString(value);
            }

            params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
        }

        return params.join('&');
    },

    
    fromQueryString: function(queryString, recursive) {
        var parts = queryString.replace(/^\?/, '').split('&'),
            object = {},
            temp, components, name, value, i, ln,
            part, j, subLn, matchedKeys, matchedName,
            keys, key, nextKey;

        for (i = 0, ln = parts.length; i < ln; i++) {
            part = parts[i];

            if (part.length > 0) {
                components = part.split('=');
                name = decodeURIComponent(components[0]);
                value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';

                if (!recursive) {
                    if (object.hasOwnProperty(name)) {
                        if (!Ext.isArray(object[name])) {
                            object[name] = [object[name]];
                        }

                        object[name].push(value);
                    }
                    else {
                        object[name] = value;
                    }
                }
                else {
                    matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
                    matchedName = name.match(/^([^\[]+)/);

                    if (!matchedName) {
                        Ext.Error.raise({
                            sourceClass: "Ext.Object",
                            sourceMethod: "fromQueryString",
                            queryString: queryString,
                            recursive: recursive,
                            msg: 'Malformed query string given, failed parsing name from "' + part + '"'
                        });
                    }

                    name = matchedName[0];
                    keys = [];

                    if (matchedKeys === null) {
                        object[name] = value;
                        continue;
                    }

                    for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
                        key = matchedKeys[j];
                        key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
                        keys.push(key);
                    }

                    keys.unshift(name);

                    temp = object;

                    for (j = 0, subLn = keys.length; j < subLn; j++) {
                        key = keys[j];

                        if (j === subLn - 1) {
                            if (Ext.isArray(temp) && key === '') {
                                temp.push(value);
                            }
                            else {
                                temp[key] = value;
                            }
                        }
                        else {
                            if (temp[key] === undefined || typeof temp[key] === 'string') {
                                nextKey = keys[j+1];

                                temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
                            }

                            temp = temp[key];
                        }
                    }
                }
            }
        }

        return object;
    },

    
    each: function(object, fn, scope) {
        for (var property in object) {
            if (object.hasOwnProperty(property)) {
                if (fn.call(scope || object, property, object[property], object) === false) {
                    return;
                }
            }
        }
    },

    
    merge: function(source, key, value) {
        if (typeof key === 'string') {
            if (value && value.constructor === Object) {
                if (source[key] && source[key].constructor === Object) {
                    ExtObject.merge(source[key], value);
                }
                else {
                    source[key] = Ext.clone(value);
                }
            }
            else {
                source[key] = value;
            }

            return source;
        }

        var i = 1,
            ln = arguments.length,
            object, property;

        for (; i < ln; i++) {
            object = arguments[i];

            for (property in object) {
                if (object.hasOwnProperty(property)) {
                    ExtObject.merge(source, property, object[property]);
                }
            }
        }

        return source;
    },

    
    getKey: function(object, value) {
        for (var property in object) {
            if (object.hasOwnProperty(property) && object[property] === value) {
                return property;
            }
        }

        return null;
    },

    
    getValues: function(object) {
        var values = [],
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                values.push(object[property]);
            }
        }

        return values;
    },

    
    getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
        var keys = [],
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                keys.push(property);
            }
        }

        return keys;
    },

    
    getSize: function(object) {
        var size = 0,
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                size++;
            }
        }

        return size;
    }
};



Ext.merge = Ext.Object.merge;


Ext.urlEncode = function() {
    var args = Ext.Array.from(arguments),
        prefix = '';

    
    if ((typeof args[1] === 'string')) {
        prefix = args[1] + '&';
        args[1] = false;
    }

    return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
};


Ext.urlDecode = function() {
    return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
};

})();





(function() {




function xf(format) {
    var args = Array.prototype.slice.call(arguments, 1);
    return format.replace(/\{(\d+)\}/g, function(m, i) {
        return args[i];
    });
}

Ext.Date = {
    
    now: Date.now || function() {
        return +new Date();
    },

    
    toString: function(date) {
        var pad = Ext.String.leftPad;

        return date.getFullYear() + "-"
            + pad(date.getMonth() + 1, 2, '0') + "-"
            + pad(date.getDate(), 2, '0') + "T"
            + pad(date.getHours(), 2, '0') + ":"
            + pad(date.getMinutes(), 2, '0') + ":"
            + pad(date.getSeconds(), 2, '0');
    },

    
    getElapsed: function(dateA, dateB) {
        return Math.abs(dateA - (dateB || new Date()));
    },

    
    useStrict: false,

    
    formatCodeToRegex: function(character, currentGroup) {
        
        var p = utilDate.parseCodes[character];

        if (p) {
          p = typeof p == 'function'? p() : p;
          utilDate.parseCodes[character] = p; 
        }

        return p ? Ext.applyIf({
          c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
        }, p) : {
            g: 0,
            c: null,
            s: Ext.String.escapeRegex(character) 
        };
    },

    
    parseFunctions: {
        "MS": function(input, strict) {
            
            
            var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
            var r = (input || '').match(re);
            return r? new Date(((r[1] || '') + r[2]) * 1) : null;
        }
    },
    parseRegexes: [],

    
    formatFunctions: {
        "MS": function() {
            
            return '\\/Date(' + this.getTime() + ')\\/';
        }
    },

    y2kYear : 50,

    
    MILLI : "ms",

    
    SECOND : "s",

    
    MINUTE : "mi",

    
    HOUR : "h",

    
    DAY : "d",

    
    MONTH : "mo",

    
    YEAR : "y",

    
    defaults: {},

    
    dayNames : [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
    ],

    
    monthNames : [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    ],

    
    monthNumbers : {
        Jan:0,
        Feb:1,
        Mar:2,
        Apr:3,
        May:4,
        Jun:5,
        Jul:6,
        Aug:7,
        Sep:8,
        Oct:9,
        Nov:10,
        Dec:11
    },
    
    defaultFormat : "m/d/Y",
    
    getShortMonthName : function(month) {
        return utilDate.monthNames[month].substring(0, 3);
    },

    
    getShortDayName : function(day) {
        return utilDate.dayNames[day].substring(0, 3);
    },

    
    getMonthNumber : function(name) {
        
        return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
    },

    
    formatContainsHourInfo : (function(){
        var stripEscapeRe = /(\\.)/g,
            hourInfoRe = /([gGhHisucUOPZ]|MS)/;
        return function(format){
            return hourInfoRe.test(format.replace(stripEscapeRe, ''));
        };
    })(),

    
    formatContainsDateInfo : (function(){
        var stripEscapeRe = /(\\.)/g,
            dateInfoRe = /([djzmnYycU]|MS)/;

        return function(format){
            return dateInfoRe.test(format.replace(stripEscapeRe, ''));
        };
    })(),

    
    formatCodes : {
        d: "Ext.String.leftPad(this.getDate(), 2, '0')",
        D: "Ext.Date.getShortDayName(this.getDay())", 
        j: "this.getDate()",
        l: "Ext.Date.dayNames[this.getDay()]",
        N: "(this.getDay() ? this.getDay() : 7)",
        S: "Ext.Date.getSuffix(this)",
        w: "this.getDay()",
        z: "Ext.Date.getDayOfYear(this)",
        W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
        F: "Ext.Date.monthNames[this.getMonth()]",
        m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
        M: "Ext.Date.getShortMonthName(this.getMonth())", 
        n: "(this.getMonth() + 1)",
        t: "Ext.Date.getDaysInMonth(this)",
        L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
        o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
        Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
        y: "('' + this.getFullYear()).substring(2, 4)",
        a: "(this.getHours() < 12 ? 'am' : 'pm')",
        A: "(this.getHours() < 12 ? 'AM' : 'PM')",
        g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
        G: "this.getHours()",
        h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
        H: "Ext.String.leftPad(this.getHours(), 2, '0')",
        i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
        s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
        u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
        O: "Ext.Date.getGMTOffset(this)",
        P: "Ext.Date.getGMTOffset(this, true)",
        T: "Ext.Date.getTimezone(this)",
        Z: "(this.getTimezoneOffset() * -60)",

        c: function() { 
            for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
                var e = c.charAt(i);
                code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); 
            }
            return code.join(" + ");
        },
        

        U: "Math.round(this.getTime() / 1000)"
    },

    
    isValid : function(y, m, d, h, i, s, ms) {
        
        h = h || 0;
        i = i || 0;
        s = s || 0;
        ms = ms || 0;

        
        var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);

        return y == dt.getFullYear() &&
            m == dt.getMonth() + 1 &&
            d == dt.getDate() &&
            h == dt.getHours() &&
            i == dt.getMinutes() &&
            s == dt.getSeconds() &&
            ms == dt.getMilliseconds();
    },

    
    parse : function(input, format, strict) {
        var p = utilDate.parseFunctions;
        if (p[format] == null) {
            utilDate.createParser(format);
        }
        return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
    },

    
    parseDate: function(input, format, strict){
        return utilDate.parse(input, format, strict);
    },


    
    getFormatCode : function(character) {
        var f = utilDate.formatCodes[character];

        if (f) {
          f = typeof f == 'function'? f() : f;
          utilDate.formatCodes[character] = f; 
        }

        
        return f || ("'" + Ext.String.escape(character) + "'");
    },

    
    createFormat : function(format) {
        var code = [],
            special = false,
            ch = '';

        for (var i = 0; i < format.length; ++i) {
            ch = format.charAt(i);
            if (!special && ch == "\\") {
                special = true;
            } else if (special) {
                special = false;
                code.push("'" + Ext.String.escape(ch) + "'");
            } else {
                code.push(utilDate.getFormatCode(ch));
            }
        }
        utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
    },

    
    createParser : (function() {
        var code = [
            "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
                "def = Ext.Date.defaults,",
                "results = String(input).match(Ext.Date.parseRegexes[{0}]);", 

            "if(results){",
                "{1}",

                "if(u != null){", 
                    "v = new Date(u * 1000);", 
                "}else{",
                    
                    
                    
                    "dt = Ext.Date.clearTime(new Date);",

                    
                    "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
                    "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
                    "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",

                    
                    "h  = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
                    "i  = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
                    "s  = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
                    "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",

                    "if(z >= 0 && y >= 0){",
                        
                        

                        
                        
                        "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",

                        
                        "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
                    "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", 
                        "v = null;", 
                    "}else{",
                        
                        
                        "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
                    "}",
                "}",
            "}",

            "if(v){",
                
                "if(zz != null){",
                    
                    "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
                "}else if(o){",
                    
                    "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
                "}",
            "}",

            "return v;"
        ].join('\n');

        return function(format) {
            var regexNum = utilDate.parseRegexes.length,
                currentGroup = 1,
                calc = [],
                regex = [],
                special = false,
                ch = "";

            for (var i = 0; i < format.length; ++i) {
                ch = format.charAt(i);
                if (!special && ch == "\\") {
                    special = true;
                } else if (special) {
                    special = false;
                    regex.push(Ext.String.escape(ch));
                } else {
                    var obj = utilDate.formatCodeToRegex(ch, currentGroup);
                    currentGroup += obj.g;
                    regex.push(obj.s);
                    if (obj.g && obj.c) {
                        calc.push(obj.c);
                    }
                }
            }

            utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
            utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
        };
    })(),

    
    parseCodes : {
        
        d: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        j: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,2})" 
        },
        D: function() {
            for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); 
            return {
                g:0,
                c:null,
                s:"(?:" + a.join("|") +")"
            };
        },
        l: function() {
            return {
                g:0,
                c:null,
                s:"(?:" + utilDate.dayNames.join("|") + ")"
            };
        },
        N: {
            g:0,
            c:null,
            s:"[1-7]" 
        },
        S: {
            g:0,
            c:null,
            s:"(?:st|nd|rd|th)"
        },
        w: {
            g:0,
            c:null,
            s:"[0-6]" 
        },
        z: {
            g:1,
            c:"z = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,3})" 
        },
        W: {
            g:0,
            c:null,
            s:"(?:\\d{2})" 
        },
        F: function() {
            return {
                g:1,
                c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", 
                s:"(" + utilDate.monthNames.join("|") + ")"
            };
        },
        M: function() {
            for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); 
            return Ext.applyIf({
                s:"(" + a.join("|") + ")"
            }, utilDate.formatCodeToRegex("F"));
        },
        m: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(\\d{2})" 
        },
        n: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(\\d{1,2})" 
        },
        t: {
            g:0,
            c:null,
            s:"(?:\\d{2})" 
        },
        L: {
            g:0,
            c:null,
            s:"(?:1|0)"
        },
        o: function() {
            return utilDate.formatCodeToRegex("Y");
        },
        Y: {
            g:1,
            c:"y = parseInt(results[{0}], 10);\n",
            s:"(\\d{4})" 
        },
        y: {
            g:1,
            c:"var ty = parseInt(results[{0}], 10);\n"
                + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", 
            s:"(\\d{1,2})"
        },
        
        a: {
            g:1,
            c:"if (/(am)/i.test(results[{0}])) {\n"
                + "if (!h || h == 12) { h = 0; }\n"
                + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
            s:"(am|pm|AM|PM)"
        },
        A: {
            g:1,
            c:"if (/(am)/i.test(results[{0}])) {\n"
                + "if (!h || h == 12) { h = 0; }\n"
                + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
            s:"(AM|PM|am|pm)"
        },
        g: function() {
            return utilDate.formatCodeToRegex("G");
        },
        G: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,2})" 
        },
        h: function() {
            return utilDate.formatCodeToRegex("H");
        },
        H: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        i: {
            g:1,
            c:"i = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        s: {
            g:1,
            c:"s = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        u: {
            g:1,
            c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
            s:"(\\d+)" 
        },
        O: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", 
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", 
                    "mn = o.substring(3,5) % 60;", 
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" 
            ].join("\n"),
            s: "([+\-]\\d{4})" 
        },
        P: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", 
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", 
                    "mn = o.substring(4,6) % 60;", 
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" 
            ].join("\n"),
            s: "([+\-]\\d{2}:\\d{2})" 
        },
        T: {
            g:0,
            c:null,
            s:"[A-Z]{1,4}" 
        },
        Z: {
            g:1,
            c:"zz = results[{0}] * 1;\n" 
                  + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
            s:"([+\-]?\\d{1,5})" 
        },
        c: function() {
            var calc = [],
                arr = [
                    utilDate.formatCodeToRegex("Y", 1), 
                    utilDate.formatCodeToRegex("m", 2), 
                    utilDate.formatCodeToRegex("d", 3), 
                    utilDate.formatCodeToRegex("h", 4), 
                    utilDate.formatCodeToRegex("i", 5), 
                    utilDate.formatCodeToRegex("s", 6), 
                    {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, 
                    {c:[ 
                        "if(results[8]) {", 
                            "if(results[8] == 'Z'){",
                                "zz = 0;", 
                            "}else if (results[8].indexOf(':') > -1){",
                                utilDate.formatCodeToRegex("P", 8).c, 
                            "}else{",
                                utilDate.formatCodeToRegex("O", 8).c, 
                            "}",
                        "}"
                    ].join('\n')}
                ];

            for (var i = 0, l = arr.length; i < l; ++i) {
                calc.push(arr[i].c);
            }

            return {
                g:1,
                c:calc.join(""),
                s:[
                    arr[0].s, 
                    "(?:", "-", arr[1].s, 
                        "(?:", "-", arr[2].s, 
                            "(?:",
                                "(?:T| )?", 
                                arr[3].s, ":", arr[4].s,  
                                "(?::", arr[5].s, ")?", 
                                "(?:(?:\\.|,)(\\d+))?", 
                                "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", 
                            ")?",
                        ")?",
                    ")?"
                ].join("")
            };
        },
        U: {
            g:1,
            c:"u = parseInt(results[{0}], 10);\n",
            s:"(-?\\d+)" 
        }
    },

    
    
    dateFormat: function(date, format) {
        return utilDate.format(date, format);
    },

    
    format: function(date, format) {
        if (utilDate.formatFunctions[format] == null) {
            utilDate.createFormat(format);
        }
        var result = utilDate.formatFunctions[format].call(date);
        return result + '';
    },

    
    getTimezone : function(date) {
        
        
        
        
        
        
        
        
        
        
        
        
        return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
    },

    
    getGMTOffset : function(date, colon) {
        var offset = date.getTimezoneOffset();
        return (offset > 0 ? "-" : "+")
            + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
            + (colon ? ":" : "")
            + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
    },

    
    getDayOfYear: function(date) {
        var num = 0,
            d = Ext.Date.clone(date),
            m = date.getMonth(),
            i;

        for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
            num += utilDate.getDaysInMonth(d);
        }
        return num + date.getDate() - 1;
    },

    
    getWeekOfYear : (function() {
        
        var ms1d = 864e5, 
            ms7d = 7 * ms1d; 

        return function(date) { 
            var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, 
                AWN = Math.floor(DC3 / 7), 
                Wyr = new Date(AWN * ms7d).getUTCFullYear();

            return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
        };
    })(),

    
    isLeapYear : function(date) {
        var year = date.getFullYear();
        return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
    },

    
    getFirstDayOfMonth : function(date) {
        var day = (date.getDay() - (date.getDate() - 1)) % 7;
        return (day < 0) ? (day + 7) : day;
    },

    
    getLastDayOfMonth : function(date) {
        return utilDate.getLastDateOfMonth(date).getDay();
    },


    
    getFirstDateOfMonth : function(date) {
        return new Date(date.getFullYear(), date.getMonth(), 1);
    },

    
    getLastDateOfMonth : function(date) {
        return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
    },

    
    getDaysInMonth: (function() {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

        return function(date) { 
            var m = date.getMonth();

            return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
        };
    })(),

    
    getSuffix : function(date) {
        switch (date.getDate()) {
            case 1:
            case 21:
            case 31:
                return "st";
            case 2:
            case 22:
                return "nd";
            case 3:
            case 23:
                return "rd";
            default:
                return "th";
        }
    },

    
    clone : function(date) {
        return new Date(date.getTime());
    },

    
    isDST : function(date) {
        
        
        return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
    },

    
    clearTime : function(date, clone) {
        if (clone) {
            return Ext.Date.clearTime(Ext.Date.clone(date));
        }

        
        var d = date.getDate();

        
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);

        if (date.getDate() != d) { 
            
            

            
            for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));

            date.setDate(d);
            date.setHours(c.getHours());
        }

        return date;
    },

    
    add : function(date, interval, value) {
        var d = Ext.Date.clone(date),
            Date = Ext.Date;
        if (!interval || value === 0) return d;

        switch(interval.toLowerCase()) {
            case Ext.Date.MILLI:
                d.setMilliseconds(d.getMilliseconds() + value);
                break;
            case Ext.Date.SECOND:
                d.setSeconds(d.getSeconds() + value);
                break;
            case Ext.Date.MINUTE:
                d.setMinutes(d.getMinutes() + value);
                break;
            case Ext.Date.HOUR:
                d.setHours(d.getHours() + value);
                break;
            case Ext.Date.DAY:
                d.setDate(d.getDate() + value);
                break;
            case Ext.Date.MONTH:
                var day = date.getDate();
                if (day > 28) {
                    day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
                }
                d.setDate(day);
                d.setMonth(date.getMonth() + value);
                break;
            case Ext.Date.YEAR:
                d.setFullYear(date.getFullYear() + value);
                break;
        }
        return d;
    },

    
    between : function(date, start, end) {
        var t = date.getTime();
        return start.getTime() <= t && t <= end.getTime();
    },

    
    compat: function() {
        var nativeDate = window.Date,
            p, u,
            statics = ['useStrict', 'formatCodeToRegex', 'parseFunctions', 'parseRegexes', 'formatFunctions', 'y2kYear', 'MILLI', 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'MONTH', 'YEAR', 'defaults', 'dayNames', 'monthNames', 'monthNumbers', 'getShortMonthName', 'getShortDayName', 'getMonthNumber', 'formatCodes', 'isValid', 'parseDate', 'getFormatCode', 'createFormat', 'createParser', 'parseCodes'],
            proto = ['dateFormat', 'format', 'getTimezone', 'getGMTOffset', 'getDayOfYear', 'getWeekOfYear', 'isLeapYear', 'getFirstDayOfMonth', 'getLastDayOfMonth', 'getDaysInMonth', 'getSuffix', 'clone', 'isDST', 'clearTime', 'add', 'between'];

        
        Ext.Array.forEach(statics, function(s) {
            nativeDate[s] = utilDate[s];
        });

        
        Ext.Array.forEach(proto, function(s) {
            nativeDate.prototype[s] = function() {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(this);
                return utilDate[s].apply(utilDate, args);
            };
        });
    }
};

var utilDate = Ext.Date;

})();


(function(flexSetter) {

var Base = Ext.Base = function() {};
    Base.prototype = {
        $className: 'Ext.Base',

        $class: Base,

        
        self: Base,

        
        constructor: function() {
            return this;
        },

        
        initConfig: function(config) {
            if (!this.$configInited) {
                this.config = Ext.Object.merge({}, this.config || {}, config || {});

                this.applyConfig(this.config);

                this.$configInited = true;
            }

            return this;
        },

        
        setConfig: function(config) {
            this.applyConfig(config || {});

            return this;
        },

        
        applyConfig: flexSetter(function(name, value) {
            var setter = 'set' + Ext.String.capitalize(name);

            if (typeof this[setter] === 'function') {
                this[setter].call(this, value);
            }

            return this;
        }),

        
        callParent: function(args) {
            var method = this.callParent.caller,
                parentClass, methodName;

            if (!method.$owner) {
                if (!method.caller) {
                    Ext.Error.raise({
                        sourceClass: Ext.getClassName(this),
                        sourceMethod: "callParent",
                        msg: "Attempting to call a protected method from the public scope, which is not allowed"
                    });
                }

                method = method.caller;
            }

            parentClass = method.$owner.superclass;
            methodName = method.$name;

            if (!(methodName in parentClass)) {
                Ext.Error.raise({
                    sourceClass: Ext.getClassName(this),
                    sourceMethod: methodName,
                    msg: "this.callParent() was called but there's no such method (" + methodName +
                         ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
                 });
            }

            return parentClass[methodName].apply(this, args || []);
        },


        
        statics: function() {
            var method = this.statics.caller,
                self = this.self;

            if (!method) {
                return self;
            }

            return method.$owner;
        },

        
        callOverridden: function(args) {
            var method = this.callOverridden.caller;

            if (!method.$owner) {
                Ext.Error.raise({
                    sourceClass: Ext.getClassName(this),
                    sourceMethod: "callOverridden",
                    msg: "Attempting to call a protected method from the public scope, which is not allowed"
                });
            }

            if (!method.$previous) {
                Ext.Error.raise({
                    sourceClass: Ext.getClassName(this),
                    sourceMethod: "callOverridden",
                    msg: "this.callOverridden was called in '" + method.$name +
                         "' but this method has never been overridden"
                 });
            }

            return method.$previous.apply(this, args || []);
        },

        destroy: function() {}
    };

    
    Ext.apply(Ext.Base, {
        
        create: function() {
            return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
        },

        
        own: flexSetter(function(name, value) {
            if (typeof value === 'function') {
                this.ownMethod(name, value);
            }
            else {
                this.prototype[name] = value;
            }
        }),

        
        ownMethod: function(name, fn) {
            var originalFn;

            if (fn.$owner !== undefined && fn !== Ext.emptyFn) {
                originalFn = fn;

                fn = function() {
                    return originalFn.apply(this, arguments);
                };
            }

            var className;
            className = Ext.getClassName(this);
            if (className) {
                fn.displayName = className + '#' + name;
            }
            fn.$owner = this;
            fn.$name = name;

            this.prototype[name] = fn;
        },

        
        addStatics: function(members) {
            for (var name in members) {
                if (members.hasOwnProperty(name)) {
                    this[name] = members[name];
                }
            }

            return this;
        },

        
        implement: function(members) {
            var prototype = this.prototype,
                name, i, member, previous;
            var className = Ext.getClassName(this);
            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];

                    if (typeof member === 'function') {
                        member.$owner = this;
                        member.$name = name;
                        if (className) {
                            member.displayName = className + '#' + name;
                        }
                    }

                    prototype[name] = member;
                }
            }

            if (Ext.enumerables) {
                var enumerables = Ext.enumerables;

                for (i = enumerables.length; i--;) {
                    name = enumerables[i];

                    if (members.hasOwnProperty(name)) {
                        member = members[name];
                        member.$owner = this;
                        member.$name = name;
                        prototype[name] = member;
                    }
                }
            }
        },

        
        borrow: function(fromClass, members) {
            var fromPrototype = fromClass.prototype,
                i, ln, member;

            members = Ext.Array.from(members);

            for (i = 0, ln = members.length; i < ln; i++) {
                member = members[i];

                this.own(member, fromPrototype[member]);
            }

            return this;
        },

        
        override: function(members) {
            var prototype = this.prototype,
                name, i, member, previous;

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];

                    if (typeof member === 'function') {
                        if (typeof prototype[name] === 'function') {
                            previous = prototype[name];
                            member.$previous = previous;
                        }

                        this.ownMethod(name, member);
                    }
                    else {
                        prototype[name] = member;
                    }
                }
            }

            if (Ext.enumerables) {
                var enumerables = Ext.enumerables;

                for (i = enumerables.length; i--;) {
                    name = enumerables[i];

                    if (members.hasOwnProperty(name)) {
                        if (prototype[name] !== undefined) {
                            previous = prototype[name];
                            members[name].$previous = previous;
                        }

                        this.ownMethod(name, members[name]);
                    }
                }
            }

            return this;
        },

        
        mixin: flexSetter(function(name, cls) {
            var mixin = cls.prototype,
                my = this.prototype,
                i, fn;

            for (i in mixin) {
                if (mixin.hasOwnProperty(i)) {
                    if (my[i] === undefined) {
                        if (typeof mixin[i] === 'function') {
                            fn = mixin[i];

                            if (fn.$owner === undefined) {
                                this.ownMethod(i, fn);
                            }
                            else {
                                my[i] = fn;
                            }
                        }
                        else {
                            my[i] = mixin[i];
                        }
                    }
                    else if (i === 'config' && my.config && mixin.config) {
                        Ext.Object.merge(my.config, mixin.config);
                    }
                }
            }

            if (my.mixins === undefined) {
                my.mixins = {};
            }

            my.mixins[name] = mixin;
        }),

        
        getName: function() {
            return Ext.getClassName(this);
        },

        
        createAlias: flexSetter(function(alias, origin) {
            this.prototype[alias] = this.prototype[origin];
        })
    });

})(Ext.Function.flexSetter);


(function() {

    var Class,
        Base = Ext.Base,
        baseStaticProperties = [],
        baseStaticProperty;

    for (baseStaticProperty in Base) {
        if (Base.hasOwnProperty(baseStaticProperty)) {
            baseStaticProperties.push(baseStaticProperty);
        }
    }

    
    Ext.Class = Class = function(newClass, classData, onClassCreated) {
        if (typeof newClass !== 'function') {
            onClassCreated = classData;
            classData = newClass;
            newClass = function() {
                return this.constructor.apply(this, arguments);
            };
        }

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

        var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(),
            registeredPreprocessors = Class.getPreprocessors(),
            index = 0,
            preprocessors = [],
            preprocessor, preprocessors, staticPropertyName, process, i, j, ln;

        for (i = 0, ln = baseStaticProperties.length; i < ln; i++) {
            staticPropertyName = baseStaticProperties[i];
            newClass[staticPropertyName] = Base[staticPropertyName];
        }

        delete classData.preprocessors;

        for (j = 0, ln = preprocessorStack.length; j < ln; j++) {
            preprocessor = preprocessorStack[j];

            if (typeof preprocessor === 'string') {
                preprocessor = registeredPreprocessors[preprocessor];

                if (!preprocessor.always) {
                    if (classData.hasOwnProperty(preprocessor.name)) {
                        preprocessors.push(preprocessor.fn);
                    }
                }
                else {
                    preprocessors.push(preprocessor.fn);
                }
            }
            else {
                preprocessors.push(preprocessor);
            }
        }

        classData.onClassCreated = onClassCreated;

        classData.onBeforeClassCreated = function(cls, data) {
            onClassCreated = data.onClassCreated;

            delete data.onBeforeClassCreated;
            delete data.onClassCreated;

            cls.implement(data);

            if (onClassCreated) {
                onClassCreated.call(cls, cls);
            }
        };

        process = function(cls, data) {
            preprocessor = preprocessors[index++];

            if (!preprocessor) {
                data.onBeforeClassCreated.apply(this, arguments);
                return;
            }

            if (preprocessor.call(this, cls, data, process) !== false) {
                process.apply(this, arguments);
            }
        };

        process.call(Class, newClass, classData);

        return newClass;
    };

    Ext.apply(Class, {

        
        preprocessors: {},

        
        registerPreprocessor: function(name, fn, always) {
            this.preprocessors[name] = {
                name: name,
                always: always ||  false,
                fn: fn
            };

            return this;
        },

        
        getPreprocessor: function(name) {
            return this.preprocessors[name];
        },

        getPreprocessors: function() {
            return this.preprocessors;
        },

        
        getDefaultPreprocessors: function() {
            return this.defaultPreprocessors || [];
        },

        
        setDefaultPreprocessors: function(preprocessors) {
            this.defaultPreprocessors = Ext.Array.from(preprocessors);

            return this;
        },

        
        setDefaultPreprocessorPosition: function(name, offset, relativeName) {
            var defaultPreprocessors = this.defaultPreprocessors,
                index;

            if (typeof offset === 'string') {
                if (offset === 'first') {
                    defaultPreprocessors.unshift(name);

                    return this;
                }
                else if (offset === 'last') {
                    defaultPreprocessors.push(name);

                    return this;
                }

                offset = (offset === 'after') ? 1 : -1;
            }

            index = Ext.Array.indexOf(defaultPreprocessors, relativeName);

            if (index !== -1) {
                defaultPreprocessors.splice(Math.max(0, index + offset), 0, name);
            }

            return this;
        }
    });

    Class.registerPreprocessor('extend', function(cls, data) {
        var extend = data.extend,
            base = Ext.Base,
            basePrototype = base.prototype,
            prototype = function() {},
            parent, i, k, ln, staticName, parentStatics,
            parentPrototype, clsPrototype;

        if (extend && extend !== Object) {
            parent = extend;
        }
        else {
            parent = base;
        }

        parentPrototype = parent.prototype;

        prototype.prototype = parentPrototype;
        clsPrototype = cls.prototype = new prototype();

        if (!('$class' in parent)) {
            for (i in basePrototype) {
                if (!parentPrototype[i]) {
                    parentPrototype[i] = basePrototype[i];
                }
            }
        }

        clsPrototype.self = cls;

        cls.superclass = clsPrototype.superclass = parentPrototype;

        delete data.extend;

        
        parentStatics = parentPrototype.$inheritableStatics;

        if (parentStatics) {
            for (k = 0, ln = parentStatics.length; k < ln; k++) {
                staticName = parentStatics[k];

                if (!cls.hasOwnProperty(staticName)) {
                    cls[staticName] = parent[staticName];
                }
            }
        }

        
        if (parentPrototype.config) {
            clsPrototype.config = Ext.Object.merge({}, parentPrototype.config);
        }
        else {
            clsPrototype.config = {};
        }

        if (clsPrototype.$onExtended) {
            clsPrototype.$onExtended.call(cls, cls, data);
        }

        if (data.onClassExtended) {
            clsPrototype.$onExtended = data.onClassExtended;
            delete data.onClassExtended;
        }

    }, true);

    Class.registerPreprocessor('statics', function(cls, data) {
        var statics = data.statics,
            name;

        for (name in statics) {
            if (statics.hasOwnProperty(name)) {
                cls[name] = statics[name];
            }
        }

        delete data.statics;
    });

    Class.registerPreprocessor('inheritableStatics', function(cls, data) {
        var statics = data.inheritableStatics,
            inheritableStatics,
            prototype = cls.prototype,
            name;

        inheritableStatics = prototype.$inheritableStatics;

        if (!inheritableStatics) {
            inheritableStatics = prototype.$inheritableStatics = [];
        }

        for (name in statics) {
            if (statics.hasOwnProperty(name)) {
                cls[name] = statics[name];
                inheritableStatics.push(name);
            }
        }

        delete data.inheritableStatics;
    });

    Class.registerPreprocessor('mixins', function(cls, data) {
        cls.mixin(data.mixins);

        delete data.mixins;
    });

    Class.registerPreprocessor('config', function(cls, data) {
        var prototype = cls.prototype;

        Ext.Object.each(data.config, function(name) {
            var cName = name.charAt(0).toUpperCase() + name.substr(1),
                pName = name,
                apply = 'apply' + cName,
                setter = 'set' + cName,
                getter = 'get' + cName;

            if (!(apply in prototype) && !data.hasOwnProperty(apply)) {
                data[apply] = function(val) {
                    return val;
                };
            }

            if (!(setter in prototype) && !data.hasOwnProperty(setter)) {
                data[setter] = function(val) {
                    var ret = this[apply].call(this, val, this[pName]);

                    if (ret !== undefined) {
                        this[pName] = ret;
                    }

                    return this;
                };
            }

            if (!(getter in prototype) && !data.hasOwnProperty(getter)) {
                data[getter] = function() {
                    return this[pName];
                };
            }
        });

        Ext.Object.merge(prototype.config, data.config);
        delete data.config;
    });

    Class.setDefaultPreprocessors(['extend', 'statics', 'inheritableStatics', 'mixins', 'config']);

    
    Ext.extend = function(subclass, superclass, members) {
        if (arguments.length === 2 && Ext.isObject(superclass)) {
            members = superclass;
            superclass = subclass;
            subclass = null;
        }

        var cls;

        if (!superclass) {
            Ext.Error.raise("Attempting to extend from a class which has not been loaded on the page.");
        }

        members.extend = superclass;
        members.preprocessors = ['extend', 'mixins', 'config', 'statics'];

        if (subclass) {
            cls = new Class(subclass, members);
        }
        else {
            cls = new Class(members);
        }

        cls.prototype.override = function(o) {
            for (var m in o) {
                if (o.hasOwnProperty(m)) {
                    this[m] = o[m];
                }
            }
        };

        return cls;
    };

})();


(function(Class, alias) {

    var slice = Array.prototype.slice;

    var Manager = Ext.ClassManager = {

        
        classes: {},

        
        existCache: {},

        
        namespaceRewrites: [{
            from: 'Ext.',
            to: Ext
        }],

        
        maps: {
            alternateToName: {},
            aliasToName: {},
            nameToAliases: {}
        },

        
        enableNamespaceParseCache: true,

        
        namespaceParseCache: {},

        
        instantiators: [],

        
        instantiationCounts: {},

        
        isCreated: function(className) {
            var i, ln, part, root, parts;

            if (typeof className !== 'string' || className.length < 1) {
                Ext.Error.raise({
                    sourceClass: "Ext.ClassManager",
                    sourceMethod: "exist",
                    msg: "Invalid classname, must be a string and must not be empty"
                });
            }

            if (this.classes.hasOwnProperty(className) || this.existCache.hasOwnProperty(className)) {
                return true;
            }

            root = Ext.global;
            parts = this.parseNamespace(className);

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part !== 'string') {
                    root = part;
                } else {
                    if (!root || !root[part]) {
                        return false;
                    }

                    root = root[part];
                }
            }

            Ext.Loader.historyPush(className);

            this.existCache[className] = true;

            return true;
        },

        
        parseNamespace: function(namespace) {
            if (typeof namespace !== 'string') {
                Ext.Error.raise({
                    sourceClass: "Ext.ClassManager",
                    sourceMethod: "parseNamespace",
                    msg: "Invalid namespace, must be a string"
                });
            }

            var cache = this.namespaceParseCache;

            if (this.enableNamespaceParseCache) {
                if (cache.hasOwnProperty(namespace)) {
                    return cache[namespace];
                }
            }

            var parts = [],
                rewrites = this.namespaceRewrites,
                rewrite, from, to, i, ln, root = Ext.global;

            for (i = 0, ln = rewrites.length; i < ln; i++) {
                rewrite = rewrites[i];
                from = rewrite.from;
                to = rewrite.to;

                if (namespace === from || namespace.substring(0, from.length) === from) {
                    namespace = namespace.substring(from.length);

                    if (typeof to !== 'string') {
                        root = to;
                    } else {
                        parts = parts.concat(to.split('.'));
                    }

                    break;
                }
            }

            parts.push(root);

            parts = parts.concat(namespace.split('.'));

            if (this.enableNamespaceParseCache) {
                cache[namespace] = parts;
            }

            return parts;
        },

        
        setNamespace: function(name, value) {
            var root = Ext.global,
                parts = this.parseNamespace(name),
                leaf = parts.pop(),
                i, ln, part;

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part !== 'string') {
                    root = part;
                } else {
                    if (!root[part]) {
                        root[part] = {};
                    }

                    root = root[part];
                }
            }

            root[leaf] = value;

            return root[leaf];
        },

        
        createNamespaces: function() {
            var root = Ext.global,
                parts, part, i, j, ln, subLn;

            for (i = 0, ln = arguments.length; i < ln; i++) {
                parts = this.parseNamespace(arguments[i]);

                for (j = 0, subLn = parts.length; j < subLn; j++) {
                    part = parts[j];

                    if (typeof part !== 'string') {
                        root = part;
                    } else {
                        if (!root[part]) {
                            root[part] = {};
                        }

                        root = root[part];
                    }
                }
            }

            return root;
        },

        
        set: function(name, value) {
            var targetName = this.getName(value);

            this.classes[name] = this.setNamespace(name, value);

            if (targetName && targetName !== name) {
                this.maps.alternateToName[name] = targetName;
            }

            return this;
        },

        
        get: function(name) {
            if (this.classes.hasOwnProperty(name)) {
                return this.classes[name];
            }

            var root = Ext.global,
                parts = this.parseNamespace(name),
                part, i, ln;

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part !== 'string') {
                    root = part;
                } else {
                    if (!root || !root[part]) {
                        return null;
                    }

                    root = root[part];
                }
            }

            return root;
        },

        
        setAlias: function(cls, alias) {
            var aliasToNameMap = this.maps.aliasToName,
                nameToAliasesMap = this.maps.nameToAliases,
                className;

            if (typeof cls === 'string') {
                className = cls;
            } else {
                className = this.getName(cls);
            }

            if (alias && aliasToNameMap[alias] !== className) {
                if (aliasToNameMap.hasOwnProperty(alias) && Ext.isDefined(Ext.global.console)) {
                    Ext.global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
                        "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
                }

                aliasToNameMap[alias] = className;
            }

            if (!nameToAliasesMap[className]) {
                nameToAliasesMap[className] = [];
            }

            if (alias) {
                Ext.Array.include(nameToAliasesMap[className], alias);
            }

            return this;
        },

        
        getByAlias: function(alias) {
            return this.get(this.getNameByAlias(alias));
        },

        
        getNameByAlias: function(alias) {
            return this.maps.aliasToName[alias] || '';
        },

        
        getNameByAlternate: function(alternate) {
            return this.maps.alternateToName[alternate] || '';
        },

        
        getAliasesByName: function(name) {
            return this.maps.nameToAliases[name] || [];
        },

        
        getName: function(object) {
            return object && object.$className || '';
        },

        
        getClass: function(object) {
            return object && object.self || null;
        },

        
        create: function(className, data, createdFn) {
            var manager = this;

            if (typeof className !== 'string') {
                Ext.Error.raise({
                    sourceClass: "Ext",
                    sourceMethod: "define",
                    msg: "Invalid class name '" + className + "' specified, must be a non-empty string"
                });
            }

            data.$className = className;

            return new Class(data, function() {
                var postprocessorStack = data.postprocessors || manager.defaultPostprocessors,
                    registeredPostprocessors = manager.postprocessors,
                    index = 0,
                    postprocessors = [],
                    postprocessor, postprocessors, process, i, ln;

                delete data.postprocessors;

                for (i = 0, ln = postprocessorStack.length; i < ln; i++) {
                    postprocessor = postprocessorStack[i];

                    if (typeof postprocessor === 'string') {
                        postprocessor = registeredPostprocessors[postprocessor];

                        if (!postprocessor.always) {
                            if (data[postprocessor.name] !== undefined) {
                                postprocessors.push(postprocessor.fn);
                            }
                        }
                        else {
                            postprocessors.push(postprocessor.fn);
                        }
                    }
                    else {
                        postprocessors.push(postprocessor);
                    }
                }

                process = function(clsName, cls, clsData) {
                    postprocessor = postprocessors[index++];

                    if (!postprocessor) {
                        manager.set(className, cls);

                        Ext.Loader.historyPush(className);

                        if (createdFn) {
                            createdFn.call(cls, cls);
                        }

                        return;
                    }

                    if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
                        process.apply(this, arguments);
                    }
                };

                process.call(manager, className, this, data);
            });
        },

        
        instantiateByAlias: function() {
            var alias = arguments[0],
                args = slice.call(arguments),
                className = this.getNameByAlias(alias);

            if (!className) {
                className = this.maps.aliasToName[alias];

                if (!className) {
                    Ext.Error.raise({
                        sourceClass: "Ext",
                        sourceMethod: "createByAlias",
                        msg: "Cannot create an instance of unrecognized alias: " + alias
                    });
                }

                if (Ext.global.console) {
                    Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
                         "Ext.require('" + alias + "') above Ext.onReady");
                }

                Ext.syncRequire(className);
            }

            args[0] = className;

            return this.instantiate.apply(this, args);
        },

        
        instantiate: function() {
            var name = arguments[0],
                args = slice.call(arguments, 1),
                alias = name,
                possibleName, cls;

            if (typeof name !== 'function') {
                if ((typeof name !== 'string' || name.length < 1)) {
                    Ext.Error.raise({
                        sourceClass: "Ext",
                        sourceMethod: "create",
                        msg: "Invalid class name or alias '" + name + "' specified, must be a non-empty string"
                    });
                }

                cls = this.get(name);
            }
            else {
                cls = name;
            }

            
            if (!cls) {
                possibleName = this.getNameByAlias(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            
            if (!cls) {
                possibleName = this.getNameByAlternate(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            
            if (!cls) {
                if (Ext.global.console) {
                    Ext.global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
                         "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
                }

                Ext.syncRequire(name);

                cls = this.get(name);
            }

            if (!cls) {
                Ext.Error.raise({
                    sourceClass: "Ext",
                    sourceMethod: "create",
                    msg: "Cannot create an instance of unrecognized class name / alias: " + alias
                });
            }

            if (typeof cls !== 'function') {
                Ext.Error.raise({
                    sourceClass: "Ext",
                    sourceMethod: "create",
                    msg: "'" + name + "' is a singleton and cannot be instantiated"
                });
            }

            if (!this.instantiationCounts[name]) {
                this.instantiationCounts[name] = 0;
            }

            this.instantiationCounts[name]++;

            return this.getInstantiator(args.length)(cls, args);
        },

        
        dynInstantiate: function(name, args) {
            args = Ext.Array.from(args, true);
            args.unshift(name);

            return this.instantiate.apply(this, args);
        },

        
        getInstantiator: function(length) {
            if (!this.instantiators[length]) {
                var i = length,
                    args = [];

                for (i = 0; i < length; i++) {
                    args.push('a['+i+']');
                }

                this.instantiators[length] = new Function('c', 'a', 'return new c('+args.join(',')+')');
            }

            return this.instantiators[length];
        },

        
        postprocessors: {},

        
        defaultPostprocessors: [],

        
        registerPostprocessor: function(name, fn, always) {
            this.postprocessors[name] = {
                name: name,
                always: always ||  false,
                fn: fn
            };

            return this;
        },

        
        setDefaultPostprocessors: function(postprocessors) {
            this.defaultPostprocessors = Ext.Array.from(postprocessors);

            return this;
        },

        
        setDefaultPostprocessorPosition: function(name, offset, relativeName) {
            var defaultPostprocessors = this.defaultPostprocessors,
                index;

            if (typeof offset === 'string') {
                if (offset === 'first') {
                    defaultPostprocessors.unshift(name);

                    return this;
                }
                else if (offset === 'last') {
                    defaultPostprocessors.push(name);

                    return this;
                }

                offset = (offset === 'after') ? 1 : -1;
            }

            index = Ext.Array.indexOf(defaultPostprocessors, relativeName);

            if (index !== -1) {
                defaultPostprocessors.splice(Math.max(0, index + offset), 0, name);
            }

            return this;
        },

        
        getNamesByExpression: function(expression) {
            var nameToAliasesMap = this.maps.nameToAliases,
                names = [],
                name, alias, aliases, possibleName, regex, i, ln;

            if (typeof expression !== 'string' || expression.length < 1) {
                Ext.Error.raise({
                    sourceClass: "Ext.ClassManager",
                    sourceMethod: "getNamesByExpression",
                    msg: "Expression " + expression + " is invalid, must be a non-empty string"
                });
            }

            if (expression.indexOf('*') !== -1) {
                expression = expression.replace(/\*/g, '(.*?)');
                regex = new RegExp('^' + expression + '$');

                for (name in nameToAliasesMap) {
                    if (nameToAliasesMap.hasOwnProperty(name)) {
                        aliases = nameToAliasesMap[name];

                        if (name.search(regex) !== -1) {
                            names.push(name);
                        }
                        else {
                            for (i = 0, ln = aliases.length; i < ln; i++) {
                                alias = aliases[i];

                                if (alias.search(regex) !== -1) {
                                    names.push(name);
                                    break;
                                }
                            }
                        }
                    }
                }

            } else {
                possibleName = this.getNameByAlias(expression);

                if (possibleName) {
                    names.push(possibleName);
                } else {
                    possibleName = this.getNameByAlternate(expression);

                    if (possibleName) {
                        names.push(possibleName);
                    } else {
                        names.push(expression);
                    }
                }
            }

            return names;
        }
    };

    Manager.registerPostprocessor('alias', function(name, cls, data) {
        var aliases = data.alias,
            widgetPrefix = 'widget.',
            i, ln, alias;

        if (!(aliases instanceof Array)) {
            aliases = [aliases];
        }

        for (i = 0, ln = aliases.length; i < ln; i++) {
            alias = aliases[i];

            if (typeof alias !== 'string') {
                Ext.Error.raise({
                    sourceClass: "Ext",
                    sourceMethod: "define",
                    msg: "Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"
                });
            }

            this.setAlias(cls, alias);
        }

        
        for (i = 0, ln = aliases.length; i < ln; i++) {
            alias = aliases[i];

            if (alias.substring(0, widgetPrefix.length) === widgetPrefix) {
                
                cls.xtype = cls.$xtype = alias.substring(widgetPrefix.length);
                break;
            }
        }
    });

    Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
        fn.call(this, name, new cls(), data);
        return false;
    });

    Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
        var alternates = data.alternateClassName,
            i, ln, alternate;

        if (!(alternates instanceof Array)) {
            alternates = [alternates];
        }

        for (i = 0, ln = alternates.length; i < ln; i++) {
            alternate = alternates[i];

            if (typeof alternate !== 'string') {
                Ext.Error.raise({
                    sourceClass: "Ext",
                    sourceMethod: "define",
                    msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"
                });
            }

            this.set(alternate, cls);
        }
    });

    Manager.setDefaultPostprocessors(['alias', 'singleton', 'alternateClassName']);

    Ext.apply(Ext, {
        
        create: alias(Manager, 'instantiate'),

        
        factory: function(item, namespace) {
            if (item instanceof Array) {
                var i, ln;

                for (i = 0, ln = item.length; i < ln; i++) {
                    item[i] = Ext.factory(item[i], namespace);
                }

                return item;
            }

            var isString = (typeof item === 'string');

            if (isString || (item instanceof Object && item.constructor === Object)) {
                var name, config = {};

                if (isString) {
                    name = item;
                }
                else {
                    name = item.className;
                    config = item;
                    delete config.className;
                }

                if (namespace !== undefined && name.indexOf(namespace) === -1) {
                    name = namespace + '.' + Ext.String.capitalize(name);
                }

                return Ext.create(name, config);
            }

            if (typeof item === 'function') {
                return Ext.create(item);
            }

            return item;
        },

        
        widget: function(name) {
            var args = slice.call(arguments);
            args[0] = 'widget.' + name;

            return Manager.instantiateByAlias.apply(Manager, args);
        },

        
        createByAlias: alias(Manager, 'instantiateByAlias'),

        
        define: alias(Manager, 'create'),

        
        getClassName: alias(Manager, 'getName'),

        
        getDisplayName: function(object) {
            if (object.displayName) {
                return object.displayName;
            }

            if (object.$name && object.$class) {
                return Ext.getClassName(object.$class) + '#' + object.$name;
            }

            if (object.$className) {
                return object.$className;
            }

            return 'Anonymous';
        },

        
        getClass: alias(Manager, 'getClass'),

        
        namespace: alias(Manager, 'createNamespaces')
    });

    Ext.createWidget = Ext.widget;

    
    Ext.ns = Ext.namespace;

    Class.registerPreprocessor('className', function(cls, data) {
        if (data.$className) {
            cls.$className = data.$className;
            cls.displayName = cls.$className;
        }
    }, true);

    Class.setDefaultPreprocessorPosition('className', 'first');

})(Ext.Class, Ext.Function.alias);



(function(Manager, Class, flexSetter, alias) {

    var
        dependencyProperties = ['extend', 'mixins', 'requires'],
        Loader;

    Loader = Ext.Loader = {
        
        documentHead: typeof document !== 'undefined' && (document.head || document.getElementsByTagName('head')[0]),

        
        isLoading: false,

        
        queue: [],

        
        isFileLoaded: {},

        
        readyListeners: [],

        
        optionalRequires: [],

        
        requiresMap: {},

        
        numPendingFiles: 0,

        
        numLoadedFiles: 0,

        
        hasFileLoadError: false,

        
        classNameToFilePathMap: {},

        
        history: [],

        
        config: {
            
            enabled: false,

            
            disableCaching: true,

            
            disableCachingParam: '_dc',

            
            paths: {
                'Ext': '.'
            }
        },

        
        setConfig: function(name, value) {
            if (Ext.isObject(name) && arguments.length === 1) {
                Ext.Object.merge(this.config, name);
            }
            else {
                this.config[name] = (Ext.isObject(value)) ? Ext.Object.merge(this.config[name], value) : value;
            }

            return this;
        },

        
        getConfig: function(name) {
            if (name) {
                return this.config[name];
            }

            return this.config;
        },

        
        setPath: flexSetter(function(name, path) {
            this.config.paths[name] = path;

            return this;
        }),

        
        getPath: function(className) {
            var path = '',
                paths = this.config.paths,
                prefix = this.getPrefix(className);

            if (prefix.length > 0) {
                if (prefix === className) {
                    return paths[prefix];
                }

                path = paths[prefix];
                className = className.substring(prefix.length + 1);
            }

            if (path.length > 0) {
                path += '/';
            }

            return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
        },

        
        getPrefix: function(className) {
            var paths = this.config.paths,
                prefix, deepestPrefix = '';

            if (paths.hasOwnProperty(className)) {
                return className;
            }

            for (prefix in paths) {
                if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
                    if (prefix.length > deepestPrefix.length) {
                        deepestPrefix = prefix;
                    }
                }
            }

            return deepestPrefix;
        },

        
        refreshQueue: function() {
            var ln = this.queue.length,
                i, item, j, requires;

            if (ln === 0) {
                this.triggerReady();
                return;
            }

            for (i = 0; i < ln; i++) {
                item = this.queue[i];

                if (item) {
                    requires = item.requires;

                    
                    
                    if (requires.length > this.numLoadedFiles) {
                        continue;
                    }

                    j = 0;

                    do {
                        if (Manager.isCreated(requires[j])) {
                            
                            requires.splice(j, 1);
                        }
                        else {
                            j++;
                        }
                    } while (j < requires.length);

                    if (item.requires.length === 0) {
                        this.queue.splice(i, 1);
                        item.callback.call(item.scope);
                        this.refreshQueue();
                        break;
                    }
                }
            }

            return this;
        },

        
        injectScriptElement: function(url, onLoad, onError, scope) {
            var script = document.createElement('script'),
                me = this,
                onLoadFn = function() {
                    me.cleanupScriptElement(script);
                    onLoad.call(scope);
                },
                onErrorFn = function() {
                    me.cleanupScriptElement(script);
                    onError.call(scope);
                };

            script.type = 'text/javascript';
            script.src = url;
            script.onload = onLoadFn;
            script.onerror = onErrorFn;
            script.onreadystatechange = function() {
                if (this.readyState === 'loaded' || this.readyState === 'complete') {
                    onLoadFn();
                }
            };

            this.documentHead.appendChild(script);

            return script;
        },

        
        cleanupScriptElement: function(script) {
            script.onload = null;
            script.onreadystatechange = null;
            script.onerror = null;

            return this;
        },

        
        loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
            var me = this,
                noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
                fileName = url.split('/').pop(),
                isCrossOriginRestricted = false,
                xhr, status, onScriptError;

            scope = scope || this;

            this.isLoading = true;

            if (!synchronous) {
                onScriptError = function() {
                    onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
                };

                if (!Ext.isReady && Ext.onDocumentReady) {
                    Ext.onDocumentReady(function() {
                        me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
                    });
                }
                else {
                    this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
                }
            }
            else {
                if (typeof XMLHttpRequest !== 'undefined') {
                    xhr = new XMLHttpRequest();
                } else {
                    xhr = new ActiveXObject('Microsoft.XMLHTTP');
                }

                try {
                    xhr.open('GET', noCacheUrl, false);
                    xhr.send(null);
                } catch (e) {
                    isCrossOriginRestricted = true;
                }

                status = (xhr.status === 1223) ? 204 : xhr.status;

                if (!isCrossOriginRestricted) {
                    isCrossOriginRestricted = (status === 0);
                }

                if (isCrossOriginRestricted
                ) {
                    onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
                                       "being loaded from a different domain or from the local file system whereby cross origin " +
                                       "requests are not allowed due to security reasons. Use asynchronous loading with " +
                                       "Ext.require instead.", synchronous);
                }
                else if (status >= 200 && status < 300
                ) {
                    
                    new Function(xhr.responseText + "\n//@ sourceURL=" + fileName)();

                    onLoad.call(scope);
                }
                else {
                    onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
                                       "verify that the file exists. " +
                                       "XHR status code: " + status, synchronous);
                }

                
                xhr = null;
            }
        },

        
        exclude: function(excludes) {
            var me = this;

            return {
                require: function(expressions, fn, scope) {
                    return me.require(expressions, fn, scope, excludes);
                },

                syncRequire: function(expressions, fn, scope) {
                    return me.syncRequire(expressions, fn, scope, excludes);
                }
            };
        },

        
        syncRequire: function() {
            this.syncModeEnabled = true;
            this.require.apply(this, arguments);
            this.refreshQueue();
            this.syncModeEnabled = false;
        },

        
        require: function(expressions, fn, scope, excludes) {
            var filePath, expression, exclude, className, excluded = {},
                excludedClassNames = [],
                possibleClassNames = [],
                possibleClassName, classNames = [],
                i, j, ln, subLn;

            expressions = Ext.Array.from(expressions);
            excludes = Ext.Array.from(excludes);

            fn = fn || Ext.emptyFn;

            scope = scope || Ext.global;

            for (i = 0, ln = excludes.length; i < ln; i++) {
                exclude = excludes[i];

                if (typeof exclude === 'string' && exclude.length > 0) {
                    excludedClassNames = Manager.getNamesByExpression(exclude);

                    for (j = 0, subLn = excludedClassNames.length; j < subLn; j++) {
                        excluded[excludedClassNames[j]] = true;
                    }
                }
            }

            for (i = 0, ln = expressions.length; i < ln; i++) {
                expression = expressions[i];

                if (typeof expression === 'string' && expression.length > 0) {
                    possibleClassNames = Manager.getNamesByExpression(expression);

                    for (j = 0, subLn = possibleClassNames.length; j < subLn; j++) {
                        possibleClassName = possibleClassNames[j];

                        if (!excluded.hasOwnProperty(possibleClassName) && !Manager.isCreated(possibleClassName)) {
                            Ext.Array.include(classNames, possibleClassName);
                        }
                    }
                }
            }

            
            
            if (!this.config.enabled) {
                if (classNames.length > 0) {
                    Ext.Error.raise({
                        sourceClass: "Ext.Loader",
                        sourceMethod: "require",
                        msg: "Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
                             "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', ')
                    });
                }
            }

            if (classNames.length === 0) {
                fn.call(scope);
                return this;
            }

            this.queue.push({
                requires: classNames,
                callback: fn,
                scope: scope
            });

            classNames = classNames.slice();

            for (i = 0, ln = classNames.length; i < ln; i++) {
                className = classNames[i];

                if (!this.isFileLoaded.hasOwnProperty(className)) {
                    this.isFileLoaded[className] = false;

                    filePath = this.getPath(className);

                    this.classNameToFilePathMap[className] = filePath;

                    this.numPendingFiles++;

                    this.loadScriptFile(
                        filePath,
                        Ext.Function.pass(this.onFileLoaded, [className, filePath], this),
                        Ext.Function.pass(this.onFileLoadError, [className, filePath]),
                        this,
                        this.syncModeEnabled
                    );
                }
            }

            return this;
        },

        
        onFileLoaded: function(className, filePath) {
            this.numLoadedFiles++;

            this.isFileLoaded[className] = true;

            this.numPendingFiles--;

            if (this.numPendingFiles === 0) {
                this.refreshQueue();
            }

            if (this.numPendingFiles <= 1) {
                window.status = "Finished loading all dependencies, onReady fired!";
            }
            else {
                window.status = "Loading dependencies, " + this.numPendingFiles + " files left...";
            }

            if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
                var queue = this.queue,
                    requires,
                    i, ln, j, subLn, missingClasses = [], missingPaths = [];

                for (i = 0, ln = queue.length; i < ln; i++) {
                    requires = queue[i].requires;

                    for (j = 0, subLn = requires.length; j < ln; j++) {
                        if (this.isFileLoaded[requires[j]]) {
                            missingClasses.push(requires[j]);
                        }
                    }
                }

                if (missingClasses.length < 1) {
                    return;
                }

                missingClasses = Ext.Array.filter(missingClasses, function(item) {
                    return !this.requiresMap.hasOwnProperty(item);
                }, this);

                for (i = 0,ln = missingClasses.length; i < ln; i++) {
                    missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
                }

                Ext.Error.raise({
                    sourceClass: "Ext.Loader",
                    sourceMethod: "onFileLoaded",
                    msg: "The following classes are not declared even if their files have been " +
                            "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
                            "corresponding files for possible typos: '" + missingPaths.join("', '") + "'"
                });
            }
        },

        
        onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
            this.numPendingFiles--;
            this.hasFileLoadError = true;

            Ext.Error.raise({
                sourceClass: "Ext.Loader",
                classToLoad: className,
                loadPath: filePath,
                loadingType: isSynchronous ? 'synchronous' : 'async',
                msg: errorMessage
            });
        },

        
        addOptionalRequires: function(requires) {
            var optionalRequires = this.optionalRequires,
                i, ln, require;

            requires = Ext.Array.from(requires);

            for (i = 0, ln = requires.length; i < ln; i++) {
                require = requires[i];

                Ext.Array.include(optionalRequires, require);
            }

            return this;
        },

        
        triggerReady: function(force) {
            var readyListeners = this.readyListeners,
                optionalRequires, listener;

            if (this.isLoading || force) {
                this.isLoading = false;

                if (this.optionalRequires.length) {
                    
                    optionalRequires = Ext.Array.clone(this.optionalRequires);

                    
                    this.optionalRequires.length = 0;

                    this.require(optionalRequires, Ext.Function.pass(this.triggerReady, [true], this), this);
                    return this;
                }

                while (readyListeners.length) {
                    listener = readyListeners.shift();
                    listener.fn.call(listener.scope);

                    if (this.isLoading) {
                        return this;
                    }
                }
            }

            return this;
        },

        
        onReady: function(fn, scope, withDomReady, options) {
            var oldFn;

            if (withDomReady !== false && Ext.onDocumentReady) {
                oldFn = fn;

                fn = function() {
                    Ext.onDocumentReady(oldFn, scope, options);
                };
            }

            if (!this.isLoading) {
                fn.call(scope);
            }
            else {
                this.readyListeners.push({
                    fn: fn,
                    scope: scope
                });
            }
        },

        
        historyPush: function(className) {
            if (className && this.isFileLoaded.hasOwnProperty(className)) {
                Ext.Array.include(this.history, className);
            }

            return this;
        }
    };

    
    Ext.require = alias(Loader, 'require');

    
    Ext.syncRequire = alias(Loader, 'syncRequire');

    
    Ext.exclude = alias(Loader, 'exclude');

    
    Ext.onReady = function(fn, scope, options) {
        Loader.onReady(fn, scope, true, options);
    };

    Class.registerPreprocessor('loader', function(cls, data, continueFn) {
        var me = this,
            dependencies = [],
            className = Manager.getName(cls),
            i, j, ln, subLn, value, propertyName, propertyValue;

        

        for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
            propertyName = dependencyProperties[i];

            if (data.hasOwnProperty(propertyName)) {
                propertyValue = data[propertyName];

                if (typeof propertyValue === 'string') {
                    dependencies.push(propertyValue);
                }
                else if (propertyValue instanceof Array) {
                    for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
                        value = propertyValue[j];

                        if (typeof value === 'string') {
                            dependencies.push(value);
                        }
                    }
                }
                else {
                    for (j in propertyValue) {
                        if (propertyValue.hasOwnProperty(j)) {
                            value = propertyValue[j];

                            if (typeof value === 'string') {
                                dependencies.push(value);
                            }
                        }
                    }
                }
            }
        }

        if (dependencies.length === 0) {

            return;
        }

        var deadlockPath = [],
            requiresMap = Loader.requiresMap,
            detectDeadlock;

        

        if (className) {
            requiresMap[className] = dependencies;

            detectDeadlock = function(cls) {
                deadlockPath.push(cls);

                if (requiresMap[cls]) {
                    if (Ext.Array.contains(requiresMap[cls], className)) {
                        Ext.Error.raise({
                            sourceClass: "Ext.Loader",
                            msg: "Deadlock detected while loading dependencies! '" + className + "' and '" +
                                deadlockPath[1] + "' " + "mutually require each other. Path: " +
                                deadlockPath.join(' -> ') + " -> " + deadlockPath[0]
                        });
                    }

                    for (i = 0, ln = requiresMap[cls].length; i < ln; i++) {
                        detectDeadlock(requiresMap[cls][i]);
                    }
                }
            };

            detectDeadlock(className);
        }


        Loader.require(dependencies, function() {
            for (i = 0, ln = dependencyProperties.length; i < ln; i++) {
                propertyName = dependencyProperties[i];

                if (data.hasOwnProperty(propertyName)) {
                    propertyValue = data[propertyName];

                    if (typeof propertyValue === 'string') {
                        data[propertyName] = Manager.get(propertyValue);
                    }
                    else if (propertyValue instanceof Array) {
                        for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
                            value = propertyValue[j];

                            if (typeof value === 'string') {
                                data[propertyName][j] = Manager.get(value);
                            }
                        }
                    }
                    else {
                        for (var k in propertyValue) {
                            if (propertyValue.hasOwnProperty(k)) {
                                value = propertyValue[k];

                                if (typeof value === 'string') {
                                    data[propertyName][k] = Manager.get(value);
                                }
                            }
                        }
                    }
                }
            }

            continueFn.call(me, cls, data);
        });

        return false;
    }, true);

    Class.setDefaultPreprocessorPosition('loader', 'after', 'className');

    Manager.registerPostprocessor('uses', function(name, cls, data) {
        var uses = Ext.Array.from(data.uses),
            items = [],
            i, ln, item;

        for (i = 0, ln = uses.length; i < ln; i++) {
            item = uses[i];

            if (typeof item === 'string') {
                items.push(item);
            }
        }

        Loader.addOptionalRequires(items);
    });

    Manager.setDefaultPostprocessorPosition('uses', 'last');

})(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias);


Ext.Error = Ext.extend(Error, {
    statics: {
        
        ignore: false,

        
        

        
        raise: function(err){
            err = err || {};
            if (Ext.isString(err)) {
                err = { msg: err };
            }

            var method = this.raise.caller;

            if (method) {
                if (method.$name) {
                    err.sourceMethod = method.$name;
                }
                if (method.$owner) {
                    err.sourceClass = method.$owner.$className;
                }
            }

            if (Ext.Error.handle(err) !== true) {
                var msg = Ext.Error.prototype.toString.call(err);

                Ext.log({
                    msg: msg,
                    level: 'error',
                    dump: err,
                    stack: true
                });

                throw new Ext.Error(err);
            }
        },

        
        handle: function(){
            return Ext.Error.ignore;
        }
    },

    
    name: 'Ext.Error',

    
    constructor: function(config){
        if (Ext.isString(config)) {
            config = { msg: config };
        }

        var me = this;

        Ext.apply(me, config);

        me.message = me.message || me.msg; 
        
    },

    
    toString: function(){
        var me = this,
            className = me.className ? me.className  : '',
            methodName = me.methodName ? '.' + me.methodName + '(): ' : '',
            msg = me.msg || '(No description provided)';

        return className + methodName + msg;
    }
});


(function () {
    var prevOnError, timer, errors = 0,
        extraordinarilyBad = /(out of stack)|(too much recursion)|(stack overflow)|(out of memory)/i,
        win = Ext.global;

    if (typeof window === 'undefined') {
        return; 
    }

    
    function notify () {
        var counters = Ext.log.counters,
            supports = Ext.supports,
            hasOnError = supports && supports.WindowOnError; 

        
        if (counters && (counters.error + counters.warn + counters.info + counters.log)) {
            var msg = [ 'Logged Errors:',counters.error, 'Warnings:',counters.warn,
                        'Info:',counters.info, 'Log:',counters.log].join(' ');
            if (errors) {
                msg = '*** Errors: ' + errors + ' - ' + msg;
            } else if (counters.error) {
                msg = '*** ' + msg;
            }
            win.status = msg;
        }

        
        if (!Ext.isDefined(Ext.Error.notify)) {
            Ext.Error.notify = Ext.isIE6 || Ext.isIE7; 
        }
        if (Ext.Error.notify && (hasOnError ? errors : (counters && counters.error))) {
            Ext.Error.notify = false;

            if (timer) {
                win.clearInterval(timer); 
                timer = null;
            }

            alert('Unhandled error on page: See console or log');
            poll();
        }
    }

    
    
    
    function poll () {
        timer = win.setInterval(notify, 1000);
    }

    
    
    prevOnError = win.onerror || Ext.emptyFn;
    win.onerror = function (message) {
        ++errors;

        if (!extraordinarilyBad.test(message)) {
            
            
            notify();
        }

        return prevOnError.apply(this, arguments);
    };
    poll();
})();





Ext.JSON = new(function() {
    var useHasOwn = !! {}.hasOwnProperty,
    isNative = function() {
        var useNative = null;

        return function() {
            if (useNative === null) {
                useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
            }

            return useNative;
        };
    }(),
    pad = function(n) {
        return n < 10 ? "0" + n : n;
    },
    doDecode = function(json) {
        return eval("(" + json + ')');
    },
    doEncode = function(o) {
        if (!Ext.isDefined(o) || o === null) {
            return "null";
        } else if (Ext.isArray(o)) {
            return encodeArray(o);
        } else if (Ext.isDate(o)) {
            return Ext.JSON.encodeDate(o);
        } else if (Ext.isString(o)) {
            return encodeString(o);
        } else if (typeof o == "number") {
            
            return isFinite(o) ? String(o) : "null";
        } else if (Ext.isBoolean(o)) {
            return String(o);
        } else if (Ext.isObject(o)) {
            return encodeObject(o);
        } else if (typeof o === "function") {
            return "null";
        }
        return 'undefined';
    },
    m = {
        "\b": '\\b',
        "\t": '\\t',
        "\n": '\\n',
        "\f": '\\f',
        "\r": '\\r',
        '"': '\\"',
        "\\": '\\\\',
        '\x0b': '\\u000b' 
    },
    charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
    encodeString = function(s) {
        return '"' + s.replace(charToReplace, function(a) {
            var c = m[a];
            return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"';
    },
    encodeArray = function(o) {
        var a = ["[", ""],
        
        len = o.length,
        i;
        for (i = 0; i < len; i += 1) {
            a.push(doEncode(o[i]), ',');
        }
        
        a[a.length - 1] = ']';
        return a.join("");
    },
    encodeObject = function(o) {
        var a = ["{", ""],
        
        i;
        for (i in o) {
            if (!useHasOwn || o.hasOwnProperty(i)) {
                a.push(doEncode(i), ":", doEncode(o[i]), ',');
            }
        }
        
        a[a.length - 1] = '}';
        return a.join("");
    };

    
    this.encodeDate = function(o) {
        return '"' + o.getFullYear() + "-" 
        + pad(o.getMonth() + 1) + "-"
        + pad(o.getDate()) + "T"
        + pad(o.getHours()) + ":"
        + pad(o.getMinutes()) + ":"
        + pad(o.getSeconds()) + '"';
    };

    
    this.encode = function() {
        var ec;
        return function(o) {
            if (!ec) {
                
                ec = isNative() ? JSON.stringify : doEncode;
            }
            return ec(o);
        };
    }();


    
    this.decode = function() {
        var dc;
        return function(json, safe) {
            if (!dc) {
                
                dc = isNative() ? JSON.parse : doDecode;
            }
            try {
                return dc(json);
            } catch (e) {
                if (safe === true) {
                    return null;
                }
                Ext.Error.raise({
                    sourceClass: "Ext.JSON",
                    sourceMethod: "decode",
                    msg: "You're trying to decode and invalid JSON String: " + json
                });
            }
        };
    }();

})();

Ext.encode = Ext.JSON.encode;

Ext.decode = Ext.JSON.decode;



Ext.apply(Ext, {
    userAgent: navigator.userAgent.toLowerCase(),
    cache: {},
    idSeed: 1000,
    BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
    isStrict: document.compatMode == "CSS1Compat",
    windowId: 'ext-window',
    documentId: 'ext-document',

    
    isReady: false,

    
    enableGarbageCollector: true,

    
    enableListenerCollection: true,

    
    id: function(el, prefix) {
        el = Ext.getDom(el, true) || {};
        if (el === document) {
            el.id = this.documentId;
        }
        else if (el === window) {
            el.id = this.windowId;
        }
        if (!el.id) {
            el.id = (prefix || "ext-gen") + (++Ext.idSeed);
        }
        return el.id;
    },

    
    getBody: function() {
        return Ext.get(document.body || false);
    },

    
    getHead: function() {
        var head;

        return function() {
            if (head == undefined) {
                head = Ext.get(document.getElementsByTagName("head")[0]);
            }

            return head;
        };
    }(),

    
    getDoc: function() {
        return Ext.get(document);
    },

    
    getCmp: function(id) {
        return Ext.ComponentManager.get(id);
    },

    
    getOrientation: function() {
        return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
    },

    
    destroy: function() {
        var ln = arguments.length,
        i, arg;

        for (i = 0; i < ln; i++) {
            arg = arguments[i];
            if (arg) {
                if (Ext.isArray(arg)) {
                    this.destroy.apply(this, arg);
                }
                else if (Ext.isFunction(arg.destroy)) {
                    arg.destroy();
                }
                else if (arg.dom) {
                    arg.remove();
                }
            }
        }
    },

    
    callback: function(callback, scope, args, delay){
        if(Ext.isFunction(callback)){
            args = args || [];
            scope = scope || window;
            if (delay) {
                Ext.defer(callback, delay, scope, args);
            } else {
                callback.apply(scope, args);
            }
        }
    },

    
    htmlEncode : function(value) {
        return Ext.String.htmlEncode(value);
    },

    
    htmlDecode : function(value) {
         return Ext.String.htmlDecode(value);
    },

    
    urlAppend : function(url, s) {
        if (!Ext.isEmpty(s)) {
            return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
        }
        return url;
    }
});


Ext.ns = Ext.namespace;


window.undefined = window.undefined;


(function(){
    var check = function(regex){
            return regex.test(Ext.userAgent);
        },
        docMode = document.documentMode,
        isOpera = check(/opera/),
        isOpera10_5 = isOpera && check(/version\/10\.5/),
        isChrome = check(/\bchrome\b/),
        isWebKit = check(/webkit/),
        isSafari = !isChrome && check(/safari/),
        isSafari2 = isSafari && check(/applewebkit\/4/), 
        isSafari3 = isSafari && check(/version\/3/),
        isSafari4 = isSafari && check(/version\/4/),
        isIE = !isOpera && check(/msie/),
        isIE7 = isIE && (check(/msie 7/) || docMode == 7),
        isIE8 = isIE && (check(/msie 8/) && docMode != 7 && docMode != 9 || docMode == 8),
        isIE9 = isIE && (check(/msie 9/) && docMode != 7 && docMode != 8 || docMode == 9),
        isIE6 = isIE && check(/msie 6/),
        isGecko = !isWebKit && check(/gecko/),
        isGecko3 = isGecko && check(/rv:1\.9/),
        isGecko4 = isGecko && check(/rv:2\.0/),
        isFF3_0 = isGecko3 && check(/rv:1\.9\.0/),
        isFF3_5 = isGecko3 && check(/rv:1\.9\.1/),
        isFF3_6 = isGecko3 && check(/rv:1\.9\.2/),
        isWindows = check(/windows|win32/),
        isMac = check(/macintosh|mac os x/),
        isLinux = check(/linux/),
        scrollWidth = null,
        webKitVersion = isWebKit && (/webkit\/(\d+\.\d+)/.exec(Ext.userAgent));

    
    try {
        document.execCommand("BackgroundImageCache", false, true);
    } catch(e) {}

    Ext.setVersion('extjs', '4.0.1');
    Ext.apply(Ext, {
        
        SSL_SECURE_URL : Ext.isSecure && isIE ? 'javascript:""' : 'about:blank',

        

        
        scopeResetCSS : Ext.buildSettings.scopeResetCSS,

        
        enableNestedListenerRemoval : false,

        
        USE_NATIVE_JSON : false,

        
        getDom : function(el, strict) {
            if (!el || !document) {
                return null;
            }
            if (el.dom) {
                return el.dom;
            } else {
                if (typeof el == 'string') {
                    var e = document.getElementById(el);
                    
                    
                    if (e && isIE && strict) {
                        if (el == e.getAttribute('id')) {
                            return e;
                        } else {
                            return null;
                        }
                    }
                    return e;
                } else {
                    return el;
                }
            }
        },

        
        removeNode : isIE6 || isIE7 ? function() {
            var d;
            return function(n){
                if(n && n.tagName != 'BODY'){
                    (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
                    d = d || document.createElement('div');
                    d.appendChild(n);
                    d.innerHTML = '';
                    delete Ext.cache[n.id];
                }
            };
        }() : function(n) {
            if (n && n.parentNode && n.tagName != 'BODY') {
                (Ext.enableNestedListenerRemoval) ? Ext.EventManager.purgeElement(n) : Ext.EventManager.removeAll(n);
                n.parentNode.removeChild(n);
                delete Ext.cache[n.id];
            }
        },

        
        isOpera : isOpera,

        
        isOpera10_5 : isOpera10_5,

        
        isWebKit : isWebKit,

        
        isChrome : isChrome,

        
        isSafari : isSafari,

        
        isSafari3 : isSafari3,

        
        isSafari4 : isSafari4,

        
        isSafari2 : isSafari2,

        
        isIE : isIE,

        
        isIE6 : isIE6,

        
        isIE7 : isIE7,

        
        isIE8 : isIE8,

        
        isIE9 : isIE9,

        
        isGecko : isGecko,

        
        isGecko3 : isGecko3,

        
        isGecko4 : isGecko4,

        

        isFF3_0 : isFF3_0,
        

        isFF3_5 : isFF3_5,
        
        isFF3_6 : isFF3_6,

        
        isLinux : isLinux,

        
        isWindows : isWindows,

        
        isMac : isMac,

        
        webKitVersion: webKitVersion ? parseFloat(webKitVersion[1]) : -1,

        
        BLANK_IMAGE_URL : (isIE6 || isIE7) ? 'http:/' + '/www.sencha.com/s.gif' : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',

        
        value : function(v, defaultValue, allowBlank){
            return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
        },

        
        escapeRe : function(s) {
            return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
        },

        
        addBehaviors : function(o){
            if(!Ext.isReady){
                Ext.onReady(function(){
                    Ext.addBehaviors(o);
                });
            } else {
                var cache = {}, 
                    parts,
                    b,
                    s;
                for (b in o) {
                    if ((parts = b.split('@'))[1]) { 
                        s = parts[0];
                        if(!cache[s]){
                            cache[s] = Ext.select(s);
                        }
                        cache[s].on(parts[1], o[b]);
                    }
                }
                cache = null;
            }
        },

        
        getScrollBarWidth: function(force){
            if(!Ext.isReady){
                return 0;
            }

            if(force === true || scrollWidth === null){
                
                
                
                var cssClass = Ext.isIE9 ? '' : Ext.baseCSSPrefix + 'hide-offsets';
                    
                var div = Ext.getBody().createChild('<div class="' + cssClass + '" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
                    child = div.child('div', true);
                var w1 = child.offsetWidth;
                div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
                var w2 = child.offsetWidth;
                div.remove();
                
                scrollWidth = w1 - w2 + 2;
            }
            return scrollWidth;
        },

        
        copyTo : function(dest, source, names, usePrototypeKeys){
            if(typeof names == 'string'){
                names = names.split(/[,;\s]/);
            }
            Ext.each(names, function(name){
                if(usePrototypeKeys || source.hasOwnProperty(name)){
                    dest[name] = source[name];
                }
            }, this);
            return dest;
        },

        
        destroyMembers : function(o, arg1, arg2, etc){
            for (var i = 1, a = arguments, len = a.length; i < len; i++) {
                Ext.destroy(o[a[i]]);
                delete o[a[i]];
            }
        },

        
        log : function (message) {
            var options, dump,
                con = Ext.global.console,
                log = Ext.log,
                level = 'log',
                stack,
                members,
                member;

            if (!Ext.isString(message)) {
                options = message;
                message = options.msg || '';
                level = options.level || level;
                dump = options.dump;
                stack = options.stack;

                if (dump && !(con && con.dir)) {
                    members = [];

                    
                    
                    Ext.Object.each(dump, function (name, value) {
                        if (typeof(value) === "function") {
                            return;
                        }

                        if (!Ext.isDefined(value) || value === null ||
                                Ext.isDate(value) ||
                                Ext.isString(value) || (typeof(value) == "number") ||
                                Ext.isBoolean(value)) {
                            member = Ext.encode(value);
                        } else if (Ext.isArray(value)) {
                            member = '[ ]';
                        } else if (Ext.isObject(value)) {
                            member = '{ }';
                        } else {
                            member = 'undefined';
                        }
                        members.push(Ext.encode(name) + ': ' + member);
                    });

                    if (members.length) {
                        message += ' \nData: {\n  ' + members.join(',\n  ') + '\n}';
                    }
                    dump = null;
                }
            }

            if (arguments.length > 1) {
                message += Array.prototype.slice.call(arguments, 1).join('');
            }

            
            
            
            if (con) { 
                if (con[level]) {
                    con[level](message);
                } else {
                    con.log(message);
                }

                if (dump) {
                    con.dir(dump);
                }

                if (stack && con.trace) {
                    
                    if (!con.firebug || level != 'error') {
                        con.trace();
                    }
                }
            } else {
                
                if (level != 'log') {
                    message = level.toUpperCase() + ': ' + message;
                }

                if (Ext.isOpera) {
                    opera.postError(message);
                } else {
                    var out = log.out || (log.out = []),
                        max = log.max || (log.max = 100);

                    if (out.length >= max) {
                        
                        
                        out.splice(0, out.length - 3 * Math.floor(max / 4)); 
                    }

                    out.push(message);
                }
            }

            
            var counters = log.counters ||
                          (log.counters = { error: 0, warn: 0, info: 0, log: 0 });

            ++counters[level];
        },

        
        partition : function(arr, truth){
            var ret = [[],[]];
            Ext.each(arr, function(v, i, a) {
                ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
            });
            return ret;
        },

        
        invoke : function(arr, methodName){
            var ret = [],
                args = Array.prototype.slice.call(arguments, 2);
            Ext.each(arr, function(v,i) {
                if (v && typeof v[methodName] == 'function') {
                    ret.push(v[methodName].apply(v, args));
                } else {
                    ret.push(undefined);
                }
            });
            return ret;
        },

        
        zip : function(){
            var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
                arrs = parts[0],
                fn = parts[1][0],
                len = Ext.max(Ext.pluck(arrs, "length")),
                ret = [];

            for (var i = 0; i < len; i++) {
                ret[i] = [];
                if(fn){
                    ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
                }else{
                    for (var j = 0, aLen = arrs.length; j < aLen; j++){
                        ret[i].push( arrs[j][i] );
                    }
                }
            }
            return ret;
        },

        
        toSentence: function(items, connector) {
            var length = items.length;

            if (length <= 1) {
                return items[0];
            } else {
                var head = items.slice(0, length - 1),
                    tail = items[length - 1];

                return Ext.util.Format.format("{0} {1} {2}", head.join(", "), connector || 'and', tail);
            }
        },

        
        useShims: isIE6
    });
})();


Ext.application = function(config) {
    Ext.require('Ext.app.Application');

    Ext.onReady(function() {
        Ext.create('Ext.app.Application', config);
    });
};


(function() {
    Ext.ns('Ext.util');

    Ext.util.Format = {};
    var UtilFormat     = Ext.util.Format,
        stripTagsRE    = /<\/?[^>]+>/gi,
        stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
        nl2brRe        = /\r?\n/g,

        
        formatCleanRe  = /[^\d\.]/g,

        
        
        I18NFormatCleanRe;

    Ext.apply(UtilFormat, {
        
        thousandSeparator: ',',

        
        decimalSeparator: '.',

        
        currencyPrecision: 2,

        
        currencySign: '$',

        
        currencyAtEnd: false,

        
        undef : function(value) {
            return value !== undefined ? value : "";
        },

        
        defaultValue : function(value, defaultValue) {
            return value !== undefined && value !== '' ? value : defaultValue;
        },

        
        substr : function(value, start, length) {
            return String(value).substr(start, length);
        },

        
        lowercase : function(value) {
            return String(value).toLowerCase();
        },

        
        uppercase : function(value) {
            return String(value).toUpperCase();
        },

        
        usMoney : function(v) {
            return UtilFormat.currency(v, '$', 2);
        },

        
        currency: function(v, currencySign, decimals, end) {
            var negativeSign = '',
                format = ",0",
                i = 0;
            v = v - 0;
            if (v < 0) {
                v = -v;
                negativeSign = '-';
            }
            decimals = decimals || UtilFormat.currencyPrecision;
            format += format + (decimals > 0 ? '.' : '');
            for (; i < decimals; i++) {
                format += '0';
            }
            v = UtilFormat.number(v, format); 
            if ((end || UtilFormat.currencyAtEnd) === true) {
                return Ext.String.format("{0}{1}{2}", negativeSign, v, currencySign || UtilFormat.currencySign);
            } else {
                return Ext.String.format("{0}{1}{2}", negativeSign, currencySign || UtilFormat.currencySign, v);
            }
        },

        
        date: function(v, format) {
            if (!v) {
                return "";
            }
            if (!Ext.isDate(v)) {
                v = new Date(Date.parse(v));
            }
            return Ext.Date.dateFormat(v, format || Ext.Date.defaultFormat);
        },

        
        dateRenderer : function(format) {
            return function(v) {
                return UtilFormat.date(v, format);
            };
        },

        
        stripTags : function(v) {
            return !v ? v : String(v).replace(stripTagsRE, "");
        },

        
        stripScripts : function(v) {
            return !v ? v : String(v).replace(stripScriptsRe, "");
        },

        
        fileSize : function(size) {
            if (size < 1024) {
                return size + " bytes";
            } else if (size < 1048576) {
                return (Math.round(((size*10) / 1024))/10) + " KB";
            } else {
                return (Math.round(((size*10) / 1048576))/10) + " MB";
            }
        },

        
        math : function(){
            var fns = {};

            return function(v, a){
                if (!fns[a]) {
                    fns[a] = Ext.functionFactory('v', 'return v ' + a + ';');
                }
                return fns[a](v);
            };
        }(),

        
        round : function(value, precision) {
            var result = Number(value);
            if (typeof precision == 'number') {
                precision = Math.pow(10, precision);
                result = Math.round(value * precision) / precision;
            }
            return result;
        },

        
        number:
            function(v, formatString) {
            if (!formatString) {
                return v;
            }
            v = Ext.Number.from(v, NaN);
            if (isNaN(v)) {
                return '';
            }
            var comma = UtilFormat.thousandSeparator,
                dec   = UtilFormat.decimalSeparator,
                i18n  = false,
                neg   = v < 0,
                hasComma,
                psplit;

            v = Math.abs(v);

            
            
            
            
            if (formatString.substr(formatString.length - 2) == '/i') {
                if (!I18NFormatCleanRe) {
                    I18NFormatCleanRe = new RegExp('[^\\d\\' + UtilFormat.decimalSeparator + ']','g');
                }
                formatString = formatString.substr(0, formatString.length - 2);
                i18n   = true;
                hasComma = formatString.indexOf(comma) != -1;
                psplit = formatString.replace(I18NFormatCleanRe, '').split(dec);
            } else {
                hasComma = formatString.indexOf(',') != -1;
                psplit = formatString.replace(formatCleanRe, '').split('.');
            }

            if (1 < psplit.length) {
                v = v.toFixed(psplit[1].length);
            } else if(2 < psplit.length) {
                Ext.Error.raise({
                    sourceClass: "Ext.util.Format",
                    sourceMethod: "number",
                    value: v,
                    formatString: formatString,
                    msg: "Invalid number format, should have no more than 1 decimal"
                });
            } else {
                v = v.toFixed(0);
            }

            var fnum = v.toString();

            psplit = fnum.split('.');

            if (hasComma) {
                var cnum = psplit[0],
                    parr = [],
                    j    = cnum.length,
                    m    = Math.floor(j / 3),
                    n    = cnum.length % 3 || 3,
                    i;

                for (i = 0; i < j; i += n) {
                    if (i !== 0) {
                        n = 3;
                    }

                    parr[parr.length] = cnum.substr(i, n);
                    m -= 1;
                }
                fnum = parr.join(comma);
                if (psplit[1]) {
                    fnum += dec + psplit[1];
                }
            } else {
                if (psplit[1]) {
                    fnum = psplit[0] + dec + psplit[1];
                }
            }

            return (neg ? '-' : '') + formatString.replace(/[\d,?\.?]+/, fnum);
        },

        
        numberRenderer : function(format) {
            return function(v) {
                return UtilFormat.number(v, format);
            };
        },

        
        plural : function(v, s, p) {
            return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
        },

        
        nl2br : function(v) {
            return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
        },

        
        capitalize: Ext.String.capitalize,

        
        ellipsis: Ext.String.ellipsis,

        
        format: Ext.String.format,

        
        htmlDecode: Ext.String.htmlDecode,

        
        htmlEncode: Ext.String.htmlEncode,

        
        leftPad: Ext.String.leftPad,

        
        trim : Ext.String.trim,

        
        parseBox : function(box) {
            if (Ext.isNumber(box)) {
                box = box.toString();
            }
            var parts  = box.split(' '),
                ln = parts.length;

            if (ln == 1) {
                parts[1] = parts[2] = parts[3] = parts[0];
            }
            else if (ln == 2) {
                parts[2] = parts[0];
                parts[3] = parts[1];
            }
            else if (ln == 3) {
                parts[3] = parts[1];
            }

            return {
                top   :parseInt(parts[0], 10) || 0,
                right :parseInt(parts[1], 10) || 0,
                bottom:parseInt(parts[2], 10) || 0,
                left  :parseInt(parts[3], 10) || 0
            };
        },

        
        escapeRegex : function(s) {
            return s.replace(/([\-.*+?\^${}()|\[\]\/\\])/g, "\\$1");
        }
    });
})();


Ext.ns('Ext.util');

Ext.util.TaskRunner = function(interval) {
    interval = interval || 10;
    var tasks = [],
    removeQueue = [],
    id = 0,
    running = false,

    
    stopThread = function() {
        running = false;
        clearInterval(id);
        id = 0;
    },

    
    startThread = function() {
        if (!running) {
            running = true;
            id = setInterval(runTasks, interval);
        }
    },

    
    removeTask = function(t) {
        removeQueue.push(t);
        if (t.onStop) {
            t.onStop.apply(t.scope || t);
        }
    },

    
    runTasks = function() {
        var rqLen = removeQueue.length,
            now = new Date().getTime(),
            i;

        if (rqLen > 0) {
            for (i = 0; i < rqLen; i++) {
                Ext.Array.remove(tasks, removeQueue[i]);
            }
            removeQueue = [];
            if (tasks.length < 1) {
                stopThread();
                return;
            }
        }
        i = 0;
        var t,
            itime,
            rt,
            len = tasks.length;
        for (; i < len; ++i) {
            t = tasks[i];
            itime = now - t.taskRunTime;
            if (t.interval <= itime) {
                rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
                t.taskRunTime = now;
                if (rt === false || t.taskRunCount === t.repeat) {
                    removeTask(t);
                    return;
                }
            }
            if (t.duration && t.duration <= (now - t.taskStartTime)) {
                removeTask(t);
            }
        }
    };

    
    this.start = function(task) {
        tasks.push(task);
        task.taskStartTime = new Date().getTime();
        task.taskRunTime = 0;
        task.taskRunCount = 0;
        startThread();
        return task;
    };

    
    this.stop = function(task) {
        removeTask(task);
        return task;
    };

    
    this.stopAll = function() {
        stopThread();
        for (var i = 0, len = tasks.length; i < len; i++) {
            if (tasks[i].onStop) {
                tasks[i].onStop();
            }
        }
        tasks = [];
        removeQueue = [];
    };
};


Ext.TaskManager = Ext.create('Ext.util.TaskRunner');

Ext.is = {
    init : function(navigator) {
        var platforms = this.platforms,
            ln = platforms.length,
            i, platform;

        navigator = navigator || window.navigator;

        for (i = 0; i < ln; i++) {
            platform = platforms[i];
            this[platform.identity] = platform.regex.test(navigator[platform.property]);
        }

        
        this.Desktop = this.Mac || this.Windows || (this.Linux && !this.Android);
        
        this.Tablet = this.iPad;
        
        this.Phone = !this.Desktop && !this.Tablet;
        
        this.iOS = this.iPhone || this.iPad || this.iPod;
        
        
        this.Standalone = !!window.navigator.standalone;
    },
    
    
    platforms: [{
        property: 'platform',
        regex: /iPhone/i,
        identity: 'iPhone'
    },
    
    
    {
        property: 'platform',
        regex: /iPod/i,
        identity: 'iPod'
    },
    
    
    {
        property: 'userAgent',
        regex: /iPad/i,
        identity: 'iPad'
    },
    
    
    {
        property: 'userAgent',
        regex: /Blackberry/i,
        identity: 'Blackberry'
    },
    
    
    {
        property: 'userAgent',
        regex: /Android/i,
        identity: 'Android'
    },
    
    
    {
        property: 'platform',
        regex: /Mac/i,
        identity: 'Mac'
    },
    
    
    {
        property: 'platform',
        regex: /Win/i,
        identity: 'Windows'
    },
    
    
    {
        property: 'platform',
        regex: /Linux/i,
        identity: 'Linux'
    }]
};

Ext.is.init();


Ext.supports = {
    init : function() {
        var doc = document,
            div = doc.createElement('div'),
            tests = this.tests,
            ln = tests.length,
            i, test;

        div.innerHTML = [
            '<div style="height:30px;width:50px;">',
                '<div style="height:20px;width:20px;"></div>',
            '</div>',
            '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">',
                '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>',
            '</div>',
            '<div style="float:left; background-color:transparent;"></div>'
        ].join('');

        doc.body.appendChild(div);

        for (i = 0; i < ln; i++) {
            test = tests[i];
            this[test.identity] = test.fn.call(this, doc, div);
        }

        doc.body.removeChild(div);
    },

    
    CSS3BoxShadow: Ext.isDefined(document.documentElement.style.boxShadow),

    
    ClassList: !!document.documentElement.classList,

    
    OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),
    
    
    DeviceMotion: ('ondevicemotion' in window),
    
    
    
    
    Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),

    tests: [
        
        {
            identity: 'Transitions',
            fn: function(doc, div) {
                var prefix = [
                        'webkit',
                        'Moz',
                        'o',
                        'ms',
                        'khtml'
                    ],
                    TE = 'TransitionEnd',
                    transitionEndName = [
                        prefix[0] + TE,
                        'transitionend', 
                        prefix[2] + TE,
                        prefix[3] + TE,
                        prefix[4] + TE
                    ],
                    ln = prefix.length,
                    i = 0,
                    out = false;
                div = Ext.get(div);
                for (; i < ln; i++) {
                    if (div.getStyle(prefix[i] + "TransitionProperty")) {
                        Ext.supports.CSS3Prefix = prefix[i];
                        Ext.supports.CSS3TransitionEnd = transitionEndName[i];
                        out = true;
                        break;
                    }
                }
                return out;
            }
        },
        
        
        {
            identity: 'RightMargin',
            fn: function(doc, div) {
                var view = doc.defaultView;
                return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
            }
        },

        
        {
            identity: 'DisplayChangeInputSelectionBug',
            fn: function() {
                var webKitVersion = Ext.webKitVersion;
                
                return 0 < webKitVersion && webKitVersion < 533;
            }
        },

        
        {
            identity: 'DisplayChangeTextAreaSelectionBug',
            fn: function() {
                var webKitVersion = Ext.webKitVersion;

                
                return 0 < webKitVersion && webKitVersion < 534.24;
            }
        },

        
        {
            identity: 'TransparentColor',
            fn: function(doc, div, view) {
                view = doc.defaultView;
                return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
            }
        },

        
        {
            identity: 'ComputedStyle',
            fn: function(doc, div, view) {
                view = doc.defaultView;
                return view && view.getComputedStyle;
            }
        },
        
        
        {
            identity: 'Svg',
            fn: function(doc) {
                return !!doc.createElementNS && !!doc.createElementNS( "http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
            }
        },
    
        
        {
            identity: 'Canvas',
            fn: function(doc) {
                return !!doc.createElement('canvas').getContext;
            }
        },
        
        
        {
            identity: 'Vml',
            fn: function(doc) {
                var d = doc.createElement("div");
                d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
                return (d.childNodes.length == 2);
            }
        },
        
        
        {
            identity: 'Float',
            fn: function(doc, div) {
                return !!div.lastChild.style.cssFloat;
            }
        },
        
        
        {
            identity: 'AudioTag',
            fn: function(doc) {
                return !!doc.createElement('audio').canPlayType;
            }
        },
        
        
        {
            identity: 'History',
            fn: function() {
                return !!(window.history && history.pushState);
            }
        },
        
        
        {
            identity: 'CSS3DTransform',
            fn: function() {
                return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
            }
        },

		
        {
            identity: 'CSS3LinearGradient',
            fn: function(doc, div) {
                var property = 'background-image:',
                    webkit   = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
                    w3c      = 'linear-gradient(left top, black, white)',
                    moz      = '-moz-' + w3c,
                    options  = [property + webkit, property + w3c, property + moz];
                
                div.style.cssText = options.join(';');
                
                return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
            }
        },
        
        
        {
            identity: 'CSS3BorderRadius',
            fn: function(doc, div) {
                var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
                    pass = false,
                    i;
                for (i = 0; i < domPrefixes.length; i++) {
                    if (document.body.style[domPrefixes[i]] !== undefined) {
                        return true;
                    }
                }
                return pass;
            }
        },
        
        
        {
            identity: 'GeoLocation',
            fn: function() {
                return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
            }
        },
        
        {
            identity: 'MouseEnterLeave',
            fn: function(doc, div){
                return ('onmouseenter' in div && 'onmouseleave' in div);
            }
        },
        
        {
            identity: 'MouseWheel',
            fn: function(doc, div) {
                return ('onmousewheel' in div);
            }
        },
        
        {
            identity: 'Opacity',
            fn: function(doc, div){
                
                if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
                    return false;
                }
                div.firstChild.style.cssText = 'opacity:0.73';
                return div.firstChild.style.opacity == '0.73';
            }
        },
        
        {
            identity: 'Placeholder',
            fn: function(doc) {
                return 'placeholder' in doc.createElement('input');
            }
        },
        
        
        {
            identity: 'Direct2DBug',
            fn: function() {
                return Ext.isString(document.body.style.msTransformOrigin);
            }
        },
        
        {
            identity: 'BoundingClientRect',
            fn: function(doc, div) {
                return Ext.isFunction(div.getBoundingClientRect);
            }
        },
        {
            identity: 'IncludePaddingInWidthCalculation',
            fn: function(doc, div){
                var el = Ext.get(div.childNodes[1].firstChild);
                return el.getWidth() == 210;
            }
        },
        {
            identity: 'IncludePaddingInHeightCalculation',
            fn: function(doc, div){
                var el = Ext.get(div.childNodes[1].firstChild);
                return el.getHeight() == 210;
            }
        },
        
        
        {
            identity: 'ArraySort',
            fn: function() {
                var a = [1,2,3,4,5].sort(function(){ return 0; });
                return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
            }
        },
        
        {
            identity: 'Range',
            fn: function() {
                return !!document.createRange;
            }
        },
        
        {
            identity: 'CreateContextualFragment',
            fn: function() {
                var range = Ext.supports.Range ? document.createRange() : false;
                
                return range && !!range.createContextualFragment;
            }
        },

        
        {
            identity: 'WindowOnError',
            fn: function () {
                
                return Ext.isIE || Ext.isGecko || Ext.webKitVersion >= 534.16; 
            }
        }
    ]
};





Ext.ns('Ext.core');
Ext.core.DomHelper = function(){
    var tempTableEl = null,
        emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
        tableRe = /^table|tbody|tr|td$/i,
        confRe = /tag|children|cn|html$/i,
        tableElRe = /td|tr|tbody/i,
        endRe = /end/i,
        pub,
        
        afterbegin = 'afterbegin',
        afterend = 'afterend',
        beforebegin = 'beforebegin',
        beforeend = 'beforeend',
        ts = '<table>',
        te = '</table>',
        tbs = ts+'<tbody>',
        tbe = '</tbody>'+te,
        trs = tbs + '<tr>',
        tre = '</tr>'+tbe;

    
    function doInsert(el, o, returnElement, pos, sibling, append){
        el = Ext.getDom(el);
        var newNode;
        if (pub.useDom) {
            newNode = createDom(o, null);
            if (append) {
                el.appendChild(newNode);
            } else {
                (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
            }
        } else {
            newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o));
        }
        return returnElement ? Ext.get(newNode, true) : newNode;
    }
    
    function createDom(o, parentNode){
        var el,
            doc = document,
            useSet,
            attr,
            val,
            cn;

        if (Ext.isArray(o)) {                       
            el = doc.createDocumentFragment(); 
            for (var i = 0, l = o.length; i < l; i++) {
                createDom(o[i], el);
            }
        } else if (typeof o == 'string') {         
            el = doc.createTextNode(o);
        } else {
            el = doc.createElement( o.tag || 'div' );
            useSet = !!el.setAttribute; 
            for (attr in o) {
                if(!confRe.test(attr)){
                    val = o[attr];
                    if(attr == 'cls'){
                        el.className = val;
                    }else{
                        if(useSet){
                            el.setAttribute(attr, val);
                        }else{
                            el[attr] = val;
                        }
                    }
                }
            }
            Ext.core.DomHelper.applyStyles(el, o.style);

            if ((cn = o.children || o.cn)) {
                createDom(cn, el);
            } else if (o.html) {
                el.innerHTML = o.html;
            }
        }
        if(parentNode){
           parentNode.appendChild(el);
        }
        return el;
    }

    
    function createHtml(o){
        var b = '',
            attr,
            val,
            key,
            cn,
            i;

        if(typeof o == "string"){
            b = o;
        } else if (Ext.isArray(o)) {
            for (i=0; i < o.length; i++) {
                if(o[i]) {
                    b += createHtml(o[i]);
                }
            }
        } else {
            b += '<' + (o.tag = o.tag || 'div');
            for (attr in o) {
                val = o[attr];
                if(!confRe.test(attr)){
                    if (typeof val == "object") {
                        b += ' ' + attr + '="';
                        for (key in val) {
                            b += key + ':' + val[key] + ';';
                        }
                        b += '"';
                    }else{
                        b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
                    }
                }
            }
            
            if (emptyTags.test(o.tag)) {
                b += '/>';
            } else {
                b += '>';
                if ((cn = o.children || o.cn)) {
                    b += createHtml(cn);
                } else if(o.html){
                    b += o.html;
                }
                b += '</' + o.tag + '>';
            }
        }
        return b;
    }

    function ieTable(depth, s, h, e){
        tempTableEl.innerHTML = [s, h, e].join('');
        var i = -1,
            el = tempTableEl,
            ns;
        while(++i < depth){
            el = el.firstChild;
        }

        ns = el.nextSibling;
        if (ns){
            var df = document.createDocumentFragment();
            while(el){
                ns = el.nextSibling;
                df.appendChild(el);
                el = ns;
            }
            el = df;
        }
        return el;
    }

    
    function insertIntoTable(tag, where, el, html) {
        var node,
            before;

        tempTableEl = tempTableEl || document.createElement('div');

        if(tag == 'td' && (where == afterbegin || where == beforeend) ||
           !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
            return null;
        }
        before = where == beforebegin ? el :
                 where == afterend ? el.nextSibling :
                 where == afterbegin ? el.firstChild : null;

        if (where == beforebegin || where == afterend) {
            el = el.parentNode;
        }

        if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
            node = ieTable(4, trs, html, tre);
        } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
                   (tag == 'tr' && (where == beforebegin || where == afterend))) {
            node = ieTable(3, tbs, html, tbe);
        } else {
            node = ieTable(2, ts, html, te);
        }
        el.insertBefore(node, before);
        return node;
    }
    
       
    function createContextualFragment(html){
        var div = document.createElement("div"),
            fragment = document.createDocumentFragment(),
            i = 0,
            length, childNodes;
        
        div.innerHTML = html;
        childNodes = div.childNodes;
        length = childNodes.length;

        for (; i < length; i++) {
            fragment.appendChild(childNodes[i].cloneNode(true));
        }

        return fragment;
    }
    
    pub = {
        
        markup : function(o){
            return createHtml(o);
        },

        
        applyStyles : function(el, styles){
            if (styles) {
                el = Ext.fly(el);
                if (typeof styles == "function") {
                    styles = styles.call();
                }
                if (typeof styles == "string") {
                    styles = Ext.core.Element.parseStyles(styles);
                }
                if (typeof styles == "object") {
                    el.setStyle(styles);
                }
            }
        },

        
        insertHtml : function(where, el, html){
            var hash = {},
                hashVal,
                range,
                rangeEl,
                setStart,
                frag,
                rs;

            where = where.toLowerCase();
            
            hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
            hash[afterend] = ['AfterEnd', 'nextSibling'];
            
            
            if (el.insertAdjacentHTML) {
                if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
                    return rs;
                }
                
                
                hash[afterbegin] = ['AfterBegin', 'firstChild'];
                hash[beforeend] = ['BeforeEnd', 'lastChild'];
                if ((hashVal = hash[where])) {
                    el.insertAdjacentHTML(hashVal[0], html);
                    return el[hashVal[1]];
                }
            
            } else {
                
                if (Ext.isTextNode(el)) {
                    where = where === 'afterbegin' ? 'beforebegin' : where; 
                    where = where === 'beforeend' ? 'afterend' : where;
                }
                range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
                setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
                if (hash[where]) {
                    if (range) {
                        range[setStart](el);
                        frag = range.createContextualFragment(html);
                    } else {
                        frag = createContextualFragment(html);
                    }
                    el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
                    return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
                } else {
                    rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
                    if (el.firstChild) {
                        if (range) {
                            range[setStart](el[rangeEl]);
                            frag = range.createContextualFragment(html);
                        } else {
                            frag = createContextualFragment(html);
                        }
                        
                        if(where == afterbegin){
                            el.insertBefore(frag, el.firstChild);
                        }else{
                            el.appendChild(frag);
                        }
                    } else {
                        el.innerHTML = html;
                    }
                    return el[rangeEl];
                }
            }
            Ext.Error.raise({
                sourceClass: 'Ext.core.DomHelper',
                sourceMethod: 'insertHtml',
                htmlToInsert: html,
                targetElement: el,
                msg: 'Illegal insertion point reached: "' + where + '"'
            });
        },

        
        insertBefore : function(el, o, returnElement){
            return doInsert(el, o, returnElement, beforebegin);
        },

        
        insertAfter : function(el, o, returnElement){
            return doInsert(el, o, returnElement, afterend, 'nextSibling');
        },

        
        insertFirst : function(el, o, returnElement){
            return doInsert(el, o, returnElement, afterbegin, 'firstChild');
        },

        
        append : function(el, o, returnElement){
            return doInsert(el, o, returnElement, beforeend, '', true);
        },

        
        overwrite : function(el, o, returnElement){
            el = Ext.getDom(el);
            el.innerHTML = createHtml(o);
            return returnElement ? Ext.get(el.firstChild) : el.firstChild;
        },

        createHtml : createHtml,
        
        
        createDom: createDom,
        
        
        useDom : false,
        
        
        createTemplate : function(o){
            var html = Ext.core.DomHelper.createHtml(o);
            return Ext.create('Ext.Template', html);
        }
    };
    return pub;
}();



Ext.ns('Ext.core');

Ext.core.DomQuery = Ext.DomQuery = function(){
    var cache = {},
        simpleCache = {},
        valueCache = {},
        nonSpace = /\S/,
        trimRe = /^\s+|\s+$/g,
        tplRe = /\{(\d+)\}/g,
        modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
        tagTokenRe = /^(#)?([\w-\*]+)/,
        nthRe = /(\d*)n\+?(\d*)/,
        nthRe2 = /\D/,
        
    
    
    isIE = window.ActiveXObject ? true : false,
    key = 30803;

    
    
    eval("var batch = 30803;");

    
    
    function child(parent, index){
        var i = 0,
            n = parent.firstChild;
        while(n){
            if(n.nodeType == 1){
               if(++i == index){
                   return n;
               }
            }
            n = n.nextSibling;
        }
        return null;
    }

    
    function next(n){
        while((n = n.nextSibling) && n.nodeType != 1);
        return n;
    }

    
    function prev(n){
        while((n = n.previousSibling) && n.nodeType != 1);
        return n;
    }

    
    
    function children(parent){
        var n = parent.firstChild,
        nodeIndex = -1,
        nextNode;
        while(n){
            nextNode = n.nextSibling;
            
            if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
            parent.removeChild(n);
            }else{
            
            n.nodeIndex = ++nodeIndex;
            }
            n = nextNode;
        }
        return this;
    }


    
    
    function byClassName(nodeSet, cls){
        if(!cls){
            return nodeSet;
        }
        var result = [], ri = -1;
        for(var i = 0, ci; ci = nodeSet[i]; i++){
            if((' '+ci.className+' ').indexOf(cls) != -1){
                result[++ri] = ci;
            }
        }
        return result;
    };

    function attrValue(n, attr){
        
        if(!n.tagName && typeof n.length != "undefined"){
            n = n[0];
        }
        if(!n){
            return null;
        }

        if(attr == "for"){
            return n.htmlFor;
        }
        if(attr == "class" || attr == "className"){
            return n.className;
        }
        return n.getAttribute(attr) || n[attr];

    };


    
    
    
    function getNodes(ns, mode, tagName){
        var result = [], ri = -1, cs;
        if(!ns){
            return result;
        }
        tagName = tagName || "*";
        
        if(typeof ns.getElementsByTagName != "undefined"){
            ns = [ns];
        }

        
        
        if(!mode){
            for(var i = 0, ni; ni = ns[i]; i++){
                cs = ni.getElementsByTagName(tagName);
                for(var j = 0, ci; ci = cs[j]; j++){
                    result[++ri] = ci;
                }
            }
        
        
        } else if(mode == "/" || mode == ">"){
            var utag = tagName.toUpperCase();
            for(var i = 0, ni, cn; ni = ns[i]; i++){
                cn = ni.childNodes;
                for(var j = 0, cj; cj = cn[j]; j++){
                    if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
                        result[++ri] = cj;
                    }
                }
            }
        
        
        }else if(mode == "+"){
            var utag = tagName.toUpperCase();
            for(var i = 0, n; n = ns[i]; i++){
                while((n = n.nextSibling) && n.nodeType != 1);
                if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
                    result[++ri] = n;
                }
            }
        
        
        }else if(mode == "~"){
            var utag = tagName.toUpperCase();
            for(var i = 0, n; n = ns[i]; i++){
                while((n = n.nextSibling)){
                    if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
                        result[++ri] = n;
                    }
                }
            }
        }
        return result;
    }

    function concat(a, b){
        if(b.slice){
            return a.concat(b);
        }
        for(var i = 0, l = b.length; i < l; i++){
            a[a.length] = b[i];
        }
        return a;
    }

    function byTag(cs, tagName){
        if(cs.tagName || cs == document){
            cs = [cs];
        }
        if(!tagName){
            return cs;
        }
        var result = [], ri = -1;
        tagName = tagName.toLowerCase();
        for(var i = 0, ci; ci = cs[i]; i++){
            if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
                result[++ri] = ci;
            }
        }
        return result;
    }

    function byId(cs, id){
        if(cs.tagName || cs == document){
            cs = [cs];
        }
        if(!id){
            return cs;
        }
        var result = [], ri = -1;
        for(var i = 0, ci; ci = cs[i]; i++){
            if(ci && ci.id == id){
                result[++ri] = ci;
                return result;
            }
        }
        return result;
    }

    
    
    function byAttribute(cs, attr, value, op, custom){
        var result = [],
            ri = -1,
            useGetStyle = custom == "{",
            fn = Ext.DomQuery.operators[op],
            a,
            xml,
            hasXml;

        for(var i = 0, ci; ci = cs[i]; i++){
            
            if(ci.nodeType != 1){
                continue;
            }
            
            if(!hasXml){
                xml = Ext.DomQuery.isXml(ci);
                hasXml = true;
            }

            
            if(!xml){
                if(useGetStyle){
                    a = Ext.DomQuery.getStyle(ci, attr);
                } else if (attr == "class" || attr == "className"){
                    a = ci.className;
                } else if (attr == "for"){
                    a = ci.htmlFor;
                } else if (attr == "href"){
                    
                    
                    a = ci.getAttribute("href", 2);
                } else{
                    a = ci.getAttribute(attr);
                }
            }else{
                a = ci.getAttribute(attr);
            }
            if((fn && fn(a, value)) || (!fn && a)){
                result[++ri] = ci;
            }
        }
        return result;
    }

    function byPseudo(cs, name, value){
        return Ext.DomQuery.pseudos[name](cs, value);
    }

    function nodupIEXml(cs){
        var d = ++key,
            r;
        cs[0].setAttribute("_nodup", d);
        r = [cs[0]];
        for(var i = 1, len = cs.length; i < len; i++){
            var c = cs[i];
            if(!c.getAttribute("_nodup") != d){
                c.setAttribute("_nodup", d);
                r[r.length] = c;
            }
        }
        for(var i = 0, len = cs.length; i < len; i++){
            cs[i].removeAttribute("_nodup");
        }
        return r;
    }

    function nodup(cs){
        if(!cs){
            return [];
        }
        var len = cs.length, c, i, r = cs, cj, ri = -1;
        if(!len || typeof cs.nodeType != "undefined" || len == 1){
            return cs;
        }
        if(isIE && typeof cs[0].selectSingleNode != "undefined"){
            return nodupIEXml(cs);
        }
        var d = ++key;
        cs[0]._nodup = d;
        for(i = 1; c = cs[i]; i++){
            if(c._nodup != d){
                c._nodup = d;
            }else{
                r = [];
                for(var j = 0; j < i; j++){
                    r[++ri] = cs[j];
                }
                for(j = i+1; cj = cs[j]; j++){
                    if(cj._nodup != d){
                        cj._nodup = d;
                        r[++ri] = cj;
                    }
                }
                return r;
            }
        }
        return r;
    }

    function quickDiffIEXml(c1, c2){
        var d = ++key,
            r = [];
        for(var i = 0, len = c1.length; i < len; i++){
            c1[i].setAttribute("_qdiff", d);
        }
        for(var i = 0, len = c2.length; i < len; i++){
            if(c2[i].getAttribute("_qdiff") != d){
                r[r.length] = c2[i];
            }
        }
        for(var i = 0, len = c1.length; i < len; i++){
           c1[i].removeAttribute("_qdiff");
        }
        return r;
    }

    function quickDiff(c1, c2){
        var len1 = c1.length,
            d = ++key,
            r = [];
        if(!len1){
            return c2;
        }
        if(isIE && typeof c1[0].selectSingleNode != "undefined"){
            return quickDiffIEXml(c1, c2);
        }
        for(var i = 0; i < len1; i++){
            c1[i]._qdiff = d;
        }
        for(var i = 0, len = c2.length; i < len; i++){
            if(c2[i]._qdiff != d){
                r[r.length] = c2[i];
            }
        }
        return r;
    }

    function quickId(ns, mode, root, id){
        if(ns == root){
           var d = root.ownerDocument || root;
           return d.getElementById(id);
        }
        ns = getNodes(ns, mode, "*");
        return byId(ns, id);
    }

    return {
        getStyle : function(el, name){
            return Ext.fly(el).getStyle(name);
        },
        
        compile : function(path, type){
            type = type || "select";

            
            var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
                mode,
                lastPath,
                matchers = Ext.DomQuery.matchers,
                matchersLn = matchers.length,
                modeMatch,
                
                lmode = path.match(modeRe);

            if(lmode && lmode[1]){
                fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
                path = path.replace(lmode[1], "");
            }

            
            while(path.substr(0, 1)=="/"){
                path = path.substr(1);
            }

            while(path && lastPath != path){
                lastPath = path;
                var tokenMatch = path.match(tagTokenRe);
                if(type == "select"){
                    if(tokenMatch){
                        
                        if(tokenMatch[1] == "#"){
                            fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
                        }else{
                            fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
                        }
                        path = path.replace(tokenMatch[0], "");
                    }else if(path.substr(0, 1) != '@'){
                        fn[fn.length] = 'n = getNodes(n, mode, "*");';
                    }
                
                }else{
                    if(tokenMatch){
                        if(tokenMatch[1] == "#"){
                            fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
                        }else{
                            fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
                        }
                        path = path.replace(tokenMatch[0], "");
                    }
                }
                while(!(modeMatch = path.match(modeRe))){
                    var matched = false;
                    for(var j = 0; j < matchersLn; j++){
                        var t = matchers[j];
                        var m = path.match(t.re);
                        if(m){
                            fn[fn.length] = t.select.replace(tplRe, function(x, i){
                                return m[i];
                            });
                            path = path.replace(m[0], "");
                            matched = true;
                            break;
                        }
                    }
                    
                    if(!matched){
                        Ext.Error.raise({
                            sourceClass: 'Ext.DomQuery',
                            sourceMethod: 'compile',
                            msg: 'Error parsing selector. Parsing failed at "' + path + '"'
                        });
                    }
                }
                if(modeMatch[1]){
                    fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
                    path = path.replace(modeMatch[1], "");
                }
            }
            
            fn[fn.length] = "return nodup(n);\n}";

            
            eval(fn.join(""));
            return f;
        },

        
        jsSelect: function(path, root, type){
            
            root = root || document;

            if(typeof root == "string"){
                root = document.getElementById(root);
            }
            var paths = path.split(","),
                results = [];

            
            for(var i = 0, len = paths.length; i < len; i++){
                var subPath = paths[i].replace(trimRe, "");
                
                if(!cache[subPath]){
                    cache[subPath] = Ext.DomQuery.compile(subPath);
                    if(!cache[subPath]){
                        Ext.Error.raise({
                            sourceClass: 'Ext.DomQuery',
                            sourceMethod: 'jsSelect',
                            msg: subPath + ' is not a valid selector'
                        });
                    }
                }
                var result = cache[subPath](root);
                if(result && result != document){
                    results = results.concat(result);
                }
            }

            
            
            if(paths.length > 1){
                return nodup(results);
            }
            return results;
        },

        isXml: function(el) {
            var docEl = (el ? el.ownerDocument || el : 0).documentElement;
            return docEl ? docEl.nodeName !== "HTML" : false;
        },

        
        select : document.querySelectorAll ? function(path, root, type) {
            root = root || document;
            if (!Ext.DomQuery.isXml(root)) {
            try {
                var cs = root.querySelectorAll(path);
                return Ext.Array.toArray(cs);
            }
            catch (ex) {}
            }
            return Ext.DomQuery.jsSelect.call(this, path, root, type);
        } : function(path, root, type) {
            return Ext.DomQuery.jsSelect.call(this, path, root, type);
        },

        
        selectNode : function(path, root){
            return Ext.DomQuery.select(path, root)[0];
        },

        
        selectValue : function(path, root, defaultValue){
            path = path.replace(trimRe, "");
            if(!valueCache[path]){
                valueCache[path] = Ext.DomQuery.compile(path, "select");
            }
            var n = valueCache[path](root), v;
            n = n[0] ? n[0] : n;

            
            
            
            
            if (typeof n.normalize == 'function') n.normalize();

            v = (n && n.firstChild ? n.firstChild.nodeValue : null);
            return ((v === null||v === undefined||v==='') ? defaultValue : v);
        },

        
        selectNumber : function(path, root, defaultValue){
            var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
            return parseFloat(v);
        },

        
        is : function(el, ss){
            if(typeof el == "string"){
                el = document.getElementById(el);
            }
            var isArray = Ext.isArray(el),
                result = Ext.DomQuery.filter(isArray ? el : [el], ss);
            return isArray ? (result.length == el.length) : (result.length > 0);
        },

        
        filter : function(els, ss, nonMatches){
            ss = ss.replace(trimRe, "");
            if(!simpleCache[ss]){
                simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
            }
            var result = simpleCache[ss](els);
            return nonMatches ? quickDiff(result, els) : result;
        },

        
        matchers : [{
                re: /^\.([\w-]+)/,
                select: 'n = byClassName(n, " {1} ");'
            }, {
                re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
                select: 'n = byPseudo(n, "{1}", "{2}");'
            },{
                re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
                select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
            }, {
                re: /^#([\w-]+)/,
                select: 'n = byId(n, "{1}");'
            },{
                re: /^@([\w-]+)/,
                select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
            }
        ],

        
        operators : {
            "=" : function(a, v){
                return a == v;
            },
            "!=" : function(a, v){
                return a != v;
            },
            "^=" : function(a, v){
                return a && a.substr(0, v.length) == v;
            },
            "$=" : function(a, v){
                return a && a.substr(a.length-v.length) == v;
            },
            "*=" : function(a, v){
                return a && a.indexOf(v) !== -1;
            },
            "%=" : function(a, v){
                return (a % v) == 0;
            },
            "|=" : function(a, v){
                return a && (a == v || a.substr(0, v.length+1) == v+'-');
            },
            "~=" : function(a, v){
                return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
            }
        },

        
        pseudos : {
            "first-child" : function(c){
                var r = [], ri = -1, n;
                for(var i = 0, ci; ci = n = c[i]; i++){
                    while((n = n.previousSibling) && n.nodeType != 1);
                    if(!n){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "last-child" : function(c){
                var r = [], ri = -1, n;
                for(var i = 0, ci; ci = n = c[i]; i++){
                    while((n = n.nextSibling) && n.nodeType != 1);
                    if(!n){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "nth-child" : function(c, a) {
                var r = [], ri = -1,
                    m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
                    f = (m[1] || 1) - 0, l = m[2] - 0;
                for(var i = 0, n; n = c[i]; i++){
                    var pn = n.parentNode;
                    if (batch != pn._batch) {
                        var j = 0;
                        for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
                            if(cn.nodeType == 1){
                               cn.nodeIndex = ++j;
                            }
                        }
                        pn._batch = batch;
                    }
                    if (f == 1) {
                        if (l == 0 || n.nodeIndex == l){
                            r[++ri] = n;
                        }
                    } else if ((n.nodeIndex + l) % f == 0){
                        r[++ri] = n;
                    }
                }

                return r;
            },

            "only-child" : function(c){
                var r = [], ri = -1;;
                for(var i = 0, ci; ci = c[i]; i++){
                    if(!prev(ci) && !next(ci)){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "empty" : function(c){
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    var cns = ci.childNodes, j = 0, cn, empty = true;
                    while(cn = cns[j]){
                        ++j;
                        if(cn.nodeType == 1 || cn.nodeType == 3){
                            empty = false;
                            break;
                        }
                    }
                    if(empty){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "contains" : function(c, v){
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "nodeValue" : function(c, v){
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    if(ci.firstChild && ci.firstChild.nodeValue == v){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "checked" : function(c){
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    if(ci.checked == true){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "not" : function(c, ss){
                return Ext.DomQuery.filter(c, ss, true);
            },

            "any" : function(c, selectors){
                var ss = selectors.split('|'),
                    r = [], ri = -1, s;
                for(var i = 0, ci; ci = c[i]; i++){
                    for(var j = 0; s = ss[j]; j++){
                        if(Ext.DomQuery.is(ci, s)){
                            r[++ri] = ci;
                            break;
                        }
                    }
                }
                return r;
            },

            "odd" : function(c){
                return this["nth-child"](c, "odd");
            },

            "even" : function(c){
                return this["nth-child"](c, "even");
            },

            "nth" : function(c, a){
                return c[a-1] || [];
            },

            "first" : function(c){
                return c[0] || [];
            },

            "last" : function(c){
                return c[c.length-1] || [];
            },

            "has" : function(c, ss){
                var s = Ext.DomQuery.select,
                    r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    if(s(ss, ci).length > 0){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "next" : function(c, ss){
                var is = Ext.DomQuery.is,
                    r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    var n = next(ci);
                    if(n && is(n, ss)){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "prev" : function(c, ss){
                var is = Ext.DomQuery.is,
                    r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    var n = prev(ci);
                    if(n && is(n, ss)){
                        r[++ri] = ci;
                    }
                }
                return r;
            }
        }
    };
}();


Ext.query = Ext.DomQuery.select;


 (function() {
    var DOC = document,
        EC = Ext.cache;

    Ext.Element = Ext.core.Element = function(element, forceNew) {
        var dom = typeof element == "string" ? DOC.getElementById(element) : element,
        id;

        if (!dom) {
            return null;
        }

        id = dom.id;

        if (!forceNew && id && EC[id]) {
            
            return EC[id].el;
        }

        
        this.dom = dom;

        
        this.id = id || Ext.id(dom);
    };

    var DH = Ext.core.DomHelper,
    El = Ext.core.Element;


    El.prototype = {
        
        set: function(o, useSet) {
            var el = this.dom,
                attr,
                val;
            useSet = (useSet !== false) && !!el.setAttribute;

            for (attr in o) {
                if (o.hasOwnProperty(attr)) {
                    val = o[attr];
                    if (attr == 'style') {
                        DH.applyStyles(el, val);
                    } else if (attr == 'cls') {
                        el.className = val;
                    } else if (useSet) {
                        el.setAttribute(attr, val);
                    } else {
                        el[attr] = val;
                    }
                }
            }
            return this;
        },

        
        
        
        
        
        
        
        
        
        
        

        
        
        
        


        
        
        
        
        
        
        

        
        
        
        
        
        
        

        
        
        
        

        
        
        
        
        
        
        
        

        
        defaultUnit: "px",

        
        is: function(simpleSelector) {
            return Ext.DomQuery.is(this.dom, simpleSelector);
        },

        
        focus: function(defer,
                        
                        dom) {
            var me = this;
            dom = dom || me.dom;
            try {
                if (Number(defer)) {
                    Ext.defer(me.focus, defer, null, [null, dom]);
                } else {
                    dom.focus();
                }
            } catch(e) {}
            return me;
        },

        
        blur: function() {
            try {
                this.dom.blur();
            } catch(e) {}
            return this;
        },

        
        getValue: function(asNumber) {
            var val = this.dom.value;
            return asNumber ? parseInt(val, 10) : val;
        },

        
        addListener: function(eventName, fn, scope, options) {
            Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
            return this;
        },

        
        removeListener: function(eventName, fn, scope) {
            Ext.EventManager.un(this.dom, eventName, fn, scope || this);
            return this;
        },

        
        removeAllListeners: function() {
            Ext.EventManager.removeAll(this.dom);
            return this;
        },

        
        purgeAllListeners: function() {
            Ext.EventManager.purgeElement(this);
            return this;
        },

        
        addUnits: function(size, units) {

            
            if (Ext.isNumber(size)) {
                return size + (units || this.defaultUnit || 'px');
            }

            
            if (size === "" || size == "auto" || size === undefined || size === null) {
                return size || '';
            }

            
            if (!unitPattern.test(size)) {
                if (Ext.isDefined(Ext.global.console)) {
                    Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
                }
                return size || '';
            }
            return size;
        },

        
        isBorderBox: function() {
            return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
        },

        
        remove: function() {
            var me = this,
            dom = me.dom;

            if (dom) {
                delete me.dom;
                Ext.removeNode(dom);
            }
        },

        
        hover: function(overFn, outFn, scope, options) {
            var me = this;
            me.on('mouseenter', overFn, scope || me.dom, options);
            me.on('mouseleave', outFn, scope || me.dom, options);
            return me;
        },

        
        contains: function(el) {
            return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el);
        },

        
        getAttributeNS: function(ns, name) {
            return this.getAttribute(name, ns);
        },

        
        getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
        function(name, ns) {
            var d = this.dom,
            type;
            if(ns) {
                type = typeof d[ns + ":" + name];
                if (type != 'undefined' && type != 'unknown') {
                    return d[ns + ":" + name] || null;
                }
                return null;
            }
            if (name === "for") {
                name = "htmlFor";
            }
            return d[name] || null;
        }: function(name, ns) {
            var d = this.dom;
            if (ns) {
               return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
            }
            return  d.getAttribute(name) || d[name] || null;
        },

        
        update: function(html) {
            if (this.dom) {
                this.dom.innerHTML = html;
            }
            return this;
        }
    };

    var ep = El.prototype;

    El.addMethods = function(o) {
        Ext.apply(ep, o);
    };

    
    ep.on = ep.addListener;

    
    ep.un = ep.removeListener;

    
    ep.clearListeners = ep.removeAllListeners;

    
    ep.destroy = ep.remove;

    
    ep.autoBoxAdjust = true;

    
    var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
    docEl;

    
    El.get = function(el) {
        var ex,
        elm,
        id;
        if (!el) {
            return null;
        }
        if (typeof el == "string") {
            
            if (! (elm = DOC.getElementById(el))) {
                return null;
            }
            if (EC[el] && EC[el].el) {
                ex = EC[el].el;
                ex.dom = elm;
            } else {
                ex = El.addToCache(new El(elm));
            }
            return ex;
        } else if (el.tagName) {
            
            if (! (id = el.id)) {
                id = Ext.id(el);
            }
            if (EC[id] && EC[id].el) {
                ex = EC[id].el;
                ex.dom = el;
            } else {
                ex = El.addToCache(new El(el));
            }
            return ex;
        } else if (el instanceof El) {
            if (el != docEl) {
                
                
                
                if (Ext.isIE && (el.id == undefined || el.id == '')) {
                    el.dom = el.dom;
                } else {
                    el.dom = DOC.getElementById(el.id) || el.dom;
                }
            }
            return el;
        } else if (el.isComposite) {
            return el;
        } else if (Ext.isArray(el)) {
            return El.select(el);
        } else if (el == DOC) {
            
            if (!docEl) {
                var f = function() {};
                f.prototype = El.prototype;
                docEl = new f();
                docEl.dom = DOC;
            }
            return docEl;
        }
        return null;
    };

    El.addToCache = function(el, id) {
        if (el) {
            id = id || el.id;
            EC[id] = {
                el: el,
                data: {},
                events: {}
            };
        }
        return el;
    };

    
    El.data = function(el, key, value) {
        el = El.get(el);
        if (!el) {
            return null;
        }
        var c = EC[el.id].data;
        if (arguments.length == 2) {
            return c[key];
        } else {
            return (c[key] = value);
        }
    };

    
    
    
    function garbageCollect() {
        if (!Ext.enableGarbageCollector) {
            clearInterval(El.collectorThreadId);
        } else {
            var eid,
            el,
            d,
            o;

            for (eid in EC) {
                if (!EC.hasOwnProperty(eid)) {
                    continue;
                }
                o = EC[eid];
                if (o.skipGarbageCollection) {
                    continue;
                }
                el = o.el;
                d = el.dom;
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
                    if (d && Ext.enableListenerCollection) {
                        Ext.EventManager.removeAll(d);
                    }
                    delete EC[eid];
                }
            }
            
            if (Ext.isIE) {
                var t = {};
                for (eid in EC) {
                    if (!EC.hasOwnProperty(eid)) {
                        continue;
                    }
                    t[eid] = EC[eid];
                }
                EC = Ext.cache = t;
            }
        }
    }
    El.collectorThreadId = setInterval(garbageCollect, 30000);

    var flyFn = function() {};
    flyFn.prototype = El.prototype;

    
    El.Flyweight = function(dom) {
        this.dom = dom;
    };

    El.Flyweight.prototype = new flyFn();
    El.Flyweight.prototype.isFlyweight = true;
    El._flyweights = {};

    
    El.fly = function(el, named) {
        var ret = null;
        named = named || '_global';
        el = Ext.getDom(el);
        if (el) {
            (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
            ret = El._flyweights[named];
        }
        return ret;
    };

    
    Ext.get = El.get;

    
    Ext.fly = El.fly;

    
    var noBoxAdjust = Ext.isStrict ? {
        select: 1
    }: {
        input: 1,
        select: 1,
        textarea: 1
    };
    if (Ext.isIE || Ext.isGecko) {
        noBoxAdjust['button'] = 1;
    }
})();


Ext.core.Element.addMethods({
    
    findParent : function(simpleSelector, maxDepth, returnEl) {
        var p = this.dom,
            b = document.body,
            depth = 0,
            stopEl;

        maxDepth = maxDepth || 50;
        if (isNaN(maxDepth)) {
            stopEl = Ext.getDom(maxDepth);
            maxDepth = Number.MAX_VALUE;
        }
        while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
            if (Ext.DomQuery.is(p, simpleSelector)) {
                return returnEl ? Ext.get(p) : p;
            }
            depth++;
            p = p.parentNode;
        }
        return null;
    },
    
    
    findParentNode : function(simpleSelector, maxDepth, returnEl) {
        var p = Ext.fly(this.dom.parentNode, '_internal');
        return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
    },

    
    up : function(simpleSelector, maxDepth) {
        return this.findParentNode(simpleSelector, maxDepth, true);
    },

    
    select : function(selector) {
        return Ext.core.Element.select(selector, false,  this.dom);
    },

    
    query : function(selector) {
        return Ext.DomQuery.select(selector, this.dom);
    },

    
    down : function(selector, returnDom) {
        var n = Ext.DomQuery.selectNode(selector, this.dom);
        return returnDom ? n : Ext.get(n);
    },

    
    child : function(selector, returnDom) {
        var node,
            me = this,
            id;
        id = Ext.get(me).id;
        
        id = id.replace(/[\.:]/g, "\\$0");
        node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
        return returnDom ? node : Ext.get(node);
    },

     
    parent : function(selector, returnDom) {
        return this.matchNode('parentNode', 'parentNode', selector, returnDom);
    },

     
    next : function(selector, returnDom) {
        return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
    },

    
    prev : function(selector, returnDom) {
        return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
    },


    
    first : function(selector, returnDom) {
        return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
    },

    
    last : function(selector, returnDom) {
        return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
    },

    matchNode : function(dir, start, selector, returnDom) {
        if (!this.dom) {
            return null;
        }
        
        var n = this.dom[start];
        while (n) {
            if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
                return !returnDom ? Ext.get(n) : n;
            }
            n = n[dir];
        }
        return null;
    }
});


Ext.core.Element.addMethods({
    
    appendChild : function(el) {
        return Ext.get(el).appendTo(this);
    },

    
    appendTo : function(el) {
        Ext.getDom(el).appendChild(this.dom);
        return this;
    },

    
    insertBefore : function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el);
        return this;
    },

    
    insertAfter : function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el.nextSibling);
        return this;
    },

    
    insertFirst : function(el, returnDom) {
        el = el || {};
        if (el.nodeType || el.dom || typeof el == 'string') { 
            el = Ext.getDom(el);
            this.dom.insertBefore(el, this.dom.firstChild);
            return !returnDom ? Ext.get(el) : el;
        }
        else { 
            return this.createChild(el, this.dom.firstChild, returnDom);
        }
    },

    
    insertSibling: function(el, where, returnDom){
        var me = this, rt,
        isAfter = (where || 'before').toLowerCase() == 'after',
        insertEl;

        if(Ext.isArray(el)){
            insertEl = me;
            Ext.each(el, function(e) {
                rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
                if(isAfter){
                    insertEl = rt;
                }
            });
            return rt;
        }

        el = el || {};

        if(el.nodeType || el.dom){
            rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
            if (!returnDom) {
                rt = Ext.get(rt);
            }
        }else{
            if (isAfter && !me.dom.nextSibling) {
                rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
            } else {
                rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
            }
        }
        return rt;
    },

    
    replace : function(el) {
        el = Ext.get(el);
        this.insertBefore(el);
        el.remove();
        return this;
    },
    
    
    replaceWith: function(el){
        var me = this;
            
        if(el.nodeType || el.dom || typeof el == 'string'){
            el = Ext.get(el);
            me.dom.parentNode.insertBefore(el, me.dom);
        }else{
            el = Ext.core.DomHelper.insertBefore(me.dom, el);
        }
        
        delete Ext.cache[me.id];
        Ext.removeNode(me.dom);      
        me.id = Ext.id(me.dom = el);
        Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me);     
        return me;
    },
    
    
    createChild : function(config, insertBefore, returnDom) {
        config = config || {tag:'div'};
        if (insertBefore) {
            return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
        }
        else {
            return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config,  returnDom !== true);
        }
    },

    
    wrap : function(config, returnDom) {
        var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
            d = newEl.dom || newEl;

        d.appendChild(this.dom);
        return newEl;
    },

    
    insertHtml : function(where, html, returnEl) {
        var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
        return returnEl ? Ext.get(el) : el;
    }
});


(function(){
    Ext.core.Element.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
    
    var supports = Ext.supports,
        view = document.defaultView,
        opacityRe = /alpha\(opacity=(.*)\)/i,
        trimRe = /^\s+|\s+$/g,
        spacesRe = /\s+/,
        wordsRe = /\w/g,
        adjustDirect2DTableRe = /table-row|table-.*-group/,
        INTERNAL = '_internal',
        PADDING = 'padding',
        MARGIN = 'margin',
        BORDER = 'border',
        LEFT = '-left',
        RIGHT = '-right',
        TOP = '-top',
        BOTTOM = '-bottom',
        WIDTH = '-width',
        MATH = Math,
        HIDDEN = 'hidden',
        ISCLIPPED = 'isClipped',
        OVERFLOW = 'overflow',
        OVERFLOWX = 'overflow-x',
        OVERFLOWY = 'overflow-y',
        ORIGINALCLIP = 'originalClip',
        
        borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
        paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
        margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
        data = Ext.core.Element.data;

    Ext.override(Ext.core.Element, {
        
        
        
        adjustWidth : function(width) {
            var me = this,
                isNum = (typeof width == 'number');
                
            if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
               width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
            }
            return (isNum && width < 0) ? 0 : width;
        },

        
        adjustHeight : function(height) {
            var me = this,
                isNum = (typeof height == "number");
                
            if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
               height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
            }
            return (isNum && height < 0) ? 0 : height;
        },


        
        addCls : function(className){
            var me = this,
                cls = [],
                space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
                i, len, v;
            if (!Ext.isDefined(className)) {
                return me;
            }
            
            if (!Ext.isArray(className)) {
                if (typeof className === 'string') {
                    className = className.replace(trimRe, '').split(spacesRe);
                    if (className.length === 1) {
                        className = className[0];
                        if (!me.hasCls(className)) {
                            me.dom.className += space + className;
                        }
                    } else {
                        this.addCls(className);
                    }
                }
            } else {
                for (i = 0, len = className.length; i < len; i++) {
                    v = className[i];
                    if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
                        cls.push(v);
                    }
                }
                if (cls.length) {
                    me.dom.className += space + cls.join(" ");
                }
            }
            return me;
        },

        
        removeCls : function(className){
            var me = this,
                i, idx, len, cls, elClasses;
            if (!Ext.isDefined(className)) {
                return me;
            }
            if (!Ext.isArray(className)){
                className = className.replace(trimRe, '').split(spacesRe);
            }
            if (me.dom && me.dom.className) {
                elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
                for (i = 0, len = className.length; i < len; i++) {
                    cls = className[i];
                    if (typeof cls == 'string') {
                        cls = cls.replace(trimRe, '');
                        idx = Ext.Array.indexOf(elClasses, cls);
                        if (idx != -1) {
                            elClasses.splice(idx, 1);
                        }
                    }
                }
                me.dom.className = elClasses.join(" ");
            }
            return me;
        },

        
        radioCls : function(className){
            var cn = this.dom.parentNode.childNodes,
                v, i, len;
            className = Ext.isArray(className) ? className : [className];
            for (i = 0, len = cn.length; i < len; i++) {
                v = cn[i];
                if (v && v.nodeType == 1) {
                    Ext.fly(v, '_internal').removeCls(className);
                }
            }
            return this.addCls(className);
        },

        
        toggleCls : Ext.supports.ClassList ?
            function(className) {
                this.dom.classList.toggle(Ext.String.trim(className));
                return this;
            } :
            function(className) {
                return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
            },

        
        hasCls : Ext.supports.ClassList ?
            function(className) {
                if (!className) {
                    return false;
                }
                className = className.split(spacesRe);
                var ln = className.length,
                    i = 0;
                for (; i < ln; i++) {
                    if (className[i] && this.dom.classList.contains(className[i])) {
                        return true;
                    }
                }
                return false;
            } :
            function(className){
                return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
            },

        
        replaceCls : function(oldClassName, newClassName){
            return this.removeCls(oldClassName).addCls(newClassName);
        },

        isStyle : function(style, val) {
            return this.getStyle(style) == val;
        },

        
        getStyle : function(){
            return view && view.getComputedStyle ?
                function(prop){
                    var el = this.dom,
                        v, cs, out, display, cleaner;

                    if(el == document){
                        return null;
                    }
                    prop = Ext.core.Element.normalize(prop);
                    out = (v = el.style[prop]) ? v :
                           (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
                           
                    
                    
                    if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
                        cleaner = Ext.core.Element.getRightMarginFixCleaner(el);
                        display = this.getStyle('display');
                        el.style.display = 'inline-block';
                        out = view.getComputedStyle(el, '').marginRight;
                        el.style.display = display;
                        cleaner();
                    }
                    
                    if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
                        out = 'transparent';
                    }
                    return out;
                } :
                function(prop){
                    var el = this.dom,
                        m, cs;

                    if (el == document) {
                        return null;
                    }
                    
                    if (prop == 'opacity') {
                        if (el.style.filter.match) {
                            m = el.style.filter.match(opacityRe);
                            if(m){
                                var fv = parseFloat(m[1]);
                                if(!isNaN(fv)){
                                    return fv ? fv / 100 : 0;
                                }
                            }
                        }
                        return 1;
                    }
                    prop = Ext.core.Element.normalize(prop);
                    return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
                };
        }(),

        
        getColor : function(attr, defaultValue, prefix){
            var v = this.getStyle(attr),
                color = prefix || prefix === '' ? prefix : '#',
                h;

            if(!v || (/transparent|inherit/.test(v))) {
                return defaultValue;
            }
            if(/^r/.test(v)){
                Ext.each(v.slice(4, v.length -1).split(','), function(s){
                    h = parseInt(s, 10);
                    color += (h < 16 ? '0' : '') + h.toString(16);
                });
            }else{
                v = v.replace('#', '');
                color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
            }
            return(color.length > 5 ? color.toLowerCase() : defaultValue);
        },

        
        setStyle : function(prop, value){
            var me = this,
                tmp, style;

            if (!me.dom) {
                return me;
            }

            if (!Ext.isObject(prop)) {
                tmp = {};
                tmp[prop] = value;
                prop = tmp;
            }
            for (style in prop) {
                if (prop.hasOwnProperty(style)) {
                    value = Ext.value(prop[style], '');
                    if (style == 'opacity') {
                        me.setOpacity(value);
                    }
                    else {
                        me.dom.style[Ext.core.Element.normalize(style)] = value;
                    }
                }
            }
            return me;
        },

        
        setOpacity: function(opacity, animate) {
            var me = this,
                dom = me.dom,
                val,
                style;

            if (!me.dom) {
                return me;
            }

            style = me.dom.style;

            if (!animate || !me.anim) {
                if (!Ext.supports.Opacity) {
                    opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
                    val = style.filter.replace(opacityRe, '').replace(trimRe, '');

                    style.zoom = 1;
                    style.filter = val + (val.length > 0 ? ' ': '') + opacity;
                }
                else {
                    style.opacity = opacity;
                }
            }
            else {
                if (!Ext.isObject(animate)) {
                    animate = {
                        duration: 350,
                        easing: 'ease-in'
                    };
                }
                me.animate(Ext.applyIf({
                    to: {
                        opacity: opacity
                    }
                },
                animate));
            }
            return me;
        },


        
        clearOpacity : function(){
            var style = this.dom.style;
            if(!Ext.supports.Opacity){
                if(!Ext.isEmpty(style.filter)){
                    style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
                }
            }else{
                style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
            }
            return this;
        },
        
        
        adjustDirect2DDimension: function(dimension) {
            var me = this,
                dom = me.dom,
                display = me.getStyle('display'),
                inlineDisplay = dom.style['display'],
                inlinePosition = dom.style['position'],
                originIndex = dimension === 'width' ? 0 : 1,
                floating;
                
            if (display === 'inline') {
                dom.style['display'] = 'inline-block';
            }

            dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';

            
            
            floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
            
            dom.style['position'] = inlinePosition;
            
            if (display === 'inline') {
                dom.style['display'] = inlineDisplay;
            }

            return floating;
        },
        
        
        getHeight: function(contentHeight, preciseHeight) {
            var me = this,
                dom = me.dom,
                hidden = Ext.isIE && me.isStyle('display', 'none'),
                height, overflow, style, floating;

            
            
            if (Ext.isIEQuirks) {
                style = dom.style;
                overflow = style.overflow;
                me.setStyle({ overflow: 'hidden'});
            }

            height = dom.offsetHeight;

            height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;

            
            if (!hidden && Ext.supports.Direct2DBug) {
                floating = me.adjustDirect2DDimension('height');
                if (preciseHeight) {
                    height += floating;
                }
                else if (floating > 0 && floating < 0.5) {
                    height++;
                }
            }

            if (contentHeight) {
                height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
            }

            if (Ext.isIEQuirks) {
                me.setStyle({ overflow: overflow});
            }

            if (height < 0) {
                height = 0;
            }
            return height;
        },
                
        
        getWidth: function(contentWidth, preciseWidth) {
            var me = this,
                dom = me.dom,
                hidden = Ext.isIE && me.isStyle('display', 'none'),
                rect, width, overflow, style, floating, parentPosition;

            
            
            if (Ext.isIEQuirks) {
                style = dom.style;
                overflow = style.overflow;
                me.setStyle({overflow: 'hidden'});
            }
            
            
            if (Ext.isOpera10_5) {
                if (dom.parentNode.currentStyle.position === 'relative') {
                    parentPosition = dom.parentNode.style.position;
                    dom.parentNode.style.position = 'static';
                    width = dom.offsetWidth;
                    dom.parentNode.style.position = parentPosition;
                }
                width = Math.max(width || 0, dom.offsetWidth);
            
            
            
            
            
            
            } else if (Ext.supports.BoundingClientRect) {
                rect = dom.getBoundingClientRect();
                width = rect.right - rect.left;
                width = preciseWidth ? width : Math.ceil(width);
            } else {
                width = dom.offsetWidth;
            }

            width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;

            
            if (!hidden && Ext.supports.Direct2DBug) {
                floating = me.adjustDirect2DDimension('width');
                if (preciseWidth) {
                    width += floating;
                }
                else if (floating > 0 && floating < 0.5) {
                    width++;
                }
            }
            
            if (contentWidth) {
                width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
            }
            
            if (Ext.isIEQuirks) {
                me.setStyle({ overflow: overflow});
            }

            if (width < 0) {
                width = 0;
            }
            return width;
        },

        
        setWidth : function(width, animate){
            var me = this;
            width = me.adjustWidth(width);
            if (!animate || !me.anim) {
                me.dom.style.width = me.addUnits(width);
            }
            else {
                if (!Ext.isObject(animate)) {
                    animate = {};
                }
                me.animate(Ext.applyIf({
                    to: {
                        width: width
                    }
                }, animate));
            }
            return me;
        },

        
         setHeight : function(height, animate){
            var me = this;
            height = me.adjustHeight(height);
            if (!animate || !me.anim) {
                me.dom.style.height = me.addUnits(height);
            }
            else {
                if (!Ext.isObject(animate)) {
                    animate = {};
                }
                me.animate(Ext.applyIf({
                    to: {
                        height: height
                    }
                }, animate));
            }
            return me;
        },

        
        getBorderWidth : function(side){
            return this.addStyles(side, borders);
        },

        
        getPadding : function(side){
            return this.addStyles(side, paddings);
        },

        
        clip : function(){
            var me = this,
                dom = me.dom;

            if(!data(dom, ISCLIPPED)){
                data(dom, ISCLIPPED, true);
                data(dom, ORIGINALCLIP, {
                    o: me.getStyle(OVERFLOW),
                    x: me.getStyle(OVERFLOWX),
                    y: me.getStyle(OVERFLOWY)
                });
                me.setStyle(OVERFLOW, HIDDEN);
                me.setStyle(OVERFLOWX, HIDDEN);
                me.setStyle(OVERFLOWY, HIDDEN);
            }
            return me;
        },

        
        unclip : function(){
            var me = this,
                dom = me.dom,
                clip;

            if(data(dom, ISCLIPPED)){
                data(dom, ISCLIPPED, false);
                clip = data(dom, ORIGINALCLIP);
                if(o.o){
                    me.setStyle(OVERFLOW, o.o);
                }
                if(o.x){
                    me.setStyle(OVERFLOWX, o.x);
                }
                if(o.y){
                    me.setStyle(OVERFLOWY, o.y);
                }
            }
            return me;
        },

        
        addStyles : function(sides, styles){
            var totalSize = 0,
                sidesArr = sides.match(wordsRe),
                i = 0,
                len = sidesArr.length,
                side, size;
            for (; i < len; i++) {
                side = sidesArr[i];
                size = side && parseInt(this.getStyle(styles[side]), 10);
                if (size) {
                    totalSize += MATH.abs(size);
                }
            }
            return totalSize;
        },

        margins : margins,
        
        
        applyStyles : function(style){
            Ext.core.DomHelper.applyStyles(this.dom, style);
            return this;
        },

        
        getStyles : function(){
            var styles = {},
                len = arguments.length,
                i = 0, style;
                
            for(; i < len; ++i) {
                style = arguments[i];
                styles[style] = this.getStyle(style);
            }
            return styles;
        },

       
        boxWrap : function(cls){
            cls = cls || Ext.baseCSSPrefix + 'box';
            var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
            Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
            return el;
        },

        
        setSize : function(width, height, animate){
            var me = this;
            if (Ext.isObject(width)){ 
                height = width.height;
                width = width.width;
            }
            width = me.adjustWidth(width);
            height = me.adjustHeight(height);
            if(!animate || !me.anim){
                me.dom.style.width = me.addUnits(width);
                me.dom.style.height = me.addUnits(height);
            }
            else {
                if (!Ext.isObject(animate)) {
                    animate = {};
                }
                me.animate(Ext.applyIf({
                    to: {
                        width: width,
                        height: height
                    }
                }, animate));
            }
            return me;
        },

        
        getComputedHeight : function(){
            var me = this,
                h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
            if(!h){
                h = parseFloat(me.getStyle('height')) || 0;
                if(!me.isBorderBox()){
                    h += me.getFrameWidth('tb');
                }
            }
            return h;
        },

        
        getComputedWidth : function(){
            var me = this,
                w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
                
            if(!w){
                w = parseFloat(me.getStyle('width')) || 0;
                if(!me.isBorderBox()){
                    w += me.getFrameWidth('lr');
                }
            }
            return w;
        },

        
        getFrameWidth : function(sides, onlyContentBox){
            return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
        },

        
        addClsOnOver : function(className){
            var dom = this.dom;
            this.hover(
                function(){
                    Ext.fly(dom, INTERNAL).addCls(className);
                },
                function(){
                    Ext.fly(dom, INTERNAL).removeCls(className);
                }
            );
            return this;
        },

        
        addClsOnFocus : function(className){
            var me = this,
                dom = me.dom;
            me.on("focus", function(){
                Ext.fly(dom, INTERNAL).addCls(className);
            });
            me.on("blur", function(){
                Ext.fly(dom, INTERNAL).removeCls(className);
            });
            return me;
        },

        
        addClsOnClick : function(className){
            var dom = this.dom;
            this.on("mousedown", function(){
                Ext.fly(dom, INTERNAL).addCls(className);
                var d = Ext.getDoc(),
                    fn = function(){
                        Ext.fly(dom, INTERNAL).removeCls(className);
                        d.removeListener("mouseup", fn);
                    };
                d.on("mouseup", fn);
            });
            return this;
        },

        

        getViewSize : function(){
            var me = this,
                dom = me.dom,
                isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
                style, overflow, ret;

            
            if (isDoc) {
                ret = {
                    width : Ext.core.Element.getViewWidth(),
                    height : Ext.core.Element.getViewHeight()
                };

            
            }
            else {
                
                
                if (Ext.isIE6 || Ext.isIEQuirks) {
                    style = dom.style;
                    overflow = style.overflow;
                    me.setStyle({ overflow: 'hidden'});
                }
                ret = {
                    width : dom.clientWidth,
                    height : dom.clientHeight
                };
                if (Ext.isIE6 || Ext.isIEQuirks) {
                    me.setStyle({ overflow: overflow });
                }
            }
            return ret;
        },

        

        getStyleSize : function(){
            var me = this,
                doc = document,
                d = this.dom,
                isDoc = (d == doc || d == doc.body),
                s = d.style,
                w, h;

            
            if (isDoc) {
                return {
                    width : Ext.core.Element.getViewWidth(),
                    height : Ext.core.Element.getViewHeight()
                };
            }
            
            if(s.width && s.width != 'auto'){
                w = parseFloat(s.width);
                if(me.isBorderBox()){
                   w -= me.getFrameWidth('lr');
                }
            }
            
            if(s.height && s.height != 'auto'){
                h = parseFloat(s.height);
                if(me.isBorderBox()){
                   h -= me.getFrameWidth('tb');
                }
            }
            
            return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
        },

        
        getSize : function(contentSize){
            return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
        },

        
        repaint : function(){
            var dom = this.dom;
            this.addCls(Ext.baseCSSPrefix + 'repaint');
            setTimeout(function(){
                Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
            }, 1);
            return this;
        },

        
        unselectable : function(){
            var me = this;
            me.dom.unselectable = "on";

            me.swallowEvent("selectstart", true);
            me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
            me.addCls(Ext.baseCSSPrefix + 'unselectable');
            
            return me;
        },

        
        getMargin : function(side){
            var me = this,
                hash = {t:"top", l:"left", r:"right", b: "bottom"},
                o = {},
                key;

            if (!side) {
                for (key in me.margins){
                    o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
                }
                return o;
            } else {
                return me.addStyles.call(me, side, me.margins);
            }
        }
    });
})();


Ext.core.Element.VISIBILITY = 1;

Ext.core.Element.DISPLAY = 2;


Ext.core.Element.OFFSETS = 3;


Ext.core.Element.ASCLASS = 4;


Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';

Ext.core.Element.addMethods(function(){
    var El = Ext.core.Element,
        OPACITY = "opacity",
        VISIBILITY = "visibility",
        DISPLAY = "display",
        HIDDEN = "hidden",
        OFFSETS = "offsets",
        ASCLASS = "asclass",
        NONE = "none",
        NOSIZE = 'nosize',
        ORIGINALDISPLAY = 'originalDisplay',
        VISMODE = 'visibilityMode',
        ISVISIBLE = 'isVisible',
        data = El.data,
        getDisplay = function(dom){
            var d = data(dom, ORIGINALDISPLAY);
            if(d === undefined){
                data(dom, ORIGINALDISPLAY, d = '');
            }
            return d;
        },
        getVisMode = function(dom){
            var m = data(dom, VISMODE);
            if(m === undefined){
                data(dom, VISMODE, m = 1);
            }
            return m;
        };

    return {
        
        originalDisplay : "",
        visibilityMode : 1,

        
        setVisibilityMode : function(visMode){
            data(this.dom, VISMODE, visMode);
            return this;
        },

        
        isVisible : function() {
            var me = this,
                dom = me.dom,
                visible = data(dom, ISVISIBLE);

            if(typeof visible == 'boolean'){ 
                return visible;
            }
            
            visible = !me.isStyle(VISIBILITY, HIDDEN) &&
                      !me.isStyle(DISPLAY, NONE) &&
                      !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));

            data(dom, ISVISIBLE, visible);
            return visible;
        },

        
        setVisible : function(visible, animate){
            var me = this, isDisplay, isVisibility, isOffsets, isNosize,
                dom = me.dom,
                visMode = getVisMode(dom);


            
            if (typeof animate == 'string'){
                switch (animate) {
                    case DISPLAY:
                        visMode = El.DISPLAY;
                        break;
                    case VISIBILITY:
                        visMode = El.VISIBILITY;
                        break;
                    case OFFSETS:
                        visMode = El.OFFSETS;
                        break;
                    case NOSIZE:
                    case ASCLASS:
                        visMode = El.ASCLASS;
                        break;
                }
                me.setVisibilityMode(visMode);
                animate = false;
            }

            if (!animate || !me.anim) {
                if(visMode == El.ASCLASS ){

                    me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);

                } else if (visMode == El.DISPLAY){

                    return me.setDisplayed(visible);

                } else if (visMode == El.OFFSETS){

                    if (!visible){
                        
                        if (!me.hideModeStyles) {
                            me.hideModeStyles = {
                                position: me.getStyle('position'),
                                top: me.getStyle('top'),
                                left: me.getStyle('left')
                            };
                        }
                        me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
                    }

                    
                    
                    else if (me.hideModeStyles) {
                        me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
                        delete me.hideModeStyles;
                    }

                }else{
                    me.fixDisplay();
                    
                    dom.style.visibility = visible ? '' : HIDDEN;
                }
            }else{
                
                if(visible){
                    me.setOpacity(0.01);
                    me.setVisible(true);
                }
                if (!Ext.isObject(animate)) {
                    animate = {
                        duration: 350,
                        easing: 'ease-in'
                    };
                }
                me.animate(Ext.applyIf({
                    callback: function() {
                        visible || me.setVisible(false).setOpacity(1);
                    },
                    to: {
                        opacity: (visible) ? 1 : 0
                    }
                }, animate));
            }
            data(dom, ISVISIBLE, visible);  
            return me;
        },


        
        hasMetrics  : function(){
            var dom = this.dom;
            return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
        },

        
        toggle : function(animate){
            var me = this;
            me.setVisible(!me.isVisible(), me.anim(animate));
            return me;
        },

        
        setDisplayed : function(value) {
            if(typeof value == "boolean"){
               value = value ? getDisplay(this.dom) : NONE;
            }
            this.setStyle(DISPLAY, value);
            return this;
        },

        
        fixDisplay : function(){
            var me = this;
            if (me.isStyle(DISPLAY, NONE)) {
                me.setStyle(VISIBILITY, HIDDEN);
                me.setStyle(DISPLAY, getDisplay(this.dom)); 
                if (me.isStyle(DISPLAY, NONE)) { 
                    me.setStyle(DISPLAY, "block");
                }
            }
        },

        
        hide : function(animate){
            
            if (typeof animate == 'string'){
                this.setVisible(false, animate);
                return this;
            }
            this.setVisible(false, this.anim(animate));
            return this;
        },

        
        show : function(animate){
            
            if (typeof animate == 'string'){
                this.setVisible(true, animate);
                return this;
            }
            this.setVisible(true, this.anim(animate));
            return this;
        }
    };
}());

Ext.applyIf(Ext.core.Element.prototype, {
    
    animate: function(config) {
        var me = this;
        if (!me.id) {
            me = Ext.get(me.dom);
        }
        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }
        Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
        return this;
    },

    
    anim: function(config) {
        if (!Ext.isObject(config)) {
            return (config) ? {} : false;
        }

        var me = this,
            duration = config.duration || Ext.fx.Anim.prototype.duration,
            easing = config.easing || 'ease',
            animConfig;

        if (config.stopAnimation) {
            me.stopAnimation();
        }

        Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));

        
        Ext.fx.Manager.setFxDefaults(me.id, {
            delay: 0
        });

        animConfig = {
            target: me,
            remove: config.remove,
            alternate: config.alternate || false,
            duration: duration,
            easing: easing,
            callback: config.callback,
            listeners: config.listeners,
            iterations: config.iterations || 1,
            scope: config.scope,
            block: config.block,
            concurrent: config.concurrent,
            delay: config.delay || 0,
            paused: true,
            keyframes: config.keyframes,
            from: config.from || {},
            to: Ext.apply({}, config)
        };
        Ext.apply(animConfig.to, config.to);

        
        delete animConfig.to.to;
        delete animConfig.to.from;
        delete animConfig.to.remove;
        delete animConfig.to.alternate;
        delete animConfig.to.keyframes;
        delete animConfig.to.iterations;
        delete animConfig.to.listeners;
        delete animConfig.to.target;
        delete animConfig.to.paused;
        delete animConfig.to.callback;
        delete animConfig.to.scope;
        delete animConfig.to.duration;
        delete animConfig.to.easing;
        delete animConfig.to.concurrent;
        delete animConfig.to.block;
        delete animConfig.to.stopAnimation;
        delete animConfig.to.delay;
        return animConfig;
    },

    
    slideIn: function(anchor, obj, slideOut) { 
        var me = this,
            elStyle = me.dom.style,
            beforeAnim, wrapAnim;

        anchor = anchor || "t";
        obj = obj || {};

        beforeAnim = function() {
            var animScope = this,
                listeners = obj.listeners,
                box, position, restoreSize, wrap, anim;

            if (!slideOut) {
                me.fixDisplay();
            }

            box = me.getBox();
            if ((anchor == 't' || anchor == 'b') && box.height == 0) {
                box.height = me.dom.scrollHeight;
            }
            else if ((anchor == 'l' || anchor == 'r') && box.width == 0) {
                box.width = me.dom.scrollWidth;
            }
            
            position = me.getPositioning();
            me.setSize(box.width, box.height);

            wrap = me.wrap({
                style: {
                    visibility: slideOut ? 'visible' : 'hidden'
                }
            });
            wrap.setPositioning(position);
            if (wrap.isStyle('position', 'static')) {
                wrap.position('relative');
            }
            me.clearPositioning('auto');
            wrap.clip();

            
            
            
            me.setStyle({
                visibility: '',
                position: 'absolute'
            });
            if (slideOut) {
                wrap.setSize(box.width, box.height);
            }

            switch (anchor) {
                case 't':
                    anim = {
                        from: {
                            width: box.width + 'px',
                            height: '0px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    break;
                case 'l':
                    anim = {
                        from: {
                            width: '0px',
                            height: box.height + 'px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.right = '0px';
                    break;
                case 'r':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            width: '0px',
                            height: box.height + 'px'
                        },
                        to: {
                            x: box.x,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    break;
                case 'b':
                    anim = {
                        from: {
                            y: box.y + box.height,
                            width: box.width + 'px',
                            height: '0px'
                        },
                        to: {
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    break;
                case 'tl':
                    anim = {
                        from: {
                            x: box.x,
                            y: box.y,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    elStyle.right = '0px';
                    break;
                case 'bl':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            x: box.x,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.right = '0px';
                    break;
                case 'br':
                    anim = {
                        from: {
                            x: box.x + box.width,
                            y: box.y + box.height,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            x: box.x,
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    break;
                case 'tr':
                    anim = {
                        from: {
                            y: box.y + box.height,
                            width: '0px',
                            height: '0px'
                        },
                        to: {
                            y: box.y,
                            width: box.width + 'px',
                            height: box.height + 'px'
                        }
                    };
                    elStyle.bottom = '0px';
                    break;
            }

            wrap.show();
            wrapAnim = Ext.apply({}, obj);
            delete wrapAnim.listeners;
            wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
                target: wrap,
                duration: 500,
                easing: 'ease-out',
                from: slideOut ? anim.to : anim.from,
                to: slideOut ? anim.from : anim.to
            }));

            
            wrapAnim.on('afteranimate', function() {
                if (slideOut) {
                    me.setPositioning(position);
                    if (obj.useDisplay) {
                        me.setDisplayed(false);
                    } else {
                        me.hide();   
                    }
                }
                else {
                    me.clearPositioning();
                    me.setPositioning(position);
                }
                if (wrap.dom) {
                    wrap.dom.parentNode.insertBefore(me.dom, wrap.dom); 
                    wrap.remove();
                }
                me.setSize(box.width, box.height);
                animScope.end();
            });
            
            if (listeners) {
                wrapAnim.on(listeners);
            }
        };

        me.animate({
            duration: obj.duration ? obj.duration * 2 : 1000,
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                },
                afteranimate: {
                    fn: function() {
                        if (wrapAnim && wrapAnim.running) {
                            wrapAnim.end();
                        }
                    }
                }
            }
        });
        return me;
    },

    
    
    slideOut: function(anchor, o) {
        return this.slideIn(anchor, o, true);
    },

    

    puff: function(obj) {
        var me = this,
            beforeAnim;
        obj = Ext.applyIf(obj || {}, {
            easing: 'ease-out',
            duration: 500,
            useDisplay: false
        });

        beforeAnim = function() {
            me.clearOpacity();
            me.show();

            var box = me.getBox(),
                fontSize = me.getStyle('fontSize'),
                position = me.getPositioning();
            this.to = {
                width: box.width * 2,
                height: box.height * 2,
                x: box.x - (box.width / 2),
                y: box.y - (box.height /2),
                opacity: 0,
                fontSize: '200%'
            };
            this.on('afteranimate',function() {
                if (me.dom) {
                    if (obj.useDisplay) {
                        me.setDisplayed(false);
                    } else {
                        me.hide();
                    }
                    me.clearOpacity();  
                    me.setPositioning(position);
                    me.setStyle({fontSize: fontSize});
                }
            });
        };

        me.animate({
            duration: obj.duration,
            easing: obj.easing,
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            }
        });
        return me;
    },

    
    switchOff: function(obj) {
        var me = this,
            beforeAnim;
        
        obj = Ext.applyIf(obj || {}, {
            easing: 'ease-in',
            duration: 500,
            remove: false,
            useDisplay: false
        });

        beforeAnim = function() {
            var animScope = this,
                size = me.getSize(),
                xy = me.getXY(),
                keyframe, position;
            me.clearOpacity();
            me.clip();
            position = me.getPositioning();

            keyframe = Ext.create('Ext.fx.Animator', {
                target: me,
                duration: obj.duration,
                easing: obj.easing,
                keyframes: {
                    33: {
                        opacity: 0.3
                    },
                    66: {
                        height: 1,
                        y: xy[1] + size.height / 2
                    },
                    100: {
                        width: 1,
                        x: xy[0] + size.width / 2
                    }
                }
            });
            keyframe.on('afteranimate', function() {
                if (obj.useDisplay) {
                    me.setDisplayed(false);
                } else {
                    me.hide();
                }  
                me.clearOpacity();
                me.setPositioning(position);
                me.setSize(size);
                animScope.end();
            });
        };
        me.animate({
            duration: (obj.duration * 2),
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            }
        });
        return me;
    },

   
    frame : function(color, count, obj){
        var me = this,
            beforeAnim;

        color = color || '#C3DAF9';
        count = count || 1;
        obj = obj || {};

        beforeAnim = function() {
            me.show();
            var animScope = this,
                box = me.getBox(),
                proxy = Ext.getBody().createChild({
                    style: {
                        position : 'absolute',
                        'pointer-events': 'none',
                        'z-index': 35000,
                        border : '0px solid ' + color
                    }
                }),
                proxyAnim;
            proxyAnim = Ext.create('Ext.fx.Anim', {
                target: proxy,
                duration: obj.duration || 1000,
                iterations: count,
                from: {
                    top: box.y,
                    left: box.x,
                    borderWidth: 0,
                    opacity: 1,
                    height: box.height,
                    width: box.width
                },
                to: {
                    top: box.y - 20,
                    left: box.x - 20,
                    borderWidth: 10,
                    opacity: 0,
                    height: box.height + 40,
                    width: box.width + 40
                }
            });
            proxyAnim.on('afteranimate', function() {
                proxy.remove();
                animScope.end();
            });
        };

        me.animate({
            duration: (obj.duration * 2) || 2000,
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            }
        });
        return me;
    },

    
    ghost: function(anchor, obj) {
        var me = this,
            beforeAnim;

        anchor = anchor || "b";
        beforeAnim = function() {
            var width = me.getWidth(),
                height = me.getHeight(),
                xy = me.getXY(),
                position = me.getPositioning(),
                to = {
                    opacity: 0
                };
            switch (anchor) {
                case 't':
                    to.y = xy[1] - height;
                    break;
                case 'l':
                    to.x = xy[0] - width;
                    break;
                case 'r':
                    to.x = xy[0] + width;
                    break;
                case 'b':
                    to.y = xy[1] + height;
                    break;
                case 'tl':
                    to.x = xy[0] - width;
                    to.y = xy[1] - height;
                    break;
                case 'bl':
                    to.x = xy[0] - width;
                    to.y = xy[1] + height;
                    break;
                case 'br':
                    to.x = xy[0] + width;
                    to.y = xy[1] + height;
                    break;
                case 'tr':
                    to.x = xy[0] + width;
                    to.y = xy[1] - height;
                    break;
            }
            this.to = to;
            this.on('afteranimate', function () {
                if (me.dom) {
                    me.hide();
                    me.clearOpacity();
                    me.setPositioning(position);
                }
            });
        };

        me.animate(Ext.applyIf(obj || {}, {
            duration: 500,
            easing: 'ease-out',
            listeners: {
                beforeanimate: {
                    fn: beforeAnim
                }
            }
        }));
        return me;
    },

     
    highlight: function(color, o) {
        var me = this,
            dom = me.dom,
            from = {},
            restore, to, attr, lns, event, fn;

        o = o || {};
        lns = o.listeners || {};
        attr = o.attr || 'backgroundColor';
        from[attr] = color || 'ffff9c';
        
        if (!o.to) {
            to = {};
            to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
        }
        else {
            to = o.to;
        }
        
        
        o.listeners = Ext.apply(Ext.apply({}, lns), {
            beforeanimate: function() {
                restore = dom.style[attr];
                me.clearOpacity();
                me.show();
                
                event = lns.beforeanimate;
                if (event) {
                    fn = event.fn || event;
                    return fn.apply(event.scope || lns.scope || window, arguments);
                }
            },
            afteranimate: function() {
                if (dom) {
                    dom.style[attr] = restore;
                }
                
                event = lns.afteranimate;
                if (event) {
                    fn = event.fn || event;
                    fn.apply(event.scope || lns.scope || window, arguments);
                }
            }
        });

        me.animate(Ext.apply({}, o, {
            duration: 1000,
            easing: 'ease-in',
            from: from,
            to: to
        }));
        return me;
    },

   
    pause: function(ms) {
        var me = this;
        Ext.fx.Manager.setFxDefaults(me.id, {
            delay: ms
        });
        return me;
    },

   
    fadeIn: function(o) {
        this.animate(Ext.apply({}, o, {
            opacity: 1
        }));
        return this;
    },

   
    fadeOut: function(o) {
        this.animate(Ext.apply({}, o, {
            opacity: 0
        }));
        return this;
    },

   
    scale: function(w, h, o) {
        this.animate(Ext.apply({}, o, {
            width: w,
            height: h
        }));
        return this;
    },

   
    shift: function(config) {
        this.animate(config);
        return this;
    }
});


Ext.applyIf(Ext.core.Element, {
    unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
    camelRe: /(-[a-z])/gi,
    opacityRe: /alpha\(opacity=(.*)\)/i,
    cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
    propertyCache: {},
    defaultUnit : "px",
    borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
    paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
    margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},

    
    addUnits : Ext.core.Element.prototype.addUnits,

    
    parseBox : function(box) {
        if (Ext.isObject(box)) {
            return {
                top: box.top || 0,
                right: box.right || 0,
                bottom: box.bottom || 0,
                left: box.left || 0
            };
        } else {
            if (typeof box != 'string') {
                box = box.toString();
            }
            var parts  = box.split(' '),
                ln = parts.length;
    
            if (ln == 1) {
                parts[1] = parts[2] = parts[3] = parts[0];
            }
            else if (ln == 2) {
                parts[2] = parts[0];
                parts[3] = parts[1];
            }
            else if (ln == 3) {
                parts[3] = parts[1];
            }
    
            return {
                top   :parseFloat(parts[0]) || 0,
                right :parseFloat(parts[1]) || 0,
                bottom:parseFloat(parts[2]) || 0,
                left  :parseFloat(parts[3]) || 0
            };
        }
        
    },
    
    
    unitizeBox : function(box, units) {
        var A = this.addUnits,
            B = this.parseBox(box);
            
        return A(B.top, units) + ' ' +
               A(B.right, units) + ' ' +
               A(B.bottom, units) + ' ' +
               A(B.left, units);
        
    },

    
    camelReplaceFn : function(m, a) {
        return a.charAt(1).toUpperCase();
    },

    
    normalize : function(prop) {
        if (prop == 'float') {
            prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
        }
        return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
    },

    
    getDocumentHeight: function() {
        return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
    },

    
    getDocumentWidth: function() {
        return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
    },

    
    getViewportHeight: function(){
        return window.innerHeight;
    },

    
    getViewportWidth : function() {
        return window.innerWidth;
    },

    
    getViewSize : function() {
        return {
            width: window.innerWidth,
            height: window.innerHeight
        };
    },

    
    getOrientation : function() {
        if (Ext.supports.OrientationChange) {
            return (window.orientation == 0) ? 'portrait' : 'landscape';
        }
        
        return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
    },

    
    fromPoint: function(x, y) {
        return Ext.get(document.elementFromPoint(x, y));
    },
    
    
    parseStyles: function(styles){
        var out = {},
            cssRe = this.cssRe,
            matches;
            
        if (styles) {
            
            
            
            
            cssRe.lastIndex = 0;
            while ((matches = cssRe.exec(styles))) {
                out[matches[1]] = matches[2];
            }
        }
        return out;
    }
});


Ext.CompositeElementLite = function(els, root){
    
    this.elements = [];
    this.add(els, root);
    this.el = new Ext.core.Element.Flyweight();
};

Ext.CompositeElementLite.prototype = {
    isComposite: true,

    
    getElement : function(el){
        
        var e = this.el;
        e.dom = el;
        e.id = el.id;
        return e;
    },

    
    transformElement : function(el){
        return Ext.getDom(el);
    },

    
    getCount : function(){
        return this.elements.length;
    },
    
    add : function(els, root){
        var me = this,
            elements = me.elements;
        if(!els){
            return this;
        }
        if(typeof els == "string"){
            els = Ext.core.Element.selectorFunction(els, root);
        }else if(els.isComposite){
            els = els.elements;
        }else if(!Ext.isIterable(els)){
            els = [els];
        }

        for(var i = 0, len = els.length; i < len; ++i){
            elements.push(me.transformElement(els[i]));
        }
        return me;
    },

    invoke : function(fn, args){
        var me = this,
            els = me.elements,
            len = els.length,
            e,
            i;

        for(i = 0; i < len; i++) {
            e = els[i];
            if(e){
                Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
            }
        }
        return me;
    },
    
    item : function(index){
        var me = this,
            el = me.elements[index],
            out = null;

        if(el){
            out = me.getElement(el);
        }
        return out;
    },

    
    addListener : function(eventName, handler, scope, opt){
        var els = this.elements,
            len = els.length,
            i, e;

        for(i = 0; i<len; i++) {
            e = els[i];
            if(e) {
                Ext.EventManager.on(e, eventName, handler, scope || e, opt);
            }
        }
        return this;
    },
    
    each : function(fn, scope){
        var me = this,
            els = me.elements,
            len = els.length,
            i, e;

        for(i = 0; i<len; i++) {
            e = els[i];
            if(e){
                e = this.getElement(e);
                if(fn.call(scope || e, e, me, i) === false){
                    break;
                }
            }
        }
        return me;
    },

    
    fill : function(els){
        var me = this;
        me.elements = [];
        me.add(els);
        return me;
    },

    
    filter : function(selector){
        var els = [],
            me = this,
            fn = Ext.isFunction(selector) ? selector
                : function(el){
                    return el.is(selector);
                };

        me.each(function(el, self, i) {
            if (fn(el, i) !== false) {
                els[els.length] = me.transformElement(el);
            }
        });
        
        me.elements = els;
        return me;
    },

    
    indexOf : function(el){
        return Ext.Array.indexOf(this.elements, this.transformElement(el));
    },

    
    replaceElement : function(el, replacement, domReplace){
        var index = !isNaN(el) ? el : this.indexOf(el),
            d;
        if(index > -1){
            replacement = Ext.getDom(replacement);
            if(domReplace){
                d = this.elements[index];
                d.parentNode.insertBefore(replacement, d);
                Ext.removeNode(d);
            }
            this.elements.splice(index, 1, replacement);
        }
        return this;
    },

    
    clear : function(){
        this.elements = [];
    }
};

Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;


Ext.CompositeElementLite.importElementMethods = function() {
    var fnName,
        ElProto = Ext.core.Element.prototype,
        CelProto = Ext.CompositeElementLite.prototype;

    for (fnName in ElProto) {
        if (typeof ElProto[fnName] == 'function'){
            (function(fnName) {
                CelProto[fnName] = CelProto[fnName] || function() {
                    return this.invoke(fnName, arguments);
                };
            }).call(CelProto, fnName);

        }
    }
};

Ext.CompositeElementLite.importElementMethods();

if(Ext.DomQuery){
    Ext.core.Element.selectorFunction = Ext.DomQuery.select;
}


Ext.core.Element.select = function(selector, root){
    var els;
    if(typeof selector == "string"){
        els = Ext.core.Element.selectorFunction(selector, root);
    }else if(selector.length !== undefined){
        els = selector;
    }else{
        Ext.Error.raise({
            sourceClass: "Ext.core.Element",
            sourceMethod: "select",
            selector: selector,
            root: root,
            msg: "Invalid selector specified: " + selector
        });
    }
    return new Ext.CompositeElementLite(els);
};

Ext.select = Ext.core.Element.select;


Ext.util.DelayedTask = function(fn, scope, args) {
    var me = this,
        id,
        call = function() {
            clearInterval(id);
            id = null;
            fn.apply(scope, args || []);
        };

    
    this.delay = function(delay, newFn, newScope, newArgs) {
        me.cancel();
        fn = newFn || fn;
        scope = newScope || scope;
        args = newArgs || args;
        id = setInterval(call, delay);
    };

    
    this.cancel = function(){
        if (id) {
            clearInterval(id);
            id = null;
        }
    };
};
Ext.require('Ext.util.DelayedTask', function() {

    Ext.util.Event = Ext.extend(Object, (function() {
        function createBuffered(handler, listener, o, scope) {
            listener.task = new Ext.util.DelayedTask();
            return function() {
                listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
            };
        }

        function createDelayed(handler, listener, o, scope) {
            return function() {
                var task = new Ext.util.DelayedTask();
                if (!listener.tasks) {
                    listener.tasks = [];
                }
                listener.tasks.push(task);
                task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
            };
        }

        function createSingle(handler, listener, o, scope) {
            return function() {
                listener.ev.removeListener(listener.fn, scope);
                return handler.apply(scope, arguments);
            };
        }

        return {
            isEvent: true,

            constructor: function(observable, name) {
                this.name = name;
                this.observable = observable;
                this.listeners = [];
            },

            addListener: function(fn, scope, options) {
                var me = this,
                    listener;
                    scope = scope || me.observable;

                if (!fn) {
                    Ext.Error.raise({
                        sourceClass: Ext.getClassName(this.observable),
                        sourceMethod: "addListener",
                        msg: "The specified callback function is undefined"
                    });
                }

                if (!me.isListening(fn, scope)) {
                    listener = me.createListener(fn, scope, options);
                    if (me.firing) {
                        
                        me.listeners = me.listeners.slice(0);
                    }
                    me.listeners.push(listener);
                }
            },

            createListener: function(fn, scope, o) {
                o = o || {};
                scope = scope || this.observable;

                var listener = {
                        fn: fn,
                        scope: scope,
                        o: o,
                        ev: this
                    },
                    handler = fn;

                
                
                if (o.single) {
                    handler = createSingle(handler, listener, o, scope);
                }
                if (o.delay) {
                    handler = createDelayed(handler, listener, o, scope);
                }
                if (o.buffer) {
                    handler = createBuffered(handler, listener, o, scope);
                }

                listener.fireFn = handler;
                return listener;
            },

            findListener: function(fn, scope) {
                var listeners = this.listeners,
                i = listeners.length,
                listener,
                s;

                while (i--) {
                    listener = listeners[i];
                    if (listener) {
                        s = listener.scope;
                        if (listener.fn == fn && (s == scope || s == this.observable)) {
                            return i;
                        }
                    }
                }

                return - 1;
            },

            isListening: function(fn, scope) {
                return this.findListener(fn, scope) !== -1;
            },

            removeListener: function(fn, scope) {
                var me = this,
                    index,
                    listener,
                    k;
                index = me.findListener(fn, scope);
                if (index != -1) {
                    listener = me.listeners[index];

                    if (me.firing) {
                        me.listeners = me.listeners.slice(0);
                    }

                    
                    if (listener.task) {
                        listener.task.cancel();
                        delete listener.task;
                    }

                    
                    k = listener.tasks && listener.tasks.length;
                    if (k) {
                        while (k--) {
                            listener.tasks[k].cancel();
                        }
                        delete listener.tasks;
                    }

                    
                    me.listeners.splice(index, 1);
                    return true;
                }

                return false;
            },

            
            clearListeners: function() {
                var listeners = this.listeners,
                    i = listeners.length;

                while (i--) {
                    this.removeListener(listeners[i].fn, listeners[i].scope);
                }
            },

            fire: function() {
                var me = this,
                    listeners = me.listeners,
                    count = listeners.length,
                    i,
                    args,
                    listener;

                if (count > 0) {
                    me.firing = true;
                    for (i = 0; i < count; i++) {
                        listener = listeners[i];
                        args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
                        if (listener.o) {
                            args.push(listener.o);
                        }
                        if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
                            return (me.firing = false);
                        }
                    }
                }
                me.firing = false;
                return true;
            }
        };
    })());
});


Ext.EventManager = {

    

    
    hasBoundOnReady: false,

    
    hasFiredReady: false,

    
    readyTimeout: null,

    
    hasOnReadyStateChange: false,

    
    readyEvent: new Ext.util.Event(),

    
    checkReadyState: function(){
        var me = Ext.EventManager;

        if(window.attachEvent){
            
            if (window != top) {
                return false;
            }
            try{
                document.documentElement.doScroll('left');
            }catch(e){
                return false;
            }
            me.fireDocReady();
            return true;
        }
        if (document.readyState == 'complete') {
            me.fireDocReady();
            return true;
        }
        me.readyTimeout = setTimeout(arguments.callee, 2);
        return false;
    },

    
    bindReadyEvent: function(){
        var me = Ext.EventManager;
        if (me.hasBoundOnReady) {
            return;
        }

        if (document.addEventListener) {
            document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
            
            window.addEventListener('load', me.fireDocReady, false);
        } else {
            
            if (!me.checkReadyState()) {
                document.attachEvent('onreadystatechange', me.checkReadyState);
                me.hasOnReadyStateChange = true;
            }
            
            window.attachEvent('onload', me.fireDocReady, false);
        }
        me.hasBoundOnReady = true;
    },

    
    fireDocReady: function(){
        var me = Ext.EventManager;

        
        if (!me.hasFiredReady) {
            me.hasFiredReady = true;

            if (document.addEventListener) {
                document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
                window.removeEventListener('load', me.fireDocReady, false);
            } else {
                if (me.readyTimeout !== null) {
                    clearTimeout(me.readyTimeout);
                }
                if (me.hasOnReadyStateChange) {
                    document.detachEvent('onreadystatechange', me.checkReadyState);
                }
                window.detachEvent('onload', me.fireDocReady);
            }
            Ext.supports.init();
        }
        if (!Ext.isReady) {
            Ext.isReady = true;
            me.onWindowUnload();
            me.readyEvent.fire();
        }
    },

    
    onDocumentReady: function(fn, scope, options){
        options = options || {};
        var me = Ext.EventManager,
            readyEvent = me.readyEvent;

        
        options.single = true;

        
        if (Ext.isReady) {
            readyEvent.addListener(fn, scope, options);
            readyEvent.fire();
        } else {
            options.delay = options.delay || 1;
            readyEvent.addListener(fn, scope, options);
            me.bindReadyEvent();
        }
    },


    

    
    stoppedMouseDownEvent: new Ext.util.Event(),

    
    propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,

    
    getId : function(element) {
        var skipGarbageCollection = false,
            id;
    
        element = Ext.getDom(element);
    
        if (element === document || element === window) {
            id = element === document ? Ext.documentId : Ext.windowId;
        }
        else {
            id = Ext.id(element);
        }
        
        if (element && (element.getElementById || element.navigator)) {
            skipGarbageCollection = true;
        }
    
        if (!Ext.cache[id]){
            Ext.core.Element.addToCache(new Ext.core.Element(element), id);
            if (skipGarbageCollection) {
                Ext.cache[id].skipGarbageCollection = true;
            }
        }
        return id;
    },

    
    prepareListenerConfig: function(element, config, isRemove){
        var me = this,
            propRe = me.propRe,
            key, value, args;

        
        for (key in config) {
            if (config.hasOwnProperty(key)) {
                
                if (!propRe.test(key)) {
                    value = config[key];
                    
                    
                    if (Ext.isFunction(value)) {
                        
                        args = [element, key, value, config.scope, config];
                    } else {
                        
                        args = [element, key, value.fn, value.scope, value];
                    }

                    if (isRemove === true) {
                        me.removeListener.apply(this, args);
                    } else {
                        me.addListener.apply(me, args);
                    }
                }
            }
        }
    },

    
    normalizeEvent: function(eventName, fn){
        if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
            if (fn) {
                fn = Ext.Function.createInterceptor(fn, this.contains, this);
            }
            eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
        } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
            eventName = 'DOMMouseScroll';
        }
        return {
            eventName: eventName,
            fn: fn
        };
    },

    
    contains: function(event){
        var parent = event.browserEvent.currentTarget,
            child = this.getRelatedTarget(event);

        if (parent && parent.firstChild) {
            while (child) {
                if (child === parent) {
                    return false;
                }
                child = child.parentNode;
                if (child && (child.nodeType != 1)) {
                    child = null;
                }
            }
        }
        return true;
    },

    
    addListener: function(element, eventName, fn, scope, options){
        
        if (Ext.isObject(eventName)) {
            this.prepareListenerConfig(element, eventName);
            return;
        }

        var dom = Ext.getDom(element),
            bind,
            wrap;

        if (!dom){
            Ext.Error.raise({
                sourceClass: 'Ext.EventManager',
                sourceMethod: 'addListener',
                targetElement: element,
                eventName: eventName,
                msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
            });
        }
        if (!fn) {
            Ext.Error.raise({
                sourceClass: 'Ext.EventManager',
                sourceMethod: 'addListener',
                targetElement: element,
                eventName: eventName,
                msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
            });
        }

        
        options = options || {};

        bind = this.normalizeEvent(eventName, fn);
        wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);


        if (dom.attachEvent) {
            dom.attachEvent('on' + bind.eventName, wrap);
        } else {
            dom.addEventListener(bind.eventName, wrap, options.capture || false);
        }

        if (dom == document && eventName == 'mousedown') {
            this.stoppedMouseDownEvent.addListener(wrap);
        }

        
        this.getEventListenerCache(dom, eventName).push({
            fn: fn,
            wrap: wrap,
            scope: scope
        });
    },

    
    removeListener : function(element, eventName, fn, scope) {
        
        if (Ext.isObject(eventName)) {
            this.prepareListenerConfig(element, eventName, true);
            return;
        }

        var dom = Ext.getDom(element),
            cache = this.getEventListenerCache(dom, eventName),
            bindName = this.normalizeEvent(eventName).eventName,
            i = cache.length, j,
            listener, wrap, tasks;


        while (i--) {
            listener = cache[i];

            if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
                wrap = listener.wrap;

                
                if (wrap.task) {
                    clearTimeout(wrap.task);
                    delete wrap.task;
                }

                
                j = wrap.tasks && wrap.tasks.length;
                if (j) {
                    while (j--) {
                        clearTimeout(wrap.tasks[j]);
                    }
                    delete wrap.tasks;
                }

                if (dom.detachEvent) {
                    dom.detachEvent('on' + bindName, wrap);
                } else {
                    dom.removeEventListener(bindName, wrap, false);
                }

                if (wrap && dom == document && eventName == 'mousedown') {
                    this.stoppedMouseDownEvent.removeListener(wrap);
                }

                
                cache.splice(i, 1);
            }
        }
    },

    
    removeAll : function(element){
        var dom = Ext.getDom(element),
            cache, ev;
        if (!dom) {
            return;
        }
        cache = this.getElementEventCache(dom);

        for (ev in cache) {
            if (cache.hasOwnProperty(ev)) {
                this.removeListener(dom, ev);
            }
        }
        Ext.cache[dom.id].events = {};
    },

    
    purgeElement : function(element, eventName) {
        var dom = Ext.getDom(element),
            i = 0, len;

        if(eventName) {
            this.removeListener(dom, eventName);
        }
        else {
            this.removeAll(dom);
        }

        if(dom && dom.childNodes) {
            for(len = element.childNodes.length; i < len; i++) {
                this.purgeElement(element.childNodes[i], eventName);
            }
        }
    },

    
    createListenerWrap : function(dom, ename, fn, scope, options) {
        options = !Ext.isObject(options) ? {} : options;

        var f, gen;

        return function wrap(e, args) {
            
            if (!gen) {
                f = ['if(!Ext) {return;}'];

                if(options.buffer || options.delay || options.freezeEvent) {
                    f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
                } else {
                    f.push('e = Ext.EventObject.setEvent(e);');
                }

                if (options.delegate) {
                    f.push('var t = e.getTarget("' + options.delegate + '", this);');
                    f.push('if(!t) {return;}');
                } else {
                    f.push('var t = e.target;');
                }

                if (options.target) {
                    f.push('if(e.target !== options.target) {return;}');
                }

                if(options.stopEvent) {
                    f.push('e.stopEvent();');
                } else {
                    if(options.preventDefault) {
                        f.push('e.preventDefault();');
                    }
                    if(options.stopPropagation) {
                        f.push('e.stopPropagation();');
                    }
                }

                if(options.normalized === false) {
                    f.push('e = e.browserEvent;');
                }

                if(options.buffer) {
                    f.push('(wrap.task && clearTimeout(wrap.task));');
                    f.push('wrap.task = setTimeout(function(){');
                }

                if(options.delay) {
                    f.push('wrap.tasks = wrap.tasks || [];');
                    f.push('wrap.tasks.push(setTimeout(function(){');
                }

                
                f.push('fn.call(scope || dom, e, t, options);');

                if(options.single) {
                    f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
                }

                if(options.delay) {
                    f.push('}, ' + options.delay + '));');
                }

                if(options.buffer) {
                    f.push('}, ' + options.buffer + ');');
                }

                gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
            }

            gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
        };
    },

    
    getEventListenerCache : function(element, eventName) {
        var eventCache = this.getElementEventCache(element);
        return eventCache[eventName] || (eventCache[eventName] = []);
    },

    
    getElementEventCache : function(element) {
        var elementCache = Ext.cache[this.getId(element)];
        return elementCache.events || (elementCache.events = {});
    },

    
    mouseLeaveRe: /(mouseout|mouseleave)/,
    mouseEnterRe: /(mouseover|mouseenter)/,

    
    stopEvent: function(event) {
        this.stopPropagation(event);
        this.preventDefault(event);
    },

    
    stopPropagation: function(event) {
        event = event.browserEvent || event;
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    },

    
    preventDefault: function(event) {
        event = event.browserEvent || event;
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
            
            try {
              
              if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
                  event.keyCode = -1;
              }
            } catch (e) {
                
            }
        }
    },

    
    getRelatedTarget: function(event) {
        event = event.browserEvent || event;
        var target = event.relatedTarget;
        if (!target) {
            if (this.mouseLeaveRe.test(event.type)) {
                target = event.toElement;
            } else if (this.mouseEnterRe.test(event.type)) {
                target = event.fromElement;
            }
        }
        return this.resolveTextNode(target);
    },

    
    getPageX: function(event) {
        return this.getXY(event)[0];
    },

    
    getPageY: function(event) {
        return this.getXY(event)[1];
    },

    
    getPageXY: function(event) {
        event = event.browserEvent || event;
        var x = event.pageX,
            y = event.pageY,
            doc = document.documentElement,
            body = document.body;

        
        if (!x && x !== 0) {
            x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
            y = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);
        }
        return [x, y];
    },

    
    getTarget: function(event) {
        event = event.browserEvent || event;
        return this.resolveTextNode(event.target || event.srcElement);
    },

    
    
    resolveTextNode: Ext.isGecko ?
        function(node) {
            if (!node) {
                return;
            }
            
            var s = HTMLElement.prototype.toString.call(node);
            if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
                return;
            }
                return node.nodeType == 3 ? node.parentNode: node;
            }: function(node) {
                return node && node.nodeType == 3 ? node.parentNode: node;
            },

    

    
    curWidth: 0,
    curHeight: 0,

    
    onWindowResize: function(fn, scope, options){
        var resize = this.resizeEvent;
        if(!resize){
            this.resizeEvent = resize = new Ext.util.Event();
            this.on(window, 'resize', this.fireResize, this, {buffer: 100});
        }
        resize.addListener(fn, scope, options);
    },

    
    fireResize: function(){
        var me = this,
            w = Ext.core.Element.getViewWidth(),
            h = Ext.core.Element.getViewHeight();

         
         if(me.curHeight != h || me.curWidth != w){
             me.curHeight = h;
             me.curWidth = w;
             me.resizeEvent.fire(w, h);
         }
    },

    
    removeResizeListener: function(fn, scope){
        if (this.resizeEvent) {
            this.resizeEvent.removeListener(fn, scope);
        }
    },

    onWindowUnload: function() {
        var unload = this.unloadEvent;
        if (!unload) {
            this.unloadEvent = unload = new Ext.util.Event();
            this.addListener(window, 'unload', this.fireUnload, this);
        }
    },

    
    fireUnload: function() {
        
        try {
            this.removeUnloadListener();
            
            if (Ext.isGecko3) {
                var gridviews = Ext.ComponentQuery.query('gridview'),
                    i = 0,
                    ln = gridviews.length;
                for (; i < ln; i++) {
                    gridviews[i].scrollToTop();
                }
            }
            
            var el,
                cache = Ext.cache;
            for (el in cache) {
                if (cache.hasOwnProperty(el)) {
                    Ext.EventManager.removeAll(el);
                }
            }
        } catch(e) {
        }
    },

    
    removeUnloadListener: function(){
        if (this.unloadEvent) {
            this.removeListener(window, 'unload', this.fireUnload);
        }
    },

    
    useKeyDown: Ext.isWebKit ?
                   parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
                   !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),

    
    getKeyEvent: function(){
        return this.useKeyDown ? 'keydown' : 'keypress';
    }
};


Ext.onReady = function(fn, scope, options) {
    Ext.Loader.onReady(fn, scope, true, options);
};


Ext.onDocumentReady = Ext.EventManager.onDocumentReady;


Ext.EventManager.on = Ext.EventManager.addListener;


Ext.EventManager.un = Ext.EventManager.removeListener;

(function(){
    var initExtCss = function() {
        
        var bd = document.body || document.getElementsByTagName('body')[0],
            baseCSSPrefix = Ext.baseCSSPrefix,
            cls = [],
            htmlCls = [],
            html;

        if (!bd) {
            return false;
        }

        html = bd.parentNode;

        
        if (Ext.isIE) {
            cls.push(baseCSSPrefix + 'ie');
        }
        if (Ext.isIE6) {
            cls.push(baseCSSPrefix + 'ie6');
        }
        if (Ext.isIE7) {
            cls.push(baseCSSPrefix + 'ie7');
        }
        if (Ext.isIE8) {
            cls.push(baseCSSPrefix + 'ie8');
        }
        if (Ext.isIE9) {
            cls.push(baseCSSPrefix + 'ie9');
        }
        if (Ext.isGecko) {
            cls.push(baseCSSPrefix + 'gecko');
        }
        if (Ext.isGecko3) {
            cls.push(baseCSSPrefix + 'gecko3');
        }
        if (Ext.isGecko4) {
            cls.push(baseCSSPrefix + 'gecko4');
        }
        if (Ext.isOpera) {
            cls.push(baseCSSPrefix + 'opera');
        }
        if (Ext.isWebKit) {
            cls.push(baseCSSPrefix + 'webkit');
        }
        if (Ext.isSafari) {
            cls.push(baseCSSPrefix + 'safari');
        }
        if (Ext.isSafari2) {
            cls.push(baseCSSPrefix + 'safari2');
        }
        if (Ext.isSafari3) {
            cls.push(baseCSSPrefix + 'safari3');
        }
        if (Ext.isSafari4) {
            cls.push(baseCSSPrefix + 'safari4');
        }
        if (Ext.isChrome) {
            cls.push(baseCSSPrefix + 'chrome');
        }
        if (Ext.isMac) {
            cls.push(baseCSSPrefix + 'mac');
        }
        if (Ext.isLinux) {
            cls.push(baseCSSPrefix + 'linux');
        }
        if (!Ext.supports.CSS3BorderRadius) {
            cls.push(baseCSSPrefix + 'nbr');
        }
        if (!Ext.supports.CSS3LinearGradient) {
            cls.push(baseCSSPrefix + 'nlg');
        }
        if (!Ext.scopeResetCSS) {
            cls.push(baseCSSPrefix + 'reset');
        }

        
        if (html) {
            if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
                Ext.isBorderBox = false;
            }
            else {
                Ext.isBorderBox = true;
            }

            htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
            if (!Ext.isStrict) {
                htmlCls.push(baseCSSPrefix + 'quirks');
                if (Ext.isIE && !Ext.isStrict) {
                    Ext.isIEQuirks = true;
                }
            }
            Ext.fly(html, '_internal').addCls(htmlCls);
        }

        Ext.fly(bd, '_internal').addCls(cls);
        return true;
    };

    Ext.onReady(initExtCss);
})();


Ext.define('Ext.EventObjectImpl', {
    uses: ['Ext.util.Point'],

    
    BACKSPACE: 8,
    
    TAB: 9,
    
    NUM_CENTER: 12,
    
    ENTER: 13,
    
    RETURN: 13,
    
    SHIFT: 16,
    
    CTRL: 17,
    
    ALT: 18,
    
    PAUSE: 19,
    
    CAPS_LOCK: 20,
    
    ESC: 27,
    
    SPACE: 32,
    
    PAGE_UP: 33,
    
    PAGE_DOWN: 34,
    
    END: 35,
    
    HOME: 36,
    
    LEFT: 37,
    
    UP: 38,
    
    RIGHT: 39,
    
    DOWN: 40,
    
    PRINT_SCREEN: 44,
    
    INSERT: 45,
    
    DELETE: 46,
    
    ZERO: 48,
    
    ONE: 49,
    
    TWO: 50,
    
    THREE: 51,
    
    FOUR: 52,
    
    FIVE: 53,
    
    SIX: 54,
    
    SEVEN: 55,
    
    EIGHT: 56,
    
    NINE: 57,
    
    A: 65,
    
    B: 66,
    
    C: 67,
    
    D: 68,
    
    E: 69,
    
    F: 70,
    
    G: 71,
    
    H: 72,
    
    I: 73,
    
    J: 74,
    
    K: 75,
    
    L: 76,
    
    M: 77,
    
    N: 78,
    
    O: 79,
    
    P: 80,
    
    Q: 81,
    
    R: 82,
    
    S: 83,
    
    T: 84,
    
    U: 85,
    
    V: 86,
    
    W: 87,
    
    X: 88,
    
    Y: 89,
    
    Z: 90,
    
    CONTEXT_MENU: 93,
    
    NUM_ZERO: 96,
    
    NUM_ONE: 97,
    
    NUM_TWO: 98,
    
    NUM_THREE: 99,
    
    NUM_FOUR: 100,
    
    NUM_FIVE: 101,
    
    NUM_SIX: 102,
    
    NUM_SEVEN: 103,
    
    NUM_EIGHT: 104,
    
    NUM_NINE: 105,
    
    NUM_MULTIPLY: 106,
    
    NUM_PLUS: 107,
    
    NUM_MINUS: 109,
    
    NUM_PERIOD: 110,
    
    NUM_DIVISION: 111,
    
    F1: 112,
    
    F2: 113,
    
    F3: 114,
    
    F4: 115,
    
    F5: 116,
    
    F6: 117,
    
    F7: 118,
    
    F8: 119,
    
    F9: 120,
    
    F10: 121,
    
    F11: 122,
    
    F12: 123,

    
    clickRe: /(dbl)?click/,
    
    safariKeys: {
        3: 13, 
        63234: 37, 
        63235: 39, 
        63232: 38, 
        63233: 40, 
        63276: 33, 
        63277: 34, 
        63272: 46, 
        63273: 36, 
        63275: 35 
    },
    
    btnMap: Ext.isIE ? {
        1: 0,
        4: 1,
        2: 2
    } : {
        0: 0,
        1: 1,
        2: 2
    },

    constructor: function(event, freezeEvent){
        if (event) {
            this.setEvent(event.browserEvent || event, freezeEvent);
        }
    },

    setEvent: function(event, freezeEvent){
        var me = this, button, options;

        if (event == me || (event && event.browserEvent)) { 
            return event;
        }
        me.browserEvent = event;
        if (event) {
            
            button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
            if (me.clickRe.test(event.type) && button == -1) {
                button = 0;
            }
            options = {
                type: event.type,
                button: button,
                shiftKey: event.shiftKey,
                
                ctrlKey: event.ctrlKey || event.metaKey || false,
                altKey: event.altKey,
                
                keyCode: event.keyCode,
                charCode: event.charCode,
                
                target: Ext.EventManager.getTarget(event),
                relatedTarget: Ext.EventManager.getRelatedTarget(event),
                currentTarget: event.currentTarget,
                xy: (freezeEvent ? me.getXY() : null)
            };
        } else {
            options = {
                button: -1,
                shiftKey: false,
                ctrlKey: false,
                altKey: false,
                keyCode: 0,
                charCode: 0,
                target: null,
                xy: [0, 0]
            };
        }
        Ext.apply(me, options);
        return me;
    },

    
    stopEvent: function(){
        this.stopPropagation();
        this.preventDefault();
    },

    
    preventDefault: function(){
        if (this.browserEvent) {
            Ext.EventManager.preventDefault(this.browserEvent);
        }
    },

    
    stopPropagation: function(){
        var browserEvent = this.browserEvent;

        if (browserEvent) {
            if (browserEvent.type == 'mousedown') {
                Ext.EventManager.stoppedMouseDownEvent.fire(this);
            }
            Ext.EventManager.stopPropagation(browserEvent);
        }
    },

    
    getCharCode: function(){
        return this.charCode || this.keyCode;
    },

    
    getKey: function(){
        return this.normalizeKey(this.keyCode || this.charCode);
    },

    
    normalizeKey: function(key){
        
        return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
    },

    
    getPageX: function(){
        return this.getX();
    },

    
    getPageY: function(){
        return this.getY();
    },
    
    
    getX: function() {
        return this.getXY()[0];
    },    
    
    
    getY: function() {
        return this.getXY()[1];
    },
        
    
    getXY: function() {
        if (!this.xy) {
            
            this.xy = Ext.EventManager.getPageXY(this.browserEvent);
        }
        return this.xy;
    },

    
    getTarget : function(selector, maxDepth, returnEl){
        if (selector) {
            return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
        }
        return returnEl ? Ext.get(this.target) : this.target;
    },

    
    getRelatedTarget : function(selector, maxDepth, returnEl){
        if (selector) {
            return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
        }
        return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
    },

    
    getWheelDelta : function(){
        var event = this.browserEvent,
            delta = 0;

        if (event.wheelDelta) { 
            delta = event.wheelDelta / 120;
        } else if (event.detail){ 
            delta = -event.detail / 3;
        }
        return delta;
    },

    
    within : function(el, related, allowEl){
        if(el){
            var t = related ? this.getRelatedTarget() : this.getTarget(),
                result;

            if (t) {
                result = Ext.fly(el).contains(t);
                if (!result && allowEl) {
                    result = t == Ext.getDom(el);
                }
                return result;
            }
        }
        return false;
    },

    
    isNavKeyPress : function(){
        var me = this,
            k = this.normalizeKey(me.keyCode);

       return (k >= 33 && k <= 40) ||  
       k == me.RETURN ||
       k == me.TAB ||
       k == me.ESC;
    },

    
    isSpecialKey : function(){
        var k = this.normalizeKey(this.keyCode);
        return (this.type == 'keypress' && this.ctrlKey) ||
        this.isNavKeyPress() ||
        (k == this.BACKSPACE) || 
        (k >= 16 && k <= 20) || 
        (k >= 44 && k <= 46);   
    },

    
    getPoint : function(){
        var xy = this.getXY();
        return Ext.create('Ext.util.Point', xy[0], xy[1]);
    },

   
    hasModifier : function(){
        return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
    },

    
    injectEvent: function () {
        var API,
            dispatchers = {}; 

        

        
        

        if (!Ext.isIE && document.createEvent) { 
            API = {
                createHtmlEvent: function (doc, type, bubbles, cancelable) {
                    var event = doc.createEvent('HTMLEvents');

                    event.initEvent(type, bubbles, cancelable);
                    return event;
                },

                createMouseEvent: function (doc, type, bubbles, cancelable, detail,
                                            clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
                                            button, relatedTarget) {
                    var event = doc.createEvent('MouseEvents'),
                        view = doc.defaultView || window;

                    if (event.initMouseEvent) {
                        event.initMouseEvent(type, bubbles, cancelable, view, detail,
                                    clientX, clientY, clientX, clientY, ctrlKey, altKey,
                                    shiftKey, metaKey, button, relatedTarget);
                    } else { 
                        event = doc.createEvent('UIEvents');
                        event.initEvent(type, bubbles, cancelable);
                        event.view = view;
                        event.detail = detail;
                        event.screenX = clientX;
                        event.screenY = clientY;
                        event.clientX = clientX;
                        event.clientY = clientY;
                        event.ctrlKey = ctrlKey;
                        event.altKey = altKey;
                        event.metaKey = metaKey;
                        event.shiftKey = shiftKey;
                        event.button = button;
                        event.relatedTarget = relatedTarget;
                    }

                    return event;
                },

                createUIEvent: function (doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEvent('UIEvents'),
                        view = doc.defaultView || window;

                    event.initUIEvent(type, bubbles, cancelable, view, detail);
                    return event;
                },

                fireEvent: function (target, type, event) {
                    target.dispatchEvent(event);
                },

                fixTarget: function (target) {
                    
                    if (target == window && !target.dispatchEvent) {
                        return document;
                    }

                    return target;
                }
            }
        } else if (document.createEventObject) { 
            var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };

            API = {
                createHtmlEvent: function (doc, type, bubbles, cancelable) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },

                createMouseEvent: function (doc, type, bubbles, cancelable, detail,
                                            clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
                                            button, relatedTarget) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    event.detail = detail;
                    event.screenX = clientX;
                    event.screenY = clientY;
                    event.clientX = clientX;
                    event.clientY = clientY;
                    event.ctrlKey = ctrlKey;
                    event.altKey = altKey;
                    event.shiftKey = shiftKey;
                    event.metaKey = metaKey;
                    event.button = crazyIEButtons[button] || button;
                    event.relatedTarget = relatedTarget; 
                    return event;
                },

                createUIEvent: function (doc, type, bubbles, cancelable, detail) {
                    var event = doc.createEventObject();
                    event.bubbles = bubbles;
                    event.cancelable = cancelable;
                    return event;
                },

                fireEvent: function (target, type, event) {
                    target.fireEvent('on' + type, event);
                },

                fixTarget: function (target) {
                    if (target == document) {
                        
                        
                        return document.documentElement;
                    }

                    return target;
                }
            };
        }

        
        

        Ext.Object.each({
                load:   [false, false],
                unload: [false, false],
                select: [true, false],
                change: [true, false],
                submit: [true, true],
                reset:  [true, false],
                resize: [true, false],
                scroll: [true, false]
            },
            function (name, value) {
                var bubbles = value[0], cancelable = value[1];
                dispatchers[name] = function (targetEl, srcEvent) {
                    var e = API.createHtmlEvent(name, bubbles, cancelable);
                    API.fireEvent(targetEl, name, e);
                };
            });

        
        

        function createMouseEventDispatcher (type, detail) {
            var cancelable = (type != 'mousemove');
            return function (targetEl, srcEvent) {
                var xy = srcEvent.getXY(),
                    e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
                                detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
                                srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
                                srcEvent.relatedTarget);
                API.fireEvent(targetEl, type, e);
            };
        }

        Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
            function (eventName) {
                dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
            });

        
        

        Ext.Object.each({
                focusin:  [true, false],
                focusout: [true, false],
                activate: [true, true],
                focus:    [false, false],
                blur:     [false, false]
            },
            function (name, value) {
                var bubbles = value[0], cancelable = value[1];
                dispatchers[name] = function (targetEl, srcEvent) {
                    var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
                    API.fireEvent(targetEl, name, e);
                };
            });

        
        if (!API) {
            

            dispatchers = {}; 

            API = {
                fixTarget: function (t) {
                    return t;
                }
            };
        }

        function cannotInject (target, srcEvent) {
            
        }

        return function (target) {
            var me = this,
                dispatcher = dispatchers[me.type] || cannotInject,
                t = target ? (target.dom || target) : me.getTarget();

            t = API.fixTarget(t);
            dispatcher(t, me);
        };
    }() 

}, function() {

Ext.EventObject = new Ext.EventObjectImpl();

});



(function(){
    var doc = document,
        activeElement = null,
        isCSS1 = doc.compatMode == "CSS1Compat",
        ELEMENT = Ext.core.Element,
        fly = function(el){
            if (!_fly) {
                _fly = new Ext.core.Element.Flyweight();
            }
            _fly.dom = el;
            return _fly;
        }, _fly;

    
    
    
    if (!('activeElement' in doc) && doc.addEventListener) {
        doc.addEventListener('focus',
            function (ev) {
                if (ev && ev.target) {
                    activeElement = (ev.target == doc) ? null : ev.target;
                }
            }, true);
    }

    
    function makeSelectionRestoreFn (activeEl, start, end) {
        return function () {
            activeEl.selectionStart = start;
            activeEl.selectionEnd = end;
        };
    }

    Ext.apply(ELEMENT, {
        isAncestor : function(p, c) {
            var ret = false;

            p = Ext.getDom(p);
            c = Ext.getDom(c);
            if (p && c) {
                if (p.contains) {
                    return p.contains(c);
                } else if (p.compareDocumentPosition) {
                    return !!(p.compareDocumentPosition(c) & 16);
                } else {
                    while ((c = c.parentNode)) {
                        ret = c == p || ret;
                    }
                }
            }
            return ret;
        },

        
        getActiveElement: function () {
            return doc.activeElement || activeElement;
        },

        
        getRightMarginFixCleaner: function (target) {
            var supports = Ext.supports,
                hasInputBug = supports.DisplayChangeInputSelectionBug,
                hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;

            if (hasInputBug || hasTextAreaBug) {
                var activeEl = doc.activeElement || activeElement, 
                    tag = activeEl && activeEl.tagName,
                    start,
                    end;

                if ((hasTextAreaBug && tag == 'TEXTAREA') ||
                    (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
                    if (ELEMENT.isAncestor(target, activeEl)) {
                        start = activeEl.selectionStart;
                        end = activeEl.selectionEnd;

                        if (Ext.isNumber(start) && Ext.isNumber(end)) { 
                            
                            
                            
                            
                            return makeSelectionRestoreFn(activeEl, start, end);
                        }
                    }
                }
            }

            return Ext.emptyFn; 
        },

        getViewWidth : function(full) {
            return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
        },

        getViewHeight : function(full) {
            return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
        },

        getDocumentHeight: function() {
            return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
        },

        getDocumentWidth: function() {
            return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
        },

        getViewportHeight: function(){
            return Ext.isIE ?
                   (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
                   self.innerHeight;
        },

        getViewportWidth : function() {
            return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
                   Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
        },

        getY : function(el) {
            return ELEMENT.getXY(el)[1];
        },

        getX : function(el) {
            return ELEMENT.getXY(el)[0];
        },

        getXY : function(el) {
            var p,
                pe,
                b,
                bt,
                bl,
                dbd,
                x = 0,
                y = 0,
                scroll,
                hasAbsolute,
                bd = (doc.body || doc.documentElement),
                ret = [0,0];

            el = Ext.getDom(el);

            if(el != bd){
                hasAbsolute = fly(el).isStyle("position", "absolute");

                if (el.getBoundingClientRect) {
                    b = el.getBoundingClientRect();
                    scroll = fly(document).getScroll();
                    ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
                } else {
                    p = el;

                    while (p) {
                        pe = fly(p);
                        x += p.offsetLeft;
                        y += p.offsetTop;

                        hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");

                        if (Ext.isGecko) {
                            y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
                            x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;

                            if (p != el && !pe.isStyle('overflow','visible')) {
                                x += bl;
                                y += bt;
                            }
                        }
                        p = p.offsetParent;
                    }

                    if (Ext.isSafari && hasAbsolute) {
                        x -= bd.offsetLeft;
                        y -= bd.offsetTop;
                    }

                    if (Ext.isGecko && !hasAbsolute) {
                        dbd = fly(bd);
                        x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
                        y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
                    }

                    p = el.parentNode;
                    while (p && p != bd) {
                        if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
                            x -= p.scrollLeft;
                            y -= p.scrollTop;
                        }
                        p = p.parentNode;
                    }
                    ret = [x,y];
                }
            }
            return ret;
        },

        setXY : function(el, xy) {
            (el = Ext.fly(el, '_setXY')).position();

            var pts = el.translatePoints(xy),
                style = el.dom.style,
                pos;

            for (pos in pts) {
                if (!isNaN(pts[pos])) {
                    style[pos] = pts[pos] + "px";
                }
            }
        },

        setX : function(el, x) {
            ELEMENT.setXY(el, [x, false]);
        },

        setY : function(el, y) {
            ELEMENT.setXY(el, [false, y]);
        },

        
        serializeForm: function(form) {
            var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
                hasSubmit = false,
                encoder = encodeURIComponent,
                name,
                data = '',
                type,
                hasValue;

            Ext.each(fElements, function(element){
                name = element.name;
                type = element.type;

                if (!element.disabled && name) {
                    if (/select-(one|multiple)/i.test(type)) {
                        Ext.each(element.options, function(opt){
                            if (opt.selected) {
                                hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
                                data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
                            }
                        });
                    } else if (!(/file|undefined|reset|button/i.test(type))) {
                        if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
                            data += encoder(name) + '=' + encoder(element.value) + '&';
                            hasSubmit = /submit/i.test(type);
                        }
                    }
                }
            });
            return data.substr(0, data.length - 1);
        }
    });
})();



Ext.core.Element.addMethods({

    
    monitorMouseLeave: function(delay, handler, scope) {
        var me = this,
            timer,
            listeners = {
                mouseleave: function(e) {
                    timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
                },
                mouseenter: function() {
                    clearTimeout(timer);
                },
                freezeEvent: true
            };

        me.on(listeners);
        return listeners;
    },

    
    swallowEvent : function(eventName, preventDefault) {
        var me = this;
        function fn(e) {
            e.stopPropagation();
            if (preventDefault) {
                e.preventDefault();
            }
        }
        
        if (Ext.isArray(eventName)) {
            Ext.each(eventName, function(e) {
                 me.on(e, fn);
            });
            return me;
        }
        me.on(eventName, fn);
        return me;
    },

    
    relayEvent : function(eventName, observable) {
        this.on(eventName, function(e) {
            observable.fireEvent(eventName, e);
        });
    },

    
    clean : function(forceReclean) {
        var me  = this,
            dom = me.dom,
            n   = dom.firstChild,
            nx,
            ni  = -1;

        if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
            return me;
        }

        while (n) {
            nx = n.nextSibling;
            if (n.nodeType == 3) {
                
                if (!(/\S/.test(n.nodeValue))) {
                    dom.removeChild(n);
                
                } else if (nx && nx.nodeType == 3) {
                    n.appendData(Ext.String.trim(nx.data));
                    dom.removeChild(nx);
                    nx = n.nextSibling;
                    n.nodeIndex = ++ni;
                }
            } else {
                
                Ext.fly(n).clean();
                n.nodeIndex = ++ni;
            }
            n = nx;
        }

        Ext.core.Element.data(dom, 'isCleaned', true);
        return me;
    },

    
    load : function(options) {
        this.getLoader().load(options);
        return this;
    },

    
    getLoader : function() {
        var dom = this.dom,
            data = Ext.core.Element.data,
            loader = data(dom, 'loader');
            
        if (!loader) {
            loader = Ext.create('Ext.ElementLoader', {
                target: this
            });
            data(dom, 'loader', loader);
        }
        return loader;
    },

    
    update : function(html, loadScripts, callback) {
        var me = this,
            id,
            dom,
            interval;
            
        if (!me.dom) {
            return me;
        }
        html = html || '';
        dom = me.dom;

        if (loadScripts !== true) {
            dom.innerHTML = html;
            Ext.callback(callback, me);
            return me;
        }

        id  = Ext.id();
        html += '<span id="' + id + '"></span>';

        interval = setInterval(function(){
            if (!document.getElementById(id)) {
                return false;    
            }
            clearInterval(interval);
            var DOC    = document,
                hd     = DOC.getElementsByTagName("head")[0],
                re     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
                srcRe  = /\ssrc=([\'\"])(.*?)\1/i,
                typeRe = /\stype=([\'\"])(.*?)\1/i,
                match,
                attrs,
                srcMatch,
                typeMatch,
                el,
                s;

            while ((match = re.exec(html))) {
                attrs = match[1];
                srcMatch = attrs ? attrs.match(srcRe) : false;
                if (srcMatch && srcMatch[2]) {
                   s = DOC.createElement("script");
                   s.src = srcMatch[2];
                   typeMatch = attrs.match(typeRe);
                   if (typeMatch && typeMatch[2]) {
                       s.type = typeMatch[2];
                   }
                   hd.appendChild(s);
                } else if (match[2] && match[2].length > 0) {
                    if (window.execScript) {
                       window.execScript(match[2]);
                    } else {
                       window.eval(match[2]);
                    }
                }
            }
            
            el = DOC.getElementById(id);
            if (el) {
                Ext.removeNode(el);
            }
            Ext.callback(callback, me);
        }, 20);
        dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
        return me;
    },

    
    removeAllListeners : function() {
        this.removeAnchor();
        Ext.EventManager.removeAll(this.dom);
        return this;
    },

    
    createProxy : function(config, renderTo, matchBox) {
        config = (typeof config == 'object') ? config : {tag : "div", cls: config};

        var me = this,
            proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
                               Ext.core.DomHelper.insertBefore(me.dom, config, true);

        proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
        proxy.hide();
        if (matchBox && me.setBox && me.getBox) { 
           proxy.setBox(me.getBox());
        }
        return proxy;
    }
});
Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;


Ext.core.Element.addMethods({
    
    getAnchorXY : function(anchor, local, s){
        
        
        anchor = (anchor || "tl").toLowerCase();
        s = s || {};

        var me = this,
            vp = me.dom == document.body || me.dom == document,
            w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(),
            h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(),
            xy,
            r = Math.round,
            o = me.getXY(),
            scroll = me.getScroll(),
            extraX = vp ? scroll.left : !local ? o[0] : 0,
            extraY = vp ? scroll.top : !local ? o[1] : 0,
            hash = {
                c  : [r(w * 0.5), r(h * 0.5)],
                t  : [r(w * 0.5), 0],
                l  : [0, r(h * 0.5)],
                r  : [w, r(h * 0.5)],
                b  : [r(w * 0.5), h],
                tl : [0, 0],
                bl : [0, h],
                br : [w, h],
                tr : [w, 0]
            };

        xy = hash[anchor];
        return [xy[0] + extraX, xy[1] + extraY];
    },

    
    anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
        var me = this,
            dom = me.dom,
            scroll = !Ext.isEmpty(monitorScroll),
            action = function(){
                Ext.fly(dom).alignTo(el, alignment, offsets, animate);
                Ext.callback(callback, Ext.fly(dom));
            },
            anchor = this.getAnchor();

        
        this.removeAnchor();
        Ext.apply(anchor, {
            fn: action,
            scroll: scroll
        });

        Ext.EventManager.onWindowResize(action, null);

        if(scroll){
            Ext.EventManager.on(window, 'scroll', action, null,
                {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
        }
        action.call(me); 
        return me;
    },

    
    removeAnchor : function(){
        var me = this,
            anchor = this.getAnchor();

        if(anchor && anchor.fn){
            Ext.EventManager.removeResizeListener(anchor.fn);
            if(anchor.scroll){
                Ext.EventManager.un(window, 'scroll', anchor.fn);
            }
            delete anchor.fn;
        }
        return me;
    },

    
    getAnchor : function(){
        var data = Ext.core.Element.data,
            dom = this.dom;
            if (!dom) {
                return;
            }
            var anchor = data(dom, '_anchor');

        if(!anchor){
            anchor = data(dom, '_anchor', {});
        }
        return anchor;
    },

    getAlignVector: function(el, spec, offset) {
        var me = this,
            side = {t:"top", l:"left", r:"right", b: "bottom"},
            thisRegion = me.getRegion(),
            elRegion;

        el = Ext.get(el);
        if(!el || !el.dom){
            Ext.Error.raise({
                sourceClass: 'Ext.core.Element',
                sourceMethod: 'getAlignVector',
                msg: 'Attempted to align an element that doesn\'t exist'
            });
        }

        elRegion = el.getRegion();
    },

    
    getAlignToXY : function(el, p, o){
        el = Ext.get(el);

        if(!el || !el.dom){
            Ext.Error.raise({
                sourceClass: 'Ext.core.Element',
                sourceMethod: 'getAlignToXY',
                msg: 'Attempted to align an element that doesn\'t exist'
            });
        }

        o = o || [0,0];
        p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();

        var me = this,
            d = me.dom,
            a1,
            a2,
            x,
            y,
            
            w,
            h,
            r,
            dw = Ext.core.Element.getViewWidth() -10, 
            dh = Ext.core.Element.getViewHeight()-10, 
            p1y,
            p1x,
            p2y,
            p2x,
            swapY,
            swapX,
            doc = document,
            docElement = doc.documentElement,
            docBody = doc.body,
            scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
            scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
            c = false, 
            p1 = "",
            p2 = "",
            m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);

        if(!m){
            Ext.Error.raise({
                sourceClass: 'Ext.core.Element',
                sourceMethod: 'getAlignToXY',
                el: el,
                position: p,
                offset: o,
                msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
            });
        }

        p1 = m[1];
        p2 = m[2];
        c = !!m[3];

        
        
        a1 = me.getAnchorXY(p1, true);
        a2 = el.getAnchorXY(p2, false);

        x = a2[0] - a1[0] + o[0];
        y = a2[1] - a1[1] + o[1];

        if(c){
           w = me.getWidth();
           h = me.getHeight();
           r = el.getRegion();
           
           
           
           p1y = p1.charAt(0);
           p1x = p1.charAt(p1.length-1);
           p2y = p2.charAt(0);
           p2x = p2.charAt(p2.length-1);
           swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
           swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));


           if (x + w > dw + scrollX) {
                x = swapX ? r.left-w : dw+scrollX-w;
           }
           if (x < scrollX) {
               x = swapX ? r.right : scrollX;
           }
           if (y + h > dh + scrollY) {
                y = swapY ? r.top-h : dh+scrollY-h;
            }
           if (y < scrollY){
               y = swapY ? r.bottom : scrollY;
           }
        }
        return [x,y];
    },

    
    alignTo : function(element, position, offsets, animate){
        var me = this;
        return me.setXY(me.getAlignToXY(element, position, offsets),
                        me.anim && !!animate ? me.anim(animate) : false);
    },

    
    adjustForConstraints : function(xy, parent) {
        var vector = this.getConstrainVector(parent, xy);
        if (vector) {
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    },

    
    getConstrainVector: function(constrainTo, proposedPosition) {
        if (!(constrainTo instanceof Ext.util.Region)) {
            constrainTo = Ext.get(constrainTo).getViewRegion();
        }
        var thisRegion = this.getRegion(),
            vector = [0, 0],
            shadowSize = this.shadow && this.shadow.offset,
            overflowed = false;

        
        if (proposedPosition) {
            thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
        }

        
        
        if (shadowSize) {
            constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
        }

        
        if (thisRegion.right > constrainTo.right) {
            overflowed = true;
            vector[0] = (constrainTo.right - thisRegion.right);    
        }
        if (thisRegion.left + vector[0] < constrainTo.left) {
            overflowed = true;
            vector[0] = (constrainTo.left - thisRegion.left);      
        }

        
        if (thisRegion.bottom > constrainTo.bottom) {
            overflowed = true;
            vector[1] = (constrainTo.bottom - thisRegion.bottom);  
        }
        if (thisRegion.top + vector[1] < constrainTo.top) {
            overflowed = true;
            vector[1] = (constrainTo.top - thisRegion.top);        
        }
        return overflowed ? vector : false;
    },

    
    getCenterXY : function(){
        return this.getAlignToXY(document, 'c-c');
    },

    
    center : function(centerIn){
        return this.alignTo(centerIn || document, 'c-c');
    }
});


(function(){

var ELEMENT = Ext.core.Element,
    LEFT = "left",
    RIGHT = "right",
    TOP = "top",
    BOTTOM = "bottom",
    POSITION = "position",
    STATIC = "static",
    RELATIVE = "relative",
    AUTO = "auto",
    ZINDEX = "z-index";

Ext.override(Ext.core.Element, {
    
    getX : function(){
        return ELEMENT.getX(this.dom);
    },

    
    getY : function(){
        return ELEMENT.getY(this.dom);
    },

    
    getXY : function(){
        return ELEMENT.getXY(this.dom);
    },

    
    getOffsetsTo : function(el){
        var o = this.getXY(),
            e = Ext.fly(el, '_internal').getXY();
        return [o[0]-e[0],o[1]-e[1]];
    },

    
    setX : function(x, animate){
        return this.setXY([x, this.getY()], animate);
    },

    
    setY : function(y, animate){
        return this.setXY([this.getX(), y], animate);
    },

    
    setLeft : function(left){
        this.setStyle(LEFT, this.addUnits(left));
        return this;
    },

    
    setTop : function(top){
        this.setStyle(TOP, this.addUnits(top));
        return this;
    },

    
    setRight : function(right){
        this.setStyle(RIGHT, this.addUnits(right));
        return this;
    },

    
    setBottom : function(bottom){
        this.setStyle(BOTTOM, this.addUnits(bottom));
        return this;
    },

    
    setXY: function(pos, animate) {
        var me = this;
        if (!animate || !me.anim) {
            ELEMENT.setXY(me.dom, pos);
        }
        else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
        }
        return me;
    },

    
    setLocation : function(x, y, animate){
        return this.setXY([x, y], animate);
    },

    
    moveTo : function(x, y, animate){
        return this.setXY([x, y], animate);
    },

    
    getLeft : function(local){
        return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
    },

    
    getRight : function(local){
        var me = this;
        return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
    },

    
    getTop : function(local) {
        return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
    },

    
    getBottom : function(local){
        var me = this;
        return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
    },

    
    position : function(pos, zIndex, x, y) {
        var me = this;

        if (!pos && me.isStyle(POSITION, STATIC)){
            me.setStyle(POSITION, RELATIVE);
        } else if(pos) {
            me.setStyle(POSITION, pos);
        }
        if (zIndex){
            me.setStyle(ZINDEX, zIndex);
        }
        if (x || y) {
            me.setXY([x || false, y || false]);
        }
    },

    
    clearPositioning : function(value){
        value = value || '';
        this.setStyle({
            left : value,
            right : value,
            top : value,
            bottom : value,
            "z-index" : "",
            position : STATIC
        });
        return this;
    },

    
    getPositioning : function(){
        var l = this.getStyle(LEFT);
        var t = this.getStyle(TOP);
        return {
            "position" : this.getStyle(POSITION),
            "left" : l,
            "right" : l ? "" : this.getStyle(RIGHT),
            "top" : t,
            "bottom" : t ? "" : this.getStyle(BOTTOM),
            "z-index" : this.getStyle(ZINDEX)
        };
    },

    
    setPositioning : function(pc){
        var me = this,
            style = me.dom.style;

        me.setStyle(pc);

        if(pc.right == AUTO){
            style.right = "";
        }
        if(pc.bottom == AUTO){
            style.bottom = "";
        }

        return me;
    },

    
    translatePoints: function(x, y) {
        if (Ext.isArray(x)) {
             y = x[1];
             x = x[0];
        }
        var me = this,
            relative = me.isStyle(POSITION, RELATIVE),
            o = me.getXY(),
            left = parseInt(me.getStyle(LEFT), 10),
            top = parseInt(me.getStyle(TOP), 10);

        if (!Ext.isNumber(left)) {
            left = relative ? 0 : me.dom.offsetLeft;
        }
        if (!Ext.isNumber(top)) {
            top = relative ? 0 : me.dom.offsetTop;
        }
        left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
        top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
        return {
            left: left,
            top: top
        };
    },

    
    setBox: function(box, adjust, animate) {
        var me = this,
            w = box.width,
            h = box.height;
        if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
            w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
            h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
        }
        me.setBounds(box.x, box.y, w, h, animate);
        return me;
    },

    
    getBox: function(contentBox, local) {
        var me = this,
            xy,
            left,
            top,
            getBorderWidth = me.getBorderWidth,
            getPadding = me.getPadding,
            l, r, t, b, w, h, bx;
        if (!local) {
            xy = me.getXY();
        } else {
            left = parseInt(me.getStyle("left"), 10) || 0;
            top = parseInt(me.getStyle("top"), 10) || 0;
            xy = [left, top];
        }
        w = me.getWidth();
        h = me.getHeight();
        if (!contentBox) {
            bx = {
                x: xy[0],
                y: xy[1],
                0: xy[0],
                1: xy[1],
                width: w,
                height: h
            };
        } else {
            l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
            r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
            t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
            b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
            bx = {
                x: xy[0] + l,
                y: xy[1] + t,
                0: xy[0] + l,
                1: xy[1] + t,
                width: w - (l + r),
                height: h - (t + b)
            };
        }
        bx.right = bx.x + bx.width;
        bx.bottom = bx.y + bx.height;
        return bx;
    },

    
    move: function(direction, distance, animate) {
        var me = this,
            xy = me.getXY(),
            x = xy[0],
            y = xy[1],
            left = [x - distance, y],
            right = [x + distance, y],
            top = [x, y - distance],
            bottom = [x, y + distance],
            hash = {
                l: left,
                left: left,
                r: right,
                right: right,
                t: top,
                top: top,
                up: top,
                b: bottom,
                bottom: bottom,
                down: bottom
            };

        direction = direction.toLowerCase();
        me.moveTo(hash[direction][0], hash[direction][1], animate);
    },

    
    setLeftTop: function(left, top) {
        var me = this,
            style = me.dom.style;
        style.left = me.addUnits(left);
        style.top = me.addUnits(top);
        return me;
    },

    
    getRegion: function() {
        return this.getPageBox(true);
    },

    
    getViewRegion: function() {
        var me = this,
            isBody = me.dom === document.body,
            scroll, pos, top, left, width, height;
            
        
        if (isBody) {
            scroll = me.getScroll();
            left = scroll.left;
            top = scroll.top;
            width = Ext.core.Element.getViewportWidth();
            height = Ext.core.Element.getViewportHeight();
        }
        else {
            pos = me.getXY();
            left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
            top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
            width = me.getWidth(true);
            height = me.getHeight(true);
        }

        return Ext.create('Ext.util.Region', top, left + width, top + height, left);
    },

    
    getPageBox : function(getRegion) {
        var me = this,
            el = me.dom,
            isDoc = el === document.body,
            w = isDoc ? Ext.core.Element.getViewWidth()  : el.offsetWidth,
            h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight,
            xy = me.getXY(),
            t = xy[1],
            r = xy[0] + w,
            b = xy[1] + h,
            l = xy[0];

        if (getRegion) {
            return Ext.create('Ext.util.Region', t, r, b, l);
        }
        else {
            return {
                left: l,
                top: t,
                width: w,
                height: h,
                right: r,
                bottom: b
            };
        }
    },

    
    setBounds: function(x, y, width, height, animate) {
        var me = this;
        if (!animate || !me.anim) {
            me.setSize(width, height);
            me.setLocation(x, y);
        } else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            me.animate(Ext.applyIf({
                to: {
                    x: x,
                    y: y,
                    width: me.adjustWidth(width),
                    height: me.adjustHeight(height)
                }
            }, animate));
        }
        return me;
    },

    
    setRegion: function(region, animate) {
        return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
    }
});
})();


Ext.override(Ext.core.Element, {
    
    isScrollable : function(){
        var dom = this.dom;
        return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
    },

    
    getScroll : function() {
        var d = this.dom, 
            doc = document,
            body = doc.body,
            docElement = doc.documentElement,
            l,
            t,
            ret;

        if (d == doc || d == body) {
            if (Ext.isIE && Ext.isStrict) {
                l = docElement.scrollLeft; 
                t = docElement.scrollTop;
            } else {
                l = window.pageXOffset;
                t = window.pageYOffset;
            }
            ret = {
                left: l || (body ? body.scrollLeft : 0), 
                top : t || (body ? body.scrollTop : 0)
            };
        } else {
            ret = {
                left: d.scrollLeft, 
                top : d.scrollTop
            };
        }
        
        return ret;
    },
    
    
    scrollTo : function(side, value, animate) {
        
        var top = /top/i.test(side),
            me = this,
            dom = me.dom,
            obj = {},
            prop;
        if (!animate || !me.anim) {
            
            prop = 'scroll' + (top ? 'Top' : 'Left');
            dom[prop] = value;
        }
        else {
            if (!Ext.isObject(animate)) {
                animate = {};
            }
            obj['scroll' + (top ? 'Top' : 'Left')] = value;
            me.animate(Ext.applyIf({
                to: obj
            }, animate));
        }
        return me;
    },

    
    scrollIntoView : function(container, hscroll) {
        container = Ext.getDom(container) || Ext.getBody().dom;
        var el = this.dom,
            offsets = this.getOffsetsTo(container),
            
            left = offsets[0] + container.scrollLeft,
            top = offsets[1] + container.scrollTop,
            bottom = top + el.offsetHeight,
            right = left + el.offsetWidth,
            
            ctClientHeight = container.clientHeight,
            ctScrollTop = parseInt(container.scrollTop, 10),
            ctScrollLeft = parseInt(container.scrollLeft, 10),
            ctBottom = ctScrollTop + ctClientHeight,
            ctRight = ctScrollLeft + container.clientWidth;

        if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
            container.scrollTop = top;
        } else if (bottom > ctBottom) {
            container.scrollTop = bottom - ctClientHeight;
        }
        
        container.scrollTop = container.scrollTop;

        if (hscroll !== false) {
            if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
                container.scrollLeft = left;
            }
            else if (right > ctRight) {
                container.scrollLeft = right - container.clientWidth;
            }
            container.scrollLeft = container.scrollLeft;
        }
        return this;
    },

    
    scrollChildIntoView : function(child, hscroll) {
        Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
    },

    
     scroll : function(direction, distance, animate) {
        if (!this.isScrollable()) {
            return false;
        }
        var el = this.dom,
            l = el.scrollLeft, t = el.scrollTop,
            w = el.scrollWidth, h = el.scrollHeight,
            cw = el.clientWidth, ch = el.clientHeight,
            scrolled = false, v,
            hash = {
                l: Math.min(l + distance, w-cw),
                r: v = Math.max(l - distance, 0),
                t: Math.max(t - distance, 0),
                b: Math.min(t + distance, h-ch)
            };
            hash.d = hash.b;
            hash.u = hash.t;

        direction = direction.substr(0, 1);
        if ((v = hash[direction]) > -1) {
            scrolled = true;
            this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
        }
        return scrolled;
    }
});

Ext.core.Element.addMethods(
    function() {
        var VISIBILITY      = "visibility",
            DISPLAY         = "display",
            HIDDEN          = "hidden",
            NONE            = "none",
            XMASKED         = Ext.baseCSSPrefix + "masked",
            XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
            data            = Ext.core.Element.data;

        return {
            
            isVisible : function(deep) {
                var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
                    p   = this.dom.parentNode;

                if (deep !== true || !vis) {
                    return vis;
                }

                while (p && !(/^body/i.test(p.tagName))) {
                    if (!Ext.fly(p, '_isVisible').isVisible()) {
                        return false;
                    }
                    p = p.parentNode;
                }
                return true;
            },

            
            isDisplayed : function() {
                return !this.isStyle(DISPLAY, NONE);
            },

            
            enableDisplayMode : function(display) {
                this.setVisibilityMode(Ext.core.Element.DISPLAY);

                if (!Ext.isEmpty(display)) {
                    data(this.dom, 'originalDisplay', display);
                }

                return this;
            },

            
            mask : function(msg, msgCls) {
                var me  = this,
                    dom = me.dom,
                    setExpression = dom.style.setExpression,
                    dh  = Ext.core.DomHelper,
                    EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
                    el,
                    mask;

                if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
                    me.addCls(XMASKEDRELATIVE);
                }
                el = data(dom, 'maskMsg');
                if (el) {
                    el.remove();
                }
                el = data(dom, 'mask');
                if (el) {
                    el.remove();
                }

                mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
                data(dom, 'mask', mask);

                me.addCls(XMASKED);
                mask.setDisplayed(true);

                if (typeof msg == 'string') {
                    var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
                    data(dom, 'maskMsg', mm);
                    mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
                    mm.dom.firstChild.innerHTML = msg;
                    mm.setDisplayed(true);
                    mm.center(me);
                }
                
                
                
                
                
                if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
                    mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
                }

                
                
                if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
                    mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
                }
                
                else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
                    mask.setSize(undefined, me.getHeight());
                }
                return mask;
            },

            
            unmask : function() {
                var me      = this,
                    dom     = me.dom,
                    mask    = data(dom, 'mask'),
                    maskMsg = data(dom, 'maskMsg');

                if (mask) {
                    
                    if (mask.dom.style.clearExpression) {
                        mask.dom.style.clearExpression('width');
                        mask.dom.style.clearExpression('height');
                    }
                    if (maskMsg) {
                        maskMsg.remove();
                        data(dom, 'maskMsg', undefined);
                    }

                    mask.remove();
                    data(dom, 'mask', undefined);
                    me.removeCls([XMASKED, XMASKEDRELATIVE]);
                }
            },
            
            isMasked : function() {
                var me = this,
                    mask = data(me.dom, 'mask'),
                    maskMsg = data(me.dom, 'maskMsg');

                if (mask && mask.isVisible()) {
                    if (maskMsg) {
                        maskMsg.center(me);
                    }
                    return true;
                }
                return false;
            },

            
            createShim : function() {
                var el = document.createElement('iframe'),
                    shim;

                el.frameBorder = '0';
                el.className = Ext.baseCSSPrefix + 'shim';
                el.src = Ext.SSL_SECURE_URL;
                shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
                shim.autoBoxAdjust = false;
                return shim;
            }
        };
    }()
);

Ext.core.Element.addMethods({
    
    addKeyListener : function(key, fn, scope){
        var config;
        if(typeof key != 'object' || Ext.isArray(key)){
            config = {
                key: key,
                fn: fn,
                scope: scope
            };
        }else{
            config = {
                key : key.key,
                shift : key.shift,
                ctrl : key.ctrl,
                alt : key.alt,
                fn: fn,
                scope: scope
            };
        }
        return Ext.create('Ext.util.KeyMap', this, config);
    },

    
    addKeyMap : function(config){
        return Ext.create('Ext.util.KeyMap', this, config);
    }
});



Ext.CompositeElementLite.importElementMethods();


Ext.apply(Ext.CompositeElementLite.prototype, {
    addElements : function(els, root){
        if(!els){
            return this;
        }
        if(typeof els == "string"){
            els = Ext.core.Element.selectorFunction(els, root);
        }
        var yels = this.elements;
        Ext.each(els, function(e) {
            yels.push(Ext.get(e));
        });
        return this;
    },

    
    first : function(){
        return this.item(0);
    },

    
    last : function(){
        return this.item(this.getCount()-1);
    },

    
    contains : function(el){
        return this.indexOf(el) != -1;
    },

    
    removeElement : function(keys, removeDom){
        var me = this,
            els = this.elements,
            el;
        Ext.each(keys, function(val){
            if ((el = (els[val] || els[val = me.indexOf(val)]))) {
                if(removeDom){
                    if(el.dom){
                        el.remove();
                    }else{
                        Ext.removeNode(el);
                    }
                }
                els.splice(val, 1);
            }
        });
        return this;
    }
});


Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
    
    constructor : function(els, root){
        this.elements = [];
        this.add(els, root);
    },
    
    
    getElement : function(el){
        
        return el;
    },
    
    
    transformElement : function(el){
        return Ext.get(el);
    }

    

    

    
});


Ext.core.Element.select = function(selector, unique, root){
    var els;
    if(typeof selector == "string"){
        els = Ext.core.Element.selectorFunction(selector, root);
    }else if(selector.length !== undefined){
        els = selector;
    }else{
        Ext.Error.raise({
            sourceClass: "Ext.core.Element",
            sourceMethod: "select",
            selector: selector,
            unique: unique,
            root: root,
            msg: "Invalid selector specified: " + selector
        });
    }
    return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
};


Ext.select = Ext.core.Element.select;





Ext.define('Ext.util.Observable', {

    

    requires: ['Ext.util.Event'],

    statics: {
        
        releaseCapture: function(o) {
            o.fireEvent = this.prototype.fireEvent;
        },

        
        capture: function(o, fn, scope) {
            o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
        },

        
        observe: function(cls, listeners) {
            if (cls) {
                if (!cls.isObservable) {
                    Ext.applyIf(cls, new this());
                    this.capture(cls.prototype, cls.fireEvent, cls);
                }
                if (Ext.isObject(listeners)) {
                    cls.on(listeners);
                }
                return cls;
            }
        }
    },

    

    
    
    isObservable: true,

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

        Ext.apply(me, config);
        if (me.listeners) {
            me.on(me.listeners);
            delete me.listeners;
        }
        me.events = me.events || {};

        if (me.bubbleEvents) {
            me.enableBubble(me.bubbleEvents);
        }
    },

    
    eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal)$/,

    
    addManagedListener : function(item, ename, fn, scope, options) {
        var me = this,
            managedListeners = me.managedListeners = me.managedListeners || [],
            config;

        if (Ext.isObject(ename)) {
            options = ename;
            for (ename in options) {
                if (options.hasOwnProperty(ename)) {
                    config = options[ename];
                    if (!me.eventOptionsRe.test(ename)) {
                        me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
                    }
                }
            }
        }
        else {
            managedListeners.push({
                item: item,
                ename: ename,
                fn: fn,
                scope: scope,
                options: options
            });

            item.on(ename, fn, scope, options);
        }
    },

    
     removeManagedListener : function(item, ename, fn, scope) {
        var me = this,
            options,
            config,
            managedListeners,
            length,
            i;

        if (Ext.isObject(ename)) {
            options = ename;
            for (ename in options) {
                if (options.hasOwnProperty(ename)) {
                    config = options[ename];
                    if (!me.eventOptionsRe.test(ename)) {
                        me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
                    }
                }
            }
        }

        managedListeners = me.managedListeners ? me.managedListeners.slice() : [];

        for (i = 0, length = managedListeners.length; i < length; i++) {
            me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
        }
    },

    
    fireEvent: function() {
        var me = this,
            args = Ext.Array.toArray(arguments),
            ename = args[0].toLowerCase(),
            ret = true,
            event = me.events[ename],
            queue = me.eventQueue,
            parent;

        if (me.eventsSuspended === true) {
            if (queue) {
                queue.push(args);
            }
        } else if (event && Ext.isObject(event) && event.bubble) {
            if (event.fire.apply(event, args.slice(1)) === false) {
                return false;
            }
            parent = me.getBubbleTarget && me.getBubbleTarget();
            if (parent && parent.isObservable) {
                if (!parent.events[ename] || !Ext.isObject(parent.events[ename]) || !parent.events[ename].bubble) {
                    parent.enableBubble(ename);
                }
                return parent.fireEvent.apply(parent, args);
            }
        } else if (event && Ext.isObject(event)) {
            args.shift();
            ret = event.fire.apply(event, args);
        }
        return ret;
    },

    
    addListener: function(ename, fn, scope, options) {
        var me = this,
            config,
            event;

        if (Ext.isObject(ename)) {
            options = ename;
            for (ename in options) {
                if (options.hasOwnProperty(ename)) {
                    config = options[ename];
                    if (!me.eventOptionsRe.test(ename)) {
                        me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
                    }
                }
            }
        }
        else {
            ename = ename.toLowerCase();
            me.events[ename] = me.events[ename] || true;
            event = me.events[ename] || true;
            if (Ext.isBoolean(event)) {
                me.events[ename] = event = new Ext.util.Event(me, ename);
            }
            event.addListener(fn, scope, Ext.isObject(options) ? options : {});
        }
    },

    
    removeListener: function(ename, fn, scope) {
        var me = this,
            config,
            event,
            options;

        if (Ext.isObject(ename)) {
            options = ename;
            for (ename in options) {
                if (options.hasOwnProperty(ename)) {
                    config = options[ename];
                    if (!me.eventOptionsRe.test(ename)) {
                        me.removeListener(ename, config.fn || config, config.scope || options.scope);
                    }
                }
            }
        } else {
            ename = ename.toLowerCase();
            event = me.events[ename];
            if (event && event.isEvent) {
                event.removeListener(fn, scope);
            }
        }
    },

    
    clearListeners: function() {
        var events = this.events,
            event,
            key;

        for (key in events) {
            if (events.hasOwnProperty(key)) {
                event = events[key];
                if (event.isEvent) {
                    event.clearListeners();
                }
            }
        }

        this.clearManagedListeners();
    },

    purgeListeners : function() {
        if (Ext.global.console) {
            Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
        }
        return this.clearListeners.apply(this, arguments);
    },

    
    clearManagedListeners : function() {
        var managedListeners = this.managedListeners || [],
            i = 0,
            len = managedListeners.length;

        for (; i < len; i++) {
            this.removeManagedListenerItem(true, managedListeners[i]);
        }

        this.managedListeners = [];
    },
    
    
    removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
        if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
            managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
            if (!isClear) {
                Ext.Array.remove(this.managedListeners, managedListener);
            }    
        }
    },

    purgeManagedListeners : function() {
        if (Ext.global.console) {
            Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
        }
        return this.clearManagedListeners.apply(this, arguments);
    },

    
    addEvents: function(o) {
        var me = this,
            args,
            len,
            i;
            
            me.events = me.events || {};
        if (Ext.isString(o)) {
            args = arguments;
            i = args.length;
            
            while (i--) {
                me.events[args[i]] = me.events[args[i]] || true;
            }
        } else {
            Ext.applyIf(me.events, o);
        }
    },

    
    hasListener: function(ename) {
        var event = this.events[ename.toLowerCase()];
        return event && event.isEvent === true && event.listeners.length > 0;
    },

    
    suspendEvents: function(queueSuspended) {
        this.eventsSuspended = true;
        if (queueSuspended && !this.eventQueue) {
            this.eventQueue = [];
        }
    },

    
    resumeEvents: function() {
        var me = this,
            queued = me.eventQueue || [];

        me.eventsSuspended = false;
        delete me.eventQueue;

        Ext.each(queued,
        function(e) {
            me.fireEvent.apply(me, e);
        });
    },

    
    relayEvents : function(origin, events, prefix) {
        prefix = prefix || '';
        var me = this,
            len = events.length,
            i = 0,
            oldName,
            newName;

        for (; i < len; i++) {
            oldName = events[i].substr(prefix.length);
            newName = prefix + oldName;
            me.events[newName] = me.events[newName] || true;
            origin.on(oldName, me.createRelayer(newName));
        }
    },

    
    createRelayer: function(newName){
        var me = this;
        return function(){
            return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
        };
    },

    
    enableBubble: function(events) {
        var me = this;
        if (!Ext.isEmpty(events)) {
            events = Ext.isArray(events) ? events: Ext.Array.toArray(arguments);
            Ext.each(events,
            function(ename) {
                ename = ename.toLowerCase();
                var ce = me.events[ename] || true;
                if (Ext.isBoolean(ce)) {
                    ce = new Ext.util.Event(me, ename);
                    me.events[ename] = ce;
                }
                ce.bubble = true;
            });
        }
    }
}, function() {
    

    

    this.createAlias({
        on: 'addListener',
        un: 'removeListener',
        mon: 'addManagedListener',
        mun: 'removeManagedListener'
    });

    
    this.observeClass = this.observe;

    Ext.apply(Ext.util.Observable.prototype, function(){
        
        
        
        function getMethodEvent(method){
            var e = (this.methodEvents = this.methodEvents || {})[method],
                returnValue,
                v,
                cancel,
                obj = this;

            if (!e) {
                this.methodEvents[method] = e = {};
                e.originalFn = this[method];
                e.methodName = method;
                e.before = [];
                e.after = [];

                var makeCall = function(fn, scope, args){
                    if((v = fn.apply(scope || obj, args)) !== undefined){
                        if (typeof v == 'object') {
                            if(v.returnValue !== undefined){
                                returnValue = v.returnValue;
                            }else{
                                returnValue = v;
                            }
                            cancel = !!v.cancel;
                        }
                        else
                            if (v === false) {
                                cancel = true;
                            }
                            else {
                                returnValue = v;
                            }
                    }
                };

                this[method] = function(){
                    var args = Array.prototype.slice.call(arguments, 0),
                        b, i, len;
                    returnValue = v = undefined;
                    cancel = false;

                    for(i = 0, len = e.before.length; i < len; i++){
                        b = e.before[i];
                        makeCall(b.fn, b.scope, args);
                        if (cancel) {
                            return returnValue;
                        }
                    }

                    if((v = e.originalFn.apply(obj, args)) !== undefined){
                        returnValue = v;
                    }

                    for(i = 0, len = e.after.length; i < len; i++){
                        b = e.after[i];
                        makeCall(b.fn, b.scope, args);
                        if (cancel) {
                            return returnValue;
                        }
                    }
                    return returnValue;
                };
            }
            return e;
        }

        return {
            
            
            
            beforeMethod : function(method, fn, scope){
                getMethodEvent.call(this, method).before.push({
                    fn: fn,
                    scope: scope
                });
            },

            
            afterMethod : function(method, fn, scope){
                getMethodEvent.call(this, method).after.push({
                    fn: fn,
                    scope: scope
                });
            },

            removeMethodListener: function(method, fn, scope){
                var e = this.getMethodEvent(method),
                    i, len;
                for(i = 0, len = e.before.length; i < len; i++){
                    if(e.before[i].fn == fn && e.before[i].scope == scope){
                        e.before.splice(i, 1);
                        return;
                    }
                }
                for(i = 0, len = e.after.length; i < len; i++){
                    if(e.after[i].fn == fn && e.after[i].scope == scope){
                        e.after.splice(i, 1);
                        return;
                    }
                }
            },

            toggleEventLogging: function(toggle) {
                Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
                    if (Ext.isDefined(Ext.global.console)) {
                        Ext.global.console.log(en, arguments);
                    }
                });
            }
        };
    }());
});


Ext.define('Ext.util.Animate', {

    uses: ['Ext.fx.Manager', 'Ext.fx.Anim'],

    
    animate: function(animObj) {
        var me = this;
        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }
        Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(animObj)));
        return this;
    },

    
    anim: function(config) {
        if (!Ext.isObject(config)) {
            return (config) ? {} : false;
        }

        var me = this;

        if (config.stopAnimation) {
            me.stopAnimation();
        }

        Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));

        return Ext.apply({
            target: me,
            paused: true
        }, config);
    },

    
    stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),

    
    stopAnimation: function() {
        Ext.fx.Manager.stopAnimation(this.id);
        return this;
    },

    
    syncFx: function() {
        Ext.fx.Manager.setFxDefaults(this.id, {
            concurrent: true
        });
        return this;
    },

    
    sequenceFx: function() {
        Ext.fx.Manager.setFxDefaults(this.id, {
            concurrent: false
        });
        return this;
    },

    
    hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),

    
    getActiveAnimation: function() {
        return Ext.fx.Manager.getActiveAnimation(this.id);
    }
});


Ext.applyIf(Ext.core.Element.prototype, Ext.util.Animate.prototype);

Ext.define('Ext.state.Provider', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    
    prefix: 'ext-',
    
    constructor : function(config){
        config = config || {};
        var me = this;
        Ext.apply(me, config);
        
        me.addEvents("statechange");
        me.state = {};
        me.mixins.observable.constructor.call(me);
    },
    
    
    get : function(name, defaultValue){
        return typeof this.state[name] == "undefined" ?
            defaultValue : this.state[name];
    },

    
    clear : function(name){
        var me = this;
        delete me.state[name];
        me.fireEvent("statechange", me, name, null);
    },

    
    set : function(name, value){
        var me = this;
        me.state[name] = value;
        me.fireEvent("statechange", me, name, value);
    },

    
    decodeValue : function(value){

        
        
        
        
        
        
        

        var me = this,
            re = /^(a|n|d|b|s|o|e)\:(.*)$/,
            matches = re.exec(unescape(value)),
            all,
            type,
            value,
            keyValue;
            
        if(!matches || !matches[1]){
            return; 
        }
        
        type = matches[1];
        value = matches[2];
        switch (type) {
            case 'e':
                return null;
            case 'n':
                return parseFloat(value);
            case 'd':
                return new Date(Date.parse(value));
            case 'b':
                return (value == '1');
            case 'a':
                all = [];
                if(value != ''){
                    Ext.each(value.split('^'), function(val){
                        all.push(me.decodeValue(val));
                    }, me);
                }
                return all;
           case 'o':
                all = {};
                if(value != ''){
                    Ext.each(value.split('^'), function(val){
                        keyValue = val.split('=');
                        all[keyValue[0]] = me.decodeValue(keyValue[1]);
                    }, me);
                }
                return all;
           default:
                return value;
        }
    },

    
    encodeValue : function(value){
        var flat = '',
            i = 0,
            enc,
            len,
            key;
            
        if (value == null) {
            return 'e:1';    
        } else if(typeof value == 'number') {
            enc = 'n:' + value;
        } else if(typeof value == 'boolean') {
            enc = 'b:' + (value ? '1' : '0');
        } else if(Ext.isDate(value)) {
            enc = 'd:' + value.toGMTString();
        } else if(Ext.isArray(value)) {
            for (len = value.length; i < len; i++) {
                flat += this.encodeValue(value[i]);
                if (i != len - 1) {
                    flat += '^';
                }
            }
            enc = 'a:' + flat;
        } else if (typeof value == 'object') {
            for (key in value) {
                if (typeof value[key] != 'function' && value[key] !== undefined) {
                    flat += key + '=' + this.encodeValue(value[key]) + '^';
                }
            }
            enc = 'o:' + flat.substring(0, flat.length-1);
        } else {
            enc = 's:' + value;
        }
        return escape(enc);
    }
});

Ext.define('Ext.util.HashMap', {

    

    mixins: {
        observable: 'Ext.util.Observable'
    },

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

        me.addEvents(
            
            'add',
            
            'clear',
            
            'remove',
            
            'replace'
        );

        me.mixins.observable.constructor.call(me, config);
        me.clear(true);
    },

    
    getCount: function() {
        return this.length;
    },

    
    getData: function(key, value) {
        
        if (value === undefined) {
            value = key;
            key = this.getKey(value);
        }

        return [key, value];
    },

    
    getKey: function(o) {
        return o.id;
    },

    
    add: function(key, value) {
        var me = this,
            data;

        if (arguments.length === 1) {
            value = key;
            key = me.getKey(value);
        }

        if (me.containsKey(key)) {
            me.replace(key, value);
        }

        data = me.getData(key, value);
        key = data[0];
        value = data[1];
        me.map[key] = value;
        ++me.length;
        me.fireEvent('add', me, key, value);
        return value;
    },

    
    replace: function(key, value) {
        var me = this,
            map = me.map,
            old;

        if (!me.containsKey(key)) {
            me.add(key, value);
        }
        old = map[key];
        map[key] = value;
        me.fireEvent('replace', me, key, value, old);
        return value;
    },

    
    remove: function(o) {
        var key = this.findKey(o);
        if (key !== undefined) {
            return this.removeAtKey(key);
        }
        return false;
    },

    
    removeAtKey: function(key) {
        var me = this,
            value;

        if (me.containsKey(key)) {
            value = me.map[key];
            delete me.map[key];
            --me.length;
            me.fireEvent('remove', me, key, value);
            return true;
        }
        return false;
    },

    
    get: function(key) {
        return this.map[key];
    },

    
    clear: function( initial) {
        var me = this;
        me.map = {};
        me.length = 0;
        if (initial !== true) {
            me.fireEvent('clear', me);
        }
        return me;
    },

    
    containsKey: function(key) {
        return this.map[key] !== undefined;
    },

    
    contains: function(value) {
        return this.containsKey(this.findKey(value));
    },

    
    getKeys: function() {
        return this.getArray(true);
    },

    
    getValues: function() {
        return this.getArray(false);
    },

    
    getArray: function(isKey) {
        var arr = [],
            key,
            map = this.map;
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                arr.push(isKey ? key: map[key]);
            }
        }
        return arr;
    },

    
    each: function(fn, scope) {
        
        var items = Ext.apply({}, this.map),
            key,
            length = this.length;

        scope = scope || this;
        for (key in items) {
            if (items.hasOwnProperty(key)) {
                if (fn.call(scope, key, items[key], length) === false) {
                    break;
                }
            }
        }
        return this;
    },

    
    clone: function() {
        var hash = new this.self(),
            map = this.map,
            key;

        hash.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                hash.add(key, map[key]);
            }
        }
        hash.resumeEvents();
        return hash;
    },

    
    findKey: function(value) {
        var key,
            map = this.map;

        for (key in map) {
            if (map.hasOwnProperty(key) && map[key] === value) {
                return key;
            }
        }
        return undefined;
    }
});



Ext.define('Ext.Template', {

    

    requires: ['Ext.core.DomHelper', 'Ext.util.Format'],

    statics: {
        
        from: function(el, config) {
            el = Ext.getDom(el);
            return new this(el.value || el.innerHTML, config || '');
        }
    },

    

    constructor: function(html) {
        var me = this,
            args = arguments,
            buffer = [],
            i = 0,
            length = args.length,
            value;

        me.initialConfig = {};

        if (length > 1) {
            for (; i < length; i++) {
                value = args[i];
                if (typeof value == 'object') {
                    Ext.apply(me.initialConfig, value);
                    Ext.apply(me, value);
                } else {
                    buffer.push(value);
                }
            }
            html = buffer.join('');
        } else {
            if (Ext.isArray(html)) {
                buffer.push(html.join(''));
            } else {
                buffer.push(html);
            }
        }

        
        me.html = buffer.join('');

        if (me.compiled) {
            me.compile();
        }
    },
    isTemplate: true,
    
    disableFormats: false,

    re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
    
    applyTemplate: function(values) {
        var me = this,
            useFormat = me.disableFormats !== true,
            fm = Ext.util.Format,
            tpl = me;

        if (me.compiled) {
            return me.compiled(values);
        }
        function fn(m, name, format, args) {
            if (format && useFormat) {
                if (args) {
                    args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')());
                } else {
                    args = [values[name]];
                }
                if (format.substr(0, 5) == "this.") {
                    return tpl[format.substr(5)].apply(tpl, args);
                }
                else {
                    return fm[format].apply(fm, args);
                }
            }
            else {
                return values[name] !== undefined ? values[name] : "";
            }
        }
        return me.html.replace(me.re, fn);
    },

    
    set: function(html, compile) {
        var me = this;
        me.html = html;
        me.compiled = null;
        return compile ? me.compile() : me;
    },

    compileARe: /\\/g,
    compileBRe: /(\r\n|\n)/g,
    compileCRe: /'/g,
    /**
     * Compiles the template into an internal function, eliminating the RegEx overhead.
     * @return {Ext.Template} this
     * @hide repeat doc
     */
    compile: function() {
        var me = this,
            fm = Ext.util.Format,
            useFormat = me.disableFormats !== true,
            body, bodyReturn;

        function fn(m, name, format, args) {
            if (format && useFormat) {
                args = args ? ',' + args: "";
                if (format.substr(0, 5) != "this.") {
                    format = "fm." + format + '(';
                }
                else {
                    format = 'this.' + format.substr(5) + '(';
                }
            }
            else {
                args = '';
                format = "(values['" + name + "'] == undefined ? '' : ";
            }
            return "'," + format + "values['" + name + "']" + args + ") ,'";
        }

        bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
        body = "this.compiled = function(values){ return ['" + bodyReturn + "'].join('');};";
        eval(body);
        return me;
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
     * @param {Mixed} el The context element
     * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
     * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined)
     * @return {HTMLElement/Ext.core.Element} The new node or Element
     */
    insertFirst: function(el, values, returnElement) {
        return this.doInsert('afterBegin', el, values, returnElement);
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) before el.
     * @param {Mixed} el The context element
     * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
     * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined)
     * @return {HTMLElement/Ext.core.Element} The new node or Element
     */
    insertBefore: function(el, values, returnElement) {
        return this.doInsert('beforeBegin', el, values, returnElement);
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) after el.
     * @param {Mixed} el The context element
     * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
     * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined)
     * @return {HTMLElement/Ext.core.Element} The new node or Element
     */
    insertAfter: function(el, values, returnElement) {
        return this.doInsert('afterEnd', el, values, returnElement);
    },

    /**
     * Applies the supplied <code>values</code> to the template and appends
     * the new node(s) to the specified <code>el</code>.
     * <p>For example usage {@link #Template see the constructor}.</p>
     * @param {Mixed} el The context element
     * @param {Object/Array} values
     * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
     * or an object (i.e. <code>{foo: 'bar'}</code>).
     * @param {Boolean} returnElement (optional) true to return an Ext.core.Element (defaults to undefined)
     * @return {HTMLElement/Ext.core.Element} The new node or Element
     */
    append: function(el, values, returnElement) {
        return this.doInsert('beforeEnd', el, values, returnElement);
    },

    doInsert: function(where, el, values, returnEl) {
        el = Ext.getDom(el);
        var newNode = Ext.core.DomHelper.insertHtml(where, el, this.applyTemplate(values));
        return returnEl ? Ext.get(newNode, true) : newNode;
    },

    /**
     * Applies the supplied values to the template and overwrites the content of el with the new node(s).
     * @param {Mixed} el The context element
     * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
     * @param {Boolean} returnElement (optional) true to return a Ext.core.Element (defaults to undefined)
     * @return {HTMLElement/Ext.core.Element} The new node or Element
     */
    overwrite: function(el, values, returnElement) {
        el = Ext.getDom(el);
        el.innerHTML = this.applyTemplate(values);
        return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
    }
}, function() {

    /**
     * Alias for {@link #applyTemplate}
     * Returns an HTML fragment of this template with the specified <code>values</code> applied.
     * @param {Object/Array} values
     * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
     * or an object (i.e. <code>{foo: 'bar'}</code>).
     * @return {String} The HTML fragment
     * @member Ext.Template
     * @method apply
     */
    this.createAlias('apply', 'applyTemplate');
});

/**
 * @class Ext.ComponentQuery
 * @extends Object
 *
 * Provides searching of Components within Ext.ComponentManager (globally) or a specific
 * Ext.container.Container on the document with a similar syntax to a CSS selector.
 *
 * Components can be retrieved by using their {@link Ext.Component xtype} with an optional . prefix
<ul>
    <li>component or .component</li>
    <li>gridpanel or .gridpanel</li>
</ul>
 *
 * An itemId or id must be prefixed with a #
<ul>
    <li>#myContainer</li>
</ul>
 *
 *
 * Attributes must be wrapped in brackets
<ul>
    <li>component[autoScroll]</li>
    <li>panel[title="Test"]</li>
</ul>
 *
 * Member expressions from candidate Components may be tested. If the expression returns a <i>truthy</i> value,
 * the candidate Component will be included in the query:<pre><code>
var disabledFields = myFormPanel.query("{isDisabled()}");
</code></pre>
 *
 * Pseudo classes may be used to filter results in the same way as in {@link Ext.DomQuery DomQuery}:<code><pre>
// Function receives array and returns a filtered array.
Ext.ComponentQuery.pseudos.invalid = function(items) {
    var i = 0, l = items.length, c, result = [];
    for (; i < l; i++) {
        if (!(c = items[i]).isValid()) {
            result.push(c);
        }
    }
    return result;
};

var invalidFields = myFormPanel.query('field:invalid');
if (invalidFields.length) {
    invalidFields[0].getEl().scrollIntoView(myFormPanel.body);
    for (var i = 0, l = invalidFields.length; i < l; i++) {
        invalidFields[i].getEl().frame("red");
    }
}
</pre></code>
 * <p>
 * Default pseudos include:<br />
 * - not
 * </p>
 *
 * Queries return an array of components.
 * Here are some example queries.
<pre><code>
    // retrieve all Ext.Panels in the document by xtype
    var panelsArray = Ext.ComponentQuery.query('panel');

    // retrieve all Ext.Panels within the container with an id myCt
    var panelsWithinmyCt = Ext.ComponentQuery.query('#myCt panel');

    // retrieve all direct children which are Ext.Panels within myCt
    var directChildPanel = Ext.ComponentQuery.query('#myCt > panel');

    // retrieve all gridpanels and listviews
    var gridsAndLists = Ext.ComponentQuery.query('gridpanel, listview');
</code></pre>

For easy access to queries based from a particular Container see the {@link Ext.container.Container#query},
{@link Ext.container.Container#down} and {@link Ext.container.Container#child} methods. Also see
{@link Ext.Component#up}.
 * @singleton
 */
Ext.define('Ext.ComponentQuery', {
    singleton: true,
    uses: ['Ext.ComponentManager']
}, function() {

    var cq = this,

        // A function source code pattern with a placeholder which accepts an expression which yields a truth value when applied
        // as a member on each item in the passed array.
        filterFnPattern = [
            'var r = [],',
                'i = 0,',
                'it = items,',
                'l = it.length,',
                'c;',
            'for (; i < l; i++) {',
                'c = it[i];',
                'if (c.{0}) {',
                   'r.push(c);',
                '}',
            '}',
            'return r;'
        ].join(''),

        filterItems = function(items, operation) {
            // Argument list for the operation is [ itemsArray, operationArg1, operationArg2...]
            // The operation's method loops over each item in the candidate array and
            // returns an array of items which match its criteria
            return operation.method.apply(this, [ items ].concat(operation.args));
        },

        getItems = function(items, mode) {
            var result = [],
                i = 0,
                length = items.length,
                candidate,
                deep = mode !== '>';
                
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getRefItems) {
                    result = result.concat(candidate.getRefItems(deep));
                }
            }
            return result;
        },

        getAncestors = function(items) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                while (!!(candidate = (candidate.ownerCt || candidate.floatParent))) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which match the passed xtype
        filterByXType = function(items, xtype, shallow) {
            if (xtype === '*') {
                return items.slice();
            }
            else {
                var result = [],
                    i = 0,
                    length = items.length,
                    candidate;
                for (; i < length; i++) {
                    candidate = items[i];
                    if (candidate.isXType(xtype, shallow)) {
                        result.push(candidate);
                    }
                }
                return result;
            }
        },

        // Filters the passed candidate array and returns only items which have the passed className
        filterByClassName = function(items, className) {
            var EA = Ext.Array,
                result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.el ? candidate.el.hasCls(className) : EA.contains(candidate.initCls(), className)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which have the specified property match
        filterByAttribute = function(items, property, operator, value) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (!value ? !!candidate[property] : (String(candidate[property]) === value)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which have the specified itemId or id
        filterById = function(items, id) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getItemId() === id) {
                    result.push(candidate);
                }
            }
            return result;
        },

        // Filters the passed candidate array and returns only items which the named pseudo class matcher filters in
        filterByPseudo = function(items, name, value) {
            return cq.pseudos[name](items, value);
        },

        // Determines leading mode
        // > for direct child, and ^ to switch to ownerCt axis
        modeRe = /^(\s?([>\^])\s?|\s|$)/,

        // Matches a token with possibly (true|false) appended for the "shallow" parameter
        tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,

        matchers = [{
            // Checks for .xtype with possibly (true|false) appended for the "shallow" parameter
            re: /^\.([\w\-]+)(?:\((true|false)\))?/,
            method: filterByXType
        },{
            // checks for [attribute=value]
            re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/,
            method: filterByAttribute
        }, {
            // checks for #cmpItemId
            re: /^#([\w\-]+)/,
            method: filterById
        }, {
            // checks for :<pseudo_class>(<selector>)
            re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
            method: filterByPseudo
        }, {
            // checks for {<member_expression>}
            re: /^(?:\{([^\}]+)\})/,
            method: filterFnPattern
        }];

    /**
     * @class Ext.ComponentQuery.Query
     * @extends Object
     * @private
     */
    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },

        /**
         * @private
         * Executes this Query upon the selected root.
         * The root provides the initial source of candidate Component matches which are progressively
         * filtered by iterating through this Query's operations cache.
         * If no root is provided, all registered Components are searched via the ComponentManager.
         * root may be a Container who's descendant Components are filtered
         * root may be a Component with an implementation of getRefItems which provides some nested Components such as the
         * docked items within a Panel.
         * root may be an array of candidate Components to filter using this Query.
         */
        execute : function(root) {
            var operations = this.operations,
                i = 0,
                length = operations.length,
                operation,
                workingItems;

            // no root, use all Components in the document
            if (!root) {
                workingItems = Ext.ComponentManager.all.getArray();
            }
            // Root is a candidate Array
            else if (Ext.isArray(root)) {
                workingItems = root;
            }

            // We are going to loop over our operations and take care of them
            // one by one.
            for (; i < length; i++) {
                operation = operations[i];

                // The mode operation requires some custom handling.
                // All other operations essentially filter down our current
                // working items, while mode replaces our current working
                // items by getting children from each one of our current
                // working items. The type of mode determines the type of
                // children we get. (e.g. > only gets direct children)
                if (operation.mode === '^') {
                    workingItems = getAncestors(workingItems || [root]);
                }
                else if (operation.mode) {
                    workingItems = getItems(workingItems || [root], operation.mode);
                }
                else {
                    workingItems = filterItems(workingItems || getItems([root]), operation);
                }

                // If this is the last operation, it means our current working
                // items are the final matched items. Thus return them!
                if (i === length -1) {
                    return workingItems;
                }
            }
            return [];
        },

        is: function(component) {
            var operations = this.operations,
                components = Ext.isArray(component) ? component : [component],
                originalLength = components.length,
                lastOperation = operations[operations.length-1],
                ln, i;

            components = filterItems(components, lastOperation);
            if (components.length === originalLength) {
                if (operations.length > 1) {
                    for (i = 0, ln = components.length; i < ln; i++) {
                        if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
                            return false;
                        }
                    }
                }
                return true;
            }
            return false;
        }
    });

    Ext.apply(this, {

        // private cache of selectors and matching ComponentQuery.Query objects
        cache: {},

        // private cache of pseudo class filter functions
        pseudos: {
            not: function(components, selector){
                var CQ = Ext.ComponentQuery,
                    i = 0,
                    length = components.length,
                    results = [],
                    index = -1,
                    component;
                
                for(; i < length; ++i) {
                    component = components[i];
                    if (!CQ.is(component, selector)) {
                        results[++index] = component;
                    }
                }
                return results;
            }
        },

        /**
         * <p>Returns an array of matched Components from within the passed root object.</p>
         * <p>This method filters returned Components in a similar way to how CSS selector based DOM
         * queries work using a textual selector string.</p>
         * <p>See class summary for details.</p>
         * @param selector The selector string to filter returned Components
         * @param root <p>The Container within which to perform the query. If omitted, all Components
         * within the document are included in the search.</p>
         * <p>This parameter may also be an array of Components to filter according to the selector.</p>
         * @returns {Array} The matched Components.
         * @member Ext.ComponentQuery
         */
        query: function(selector, root) {
            var selectors = selector.split(','),
                length = selectors.length,
                i = 0,
                results = [],
                noDupResults = [], 
                dupMatcher = {}, 
                query, resultsLn, cmp;

            for (; i < length; i++) {
                selector = Ext.String.trim(selectors[i]);
                query = this.cache[selector];
                if (!query) {
                    this.cache[selector] = query = this.parse(selector);
                }
                results = results.concat(query.execute(root));
            }

            // multiple selectors, potential to find duplicates
            // lets filter them out.
            if (length > 1) {
                resultsLn = results.length;
                for (i = 0; i < resultsLn; i++) {
                    cmp = results[i];
                    if (!dupMatcher[cmp.id]) {
                        noDupResults.push(cmp);
                        dupMatcher[cmp.id] = true;
                    }
                }
                results = noDupResults;
            }
            return results;
        },

        /**
         * Tests whether the passed Component matches the selector string.
         * @param component The Component to test
         * @param selector The selector string to test against.
         * @return {Boolean} True if the Component matches the selector.
         * @member Ext.ComponentQuery
         */
        is: function(component, selector) {
            if (!selector) {
                return true;
            }
            var query = this.cache[selector];
            if (!query) {
                this.cache[selector] = query = this.parse(selector);
            }
            return query.is(component);
        },

        parse: function(selector) {
            var operations = [],
                length = matchers.length,
                lastSelector,
                tokenMatch,
                matchedChar,
                modeMatch,
                selectorMatch,
                i, matcher, method;

            // We are going to parse the beginning of the selector over and
            // over again, slicing off the selector any portions we converted into an
            // operation, until it is an empty string.
            while (selector && lastSelector !== selector) {
                lastSelector = selector;

                // First we check if we are dealing with a token like #, * or an xtype
                tokenMatch = selector.match(tokenRe);

                if (tokenMatch) {
                    matchedChar = tokenMatch[1];

                    // If the token is prefixed with a # we push a filterById operation to our stack
                    if (matchedChar === '#') {
                        operations.push({
                            method: filterById,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    // If the token is prefixed with a . we push a filterByClassName operation to our stack
                    // FIXME: Not enabled yet. just needs \. adding to the tokenRe prefix
                    else if (matchedChar === '.') {
                        operations.push({
                            method: filterByClassName,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    // If the token is a * or an xtype string, we push a filterByXType
                    // operation to the stack.
                    else {
                        operations.push({
                            method: filterByXType,
                            args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
                        });
                    }

                    // Now we slice of the part we just converted into an operation
                    selector = selector.replace(tokenMatch[0], '');
                }

                // If the next part of the query is not a space or > or ^, it means we
                // are going to check for more things that our current selection
                // has to comply to.
                while (!(modeMatch = selector.match(modeRe))) {
                    // Lets loop over each type of matcher and execute it
                    // on our current selector.
                    for (i = 0; selector && i < length; i++) {
                        matcher = matchers[i];
                        selectorMatch = selector.match(matcher.re);
                        method = matcher.method;

                        // If we have a match, add an operation with the method
                        // associated with this matcher, and pass the regular
                        // expression matches are arguments to the operation.
                        if (selectorMatch) {
                            operations.push({
                                method: Ext.isString(matcher.method)
                                    // Turn a string method into a function by formatting the string with our selector matche expression
                                    // A new method is created for different match expressions, eg {id=='textfield-1024'}
                                    // Every expression may be different in different selectors.
                                    ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
                                    : matcher.method,
                                args: selectorMatch.slice(1)
                            });
                            selector = selector.replace(selectorMatch[0], '');
                            break; // Break on match
                        }
                        // Exhausted all matches: It's an error
                        if (i === (length - 1)) {
                            Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
                        }
                    }
                }

                // Now we are going to check for a mode change. This means a space
                // or a > to determine if we are going to select all the children
                // of the currently matched items, or a ^ if we are going to use the
                // ownerCt axis as the candidate source.
                if (modeMatch[1]) { // Assignment, and test for truthiness!
                    operations.push({
                        mode: modeMatch[2]||modeMatch[1]
                    });
                    selector = selector.replace(modeMatch[0], '');
                }
            }

            //  Now that we have all our operations in an array, we are going
            // to create a new Query using these operations.
            return new cq.Query({
                operations: operations
            });
        }
    });
});
/**
 * @class Ext.util.Filter
 * @extends Object
 * <p>Represents a filter that can be applied to a {@link Ext.util.MixedCollection MixedCollection}. Can either simply
 * filter on a property/value pair or pass in a filter function with custom logic. Filters are always used in the context
 * of MixedCollections, though {@link Ext.data.Store Store}s frequently create them when filtering and searching on their
 * records. Example usage:</p>
<pre><code>
//set up a fictional MixedCollection containing a few people to filter on
var allNames = new Ext.util.MixedCollection();
allNames.addAll([
    {id: 1, name: 'Ed',    age: 25},
    {id: 2, name: 'Jamie', age: 37},
    {id: 3, name: 'Abe',   age: 32},
    {id: 4, name: 'Aaron', age: 26},
    {id: 5, name: 'David', age: 32}
]);

var ageFilter = new Ext.util.Filter({
    property: 'age',
    value   : 32
});

var longNameFilter = new Ext.util.Filter({
    filterFn: function(item) {
        return item.name.length > 4;
    }
});

//a new MixedCollection with the 3 names longer than 4 characters
var longNames = allNames.filter(longNameFilter);

//a new MixedCollection with the 2 people of age 24:
var youngFolk = allNames.filter(ageFilter);
</code></pre>
 * @constructor
 * @param {Object} config Config object
 */
Ext.define('Ext.util.Filter', {

    /* Begin Definitions */

    /* End Definitions */
    /**
     * @cfg {String} property The property to filter on. Required unless a {@link #filter} is passed
     */
    
    /**
     * @cfg {Function} filterFn A custom filter function which is passed each item in the {@link Ext.util.MixedCollection} 
     * in turn. Should return true to accept each item or false to reject it
     */
    
    /**
     * @cfg {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
     */
    anyMatch: false,
    
    /**
     * @cfg {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
     * Ignored if anyMatch is true.
     */
    exactMatch: false,
    
    /**
     * @cfg {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
     */
    caseSensitive: false,
    
    /**
     * @cfg {String} root Optional root property. This is mostly useful when filtering a Store, in which case we set the
     * root to 'data' to make the filter pull the {@link #property} out of the data object of each item
     */
    
    constructor: function(config) {
        Ext.apply(this, config);
        
        //we're aliasing filter to filterFn mostly for API cleanliness reasons, despite the fact it dirties the code here.
        //Ext.util.Sorter takes a sorterFn property but allows .sort to be called - we do the same here
        this.filter = this.filter || this.filterFn;
        
        if (this.filter == undefined) {
            if (this.property == undefined || this.value == undefined) {
                // Commented this out temporarily because it stops us using string ids in models. TODO: Remove this once
                // Model has been updated to allow string ids
                
                // Ext.Error.raise("A Filter requires either a property or a filterFn to be set");
            } else {
                this.filter = this.createFilterFn();
            }
            
            this.filterFn = this.filter;
        }
    },
    
    /**
     * @private
     * Creates a filter function for the configured property/value/anyMatch/caseSensitive options for this Filter
     */
    createFilterFn: function() {
        var me       = this,
            matcher  = me.createValueMatcher(),
            property = me.property;
        
        return function(item) {
            return matcher.test(me.getRoot.call(me, item)[property]);
        };
    },
    
    /**
     * @private
     * Returns the root property of the given item, based on the configured {@link #root} property
     * @param {Object} item The item
     * @return {Object} The root property of the object
     */
    getRoot: function(item) {
        return this.root == undefined ? item : item[this.root];
    },
    
    /**
     * @private
     * Returns a regular expression based on the given value and matching options
     */
    createValueMatcher : function() {
        var me            = this,
            value         = me.value,
            anyMatch      = me.anyMatch,
            exactMatch    = me.exactMatch,
            caseSensitive = me.caseSensitive,
            escapeRe      = Ext.String.escapeRegex;
        
        if (!value.exec) { // not a regex
            value = String(value);

            if (anyMatch === true) {
                value = escapeRe(value);
            } else {
                value = '^' + escapeRe(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
         }
         
         return value;
    }
});
/**
 * @class Ext.util.Sorter
 * @extends Object
 * Represents a single sorter that can be applied to a Store
 */
Ext.define('Ext.util.Sorter', {

    /**
     * @cfg {String} property The property to sort by. Required unless {@link #sorter} is provided
     */
    
    /**
     * @cfg {Function} sorterFn A specific sorter function to execute. Can be passed instead of {@link #property}
     */
    
    /**
     * @cfg {String} root Optional root property. This is mostly useful when sorting a Store, in which case we set the
     * root to 'data' to make the filter pull the {@link #property} out of the data object of each item
     */
    
    /**
     * @cfg {Function} transform A function that will be run on each value before
     * it is compared in the sorter. The function will receive a single argument,
     * the value.
     */
    
    /**
     * @cfg {String} direction The direction to sort by. Defaults to ASC
     */
    direction: "ASC",
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config);
        
        if (me.property == undefined && me.sorterFn == undefined) {
            Ext.Error.raise("A Sorter requires either a property or a sorter function");
        }
        
        me.updateSortFunction();
    },
    
    /**
     * @private
     * Creates and returns a function which sorts an array by the given property and direction
     * @return {Function} A function which sorts by the property/direction combination provided
     */
    createSortFunction: function(sorterFn) {
        var me        = this,
            property  = me.property,
            direction = me.direction || "ASC",
            modifier  = direction.toUpperCase() == "DESC" ? -1 : 1;
        
        //create a comparison function. Takes 2 objects, returns 1 if object 1 is greater,
        //-1 if object 2 is greater or 0 if they are equal
        return function(o1, o2) {
            return modifier * sorterFn.call(me, o1, o2);
        };
    },
    
    /**
     * @private
     * Basic default sorter function that just compares the defined property of each object
     */
    defaultSorterFn: function(o1, o2) {
        var me = this,
            transform = me.transform,
            v1 = me.getRoot(o1)[me.property],
            v2 = me.getRoot(o2)[me.property];
            
        if (transform) {
            v1 = transform(v1);
            v2 = transform(v2);
        }

        return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
    },
    
    /**
     * @private
     * Returns the root property of the given item, based on the configured {@link #root} property
     * @param {Object} item The item
     * @return {Object} The root property of the object
     */
    getRoot: function(item) {
        return this.root == undefined ? item : item[this.root];
    },
    
    // @TODO: Add docs for these three methods
    setDirection: function(direction) {
        var me = this;
        me.direction = direction;
        me.updateSortFunction();
    },
    
    toggle: function() {
        var me = this;
        me.direction = Ext.String.toggle(me.direction, "ASC", "DESC");
        me.updateSortFunction();
    },
    
    updateSortFunction: function() {
        var me = this;
        me.sort = me.createSortFunction(me.sorterFn || me.defaultSorterFn);
    }
});
/**
 * @class Ext.ElementLoader
 * A class used to load remote content to an Element. Sample usage:
 * <pre><code>
Ext.get('el').load({
    url: 'myPage.php',
    scripts: true,
    params: {
        id: 1
    }
});
 * </code></pre>
 * <p>
 * In general this class will not be instanced directly, rather the {@link Ext.core.Element#load} method
 * will be used.
 * </p>
 */
Ext.define('Ext.ElementLoader', {

    /* Begin Definitions */

    mixins: {
        observable: 'Ext.util.Observable'
    },

    uses: [
        'Ext.data.Connection',
        'Ext.Ajax'
    ],
    
    statics: {
        Renderer: {
            Html: function(loader, response, active){
                loader.getTarget().update(response.responseText, active.scripts === true);
                return true;
            }
        }     
    },

    /* End Definitions */

    /**
     * @cfg {String} url The url to retrieve the content from. Defaults to <tt>null</tt>.
     */
    url: null,

    /**
     * @cfg {Object} params Any params to be attached to the Ajax request. These parameters will
     * be overridden by any params in the load options. Defaults to <tt>null</tt>.
     */
    params: null,

    /**
     * @cfg {Object} baseParams Params that will be attached to every request. These parameters
     * will not be overridden by any params in the load options. Defaults to <tt>null</tt>.
     */
    baseParams: null,

    /**
     * @cfg {Boolean/Object} autoLoad True to have the loader make a request as soon as it is created. Defaults to <tt>false</tt>.
     * This argument can also be a set of options that will be passed to {@link #load} is called.
     */
    autoLoad: false,

    /**
     * @cfg {Mixed} target The target element for the loader. It can be the DOM element, the id or an Ext.Element.
     */
    target: null,

    /**
     * @cfg {Mixed} loadMask True or a string to show when the element is loading.
     */
    loadMask: false,

    /**
     * @cfg {Object} ajaxOptions Any additional options to be passed to the request, for example timeout or headers. Defaults to <tt>null</tt>.
     */
    ajaxOptions: null,
    
    /**
     * @cfg {Boolean} scripts True to parse any inline script tags in the response.
     */
    scripts: false,

    /**
     * @cfg {Function} success A function to be called when a load request is successful.
     */

    /**
     * @cfg {Function} failure A function to be called when a load request fails.
     */

    /**
     * @cfg {Object} scope The scope to execute the {@link #success} and {@link #failure} functions in.
     */
    
    /**
     * @cfg {Function} renderer A custom function to render the content to the element. The passed parameters
     * are
     * <ul>
     * <li>The loader</li>
     * <li>The response</li>
     * <li>The active request</li>
     * </ul>
     */

    isLoader: true,

    constructor: function(config) {
        var me = this,
            autoLoad;
        
        config = config || {};
        Ext.apply(me, config);
        me.setTarget(me.target);
        me.addEvents(
            /**
             * @event beforeload
             * Fires before a load request is made to the server.
             * Returning false from an event listener can prevent the load
             * from occurring.
             * @param {Ext.ElementLoader} this
             * @param {Object} options The options passed to the request
             */
            'beforeload',

            /**
             * @event exception
             * Fires after an unsuccessful load.
             * @param {Ext.ElementLoader} this
             * @param {Object} response The response from the server
             * @param {Object} options The options passed to the request
             */
            'exception',

            /**
             * @event exception
             * Fires after a successful load.
             * @param {Ext.ElementLoader} this
             * @param {Object} response The response from the server
             * @param {Object} options The options passed to the request
             */
            'load'
        );

        // don't pass config because we have already applied it.
        me.mixins.observable.constructor.call(me);

        if (me.autoLoad) {
            autoLoad = me.autoLoad;
            if (autoLoad === true) {
                autoLoad = {};
            }
            me.load(autoLoad);
        }
    },

    /**
     * Set an {Ext.Element} as the target of this loader. Note that if the target is changed,
     * any active requests will be aborted.
     * @param {Mixed} target The element
     */
    setTarget: function(target){
        var me = this;
        target = Ext.get(target);
        if (me.target && me.target != target) {
            me.abort();
        }
        me.target = target;
    },

    /**
     * Get the target of this loader.
     * @return {Ext.Component} target The target, null if none exists.
     */
    getTarget: function(){
        return this.target || null;
    },

    /**
     * Aborts the active load request
     */
    abort: function(){
        var active = this.active;
        if (active !== undefined) {
            Ext.Ajax.abort(active.request);
            if (active.mask) {
                this.removeMask();
            }
            delete this.active;
        }
    },
    
    /**
     * Remove the mask on the target
     * @private
     */
    removeMask: function(){
        this.target.unmask();
    },
    
    /**
     * Add the mask on the target
     * @private
     * @param {Mixed} mask The mask configuration
     */
    addMask: function(mask){
        this.target.mask(mask === true ? null : mask);
    },

    /**
     * Load new data from the server.
     * @param {Object} options The options for the request. They can be any configuration option that can be specified for
     * the class, with the exception of the target option. Note that any options passed to the method will override any
     * class defaults.
     */
    load: function(options) {
        if (!this.target) {
            Ext.Error.raise('A valid target is required when loading content');
        }

        options = Ext.apply({}, options);

        var me = this,
            target = me.target,
            mask = Ext.isDefined(options.loadMask) ? options.loadMask : me.loadMask,
            params = Ext.apply({}, options.params),
            ajaxOptions = Ext.apply({}, options.ajaxOptions),
            callback = options.callback || me.callback,
            scope = options.scope || me.scope || me,
            request;

        Ext.applyIf(ajaxOptions, me.ajaxOptions);
        Ext.applyIf(options, ajaxOptions);

        Ext.applyIf(params, me.params);
        Ext.apply(params, me.baseParams);

        Ext.applyIf(options, {
            url: me.url
        });

        if (!options.url) {
            Ext.Error.raise('You must specify the URL from which content should be loaded');
        }

        Ext.apply(options, {
            scope: me,
            params: params,
            callback: me.onComplete
        });

        if (me.fireEvent('beforeload', me, options) === false) {
            return;
        }

        if (mask) {
            me.addMask(mask);
        }

        request = Ext.Ajax.request(options);
        me.active = {
            request: request,
            options: options,
            mask: mask,
            scope: scope,
            callback: callback,
            success: options.success || me.success,
            failure: options.failure || me.failure,
            renderer: options.renderer || me.renderer,
            scripts: Ext.isDefined(options.scripts) ? options.scripts : me.scripts
        };
        me.setOptions(me.active, options);
    },
    
    /**
     * Set any additional options on the active request
     * @private
     * @param {Object} active The active request
     * @param {Object} options The initial options
     */
    setOptions: Ext.emptyFn,

    /**
     * Parse the response after the request completes
     * @private
     * @param {Object} options Ajax options
     * @param {Boolean} success Success status of the request
     * @param {Object} response The response object
     */
    onComplete: function(options, success, response) {
        var me = this,
            active = me.active,
            scope = active.scope,
            renderer = me.getRenderer(active.renderer);


        if (success) {
            success = renderer.call(me, me, response, active);
        }

        if (success) {
            Ext.callback(active.success, scope, [me, response, options]);
            me.fireEvent('load', me, response, options);
        } else {
            Ext.callback(active.failure, scope, [me, response, options]);
            me.fireEvent('exception', me, response, options);
        }
        Ext.callback(active.callback, scope, [me, success, response, options]);

        if (active.mask) {
            me.removeMask();
        }

        delete me.active;
    },

    /**
     * Gets the renderer to use
     * @private
     * @param {String/Function} renderer The renderer to use
     * @return {Function} A rendering function to use.
     */
    getRenderer: function(renderer){
        if (Ext.isFunction(renderer)) {
            return renderer;
        }
        return this.statics().Renderer.Html;
    },
    
    /**
     * Automatically refreshes the content over a specified period.
     * @param {Number} interval The interval to refresh in ms.
     * @param {Object} options (optional) The options to pass to the load method. See {@link #load}
     */
    startAutoRefresh: function(interval, options){
        var me = this;
        me.stopAutoRefresh();
        me.autoRefresh = setInterval(function(){
            me.load(options);
        }, interval);
    },
    
    /**
     * Clears any auto refresh. See {@link #startAutoRefresh}.
     */
    stopAutoRefresh: function(){
        clearInterval(this.autoRefresh);
        delete this.autoRefresh;
    },
    
    /**
     * Checks whether the loader is automatically refreshing. See {@link #startAutoRefresh}.
     * @return {Boolean} True if the loader is automatically refreshing
     */
    isAutoRefreshing: function(){
        return Ext.isDefined(this.autoRefresh);
    },

    /**
     * Destroys the loader. Any active requests will be aborted.
     */
    destroy: function(){
        var me = this;
        me.stopAutoRefresh();
        delete me.target;
        me.abort();
        me.clearListeners();
    }
});

/**
 * @class Ext.layout.Layout
 * @extends Object
 * @private
 * Base Layout class - extended by ComponentLayout and ContainerLayout
 */

Ext.define('Ext.layout.Layout', {

    /* Begin Definitions */

    /* End Definitions */

    isLayout: true,
    initialized: false,

    statics: {
        create: function(layout, defaultType) {
            var type;
            if (layout instanceof Ext.layout.Layout) {
                return Ext.createByAlias('layout.' + layout);
            } else {
                if (Ext.isObject(layout)) {
                    type = layout.type;
                }
                else {
                    type = layout || defaultType;
                    layout = {};
                }
                return Ext.createByAlias('layout.' + type, layout || {});
            }
        }
    },

    constructor : function(config) {
        this.id = Ext.id(null, this.type + '-');
        Ext.apply(this, config);
    },

    /**
     * @private
     */
    layout : function() {
        var me = this;
        me.layoutBusy = true;
        me.initLayout();

        if (me.beforeLayout.apply(me, arguments) !== false) {
            me.layoutCancelled = false;
            me.onLayout.apply(me, arguments);
            me.childrenChanged = false;
            me.owner.needsLayout = false;
            me.layoutBusy = false;
            me.afterLayout.apply(me, arguments);
        }
        else {
            me.layoutCancelled = true;
        }
        me.layoutBusy = false;
        me.doOwnerCtLayouts();
    },

    beforeLayout : function() {
        this.renderItems(this.getLayoutItems(), this.getRenderTarget());
        return true;
    },

    /**
     * @private
     * Iterates over all passed items, ensuring they are rendered.  If the items are already rendered,
     * also determines if the items are in the proper place dom.
     */
    renderItems : function(items, target) {
        var ln = items.length,
            i = 0,
            item;

        for (; i < ln; i++) {
            item = items[i];
            if (item && !item.rendered) {
                this.renderItem(item, target, i);
            }
            else if (!this.isValidParent(item, target, i)) {
                this.moveItem(item, target, i);
            }
        }
    },

    // @private - Validates item is in the proper place in the dom.
    isValidParent : function(item, target, position) {
        var dom = item.el ? item.el.dom : Ext.getDom(item);
        if (dom && target && target.dom) {
            if (Ext.isNumber(position) && dom !== target.dom.childNodes[position]) {
                return false;
            }
            return (dom.parentNode == (target.dom || target));
        }
        return false;
    },

    /**
     * @private
     * Renders the given Component into the target Element.
     * @param {Ext.Component} item The Component to render
     * @param {Ext.core.Element} target The target Element
     * @param {Number} position The position within the target to render the item to
     */
    renderItem : function(item, target, position) {
        if (!item.rendered) {
            item.render(target, position);
            this.configureItem(item);
            this.childrenChanged = true;
        }
    },

    /**
     * @private
     * Moved Component to the provided target instead.
     */
    moveItem : function(item, target, position) {
        // Make sure target is a dom element
        target = target.dom || target;
        if (typeof position == 'number') {
            position = target.childNodes[position];
        }
        target.insertBefore(item.el.dom, position || null);
        item.container = Ext.get(target);
        this.configureItem(item);
        this.childrenChanged = true;
    },

    /**
     * @private
     * Adds the layout's targetCls if necessary and sets
     * initialized flag when complete.
     */
    initLayout : function() {
        if (!this.initialized && !Ext.isEmpty(this.targetCls)) {
            this.getTarget().addCls(this.targetCls);
        }
        this.initialized = true;
    },

    // @private Sets the layout owner
    setOwner : function(owner) {
        this.owner = owner;
    },

    // @private - Returns empty array
    getLayoutItems : function() {
        return [];
    },

    /**
     * @private
     * Applies itemCls
     */
    configureItem: function(item) {
        var me = this,
            el = item.el,
            owner = me.owner;
            
        if (me.itemCls) {
            el.addCls(me.itemCls);
        }
        if (owner.itemCls) {
            el.addCls(owner.itemCls);
        }
    },
    
    // Placeholder empty functions for subclasses to extend
    onLayout : Ext.emptyFn,
    afterLayout : Ext.emptyFn,
    onRemove : Ext.emptyFn,
    onDestroy : Ext.emptyFn,
    doOwnerCtLayouts : Ext.emptyFn,

    /**
     * @private
     * Removes itemCls
     */
    afterRemove : function(item) {
        var me = this,
            el = item.el,
            owner = me.owner;
            
        if (item.rendered) {
            if (me.itemCls) {
                el.removeCls(me.itemCls);
            }
            if (owner.itemCls) {
                el.removeCls(owner.itemCls);
            }
        }
    },

    /*
     * Destroys this layout. This is a template method that is empty by default, but should be implemented
     * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
     * @protected
     */
    destroy : function() {
        if (!Ext.isEmpty(this.targetCls)) {
            var target = this.getTarget();
            if (target) {
                target.removeCls(this.targetCls);
            }
        }
        this.onDestroy();
    }
});
/**
 * @class Ext.layout.component.Component
 * @extends Ext.layout.Layout
 * @private
 * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Component#componentLayout layout}</b></tt>
 * configuration property.  See <tt><b>{@link Ext.Component#componentLayout}</b></tt> for additional details.</p>
 */

Ext.define('Ext.layout.component.Component', {

    /* Begin Definitions */

    extend: 'Ext.layout.Layout',

    /* End Definitions */

    type: 'component',

    monitorChildren: true,

    initLayout : function() {
        var me = this,
            owner = me.owner,
            ownerEl = owner.el;

        if (!me.initialized) {
            if (owner.frameSize) {
                me.frameSize = owner.frameSize;
            }
            else {
                owner.frameSize = me.frameSize = {
                    top: 0,
                    left: 0,
                    bottom: 0,
                    right: 0
                }; 
            }
        }
        me.callParent(arguments);
    },

    beforeLayout : function(width, height, isSetSize, layoutOwner) {
        this.callParent(arguments);

        var me = this,
            owner = me.owner,
            ownerCt = owner.ownerCt,
            layout = owner.layout,
            isVisible = owner.isVisible(true),
            ownerElChild = owner.el.child,
            layoutCollection;

        /*
         * Do not layout calculatedSized components for fixedLayouts unless the ownerCt == layoutOwner
         * fixedLayouts means layouts which are never auto/auto in the sizing that comes from their ownerCt.
         * Currently 3 layouts MAY be auto/auto (Auto, Border, and Box)
         * The reason for not allowing component layouts is to stop component layouts from things such as Updater and
         * form Validation.
         */
        if (!isSetSize && !(Ext.isNumber(width) && Ext.isNumber(height)) && ownerCt && ownerCt.layout && ownerCt.layout.fixedLayout && ownerCt != layoutOwner) {
            me.doContainerLayout();
            return false;
        }

        // If an ownerCt is hidden, add my reference onto the layoutOnShow stack.  Set the needsLayout flag.
        // If the owner itself is a directly hidden floater, set the needsLayout object on that for when it is shown.
        if (!isVisible && (owner.hiddenAncestor || owner.floating)) {
            if (owner.hiddenAncestor) {
                layoutCollection = owner.hiddenAncestor.layoutOnShow;
                layoutCollection.remove(owner);
                layoutCollection.add(owner);
            }
            owner.needsLayout = {
                width: width,
                height: height,
                isSetSize: false
            };
        }

        if (isVisible && this.needsLayout(width, height)) {
            me.rawWidth = width;
            me.rawHeight = height;
            return owner.beforeComponentLayout(width, height, isSetSize, layoutOwner);
        }
        else {
            return false;
        }
    },

    /**
    * Check if the new size is different from the current size and only
    * trigger a layout if it is necessary.
    * @param {Mixed} width The new width to set.
    * @param {Mixed} height The new height to set.
    */
    needsLayout : function(width, height) {
        this.lastComponentSize = this.lastComponentSize || {
            width: -Infinity,
            height: -Infinity
        };
        return (this.childrenChanged || this.lastComponentSize.width !== width || this.lastComponentSize.height !== height);
    },

    /**
    * Set the size of any element supporting undefined, null, and values.
    * @param {Mixed} width The new width to set.
    * @param {Mixed} height The new height to set.
    */
    setElementSize: function(el, width, height) {
        if (width !== undefined && height !== undefined) {
            el.setSize(width, height);
        }
        else if (height !== undefined) {
            el.setHeight(height);
        }
        else if (width !== undefined) {
            el.setWidth(width);
        }
    },

    /**
     * Returns the owner component's resize element.
     * @return {Ext.core.Element}
     */
     getTarget : function() {
         return this.owner.el;
     },

    /**
     * <p>Returns the element into which rendering must take place. Defaults to the owner Component's encapsulating element.</p>
     * May be overridden in Component layout managers which implement an inner element.
     * @return {Ext.core.Element}
     */
    getRenderTarget : function() {
        return this.owner.el;
    },

    /**
    * Set the size of the target element.
    * @param {Mixed} width The new width to set.
    * @param {Mixed} height The new height to set.
    */
    setTargetSize : function(width, height) {
        var me = this;
        me.setElementSize(me.owner.el, width, height);

        if (me.owner.frameBody) {
            var targetInfo = me.getTargetInfo(),
                padding = targetInfo.padding,
                border = targetInfo.border,
                frameSize = me.frameSize;

            me.setElementSize(me.owner.frameBody,
                Ext.isNumber(width) ? (width - frameSize.left - frameSize.right - padding.left - padding.right - border.left - border.right) : width,
                Ext.isNumber(height) ? (height - frameSize.top - frameSize.bottom - padding.top - padding.bottom - border.top - border.bottom) : height
            );
        }

        me.autoSized = {
            width: !Ext.isNumber(width),
            height: !Ext.isNumber(height)
        };

        me.lastComponentSize = {
            width: width,
            height: height
        };
    },

    getTargetInfo : function() {
        if (!this.targetInfo) {
            var target = this.getTarget(),
                body = this.owner.getTargetEl();

            this.targetInfo = {
                padding: {
                    top: target.getPadding('t'),
                    right: target.getPadding('r'),
                    bottom: target.getPadding('b'),
                    left: target.getPadding('l')
                },
                border: {
                    top: target.getBorderWidth('t'),
                    right: target.getBorderWidth('r'),
                    bottom: target.getBorderWidth('b'),
                    left: target.getBorderWidth('l')
                },
                bodyMargin: {
                    top: body.getMargin('t'),
                    right: body.getMargin('r'),
                    bottom: body.getMargin('b'),
                    left: body.getMargin('l')
                } 
            };
        }
        return this.targetInfo;
    },

    // Start laying out UP the ownerCt's layout when flagged to do so.
    doOwnerCtLayouts: function() {
        var owner = this.owner,
            ownerCt = owner.ownerCt,
            ownerCtComponentLayout, ownerCtContainerLayout;

        if (!ownerCt) {
            return;
        }

        ownerCtComponentLayout = ownerCt.componentLayout;
        ownerCtContainerLayout = ownerCt.layout;

        if (!owner.floating && ownerCtComponentLayout && ownerCtComponentLayout.monitorChildren && !ownerCtComponentLayout.layoutBusy) {
            if (!ownerCt.suspendLayout && ownerCtContainerLayout && !ownerCtContainerLayout.layoutBusy) {
                // AutoContainer Layout and Dock with auto in some dimension
                if (ownerCtContainerLayout.bindToOwnerCtComponent === true) {
                    ownerCt.doComponentLayout();
                }
                // Box Layouts
                else if (ownerCtContainerLayout.bindToOwnerCtContainer === true) {
                    ownerCtContainerLayout.layout();
                }
            }
        }
    },

    doContainerLayout: function() {
        var me = this,
            owner = me.owner,
            ownerCt = owner.ownerCt,
            layout = owner.layout,
            ownerCtComponentLayout;

        // Run the container layout if it exists (layout for child items)
        // **Unless automatic laying out is suspended, or the layout is currently running**
        if (!owner.suspendLayout && layout && layout.isLayout && !layout.layoutBusy) {
            layout.layout();
        }

        // Tell the ownerCt that it's child has changed and can be re-layed by ignoring the lastComponentSize cache.
        if (ownerCt && ownerCt.componentLayout) {
            ownerCtComponentLayout = ownerCt.componentLayout;
            if (!owner.floating && ownerCtComponentLayout.monitorChildren && !ownerCtComponentLayout.layoutBusy) {
                ownerCtComponentLayout.childrenChanged = true;
            }
        }
    },

    afterLayout : function(width, height, isSetSize, layoutOwner) {
        this.doContainerLayout();
        this.owner.afterComponentLayout(width, height, isSetSize, layoutOwner);
    }
});

/**
 * @class Ext.state.Manager
 * This is the global state manager. By default all components that are "state aware" check this class
 * for state information if you don't pass them a custom state provider. In order for this class
 * to be useful, it must be initialized with a provider when your application initializes. Example usage:
 <pre><code>
// in your initialization function
init : function(){
   Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
   var win = new Window(...);
   win.restoreState();
}
 </code></pre>
 * This class passes on calls from components to the underlying {@link Ext.state.Provider} so that
 * there is a common interface that can be used without needing to refer to a specific provider instance
 * in every component.
 * @singleton
 * @docauthor Evan Trimboli <evan@sencha.com>
 */
Ext.define('Ext.state.Manager', {
    singleton: true,
    requires: ['Ext.state.Provider'],
    constructor: function() {
        this.provider = Ext.create('Ext.state.Provider');
    },
    
    
    /**
     * Configures the default state provider for your application
     * @param {Provider} stateProvider The state provider to set
     */
    setProvider : function(stateProvider){
        this.provider = stateProvider;
    },

    /**
     * Returns the current value for a key
     * @param {String} name The key name
     * @param {Mixed} defaultValue The default value to return if the key lookup does not match
     * @return {Mixed} The state data
     */
    get : function(key, defaultValue){
        return this.provider.get(key, defaultValue);
    },

    /**
     * Sets the value for a key
     * @param {String} name The key name
     * @param {Mixed} value The state data
     */
     set : function(key, value){
        this.provider.set(key, value);
    },

    /**
     * Clears a value from the state
     * @param {String} name The key name
     */
    clear : function(key){
        this.provider.clear(key);
    },

    /**
     * Gets the currently configured state provider
     * @return {Provider} The state provider
     */
    getProvider : function(){
        return this.provider;
    }
});
/**
 * @class Ext.state.Stateful
 * A mixin for being able to save the state of an object to an underlying 
 * {@link Ext.state.Provider}.
 */
Ext.define('Ext.state.Stateful', {
    
    /* Begin Definitions */
   
   mixins: {
        observable: 'Ext.util.Observable'
    },
    
    requires: ['Ext.state.Manager'],
    
    /* End Definitions */
    
    /**
     * @cfg {Boolean} stateful
     * <p>A flag which causes the object to attempt to restore the state of
     * internal properties from a saved state on startup. The object must have
     * a <code>{@link #stateId}</code> for state to be managed. 
     * Auto-generated ids are not guaranteed to be stable across page loads and 
     * cannot be relied upon to save and restore the same state for a object.<p>
     * <p>For state saving to work, the state manager's provider must have been
     * set to an implementation of {@link Ext.state.Provider} which overrides the
     * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}
     * methods to save and recall name/value pairs. A built-in implementation,
     * {@link Ext.state.CookieProvider} is available.</p>
     * <p>To set the state provider for the current page:</p>
     * <pre><code>
Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
    expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
}));
     * </code></pre>
     * <p>A stateful object attempts to save state when one of the events
     * listed in the <code>{@link #stateEvents}</code> configuration fires.</p>
     * <p>To save state, a stateful object first serializes its state by
     * calling <b><code>{@link #getState}</code></b>. By default, this function does
     * nothing. The developer must provide an implementation which returns an
     * object hash which represents the restorable state of the object.</p>
     * <p>The value yielded by getState is passed to {@link Ext.state.Manager#set}
     * which uses the configured {@link Ext.state.Provider} to save the object
     * keyed by the <code>{@link stateId}</code></p>.
     * <p>During construction, a stateful object attempts to <i>restore</i>
     * its state by calling {@link Ext.state.Manager#get} passing the
     * <code>{@link #stateId}</code></p>
     * <p>The resulting object is passed to <b><code>{@link #applyState}</code></b>.
     * The default implementation of <code>{@link #applyState}</code> simply copies
     * properties into the object, but a developer may override this to support
     * more behaviour.</p>
     * <p>You can perform extra processing on state save and restore by attaching
     * handlers to the {@link #beforestaterestore}, {@link #staterestore},
     * {@link #beforestatesave} and {@link #statesave} events.</p>
     */
    stateful: true,
    
    /**
     * @cfg {String} stateId
     * The unique id for this object to use for state management purposes.
     * <p>See {@link #stateful} for an explanation of saving and restoring state.</p>
     */
    
    /**
     * @cfg {Array} stateEvents
     * <p>An array of events that, when fired, should trigger this object to
     * save its state (defaults to none). <code>stateEvents</code> may be any type
     * of event supported by this object, including browser or custom events
     * (e.g., <tt>['click', 'customerchange']</tt>).</p>
     * <p>See <code>{@link #stateful}</code> for an explanation of saving and
     * restoring object state.</p>
     */
    
    /**
     * @cfg {Number} saveBuffer A buffer to be applied if many state events are fired within
     * a short period. Defaults to 100.
     */
    saveDelay: 100,
    
    autoGenIdRe: /^((\w+-)|(ext-comp-))\d{4,}$/i,
    
    constructor: function(config) {
        var me = this;
        
        config = config || {};
        if (Ext.isDefined(config.stateful)) {
            me.stateful = config.stateful;
        }
        if (Ext.isDefined(config.saveDelay)) {
            me.saveDelay = config.saveDelay;
        }
        me.stateId = config.stateId;
        
        if (!me.stateEvents) {
            me.stateEvents = [];
        }
        if (config.stateEvents) {
            me.stateEvents.concat(config.stateEvents);
        }
        this.addEvents(
            /**
             * @event beforestaterestore
             * Fires before the state of the object is restored. Return false from an event handler to stop the restore.
             * @param {Ext.state.Stateful} this
             * @param {Object} state The hash of state values returned from the StateProvider. If this
             * event is not vetoed, then the state object is passed to <b><tt>applyState</tt></b>. By default,
             * that simply copies property values into this object. The method maybe overriden to
             * provide custom state restoration.
             */
            'beforestaterestore',
            
            /**
             * @event staterestore
             * Fires after the state of the object is restored.
             * @param {Ext.state.Stateful} this
             * @param {Object} state The hash of state values returned from the StateProvider. This is passed
             * to <b><tt>applyState</tt></b>. By default, that simply copies property values into this
             * object. The method maybe overriden to provide custom state restoration.
             */
            'staterestore',
            
            /**
             * @event beforestatesave
             * Fires before the state of the object is saved to the configured state provider. Return false to stop the save.
             * @param {Ext.state.Stateful} this
             * @param {Object} state The hash of state values. This is determined by calling
             * <b><tt>getState()</tt></b> on the object. This method must be provided by the
             * developer to return whetever representation of state is required, by default, Ext.state.Stateful
             * has a null implementation.
             */
            'beforestatesave',
            
            /**
             * @event statesave
             * Fires after the state of the object is saved to the configured state provider.
             * @param {Ext.state.Stateful} this
             * @param {Object} state The hash of state values. This is determined by calling
             * <b><tt>getState()</tt></b> on the object. This method must be provided by the
             * developer to return whetever representation of state is required, by default, Ext.state.Stateful
             * has a null implementation.
             */
            'statesave'
        );
        me.mixins.observable.constructor.call(me);
        if (me.stateful !== false) {
            me.initStateEvents();
            me.initState();
        }
    },
    
    /**
     * Initializes any state events for this object.
     * @private
     */
    initStateEvents: function() {
        this.addStateEvents(this.stateEvents);
    },
    
    /**
     * Add events that will trigger the state to be saved.
     * @param {String/Array} events The event name or an array of event names.
     */
    addStateEvents: function(events){
        if (!Ext.isArray(events)) {
            events = [events];
        }
        
        var me = this,
            i = 0,
            len = events.length;
            
        for (; i < len; ++i) {
            me.on(events[i], me.onStateChange, me);
        }
    },
    
    /**
     * This method is called when any of the {@link #stateEvents} are fired.
     * @private
     */
    onStateChange: function(){
        var me = this,
            delay = me.saveDelay;
        
        if (delay > 0) {
            if (!me.stateTask) {
                me.stateTask = Ext.create('Ext.util.DelayedTask', me.saveState, me);
            }
            me.stateTask.delay(me.saveDelay);
        } else {
            me.saveState();
        }
    },
    
    /**
     * Saves the state of the object to the persistence store.
     * @private
     */
    saveState: function() {
        var me = this,
            id,
            state;
        
        if (me.stateful !== false) {
            id = me.getStateId();
            if (id) {
                state = me.getState();
                if (me.fireEvent('beforestatesave', me, state) !== false) {
                    Ext.state.Manager.set(id, state);
                    me.fireEvent('statesave', me, state);
                }
            }
        }
    },
    
    /**
     * Gets the current state of the object. By default this function returns null,
     * it should be overridden in subclasses to implement methods for getting the state.
     * @return {Object} The current state
     */
    getState: function(){
        return null;    
    },
    
    /**
     * Applies the state to the object. This should be overridden in subclasses to do
     * more complex state operations. By default it applies the state properties onto
     * the current object.
     * @param {Object} state The state
     */
    applyState: function(state) {
        if (state) {
            Ext.apply(this, state);
        }
    },
    
    /**
     * Gets the state id for this object.
     * @return {String} The state id, null if not found.
     */
    getStateId: function() {
        var me = this,
            id = me.stateId;
        
        if (!id) {
            id = me.autoGenIdRe.test(String(me.id)) ? null : me.id;
        }
        return id;
    },
    
    /**
     * Initializes the state of the object upon construction.
     * @private
     */
    initState: function(){
        var me = this,
            id = me.getStateId(),
            state;
            
        if (me.stateful !== false) {
            if (id) {
                state = Ext.state.Manager.get(id);
                if (state) {
                    state = Ext.apply({}, state);
                    if (me.fireEvent('beforestaterestore', me, state) !== false) {
                        me.applyState(state);
                        me.fireEvent('staterestore', me, state);
                    }
                }
            }
        }
    },
    
    /**
     * Destroys this stateful object.
     */
    destroy: function(){
        var task = this.stateTask;
        if (task) {
            task.cancel();
        }
        this.clearListeners();
        
    }
    
});

/**
 * @class Ext.AbstractManager
 * @extends Object
 * @ignore
 * Base Manager class
 */

Ext.define('Ext.AbstractManager', {

    /* Begin Definitions */

    requires: ['Ext.util.HashMap'],

    /* End Definitions */

    typeName: 'type',

    constructor: function(config) {
        Ext.apply(this, config || {});

        /**
         * Contains all of the items currently managed
         * @property all
         * @type Ext.util.MixedCollection
         */
        this.all = Ext.create('Ext.util.HashMap');

        this.types = {};
    },

    /**
     * Returns an item by id.
     * For additional details see {@link Ext.util.HashMap#get}.
     * @param {String} id The id of the item
     * @return {Mixed} The item, <code>undefined</code> if not found.
     */
    get : function(id) {
        return this.all.get(id);
    },

    /**
     * Registers an item to be managed
     * @param {Mixed} item The item to register
     */
    register: function(item) {
        this.all.add(item);
    },

    /**
     * Unregisters an item by removing it from this manager
     * @param {Mixed} item The item to unregister
     */
    unregister: function(item) {
        this.all.remove(item);
    },

    /**
     * <p>Registers a new item constructor, keyed by a type key.
     * @param {String} type The mnemonic string by which the class may be looked up.
     * @param {Constructor} cls The new instance class.
     */
    registerType : function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
    },

    /**
     * Checks if an item type is registered.
     * @param {String} type The mnemonic string by which the class may be looked up
     * @return {Boolean} Whether the type is registered.
     */
    isRegistered : function(type){
        return this.types[type] !== undefined;
    },

    /**
     * Creates and returns an instance of whatever this manager manages, based on the supplied type and config object
     * @param {Object} config The config object
     * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
     * @return {Mixed} The instance of whatever this manager is managing
     */
    create: function(config, defaultType) {
        var type        = config[this.typeName] || config.type || defaultType,
            Constructor = this.types[type];

        if (Constructor == undefined) {
            Ext.Error.raise("The '" + type + "' type has not been registered with this manager");
        }

        return new Constructor(config);
    },

    /**
     * Registers a function that will be called when an item with the specified id is added to the manager. This will happen on instantiation.
     * @param {String} id The item id
     * @param {Function} fn The callback function. Called with a single parameter, the item.
     * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the item.
     */
    onAvailable : function(id, fn, scope){
        var all = this.all,
            item;
        
        if (all.containsKey(id)) {
            item = all.get(id);
            fn.call(scope || item, item);
        } else {
            all.on('add', function(map, key, item){
                if (key == id) {
                    fn.call(scope || item, item);
                    all.un('add', fn, scope);
                }
            });
        }
    },
    
    /**
     * Executes the specified function once for each item in the collection.
     * Returning false from the function will cease iteration.
     * 
     * The paramaters passed to the function are:
     * <div class="mdetail-params"><ul>
     * <li><b>key</b> : String<p class="sub-desc">The key of the item</p></li>
     * <li><b>value</b> : Number<p class="sub-desc">The value of the item</p></li>
     * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
     * </ul></div>
     * @param {Object} fn The function to execute.
     * @param {Object} scope The scope to execute in. Defaults to <tt>this</tt>.
     */
    each: function(fn, scope){
        this.all.each(fn, scope || this);    
    },
    
    /**
     * Gets the number of items in the collection.
     * @return {Number} The number of items in the collection.
     */
    getCount: function(){
        return this.all.getCount();
    }
});

/**
 * @class Ext.PluginManager
 * @extends Ext.AbstractManager
 * <p>Provides a registry of available Plugin <i>classes</i> indexed by a mnemonic code known as the Plugin's ptype.
 * The <code>{@link Ext.Component#xtype xtype}</code> provides a way to avoid instantiating child Components
 * when creating a full, nested config object for a complete Ext page.</p>
 * <p>A child Component may be specified simply as a <i>config object</i>
 * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
 * needs rendering, the correct type can be looked up for lazy instantiation.</p>
 * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
 * @singleton
 */
Ext.define('Ext.PluginManager', {
    extend: 'Ext.AbstractManager',
    alternateClassName: 'Ext.PluginMgr',
    singleton: true,
    typeName: 'ptype',

    /**
     * Creates a new Plugin from the specified config object using the
     * config object's ptype to determine the class to instantiate.
     * @param {Object} config A configuration object for the Plugin you wish to create.
     * @param {Constructor} defaultType The constructor to provide the default Plugin type if
     * the config object does not contain a <code>ptype</code>. (Optional if the config contains a <code>ptype</code>).
     * @return {Ext.Component} The newly instantiated Plugin.
     */
    //create: function(plugin, defaultType) {
    //    if (plugin instanceof this) {
    //        return plugin;
    //    } else {
    //        var type, config = {};
    //
    //        if (Ext.isString(plugin)) {
    //            type = plugin;
    //        }
    //        else {
    //            type = plugin[this.typeName] || defaultType;
    //            config = plugin;
    //        }
    //
    //        return Ext.createByAlias('plugin.' + type, config);
    //    }
    //},

    create : function(config, defaultType){
        if (config.init) {
            return config;
        } else {
            return Ext.createByAlias('plugin.' + (config.ptype || defaultType), config);
        }
        
        // Prior system supported Singleton plugins.
        //var PluginCls = this.types[config.ptype || defaultType];
        //if (PluginCls.init) {
        //    return PluginCls;
        //} else {
        //    return new PluginCls(config);
        //}
    },

    /**
     * Returns all plugins registered with the given type. Here, 'type' refers to the type of plugin, not its ptype.
     * @param {String} type The type to search for
     * @param {Boolean} defaultsOnly True to only return plugins of this type where the plugin's isDefault property is truthy
     * @return {Array} All matching plugins
     */
    findByType: function(type, defaultsOnly) {
        var matches = [],
            types   = this.types;

        for (var name in types) {
            if (!types.hasOwnProperty(name)) {
                continue;
            }
            var item = types[name];

            if (item.type == type && (!defaultsOnly || (defaultsOnly === true && item.isDefault))) {
                matches.push(item);
            }
        }

        return matches;
    }
}, function() {    
    /**
     * Shorthand for {@link Ext.PluginManager#registerType}
     * @param {String} ptype The ptype mnemonic string by which the Plugin class
     * may be looked up.
     * @param {Constructor} cls The new Plugin class.
     * @member Ext
     * @method preg
     */
    Ext.preg = function() {
        return Ext.PluginManager.registerType.apply(Ext.PluginManager, arguments);
    };
});

/**
 * @class Ext.ComponentManager
 * @extends Ext.AbstractManager
 * <p>Provides a registry of all Components (instances of {@link Ext.Component} or any subclass
 * thereof) on a page so that they can be easily accessed by {@link Ext.Component component}
 * {@link Ext.Component#id id} (see {@link #get}, or the convenience method {@link Ext#getCmp Ext.getCmp}).</p>
 * <p>This object also provides a registry of available Component <i>classes</i>
 * indexed by a mnemonic code known as the Component's {@link Ext.Component#xtype xtype}.
 * The <code>xtype</code> provides a way to avoid instantiating child Components
 * when creating a full, nested config object for a complete Ext page.</p>
 * <p>A child Component may be specified simply as a <i>config object</i>
 * as long as the correct <code>{@link Ext.Component#xtype xtype}</code> is specified so that if and when the Component
 * needs rendering, the correct type can be looked up for lazy instantiation.</p>
 * <p>For a list of all available <code>{@link Ext.Component#xtype xtypes}</code>, see {@link Ext.Component}.</p>
 * @singleton
 */
Ext.define('Ext.ComponentManager', {
    extend: 'Ext.AbstractManager',
    alternateClassName: 'Ext.ComponentMgr',
    
    singleton: true,
    
    typeName: 'xtype',
    
    /**
     * Creates a new Component from the specified config object using the
     * config object's xtype to determine the class to instantiate.
     * @param {Object} config A configuration object for the Component you wish to create.
     * @param {Constructor} defaultType The constructor to provide the default Component type if
     * the config object does not contain a <code>xtype</code>. (Optional if the config contains a <code>xtype</code>).
     * @return {Ext.Component} The newly instantiated Component.
     */
    create: function(component, defaultType){
        if (component instanceof Ext.AbstractComponent) {
            return component;
        }
        else if (Ext.isString(component)) {
            return Ext.createByAlias('widget.' + component);
        }
        else {
            var type = component.xtype || defaultType,
                config = component;
            
            return Ext.createByAlias('widget.' + type, config);
        }
    },

    registerType: function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
        cls.prototype[this.typeName] = type;
    }
});
/**
 * @class Ext.XTemplate
 * @extends Ext.Template
 * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
 * <li>Autofilling arrays using templates and sub-templates</li>
 * <li>Conditional processing with basic comparison operators</li>
 * <li>Basic math function support</li>
 * <li>Execute arbitrary inline code with special built-in template variables</li>
 * <li>Custom member functions</li>
 * <li>Many special tags and built-in operators that aren't defined as part of
 * the API, but are supported in the templates that can be created</li>
 * </ul></div></p>
 * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
 * <li>{@link Ext.view.View}</li>
 * </ul></div></p>
 *
 * The {@link Ext.Template} describes
 * the acceptable parameters to pass to the constructor. The following
 * examples demonstrate all of the supported features.</p>
 *
 * <div class="mdetail-params"><ul>
 *
 * <li><b><u>Sample Data</u></b>
 * <div class="sub-desc">
 * <p>This is the data object used for reference in each code example:</p>
 * <pre><code>
var data = {
name: 'Tommy Maintz',
title: 'Lead Developer',
company: 'Sencha Inc.',
email: 'tommy@sencha.com',
address: '5 Cups Drive',
city: 'Palo Alto',
state: 'CA',
zip: '44102',
drinks: ['Coffee', 'Soda', 'Water'],
kids: [{
        name: 'Joshua',
        age:3
    },{
        name: 'Matthew',
        age:2
    },{
        name: 'Solomon',
        age:0
}]
};
 </code></pre>
 * </div>
 * </li>
 *
 *
 * <li><b><u>Auto filling of arrays</u></b>
 * <div class="sub-desc">
 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
 * to process the provided data object:
 * <ul>
 * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
 * repeating the template block inside the <tt>tpl</tt> tag for each item in the
 * array.</li>
 * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
 * <li>While processing an array, the special variable <tt>{#}</tt>
 * will provide the current array index + 1 (starts at 1, not 0).</li>
 * </ul>
 * </p>
 * <pre><code>
&lt;tpl <b>for</b>=".">...&lt;/tpl>       // loop through array at root node
&lt;tpl <b>for</b>="foo">...&lt;/tpl>     // loop through array at foo node
&lt;tpl <b>for</b>="foo.bar">...&lt;/tpl> // loop through array at foo.bar node
 </code></pre>
 * Using the sample data above:
 * <pre><code>
var tpl = new Ext.XTemplate(
    '&lt;p>Kids: ',
    '&lt;tpl <b>for</b>=".">',       // process the data.kids node
        '&lt;p>{#}. {name}&lt;/p>',  // use current array index to autonumber
    '&lt;/tpl>&lt;/p>'
);
tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
 </code></pre>
 * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
 * to access specified members of the provided data object to populate the template:</p>
 * <pre><code>
var tpl = new Ext.XTemplate(
    '&lt;p>Name: {name}&lt;/p>',
    '&lt;p>Title: {title}&lt;/p>',
    '&lt;p>Company: {company}&lt;/p>',
    '&lt;p>Kids: ',
    '&lt;tpl <b>for="kids"</b>>',     // interrogate the kids property within the data
        '&lt;p>{name}&lt;/p>',
    '&lt;/tpl>&lt;/p>'
);
tpl.overwrite(panel.body, data);  // pass the root node of the data object
 </code></pre>
 * <p>Flat arrays that contain values (and not objects) can be auto-rendered
 * using the special <b><tt>{.}</tt></b> variable inside a loop.  This variable
 * will represent the value of the array at the current index:</p>
 * <pre><code>
var tpl = new Ext.XTemplate(
    '&lt;p>{name}\&#39;s favorite beverages:&lt;/p>',
    '&lt;tpl for="drinks">',
        '&lt;div> - {.}&lt;/div>',
    '&lt;/tpl>'
);
tpl.overwrite(panel.body, data);
 </code></pre>
 * <p>When processing a sub-template, for example while looping through a child array,
 * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
 * <pre><code>
var tpl = new Ext.XTemplate(
    '&lt;p>Name: {name}&lt;/p>',
    '&lt;p>Kids: ',
    '&lt;tpl for="kids">',
        '&lt;tpl if="age &amp;gt; 1">',
            '&lt;p>{name}&lt;/p>',
            '&lt;p>Dad: {<b>parent</b>.name}&lt;/p>',
        '&lt;/tpl>',
    '&lt;/tpl>&lt;/p>'
);
tpl.overwrite(panel.body, data);
 </code></pre>
 * </div>
 * </li>
 *
 *
 * <li><b><u>Conditional processing with basic comparison operators</u></b>
 * <div class="sub-desc">
 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
 * to provide conditional checks for deciding whether or not to render specific
 * parts of the template. Notes:<div class="sub-desc"><ul>
 * <li>Double quotes must be encoded if used within the conditional</li>
 * <li>There is no <tt>else</tt> operator &mdash; if needed, two opposite
 * <tt>if</tt> statements should be used.</li>
 * </ul></div>
 * <pre><code>
&lt;tpl if="age &gt; 1 &amp;&amp; age &lt; 10">Child&lt;/tpl>
&lt;tpl if="age >= 10 && age < 18">Teenager&lt;/tpl>
&lt;tpl <b>if</b>="this.isGirl(name)">...&lt;/tpl>
&lt;tpl <b>if</b>="id==\'download\'">...&lt;/tpl>
&lt;tpl <b>if</b>="needsIcon">&lt;img src="{icon}" class="{iconCls}"/>&lt;/tpl>
// no good:
&lt;tpl if="name == "Tommy"">Hello&lt;/tpl>
// encode &#34; if it is part of the condition, e.g.
&lt;tpl if="name == &#38;quot;Tommy&#38;quot;">Hello&lt;/tpl>
 * </code></pre>
 * Using the sample data above:
 * <pre><code>
var tpl = new Ext.XTemplate(
    '&lt;p>Name: {name}&lt;/p>',
    '&lt;p>Kids: ',
    '&lt;tpl for="kids">',
        '&lt;tpl if="age &amp;gt; 1">',
            '&lt;p>{name}&lt;/p>',
        '&lt;/tpl>',
    '&lt;/tpl>&lt;/p>'
);
tpl.overwrite(panel.body, data);
 </code></pre>
 * </div>
 * </li>
 *
 *
 * <li><b><u>Basic math support</u></b>
 * <div class="sub-desc">
 * <p>The following basic math operators may be applied directly on numeric
 * data values:</p><pre>
 * + - * /
 * </pre>
 * For example:
 * <pre><code>
var tpl = new Ext.XTemplate(
    '&lt;p>Name: {name}&lt;/p>',
    '&lt;p>Kids: ',
    '&lt;tpl for="kids">',
        '&lt;tpl if="age &amp;gt; 1">',  // <-- Note that the &gt; is encoded
            '&lt;p>{#}: {name}&lt;/p>',  // <-- Auto-number each item
            '&lt;p>In 5 Years: {age+5}&lt;/p>',  // <-- Basic math
            '&lt;p>Dad: {parent.name}&lt;/p>',
        '&lt;/tpl>',
    '&lt;/tpl>&lt;/p>'
);
tpl.overwrite(panel.body, data);
 </code></pre>
 * </div>
 * </li>
 *
 *
 * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
 * <div class="sub-desc">
 * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
 * in the scope of the template. There are some special variables available in that code:
 * <ul>
 * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
 * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
 * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
 * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
 * loop you are in (1-based).</li>
 * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
 * of the array you are looping.</li>
 * </ul>
 * This example demonstrates basic row striping using an inline code block and the
 * <tt>xindex</tt> variable:</p>
 * <pre><code>
var tpl = new Ext.XTemplate(
    '&lt;p>Name: {name}&lt;/p>',
    '&lt;p>Company: {[values.company.toUpperCase() + ", " + values.title]}&lt;/p>',
    '&lt;p>Kids: ',
    '&lt;tpl for="kids">',
        '&lt;div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
        '{name}',
        '&lt;/div>',
    '&lt;/tpl>&lt;/p>'
 );
tpl.overwrite(panel.body, data);
 </code></pre>
 * </div>
 * </li>
 *
 * <li><b><u>Template member functions</u></b>
 * <div class="sub-desc">
 * <p>One or more member functions can be specified in a configuration
 * object passed into the XTemplate constructor for more complex processing:</p>
 * <pre><code>
var tpl = new Ext.XTemplate(
    '&lt;p>Name: {name}&lt;/p>',
    '&lt;p>Kids: ',
    '&lt;tpl for="kids">',
        '&lt;tpl if="this.isGirl(name)">',
            '&lt;p>Girl: {name} - {age}&lt;/p>',
        '&lt;/tpl>',
         // use opposite if statement to simulate 'else' processing:
        '&lt;tpl if="this.isGirl(name) == false">',
            '&lt;p>Boy: {name} - {age}&lt;/p>',
        '&lt;/tpl>',
        '&lt;tpl if="this.isBaby(age)">',
            '&lt;p>{name} is a baby!&lt;/p>',
        '&lt;/tpl>',
    '&lt;/tpl>&lt;/p>',
    {
        // XTemplate configuration:
        compiled: true,
        // member functions:
        isGirl: function(name){
           return name == 'Sara Grace';
        },
        isBaby: function(age){
           return age < 1;
        }
    }
);
tpl.overwrite(panel.body, data);
 </code></pre>
 * </div>
 * </li>
 *
 * </ul></div>
 *
 * @param {Mixed} config
 */

Ext.define('Ext.XTemplate', {

    /* Begin Definitions */

    extend: 'Ext.Template',

    statics: {
        /**
         * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
         * @param {String/HTMLElement} el A DOM element or its id
         * @return {Ext.Template} The created template
         * @static
         */
        from: function(el, config) {
            el = Ext.getDom(el);
            return new this(el.value || el.innerHTML, config || {});
        }
    },

    

    argsRe: /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
    nameRe: /^<tpl\b[^>]*?for="(.*?)"/,
    ifRe: /^<tpl\b[^>]*?if="(.*?)"/,
    execRe: /^<tpl\b[^>]*?exec="(.*?)"/,
    constructor: function() {
        this.callParent(arguments);

        var me = this,
            html = me.html,
            argsRe = me.argsRe,
            nameRe = me.nameRe,
            ifRe = me.ifRe,
            execRe = me.execRe,
            id = 0,
            tpls = [],
            VALUES = 'values',
            PARENT = 'parent',
            XINDEX = 'xindex',
            XCOUNT = 'xcount',
            RETURN = 'return ',
            WITHVALUES = 'with(values){ ',
            m, matchName, matchIf, matchExec, exp, fn, exec, name, i;

        html = ['<tpl>', html, '</tpl>'].join('');

        while ((m = html.match(argsRe))) {
            exp = null;
            fn = null;
            exec = null;
            matchName = m[0].match(nameRe);
            matchIf = m[0].match(ifRe);
            matchExec = m[0].match(execRe);

            exp = matchIf ? matchIf[1] : null;
            if (exp) {
                fn = Ext.functionFactory(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + 'try{' + RETURN + Ext.String.htmlDecode(exp) + ';}catch(e){return;}}');
            }

            exp = matchExec ? matchExec[1] : null;
            if (exp) {
                exec = Ext.functionFactory(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + Ext.String.htmlDecode(exp) + ';}');
            }

            name = matchName ? matchName[1] : null;
            if (name) {
                if (name === '.') {
                    name = VALUES;
                } else if (name === '..') {
                    name = PARENT;
                }
                name = Ext.functionFactory(VALUES, PARENT, 'try{' + WITHVALUES + RETURN + name + ';}}catch(e){return;}');
            }

            tpls.push({
                id: id,
                target: name,
                exec: exec,
                test: fn,
                body: m[1] || ''
            });

            html = html.replace(m[0], '{xtpl' + id + '}');
            id = id + 1;
        }

        for (i = tpls.length - 1; i >= 0; --i) {
            me.compileTpl(tpls[i]);
        }
        me.master = tpls[tpls.length - 1];
        me.tpls = tpls;
    },

    
    applySubTemplate: function(id, values, parent, xindex, xcount) {
        var me = this, t = me.tpls[id];
        return t.compiled.call(me, values, parent, xindex, xcount);
    },
    
    codeRe: /\{\[((?:\\\]|.|\n)*?)\]\}/g,

    re: /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?\}/g,

    
    compileTpl: function(tpl) {
        var fm = Ext.util.Format,
            me = this,
            useFormat = me.disableFormats !== true,
            body, bodyReturn, evaluatedFn;

        function fn(m, name, format, args, math) {
            var v;
            
            
            if (name.substr(0, 4) == 'xtpl') {
                return "',this.applySubTemplate(" + name.substr(4) + ", values, parent, xindex, xcount),'";
            }
            
            if (name == '.') {
                
                v = 'Ext.Array.indexOf(["string", "number", "boolean"], typeof values) > -1 || Ext.isDate(values) ? values : ""';
            }

            
            else if (name == '#') {
                v = 'xindex';
            }
            else if (name.substr(0, 7) == "parent.") {
                v = name;
            }
            
            else if (name.indexOf('.') != -1) {
                v = "values." + name;
            }

            
            else {
                v = "values['" + name + "']";
            }
            if (math) {
                v = '(' + v + math + ')';
            }
            if (format && useFormat) {
                args = args ? ',' + args : "";
                if (format.substr(0, 5) != "this.") {
                    format = "fm." + format + '(';
                }
                else {
                    format = 'this.' + format.substr(5) + '(';
                }
            }
            else {
                args = '';
                format = "(" + v + " === undefined ? '' : ";
            }
            return "'," + format + v + args + "),'";
        }

        function codeFn(m, code) {
            
            return "',(" + code.replace(me.compileARe, "'") + "),'";
        }

        bodyReturn = tpl.body.replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn).replace(me.codeRe, codeFn);
        body = "evaluatedFn = function(values, parent, xindex, xcount){return ['" + bodyReturn + "'].join('');};";
        eval(body);

        tpl.compiled = function(values, parent, xindex, xcount) {
            var vs,
                length,
                buffer,
                i;

            if (tpl.test && !tpl.test.call(me, values, parent, xindex, xcount)) {
                return '';
            }

            vs = tpl.target ? tpl.target.call(me, values, parent) : values;
            if (!vs) {
               return '';
            }

            parent = tpl.target ? values : parent;
            if (tpl.target && Ext.isArray(vs)) {
                buffer = [];
                length = vs.length;
                if (tpl.exec) {
                    for (i = 0; i < length; i++) {
                        buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length);
                        tpl.exec.call(me, vs[i], parent, i + 1, length);
                    }
                } else {
                    for (i = 0; i < length; i++) {
                        buffer[buffer.length] = evaluatedFn.call(me, vs[i], parent, i + 1, length);
                    }
                }
                return buffer.join('');
            }

            if (tpl.exec) {
                tpl.exec.call(me, vs, parent, xindex, xcount);
            }
            return evaluatedFn.call(me, vs, parent, xindex, xcount);
        };

        return this;
    },

    
    applyTemplate: function(values) {
        return this.master.compiled.call(this, values, {}, 1, 1);
    },

    
    compile: function() {
        return this;
    }
}, function() {
    
    this.createAlias('apply', 'applyTemplate');
});


Ext.define('Ext.util.AbstractMixedCollection', {
    requires: ['Ext.util.Filter'],
    
    mixins: {
        observable: 'Ext.util.Observable'
    },

    constructor: function(allowFunctions, keyFn) {
        var me = this;

        me.items = [];
        me.map = {};
        me.keys = [];
        me.length = 0;

        me.addEvents(
            
            'clear',

            
            'add',

            
            'replace',

            
            'remove'
        );

        me.allowFunctions = allowFunctions === true;

        if (keyFn) {
            me.getKey = keyFn;
        }

        me.mixins.observable.constructor.call(me);
    },
    
    
    allowFunctions : false,

    
    add : function(key, obj){
        var me = this,
            myObj = obj,
            myKey = key,
            old;

        if (arguments.length == 1) {
            myObj = myKey;
            myKey = me.getKey(myObj);
        }
        if (typeof myKey != 'undefined' && myKey !== null) {
            old = me.map[myKey];
            if (typeof old != 'undefined') {
                return me.replace(myKey, myObj);
            }
            me.map[myKey] = myObj;
        }
        me.length++;
        me.items.push(myObj);
        me.keys.push(myKey);
        me.fireEvent('add', me.length - 1, myObj, myKey);
        return myObj;
    },

    
    getKey : function(o){
         return o.id;
    },

    
    replace : function(key, o){
        var me = this,
            old,
            index;

        if (arguments.length == 1) {
            o = arguments[0];
            key = me.getKey(o);
        }
        old = me.map[key];
        if (typeof key == 'undefined' || key === null || typeof old == 'undefined') {
             return me.add(key, o);
        }
        index = me.indexOfKey(key);
        me.items[index] = o;
        me.map[key] = o;
        me.fireEvent('replace', key, old, o);
        return o;
    },

    
    addAll : function(objs){
        var me = this,
            i = 0,
            args,
            len,
            key;

        if (arguments.length > 1 || Ext.isArray(objs)) {
            args = arguments.length > 1 ? arguments : objs;
            for (len = args.length; i < len; i++) {
                me.add(args[i]);
            }
        } else {
            for (key in objs) {
                if (objs.hasOwnProperty(key)) {
                    if (me.allowFunctions || typeof objs[key] != 'function') {
                        me.add(key, objs[key]);
                    }
                }
            }
        }
    },

    
    each : function(fn, scope){
        var items = [].concat(this.items), 
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (fn.call(scope || item, item, i, len) === false) {
                break;
            }
        }
    },

    
    eachKey : function(fn, scope){
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = keys.length;

        for (; i < len; i++) {
            fn.call(scope || window, keys[i], items[i], i, len);
        }
    },

    
    findBy : function(fn, scope) {
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || window, items[i], keys[i])) {
                return items[i];
            }
        }
        return null;
    },

    find : function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.util.MixedCollection: find has been deprecated. Use findBy instead.');
        }
        return this.findBy.apply(this, arguments);
    },

    
    insert : function(index, key, obj){
        var me = this,
            myKey = key,
            myObj = obj;

        if (arguments.length == 2) {
            myObj = myKey;
            myKey = me.getKey(myObj);
        }
        if (me.containsKey(myKey)) {
            me.suspendEvents();
            me.removeAtKey(myKey);
            me.resumeEvents();
        }
        if (index >= me.length) {
            return me.add(myKey, myObj);
        }
        me.length++;
        me.items.splice(index, 0, myObj);
        if (typeof myKey != 'undefined' && myKey !== null) {
            me.map[myKey] = myObj;
        }
        me.keys.splice(index, 0, myKey);
        me.fireEvent('add', index, myObj, myKey);
        return myObj;
    },

    
    remove : function(o){
        return this.removeAt(this.indexOf(o));
    },

    
    removeAll : function(items){
        Ext.each(items || [], function(item) {
            this.remove(item);
        }, this);

        return this;
    },

    
    removeAt : function(index){
        var me = this,
            o,
            key;

        if (index < me.length && index >= 0) {
            me.length--;
            o = me.items[index];
            me.items.splice(index, 1);
            key = me.keys[index];
            if (typeof key != 'undefined') {
                delete me.map[key];
            }
            me.keys.splice(index, 1);
            me.fireEvent('remove', o, key);
            return o;
        }
        return false;
    },

    
    removeAtKey : function(key){
        return this.removeAt(this.indexOfKey(key));
    },

    
    getCount : function(){
        return this.length;
    },

    
    indexOf : function(o){
        return Ext.Array.indexOf(this.items, o);
    },

    
    indexOfKey : function(key){
        return Ext.Array.indexOf(this.keys, key);
    },

    
    get : function(key) {
        var me = this,
            mk = me.map[key],
            item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined;
        return typeof item != 'function' || me.allowFunctions ? item : null; 
    },

    
    getAt : function(index) {
        return this.items[index];
    },

    
    getByKey : function(key) {
        return this.map[key];
    },

    
    contains : function(o){
        return Ext.Array.contains(this.items, o);
    },

    
    containsKey : function(key){
        return typeof this.map[key] != 'undefined';
    },

    
    clear : function(){
        var me = this;

        me.length = 0;
        me.items = [];
        me.keys = [];
        me.map = {};
        me.fireEvent('clear');
    },

    
    first : function() {
        return this.items[0];
    },

    
    last : function() {
        return this.items[this.length - 1];
    },

    
    sum: function(property, root, start, end) {
        var values = this.extractValues(property, root),
            length = values.length,
            sum    = 0,
            i;

        start = start || 0;
        end   = (end || end === 0) ? end : length - 1;

        for (i = start; i <= end; i++) {
            sum += values[i];
        }

        return sum;
    },

    
    collect: function(property, root, allowNull) {
        var values = this.extractValues(property, root),
            length = values.length,
            hits   = {},
            unique = [],
            value, strValue, i;

        for (i = 0; i < length; i++) {
            value = values[i];
            strValue = String(value);

            if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) {
                hits[strValue] = true;
                unique.push(value);
            }
        }

        return unique;
    },

    
    extractValues: function(property, root) {
        var values = this.items;

        if (root) {
            values = Ext.Array.pluck(values, root);
        }

        return Ext.Array.pluck(values, property);
    },

    
    getRange : function(start, end){
        var me = this,
            items = me.items,
            range = [],
            i;

        if (items.length < 1) {
            return range;
        }

        start = start || 0;
        end = Math.min(typeof end == 'undefined' ? me.length - 1 : end, me.length - 1);
        if (start <= end) {
            for (i = start; i <= end; i++) {
                range[range.length] = items[i];
            }
        } else {
            for (i = start; i >= end; i--) {
                range[range.length] = items[i];
            }
        }
        return range;
    },

    
    filter : function(property, value, anyMatch, caseSensitive) {
        var filters = [],
            filterFn;

        
        if (Ext.isString(property)) {
            filters.push(Ext.create('Ext.util.Filter', {
                property     : property,
                value        : value,
                anyMatch     : anyMatch,
                caseSensitive: caseSensitive
            }));
        } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) {
            filters = filters.concat(property);
        }

        
        
        filterFn = function(record) {
            var isMatch = true,
                length = filters.length,
                i;

            for (i = 0; i < length; i++) {
                var filter = filters[i],
                    fn     = filter.filterFn,
                    scope  = filter.scope;

                isMatch = isMatch && fn.call(scope, record);
            }

            return isMatch;
        };

        return this.filterBy(filterFn);
    },

    
    filterBy : function(fn, scope) {
        var me = this,
            newMC  = new this.self(),
            keys   = me.keys,
            items  = me.items,
            length = items.length,
            i;

        newMC.getKey = me.getKey;

        for (i = 0; i < length; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                newMC.add(keys[i], items[i]);
            }
        }

        return newMC;
    },

    
    findIndex : function(property, value, start, anyMatch, caseSensitive){
        if(Ext.isEmpty(value, false)){
            return -1;
        }
        value = this.createValueMatcher(value, anyMatch, caseSensitive);
        return this.findIndexBy(function(o){
            return o && value.test(o[property]);
        }, null, start);
    },

    
    findIndexBy : function(fn, scope, start){
        var me = this,
            keys = me.keys,
            items = me.items,
            i = start || 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                return i;
            }
        }
        return -1;
    },

    
    createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
        if (!value.exec) { 
            var er = Ext.String.escapeRegex;
            value = String(value);

            if (anyMatch === true) {
                value = er(value);
            } else {
                value = '^' + er(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
        }
        return value;
    },

    
    clone : function() {
        var me = this,
            copy = new this.self(),
            keys = me.keys,
            items = me.items,
            i = 0,
            len = items.length;

        for(; i < len; i++){
            copy.add(keys[i], items[i]);
        }
        copy.getKey = me.getKey;
        return copy;
    }
});


Ext.define("Ext.util.Sortable", {
    
    isSortable: true,
    
    
    defaultSortDirection: "ASC",
    
    requires: [
        'Ext.util.Sorter'
    ],

        
    sortRoot: null,
    
    
    initSortable: function() {
        var me = this,
            sorters = me.sorters;
        
        
        me.sorters = Ext.create('Ext.util.AbstractMixedCollection', false, function(item) {
            return item.id || item.property;
        });
        
        if (sorters) {
            me.sorters.addAll(me.decodeSorters(sorters));
        }
    },

    
    sort: function(sorters, direction, where, doSort) {
        var me = this,
            sorter, sorterFn,
            newSorters;
        
        if (Ext.isArray(sorters)) {
            doSort = where;
            where = direction;
            newSorters = sorters;
        }
        else if (Ext.isObject(sorters)) {
            doSort = where;
            where = direction;
            newSorters = [sorters];
        }
        else if (Ext.isString(sorters)) {
            sorter = me.sorters.get(sorters);

            if (!sorter) {
                sorter = {
                    property : sorters,
                    direction: direction
                };
                newSorters = [sorter];
            }
            else if (direction === undefined) {
                sorter.toggle();
            }
            else {
                sorter.setDirection(direction);
            }
        }
        
        if (newSorters && newSorters.length) {
            newSorters = me.decodeSorters(newSorters);
            if (Ext.isString(where)) {
                if (where === 'prepend') {
                    sorters = me.sorters.clone().items;
                    
                    me.sorters.clear();
                    me.sorters.addAll(newSorters);
                    me.sorters.addAll(sorters);
                }
                else {
                    me.sorters.addAll(newSorters);
                }
            }
            else {
                me.sorters.clear();
                me.sorters.addAll(newSorters);
            }
            
            if (doSort !== false) {
                me.onBeforeSort(newSorters);
            }
        }
        
        if (doSort !== false) {
            sorters = me.sorters.items;
            if (sorters.length) {
                
                sorterFn = function(r1, r2) {
                    var result = sorters[0].sort(r1, r2),
                        length = sorters.length,
                        i;

                        
                        for (i = 1; i < length; i++) {
                            result = result || sorters[i].sort.call(this, r1, r2);
                        }

                    return result;
                };

                me.doSort(sorterFn);                
            }
        }
        
        return sorters;
    },
    
    onBeforeSort: Ext.emptyFn,
        
    
    decodeSorters: function(sorters) {
        if (!Ext.isArray(sorters)) {
            if (sorters === undefined) {
                sorters = [];
            } else {
                sorters = [sorters];
            }
        }

        var length = sorters.length,
            Sorter = Ext.util.Sorter,
            fields = this.model ? this.model.prototype.fields : null,
            field,
            config, i;

        for (i = 0; i < length; i++) {
            config = sorters[i];

            if (!(config instanceof Sorter)) {
                if (Ext.isString(config)) {
                    config = {
                        property: config
                    };
                }
                
                Ext.applyIf(config, {
                    root     : this.sortRoot,
                    direction: "ASC"
                });

                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                
                if (fields && !config.transform) {
                    field = fields.get(config.property);
                    config.transform = field ? field.sortType : undefined;
                }
                sorters[i] = Ext.create('Ext.util.Sorter', config);
            }
        }

        return sorters;
    },
    
    getSorters: function() {
        return this.sorters.items;
    }
});

Ext.define('Ext.util.MixedCollection', {
    extend: 'Ext.util.AbstractMixedCollection',
    mixins: {
        sortable: 'Ext.util.Sortable'
    },

    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.addEvents('sort');
        me.mixins.sortable.initSortable.call(me);
    },

    doSort: function(sorterFn) {
        this.sortBy(sorterFn);
    },

    
    _sort : function(property, dir, fn){
        var me = this,
            i, len,
            dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,

            
            c     = [],
            keys  = me.keys,
            items = me.items;

        
        fn = fn || function(a, b) {
            return a - b;
        };

        
        for(i = 0, len = items.length; i < len; i++){
            c[c.length] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        
        Ext.Array.sort(c, function(a, b){
            var v = fn(a[property], b[property]) * dsc;
            if(v === 0){
                v = (a.index < b.index ? -1 : 1);
            }
            return v;
        });

        
        for(i = 0, len = c.length; i < len; i++){
            items[i] = c[i].value;
            keys[i]  = c[i].key;
        }

        me.fireEvent('sort', me);
    },

    
    sortBy: function(sorterFn) {
        var me     = this,
            items  = me.items,
            keys   = me.keys,
            length = items.length,
            temp   = [],
            i;

        
        for (i = 0; i < length; i++) {
            temp[i] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        Ext.Array.sort(temp, function(a, b) {
            var v = sorterFn(a.value, b.value);
            if (v === 0) {
                v = (a.index < b.index ? -1 : 1);
            }

            return v;
        });

        
        for (i = 0; i < length; i++) {
            items[i] = temp[i].value;
            keys[i]  = temp[i].key;
        }
        
        me.fireEvent('sort', me, items, keys);
    },

    
    reorder: function(mapping) {
        var me = this,
            items = me.items,
            index = 0,
            length = items.length,
            order = [],
            remaining = [],
            oldIndex;

        me.suspendEvents();

        
        for (oldIndex in mapping) {
            order[mapping[oldIndex]] = items[oldIndex];
        }

        for (index = 0; index < length; index++) {
            if (mapping[index] == undefined) {
                remaining.push(items[index]);
            }
        }

        for (index = 0; index < length; index++) {
            if (order[index] == undefined) {
                order[index] = remaining.shift();
            }
        }

        me.clear();
        me.addAll(order);

        me.resumeEvents();
        me.fireEvent('sort', me);
    },

    
    sortByKey : function(dir, fn){
        this._sort('key', dir, fn || function(a, b){
            var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
            return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
        });
    }
});


Ext.define('Ext.data.StoreManager', {
    extend: 'Ext.util.MixedCollection',
    alternateClassName: ['Ext.StoreMgr', 'Ext.data.StoreMgr', 'Ext.StoreManager'],
    singleton: true,
    uses: ['Ext.data.ArrayStore'],
    
    

    
    register : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.add(s);
        }
    },

    
    unregister : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.remove(this.lookup(s));
        }
    },

    
    lookup : function(store) {
        
        if (Ext.isArray(store)) {
            var fields = ['field1'], 
                expand = !Ext.isArray(store[0]),
                data = store,
                i,
                len;
                
            if(expand){
                data = [];
                for (i = 0, len = store.length; i < len; ++i) {
                    data.push([store[i]]);
                }
            } else {
                for(i = 2, len = store[0].length; i <= len; ++i){
                    fields.push('field' + i);
                }
            }
            return Ext.create('Ext.data.ArrayStore', {
                data  : data,
                fields: fields,
                autoDestroy: true,
                autoCreated: true,
                expanded: expand
            });
        }
        
        if (Ext.isString(store)) {
            
            return this.get(store);
        } else {
            
            return Ext.data.AbstractStore.create(store);
        }
    },

    
    getKey : function(o) {
         return o.storeId;
    }
}, function() {    
    
    Ext.regStore = function(name, config) {
        var store;

        if (Ext.isObject(name)) {
            config = name;
        } else {
            config.storeId = name;
        }

        if (config instanceof Ext.data.Store) {
            store = config;
        } else {
            store = Ext.create('Ext.data.Store', config);
        }

        return Ext.data.StoreManager.register(store);
    };

    
    Ext.getStore = function(name) {
        return Ext.data.StoreManager.lookup(name);
    };
});



Ext.define('Ext.LoadMask', {

    

    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: ['Ext.data.StoreManager'],

    

    

    
    msg : 'Loading...',
    
    msgCls : Ext.baseCSSPrefix + 'mask-loading',
    
    
    useMsg: true,

    
    disabled: false,

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

        if (el.isComponent) {
            me.bindComponent(el);
        } else {
            me.el = Ext.get(el);
        }
        Ext.apply(me, config);

        me.addEvents('beforeshow', 'show', 'hide');
        if (me.store) {
            me.bindStore(me.store, true);
        }
        me.mixins.observable.constructor.call(me, config);
    },

    bindComponent: function(comp) {
        var me = this,
            listeners = {
                resize: me.onComponentResize,
                scope: me
            };

        if (comp.el) {
            me.onComponentRender(comp);
        } else {
            listeners.render = {
                fn: me.onComponentRender,
                scope: me,
                single: true
            };
        }
        me.mon(comp, listeners);
    },

    
    onComponentRender: function(comp) {
        this.el = comp.getContentTarget();
    },

    
    onComponentResize: function(comp, w, h) {
        this.el.isMasked();
    },

    
    bindStore : function(store, initial) {
        var me = this;

        if (!initial && me.store) {
            me.mun(me.store, {
                scope: me,
                beforeload: me.onBeforeLoad,
                load: me.onLoad,
                exception: me.onLoad
            });
            if(!store) {
                me.store = null;
            }
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            me.mon(store, {
                scope: me,
                beforeload: me.onBeforeLoad,
                load: me.onLoad,
                exception: me.onLoad
            });

        }
        me.store = store;
        if (store && store.isLoading()) {
            me.onBeforeLoad();
        }
    },

    
    disable : function() {
        var me = this;

       me.disabled = true;
       if (me.loading) {
           me.onLoad();
       }
    },

    
    enable : function() {
        this.disabled = false;
    },

    
    isDisabled : function() {
        return this.disabled;
    },

    
    onLoad : function() {
        var me = this;

        me.loading = false;
        me.el.unmask();
        me.fireEvent('hide', me, me.el, me.store);
    },

    
    onBeforeLoad : function() {
        var me = this;

        if (!me.disabled && !me.loading && me.fireEvent('beforeshow', me, me.el, me.store) !== false) {
            if (me.useMsg) {
                me.el.mask(me.msg, me.msgCls, false);
            } else {
                me.el.mask();
            }
            
            me.fireEvent('show', me, me.el, me.store);
            me.loading = true;
        }
    },

    
    show: function() {
        this.onBeforeLoad();
    },

    
    hide: function() {
        this.onLoad();
    },

    
    destroy : function() {
        this.hide();
        this.clearListeners();
    }
});


Ext.define('Ext.ComponentLoader', {

    
    
    extend: 'Ext.ElementLoader',

    statics: {
        Renderer: {
            Data: function(loader, response, active){
                var success = true;
                try {
                    loader.getTarget().update(Ext.decode(response.responseText));
                } catch (e) {
                    success = false;
                }
                return success;
            },

            Component: function(loader, response, active){
                var success = true,
                    target = loader.getTarget(),
                    items = [];

                if (!target.isContainer) {
                    Ext.Error.raise({
                        target: target,
                        msg: 'Components can only be loaded into a container'
                    });
                }

                try {
                    items = Ext.decode(response.responseText);
                } catch (e) {
                    success = false;
                }

                if (success) {
                    if (active.removeAll) {
                        target.removeAll();
                    }
                    target.add(items);
                }
                return success;
            }
        }
    },

    

    
    target: null,

    
    loadMask: false,
    
    

    
    renderer: 'html',

    
    setTarget: function(target){
        var me = this;
        
        if (Ext.isString(target)) {
            target = Ext.getCmp(target);
        }

        if (me.target && me.target != target) {
            me.abort();
        }
        me.target = target;
    },
    
    
    removeMask: function(){
        this.target.setLoading(false);
    },
    
    
    addMask: function(mask){
        this.target.setLoading(mask);
    },

    
    
    setOptions: function(active, options){
        active.removeAll = Ext.isDefined(options.removeAll) ? options.removeAll : this.removeAll;
    },

    
    getRenderer: function(renderer){
        if (Ext.isFunction(renderer)) {
            return renderer;
        }

        var renderers = this.statics().Renderer;
        switch (renderer) {
            case 'component':
                return renderers.Component;
            case 'data':
                return renderers.Data;
            default:
                return Ext.ElementLoader.Renderer.Html;
        }
    }
});



Ext.define('Ext.layout.component.Auto', {

    

    alias: 'layout.autocomponent',

    extend: 'Ext.layout.component.Component',

    

    type: 'autocomponent',

    onLayout : function(width, height) {
        this.setTargetSize(width, height);
    }
});


Ext.define('Ext.AbstractComponent', {

    

    mixins: {
        observable: 'Ext.util.Observable',
        animate: 'Ext.util.Animate',
        state: 'Ext.state.Stateful'
    },

    requires: [
        'Ext.PluginManager',
        'Ext.ComponentManager',
        'Ext.core.Element',
        'Ext.core.DomHelper',
        'Ext.XTemplate',
        'Ext.ComponentQuery',
        'Ext.LoadMask',
        'Ext.ComponentLoader',
        'Ext.EventManager',
        'Ext.layout.Layout',
        'Ext.layout.component.Auto'
    ],

    
    
    uses: [
        'Ext.ZIndexManager'
    ],

    statics: {
        AUTO_ID: 1000
    },

    

    isComponent: true,

    getAutoId: function() {
        return ++Ext.AbstractComponent.AUTO_ID;
    },

    

    

    

    

    
    renderTpl: null,

    

    

    

    

    

    

    

    
    tplWriteMode: 'overwrite',

    
    baseCls: Ext.baseCSSPrefix + 'component',

    

    

    

    
    disabledCls: Ext.baseCSSPrefix + 'item-disabled',

    
    ui: 'default',
    
    
    uiCls: [],
    
    

    

    

    

    

    

    
    hidden: false,

    
    disabled: false,

    

    
    draggable: false,

    
    floating: false,

    
    hideMode: 'display',

    

    

    
    styleHtmlContent: false,

    
    styleHtmlCls: Ext.baseCSSPrefix + 'html',

    
    
    
    

    

     
     allowDomMove: true,

     
     autoShow: false,

    
     autoRender: false,

     needsLayout: false,

    

    
    rendered: false,

    weight: 0,

    trimRe: /^\s+|\s+$/g,
    spacesRe: /\s+/,
    
    
         
    maskOnDisable: true,

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

        config = config || {};
        me.initialConfig = config;
        Ext.apply(me, config);

        me.addEvents(
            
             'beforeactivate',
            
             'activate',
            
             'beforedeactivate',
            
             'deactivate',
            
             'added',
            
             'disable',
            
             'enable',
            
             'beforeshow',
            
             'show',
            
             'beforehide',
            
             'hide',
            
             'removed',
            
             'beforerender',
            
             'render',
            
             'afterrender',
            
             'beforedestroy',
            
             'destroy',
            
             'resize',
            
             'move'
        );

        me.getId();

        me.mons = [];
        me.additionalCls = [];
        me.renderData = me.renderData || {};
        me.renderSelectors = me.renderSelectors || {};

        if (me.plugins) {
            me.plugins = [].concat(me.plugins);
            for (i = 0, len = me.plugins.length; i < len; i++) {
                me.plugins[i] = me.constructPlugin(me.plugins[i]);
            }
        }
        
        me.initComponent();

        
        Ext.ComponentManager.register(me);

        
        me.mixins.observable.constructor.call(me);
        me.mixins.state.constructor.call(me, config);

        
        if (me.plugins) {
            me.plugins = [].concat(me.plugins);
            for (i = 0, len = me.plugins.length; i < len; i++) {
                me.plugins[i] = me.initPlugin(me.plugins[i]);
            }
        }

        me.loader = me.getLoader();

        if (me.renderTo) {
            me.render(me.renderTo);
            
            
            
        }

        if (me.autoShow) {
            me.show();
        }
        
        if (Ext.isDefined(me.disabledClass)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.Component: disabledClass has been deprecated. Please use disabledCls.');
            }
            me.disabledCls = me.disabledClass;
            delete me.disabledClass;
        }
    },

    initComponent: Ext.emptyFn,

    show: Ext.emptyFn,

    animate: function(animObj) {
        var me = this,
            to;

        animObj = animObj || {};
        to = animObj.to || {};

        if (Ext.fx.Manager.hasFxBlock(me.id)) {
            return me;
        }
        
        if (!animObj.dynamic && (to.height || to.width)) {
            var curWidth = me.getWidth(),
                w = curWidth,
                curHeight = me.getHeight(),
                h = curHeight,
                needsResize = false;

            if (to.height && to.height > curHeight) {
                h = to.height;
                needsResize = true;
            }
            if (to.width && to.width > curWidth) {
                w = to.width;
                needsResize = true;
            }

            
            
            
            if (needsResize) {
                var clearWidth = !Ext.isNumber(me.width),
                    clearHeight = !Ext.isNumber(me.height);

                me.componentLayout.childrenChanged = true;
                me.setSize(w, h, me.ownerCt);
                me.el.setSize(curWidth, curHeight);
                if (clearWidth) {
                    delete me.width;
                }
                if (clearHeight) {
                    delete me.height;
                }
            }
        }
        return me.mixins.animate.animate.apply(me, arguments);
    },

    
    findLayoutController: function() {
        return this.findParentBy(function(c) {
            
            
            return !c.ownerCt || (c.layout.layoutBusy && !c.ownerCt.layout.layoutBusy);
        });
    },

    onShow : function() {
        
        var needsLayout = this.needsLayout;
        if (Ext.isObject(needsLayout)) {
            this.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize, needsLayout.ownerCt);
        }
    },

    constructPlugin: function(plugin) {
        if (plugin.ptype && typeof plugin.init != 'function') {
            plugin.cmp = this;
            plugin = Ext.PluginManager.create(plugin);
        }
        else if (typeof plugin == 'string') {
            plugin = Ext.PluginManager.create({
                ptype: plugin,
                cmp: this
            });
        }
        return plugin;
    },

    
    initPlugin : function(plugin) {
        plugin.init(this);

        return plugin;
    },

    
    doAutoRender: function() {
        var me = this;
        if (me.floating) {
            me.render(document.body);
        } else {
            me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
        }
    },

    
    render : function(container, position) {
        var me = this;

        if (!me.rendered && me.fireEvent('beforerender', me) !== false) {
            
            
            if (me.el) {
                me.el = Ext.get(me.el);
            }

            
            if (me.floating) {
                me.onFloatRender();
            }

            container = me.initContainer(container);

            me.onRender(container, position);

            
            
            me.el.setVisibilityMode(Ext.core.Element[me.hideMode.toUpperCase()]);

            if (me.overCls) {
                me.el.hover(me.addOverCls, me.removeOverCls, me);
            }

            me.fireEvent('render', me);

            me.initContent();

            me.afterRender(container);
            me.fireEvent('afterrender', me);

            me.initEvents();

            if (me.hidden) {
                
                
                
                me.el.hide();
            }

            if (me.disabled) {
                
                me.disable(true);
            }
        }
        return me;
    },

    
    onRender : function(container, position) {
        var me = this,
            el = me.el,
            cls = me.initCls(),
            styles = me.initStyles(),
            renderTpl, renderData, i;

        position = me.getInsertPosition(position);

        if (!el) {
            if (position) {
                el = Ext.core.DomHelper.insertBefore(position, me.getElConfig(), true);
            }
            else {
                el = Ext.core.DomHelper.append(container, me.getElConfig(), true);
            }
        }
        else if (me.allowDomMove !== false) {
            if (position) {
                container.dom.insertBefore(el.dom, position);
            } else {
                container.dom.appendChild(el.dom);
            }
        }

        if (Ext.scopeResetCSS && !me.ownerCt) {
            
            if (el.dom == Ext.getBody().dom) {
                el.parent().addCls(Ext.baseCSSPrefix + 'reset');
            }
            else {
                
                me.resetEl = el.wrap({
                    cls: Ext.baseCSSPrefix + 'reset'
                });
            }
        }

        el.addCls(cls);
        el.setStyle(styles);

        
        
        
        
        
        
        
        
        
        

        me.el = el;
        
        me.rendered = true;
        me.addUIToElement(true);
        
        for (i = 0; i < me.uiCls.length; i++) {
            me.addUIClsToElement(me.uiCls[i], true);
        }
        me.rendered = false;
        me.initFrame();

        renderTpl = me.initRenderTpl();
        if (renderTpl) {
            renderData = me.initRenderData();
            renderTpl.append(me.getTargetEl(), renderData);
        }

        me.applyRenderSelectors();
        
        me.rendered = true;
        
        me.setUI(me.ui);
    },

    
    afterRender : function() {
        var me = this,
            pos,
            xy;

        me.getComponentLayout();

        
        if (!me.ownerCt || (me.height || me.width)) {
            me.setSize(me.width, me.height);
        }

        
        
        if (me.floating && (me.x === undefined || me.y === undefined)) {
            if (me.floatParent) {
                xy = me.el.getAlignToXY(me.floatParent.getTargetEl(), 'c-c');
                pos = me.floatParent.getTargetEl().translatePoints(xy[0], xy[1]);
            } else {
                xy = me.el.getAlignToXY(me.container, 'c-c');
                pos = me.container.translatePoints(xy[0], xy[1]);
            }
            me.x = me.x === undefined ? pos.left: me.x;
            me.y = me.y === undefined ? pos.top: me.y;
        }

        if (Ext.isDefined(me.x) || Ext.isDefined(me.y)) {
            me.setPosition(me.x, me.y);
        }

        if (me.styleHtmlContent) {
            me.getTargetEl().addCls(me.styleHtmlCls);
        }
    },

    frameCls: Ext.baseCSSPrefix + 'frame',

    frameTpl: [
        '<tpl if="top">',
            '<tpl if="left"><div class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl></tpl>" style="background-position: {tl}; padding-left: {frameWidth}px" role="presentation"></tpl>',
                '<tpl if="right"><div class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl></tpl>" style="background-position: {tr}; padding-right: {frameWidth}px" role="presentation"></tpl>',
                    '<div class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl></tpl>" style="background-position: {tc}; height: {frameWidth}px" role="presentation"></div>',
                '<tpl if="right"></div></tpl>',
            '<tpl if="left"></div></tpl>',
        '</tpl>',
        '<tpl if="left"><div class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl></tpl>" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"></tpl>',
            '<tpl if="right"><div class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl></tpl>" style="background-position: {mr}; padding-right: {frameWidth}px" role="presentation"></tpl>',
                '<div class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl></tpl>" role="presentation"></div>',
            '<tpl if="right"></div></tpl>',
        '<tpl if="left"></div></tpl>',
        '<tpl if="bottom">',
            '<tpl if="left"><div class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl></tpl>" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"></tpl>',
                '<tpl if="right"><div class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl></tpl>" style="background-position: {br}; padding-right: {frameWidth}px" role="presentation"></tpl>',
                    '<div class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl></tpl>" style="background-position: {bc}; height: {frameWidth}px" role="presentation"></div>',
                '<tpl if="right"></div></tpl>',
            '<tpl if="left"></div></tpl>',
        '</tpl>'
    ],

    frameTableTpl: [
        '<table><tbody>',
            '<tpl if="top">',
                '<tr>',
                    '<tpl if="left"><td class="{frameCls}-tl {baseCls}-tl {baseCls}-{ui}-tl<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tl</tpl></tpl>" style="background-position: {tl}; padding-left:{frameWidth}px" role="presentation"></td></tpl>',
                    '<td class="{frameCls}-tc {baseCls}-tc {baseCls}-{ui}-tc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tc</tpl></tpl>" style="background-position: {tc}; height: {frameWidth}px" role="presentation"></td>',
                    '<tpl if="right"><td class="{frameCls}-tr {baseCls}-tr {baseCls}-{ui}-tr<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-tr</tpl></tpl>" style="background-position: {tr}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
                '</tr>',
            '</tpl>',
            '<tr>',
                '<tpl if="left"><td class="{frameCls}-ml {baseCls}-ml {baseCls}-{ui}-ml<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-ml</tpl></tpl>" style="background-position: {ml}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
                '<td class="{frameCls}-mc {baseCls}-mc {baseCls}-{ui}-mc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mc</tpl></tpl>" style="background-position: 0 0;" role="presentation"></td>',
                '<tpl if="right"><td class="{frameCls}-mr {baseCls}-mr {baseCls}-{ui}-mr<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-mr</tpl></tpl>" style="background-position: {mr}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
            '</tr>',
            '<tpl if="bottom">',
                '<tr>',
                    '<tpl if="left"><td class="{frameCls}-bl {baseCls}-bl {baseCls}-{ui}-bl<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bl</tpl></tpl>" style="background-position: {bl}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
                    '<td class="{frameCls}-bc {baseCls}-bc {baseCls}-{ui}-bc<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-bc</tpl></tpl>" style="background-position: {bc}; height: {frameWidth}px" role="presentation"></td>',
                    '<tpl if="right"><td class="{frameCls}-br {baseCls}-br {baseCls}-{ui}-br<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-{parent.ui}-{.}-br</tpl></tpl>" style="background-position: {br}; padding-left: {frameWidth}px" role="presentation"></td></tpl>',
                '</tr>',
            '</tpl>',
        '</tbody></table>'
    ],
    
    
    initFrame : function() {
        if (Ext.supports.CSS3BorderRadius) {
            return false;
        }
        
        var me = this,
            frameInfo = me.getFrameInfo(),
            frameWidth = frameInfo.width,
            frameTpl = me.getFrameTpl(frameInfo.table);
                        
        if (me.frame) {
            
            frameTpl.insertFirst(me.el, Ext.apply({}, {
                ui:         me.ui,
                uiCls:      me.uiCls,
                frameCls:   me.frameCls,
                baseCls:    me.baseCls,
                frameWidth: frameWidth,
                top:        !!frameInfo.top,
                left:       !!frameInfo.left,
                right:      !!frameInfo.right,
                bottom:     !!frameInfo.bottom
            }, me.getFramePositions(frameInfo)));

            
            me.frameBody = me.el.down('.' + me.frameCls + '-mc');
            
            
            Ext.apply(me.renderSelectors, {
                frameTL: '.' + me.baseCls + '-tl',
                frameTC: '.' + me.baseCls + '-tc',
                frameTR: '.' + me.baseCls + '-tr',
                frameML: '.' + me.baseCls + '-ml',
                frameMC: '.' + me.baseCls + '-mc',
                frameMR: '.' + me.baseCls + '-mr',
                frameBL: '.' + me.baseCls + '-bl',
                frameBC: '.' + me.baseCls + '-bc',
                frameBR: '.' + me.baseCls + '-br'
            });
        }
    },
    
    updateFrame: function() {
        if (Ext.supports.CSS3BorderRadius) {
            return false;
        }
        
        var me = this,
            wasTable = this.frameSize && this.frameSize.table,
            oldFrameTL = this.frameTL,
            oldFrameBL = this.frameBL,
            oldFrameML = this.frameML,
            oldFrameMC = this.frameMC,
            newMCClassName;
        
        this.initFrame();
        
        if (oldFrameMC) {
            if (me.frame) {                
                
                delete me.frameTL;
                delete me.frameTC;
                delete me.frameTR;
                delete me.frameML;
                delete me.frameMC;
                delete me.frameMR;
                delete me.frameBL;
                delete me.frameBC;
                delete me.frameBR;    
                this.applyRenderSelectors();
                
                
                newMCClassName = this.frameMC.dom.className;
                
                
                oldFrameMC.insertAfter(this.frameMC);
                this.frameMC.remove();
                
                
                this.frameBody = this.frameMC = oldFrameMC;
                
                
                oldFrameMC.dom.className = newMCClassName;
                
                
                if (wasTable) {
                    me.el.query('> table')[1].remove();
                }                                
                else {
                    if (oldFrameTL) {
                        oldFrameTL.remove();
                    }
                    if (oldFrameBL) {
                        oldFrameBL.remove();
                    }
                    oldFrameML.remove();
                }
            }
            else {
                
                
            }
        }
        else if (me.frame) {
            this.applyRenderSelectors();
        }
    },
    
    getFrameInfo: function() {
        if (Ext.supports.CSS3BorderRadius) {
            return false;
        }
        
        var me = this,
            left = me.el.getStyle('background-position-x'),
            top = me.el.getStyle('background-position-y'),
            info, frameInfo = false, max;

        
        
        if (!left && !top) {
            info = me.el.getStyle('background-position').split(' ');
            left = info[0];
            top = info[1];
        }
        
        
        
        
        if (parseInt(left, 10) >= 1000000 && parseInt(top, 10) >= 1000000) {
            max = Math.max;
            
            frameInfo = {
                
                table: left.substr(0, 3) == '110',
                
                
                vertical: top.substr(0, 3) == '110',
                
                
                top:    max(left.substr(3, 2), left.substr(5, 2)),
                right:  max(left.substr(5, 2), top.substr(3, 2)),
                bottom: max(top.substr(3, 2), top.substr(5, 2)),
                left:   max(top.substr(5, 2), left.substr(3, 2))
            };
            
            frameInfo.width = max(frameInfo.top, frameInfo.right, frameInfo.bottom, frameInfo.left);

            
            me.el.setStyle('background-image', 'none');
        }        
        
        
        
        if (me.frame === true && !frameInfo) {
            Ext.Error.raise("You have set frame: true explicity on this component while it doesn't have any " +
                            "framing defined in the CSS template. In this case IE can't figure out what sizes " +
                            "to use and thus framing on this component will be disabled.");
        }
        
        me.frame = me.frame || !!frameInfo;
        me.frameSize = frameInfo || false;
        
        return frameInfo;
    },
    
    getFramePositions: function(frameInfo) {
        var me = this,
            frameWidth = frameInfo.width,
            dock = me.dock,
            positions, tc, bc, ml, mr;
            
        if (frameInfo.vertical) {
            tc = '0 -' + (frameWidth * 0) + 'px';
            bc = '0 -' + (frameWidth * 1) + 'px';
            
            if (dock && dock == "right") {
                tc = 'right -' + (frameWidth * 0) + 'px';
                bc = 'right -' + (frameWidth * 1) + 'px';
            }
            
            positions = {
                tl: '0 -' + (frameWidth * 0) + 'px',
                tr: '0 -' + (frameWidth * 1) + 'px',
                bl: '0 -' + (frameWidth * 2) + 'px',
                br: '0 -' + (frameWidth * 3) + 'px',

                ml: '-' + (frameWidth * 1) + 'px 0',
                mr: 'right 0',

                tc: tc,
                bc: bc
            };
        } else {
            ml = '-' + (frameWidth * 0) + 'px 0';
            mr = 'right 0';
            
            if (dock && dock == "bottom") {
                ml = 'left bottom';
                mr = 'right bottom';
            }
            
            positions = {
                tl: '0 -' + (frameWidth * 2) + 'px',
                tr: 'right -' + (frameWidth * 3) + 'px',
                bl: '0 -' + (frameWidth * 4) + 'px',
                br: 'right -' + (frameWidth * 5) + 'px',

                ml: ml,
                mr: mr,

                tc: '0 -' + (frameWidth * 0) + 'px',
                bc: '0 -' + (frameWidth * 1) + 'px'
            };
        }
        
        return positions;
    },
    
    
    getFrameTpl : function(table) {
        return table ? this.getTpl('frameTableTpl') : this.getTpl('frameTpl');
    },

    
    initCls: function() {
        var me = this,
            cls = [];

        cls.push(me.baseCls);

        if (Ext.isDefined(me.cmpCls)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.Component: cmpCls has been deprecated. Please use componentCls.');
            }
            me.componentCls = me.cmpCls;
            delete me.cmpCls;
        }

        if (me.componentCls) {
            cls.push(me.componentCls);
        } else {
            me.componentCls = me.baseCls;
        }
        if (me.cls) {
            cls.push(me.cls);
            delete me.cls;
        }

        return cls.concat(me.additionalCls);
    },
    
    
    setUI: function(ui) {
        var me = this,
            oldUICls = Ext.Array.clone(me.uiCls),
            newUICls = [],
            cls,
            i;
        
        
        for (i = 0; i < oldUICls.length; i++) {
            cls = oldUICls[i];
            
            me.removeClsWithUI(cls);
            newUICls.push(cls);
        }
        
        
        me.removeUIFromElement();
        
        
        me.ui = ui;
        
        
        me.addUIToElement();
        
        
        for (i = 0; i < newUICls.length; i++) {
            cls = newUICls[i];
            
            me.addClsWithUI(cls);
        }
    },
    
    
    addClsWithUI: function(cls) {
        var me = this,
            i;
        
        if (!Ext.isArray(cls)) {
            cls = [cls];
        }
        
        for (i = 0; i < cls.length; i++) {
            if (cls[i] && !me.hasUICls(cls[i])) {
                me.uiCls = Ext.Array.clone(me.uiCls);
                me.uiCls.push(cls[i]);
                me.addUIClsToElement(cls[i]);
            }
        }
    },
    
    
    removeClsWithUI: function(cls) {
        var me = this,
            i;
        
        if (!Ext.isArray(cls)) {
            cls = [cls];
        }
        
        for (i = 0; i < cls.length; i++) {
            if (cls[i] && me.hasUICls(cls[i])) {
                me.uiCls = Ext.Array.remove(me.uiCls, cls[i]);
                me.removeUIClsFromElement(cls[i]);
            }
        }
    },
    
    
    hasUICls: function(cls) {
        var me = this,
            uiCls = me.uiCls || [];
        
        return Ext.Array.contains(uiCls, cls);
    },
    
    
    addUIClsToElement: function(cls, force) {
        var me = this;
        
        me.addCls(Ext.baseCSSPrefix + cls);
        me.addCls(me.baseCls + '-' + cls);
        me.addCls(me.baseCls + '-' + me.ui + '-' + cls);
        
        if (!force && me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
                i, el;
            
            
            for (i = 0; i < els.length; i++) {
                el = me['frame' + els[i].toUpperCase()];
                
                if (el && el.dom) {
                    el.addCls(me.baseCls + '-' + me.ui + '-' + els[i]);
                    el.addCls(me.baseCls + '-' + me.ui + '-' + cls + '-' + els[i]);
                }
            }
        }
    },
    
    
    removeUIClsFromElement: function(cls, force) {
        var me = this;
        
        me.removeCls(Ext.baseCSSPrefix + cls);
        me.removeCls(me.baseCls + '-' + cls);
        me.removeCls(me.baseCls + '-' + me.ui + '-' + cls);
        
        if (!force &&me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
                i, el;
            
            
            for (i = 0; i < els.length; i++) {
                el = me['frame' + els[i].toUpperCase()];
                if (el && el.dom) {
                    el.removeCls(me.baseCls + '-' + me.ui + '-' + cls + '-' + els[i]);
                }
            }
        }
    },
    
    
    addUIToElement: function(force) {
        var me = this;
        
        me.addCls(me.baseCls + '-' + me.ui);
        
        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
                i, el;
            
            
            for (i = 0; i < els.length; i++) {
                el = me['frame' + els[i].toUpperCase()];
                
                if (el) {
                    el.addCls(me.baseCls + '-' + me.ui + '-' + els[i]);
                }
            }
        }
    },
    
    
    removeUIFromElement: function() {
        var me = this;
        
        me.removeCls(me.baseCls + '-' + me.ui);
        
        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
            
            var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
                i, el;
            
            
            for (i = 0; i < els.length; i++) {
                el = me['frame' + els[i].toUpperCase()];
                if (el) {
                    el.removeCls(me.baseCls + '-' + me.ui + '-' + els[i]);
                }
            }
        }
    },
    
    getElConfig : function() {
        var result = this.autoEl || {tag: 'div'};
        result.id = this.id;
        return result;
    },

    
    getInsertPosition: function(position) {
        
        if (position !== undefined) {
            if (Ext.isNumber(position)) {
                position = this.container.dom.childNodes[position];
            }
            else {
                position = Ext.getDom(position);
            }
        }

        return position;
    },

    
    initContainer: function(container) {
        var me = this;

        
        
        
        if (!container && me.el) {
            container = me.el.dom.parentNode;
            me.allowDomMove = false;
        }

        me.container = Ext.get(container);

        if (me.ctCls) {
            me.container.addCls(me.ctCls);
        }

        return me.container;
    },

    
    initRenderData: function() {
        var me = this;

        return Ext.applyIf(me.renderData, {
            ui: me.ui,
            uiCls: me.uiCls,
            baseCls: me.baseCls,
            componentCls: me.componentCls,
            frame: me.frame
        });
    },

    
    getTpl: function(name) {
        var prototype = this.self.prototype,
            ownerPrototype;

        if (this.hasOwnProperty(name)) {
            if (!(this[name] instanceof Ext.XTemplate)) {
                this[name] = Ext.ClassManager.dynInstantiate('Ext.XTemplate', this[name]);
            }

            return this[name];
        }

        if (!(prototype[name] instanceof Ext.XTemplate)) {
            ownerPrototype = prototype;

            do {
                if (ownerPrototype.hasOwnProperty(name)) {
                    ownerPrototype[name] = Ext.ClassManager.dynInstantiate('Ext.XTemplate', ownerPrototype[name]);
                    break;
                }

                ownerPrototype = ownerPrototype.superclass;
            } while (ownerPrototype);
        }

        return prototype[name];
    },

    
    initRenderTpl: function() {
        return this.getTpl('renderTpl');
    },

    
    initStyles: function() {
        var style = {},
            me = this,
            Element = Ext.core.Element;

        if (Ext.isString(me.style)) {
            style = Element.parseStyles(me.style);
        } else {
            style = Ext.apply({}, me.style);
        }

        
        
        if (me.padding !== undefined) {
            style.padding = Element.unitizeBox((me.padding === true) ? 5 : me.padding);
        }

        if (me.margin !== undefined) {
            style.margin = Element.unitizeBox((me.margin === true) ? 5 : me.margin);
        }

        delete me.style;
        return style;
    },

    
    initContent: function() {
        var me = this,
            target = me.getTargetEl(),
            contentEl,
            pre;

        if (me.html) {
            target.update(Ext.core.DomHelper.markup(me.html));
            delete me.html;
        }

        if (me.contentEl) {
            contentEl = Ext.get(me.contentEl);
            pre = Ext.baseCSSPrefix;
            contentEl.removeCls([pre + 'hidden', pre + 'hide-display', pre + 'hide-offsets', pre + 'hide-nosize']);
            target.appendChild(contentEl.dom);
        }

        if (me.tpl) {
            
            if (!me.tpl.isTemplate) {
                me.tpl = Ext.create('Ext.XTemplate', me.tpl);
            }

            if (me.data) {
                me.tpl[me.tplWriteMode](target, me.data);
                delete me.data;
            }
        }
    },

    
    initEvents : function() {
        var me = this,
            afterRenderEvents = me.afterRenderEvents,
            property, listeners;
        if (afterRenderEvents) {
            for (property in afterRenderEvents) {
                if (afterRenderEvents.hasOwnProperty(property)) {
                    listeners = afterRenderEvents[property];
                    if (me[property] && me[property].on) {
                        me.mon(me[property], listeners);
                    }
                }
            }
        }
    },

    
    applyRenderSelectors: function() {
        var selectors = this.renderSelectors || {},
            el = this.el.dom,
            selector;

        for (selector in selectors) {
            if (selectors.hasOwnProperty(selector) && selectors[selector]) {
                this[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], el));
            }
        }
    },

    
    is: function(selector) {
        return Ext.ComponentQuery.is(this, selector);
    },

    
    up: function(selector) {
        var result = this.ownerCt;
        if (selector) {
            for (; result; result = result.ownerCt) {
                if (Ext.ComponentQuery.is(result, selector)) {
                    return result;
                }
            }
        }
        return result;
    },

    
    nextSibling: function(selector) {
        var o = this.ownerCt, it, last, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this) + 1;
            if (idx) {
                if (selector) {
                    for (last = it.getCount(); idx < last; idx++) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx < it.getCount()) {
                        return it.getAt(idx);
                    }
                }
            }
        }
        return null;
    },

    
    previousSibling: function(selector) {
        var o = this.ownerCt, it, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this);
            if (idx != -1) {
                if (selector) {
                    for (--idx; idx >= 0; idx--) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx) {
                        return it.getAt(--idx);
                    }
                }
            }
        }
        return null;
    },

    
    previousNode: function(selector, includeSelf) {
        var node = this,
            result,
            it, len, i;

        
        if (includeSelf && node.is(selector)) {
            return node;
        }

        result = this.prev(selector);
        if (result) {
            return result;
        }

        if (node.ownerCt) {
            for (it = node.ownerCt.items.items, i = Ext.Array.indexOf(it, node) - 1; i > -1; i--) {
                if (it[i].query) {
                    result = it[i].query(selector);
                    result = result[result.length - 1];
                    if (result) {
                        return result;
                    }
                }
            }
            return node.ownerCt.previousNode(selector, true);
        }
    },

    
    nextNode: function(selector, includeSelf) {
        var node = this,
            result,
            it, len, i;

        
        if (includeSelf && node.is(selector)) {
            return node;
        }

        result = this.next(selector);
        if (result) {
            return result;
        }

        if (node.ownerCt) {
            for (it = node.ownerCt.items, i = it.indexOf(node) + 1, it = it.items, len = it.length; i < len; i++) {
                if (it[i].down) {
                    result = it[i].down(selector);
                    if (result) {
                        return result;
                    }
                }
            }
            return node.ownerCt.nextNode(selector);
        }
    },

    
    getId : function() {
        return this.id || (this.id = 'ext-comp-' + (this.getAutoId()));
    },

    getItemId : function() {
        return this.itemId || this.id;
    },

    
    getEl : function() {
        return this.el;
    },

    
    getTargetEl: function() {
        return this.frameBody || this.el;
    },

    
    isXType: function(xtype, shallow) {
        
        if (Ext.isFunction(xtype)) {
            xtype = xtype.xtype;
            
        } else if (Ext.isObject(xtype)) {
            xtype = xtype.statics().xtype;
            
        }

        return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1: this.self.xtype == xtype;
    },

    
    getXTypes: function() {
        var self = this.self,
            xtypes      = [],
            parentPrototype  = this,
            xtype;

        if (!self.xtypes) {
            while (parentPrototype && Ext.getClass(parentPrototype)) {
                xtype = Ext.getClass(parentPrototype).xtype;

                if (xtype !== undefined) {
                    xtypes.unshift(xtype);
                }

                parentPrototype = parentPrototype.superclass;
            }

            self.xtypeChain = xtypes;
            self.xtypes = xtypes.join('/');
        }

        return self.xtypes;
    },

    
    update : function(htmlOrData, loadScripts, cb) {
        var me = this;

        if (me.tpl && !Ext.isString(htmlOrData)) {
            me.data = htmlOrData;
            if (me.rendered) {
                me.tpl[me.tplWriteMode](me.getTargetEl(), htmlOrData || {});
            }
        } else {
            me.html = Ext.isObject(htmlOrData) ? Ext.core.DomHelper.markup(htmlOrData) : htmlOrData;
            if (me.rendered) {
                me.getTargetEl().update(me.html, loadScripts, cb);
            }
        }

        if (me.rendered) {
            me.doComponentLayout();
        }
    },

    
    setVisible : function(visible) {
        return this[visible ? 'show': 'hide']();
    },

    
    isVisible: function(deep) {
        var me = this,
            child = me,
            visible = !me.hidden,
            ancestor = me.ownerCt;

        
        me.hiddenAncestor = false;
        if (me.destroyed) {
            return false;
        }

        if (deep && visible && me.rendered && ancestor) {
            while (ancestor) {
                
                
                
                
                if (ancestor.hidden || (ancestor.collapsed &&
                        !(ancestor.getDockedItems && Ext.Array.contains(ancestor.getDockedItems(), child)))) {
                    
                    me.hiddenAncestor = ancestor;
                    visible = false;
                    break;
                }
                child = ancestor;
                ancestor = ancestor.ownerCt;
            }
        }
        return visible;
    },

    
    enable: function(silent) {
        var me = this;

        if (me.rendered) {
            me.el.removeCls(me.disabledCls);
            me.el.dom.disabled = false;
            me.onEnable();
        }

        me.disabled = false;

        if (silent !== true) {
            me.fireEvent('enable', me);
        }

        return me;
    },

    
    disable: function(silent) {
        var me = this;

        if (me.rendered) {
            me.el.addCls(me.disabledCls);
            me.el.dom.disabled = true;
            me.onDisable();
        }

        me.disabled = true;

        if (silent !== true) {
            me.fireEvent('disable', me);
        }

        return me;
    },
    
    
    onEnable: function() {
        if (this.maskOnDisable) {
            this.el.unmask();
        }        
    },

    
    onDisable : function() {
        if (this.maskOnDisable) {
            this.el.mask();
        }
    },
    
    
    isDisabled : function() {
        return this.disabled;
    },

    
    setDisabled : function(disabled) {
        return this[disabled ? 'disable': 'enable']();
    },

    
    isHidden : function() {
        return this.hidden;
    },

    
    addCls : function(className) {
        var me = this;
        if (!className) {
            return me;
        }
        if (!Ext.isArray(className)){
            className = className.replace(me.trimRe, '').split(me.spacesRe);
        }
        if (me.rendered) {
            me.el.addCls(className);
        }
        else {
            me.additionalCls = Ext.Array.unique(me.additionalCls.concat(className));
        }
        return me;
    },

    
    addClass : function() {
        return this.addCls.apply(this, arguments);
    },

    
    removeCls : function(className) {
        var me = this;

        if (!className) {
            return me;
        }
        if (!Ext.isArray(className)){
            className = className.replace(me.trimRe, '').split(me.spacesRe);
        }
        if (me.rendered) {
            me.el.removeCls(className);
        }
        else if (me.additionalCls.length) {
            Ext.each(className, function(cls) {
                Ext.Array.remove(me.additionalCls, cls);
            });
        }
        return me;
    },

    removeClass : function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.Component: removeClass has been deprecated. Please use removeCls.');
        }
        return this.removeCls.apply(this, arguments);
    },

    addOverCls: function() {
        var me = this;
        if (!me.disabled) {
            me.el.addCls(me.overCls);
        }
    },

    removeOverCls: function() {
        this.el.removeCls(this.overCls);
    },

    addListener : function(element, listeners, scope, options) {
        var me = this,
            fn,
            option;

        if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) {
            if (options.element) {
                fn = listeners;

                listeners = {};
                listeners[element] = fn;
                element = options.element;
                if (scope) {
                    listeners.scope = scope;
                }

                for (option in options) {
                    if (options.hasOwnProperty(option)) {
                        if (me.eventOptionsRe.test(option)) {
                            listeners[option] = options[option];
                        }
                    }
                }
            }

            
            
            if (me[element] && me[element].on) {
                me.mon(me[element], listeners);
            } else {
                me.afterRenderEvents = me.afterRenderEvents || {};
                me.afterRenderEvents[element] = listeners;
            }
        }

        return me.mixins.observable.addListener.apply(me, arguments);
    },
    
    
    removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
        var me = this,
            element = managedListener.options ? managedListener.options.element : null;
        
        if (element) {
            element = me[element];
            if (element && element.un) {
                if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
                    element.un(managedListener.ename, managedListener.fn, managedListener.scope);
                    if (!isClear) {
                        Ext.Array.remove(me.managedListeners, managedListener);
                    }
                }
            }
        } else {
            return me.mixins.observable.removeManagedListenerItem.apply(me, arguments);
        }
    },

    
    getBubbleTarget : function() {
        return this.ownerCt;
    },

    
    isFloating : function() {
        return this.floating;
    },

    
    isDraggable : function() {
        return !!this.draggable;
    },

    
    isDroppable : function() {
        return !!this.droppable;
    },

    
    onAdded : function(container, pos) {
        this.ownerCt = container;
        this.fireEvent('added', this, container, pos);
    },

    
    onRemoved : function() {
        var me = this;

        me.fireEvent('removed', me, me.ownerCt);
        delete me.ownerCt;
    },

    
    beforeDestroy : Ext.emptyFn,
    
    
    onResize : Ext.emptyFn,

    
    setSize : function(width, height) {
        var me = this,
            layoutCollection;

        
        if (Ext.isObject(width)) {
            height = width.height;
            width  = width.width;
        }

        
        if (Ext.isNumber(width)) {
            width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
        }
        if (Ext.isNumber(height)) {
            height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
        }

        if (!me.rendered || !me.isVisible()) {
            
            if (me.hiddenAncestor) {
                layoutCollection = me.hiddenAncestor.layoutOnShow;
                layoutCollection.remove(me);
                layoutCollection.add(me);
            }
            me.needsLayout = {
                width: width,
                height: height,
                isSetSize: true
            };
            if (!me.rendered) {
                me.width  = (width !== undefined) ? width : me.width;
                me.height = (height !== undefined) ? height : me.height;
            }
            return me;
        }
        me.doComponentLayout(width, height, true);

        return me;
    },

    setCalculatedSize : function(width, height, ownerCt) {
        var me = this,
            layoutCollection;

        
        if (Ext.isObject(width)) {
            ownerCt = width.ownerCt;
            height = width.height;
            width  = width.width;
        }

        
        if (Ext.isNumber(width)) {
            width = Ext.Number.constrain(width, me.minWidth, me.maxWidth);
        }
        if (Ext.isNumber(height)) {
            height = Ext.Number.constrain(height, me.minHeight, me.maxHeight);
        }

        if (!me.rendered || !me.isVisible()) {
            
            if (me.hiddenAncestor) {
                layoutCollection = me.hiddenAncestor.layoutOnShow;
                layoutCollection.remove(me);
                layoutCollection.add(me);
            }
            me.needsLayout = {
                width: width,
                height: height,
                isSetSize: false,
                ownerCt: ownerCt
            };
            return me;
        }
        me.doComponentLayout(width, height, false, ownerCt);

        return me;
    },

    
    doComponentLayout : function(width, height, isSetSize, ownerCt) {
        var me = this,
            componentLayout = me.getComponentLayout();

        
        
        
        if (me.rendered && componentLayout) {
            width = (width !== undefined) ? width : me.width;
            height = (height !== undefined) ? height : me.height;
            if (isSetSize) {
                me.width = width;
                me.height = height;
            }

            componentLayout.layout(width, height, isSetSize, ownerCt);
        }
        return me;
    },

    
    setComponentLayout : function(layout) {
        var currentLayout = this.componentLayout;
        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null);
        }
        this.componentLayout = layout;
        layout.setOwner(this);
    },

    getComponentLayout : function() {
        var me = this;

        if (!me.componentLayout || !me.componentLayout.isLayout) {
            me.setComponentLayout(Ext.layout.Layout.create(me.componentLayout, 'autocomponent'));
        }
        return me.componentLayout;
    },

    
    afterComponentLayout: function(width, height, isSetSize, layoutOwner) {
        this.fireEvent('resize', this, width, height);
    },

    
    beforeComponentLayout: function(width, height, isSetSize, layoutOwner) {
        return true;
    },

    
    setPosition : function(x, y) {
        var me = this;

        if (Ext.isObject(x)) {
            y = x.y;
            x = x.x;
        }

        if (!me.rendered) {
            return me;
        }

        if (x !== undefined || y !== undefined) {
            me.el.setBox(x, y);
            me.onPosition(x, y);
            me.fireEvent('move', me, x, y);
        }
        return me;
    },

    
    onPosition: Ext.emptyFn,

    
    setWidth : function(width) {
        return this.setSize(width);
    },

    
    setHeight : function(height) {
        return this.setSize(undefined, height);
    },

    
    getSize : function() {
        return this.el.getSize();
    },

    
    getWidth : function() {
        return this.el.getWidth();
    },

    
    getHeight : function() {
        return this.el.getHeight();
    },

    
    getLoader: function(){
        var me = this,
            autoLoad = me.autoLoad ? (Ext.isObject(me.autoLoad) ? me.autoLoad : {url: me.autoLoad}) : null,
            loader = me.loader || autoLoad;

        if (loader) {
            if (!loader.isLoader) {
                me.loader = Ext.create('Ext.ComponentLoader', Ext.apply({
                    target: me,
                    autoLoad: autoLoad
                }, loader));
            } else {
                loader.setTarget(me);
            }
            return me.loader;

        }
        return null;
    },

    
    setLoading : function(load, targetEl) {
        var me = this,
            config;

        if (me.rendered) {
            if (load !== false && !me.collapsed) {
                if (Ext.isObject(load)) {
                    config = load;
                }
                else if (Ext.isString(load)) {
                    config = {msg: load};
                }
                else {
                    config = {};
                }
                me.loadMask = me.loadMask || Ext.create('Ext.LoadMask', targetEl ? me.getTargetEl() : me.el, config);
                me.loadMask.show();
            } else if (me.loadMask) {
                Ext.destroy(me.loadMask);
                me.loadMask = null;
            }
        }

        return me.loadMask;
    },

    
    setDocked : function(dock, layoutParent) {
        var me = this;

        me.dock = dock;
        if (layoutParent && me.ownerCt && me.rendered) {
            me.ownerCt.doComponentLayout();
        }
        return me;
    },

    onDestroy : function() {
        var me = this;

        if (me.monitorResize && Ext.EventManager.resizeEvent) {
            Ext.EventManager.resizeEvent.removeListener(me.setSize, me);
        }
        Ext.destroy(me.componentLayout, me.loadMask);
    },

    
    destroy : function() {
        var me = this;

        if (!me.isDestroyed) {
            if (me.fireEvent('beforedestroy', me) !== false) {
                me.destroying = true;
                me.beforeDestroy();

                if (me.floating) {
                    delete me.floatParent;
                    
                    
                    if (me.zIndexManager) {
                        me.zIndexManager.unregister(me);
                    }
                } else if (me.ownerCt && me.ownerCt.remove) {
                    me.ownerCt.remove(me, false);
                }

                if (me.rendered) {
                    me.el.remove();
                }

                me.onDestroy();

                
                Ext.destroy(me.plugins);

                Ext.ComponentManager.unregister(me);
                me.fireEvent('destroy', me);

                me.mixins.state.destroy.call(me);

                me.clearListeners();
                me.destroying = false;
                me.isDestroyed = true;
            }
        }
    },

    
    getPlugin: function(pluginId) {
        var i = 0,
            plugins = this.plugins,
            ln = plugins.length;
        for (; i < ln; i++) {
            if (plugins[i].pluginId === pluginId) {
                return plugins[i];
            }
        }
    },
    
    
    isDescendantOf: function(container) {
        return !!this.findParentBy(function(p){
            return p === container;
        });
    }
}, function() {
    this.createAlias({
        on: 'addListener',
        prev: 'previousSibling',
        next: 'nextSibling'
    });
});


Ext.define('Ext.AbstractPlugin', {
    disabled: false,
    
    constructor: function(config) {
        if (!config.cmp && Ext.global.console) {
            Ext.global.console.warn("Attempted to attach a plugin ");
        }
        Ext.apply(this, config);
    },
    
    getCmp: function() {
        return this.cmp;
    },

    
    init: Ext.emptyFn,

    
    destroy: Ext.emptyFn,

    
    enable: function() {
        this.disabled = false;
    },

    
    disable: function() {
        this.disabled = true;
    }
});


Ext.define('Ext.data.Connection', {
    mixins: {
        observable: 'Ext.util.Observable'
    },

    statics: {
        requestId: 0
    },

    url: null,
    async: true,
    method: null,
    username: '',
    password: '',

    
    disableCaching: true,

    
    disableCachingParam: '_dc',

    
    timeout : 30000,

    

    useDefaultHeader : true,
    defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
    useDefaultXhrHeader : true,
    defaultXhrHeader : 'XMLHttpRequest',

    constructor : function(config) {
        config = config || {};
        Ext.apply(this, config);

        this.addEvents(
            
            'beforerequest',
            
            'requestcomplete',
            
            'requestexception'
        );
        this.requests = {};
        this.mixins.observable.constructor.call(this);
    },

    
    request : function(options) {
        options = options || {};
        var me = this,
            scope = options.scope || window,
            username = options.username || me.username,
            password = options.password || me.password || '',
            async,
            requestOptions,
            request,
            headers,
            xhr;

        if (me.fireEvent('beforerequest', me, options) !== false) {

            requestOptions = me.setOptions(options, scope);

            if (this.isFormUpload(options) === true) {
                this.upload(options.form, requestOptions.url, requestOptions.data, options);
                return null;
            }

            
            if (options.autoAbort === true || me.autoAbort) {
                me.abort();
            }

            
            xhr = this.getXhrInstance();

            async = options.async !== false ? (options.async || me.async) : false;

            
            if (username) {
                xhr.open(requestOptions.method, requestOptions.url, async, username, password);
            } else {
                xhr.open(requestOptions.method, requestOptions.url, async);
            }

            headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);

            
            request = {
                id: ++Ext.data.Connection.requestId,
                xhr: xhr,
                headers: headers,
                options: options,
                async: async,
                timeout: setTimeout(function() {
                    request.timedout = true;
                    me.abort(request);
                }, options.timeout || me.timeout)
            };
            me.requests[request.id] = request;

            
            if (async) {
                xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
            }

            
            xhr.send(requestOptions.data);
            if (!async) {
                return this.onComplete(request);
            }
            return request;
        } else {
            Ext.callback(options.callback, options.scope, [options, undefined, undefined]);
            return null;
        }
    },

    
    upload: function(form, url, params, options){
        form = Ext.getDom(form);
        options = options || {};

        var id = Ext.id(),
                frame = document.createElement('iframe'),
                hiddens = [],
                encoding = 'multipart/form-data',
                buf = {
                    target: form.target,
                    method: form.method,
                    encoding: form.encoding,
                    enctype: form.enctype,
                    action: form.action
                }, hiddenItem;

        
        Ext.fly(frame).set({
            id: id,
            name: id,
            cls: Ext.baseCSSPrefix + 'hide-display',
            src: Ext.SSL_SECURE_URL
        });

        document.body.appendChild(frame);

        
        if (document.frames) {
           document.frames[id].name = id;
        }

        Ext.fly(form).set({
            target: id,
            method: 'POST',
            enctype: encoding,
            encoding: encoding,
            action: url || buf.action
        });

        
        if (params) {
            Ext.iterate(Ext.Object.fromQueryString(params), function(name, value){
                hiddenItem = document.createElement('input');
                Ext.fly(hiddenItem).set({
                    type: 'hidden',
                    value: value,
                    name: name
                });
                form.appendChild(hiddenItem);
                hiddens.push(hiddenItem);
            });
        }

        Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: true});
        form.submit();

        Ext.fly(form).set(buf);
        Ext.each(hiddens, function(h) {
            Ext.removeNode(h);
        });
    },

    onUploadComplete: function(frame, options){
        var me = this,
            
            response = {
                responseText: '',
                responseXML: null
            }, doc, firstChild;

        try {
            doc = frame.contentWindow.document || frame.contentDocument || window.frames[id].document;
            if (doc) {
                if (doc.body) {
                    if (/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)) { 
                        response.responseText = firstChild.value;
                    } else {
                        response.responseText = doc.body.innerHTML;
                    }
                }
                
                response.responseXML = doc.XMLDocument || doc;
            }
        } catch (e) {
        }

        me.fireEvent('requestcomplete', me, response, options);

        Ext.callback(options.success, options.scope, [response, options]);
        Ext.callback(options.callback, options.scope, [options, true, response]);

        setTimeout(function(){
            Ext.removeNode(frame);
        }, 100);
    },

    
    isFormUpload: function(options){
        var form = this.getForm(options);
        if (form) {
            return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
        }
        return false;
    },

    
    getForm: function(options){
        return Ext.getDom(options.form) || null;
    },

    
    setOptions: function(options, scope){
        var me =  this,
            params = options.params || {},
            extraParams = me.extraParams,
            urlParams = options.urlParams,
            url = options.url || me.url,
            jsonData = options.jsonData,
            method,
            disableCache,
            data;


        
        if (Ext.isFunction(params)) {
            params = params.call(scope, options);
        }

        
        if (Ext.isFunction(url)) {
            url = url.call(scope, options);
        }

        url = this.setupUrl(options, url);

        if (!url) {
            Ext.Error.raise({
                options: options,
                msg: 'No URL specified'
            });
        }

        
        data = options.rawData || options.xmlData || jsonData || null;
        if (jsonData && !Ext.isPrimitive(jsonData)) {
            data = Ext.encode(data);
        }

        
        if (Ext.isObject(params)) {
            params = Ext.Object.toQueryString(params);
        }

        if (Ext.isObject(extraParams)) {
            extraParams = Ext.Object.toQueryString(extraParams);
        }

        params = params + ((extraParams) ? ((params) ? '&' : '') + extraParams : '');

        urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams;

        params = this.setupParams(options, params);

        
        method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
        this.setupMethod(options, method);


        disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false;
        
        if (method === 'GET' && disableCache) {
            url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime()));
        }

        
        if ((method == 'GET' || data) && params) {
            url = Ext.urlAppend(url, params);
            params = null;
        }

        
        if (urlParams) {
            url = Ext.urlAppend(url, urlParams);
        }

        return {
            url: url,
            method: method,
            data: data || params || null
        };
    },

    
    setupUrl: function(options, url){
        var form = this.getForm(options);
        if (form) {
            url = url || form.action;
        }
        return url;
    },


    
    setupParams: function(options, params) {
        var form = this.getForm(options),
            serializedForm;
        if (form && !this.isFormUpload(options)) {
            serializedForm = Ext.core.Element.serializeForm(form);
            params = params ? (params + '&' + serializedForm) : serializedForm;
        }
        return params;
    },

    
    setupMethod: function(options, method){
        if (this.isFormUpload(options)) {
            return 'POST';
        }
        return method;
    },

    
    setupHeaders: function(xhr, options, data, params){
        var me = this,
            headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}),
            contentType = me.defaultPostHeader,
            jsonData = options.jsonData,
            xmlData = options.xmlData,
            key,
            header;

        if (!headers['Content-Type'] && (data || params)) {
            if (data) {
                if (options.rawData) {
                    contentType = 'text/plain';
                } else {
                    if (xmlData && Ext.isDefined(xmlData)) {
                        contentType = 'text/xml';
                    } else if (jsonData && Ext.isDefined(jsonData)) {
                        contentType = 'application/json';
                    }
                }
            }
            headers['Content-Type'] = contentType;
        }

        if (me.useDefaultXhrHeader && !headers['X-Requested-With']) {
            headers['X-Requested-With'] = me.defaultXhrHeader;
        }
        
        try{
            for (key in headers) {
                if (headers.hasOwnProperty(key)) {
                    header = headers[key];
                    xhr.setRequestHeader(key, header);
                }

            }
        } catch(e) {
            me.fireEvent('exception', key, header);
        }
        return headers;
    },

    
    getXhrInstance: (function(){
        var options = [function(){
            return new XMLHttpRequest();
        }, function(){
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        }, function(){
            return new ActiveXObject('MSXML2.XMLHTTP');
        }, function(){
            return new ActiveXObject('Microsoft.XMLHTTP');
        }], i = 0,
            len = options.length,
            xhr;

        for(; i < len; ++i) {
            try{
                xhr = options[i];
                xhr();
                break;
            }catch(e){}
        }
        return xhr;
    })(),

    
    isLoading : function(request) {
        if (!(request && request.xhr)) {
            return false;
        }
        
        var state = request.xhr.readyState;
        return !(state === 0 || state == 4);
    },

    
    abort : function(request) {
        var me = this,
            requests = me.requests,
            id;

        if (request && me.isLoading(request)) {
            
            request.xhr.onreadystatechange = null;
            request.xhr.abort();
            me.clearTimeout(request);
            if (!request.timedout) {
                request.aborted = true;
            }
            me.onComplete(request);
            me.cleanup(request);
        } else if (!request) {
            for(id in requests) {
                if (requests.hasOwnProperty(id)) {
                    me.abort(requests[id]);
                }
            }
        }
    },

    
    onStateChange : function(request) {
        if (request.xhr.readyState == 4) {
            this.clearTimeout(request);
            this.onComplete(request);
            this.cleanup(request);
        }
    },

    
    clearTimeout: function(request){
        clearTimeout(request.timeout);
        delete request.timeout;
    },

    
    cleanup: function(request){
        request.xhr = null;
        delete request.xhr;
    },

    
    onComplete : function(request) {
        var me = this,
            options = request.options,
            result = me.parseStatus(request.xhr.status),
            success = result.success,
            response;

        if (success) {
            response = me.createResponse(request);
            me.fireEvent('requestcomplete', me, response, options);
            Ext.callback(options.success, options.scope, [response, options]);
        } else {
            if (result.isException || request.aborted || request.timedout) {
                response = me.createException(request);
            } else {
                response = me.createResponse(request);
            }
            me.fireEvent('requestexception', me, response, options);
            Ext.callback(options.failure, options.scope, [response, options]);
        }
        Ext.callback(options.callback, options.scope, [options, success, response]);
        delete me.requests[request.id];
        return response;
    },

    
    parseStatus: function(status) {
        
        status = status == 1223 ? 204 : status;

        var success = (status >= 200 && status < 300) || status == 304,
            isException = false;

        if (!success) {
            switch (status) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    isException = true;
                    break;
            }
        }
        return {
            success: success,
            isException: isException
        };
    },

    
    createResponse : function(request) {
        var xhr = request.xhr,
            headers = {},
            lines = xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'),
            count = lines.length,
            line, index, key, value, response;

        while (count--) {
            line = lines[count];
            index = line.indexOf(':');
            if(index >= 0) {
                key = line.substr(0, index).toLowerCase();
                if (line.charAt(index + 1) == ' ') {
                    ++index;
                }
                headers[key] = line.substr(index + 1);
            }
        }

        request.xhr = null;
        delete request.xhr;

        response = {
            request: request,
            requestId : request.id,
            status : xhr.status,
            statusText : xhr.statusText,
            getResponseHeader : function(header){ return headers[header.toLowerCase()]; },
            getAllResponseHeaders : function(){ return headers; },
            responseText : xhr.responseText,
            responseXML : xhr.responseXML
        };

        
        
        xhr = null;
        return response;
    },

    
    createException : function(request) {
        return {
            request : request,
            requestId : request.id,
            status : request.aborted ? -1 : 0,
            statusText : request.aborted ? 'transaction aborted' : 'communication failure',
            aborted: request.aborted,
            timedout: request.timedout
        };
    }
});


Ext.define('Ext.Ajax', {
    extend: 'Ext.data.Connection',
    singleton: true,

    
    
    
    
    
    

    

    
    
    
    
    
    

    
    autoAbort : false
});

Ext.define('Ext.data.Association', {
    

    

    
    primaryKey: 'id',

    
    
    

    defaultReaderType: 'json',

    statics: {
        create: function(association){
            if (!association.isAssociation) {
                if (Ext.isString(association)) {
                    association = {
                        type: association
                    };
                }

                switch (association.type) {
                    case 'belongsTo':
                        return Ext.create('Ext.data.BelongsToAssociation', association);
                    case 'hasMany':
                        return Ext.create('Ext.data.HasManyAssociation', association);
                    


                    default:
                        Ext.Error.raise('Unknown Association type: "' + association.type + '"');
                }
            }
            return association;
        }
    },

    constructor: function(config) {
        Ext.apply(this, config);

        var types           = Ext.ModelManager.types,
            ownerName       = config.ownerModel,
            associatedName  = config.associatedModel,
            ownerModel      = types[ownerName],
            associatedModel = types[associatedName],
            ownerProto;

        if (ownerModel === undefined) {
            Ext.Error.raise("The configured ownerModel was not valid (you tried " + ownerName + ")");
        }
        if (associatedModel === undefined) {
            Ext.Error.raise("The configured associatedModel was not valid (you tried " + associatedName + ")");
        }

        this.ownerModel = ownerModel;
        this.associatedModel = associatedModel;

        

        

        Ext.applyIf(this, {
            ownerName : ownerName,
            associatedName: associatedName
        });
    },

    
    getReader: function(){
        var me = this,
            reader = me.reader,
            model = me.associatedModel;

        if (reader) {
            if (Ext.isString(reader)) {
                reader = {
                    type: reader
                };
            }
            if (reader.isReader) {
                reader.setModel(model);
            } else {
                Ext.applyIf(reader, {
                    model: model,
                    type : me.defaultReaderType
                });
            }
            me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        return me.reader || null;
    }
});


Ext.define('Ext.ModelManager', {
    extend: 'Ext.AbstractManager',
    alternateClassName: 'Ext.ModelMgr',
    requires: ['Ext.data.Association'],
    
    singleton: true,
    
    typeName: 'mtype',
    
    
    associationStack: [],
    
    
    registerType: function(name, config) {
        var proto = config.prototype,
            model;
        if (proto && proto.isModel) {
            
            model = config;
        } else {
            
            if (!config.extend) {
                config.extend = 'Ext.data.Model';
            }
            model = Ext.define(name, config);
        }
        this.types[name] = model;
        return model;
    },
    
    
    onModelDefined: function(model) {
        var stack  = this.associationStack,
            length = stack.length,
            create = [],
            association, i, created;
        
        for (i = 0; i < length; i++) {
            association = stack[i];
            
            if (association.associatedModel == model.modelName) {
                create.push(association);
            }
        }
        
        for (i = 0, length = create.length; i < length; i++) {
            created = create[i];
            this.types[created.ownerModel].prototype.associations.add(Ext.data.Association.create(created));
            Ext.Array.remove(stack, created);
        }
    },
    
    
    registerDeferredAssociation: function(association){
        this.associationStack.push(association);
    },
    
    
    getModel: function(id) {
        var model = id;
        if (typeof model == 'string') {
            model = this.types[model];
        }
        return model;
    },
    
    
    create: function(config, name, id) {
        var con = typeof name == 'function' ? name : this.types[name || config.name];
        
        return new con(config, id);
    }
}, function() {
    
    
    Ext.regModel = function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.regModel has been deprecated. Models can now be created by extending Ext.data.Model: Ext.define("MyModel", {extend: "Ext.data.Model", fields: []});.');
        }
        return this.ModelManager.registerType.apply(this.ModelManager, arguments);
    };
});

  
Ext.define('Ext.app.Controller', {
    

    mixins: {
        observable: 'Ext.util.Observable'
    },

    onClassExtended: function(cls, data) {
        var className = Ext.getClassName(cls),
            match = className.match(/^(.*)\.controller\./);

        if (match !== null) {
            var namespace = Ext.Loader.getPrefix(className) || match[1],
                onBeforeClassCreated = data.onBeforeClassCreated,
                requires = [],
                modules = ['model', 'view', 'store'],
                prefix;

            data.onBeforeClassCreated = function(cls, data) {
                var i, ln, module,
                    items, j, subLn, item;

                for (i = 0,ln = modules.length; i < ln; i++) {
                    module = modules[i];

                    items = Ext.Array.from(data[module + 's']);

                    for (j = 0,subLn = items.length; j < subLn; j++) {
                        item = items[j];

                        prefix = Ext.Loader.getPrefix(item);

                        if (prefix === '' || prefix === item) {
                            requires.push(namespace + '.' + module + '.' + item);
                        }
                        else {
                            requires.push(item);
                        }
                    }
                }

                Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
            };
        }
    },

    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);

        Ext.apply(this, config || {});

        this.createGetters('model', this.models);
        this.createGetters('store', this.stores);
        this.createGetters('view', this.views);

        if (this.refs) {
            this.ref(this.refs);
        }
    },

    
    init: function(application) {},
    
    onLaunch: function(application) {},

    createGetters: function(type, refs) {
        type = Ext.String.capitalize(type);
        Ext.Array.each(refs, function(ref) {
            var fn = 'get',
                parts = ref.split('.');

            
            Ext.Array.each(parts, function(part) {
                fn += Ext.String.capitalize(part);
            });
            fn += type;

            if (!this[fn]) {
                this[fn] = Ext.Function.pass(this['get' + type], [ref], this);
            }
            
            this[fn](ref);
        },
        this);
    },

    ref: function(refs) {
        var me = this;
        refs = Ext.Array.from(refs);
        Ext.Array.each(refs, function(info) {
            var ref = info.ref,
                fn = 'get' + Ext.String.capitalize(ref);
            if (!me[fn]) {
                me[fn] = Ext.Function.pass(me.getRef, [ref, info], me);
            }
        });
    },

    getRef: function(ref, info, config) {
        this.refCache = this.refCache || {};
        info = info || {};
        config = config || {};

        Ext.apply(info, config);

        if (info.forceCreate) {
            return Ext.ComponentManager.create(info, 'component');
        }

        var me = this,
            selector = info.selector,
            cached = me.refCache[ref];

        if (!cached) {
            me.refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
            if (!cached && info.autoCreate) {
                me.refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
            }
            if (cached) {
                cached.on('beforedestroy', function() {
                    me.refCache[ref] = null;
                });
            }
        }

        return cached;
    },

    control: function(selectors, listeners) {
        this.application.control(selectors, listeners, this);
    },

    getController: function(name) {
        return this.application.getController(name);
    },

    getStore: function(name) {
        return this.application.getStore(name);
    },

    getModel: function(model) {
        return this.application.getModel(model);
    },

    getView: function(view) {
        return this.application.getView(view);
    }
});


Ext.define('Ext.data.SortTypes', {
    
    singleton: true,
    
    
    none : function(s) {
        return s;
    },

    
    stripTagsRE : /<\/?[^>]+>/gi,

    
    asText : function(s) {
        return String(s).replace(this.stripTagsRE, "");
    },

    
    asUCText : function(s) {
        return String(s).toUpperCase().replace(this.stripTagsRE, "");
    },

    
    asUCString : function(s) {
        return String(s).toUpperCase();
    },

    
    asDate : function(s) {
        if(!s){
            return 0;
        }
        if(Ext.isDate(s)){
            return s.getTime();
        }
        return Date.parse(String(s));
    },

    
    asFloat : function(s) {
        var val = parseFloat(String(s).replace(/,/g, ""));
        return isNaN(val) ? 0 : val;
    },

    
    asInt : function(s) {
        var val = parseInt(String(s).replace(/,/g, ""), 10);
        return isNaN(val) ? 0 : val;
    }
});

Ext.define('Ext.data.Errors', {
    extend: 'Ext.util.MixedCollection',
    
    
    isValid: function() {
        return this.length === 0;
    },
    
    
    getByField: function(fieldName) {
        var errors = [],
            error, field, i;
            
        for (i = 0; i < this.length; i++) {
            error = this.items[i];
            
            if (error.field == fieldName) {
                errors.push(error);
            }
        }
        
        return errors;
    }
});


Ext.define('Ext.data.Operation', {
    
    synchronous: true,
    
    
    action: undefined,
    
    
    filters: undefined,
    
    
    sorters: undefined,
    
    
    group: undefined,
    
    
    start: undefined,
    
    
    limit: undefined,
    
    
    batch: undefined,
        
    
    started: false,
    
    
    running: false,
    
    
    complete: false,
    
    
    success: undefined,
    
    
    exception: false,
    
    
    error: undefined,
    
    constructor: function(config) {
        Ext.apply(this, config || {});
    },
    
    
    setStarted: function() {
        this.started = true;
        this.running = true;
    },
    
    
    setCompleted: function() {
        this.complete = true;
        this.running  = false;
    },
    
    
    setSuccessful: function() {
        this.success = true;
    },
    
    
    setException: function(error) {
        this.exception = true;
        this.success = false;
        this.running = false;
        this.error = error;
    },
    
    
    hasException: function() {
        return this.exception === true;
    },
    
    
    getError: function() {
        return this.error;
    },
    
    
    getRecords: function() {
        var resultSet = this.getResultSet();
        
        return (resultSet === undefined ? this.records : resultSet.records);
    },
    
    
    getResultSet: function() {
        return this.resultSet;
    },
    
    
    isStarted: function() {
        return this.started === true;
    },
    
    
    isRunning: function() {
        return this.running === true;
    },
    
    
    isComplete: function() {
        return this.complete === true;
    },
    
    
    wasSuccessful: function() {
        return this.isComplete() && this.success === true;
    },
    
    
    setBatch: function(batch) {
        this.batch = batch;
    },
    
    
    allowWrite: function() {
        return this.action != 'read';
    }
});

Ext.define('Ext.data.validations', {
    singleton: true,
    
    
    presenceMessage: 'must be present',
    
    
    lengthMessage: 'is the wrong length',
    
    
    formatMessage: 'is the wrong format',
    
    
    inclusionMessage: 'is not included in the list of acceptable values',
    
    
    exclusionMessage: 'is not an acceptable value',
    
    
    presence: function(config, value) {
        if (value === undefined) {
            value = config;
        }
        
        return !!value;
    },
    
    
    length: function(config, value) {
        if (value === undefined) {
            return false;
        }
        
        var length = value.length,
            min    = config.min,
            max    = config.max;
        
        if ((min && length < min) || (max && length > max)) {
            return false;
        } else {
            return true;
        }
    },
    
    
    format: function(config, value) {
        return !!(config.matcher && config.matcher.test(value));
    },
    
    
    inclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) != -1;
    },
    
    
    exclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) == -1;
    }
});

Ext.define('Ext.data.ResultSet', {
    
    loaded: true,
    
    
    count: 0,
    
    
    total: 0,
    
    
    success: false,
    
    

    constructor: function(config) {
        Ext.apply(this, config);
        
        
        this.totalRecords = this.total;
        
        if (config.count === undefined) {
            this.count = this.records.length;
        }
    }
});

Ext.define('Ext.data.writer.Writer', {
    alias: 'writer.base',
    alternateClassName: ['Ext.data.DataWriter', 'Ext.data.Writer'],
    
    
    writeAllFields: true,
    
    
    nameProperty: 'name',

    constructor: function(config) {
        Ext.apply(this, config);
    },

    
    write: function(request) {
        var operation = request.operation,
            records   = operation.records || [],
            len       = records.length,
            i         = 0,
            data      = [];

        for (; i < len; i++) {
            data.push(this.getRecordData(records[i]));
        }
        return this.writeRecords(request, data);
    },

    
    getRecordData: function(record) {
        var isPhantom = record.phantom === true,
            writeAll = this.writeAllFields || isPhantom,
            nameProperty = this.nameProperty,
            fields = record.fields,
            data = {},
            changes,
            name,
            field,
            key;
        
        if (writeAll) {
            fields.each(function(field){
                if (field.persist) {
                    name = field[nameProperty] || field.name;
                    data[name] = record.get(field.name);
                }
            });
        } else {
            
            changes = record.getChanges();
            for (key in changes) {
                if (changes.hasOwnProperty(key)) {
                    field = fields.get(key);
                    name = field[nameProperty] || field.name;
                    data[name] = changes[key];
                }
            }
            if (!isPhantom) {
                
                data[record.idProperty] = record.getId();
            }
        }
        return data;
    }
});


Ext.define('Ext.util.Floating', {

    uses: ['Ext.Layer', 'Ext.window.Window'],

    
    focusOnToFront: true,

    
    shadow: 'sides',

    constructor: function(config) {
        this.floating = true;
        this.el = Ext.create('Ext.Layer', Ext.apply({}, config, {
            hideMode: this.hideMode,
            hidden: this.hidden,
            shadow: Ext.isDefined(this.shadow) ? this.shadow : 'sides',
            shadowOffset: this.shadowOffset,
            constrain: false,
            shim: this.shim === false ? false : undefined
        }), this.el);
    },

    onFloatRender: function() {
        var me = this;
        me.zIndexParent = me.getZIndexParent();
        me.setFloatParent(me.ownerCt);
        delete me.ownerCt;

        if (me.zIndexParent) {
            me.zIndexParent.registerFloatingItem(me);
        } else {
            Ext.WindowManager.register(me);
        }
    },

    setFloatParent: function(floatParent) {
        var me = this;

        
        if (me.floatParent) {
            me.mun(me.floatParent, {
                hide: me.onFloatParentHide,
                show: me.onFloatParentShow,
                scope: me
            });
        }

        me.floatParent = floatParent;

        
        if (floatParent) {
            me.mon(me.floatParent, {
                hide: me.onFloatParentHide,
                show: me.onFloatParentShow,
                scope: me
            });
        }

        
        
        if ((me.constrain || me.constrainHeader) && !me.constrainTo) {
            me.constrainTo = floatParent ? floatParent.getTargetEl() : me.container;
        }
    },

    onFloatParentHide: function() {
        this.showOnParentShow = this.isVisible();
        this.hide();
    },

    onFloatParentShow: function() {
        if (this.showOnParentShow) {
            delete this.showOnParentShow;
            this.show();
        }
    },

    
    getZIndexParent: function() {
        var p = this.ownerCt,
            c;

        if (p) {
            while (p) {
                c = p;
                p = p.ownerCt;
            }
            if (c.floating) {
                return c;
            }
        }
    },

    
    
    
    
    
    setZIndex: function(index) {
        var me = this;
        this.el.setZIndex(index);

        
        index += 10;

        
        
        if (me.floatingItems) {
            index = Math.floor(me.floatingItems.setBase(index) / 100) * 100 + 10000;
        }
        return index;
    },

    
    doConstrain: function(constrainTo) {
        var me = this,
            vector = me.getConstrainVector(constrainTo),
            xy;

        if (vector) {
            xy = me.getPosition();
            xy[0] += vector[0];
            xy[1] += vector[1];
            me.setPosition(xy);
        }
    },
    
    
    
    getConstrainVector: function(constrainTo){
        var me = this,
            el;
            
        if (me.constrain || me.constrainHeader) {
            el = me.constrainHeader ? me.header.el : me.el;
            constrainTo = constrainTo || (me.floatParent && me.floatParent.getTargetEl()) || me.container;
            return el.getConstrainVector(constrainTo);
        }
    },

    
    alignTo: function(element, position, offsets) {
        if (element.isComponent) {
            element = element.getEl();
        }
        var xy = this.el.getAlignToXY(element, position, offsets);
        this.setPagePosition(xy);
        return this;
    },

    
    toFront: function(preventFocus) {
        var me = this;

        
        
        if (me.zIndexParent) {
            me.zIndexParent.toFront(true);
        }
        if (me.zIndexManager.bringToFront(me)) {
            if (!Ext.isDefined(preventFocus)) {
                preventFocus = !me.focusOnToFront;
            }
            if (!preventFocus) {
                
                
                
                me.focus(false, true);
            }
        }
        return me;
    },

    
    setActive: function(active, newActive) {
        if (active) {
            if ((this instanceof Ext.window.Window) && !this.maximized) {
                this.el.enableShadow(true);
            }
            this.fireEvent('activate', this);
        } else {
            
            
            if ((this instanceof Ext.window.Window) && (newActive instanceof Ext.window.Window)) {
                this.el.disableShadow();
            }
            this.fireEvent('deactivate', this);
        }
    },

    
    toBack: function() {
        this.zIndexManager.sendToBack(this);
        return this;
    },

    
    center: function() {
        var xy = this.el.getAlignToXY(this.container, 'c-c');
        this.setPagePosition(xy);
        return this;
    },

    
    syncShadow : function(){
        if (this.floating) {
            this.el.sync(true);
        }
    },

    
    fitContainer: function() {
        var parent = this.floatParent,
            container = parent ? parent.getTargetEl() : this.container,
            size = container.getViewSize(false);

        this.setSize(size);
    }
});


Ext.define('Ext.layout.container.AbstractContainer', {

    

    extend: 'Ext.layout.Layout',

    

    type: 'container',

    fixedLayout: true,

    
    managedHeight: true,
    
    managedWidth: true,

    
    bindToOwnerCtComponent: false,

    
    bindToOwnerCtContainer: false,

    

    isManaged: function(dimension) {
        dimension = Ext.String.capitalize(dimension);
        var me = this,
            child = me,
            managed = me['managed' + dimension],
            ancestor = me.owner.ownerCt;

        if (ancestor && ancestor.layout) {
            while (ancestor && ancestor.layout) {
                if (managed === false || ancestor.layout['managed' + dimension] === false) {
                    managed = false;
                    break;
                }
                ancestor = ancestor.ownerCt;
            }
        }
        return managed;
    },

    layout: function() {
        var me = this,
            owner = me.owner;
        if (Ext.isNumber(owner.height) || owner.isViewport) {
            me.managedHeight = false;
        }
        if (Ext.isNumber(owner.width) || owner.isViewport) {
            me.managedWidth = false;
        }
        me.callParent(arguments);
    },

    
    setItemSize: function(item, width, height) {
        if (Ext.isObject(width)) {
            height = width.height;
            width = width.width;
        }
        item.setCalculatedSize(width, height, this.owner);
    },

    
    getLayoutItems: function() {
        return this.owner && this.owner.items && this.owner.items.items || [];
    },

    afterLayout: function() {
        this.owner.afterLayout(this);
    },
    
     getTarget: function() {
         return this.owner.getTargetEl();
     },
    
     getRenderTarget: function() {
         return this.owner.getTargetEl();
     }
});


Ext.define('Ext.ZIndexManager', {

    alternateClassName: 'Ext.WindowGroup',

    statics: {
        zBase : 9000
    },

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

        me.list = {};
        me.zIndexStack = [];
        me.front = null;

        if (container) {

            
            if (container.isContainer) {
                container.on('resize', me._onContainerResize, me);
                me.zseed = Ext.Number.from(container.getEl().getStyle('zIndex'), me.getNextZSeed());
                
                me.targetEl = container.getTargetEl();
                me.container = container;
            }
            
            else {
                Ext.EventManager.onWindowResize(me._onContainerResize, me);
                me.zseed = me.getNextZSeed();
                me.targetEl = Ext.get(container);
            }
        }
        
        
        else {
            Ext.EventManager.onWindowResize(me._onContainerResize, me);
            me.zseed = me.getNextZSeed();
            Ext.onDocumentReady(function() {
                me.targetEl = Ext.getBody();
            });
        }
    },

    getNextZSeed: function() {
        return (Ext.ZIndexManager.zBase += 10000);
    },

    setBase: function(baseZIndex) {
        this.zseed = baseZIndex;
        return this.assignZIndices();
    },

    
    assignZIndices: function() {
        var a = this.zIndexStack,
            len = a.length,
            i = 0,
            zIndex = this.zseed,
            comp;

        for (; i < len; i++) {
            comp = a[i];
            if (comp && !comp.hidden) {

                
                
                
                
                
                
                
                zIndex = comp.setZIndex(zIndex);
            }
        }
        this._activateLast();
        return zIndex;
    },

    
    _setActiveChild: function(comp) {
        if (comp != this.front) {

            if (this.front) {
                this.front.setActive(false, comp);
            }
            this.front = comp;
            if (comp) {
                comp.setActive(true);
                if (comp.modal) {
                    this._showModalMask(comp.el.getStyle('zIndex') - 4);
                }
            }
        }
    },

    
    _activateLast: function(justHidden) {
        var comp,
            lastActivated = false,
            i;

        
        
        
        for (i = this.zIndexStack.length-1; i >= 0; --i) {
            comp = this.zIndexStack[i];
            if (!comp.hidden) {
                if (!lastActivated) {
                    this._setActiveChild(comp);
                    lastActivated = true;
                }

                
                if (comp.modal) {
                    this._showModalMask(comp.el.getStyle('zIndex') - 4);
                    return;
                }
            }
        }

        
        
        this._hideModalMask();
        if (!lastActivated) {
            this._setActiveChild(null);
        }
    },

    _showModalMask: function(zIndex) {
        if (!this.mask) {
            this.mask = this.targetEl.createChild({
                cls: Ext.baseCSSPrefix + 'mask'
            });
            this.mask.setVisibilityMode(Ext.core.Element.DISPLAY);
            this.mask.on('click', this._onMaskClick, this);
        }
        Ext.getBody().addCls(Ext.baseCSSPrefix + 'body-masked');
        this.mask.setSize(this.targetEl.getViewSize(true));
        this.mask.setStyle('zIndex', zIndex);
        this.mask.show();
    },

    _hideModalMask: function() {
        if (this.mask) {
            Ext.getBody().removeCls(Ext.baseCSSPrefix + 'body-masked');
            this.mask.hide();
        }
    },

    _onMaskClick: function() {
        if (this.front) {
            this.front.focus();
        }
    },

    _onContainerResize: function() {
        if (this.mask && this.mask.isVisible()) {
            this.mask.setSize(this.targetEl.getViewSize(true));
        }
    },

    
    register : function(comp) {
        if (comp.zIndexManager) {
            comp.zIndexManager.unregister(comp);
        }
        comp.zIndexManager = this;

        this.list[comp.id] = comp;
        this.zIndexStack.push(comp);
        comp.on('hide', this._activateLast, this);
    },

    
    unregister : function(comp) {
        delete comp.zIndexManager;
        if (this.list && this.list[comp.id]) {
            delete this.list[comp.id];
            comp.un('hide', this._activateLast);
            Ext.Array.remove(this.zIndexStack, comp);

            
            this._activateLast(comp);
        }
    },

    
    get : function(id) {
        return typeof id == "object" ? id : this.list[id];
    },

   
    bringToFront : function(comp) {
        comp = this.get(comp);
        if (comp != this.front) {
            Ext.Array.remove(this.zIndexStack, comp);
            this.zIndexStack.push(comp);
            this.assignZIndices();
            return true;
        }
        if (comp.modal) {
            Ext.getBody().addCls(Ext.baseCSSPrefix + 'body-masked');
            this.mask.setSize(Ext.core.Element.getViewWidth(true), Ext.core.Element.getViewHeight(true));
            this.mask.show();
        }
        return false;
    },

    
    sendToBack : function(comp) {
        comp = this.get(comp);
        Ext.Array.remove(this.zIndexStack, comp);
        this.zIndexStack.unshift(comp);
        this.assignZIndices();
        return comp;
    },

    
    hideAll : function() {
        for (var id in this.list) {
            if (this.list[id].isComponent && this.list[id].isVisible()) {
                this.list[id].hide();
            }
        }
    },

    
    hide: function() {
        var i = 0,
            ln = this.zIndexStack.length,
            comp;

        this.tempHidden = [];
        for (; i < ln; i++) {
            comp = this.zIndexStack[i];
            if (comp.isVisible()) {
                this.tempHidden.push(comp);
                comp.hide();
            }
        }
    },

    
    show: function() {
        var i = 0,
            ln = this.tempHidden.length,
            comp,
            x,
            y;

        for (; i < ln; i++) {
            comp = this.tempHidden[i];
            x = comp.x;
            y = comp.y;
            comp.show();
            comp.setPosition(x, y);
        }
        delete this.tempHidden;
    },

    
    getActive : function() {
        return this.front;
    },

    
    getBy : function(fn, scope) {
        var r = [],
            i = 0,
            len = this.zIndexStack.length,
            comp;

        for (; i < len; i++) {
            comp = this.zIndexStack[i];
            if (fn.call(scope||comp, comp) !== false) {
                r.push(comp);
            }
        }
        return r;
    },

    
    each : function(fn, scope) {
        var comp;
        for (var id in this.list) {
            comp = this.list[id];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },

    
    eachBottomUp: function (fn, scope) {
        var comp,
            stack = this.zIndexStack,
            i, n;

        for (i = 0, n = stack.length ; i < n; i++) {
            comp = stack[i];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },

    
    eachTopDown: function (fn, scope) {
        var comp,
            stack = this.zIndexStack,
            i;

        for (i = stack.length ; i-- > 0; ) {
            comp = stack[i];
            if (comp.isComponent && fn.call(scope || comp, comp) === false) {
                return;
            }
        }
    },

    destroy: function() {
        delete this.zIndexStack;
        delete this.list;
        delete this.container;
        delete this.targetEl;
    }
}, function() {
    
    Ext.WindowManager = Ext.WindowMgr = new this();
});


Ext.define('Ext.layout.container.boxOverflow.None', {
    
    alternateClassName: 'Ext.layout.boxOverflow.None',
    
    constructor: function(layout, config) {
        this.layout = layout;
        Ext.apply(this, config || {});
    },

    handleOverflow: Ext.emptyFn,

    clearOverflow: Ext.emptyFn,
    
    onRemove: Ext.emptyFn,

    
    getItem: function(item) {
        return this.layout.owner.getComponent(item);
    },
    
    onRemove: Ext.emptyFn
});

Ext.define('Ext.util.KeyMap', {
    alternateClassName: 'Ext.KeyMap',
    
    constructor: function(el, binding, eventName){
        var me = this;
        
        Ext.apply(me, {
            el: Ext.get(el),
            eventName: eventName || me.eventName,
            bindings: []
        });
        if (binding) {
            me.addBinding(binding);
        }
        me.enable();
    },
    
    eventName: 'keydown',

    
    addBinding : function(binding){
        if (Ext.isArray(binding)) {
            Ext.each(binding, this.addBinding, this);
            return;
        }
        
        var keyCode = binding.key,
            processed = false,
            key,
            keys,
            keyString,
            i,
            len;

        if (Ext.isString(keyCode)) {
            keys = [];
            keyString = keyCode.toLowerCase();
            
            for (i = 0, len = keyString.length; i < len; ++i){
                keys.push(keyString.charCodeAt(i));
            }
            keyCode = keys;
            processed = true;
        }
        
        if (!Ext.isArray(keyCode)) {
            keyCode = [keyCode];
        }
        
        if (!processed) {
            for (i = 0, len = keyCode.length; i < len; ++i) {
                key = keyCode[i];
                if (Ext.isString(key)) {
                    keyCode[i] = key.toLowerCase().charCodeAt(0);
                }
            }
        }
        
        this.bindings.push(Ext.apply({
            keyCode: keyCode
        }, binding));
    },
    
    
    handleKeyDown: function(event) {
        if (this.enabled) { 
            var bindings = this.bindings,
                i = 0,
                len = bindings.length;
                
            event = this.processEvent(event);
            for(; i < len; ++i){
                this.processBinding(bindings[i], event);
            }
        }
    },
    
    
    processEvent: function(event){
        return event;
    },
    
    
    processBinding: function(binding, event){
        if (this.checkModifiers(binding, event)) {
            var key = event.getKey(),
                handler = binding.fn || binding.handler,
                scope = binding.scope || this,
                keyCode = binding.keyCode,
                defaultEventAction = binding.defaultEventAction,
                i,
                len,
                keydownEvent = new Ext.EventObjectImpl(event);
                
            
            for (i = 0, len = keyCode.length; i < len; ++i) {
                if (key === keyCode[i]) {
                    if (handler.call(scope, key, event) !== true && defaultEventAction) {
                        keydownEvent[defaultEventAction]();
                    }
                    break;
                }
            }
        }
    },
    
    
    checkModifiers: function(binding, e){
        var keys = ['shift', 'ctrl', 'alt'],
            i = 0,
            len = keys.length,
            val, key;
            
        for (; i < len; ++i){
            key = keys[i];
            val = binding[key];
            if (!(val === undefined || (val === e[key + 'Key']))) {
                return false;
            }
        }
        return true;
    },

    
    on: function(key, fn, scope) {
        var keyCode, shift, ctrl, alt;
        if (Ext.isObject(key) && !Ext.isArray(key)) {
            keyCode = key.key;
            shift = key.shift;
            ctrl = key.ctrl;
            alt = key.alt;
        } else {
            keyCode = key;
        }
        this.addBinding({
            key: keyCode,
            shift: shift,
            ctrl: ctrl,
            alt: alt,
            fn: fn,
            scope: scope
        });
    },

    
    isEnabled : function(){
        return this.enabled;
    },

    
    enable: function(){
        if(!this.enabled){
            this.el.on(this.eventName, this.handleKeyDown, this);
            this.enabled = true;
        }
    },

    
    disable: function(){
        if(this.enabled){
            this.el.removeListener(this.eventName, this.handleKeyDown, this);
            this.enabled = false;
        }
    },

    
    setDisabled : function(disabled){
        if (disabled) {
            this.disable();
        } else {
            this.enable();
        }
    },
    
    
    destroy: function(removeEl){
        var me = this;
        
        me.bindings = [];
        me.disable();
        if (removeEl === true) {
            me.el.remove();
        }
        delete me.el;
    }
});


Ext.define('Ext.util.ClickRepeater', {
    extend: 'Ext.util.Observable',

    constructor : function(el, config){
        this.el = Ext.get(el);
        this.el.unselectable();

        Ext.apply(this, config);

        this.addEvents(
        
        "mousedown",
        
        "click",
        
        "mouseup"
        );

        if(!this.disabled){
            this.disabled = true;
            this.enable();
        }

        
        if(this.handler){
            this.on("click", this.handler,  this.scope || this);
        }

        this.callParent();
    },

    

    

    

    
    interval : 20,

    
    delay: 250,

    
    preventDefault : true,
    
    stopDefault : false,

    timer : 0,

    
    enable: function(){
        if(this.disabled){
            this.el.on('mousedown', this.handleMouseDown, this);
            if (Ext.isIE){
                this.el.on('dblclick', this.handleDblClick, this);
            }
            if(this.preventDefault || this.stopDefault){
                this.el.on('click', this.eventOptions, this);
            }
        }
        this.disabled = false;
    },

    
    disable: function( force){
        if(force || !this.disabled){
            clearTimeout(this.timer);
            if(this.pressedCls){
                this.el.removeCls(this.pressedCls);
            }
            Ext.getDoc().un('mouseup', this.handleMouseUp, this);
            this.el.removeAllListeners();
        }
        this.disabled = true;
    },

    
    setDisabled: function(disabled){
        this[disabled ? 'disable' : 'enable']();
    },

    eventOptions: function(e){
        if(this.preventDefault){
            e.preventDefault();
        }
        if(this.stopDefault){
            e.stopEvent();
        }
    },

    
    destroy : function() {
        this.disable(true);
        Ext.destroy(this.el);
        this.clearListeners();
    },

    handleDblClick : function(e){
        clearTimeout(this.timer);
        this.el.blur();

        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);
    },

    
    handleMouseDown : function(e){
        clearTimeout(this.timer);
        this.el.blur();
        if(this.pressedCls){
            this.el.addCls(this.pressedCls);
        }
        this.mousedownTime = new Date();

        Ext.getDoc().on("mouseup", this.handleMouseUp, this);
        this.el.on("mouseout", this.handleMouseOut, this);

        this.fireEvent("mousedown", this, e);
        this.fireEvent("click", this, e);

        
        if (this.accelerate) {
            this.delay = 400;
        }

        
        
        e = new Ext.EventObjectImpl(e);

        this.timer =  Ext.defer(this.click, this.delay || this.interval, this, [e]);
    },

    
    click : function(e){
        this.fireEvent("click", this, e);
        this.timer =  Ext.defer(this.click, this.accelerate ?
            this.easeOutExpo(Ext.Date.getElapsed(this.mousedownTime),
                400,
                -390,
                12000) :
            this.interval, this, [e]);
    },

    easeOutExpo : function (t, b, c, d) {
        return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
    },

    
    handleMouseOut : function(){
        clearTimeout(this.timer);
        if(this.pressedCls){
            this.el.removeCls(this.pressedCls);
        }
        this.el.on("mouseover", this.handleMouseReturn, this);
    },

    
    handleMouseReturn : function(){
        this.el.un("mouseover", this.handleMouseReturn, this);
        if(this.pressedCls){
            this.el.addCls(this.pressedCls);
        }
        this.click();
    },

    
    handleMouseUp : function(e){
        clearTimeout(this.timer);
        this.el.un("mouseover", this.handleMouseReturn, this);
        this.el.un("mouseout", this.handleMouseOut, this);
        Ext.getDoc().un("mouseup", this.handleMouseUp, this);
        if(this.pressedCls){
            this.el.removeCls(this.pressedCls);
        }
        this.fireEvent("mouseup", this, e);
    }
});


Ext.define('Ext.layout.component.Button', {

    

    alias: ['layout.button'],

    extend: 'Ext.layout.component.Component',

    

    type: 'button',

    cellClsRE: /-btn-(tl|br)\b/,
    htmlRE: /<.*>/,

    beforeLayout: function() {
        return this.callParent(arguments) || this.lastText !== this.owner.text;
    },

    
    onLayout: function(width, height) {
        var me = this,
            isNum = Ext.isNumber,
            owner = me.owner,
            ownerEl = owner.el,
            btnEl = owner.btnEl,
            btnInnerEl = owner.btnInnerEl,
            minWidth = owner.minWidth,
            maxWidth = owner.maxWidth,
            ownerWidth, btnFrameWidth, metrics;

        me.getTargetInfo();
        me.callParent(arguments);

        btnInnerEl.unclip();
        me.setTargetSize(width, height);

        if (!isNum(width)) {
            
            
            
            if (owner.text && Ext.isIE7 && Ext.isStrict && btnEl && btnEl.getWidth() > 20) {
                btnFrameWidth = me.btnFrameWidth;
                metrics = Ext.util.TextMetrics.measure(btnInnerEl, owner.text);
                ownerEl.setWidth(metrics.width + btnFrameWidth + me.adjWidth);
                btnEl.setWidth(metrics.width + btnFrameWidth);
                btnInnerEl.setWidth(metrics.width + btnFrameWidth);
            } else {
                
                ownerEl.setWidth(null);
                btnEl.setWidth(null);
                btnInnerEl.setWidth(null);
            }

            
            if (minWidth || maxWidth) {
                ownerWidth = ownerEl.getWidth();
                if (minWidth && (ownerWidth < minWidth)) {
                    me.setTargetSize(minWidth, height);
                }
                else if (maxWidth && (ownerWidth > maxWidth)) {
                    btnInnerEl.clip();
                    me.setTargetSize(maxWidth, height);
                }
            }
        }

        this.lastText = owner.text;
    },

    setTargetSize: function(width, height) {
        var me = this,
            owner = me.owner,
            isNum = Ext.isNumber,
            btnInnerEl = owner.btnInnerEl,
            btnWidth = (isNum(width) ? width - me.adjWidth : width),
            btnHeight = (isNum(height) ? height - me.adjHeight : height),
            btnFrameHeight = me.btnFrameHeight,
            text = owner.getText(),
            textHeight;

        me.callParent(arguments);
        me.setElementSize(owner.btnEl, btnWidth, btnHeight);
        me.setElementSize(btnInnerEl, btnWidth, btnHeight);
        if (isNum(btnHeight)) {
            btnInnerEl.setStyle('line-height', btnHeight - btnFrameHeight + 'px');
        }

        
        
        
        
        
        if (text && this.htmlRE.test(text)) {
            btnInnerEl.setStyle('line-height', 'normal');
            textHeight = Ext.util.TextMetrics.measure(btnInnerEl, text).height;
            btnInnerEl.setStyle('padding-top', me.btnFrameTop + Math.max(btnInnerEl.getHeight() - btnFrameHeight - textHeight, 0) / 2 + 'px');
            me.setElementSize(btnInnerEl, btnWidth, btnHeight);
        }
    },

    getTargetInfo: function() {
        var me = this,
            owner = me.owner,
            ownerEl = owner.el,
            frameSize = me.frameSize,
            frameBody = owner.frameBody,
            btnWrap = owner.btnWrap,
            innerEl = owner.btnInnerEl;

        if (!('adjWidth' in me)) {
            Ext.apply(me, {
                
                adjWidth: frameSize.left + frameSize.right + ownerEl.getBorderWidth('lr') + ownerEl.getPadding('lr') +
                          btnWrap.getPadding('lr') + (frameBody ? frameBody.getFrameWidth('lr') : 0),
                adjHeight: frameSize.top + frameSize.bottom + ownerEl.getBorderWidth('tb') + ownerEl.getPadding('tb') +
                           btnWrap.getPadding('tb') + (frameBody ? frameBody.getFrameWidth('tb') : 0),
                btnFrameWidth: innerEl.getFrameWidth('lr'),
                btnFrameHeight: innerEl.getFrameWidth('tb'),
                btnFrameTop: innerEl.getFrameWidth('t')
            });
        }

        return me.callParent();
    }
});

Ext.define('Ext.util.TextMetrics', {
    statics: {
        shared: null,
        
        measure: function(el, text, fixedWidth){
            var me = this,
                shared = me.shared;
            
            if(!shared){
                shared = me.shared = new me(el, fixedWidth);
            }
            shared.bind(el);
            shared.setFixedWidth(fixedWidth || 'auto');
            return shared.getSize(text);
        },
        
        
         destroy: function(){
             var me = this;
             Ext.destroy(me.shared);
             me.shared = null;
         }
    },
    
    
    constructor: function(bindTo, fixedWidth){
        var measure = this.measure = Ext.getBody().createChild({
            cls: 'x-textmetrics'
        });
        this.el = Ext.get(bindTo);
        
        measure.position('absolute');
        measure.setLeftTop(-1000, -1000);
        measure.hide();

        if (fixedWidth) {
           measure.setWidth(fixedWidth);
        }
    },
    
    
    getSize: function(text){
        var measure = this.measure,
            size;
        
        measure.update(text);
        size = measure.getSize();
        measure.update('');
        return size;
    },
    
    
    bind: function(el){
        var me = this;
        
        me.el = Ext.get(el);
        me.measure.setStyle(
            me.el.getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
        );
    },
    
    
     setFixedWidth : function(width){
         this.measure.setWidth(width);
     },
     
     
     getWidth : function(text){
         this.measure.dom.style.width = 'auto';
         return this.getSize(text).width;
     },
     
     
     getHeight : function(text){
         return this.getSize(text).height;
     },
     
     
     destroy: function(){
         var me = this;
         me.measure.remove();
         delete me.el;
         delete me.measure;
     }
}, function(){
    Ext.core.Element.addMethods({
        
        getTextWidth : function(text, min, max){
            return Ext.Number.constrain(Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width, min || 0, max || 1000000);
        }
    });
});


Ext.define('Ext.layout.container.boxOverflow.Scroller', {

    

    extend: 'Ext.layout.container.boxOverflow.None',
    requires: ['Ext.util.ClickRepeater', 'Ext.core.Element'],
    alternateClassName: 'Ext.layout.boxOverflow.Scroller',
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    

    
    animateScroll: false,

    
    scrollIncrement: 20,

    
    wheelIncrement: 10,

    
    scrollRepeatInterval: 60,

    
    scrollDuration: 400,

    

    

    
    scrollerCls: Ext.baseCSSPrefix + 'box-scroller',

    

    
    
    constructor: function(layout, config) {
        this.layout = layout;
        Ext.apply(this, config || {});
        
        this.addEvents(
            
            'scroll'
        );
    },
    
    initCSSClasses: function() {
        var me = this,
        layout = me.layout;

        if (!me.CSSinitialized) {
            me.beforeCtCls = me.beforeCtCls || Ext.baseCSSPrefix + 'box-scroller-' + layout.parallelBefore;
            me.afterCtCls  = me.afterCtCls  || Ext.baseCSSPrefix + 'box-scroller-' + layout.parallelAfter;
            me.beforeScrollerCls = me.beforeScrollerCls || Ext.baseCSSPrefix + layout.owner.getXType() + '-scroll-' + layout.parallelBefore;
            me.afterScrollerCls  = me.afterScrollerCls  || Ext.baseCSSPrefix + layout.owner.getXType() + '-scroll-' + layout.parallelAfter;
            me.CSSinitializes = true;
        }
    },

    handleOverflow: function(calculations, targetSize) {
        var me = this,
            layout = me.layout,
            methodName = 'get' + layout.parallelPrefixCap,
            newSize = {};

        me.initCSSClasses();
        me.callParent(arguments);
        this.createInnerElements();
        this.showScrollers();
        newSize[layout.perpendicularPrefix] = targetSize[layout.perpendicularPrefix];
        newSize[layout.parallelPrefix] = targetSize[layout.parallelPrefix] - (me.beforeCt[methodName]() + me.afterCt[methodName]());
        return { targetSize: newSize };
    },

    
    createInnerElements: function() {
        var me = this,
            target = me.layout.getRenderTarget();

        
        
        if (!me.beforeCt) {
            target.addCls(Ext.baseCSSPrefix + me.layout.direction + '-box-overflow-body');
            me.beforeCt = target.insertSibling({cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.beforeCtCls}, 'before');
            me.afterCt  = target.insertSibling({cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.afterCtCls},  'after');
            me.createWheelListener();
        }
    },

    
    createWheelListener: function() {
        this.layout.innerCt.on({
            scope     : this,
            mousewheel: function(e) {
                e.stopEvent();

                this.scrollBy(e.getWheelDelta() * this.wheelIncrement * -1, false);
            }
        });
    },

    
    clearOverflow: function() {
        this.hideScrollers();
    },

    
    showScrollers: function() {
        this.createScrollers();
        this.beforeScroller.show();
        this.afterScroller.show();
        this.updateScrollButtons();
        
        this.layout.owner.addClsWithUI('scroller');
    },

    
    hideScrollers: function() {
        if (this.beforeScroller != undefined) {
            this.beforeScroller.hide();
            this.afterScroller.hide();
            
            this.layout.owner.removeClsWithUI('scroller');
        }
    },

    
    createScrollers: function() {
        if (!this.beforeScroller && !this.afterScroller) {
            var before = this.beforeCt.createChild({
                cls: Ext.String.format("{0} {1} ", this.scrollerCls, this.beforeScrollerCls)
            });

            var after = this.afterCt.createChild({
                cls: Ext.String.format("{0} {1}", this.scrollerCls, this.afterScrollerCls)
            });

            before.addClsOnOver(this.beforeScrollerCls + '-hover');
            after.addClsOnOver(this.afterScrollerCls + '-hover');

            before.setVisibilityMode(Ext.core.Element.DISPLAY);
            after.setVisibilityMode(Ext.core.Element.DISPLAY);

            this.beforeRepeater = Ext.create('Ext.util.ClickRepeater', before, {
                interval: this.scrollRepeatInterval,
                handler : this.scrollLeft,
                scope   : this
            });

            this.afterRepeater = Ext.create('Ext.util.ClickRepeater', after, {
                interval: this.scrollRepeatInterval,
                handler : this.scrollRight,
                scope   : this
            });

            
            this.beforeScroller = before;

            
            this.afterScroller = after;
        }
    },

    
    destroy: function() {
        Ext.destroy(this.beforeRepeater, this.afterRepeater, this.beforeScroller, this.afterScroller, this.beforeCt, this.afterCt);
    },

    
    scrollBy: function(delta, animate) {
        this.scrollTo(this.getScrollPosition() + delta, animate);
    },

    
    getScrollAnim: function() {
        return {
            duration: this.scrollDuration, 
            callback: this.updateScrollButtons, 
            scope   : this
        };
    },

    
    updateScrollButtons: function() {
        if (this.beforeScroller == undefined || this.afterScroller == undefined) {
            return;
        }

        var beforeMeth = this.atExtremeBefore()  ? 'addCls' : 'removeCls',
            afterMeth  = this.atExtremeAfter() ? 'addCls' : 'removeCls',
            beforeCls  = this.beforeScrollerCls + '-disabled',
            afterCls   = this.afterScrollerCls  + '-disabled';
        
        this.beforeScroller[beforeMeth](beforeCls);
        this.afterScroller[afterMeth](afterCls);
        this.scrolling = false;
    },

    
    atExtremeBefore: function() {
        return this.getScrollPosition() === 0;
    },

    
    scrollLeft: function() {
        this.scrollBy(-this.scrollIncrement, false);
    },

    
    scrollRight: function() {
        this.scrollBy(this.scrollIncrement, false);
    },

    
    getScrollPosition: function(){
        var layout = this.layout;
        return parseInt(layout.innerCt.dom['scroll' + layout.parallelBeforeCap], 10) || 0;
    },

    
    getMaxScrollPosition: function() {
        var layout = this.layout;
        return layout.innerCt.dom['scroll' + layout.parallelPrefixCap] - this.layout.innerCt['get' + layout.parallelPrefixCap]();
    },

    
    atExtremeAfter: function() {
        return this.getScrollPosition() >= this.getMaxScrollPosition();
    },

    
    scrollTo: function(position, animate) {
        var me = this,
            layout = me.layout,
            oldPosition = me.getScrollPosition(),
            newPosition = Ext.Number.constrain(position, 0, me.getMaxScrollPosition());

        if (newPosition != oldPosition && !me.scrolling) {
            if (animate == undefined) {
                animate = me.animateScroll;
            }

            layout.innerCt.scrollTo(layout.parallelBefore, newPosition, animate ? me.getScrollAnim() : false);
            if (animate) {
                me.scrolling = true;
            } else {
                me.scrolling = false;
                me.updateScrollButtons();
            }
            
            me.fireEvent('scroll', me, newPosition, animate ? me.getScrollAnim() : false);
        }
    },

    
    scrollToItem: function(item, animate) {
        var me = this,
            layout = me.layout,
            visibility,
            box,
            newPos;

        item = me.getItem(item);
        if (item != undefined) {
            visibility = this.getItemVisibility(item);
            if (!visibility.fullyVisible) {
                box  = item.getBox(true, true);
                newPos = box[layout.parallelPosition];
                if (visibility.hiddenEnd) {
                    newPos -= (this.layout.innerCt['get' + layout.parallelPrefixCap]() - box[layout.parallelPrefix]);
                }
                this.scrollTo(newPos, animate);
            }
        }
    },

    
    getItemVisibility: function(item) {
        var me          = this,
            box         = me.getItem(item).getBox(true, true),
            layout      = me.layout,
            itemStart   = box[layout.parallelPosition],
            itemEnd     = itemStart + box[layout.parallelPrefix],
            scrollStart = me.getScrollPosition(),
            scrollEnd   = scrollStart + layout.innerCt['get' + layout.parallelPrefixCap]();

        return {
            hiddenStart : itemStart < scrollStart,
            hiddenEnd   : itemEnd > scrollEnd,
            fullyVisible: itemStart > scrollStart && itemEnd < scrollEnd
        };
    }
});

Ext.define('Ext.util.Offset', {

    

    statics: {
        fromObject: function(obj) {
            return new this(obj.x, obj.y);
        }
    },

    

    constructor: function(x, y) {
        this.x = (x != null && !isNaN(x)) ? x : 0;
        this.y = (y != null && !isNaN(y)) ? y : 0;

        return this;
    },

    copy: function() {
        return new Ext.util.Offset(this.x, this.y);
    },

    copyFrom: function(p) {
        this.x = p.x;
        this.y = p.y;
    },

    toString: function() {
        return "Offset[" + this.x + "," + this.y + "]";
    },

    equals: function(offset) {
        if(!(offset instanceof this.statics())) {
            Ext.Error.raise('Offset must be an instance of Ext.util.Offset');
        }

        return (this.x == offset.x && this.y == offset.y);
    },

    round: function(to) {
        if (!isNaN(to)) {
            var factor = Math.pow(10, to);
            this.x = Math.round(this.x * factor) / factor;
            this.y = Math.round(this.y * factor) / factor;
        } else {
            this.x = Math.round(this.x);
            this.y = Math.round(this.y);
        }
    },

    isZero: function() {
        return this.x == 0 && this.y == 0;
    }
});


Ext.define('Ext.util.KeyNav', {
    
    alternateClassName: 'Ext.KeyNav',
    
    requires: ['Ext.util.KeyMap'],
    
    statics: {
        keyOptions: {
            left: 37,
            right: 39,
            up: 38,
            down: 40,
            space: 32,
            pageUp: 33,
            pageDown: 34,
            del: 46,
            backspace: 8,
            home: 36,
            end: 35,
            enter: 13,
            esc: 27,
            tab: 9
        }
    },
    
    constructor: function(el, config){
        this.setConfig(el, config || {});
    },
    
    
    setConfig: function(el, config) {
        if (this.map) {
            this.map.destroy();
        }
        
        var map = Ext.create('Ext.util.KeyMap', el, null, this.getKeyEvent('forceKeyDown' in config ? config.forceKeyDown : this.forceKeyDown)),
            keys = Ext.util.KeyNav.keyOptions,
            scope = config.scope || this,
            key;
        
        this.map = map;
        for (key in keys) {
            if (keys.hasOwnProperty(key)) {
                if (config[key]) {
                    map.addBinding({
                        scope: scope,
                        key: keys[key],
                        handler: Ext.Function.bind(this.handleEvent, scope, [config[key]], true),
                        defaultEventAction: config.defaultEventAction || this.defaultEventAction
                    });
                }
            }
        }
        
        map.disable();
        if (!config.disabled) {
            map.enable();
        }
    },
    
    
    handleEvent: function(map, event, handler){
        return handler.call(this, event);
    },
    
    
    disabled: false,
    
    
    defaultEventAction: "stopEvent",
    
    
    forceKeyDown: false,
    
    
    destroy: function(removeEl){
        this.map.destroy(removeEl);
        delete this.map;
    },

    
    enable: function() {
        this.map.enable();
        this.disabled = false;
    },

    
    disable: function() {
        this.map.disable();
        this.disabled = true;
    },
    
    
    setDisabled : function(disabled){
        this.map.setDisabled(disabled);
        this.disabled = disabled;
    },
    
    
    getKeyEvent: function(forceKeyDown){
        return (forceKeyDown || Ext.EventManager.useKeyDown) ? 'keydown' : 'keypress';
    }
});



Ext.define('Ext.fx.Queue', {

    requires: ['Ext.util.HashMap'],

    constructor: function() {
        this.targets = Ext.create('Ext.util.HashMap');
        this.fxQueue = {};
    },

    
    getFxDefaults: function(targetId) {
        var target = this.targets.get(targetId);
        if (target) {
            return target.fxDefaults;
        }
        return {};
    },

    
    setFxDefaults: function(targetId, obj) {
        var target = this.targets.get(targetId);
        if (target) {
            target.fxDefaults = Ext.apply(target.fxDefaults || {}, obj);
        }
    },

    
    stopAnimation: function(targetId) {
        var me = this,
            queue = me.getFxQueue(targetId),
            ln = queue.length;
        while (ln) {
            queue[ln - 1].end();
            ln--;
        }
    },

    
    getActiveAnimation: function(targetId) {
        var queue = this.getFxQueue(targetId);
        return (queue && !!queue.length) ? queue[0] : false;
    },

    
    hasFxBlock: function(targetId) {
        var queue = this.getFxQueue(targetId);
        return queue && queue[0] && queue[0].block;
    },

    
    getFxQueue: function(targetId) {
        if (!targetId) {
            return false;
        }
        var me = this,
            queue = me.fxQueue[targetId],
            target = me.targets.get(targetId);

        if (!target) {
            return false;
        }

        if (!queue) {
            me.fxQueue[targetId] = [];
            
            if (target.type != 'element') {
                target.target.on('destroy', function() {
                    me.fxQueue[targetId] = [];
                });
            }
        }
        return me.fxQueue[targetId];
    },

    
    queueFx: function(anim) {
        var me = this,
            target = anim.target,
            queue, ln;

        if (!target) {
            return;
        }

        queue = me.getFxQueue(target.getId());
        ln = queue.length;

        if (ln) {
            if (anim.concurrent) {
                anim.paused = false;
            }
            else {
                queue[ln - 1].on('afteranimate', function() {
                    anim.paused = false;
                });
            }
        }
        else {
            anim.paused = false;
        }
        anim.on('afteranimate', function() {
            Ext.Array.remove(queue, anim);
            if (anim.remove) {
                if (target.type == 'element') {
                    var el = Ext.get(target.id);
                    if (el) {
                        el.remove();
                    }
                }
            }
        }, this);
        queue.push(anim);
    }
});


Ext.define('Ext.fx.target.Target', {

    isAnimTarget: true,

    constructor: function(target) {
        this.target = target;
        this.id = this.getId();
    },
    
    getId: function() {
        return this.target.id;
    }
});



Ext.define('Ext.fx.target.Sprite', {

    

    extend: 'Ext.fx.target.Target',

    

    type: 'draw',

    getFromPrim: function(sprite, attr) {
        var o;
        if (attr == 'translate') {
            o = {
                x: sprite.attr.translation.x || 0,
                y: sprite.attr.translation.y || 0
            };
        }
        else if (attr == 'rotate') {
            o = {
                degrees: sprite.attr.rotation.degrees || 0,
                x: sprite.attr.rotation.x,
                y: sprite.attr.rotation.y
            };
        }
        else {
            o = sprite.attr[attr];
        }
        return o;
    },

    getAttr: function(attr, val) {
        return [[this.target, val != undefined ? val : this.getFromPrim(this.target, attr)]];
    },

    setAttr: function(targetData) {
        var ln = targetData.length,
            spriteArr = [],
            attrs, attr, attrArr, attPtr, spritePtr, idx, value, i, j, x, y, ln2;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                attrArr = attrs[attr];
                ln2 = attrArr.length;
                for (j = 0; j < ln2; j++) {
                    spritePtr = attrArr[j][0];
                    attPtr = attrArr[j][1];
                    if (attr === 'translate') {
                        value = {
                            x: attPtr.x,
                            y: attPtr.y
                        };
                    }
                    else if (attr === 'rotate') {
                        x = attPtr.x;
                        if (isNaN(x)) {
                            x = null;
                        }
                        y = attPtr.y;
                        if (isNaN(y)) {
                            y = null;
                        }
                        value = {
                            degrees: attPtr.degrees,
                            x: x,
                            y: y
                        };
                    }
                    else if (attr === 'width' || attr === 'height' || attr === 'x' || attr === 'y') {
                        value = parseFloat(attPtr);
                    }
                    else {
                        value = attPtr;
                    }
                    idx = Ext.Array.indexOf(spriteArr, spritePtr);
                    if (idx == -1) {
                        spriteArr.push([spritePtr, {}]);
                        idx = spriteArr.length - 1;
                    }
                    spriteArr[idx][1][attr] = value;
                }
            }
        }
        ln = spriteArr.length;
        for (i = 0; i < ln; i++) {
            spritePtr = spriteArr[i];
            spritePtr[0].setAttributes(spritePtr[1]);
        }
        this.target.redraw();
    }
});



Ext.define('Ext.fx.target.CompositeSprite', {

    

    extend: 'Ext.fx.target.Sprite',

    

    getAttr: function(attr, val) {
        var out = [],
            target = this.target;
        target.each(function(sprite) {
            out.push([sprite, val != undefined ? val : this.getFromPrim(sprite, attr)]);
        }, this);
        return out;
    }
});


Ext.define('Ext.fx.target.Component', {

    
   
    extend: 'Ext.fx.target.Target',
    
    

    type: 'component',

    
    getPropMethod: {
        top: function() {
            return this.getPosition(true)[1];
        },
        left: function() {
            return this.getPosition(true)[0];
        },
        x: function() {
            return this.getPosition()[0];
        },
        y: function() {
            return this.getPosition()[1];
        },
        height: function() {
            return this.getHeight();
        },
        width: function() {
            return this.getWidth();
        },
        opacity: function() {
            return this.el.getStyle('opacity');
        }
    },

    compMethod: {
        top: 'setPosition',
        left: 'setPosition',
        x: 'setPagePosition',
        y: 'setPagePosition',
        height: 'setSize',
        width: 'setSize',
        opacity: 'setOpacity'
    },

    
    getAttr: function(attr, val) {
        return [[this.target, val !== undefined ? val : this.getPropMethod[attr].call(this.target)]];
    },

    setAttr: function(targetData, isFirstFrame, isLastFrame) {
        var me = this,
            target = me.target,
            ln = targetData.length,
            attrs, attr, o, i, j, meth, targets, left, top, w, h;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                targets = attrs[attr].length;
                meth = {
                    setPosition: {},
                    setPagePosition: {},
                    setSize: {},
                    setOpacity: {}
                };
                for (j = 0; j < targets; j++) {
                    o = attrs[attr][j];
                    
                    
                    
                    
                    meth[me.compMethod[attr]].target = o[0];
                    meth[me.compMethod[attr]][attr] = o[1];
                }
                if (meth.setPosition.target) {
                    o = meth.setPosition;
                    left = (o.left === undefined) ? undefined : parseInt(o.left, 10);
                    top = (o.top === undefined) ? undefined : parseInt(o.top, 10);
                    o.target.setPosition(left, top);
                }
                if (meth.setPagePosition.target) {
                    o = meth.setPagePosition;
                    o.target.setPagePosition(o.x, o.y);
                }
                if (meth.setSize.target) {
                    o = meth.setSize;
                    
                    w = (o.width === undefined) ? o.target.getWidth() : parseInt(o.width, 10);
                    h = (o.height === undefined) ? o.target.getHeight() : parseInt(o.height, 10);

                    
                    
                    
                    
                    
                    
                    
                    if (isLastFrame || me.dynamic) {
                        o.target.componentLayout.childrenChanged = true;

                        
                        if (me.layoutAnimation) {
                            o.target.setCalculatedSize(w, h);
                        } else {
                            o.target.setSize(w, h);
                        }
                    }
                    else {
                        o.target.el.setSize(w, h);
                    }
                }
                if (meth.setOpacity.target) {
                    o = meth.setOpacity;
                    o.target.el.setStyle('opacity', o.opacity);
                }
            }
        }
    }
});


Ext.define('Ext.fx.CubicBezier', {

    

    singleton: true,

    

    cubicBezierAtTime: function(t, p1x, p1y, p2x, p2y, duration) {
        var cx = 3 * p1x,
            bx = 3 * (p2x - p1x) - cx,
            ax = 1 - cx - bx,
            cy = 3 * p1y,
            by = 3 * (p2y - p1y) - cy,
            ay = 1 - cy - by;
        function sampleCurveX(t) {
            return ((ax * t + bx) * t + cx) * t;
        }
        function solve(x, epsilon) {
            var t = solveCurveX(x, epsilon);
            return ((ay * t + by) * t + cy) * t;
        }
        function solveCurveX(x, epsilon) {
            var t0, t1, t2, x2, d2, i;
            for (t2 = x, i = 0; i < 8; i++) {
                x2 = sampleCurveX(t2) - x;
                if (Math.abs(x2) < epsilon) {
                    return t2;
                }
                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
                if (Math.abs(d2) < 1e-6) {
                    break;
                }
                t2 = t2 - x2 / d2;
            }
            t0 = 0;
            t1 = 1;
            t2 = x;
            if (t2 < t0) {
                return t0;
            }
            if (t2 > t1) {
                return t1;
            }
            while (t0 < t1) {
                x2 = sampleCurveX(t2);
                if (Math.abs(x2 - x) < epsilon) {
                    return t2;
                }
                if (x > x2) {
                    t0 = t2;
                } else {
                    t1 = t2;
                }
                t2 = (t1 - t0) / 2 + t0;
            }
            return t2;
        }
        return solve(t, 1 / (200 * duration));
    },

    cubicBezier: function(x1, y1, x2, y2) {
        var fn = function(pos) {
            return Ext.fx.CubicBezier.cubicBezierAtTime(pos, x1, y1, x2, y2, 1);
        };
        fn.toCSS3 = function() {
            return 'cubic-bezier(' + [x1, y1, x2, y2].join(',') + ')';
        };
        fn.reverse = function() {
            return Ext.fx.CubicBezier.cubicBezier(1 - x2, 1 - y2, 1 - x1, 1 - y1);
        };
        return fn;
    }
});

Ext.define('Ext.draw.Color', {

    

    

    colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
    rgbRe: /\s*rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
    hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,

    
    lightnessFactor: 0.2,

    
    constructor : function(red, green, blue) {
        var me = this,
            clamp = Ext.Number.constrain;
        me.r = clamp(red, 0, 255);
        me.g = clamp(green, 0, 255);
        me.b = clamp(blue, 0, 255);
    },

    
    getRed: function() {
        return this.r;
    },

    
    getGreen: function() {
        return this.g;
    },

    
    getBlue: function() {
        return this.b;
    },

    
    getRGB: function() {
        var me = this;
        return [me.r, me.g, me.b];
    },

    
    getHSL: function() {
        var me = this,
            r = me.r / 255,
            g = me.g / 255,
            b = me.b / 255,
            max = Math.max(r, g, b),
            min = Math.min(r, g, b),
            delta = max - min,
            h,
            s = 0,
            l = 0.5 * (max + min);

        
        if (min != max) {
            s = (l < 0.5) ? delta / (max + min) : delta / (2 - max - min);
            if (r == max) {
                h = 60 * (g - b) / delta;
            } else if (g == max) {
                h = 120 + 60 * (b - r) / delta;
            } else {
                h = 240 + 60 * (r - g) / delta;
            }
            if (h < 0) {
                h += 360;
            }
            if (h >= 360) {
                h -= 360;
            }
        }
        return [h, s, l];
    },

    
    getLighter: function(factor) {
        var hsl = this.getHSL();
        factor = factor || this.lightnessFactor;
        hsl[2] = Ext.Number.constrain(hsl[2] + factor, 0, 1);
        return this.fromHSL(hsl[0], hsl[1], hsl[2]);
    },

    
    getDarker: function(factor) {
        factor = factor || this.lightnessFactor;
        return this.getLighter(-factor);
    },

    
    toString: function() {
        var me = this,
            round = Math.round,
            r = round(me.r).toString(16),
            g = round(me.g).toString(16),
            b = round(me.b).toString(16);
        r = (r.length == 1) ? '0' + r : r;
        g = (g.length == 1) ? '0' + g : g;
        b = (b.length == 1) ? '0' + b : b;
        return ['#', r, g, b].join('');
    },

    
    toHex: function(color) {
        if (Ext.isArray(color)) {
            color = color[0];
        }
        if (!Ext.isString(color)) {
            return '';
        }
        if (color.substr(0, 1) === '#') {
            return color;
        }
        var digits = this.colorToHexRe.exec(color);

        if (Ext.isArray(digits)) {
            var red = parseInt(digits[2], 10),
                green = parseInt(digits[3], 10),
                blue = parseInt(digits[4], 10),
                rgb = blue | (green << 8) | (red << 16);
            return digits[1] + '#' + ("000000" + rgb.toString(16)).slice(-6);
        }
        else {
            return '';
        }
    },

    
    fromString: function(str) {
        var values, r, g, b,
            parse = parseInt;

        if ((str.length == 4 || str.length == 7) && str.substr(0, 1) === '#') {
            values = str.match(this.hexRe);
            if (values) {
                r = parse(values[1], 16) >> 0;
                g = parse(values[2], 16) >> 0;
                b = parse(values[3], 16) >> 0;
                if (str.length == 4) {
                    r += (r * 16);
                    g += (g * 16);
                    b += (b * 16);
                }
            }
        }
        else {
            values = str.match(this.rgbRe);
            if (values) {
                r = values[1];
                g = values[2];
                b = values[3];
            }
        }

        return (typeof r == 'undefined') ? undefined : Ext.create('Ext.draw.Color', r, g, b);
    },

    
    getGrayscale: function() {
        
        return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
    },

    
    fromHSL: function(h, s, l) {
        var C, X, m, i, rgb = [],
            abs = Math.abs,
            floor = Math.floor;

        if (s == 0 || h == null) {
            
            rgb = [l, l, l];
        }
        else {
            
            
            
            
            h /= 60;
            C = s * (1 - abs(2 * l - 1));
            X = C * (1 - abs(h - 2 * floor(h / 2) - 1));
            m = l - C / 2;
            switch (floor(h)) {
                case 0:
                    rgb = [C, X, 0];
                    break;
                case 1:
                    rgb = [X, C, 0];
                    break;
                case 2:
                    rgb = [0, C, X];
                    break;
                case 3:
                    rgb = [0, X, C];
                    break;
                case 4:
                    rgb = [X, 0, C];
                    break;
                case 5:
                    rgb = [C, 0, X];
                    break;
            }
            rgb = [rgb[0] + m, rgb[1] + m, rgb[2] + m];
        }
        return Ext.create('Ext.draw.Color', rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
    }
}, function() {
    var prototype = this.prototype;

    
    this.addStatics({
        fromHSL: function() {
            return prototype.fromHSL.apply(prototype, arguments);
        },
        fromString: function() {
            return prototype.fromString.apply(prototype, arguments);
        },
        toHex: function() {
            return prototype.toHex.apply(prototype, arguments);
        }
    });
});


Ext.define('Ext.dd.StatusProxy', {
    animRepair: false,

    constructor: function(config){
        Ext.apply(this, config);
        this.id = this.id || Ext.id();
        this.proxy = Ext.createWidget('component', {
            floating: true,
            id: this.id,
            html: '<div class="' + Ext.baseCSSPrefix + 'dd-drop-icon"></div>' +
                  '<div class="' + Ext.baseCSSPrefix + 'dd-drag-ghost"></div>',
            cls: Ext.baseCSSPrefix + 'dd-drag-proxy ' + this.dropNotAllowed,
            shadow: !config || config.shadow !== false,
            renderTo: document.body
        });

        this.el = this.proxy.el;
        this.el.show();
        this.el.setVisibilityMode(Ext.core.Element.VISIBILITY);
        this.el.hide();

        this.ghost = Ext.get(this.el.dom.childNodes[1]);
        this.dropStatus = this.dropNotAllowed;
    },
    
    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    setStatus : function(cssClass){
        cssClass = cssClass || this.dropNotAllowed;
        if(this.dropStatus != cssClass){
            this.el.replaceCls(this.dropStatus, cssClass);
            this.dropStatus = cssClass;
        }
    },

    
    reset : function(clearGhost){
        this.el.dom.className = Ext.baseCSSPrefix + 'dd-drag-proxy ' + this.dropNotAllowed;
        this.dropStatus = this.dropNotAllowed;
        if(clearGhost){
            this.ghost.update("");
        }
    },

    
    update : function(html){
        if(typeof html == "string"){
            this.ghost.update(html);
        }else{
            this.ghost.update("");
            html.style.margin = "0";
            this.ghost.dom.appendChild(html);
        }
        var el = this.ghost.dom.firstChild; 
        if(el){
            Ext.fly(el).setStyle('float', 'none');
        }
    },

    
    getEl : function(){
        return this.el;
    },

    
    getGhost : function(){
        return this.ghost;
    },

    
    hide : function(clear) {
        this.proxy.hide();
        if (clear) {
            this.reset(true);
        }
    },

    
    stop : function(){
        if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
            this.anim.stop();
        }
    },

    
    show : function() {
        this.proxy.show();
        this.proxy.toFront();
    },

    
    sync : function(){
        this.proxy.el.sync();
    },

    
    repair : function(xy, callback, scope){
        this.callback = callback;
        this.scope = scope;
        if (xy && this.animRepair !== false) {
            this.el.addCls(Ext.baseCSSPrefix + 'dd-drag-repair');
            this.el.hideUnders(true);
            this.anim = this.el.animate({
                duration: this.repairDuration || 500,
                easing: 'ease-out',
                to: {
                    x: xy[0],
                    y: xy[1]
                },
                stopAnimation: true,
                callback: this.afterRepair,
                scope: this
            });
        } else {
            this.afterRepair();
        }
    },

    
    afterRepair : function(){
        this.hide(true);
        if(typeof this.callback == "function"){
            this.callback.call(this.scope || this);
        }
        this.callback = null;
        this.scope = null;
    },

    destroy: function(){
        Ext.destroy(this.ghost, this.proxy, this.el);
    }
});

Ext.define('Ext.panel.Proxy', {
    
    alternateClassName: 'Ext.dd.PanelProxy',
    
    constructor: function(panel, config){
        
        this.panel = panel;
        this.id = this.panel.id +'-ddproxy';
        Ext.apply(this, config);
    },

    
    insertProxy: true,

    
    setStatus: Ext.emptyFn,
    reset: Ext.emptyFn,
    update: Ext.emptyFn,
    stop: Ext.emptyFn,
    sync: Ext.emptyFn,

    
    getEl: function(){
        return this.ghost.el;
    },

    
    getGhost: function(){
        return this.ghost;
    },

    
    getProxy: function(){
        return this.proxy;
    },

    
    hide : function(){
        if (this.ghost) {
            if (this.proxy) {
                this.proxy.remove();
                delete this.proxy;
            }

            
            this.panel.unghost(null, false);
            delete this.ghost;
        }
    },

    
    show: function(){
        if (!this.ghost) {
            var panelSize = this.panel.getSize();
            this.panel.el.setVisibilityMode(Ext.core.Element.DISPLAY);
            this.ghost = this.panel.ghost();
            if (this.insertProxy) {
                
                
                this.proxy = this.panel.el.insertSibling({cls: Ext.baseCSSPrefix + 'panel-dd-spacer'});
                this.proxy.setSize(panelSize);
            }
        }
    },

    
    repair: function(xy, callback, scope) {
        this.hide();
        if (typeof callback == "function") {
            callback.call(scope || this);
        }
    },

    
    moveProxy : function(parentNode, before){
        if (this.proxy) {
            parentNode.insertBefore(this.proxy.dom, before);
        }
    }
});


Ext.define('Ext.layout.component.AbstractDock', {

    

    extend: 'Ext.layout.component.Component',

    

    type: 'dock',

    
    autoSizing: true,

    beforeLayout: function() {
        var returnValue = this.callParent(arguments);
        if (returnValue !== false && (!this.initializedBorders || this.childrenChanged) && (!this.owner.border || this.owner.manageBodyBorders)) {
            this.handleItemBorders();
            this.initializedBorders = true;
        }
        return returnValue;
    },
    
    handleItemBorders: function() {
        var owner = this.owner,
            body = owner.body,
            docked = this.getLayoutItems(),
            borders = {
                top: [],
                right: [],
                bottom: [],
                left: []
            },
            oldBorders = this.borders,
            opposites = {
                top: 'bottom',
                right: 'left',
                bottom: 'top',
                left: 'right'
            },
            i, ln, item, dock, side;

        for (i = 0, ln = docked.length; i < ln; i++) {
            item = docked[i];
            dock = item.dock;
            
            if (item.ignoreBorderManagement) {
                continue;
            }
            
            if (!borders[dock].satisfied) {
                borders[dock].push(item);
                borders[dock].satisfied = true;
            }
            
            if (!borders.top.satisfied && opposites[dock] !== 'top') {
                borders.top.push(item);
            }
            if (!borders.right.satisfied && opposites[dock] !== 'right') {
                borders.right.push(item);
            }            
            if (!borders.bottom.satisfied && opposites[dock] !== 'bottom') {
                borders.bottom.push(item);
            }            
            if (!borders.left.satisfied && opposites[dock] !== 'left') {
                borders.left.push(item);
            }
        }

        if (oldBorders) {
            for (side in oldBorders) {
                if (oldBorders.hasOwnProperty(side)) {
                    ln = oldBorders[side].length;
                    if (!owner.manageBodyBorders) {
                        for (i = 0; i < ln; i++) {
                            oldBorders[side][i].removeCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
                        }
                        if (!oldBorders[side].satisfied && !owner.bodyBorder) {
                            body.removeCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);                   
                        }                    
                    }
                    else if (oldBorders[side].satisfied) {
                        body.setStyle('border-' + side + '-width', '');
                    }
                }
            }
        }
                
        for (side in borders) {
            if (borders.hasOwnProperty(side)) {
                ln = borders[side].length;
                if (!owner.manageBodyBorders) {
                    for (i = 0; i < ln; i++) {
                        borders[side][i].addCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);
                    }
                    if ((!borders[side].satisfied && !owner.bodyBorder) || owner.bodyBorder === false) {
                        body.addCls(Ext.baseCSSPrefix + 'docked-noborder-' + side);                   
                    }                    
                }
                else if (borders[side].satisfied) {
                    body.setStyle('border-' + side + '-width', '1px');
                }
            }
        }
        
        this.borders = borders;
    },
    
    
    onLayout: function(width, height) {
        var me = this,
            owner = me.owner,
            body = owner.body,
            layout = owner.layout,
            target = me.getTarget(),
            autoWidth = false,
            autoHeight = false,
            padding, border, frameSize;

        
        var info = me.info = {
            boxes: [],
            size: {
                width: width,
                height: height
            },
            bodyBox: {}
        };

        Ext.applyIf(info, me.getTargetInfo());

        
        if (owner && owner.ownerCt && owner.ownerCt.layout && owner.ownerCt.layout.isLayout) {
            if (!Ext.isNumber(owner.height) || !Ext.isNumber(owner.width)) {
                owner.ownerCt.layout.bindToOwnerCtComponent = true;
            }
            else {
                owner.ownerCt.layout.bindToOwnerCtComponent = false;
            }
        }

        
        if (height === undefined || height === null || width === undefined || width === null) {
            padding = info.padding;
            border = info.border;
            frameSize = me.frameSize;

            
            if ((height === undefined || height === null) && (width === undefined || width === null)) {
                autoHeight = true;
                autoWidth = true;
                me.setTargetSize(null);
                me.setBodyBox({width: null, height: null});
            }
            
            else if (height === undefined || height === null) {
                autoHeight = true;
                
                me.setTargetSize(width);
                me.setBodyBox({width: width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right, height: null});
            
            }
            else {
                autoWidth = true;
                
                me.setTargetSize(null, height);
                me.setBodyBox({width: null, height: height - padding.top - padding.bottom - border.top - border.bottom - frameSize.top - frameSize.bottom});
            }

            
            if (layout && layout.isLayout) {
                
                layout.bindToOwnerCtComponent = true;
                layout.layout();

                
                
                
                
                
                
                
                
                
                
                
                
                info.autoSizedCtLayout = layout.autoSize === true;
            }

            
            
            
            
            
            me.dockItems(autoWidth, autoHeight);
            me.setTargetSize(info.size.width, info.size.height);
        }
        else {
            me.setTargetSize(width, height);
            me.dockItems();
        }
        me.callParent(arguments);
    },

    
    dockItems : function(autoWidth, autoHeight) {
        this.calculateDockBoxes(autoWidth, autoHeight);

        
        
        
        var info = this.info,
            boxes = info.boxes,
            ln = boxes.length,
            dock, i;

        
        
        for (i = 0; i < ln; i++) {
            dock = boxes[i];
            dock.item.setPosition(dock.x, dock.y);
            if ((autoWidth || autoHeight) && dock.layout && dock.layout.isLayout) {
                
                dock.layout.bindToOwnerCtComponent = true;
            }
        }

        
        
        if (!info.autoSizedCtLayout) {
            if (autoWidth) {
                info.bodyBox.width = null;
            }
            if (autoHeight) {
                info.bodyBox.height = null;
            }
        }

        
        
        this.setBodyBox(info.bodyBox);
    },

    
    calculateDockBoxes : function(autoWidth, autoHeight) {
        
        
        
        var me = this,
            target = me.getTarget(),
            items = me.getLayoutItems(),
            owner = me.owner,
            bodyEl = owner.body,
            info = me.info,
            size = info.size,
            ln = items.length,
            padding = info.padding,
            border = info.border,
            frameSize = me.frameSize,
            item, i, box, rect;

        
        
        if (autoHeight) {
            size.height = bodyEl.getHeight() + padding.top + border.top + padding.bottom + border.bottom + frameSize.top + frameSize.bottom;
        }
        else {
            size.height = target.getHeight();
        }
        if (autoWidth) {
            size.width = bodyEl.getWidth() + padding.left + border.left + padding.right + border.right + frameSize.left + frameSize.right;
        }
        else {
            size.width = target.getWidth();
        }

        info.bodyBox = {
            x: padding.left + frameSize.left,
            y: padding.top + frameSize.top,
            width: size.width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right,
            height: size.height - border.top - padding.top - border.bottom - padding.bottom - frameSize.top - frameSize.bottom
        };

        
        for (i = 0; i < ln; i++) {
            item = items[i];
            
            
            
            box = me.initBox(item);

            if (autoHeight === true) {
                box = me.adjustAutoBox(box, i);
            }
            else {
                box = me.adjustSizedBox(box, i);
            }

            
            
            
            info.boxes.push(box);
        }
    },

    
    adjustSizedBox : function(box, index) {
        var bodyBox = this.info.bodyBox,
            frameSize = this.frameSize,
            info = this.info,
            padding = info.padding,
            pos = box.type,
            border = info.border;

        switch (pos) {
            case 'top':
                box.y = bodyBox.y;
                break;

            case 'left':
                box.x = bodyBox.x;
                break;

            case 'bottom':
                box.y = (bodyBox.y + bodyBox.height) - box.height;
                break;

            case 'right':
                box.x = (bodyBox.x + bodyBox.width) - box.width;
                break;
        }

        if (box.ignoreFrame) {
            if (pos == 'bottom') {
                box.y += (frameSize.bottom + padding.bottom + border.bottom);
            }
            else {
                box.y -= (frameSize.top + padding.top + border.top);
            }
            if (pos == 'right') {
                box.x += (frameSize.right + padding.right + border.right);
            }
            else {
                box.x -= (frameSize.left + padding.left + border.left);
            }
        }

        
        if (!box.overlay) {
            switch (pos) {
                case 'top':
                    bodyBox.y += box.height;
                    bodyBox.height -= box.height;
                    break;

                case 'left':
                    bodyBox.x += box.width;
                    bodyBox.width -= box.width;
                    break;

                case 'bottom':
                    bodyBox.height -= box.height;
                    break;

                case 'right':
                    bodyBox.width -= box.width;
                    break;
            }
        }
        return box;
    },

    
    adjustAutoBox : function (box, index) {
        var info = this.info,
            bodyBox = info.bodyBox,
            size = info.size,
            boxes = info.boxes,
            boxesLn = boxes.length,
            pos = box.type,
            frameSize = this.frameSize,
            padding = info.padding,
            border = info.border,
            autoSizedCtLayout = info.autoSizedCtLayout,
            ln = (boxesLn < index) ? boxesLn : index,
            i, adjustBox;

        if (pos == 'top' || pos == 'bottom') {
            
            for (i = 0; i < ln; i++) {
                adjustBox = boxes[i];
                if (adjustBox.stretched && adjustBox.type == 'left' || adjustBox.type == 'right') {
                    adjustBox.height += box.height;
                }
                else if (adjustBox.type == 'bottom') {
                    adjustBox.y += box.height;
                }
            }
        }

        switch (pos) {
            case 'top':
                box.y = bodyBox.y;
                if (!box.overlay) {
                    bodyBox.y += box.height;
                }
                size.height += box.height;
                break;

            case 'bottom':
                box.y = (bodyBox.y + bodyBox.height);
                size.height += box.height;
                break;

            case 'left':
                box.x = bodyBox.x;
                if (!box.overlay) {
                    bodyBox.x += box.width;
                    if (autoSizedCtLayout) {
                        size.width += box.width;
                    } else {
                        bodyBox.width -= box.width;
                    }
                }
                break;

            case 'right':
                if (!box.overlay) {
                    if (autoSizedCtLayout) {
                        size.width += box.width;
                    } else {
                        bodyBox.width -= box.width;
                    }
                }
                box.x = (bodyBox.x + bodyBox.width);
                break;
        }

        if (box.ignoreFrame) {
            if (pos == 'bottom') {
                box.y += (frameSize.bottom + padding.bottom + border.bottom);
            }
            else {
                box.y -= (frameSize.top + padding.top + border.top);
            }
            if (pos == 'right') {
                box.x += (frameSize.right + padding.right + border.right);
            }
            else {
                box.x -= (frameSize.left + padding.left + border.left);
            }
        }
        return box;
    },

    
    initBox : function(item) {
        var me = this,
            bodyBox = me.info.bodyBox,
            horizontal = (item.dock == 'top' || item.dock == 'bottom'),
            owner = me.owner,
            frameSize = me.frameSize,
            info = me.info,
            padding = info.padding,
            border = info.border,
            box = {
                item: item,
                overlay: item.overlay,
                type: item.dock,
                offsets: Ext.core.Element.parseBox(item.offsets || {}),
                ignoreFrame: item.ignoreParentFrame
            };
        
        if (item.stretch !== false) {
            box.stretched = true;
            if (horizontal) {
                box.x = bodyBox.x + box.offsets.left;
                box.width = bodyBox.width - (box.offsets.left + box.offsets.right);
                if (box.ignoreFrame) {
                    box.width += (frameSize.left + frameSize.right + border.left + border.right + padding.left + padding.right);
                }
                item.setCalculatedSize(box.width - item.el.getMargin('lr'), undefined, owner);
            }
            else {
                box.y = bodyBox.y + box.offsets.top;
                box.height = bodyBox.height - (box.offsets.bottom + box.offsets.top);
                if (box.ignoreFrame) {
                    box.height += (frameSize.top + frameSize.bottom + border.top + border.bottom + padding.top + padding.bottom);
                }
                item.setCalculatedSize(undefined, box.height - item.el.getMargin('tb'), owner);

                
                
                if (!Ext.supports.ComputedStyle) {
                    item.el.repaint();
                }
            }
        }
        else {
            item.doComponentLayout();
            box.width = item.getWidth() - (box.offsets.left + box.offsets.right);
            box.height = item.getHeight() - (box.offsets.bottom + box.offsets.top);
            box.y += box.offsets.top;
            if (horizontal) {
                box.x = (item.align == 'right') ? bodyBox.width - box.width : bodyBox.x;
                box.x += box.offsets.left;
            }
        }

        
        
        if (box.width == undefined) {
            box.width = item.getWidth() + item.el.getMargin('lr');
        }
        if (box.height == undefined) {
            box.height = item.getHeight() + item.el.getMargin('tb');
        }

        return box;
    },

    
    getLayoutItems : function() {
        var it = this.owner.getDockedItems(),
            ln = it.length,
            i = 0,
            result = [];
        for (; i < ln; i++) {
            if (it[i].isVisible(true)) {
                result.push(it[i]);
            }
        }
        return result;
    },

    
    renderItems: function(items, target) {
        var cns = target.dom.childNodes,
            cnsLn = cns.length,
            ln = items.length,
            domLn = 0,
            i, j, cn, item;

        
        for (i = 0; i < cnsLn; i++) {
            cn = Ext.get(cns[i]);
            for (j = 0; j < ln; j++) {
                item = items[j];
                if (item.rendered && (cn.id == item.el.id || cn.down('#' + item.el.id))) {
                    break;
                }
            }

            if (j === ln) {
                domLn++;
            }
        }

        
        for (i = 0, j = 0; i < ln; i++, j++) {
            item = items[i];

            
            
            
            
            
            
            
            
            if (i === j && (item.dock === 'right' || item.dock === 'bottom')) {
                j += domLn;
            }

            
            if (item && !item.rendered) {
                this.renderItem(item, target, j);
            }
            else if (!this.isValidParent(item, target, j)) {
                this.moveItem(item, target, j);
            }
        }
    },

    
    setBodyBox : function(box) {
        var me = this,
            owner = me.owner,
            body = owner.body,
            info = me.info,
            bodyMargin = info.bodyMargin,
            padding = info.padding,
            border = info.border,
            frameSize = me.frameSize;
        
        
        if (owner.collapsed) {
            return;
        }
        
        if (Ext.isNumber(box.width)) {
            box.width -= bodyMargin.left + bodyMargin.right;
        }
        
        if (Ext.isNumber(box.height)) {
            box.height -= bodyMargin.top + bodyMargin.bottom;
        }
        
        me.setElementSize(body, box.width, box.height);
        if (Ext.isNumber(box.x)) {
            body.setLeft(box.x - padding.left - frameSize.left);
        }
        if (Ext.isNumber(box.y)) {
            body.setTop(box.y - padding.top - frameSize.top);
        }
    },

    
    configureItem : function(item, pos) {
        this.callParent(arguments);
        
        item.addCls(Ext.baseCSSPrefix + 'docked');
        item.addClsWithUI('docked-' + item.dock);
    },

    afterRemove : function(item) {
        this.callParent(arguments);
        if (this.itemCls) {
            item.el.removeCls(this.itemCls + '-' + item.dock);
        }
        var dom = item.el.dom;

        if (!item.destroying && dom) {
            dom.parentNode.removeChild(dom);
        }
        this.childrenChanged = true;
    }
});

Ext.define('Ext.app.EventBus', {
    requires: [
        'Ext.util.Event'
    ],
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    constructor: function() {
        this.mixins.observable.constructor.call(this);
        
        this.bus = {};
        
        var me = this;
        Ext.override(Ext.Component, {
            fireEvent: function(ev) {
                if (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false) {
                    return me.dispatch.call(me, ev, this, arguments);
                }
                return false;
            }
        });
    },

    dispatch: function(ev, target, args) {
        var bus = this.bus,
            selectors = bus[ev],
            selector, controllers, id, events, event, i, ln;
        
        if (selectors) {
            
            for (selector in selectors) {
                
                if (target.is(selector)) {
                    
                    controllers = selectors[selector];
                    for (id in controllers) {
                        
                        events = controllers[id];
                        for (i = 0, ln = events.length; i < ln; i++) {
                            event = events[i];
                            
                            return event.fire.apply(event, Array.prototype.slice.call(args, 1));
                        }
                    }
                }
            }
        }
    },
    
    control: function(selectors, listeners, controller) {
        var bus = this.bus,
            selector, fn;
        
        if (Ext.isString(selectors)) {
            selector = selectors;
            selectors = {};
            selectors[selector] = listeners;
            this.control(selectors, null, controller);
            return;
        }
    
        Ext.Object.each(selectors, function(selector, listeners) {
            Ext.Object.each(listeners, function(ev, listener) {
                var options = {},   
                    scope = controller,
                    event = Ext.create('Ext.util.Event', controller, ev);
                
                
                if (Ext.isObject(listener)) {
                    options = listener;
                    listener = options.fn;
                    scope = options.scope || controller;
                    delete options.fn;
                    delete options.scope;
                }
                
                event.addListener(listener, scope, options);

                
                bus[ev] = bus[ev] || {};
                bus[ev][selector] = bus[ev][selector] || {};
                bus[ev][selector][controller.id] = bus[ev][selector][controller.id] || [];            
                
                
                bus[ev][selector][controller.id].push(event);
            });
        });
    }
});

Ext.define('Ext.data.Types', {
    singleton: true,
    requires: ['Ext.data.SortTypes']
}, function() {
    var st = Ext.data.SortTypes;
    
    Ext.apply(Ext.data.Types, {
        
        stripRe: /[\$,%]/g,
        
        
        AUTO: {
            convert: function(v) {
                return v;
            },
            sortType: st.none,
            type: 'auto'
        },

        
        STRING: {
            convert: function(v) {
                var defaultValue = this.useNull ? null : '';
                return (v === undefined || v === null) ? defaultValue : String(v);
            },
            sortType: st.asUCString,
            type: 'string'
        },

        
        INT: {
            convert: function(v) {
                return v !== undefined && v !== null && v !== '' ?
                    parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'int'
        },
        
        
        FLOAT: {
            convert: function(v) {
                return v !== undefined && v !== null && v !== '' ?
                    parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'float'
        },
        
        
        BOOL: {
            convert: function(v) {
                if (this.useNull && v === undefined || v === null || v === '') {
                    return null;
                }
                return v === true || v === 'true' || v == 1;
            },
            sortType: st.none,
            type: 'bool'
        },
        
        
        DATE: {
            convert: function(v) {
                var df = this.dateFormat;
                if (!v) {
                    return null;
                }
                if (Ext.isDate(v)) {
                    return v;
                }
                if (df) {
                    if (df == 'timestamp') {
                        return new Date(v*1000);
                    }
                    if (df == 'time') {
                        return new Date(parseInt(v, 10));
                    }
                    return Ext.Date.parse(v, df);
                }
                
                var parsed = Date.parse(v);
                return parsed ? new Date(parsed) : null;
            },
            sortType: st.asDate,
            type: 'date'
        }
    });
    
    Ext.apply(Ext.data.Types, {
        
        BOOLEAN: this.BOOL,
        
        
        INTEGER: this.INT,
        
        
        NUMBER: this.FLOAT    
    });
});


Ext.define('Ext.data.Field', {
    requires: ['Ext.data.Types', 'Ext.data.SortTypes'],
    alias: 'data.field',
    
    constructor : function(config) {
        if (Ext.isString(config)) {
            config = {name: config};
        }
        Ext.apply(this, config);
        
        var types = Ext.data.Types,
            st = this.sortType,
            t;

        if (this.type) {
            if (Ext.isString(this.type)) {
                this.type = types[this.type.toUpperCase()] || types.AUTO;
            }
        } else {
            this.type = types.AUTO;
        }

        
        if (Ext.isString(st)) {
            this.sortType = Ext.data.SortTypes[st];
        } else if(Ext.isEmpty(st)) {
            this.sortType = this.type.sortType;
        }

        if (!this.convert) {
            this.convert = this.type.convert;
        }
    },
    
    
    
    
    
    
    
    dateFormat: null,
    
    
    useNull: false,
    
    
    defaultValue: "",
    
    mapping: null,
    
    sortType : null,
    
    sortDir : "ASC",
    
    allowBlank : true,
    
    
    persist: true
});


Ext.define('Ext.data.reader.Reader', {
    requires: ['Ext.data.ResultSet'],
    alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],
    
    

    
    totalProperty: 'total',

    
    successProperty: 'success',

    
    root: '',
    
    
    
    
    implicitIncludes: true,
    
    isReader: true,
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config || {});
        me.fieldCount = 0;
        me.model = Ext.ModelManager.getModel(config.model);
        if (me.model) {
            me.buildExtractors();
        }
    },

    
    setModel: function(model, setOnProxy) {
        var me = this;
        
        me.model = Ext.ModelManager.getModel(model);
        me.buildExtractors(true);
        
        if (setOnProxy && me.proxy) {
            me.proxy.setModel(me.model, true);
        }
    },

    
    read: function(response) {
        var data = response;
        
        if (response && response.responseText) {
            data = this.getResponseData(response);
        }
        
        if (data) {
            return this.readRecords(data);
        } else {
            return this.nullResultSet;
        }
    },

    
    readRecords: function(data) {
        var me  = this;
        
        
        if (me.fieldCount !== me.getFields().length) {
            me.buildExtractors(true);
        }
        
        
        me.rawData = data;

        data = me.getData(data);

        
        
        var root    = Ext.isArray(data) ? data : me.getRoot(data),
            success = true,
            recordCount = 0,
            total, value, records, message;
            
        if (root) {
            total = root.length;
        }

        if (me.totalProperty) {
            value = parseInt(me.getTotal(data), 10);
            if (!isNaN(value)) {
                total = value;
            }
        }

        if (me.successProperty) {
            value = me.getSuccess(data);
            if (value === false || value === 'false') {
                success = false;
            }
        }
        
        if (me.messageProperty) {
            message = me.getMessage(data);
        }
        
        if (root) {
            records = me.extractData(root);
            recordCount = records.length;
        } else {
            recordCount = 0;
            records = [];
        }

        return Ext.create('Ext.data.ResultSet', {
            total  : total || recordCount,
            count  : recordCount,
            records: records,
            success: success,
            message: message
        });
    },

    
    extractData : function(root) {
        var me = this,
            values  = [],
            records = [],
            Model   = me.model,
            i       = 0,
            length  = root.length,
            idProp  = me.getIdProperty(),
            node, id, record;
            
        if (!root.length && Ext.isObject(root)) {
            root = [root];
            length = 1;
        }

        for (; i < length; i++) {
            node   = root[i];
            values = me.extractValues(node);
            id     = me.getId(node);

            
            record = new Model(values, id, node);
            records.push(record);
                
            if (me.implicitIncludes) {
                me.readAssociated(record, node);
            }
        }

        return records;
    },
    
    
    readAssociated: function(record, data) {
        var associations = record.associations.items,
            i            = 0,
            length       = associations.length,
            association, associationData, proxy, reader;
        
        for (; i < length; i++) {
            association     = associations[i];
            associationData = this.getAssociatedDataRoot(data, association.associationKey || association.name);
            
            if (associationData) {
                reader = association.getReader();
                if (!reader) {
                    proxy = association.associatedModel.proxy;
                    
                    if (proxy) {
                        reader = proxy.getReader();
                    } else {
                        reader = new this.constructor({
                            model: association.associatedName
                        });
                    }
                }
                association.read(record, reader, associationData);
            }  
        }
    },
    
    
    getAssociatedDataRoot: function(data, associationName) {
        return data[associationName];
    },
    
    getFields: function() {
        return this.model.prototype.fields.items;
    },

    
    extractValues: function(data) {
        var fields = this.getFields(),
            i      = 0,
            length = fields.length,
            output = {},
            field, value;

        for (; i < length; i++) {
            field = fields[i];
            value = this.extractorFunctions[i](data);

            output[field.name] = value;
        }

        return output;
    },

    
    getData: function(data) {
        return data;
    },

    
    getRoot: function(data) {
        return data;
    },

    
    getResponseData: function(response) {
        Ext.Error.raise("getResponseData must be implemented in the Ext.data.reader.Reader subclass");
    },

    
    onMetaChange : function(meta) {
        var fields = meta.fields,
            newModel;
        
        Ext.apply(this, meta);
        
        if (fields) {
            newModel = Ext.define("Ext.data.reader.Json-Model" + Ext.id(), {
                extend: 'Ext.data.Model',
                fields: fields
            });
            this.setModel(newModel, true);
        } else {
            this.buildExtractors(true);
        }
    },
    
    
    getIdProperty: function(){
        var prop = this.idProperty;
        if (Ext.isEmpty(prop)) {
            prop = this.model.prototype.idProperty;
        }
        return prop;
    },

    
    buildExtractors: function(force) {
        var me          = this,
            idProp      = me.getIdProperty(),
            totalProp   = me.totalProperty,
            successProp = me.successProperty,
            messageProp = me.messageProperty,
            accessor;
            
        if (force === true) {
            delete me.extractorFunctions;
        }
        
        if (me.extractorFunctions) {
            return;
        }   

        
        if (totalProp) {
            me.getTotal = me.createAccessor(totalProp);
        }

        if (successProp) {
            me.getSuccess = me.createAccessor(successProp);
        }

        if (messageProp) {
            me.getMessage = me.createAccessor(messageProp);
        }

        if (idProp) {
            accessor = me.createAccessor(idProp);

            me.getId = function(record) {
                var id = accessor.call(me, record);
                return (id === undefined || id === '') ? null : id;
            };
        } else {
            me.getId = function() {
                return null;
            };
        }
        me.buildFieldExtractors();
    },

    
    buildFieldExtractors: function() {
        
        var me = this,
            fields = me.getFields(),
            ln = fields.length,
            i  = 0,
            extractorFunctions = [],
            field, map;

        for (; i < ln; i++) {
            field = fields[i];
            map   = (field.mapping !== undefined && field.mapping !== null) ? field.mapping : field.name;

            extractorFunctions.push(me.createAccessor(map));
        }
        me.fieldCount = ln;

        me.extractorFunctions = extractorFunctions;
    }
}, function() {
    Ext.apply(this, {
        
        nullResultSet: Ext.create('Ext.data.ResultSet', {
            total  : 0,
            count  : 0,
            records: [],
            success: true
        })
    });
});

Ext.define('Ext.data.reader.Json', {
    extend: 'Ext.data.reader.Reader',
    alternateClassName: 'Ext.data.JsonReader',
    alias : 'reader.json',
    
    root: '',
    
    
    
    
    useSimpleAccessors: false,
    
    
    readRecords: function(data) {
        
        if (data.metaData) {
            this.onMetaChange(data.metaData);
        }

        
        this.jsonData = data;
        return this.callParent([data]);
    },

    
    getResponseData: function(response) {
        try {
            var data = Ext.decode(response.responseText);
        }
        catch (ex) {
            Ext.Error.raise({
                response: response,
                json: response.responseText,
                parseError: ex,
                msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
            });
        }
        if (!data) {
            Ext.Error.raise('JSON object not found');
        }

        return data;
    },

    
    buildExtractors : function() {
        var me = this;
        
        me.callParent(arguments);

        if (me.root) {
            me.getRoot = me.createAccessor(me.root);
        } else {
            me.getRoot = function(root) {
                return root;
            };
        }
    },
    
    
    extractData: function(root) {
        var recordName = this.record,
            data = [],
            length, i;
        
        if (recordName) {
            length = root.length;
            
            for (i = 0; i < length; i++) {
                data[i] = root[i][recordName];
            }
        } else {
            data = root;
        }
        return this.callParent([data]);
    },

    
    createAccessor: function() {
        var re = /[\[\.]/;
        
        return function(expr) {
            if (Ext.isEmpty(expr)) {
                return Ext.emptyFn;
            }
            if (Ext.isFunction(expr)) {
                return expr;
            }
            if (this.useSimpleAccessors !== true) {
                var i = String(expr).search(re);
                if (i >= 0) {
                    return Ext.functionFactory('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
                }
            }
            return function(obj) {
                return obj[expr];
            };
        };
    }()
});

Ext.define('Ext.data.writer.Json', {
    extend: 'Ext.data.writer.Writer',
    alternateClassName: 'Ext.data.JsonWriter',
    alias: 'writer.json',
    
    
    root: undefined,
    
    
    encode: false,
    
    
    allowSingle: true,
    
    
    writeRecords: function(request, data) {
        var root = this.root;
        
        if (this.allowSingle && data.length == 1) {
            
            data = data[0];
        }
        
        if (this.encode) {
            if (root) {
                
                request.params[root] = Ext.encode(data);
            } else {
                Ext.Error.raise('Must specify a root when using encode');
            }
        } else {
            
            request.jsonData = request.jsonData || {};
            if (root) {
                request.jsonData[root] = data;
            } else {
                request.jsonData = data;
            }
        }
        return request;
    }
});


Ext.define('Ext.data.proxy.Proxy', {
    alias: 'proxy.proxy',
    alternateClassName: ['Ext.data.DataProxy', 'Ext.data.Proxy'],
    requires: [
        'Ext.data.reader.Json',
        'Ext.data.writer.Json'
    ],
    uses: [
        'Ext.data.Batch', 
        'Ext.data.Operation', 
        'Ext.data.Model'
    ],
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    
    batchOrder: 'create,update,destroy',
    
    
    batchActions: true,
    
    
    defaultReaderType: 'json',
    
    
    defaultWriterType: 'json',
    
    
    
    isProxy: true,
    
    constructor: function(config) {
        config = config || {};
        
        if (config.model === undefined) {
            delete config.model;
        }

        this.mixins.observable.constructor.call(this, config);
        
        if (this.model !== undefined && !(this.model instanceof Ext.data.Model)) {
            this.setModel(this.model);
        }
    },
    
    
    setModel: function(model, setOnStore) {
        this.model = Ext.ModelManager.getModel(model);
        
        var reader = this.reader,
            writer = this.writer;
        
        this.setReader(reader);
        this.setWriter(writer);
        
        if (setOnStore && this.store) {
            this.store.setModel(this.model);
        }
    },
    
    
    getModel: function() {
        return this.model;
    },
    
    
    setReader: function(reader) {
        var me = this;
        
        if (reader === undefined || typeof reader == 'string') {
            reader = {
                type: reader
            };
        }

        if (reader.isReader) {
            reader.setModel(me.model);
        } else {
            Ext.applyIf(reader, {
                proxy: me,
                model: me.model,
                type : me.defaultReaderType
            });

            reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        
        me.reader = reader;
        return me.reader;
    },
    
    
    getReader: function() {
        return this.reader;
    },
    
    
    setWriter: function(writer) {
        if (writer === undefined || typeof writer == 'string') {
            writer = {
                type: writer
            };
        }

        if (!(writer instanceof Ext.data.writer.Writer)) {
            Ext.applyIf(writer, {
                model: this.model,
                type : this.defaultWriterType
            });

            writer = Ext.createByAlias('writer.' + writer.type, writer);
        }
        
        this.writer = writer;
        
        return this.writer;
    },
    
    
    getWriter: function() {
        return this.writer;
    },
    
    
    create: Ext.emptyFn,
    
    
    read: Ext.emptyFn,
    
    
    update: Ext.emptyFn,
    
    
    destroy: Ext.emptyFn,
    
    
    batch: function(operations, listeners) {
        var me = this,
            batch = Ext.create('Ext.data.Batch', {
                proxy: me,
                listeners: listeners || {}
            }),
            useBatch = me.batchActions, 
            records;
        
        Ext.each(me.batchOrder.split(','), function(action) {
            records = operations[action];
            if (records) {
                if (useBatch) {
                    batch.add(Ext.create('Ext.data.Operation', {
                        action: action,
                        records: records
                    }));
                } else {
                    Ext.each(records, function(record){
                        batch.add(Ext.create('Ext.data.Operation', {
                            action : action, 
                            records: [record]
                        }));
                    });
                }
            }
        }, me);
        
        batch.start();
        return batch;
    }
}, function() {
    
    
    
    Ext.data.DataProxy = this;
    
    
    
});


Ext.define('Ext.data.proxy.Server', {
    extend: 'Ext.data.proxy.Proxy',
    alias : 'proxy.server',
    alternateClassName: 'Ext.data.ServerProxy',
    uses  : ['Ext.data.Request'],
    
    
    
    
    
    
    
    
    pageParam: 'page',
    
    
    startParam: 'start',

    
    limitParam: 'limit',
    
    
    groupParam: 'group',
    
    
    sortParam: 'sort',
    
    
    filterParam: 'filter',
    
    
    directionParam: 'dir',
    
    
    simpleSortMode: false,
    
    
    noCache : true,
    
    
    cacheString: "_dc",
    
    
    timeout : 30000,
    
    
    
    
    constructor: function(config) {
        var me = this;
        
        config = config || {};
        this.addEvents(
            
            'exception'
        );
        me.callParent([config]);
        
        
        me.extraParams = config.extraParams || {};
        
        me.api = config.api || {};
        
        
        me.nocache = me.noCache;
    },
    
    
    create: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    read: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    update: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    destroy: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    
    buildRequest: function(operation) {
        var params = Ext.applyIf(operation.params || {}, this.extraParams || {}),
            request;
        
        
        params = Ext.applyIf(params, this.getParams(params, operation));
        
        if (operation.id && !params.id) {
            params.id = operation.id;
        }
        
        request = Ext.create('Ext.data.Request', {
            params   : params,
            action   : operation.action,
            records  : operation.records,
            operation: operation,
            url      : operation.url
        });
        
        request.url = this.buildUrl(request);
        
        
        operation.request = request;
        
        return request;
    },
    
    
    processResponse: function(success, operation, request, response, callback, scope){
        var me = this,
            reader,
            result,
            records,
            length,
            mc,
            record,
            i;
            
        if (success === true) {
            reader = me.getReader();
            result = reader.read(me.extractResponseData(response));
            records = result.records;
            length = records.length;
            
            if (result.success !== false) {
                mc = Ext.create('Ext.util.MixedCollection', true, function(r) {return r.getId();});
                mc.addAll(operation.records);
                for (i = 0; i < length; i++) {
                    record = mc.get(records[i].getId());
                    
                    if (record) {
                        record.beginEdit();
                        record.set(record.data);
                        record.endEdit(true);
                    }
                }
                
                
                Ext.apply(operation, {
                    response: response,
                    resultSet: result
                });
                
                operation.setCompleted();
                operation.setSuccessful();
            } else {
                operation.setException(result.message);
                me.fireEvent('exception', this, response, operation);
            }
        } else {
            me.setException(operation, response);
            me.fireEvent('exception', this, response, operation);              
        }
            
        
        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }
            
        me.afterRequest(request, success);
    },
    
    
    setException: function(operation, response){
        operation.setException({
            status: response.status,
            statusText: response.statusText
        });     
    },
    
    
    extractResponseData: function(response){
        return response; 
    },
    
    
    applyEncoding: function(value){
        return Ext.encode(value);
    },
    
    
    encodeSorters: function(sorters) {
        var min = [],
            length = sorters.length,
            i = 0;
        
        for (; i < length; i++) {
            min[i] = {
                property : sorters[i].property,
                direction: sorters[i].direction
            };
        }
        return this.applyEncoding(min);
        
    },
    
    
    encodeFilters: function(filters) {
        var min = [],
            length = filters.length,
            i = 0;
        
        for (; i < length; i++) {
            min[i] = {
                property: filters[i].property,
                value   : filters[i].value
            };
        }
        return this.applyEncoding(min);
    },
    
    
    getParams: function(params, operation) {
        params = params || {};
        
        var me             = this,
            isDef          = Ext.isDefined,
            groupers       = operation.groupers,
            sorters        = operation.sorters,
            filters        = operation.filters,
            page           = operation.page,
            start          = operation.start,
            limit          = operation.limit,
            
            simpleSortMode = me.simpleSortMode,
            
            pageParam      = me.pageParam,
            startParam     = me.startParam,
            limitParam     = me.limitParam,
            groupParam     = me.groupParam,
            sortParam      = me.sortParam,
            filterParam    = me.filterParam,
            directionParam       = me.directionParam;
        
        if (pageParam && isDef(page)) {
            params[pageParam] = page;
        }
        
        if (startParam && isDef(start)) {
            params[startParam] = start;
        }
        
        if (limitParam && isDef(limit)) {
            params[limitParam] = limit;
        }
        
        if (groupParam && groupers && groupers.length > 0) {
            
            params[groupParam] = me.encodeSorters(groupers);
        }
        
        if (sortParam && sorters && sorters.length > 0) {
            if (simpleSortMode) {
                params[sortParam] = sorters[0].property;
                params[directionParam] = sorters[0].direction;
            } else {
                params[sortParam] = me.encodeSorters(sorters);
            }
            
        }
        
        if (filterParam && filters && filters.length > 0) {
            params[filterParam] = me.encodeFilters(filters);
        }
        
        return params;
    },
    
    
    buildUrl: function(request) {
        var me = this,
            url = me.getUrl(request);
        
        if (!url) {
            Ext.Error.raise("You are using a ServerProxy but have not supplied it with a url.");
        }
        
        if (me.noCache) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.cacheString, Ext.Date.now()));
        }
        
        return url;
    },
    
    
    getUrl: function(request){
        return request.url || this.api[request.action] || this.url;
    },
    
    
    doRequest: function(operation, callback, scope) {
        Ext.Error.raise("The doRequest function has not been implemented on your Ext.data.proxy.Server subclass. See src/data/ServerProxy.js for details");
    },
    
    
    afterRequest: Ext.emptyFn,
    
    onDestroy: function() {
        Ext.destroy(this.reader, this.writer);
    }
});


Ext.define('Ext.data.proxy.Ajax', {
    requires: ['Ext.util.MixedCollection', 'Ext.Ajax'],
    extend: 'Ext.data.proxy.Server',
    alias: 'proxy.ajax',
    alternateClassName: ['Ext.data.HttpProxy', 'Ext.data.AjaxProxy'],
    
    
    actionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'POST',
        destroy: 'POST'
    },
    
    
    
    
    doRequest: function(operation, callback, scope) {
        var writer  = this.getWriter(),
            request = this.buildRequest(operation, callback, scope);
            
        if (operation.allowWrite()) {
            request = writer.write(request);
        }
        
        Ext.apply(request, {
            headers       : this.headers,
            timeout       : this.timeout,
            scope         : this,
            callback      : this.createRequestCallback(request, operation, callback, scope),
            method        : this.getMethod(request),
            disableCaching: false 
        });
        
        Ext.Ajax.request(request);
        
        return request;
    },
    
    
    getMethod: function(request) {
        return this.actionMethods[request.action];
    },
    
    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;
        
        return function(options, success, response) {
            me.processResponse(success, operation, request, response, callback, scope);
        };
    }
}, function() {
    
    Ext.data.HttpProxy = this;
});


Ext.define('Ext.data.Model', {
    alternateClassName: 'Ext.data.Record',
    
    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: [
        'Ext.ModelManager',
        'Ext.data.Field',
        'Ext.data.Errors',
        'Ext.data.Operation',
        'Ext.data.validations',
        'Ext.data.proxy.Ajax',
        'Ext.util.MixedCollection'
    ],

    onClassExtended: function(cls, data) {
        var onBeforeClassCreated = data.onBeforeClassCreated;

        data.onBeforeClassCreated = function(cls, data) {
            var me = this,
                name = Ext.getClassName(cls),
                prototype = cls.prototype,
                superCls = cls.prototype.superclass,

                validations = data.validations || [],
                fields = data.fields || [],
                associations = data.associations || [],
                belongsTo = data.belongsTo,
                hasMany = data.hasMany,

                fieldsMixedCollection = new Ext.util.MixedCollection(false, function(field) {
                    return field.name;
                }),

                associationsMixedCollection = new Ext.util.MixedCollection(false, function(association) {
                    return association.name;
                }),

                superValidations = superCls.validations,
                superFields = superCls.fields,
                superAssociations = superCls.associations,

                association, i, ln,
                dependencies = [];

            
            cls.modelName = name;
            prototype.modelName = name;

            
            if (superValidations) {
                validations = superValidations.concat(validations);
            }

            data.validations = validations;

            
            if (superFields) {
                fields = superFields.items.concat(fields);
            }

            for (i = 0, ln = fields.length; i < ln; ++i) {
                fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
            }

            data.fields = fieldsMixedCollection;

            
            
            if (belongsTo) {
                belongsTo = Ext.Array.from(belongsTo);

                for (i = 0, ln = belongsTo.length; i < ln; ++i) {
                    association = belongsTo[i];

                    if (!Ext.isObject(association)) {
                        association = {model: association};
                    }

                    association.type = 'belongsTo';
                    associations.push(association);
                }

                delete data.belongsTo;
            }

            if (hasMany) {
                hasMany = Ext.Array.from(hasMany);
                for (i = 0, ln = hasMany.length; i < ln; ++i) {
                    association = hasMany[i];

                    if (!Ext.isObject(association)) {
                        association = {model: association};
                    }

                    association.type = 'hasMany';
                    associations.push(association);
                }

                delete data.hasMany;
            }

            if (superAssociations) {
                associations = superAssociations.items.concat(associations);
            }

            for (i = 0, ln = associations.length; i < ln; ++i) {
                dependencies.push('association.' + associations[i].type.toLowerCase());
            }

            if (data.proxy) {
                if (typeof data.proxy === 'string') {
                    dependencies.push('proxy.' + data.proxy);
                }
                else if (typeof data.proxy.type === 'string') {
                    dependencies.push('proxy.' + data.proxy.type);
                }
            }

            Ext.require(dependencies, function() {
                Ext.ModelManager.registerType(name, cls);

                for (i = 0, ln = associations.length; i < ln; ++i) {
                    association = associations[i];

                    Ext.apply(association, {
                        ownerModel: name,
                        associatedModel: association.model
                    });

                    if (Ext.ModelManager.getModel(association.model) === undefined) {
                        Ext.ModelManager.registerDeferredAssociation(association);
                    } else {
                        associationsMixedCollection.add(Ext.data.Association.create(association));
                    }
                }

                data.associations = associationsMixedCollection;

                onBeforeClassCreated.call(me, cls, data);

                cls.setProxy(cls.prototype.proxy || cls.prototype.defaultProxyType);

                
                Ext.ModelManager.onModelDefined(cls);
            });
        };
    },

    inheritableStatics: {
        
        setProxy: function(proxy) {
            
            if (!proxy.isProxy) {
                if (typeof proxy == "string") {
                    proxy = {
                        type: proxy
                    };
                }
                proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
            }
            proxy.setModel(this);
            this.proxy = this.prototype.proxy = proxy;

            return proxy;
        },

        
        getProxy: function() {
            return this.proxy;
        },

        
        load: function(id, config) {
            config = Ext.apply({}, config);
            config = Ext.applyIf(config, {
                action: 'read',
                id    : id
            });

            var operation  = Ext.create('Ext.data.Operation', config),
                scope      = config.scope || this,
                record     = null,
                callback;

            callback = function(operation) {
                if (operation.wasSuccessful()) {
                    record = operation.getRecords()[0];
                    Ext.callback(config.success, scope, [record, operation]);
                } else {
                    Ext.callback(config.failure, scope, [record, operation]);
                }
                Ext.callback(config.callback, scope, [record, operation]);
            };

            this.proxy.read(operation, callback, this);
        }
    },

    statics: {
        PREFIX : 'ext-record',
        AUTO_ID: 1,
        EDIT   : 'edit',
        REJECT : 'reject',
        COMMIT : 'commit',

        
        id: function(rec) {
            var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
            rec.phantom = true;
            rec.internalId = id;
            return id;
        }
    },
    
    
    editing : false,

    
    dirty : false,

    
    persistanceProperty: 'data',

    evented: false,
    isModel: true,

    
    phantom : false,

    
    idProperty: 'id',

    
    defaultProxyType: 'ajax',

    

    
    constructor: function(data, id, raw) {
        data = data || {};
        
        var me = this,
            fields,
            length,
            field,
            name,
            i,
            isArray = Ext.isArray(data),
            newData = isArray ? {} : null; 

        
        me.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);
        
        
        me.raw = raw;

        Ext.applyIf(me, {
            data: {}    
        });
        
        
        me.modified = {};

        me[me.persistanceProperty] = {};

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

        
        fields = me.fields.items;
        length = fields.length;

        for (i = 0; i < length; i++) {
            field = fields[i];
            name  = field.name;

            if (isArray){ 
                
                
                newData[name] = data[i];
            }
            else if (data[name] === undefined) {
                data[name] = field.defaultValue;
            }
        }

        me.set(newData || data);
        
        me.dirty = false;
        me.modified = {};

        if (me.getId()) {
            me.phantom = false;
        }

        if (typeof me.init == 'function') {
            me.init();
        }

        me.id = me.modelName + '-' + me.internalId;

        Ext.ModelManager.register(me);
    },
    
    
    get: function(field) {
        return this[this.persistanceProperty][field];
    },
    
    
    set: function(fieldName, value) {
        var me = this,
            fields = me.fields,
            modified = me.modified,
            convertFields = [],
            field, key, i, currentValue;

        
        if (arguments.length == 1 && Ext.isObject(fieldName)) {
            for (key in fieldName) {
                if (fieldName.hasOwnProperty(key)) {
                
                    
                    
                    field = fields.get(key);
                    if (field && field.convert !== field.type.convert) {
                        convertFields.push(key);
                        continue;
                    }
                    
                    me.set(key, fieldName[key]);
                }
            }

            for (i = 0; i < convertFields.length; i++) {
                field = convertFields[i];
                me.set(field, fieldName[field]);
            }

        } else {
            if (fields) {
                field = fields.get(fieldName);

                if (field && field.convert) {
                    value = field.convert(value, me);
                }
            }
            currentValue = me.get(fieldName);
            me[me.persistanceProperty][fieldName] = value;
            
            if (field && field.persist && !me.isEqual(currentValue, value)) {
                me.dirty = true;
                me.modified[fieldName] = currentValue;
            }

            if (!me.editing) {
                me.afterEdit();
            }
        }
    },
    
    
    isEqual: function(a, b){
        if (Ext.isDate(a) && Ext.isDate(b)) {
            return a.getTime() === b.getTime();
        }
        return a === b;
    },
    
    
    beginEdit : function(){
        var me = this;
        if (!me.editing) {
            me.editing = true;
            me.dirtySave = me.dirty;
            me.dataSave = Ext.apply({}, me[me.persistanceProperty]);
            me.modifiedSave = Ext.apply({}, me.modified);
        }
    },
    
    
    cancelEdit : function(){
        var me = this;
        if (me.editing) {
            me.editing = false;
            
            me.modified = me.modifiedSave;
            me[me.persistanceProperty] = me.dataSave;
            me.dirty = me.dirtySave;
            delete me.modifiedSave;
            delete me.dataSave;
            delete me.dirtySave;
        }
    },
    
    
    endEdit : function(silent){
        var me = this;
        if (me.editing) {
            me.editing = false;
            delete me.modifiedSave;
            delete me.dataSave;
            delete me.dirtySave;
            if (silent !== true && me.dirty) {
                me.afterEdit();
            }
        }
    },
    
    
    getChanges : function(){
        var modified = this.modified,
            changes  = {},
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)){
                changes[field] = this.get(field);
            }
        }

        return changes;
    },
    
    
    isModified : function(fieldName) {
        return this.modified.hasOwnProperty(fieldName);
    },
    
    
    setDirty : function() {
        var me = this,
            name;
        
        me.dirty = true;

        me.fields.each(function(field) {
            if (field.persist) {
                name = field.name;
                me.modified[name] = me.get(name);
            }
        }, me);
    },

    markDirty : function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');
        }
        return this.setDirty.apply(this, arguments);
    },
    
    
    reject : function(silent) {
        var me = this,
            modified = me.modified,
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)) {
                if (typeof modified[field] != "function") {
                    me[me.persistanceProperty][field] = modified[field];
                }
            }
        }

        me.dirty = false;
        me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterReject();
        }
    },

    
    commit : function(silent) {
        var me = this;
        
        me.dirty = false;
        me.editing = false;

        me.modified = {};

        if (silent !== true) {
            me.afterCommit();
        }
    },

    
    copy : function(newId) {
        var me = this;
        
        return new me.self(Ext.apply({}, me[me.persistanceProperty]), newId || me.internalId);
    },

    
    setProxy: function(proxy) {
        
        if (!proxy.isProxy) {
            if (typeof proxy === "string") {
                proxy = {
                    type: proxy
                };
            }
            proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
        }
        proxy.setModel(this.self);
        this.proxy = proxy;

        return proxy;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    validate: function() {
        var errors      = Ext.create('Ext.data.Errors'),
            validations = this.validations,
            validators  = Ext.data.validations,
            length, validation, field, valid, type, i;

        if (validations) {
            length = validations.length;

            for (i = 0; i < length; i++) {
                validation = validations[i];
                field = validation.field || validation.name;
                type  = validation.type;
                valid = validators[type](validation, this.get(field));

                if (!valid) {
                    errors.add({
                        field  : field,
                        message: validation.message || validators[type + 'Message']
                    });
                }
            }
        }

        return errors;
    },

    
    isValid: function(){
        return this.validate().isValid();
    },

    
    save: function(options) {
        options = Ext.apply({}, options);

        var me     = this,
            action = me.phantom ? 'create' : 'update',
            record = null,
            scope  = options.scope || me,
            operation,
            callback;

        Ext.apply(options, {
            records: [me],
            action : action
        });

        operation = Ext.create('Ext.data.Operation', options);

        callback = function(operation) {
            if (operation.wasSuccessful()) {
                record = operation.getRecords()[0];
                
                
                me.set(record.data);
                record.dirty = false;

                Ext.callback(options.success, scope, [record, operation]);
            } else {
                Ext.callback(options.failure, scope, [record, operation]);
            }

            Ext.callback(options.callback, scope, [record, operation]);
        };

        me.getProxy()[action](operation, callback, me);

        return me;
    },

    
    destroy: function(options){
        options = Ext.apply({}, options);

        var me     = this,
            record = null,
            scope  = options.scope || me,
            operation,
            callback;

        Ext.apply(options, {
            records: [me],
            action : 'destroy'
        });

        operation = Ext.create('Ext.data.Operation', options);
        callback = function(operation) {
            if (operation.wasSuccessful()) {
                Ext.callback(options.success, scope, [record, operation]);
            } else {
                Ext.callback(options.failure, scope, [record, operation]);
            }
            Ext.callback(options.callback, scope, [record, operation]);
        };

        me.getProxy().destroy(operation, callback, me);
        return me;
    },

    
    getId: function() {
        return this.get(this.idProperty);
    },

    
    setId: function(id) {
        this.set(this.idProperty, id);
    },

    
    join : function(store) {
        
        this.store = store;
    },

    
    unjoin: function() {
        delete this.store;
    },

    
    afterEdit : function() {
        this.callStore('afterEdit');
    },

    
    afterReject : function() {
        this.callStore("afterReject");
    },

    
    afterCommit: function() {
        this.callStore('afterCommit');
    },

    
    callStore: function(fn) {
        var store = this.store;

        if (store !== undefined && typeof store[fn] == "function") {
            store[fn](this);
        }
    },

    
    getAssociatedData: function(){
        return this.prepareAssociatedData(this, [], null);
    },

    
    prepareAssociatedData: function(record, ids, associationType) {
        
        var associations     = record.associations.items,
            associationCount = associations.length,
            associationData  = {},
            associatedStore, associatedName, associatedRecords, associatedRecord,
            associatedRecordCount, association, id, i, j, type, allow;

        for (i = 0; i < associationCount; i++) {
            association = associations[i];
            type = association.type;
            allow = true;
            if (associationType) {
                allow = type == associationType;
            }
            if (allow && type == 'hasMany') {

                
                associatedStore = record[association.storeName];

                
                associationData[association.name] = [];

                
                if (associatedStore && associatedStore.data.length > 0) {
                    associatedRecords = associatedStore.data.items;
                    associatedRecordCount = associatedRecords.length;

                    
                    for (j = 0; j < associatedRecordCount; j++) {
                        associatedRecord = associatedRecords[j];
                        
                        id = associatedRecord.id;

                        
                        
                        if (Ext.Array.indexOf(ids, id) == -1) {
                            ids.push(id);

                            associationData[association.name][j] = associatedRecord.data;
                            Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids, type));
                        }
                    }
                }
            } else if (allow && type == 'belongsTo') {
                associatedRecord = record[association.instanceName];
                if (associatedRecord !== undefined) {
                    id = associatedRecord.id;
                    if (Ext.Array.indexOf(ids, id) == -1) {
                        ids.push(id);
                        associationData[association.name] = associatedRecord.data;
                        Ext.apply(associationData[association.name], this.prepareAssociatedData(associatedRecord, ids, type));
                    }
                }
            }
        }

        return associationData;
    }
});



Ext.define('Ext.Component', {

    

    alias: ['widget.component', 'widget.box'],

    extend: 'Ext.AbstractComponent',

    requires: [
        'Ext.util.DelayedTask'
    ],

    uses: [
        'Ext.Layer',
        'Ext.resizer.Resizer',
        'Ext.util.ComponentDragger'
    ],

    mixins: {
        floating: 'Ext.util.Floating'
    },

    statics: {
        
        DIRECTION_TOP: 'top',
        DIRECTION_RIGHT: 'right',
        DIRECTION_BOTTOM: 'bottom',
        DIRECTION_LEFT: 'left'
    },

    

    

    
    resizeHandles: 'all',

    

    
    floating: false,

    
    toFrontOnShow: true,

    

     

    

    

    hideMode: 'display',
    
    hideParent: false,

    ariaRole: 'presentation',

    bubbleEvents: [],

    actionMode: 'el',
    monPropRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,

    
    
    
    
    
    
    constructor: function(config) {
        config = config || {};
        if (config.initialConfig) {

            
            if (config.isAction) {
                this.baseAction = config;
            }
            config = config.initialConfig;
            
        }
        else if (config.tagName || config.dom || Ext.isString(config)) {
            
            config = {
                applyTo: config,
                id: config.id || config
            };
        }

        this.callParent([config]);

        
        
        if (this.baseAction){
            this.baseAction.addComponent(this);
        }
    },

    initComponent: function() {
        var me = this;

        if (me.listeners) {
            me.on(me.listeners);
            delete me.listeners;
        }
        me.enableBubble(me.bubbleEvents);
        me.mons = [];
    },

    
    afterRender: function() {
        var me = this,
            resizable = me.resizable;

        if (me.floating) {
            me.makeFloating(me.floating);
        } else {
            me.el.setVisibilityMode(Ext.core.Element[me.hideMode.toUpperCase()]);
        }

        if (Ext.isDefined(me.autoScroll)) {
            me.setAutoScroll(me.autoScroll);
        }
        me.callParent();

        if (!(me.x && me.y) && (me.pageX || me.pageY)) {
            me.setPagePosition(me.pageX, me.pageY);
        }

        if (resizable) {
            me.initResizable(resizable);
        }

        if (me.draggable) {
            me.initDraggable();
        }

        me.initAria();
    },

    initAria: function() {
        var actionEl = this.getActionEl(),
            role = this.ariaRole;
        if (role) {
            actionEl.dom.setAttribute('role', role);
        }
    },

    
    setAutoScroll : function(scroll){
        var me = this,
            targetEl;
        scroll = !!scroll;
        if (me.rendered) {
            targetEl = me.getTargetEl();
            targetEl.setStyle('overflow', scroll ? 'auto' : '');
            if (scroll && (Ext.isIE6 || Ext.isIE7)) {
                
                
                targetEl.position();
            }
        }
        me.autoScroll = scroll;
        return me;
    },

    
    makeFloating : function(cfg){
        this.mixins.floating.constructor.call(this, cfg);
    },

    initResizable: function(resizable) {
        resizable = Ext.apply({
            target: this,
            dynamic: false,
            constrainTo: this.constrainTo,
            handles: this.resizeHandles
        }, resizable);
        resizable.target = this;
        this.resizer = Ext.create('Ext.resizer.Resizer', resizable);
    },

    getDragEl: function() {
        return this.el;
    },

    initDraggable: function() {
        var me = this,
            ddConfig = Ext.applyIf({
                el: this.getDragEl(),
                constrainTo: me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.el.dom.parentNode)
            }, this.draggable);

        
        if (me.constrain || me.constrainDelegate) {
            ddConfig.constrain = me.constrain;
            ddConfig.constrainDelegate = me.constrainDelegate;
        }

        this.dd = Ext.create('Ext.util.ComponentDragger', this, ddConfig);
    },

    
    setPosition: function(x, y, animate) {
        var me = this,
            el = me.el,
            to = {},
            adj, adjX, adjY, xIsNumber, yIsNumber;

        if (Ext.isArray(x)) {
            animate = y;
            y = x[1];
            x = x[0];
        }
        me.x = x;
        me.y = y;

        if (!me.rendered) {
            return me;
        }

        adj = me.adjustPosition(x, y);
        adjX = adj.x;
        adjY = adj.y;
        xIsNumber = Ext.isNumber(adjX);
        yIsNumber = Ext.isNumber(adjY);

        if (xIsNumber || yIsNumber) {
            if (animate) {
                if (xIsNumber) {
                    to.left = adjX;
                }
                if (yIsNumber) {
                    to.top = adjY;
                }

                me.stopAnimation();
                me.animate(Ext.apply({
                    duration: 1000,
                    listeners: {
                        afteranimate: Ext.Function.bind(me.afterSetPosition, me, [adjX, adjY])
                    },
                    to: to
                }, animate));
            }
            else {
                if (!xIsNumber) {
                    el.setTop(adjY);
                }
                else if (!yIsNumber) {
                    el.setLeft(adjX);
                }
                else {
                    el.setLeftTop(adjX, adjY);
                }
                me.afterSetPosition(adjX, adjY);
            }
        }
        return me;
    },

    
    afterSetPosition: function(ax, ay) {
        this.onPosition(ax, ay);
        this.fireEvent('move', this, ax, ay);
    },

    showAt: function(x, y, animate) {
        
        if (this.floating) {
            this.setPosition(x, y, animate);
        } else {
            this.setPagePosition(x, y, animate);
        }
        this.show();
    },

    
    setPagePosition: function(x, y, animate) {
        var me = this,
            p;

        if (Ext.isArray(x)) {
            y = x[1];
            x = x[0];
        }
        me.pageX = x;
        me.pageY = y;
        if (me.floating && me.floatParent) {
            
            p = me.floatParent.getTargetEl().getViewRegion();
            if (Ext.isNumber(x) && Ext.isNumber(p.left)) {
                x -= p.left;
            }
            if (Ext.isNumber(y) && Ext.isNumber(p.top)) {
                y -= p.top;
            }
            me.setPosition(x, y, animate);
        }
        else {
            p = me.el.translatePoints(x, y);
            me.setPosition(p.left, p.top, animate);
        }
        return me;
    },

    
    getBox : function(local){
        var pos = this.getPosition(local);
        var s = this.getSize();
        s.x = pos[0];
        s.y = pos[1];
        return s;
    },

    
    updateBox : function(box){
        this.setSize(box.width, box.height);
        this.setPagePosition(box.x, box.y);
        return this;
    },

    
    getOuterSize: function() {
        var el = this.el;
        return {
            width: el.getWidth() + el.getMargin('lr'),
            height: el.getHeight() + el.getMargin('tb')
        };
    },

    
    adjustSize: function(w, h) {
        if (this.autoWidth) {
            w = 'auto';
        }

        if (this.autoHeight) {
            h = 'auto';
        }

        return {
            width: w,
            height: h
        };
    },

    
    adjustPosition: function(x, y) {

        
        if (this.floating && this.floatParent) {
            var o = this.floatParent.getTargetEl().getViewRegion();
            x += o.left;
            y += o.top;
        }

        return {
            x: x,
            y: y
        };
    },

    
    getPosition: function(local) {
        var el = this.el,
            xy;

        if (local === true) {
            return [el.getLeft(true), el.getTop(true)];
        }
        xy = this.xy || el.getXY();

        
        if (this.floating && this.floatParent) {
            var o = this.floatParent.getTargetEl().getViewRegion();
            xy[0] -= o.left;
            xy[1] -= o.top;
        }
        return xy;
    },

    
    getId: function() {
        return this.id || (this.id = (this.getXType() || 'ext-comp') + '-' + this.getAutoId());
    },

    onEnable: function() {
        var actionEl = this.getActionEl();
        actionEl.dom.removeAttribute('aria-disabled');
        actionEl.dom.disabled = false;
        this.callParent();
    },

    onDisable: function() {
        var actionEl = this.getActionEl();
        actionEl.dom.setAttribute('aria-disabled', true);
        actionEl.dom.disabled = true;
        this.callParent();
    },

    
    show: function(animateTarget, cb, scope) {
        if (this.rendered && this.isVisible()) {
            if (this.toFrontOnShow && this.floating) {
                this.toFront();
            }
        } else if (this.fireEvent('beforeshow', this) !== false) {
            this.hidden = false;

            
            if (!this.rendered && (this.autoRender || this.floating)) {
                this.doAutoRender();
            }
            if (this.rendered) {
                this.beforeShow();
                this.onShow.apply(this, arguments);

                
                
                if (this.ownerCt && !this.floating && !(this.ownerCt.suspendLayout || this.ownerCt.layout.layoutBusy)) {
                    this.ownerCt.doLayout();
                }
                this.afterShow.apply(this, arguments);
            }
        }
        return this;
    },

    beforeShow: Ext.emptyFn,

    
    onShow: function() {
        var me = this;

        me.el.show();
        if (this.floating && this.constrain) {
            this.doConstrain();
        }
        me.callParent(arguments);
    },

    afterShow: function(animateTarget, cb, scope) {
        var me = this,
            fromBox,
            toBox,
            ghostPanel;

        
        animateTarget = animateTarget || me.animateTarget;

        
        if (!me.ghost) {
            animateTarget = null;
        }
        
        if (animateTarget) {
            animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
            toBox = me.el.getBox();
            fromBox = animateTarget.getBox();
            fromBox.width += 'px';
            fromBox.height += 'px';
            toBox.width += 'px';
            toBox.height += 'px';
            me.el.addCls(Ext.baseCSSPrefix + 'hide-offsets');
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();

            ghostPanel.el.animate({
                from: fromBox,
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        me.unghost();
                        me.el.removeCls(Ext.baseCSSPrefix + 'hide-offsets');
                        if (me.floating) {
                            me.toFront();
                        }
                        Ext.callback(cb, scope || me);
                    }
                }
            });
        }
        else {
            if (me.floating) {
                me.toFront();
            }
            Ext.callback(cb, scope || me);
        }
        me.fireEvent('show', me);
    },

    
    hide: function() {

        
        
        this.showOnParentShow = false;

        if (!(this.rendered && !this.isVisible()) && this.fireEvent('beforehide', this) !== false) {
            this.hidden = true;
            if (this.rendered) {
                this.onHide.apply(this, arguments);

                
                
                if (this.ownerCt && !this.floating && !(this.ownerCt.suspendLayout || this.ownerCt.layout.layoutBusy)) {
                    this.ownerCt.doLayout();
                }
            }
        }
        return this;
    },

    
    onHide: function(animateTarget, cb, scope) {
        var me = this,
            ghostPanel,
            toBox;

        
        animateTarget = animateTarget || me.animateTarget;

        
        if (!me.ghost) {
            animateTarget = null;
        }
        
        if (animateTarget) {
            animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
            ghostPanel = me.ghost();
            ghostPanel.el.stopAnimation();
            toBox = animateTarget.getBox();
            toBox.width += 'px';
            toBox.height += 'px';
            ghostPanel.el.animate({
                to: toBox,
                listeners: {
                    afteranimate: function() {
                        delete ghostPanel.componentLayout.lastComponentSize;
                        ghostPanel.el.hide();
                        me.afterHide(cb, scope);
                    }
                }
            });
        }
        me.el.hide();
        if (!animateTarget) {
            me.afterHide(cb, scope);
        }
    },

    afterHide: function(cb, scope) {
        Ext.callback(cb, scope || this);
        this.fireEvent('hide', this);
    },

    
    onDestroy: function() {
        var me = this;

        
        if (me.rendered) {
            Ext.destroy(
                me.proxy,
                me.resizer
            );
            
            if (me.actionMode == 'container' || me.removeMode == 'container') {
                me.container.remove();
            }
        }
        delete me.focusTask;
        me.callParent();
    },

    deleteMembers: function() {
        var args = arguments,
            len = args.length,
            i = 0;
        for (; i < len; ++i) {
            delete this[args[i]];
        }
    },

    
    focus: function(selectText, delay) {
        var me = this,
                focusEl;

        if (delay) {
            if (!me.focusTask) {
                me.focusTask = Ext.create('Ext.util.DelayedTask', me.focus);
            }
            me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [selectText, false]);
            return me;
        }

        if (me.rendered && !me.isDestroyed) {
            
            focusEl = me.getFocusEl();
            focusEl.focus();
            if (focusEl.dom && selectText === true) {
                focusEl.dom.select();
            }

            
            
            if (me.floating) {
                me.toFront(true);
            }
        }
        return me;
    },

    
    getFocusEl: function() {
        return this.el;
    },

    
    blur: function() {
        if (this.rendered) {
            this.getFocusEl().blur();
        }
        return this;
    },

    getEl: function() {
        return this.el;
    },

    
    getResizeEl: function() {
        return this.el;
    },

    
    getPositionEl: function() {
        return this.el;
    },

    
    getActionEl: function() {
        return this.el;
    },

    
    getVisibilityEl: function() {
        return this.el;
    },

    
    onResize: Ext.emptyFn,

    
    getBubbleTarget: function() {
        return this.ownerCt;
    },

    
    getContentTarget: function() {
        return this.el;
    },

    
    cloneConfig: function(overrides) {
        overrides = overrides || {};
        var id = overrides.id || Ext.id();
        var cfg = Ext.applyIf(overrides, this.initialConfig);
        cfg.id = id;

        var self = Ext.getClass(this);

        
        return new self(cfg);
    },

    
    getXType: function() {
        return this.self.xtype;
    },

    
    findParentBy: function(fn) {
        var p;

        
        for (p = this.ownerCt; p && !fn(p, this); p = p.ownerCt);
        return p || null;
    },

    
    findParentByType: function(xtype) {
        return Ext.isFunction(xtype) ?
            this.findParentBy(function(p) {
                return p.constructor === xtype;
            })
        :
            this.up(xtype);
    },

    
    bubble: function(fn, scope, args) {
        var p = this;
        while (p) {
            if (fn.apply(scope || p, args || [p]) === false) {
                break;
            }
            p = p.ownerCt;
        }
        return this;
    },

    getProxy: function() {
        if (!this.proxy) {
            this.proxy = this.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', Ext.getBody(), true);
        }
        return this.proxy;
    }

});


Ext.define('Ext.layout.container.Container', {

    

    extend: 'Ext.layout.container.AbstractContainer',
    alternateClassName: 'Ext.layout.ContainerLayout',
    
    

    layoutItem: function(item, box) {
        box = box || {};
        if (item.componentLayout.initialized !== true) {
            this.setItemSize(item, box.width || item.width || undefined, box.height || item.height || undefined);
            
        }
    },

    getLayoutTargetSize : function() {
        var target = this.getTarget(),
            ret;

        if (target) {
            ret = target.getViewSize();

            
            
            
            if (Ext.isIE && ret.width == 0){
                ret = target.getStyleSize();
            }

            ret.width -= target.getPadding('lr');
            ret.height -= target.getPadding('tb');
        }
        return ret;
    },

    beforeLayout: function() {
        if (this.owner.beforeLayout(arguments) !== false) {
            return this.callParent(arguments);
        }
        else {
            return false;
        }
    },

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

    
    getRenderedItems: function() {
        var me = this,
            target = me.getTarget(),
            items = me.getLayoutItems(),
            ln = items.length,
            renderedItems = [],
            i, item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.rendered && me.isValidParent(item, target, i)) {
                renderedItems.push(item);
            }
        }

        return renderedItems;
    },

    
    getVisibleItems: function() {
        var target   = this.getTarget(),
            items = this.getLayoutItems(),
            ln = items.length,
            visibleItems = [],
            i, item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.rendered && this.isValidParent(item, target, i) && item.hidden !== true) {
                visibleItems.push(item);
            }
        }

        return visibleItems;
    }
});


Ext.define('Ext.layout.container.Auto', {

    

    alias: ['layout.auto', 'layout.autocontainer'],

    extend: 'Ext.layout.container.Container',

    

    type: 'autocontainer',

    fixedLayout: false,

    bindToOwnerCtComponent: true,

    
    onLayout : function(owner, target) {
        var me = this,
            items = me.getLayoutItems(),
            ln = items.length,
            i;

        
        if (ln) {
            
            
            
            if (!me.clearEl) {
                me.clearEl = me.getRenderTarget().createChild({
                    cls: Ext.baseCSSPrefix + 'clear',
                    role: 'presentation'
                });
            }

            
            for (i = 0; i < ln; i++) {
                me.setItemSize(items[i]);
            }
        }
    }
});

Ext.define('Ext.container.AbstractContainer', {

    

    extend: 'Ext.Component',

    requires: [
        'Ext.util.MixedCollection',
        'Ext.layout.container.Auto',
        'Ext.ZIndexManager'
    ],

    
    

    
    
    

    
    suspendLayout : false,

    
    autoDestroy : true,

     
    defaultType: 'panel',

    isContainer : true,

    baseCls: Ext.baseCSSPrefix + 'container',

    
    bubbleEvents: ['add', 'remove'],
    
    
    initComponent : function(){
        var me = this;
        me.addEvents(
            
            'afterlayout',
            
            'beforeadd',
            
            'beforeremove',
            
            'add',
            
            'remove',
            
            'beforecardswitch',
            
            'cardswitch'
        );

        
        me.layoutOnShow = Ext.create('Ext.util.MixedCollection');
        me.callParent();
        me.initItems();
    },

    
    initItems : function() {
        var me = this,
            items = me.items;

        
        me.items = Ext.create('Ext.util.MixedCollection', false, me.getComponentId);

        if (items) {
            if (!Ext.isArray(items)) {
                items = [items];
            }

            me.add(items);
        }
    },

    
    afterRender : function() {
        this.getLayout();
        this.callParent();
    },

    
    setLayout : function(layout) {
        var currentLayout = this.layout;

        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null);
        }

        this.layout = layout;
        layout.setOwner(this);
    },

    
    getLayout : function() {
        var me = this;
        if (!me.layout || !me.layout.isLayout) {
            me.setLayout(Ext.layout.Layout.create(me.layout, 'autocontainer'));
        }

        return me.layout;
    },

    
    doLayout : function() {
        var me = this,
            layout = me.getLayout();

        if (me.rendered && layout && !me.suspendLayout) {
            
            if ((!Ext.isNumber(me.width) || !Ext.isNumber(me.height)) && me.componentLayout.type !== 'autocomponent') {
                
                if (me.componentLayout.layoutBusy !== true) {
                    me.doComponentLayout();
                    if (me.componentLayout.layoutCancelled === true) {
                        layout.layout();
                    }
                }
            }
            
            else {
                
                if (layout.layoutBusy !== true) {
                    layout.layout();
                }
            }
        }

        return me;
    },

    
    afterLayout : function(layout) {
        this.fireEvent('afterlayout', this, layout);
    },

    
    prepareItems : function(items, applyDefaults) {
        if (!Ext.isArray(items)) {
            items = [items];
        }

        
        var i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (applyDefaults) {
                item = this.applyDefaults(item);
            }
            items[i] = this.lookupComponent(item);
        }
        return items;
    },

    
    applyDefaults : function(config) {
        var defaults = this.defaults;

        if (defaults) {
            if (Ext.isFunction(defaults)) {
                defaults = defaults.call(this, config);
            }

            if (Ext.isString(config)) {
                config = Ext.ComponentManager.get(config);
                Ext.applyIf(config, defaults);
            } else if (!config.isComponent) {
                Ext.applyIf(config, defaults);
            } else {
                Ext.applyIf(config, defaults);
            }
        }

        return config;
    },

    
    lookupComponent : function(comp) {
        return Ext.isString(comp) ? Ext.ComponentManager.get(comp) : this.createComponent(comp);
    },

    
    createComponent : function(config, defaultType) {
        
        
        
        
        
        

        return Ext.ComponentManager.create(config, defaultType || this.defaultType);
    },

    
    getComponentId : function(comp) {
        return comp.getItemId();
    },

    
    add : function() {
        var me = this,
            args = Array.prototype.slice.call(arguments),
            hasMultipleArgs,
            items,
            results = [],
            i,
            ln,
            item,
            index = -1,
            cmp;

        if (typeof args[0] == 'number') {
            index = args.shift();
        }

        hasMultipleArgs = args.length > 1;
        if (hasMultipleArgs || Ext.isArray(args[0])) {

            items = hasMultipleArgs ? args : args[0];
            
            me.suspendLayout = true;
            for (i = 0, ln = items.length; i < ln; i++) {
                item = items[i];
                
                if (!item) {
                    Ext.Error.raise("Trying to add a null item as a child of Container with itemId/id: " + me.getItemId());
                }
                
                if (index != -1) {
                    item = me.add(index + i, item);
                } else {
                    item = me.add(item);
                }
                results.push(item);
            }
            
            me.suspendLayout = false;
            me.doLayout();
            return results;
        }

        cmp = me.prepareItems(args[0], true)[0];

        
        
        
        if (cmp.floating) {
            cmp.onAdded(me, index);
        } else {
            index = (index !== -1) ? index : me.items.length;
            if (me.fireEvent('beforeadd', me, cmp, index) !== false && me.onBeforeAdd(cmp) !== false) {
                me.items.insert(index, cmp);
                cmp.onAdded(me, index);
                me.onAdd(cmp, index);
                me.fireEvent('add', me, cmp, index);
            }
            me.doLayout();
        }
        return cmp;
    },

    
    registerFloatingItem: function(cmp) {
        var me = this;
        if (!me.floatingItems) {
            me.floatingItems = Ext.create('Ext.ZIndexManager', me);
        }
        me.floatingItems.register(cmp);
    },

    onAdd : Ext.emptyFn,
    onRemove : Ext.emptyFn,

    
    insert : function(index, comp) {
        return this.add(index, comp);
    },

    
    move : function(fromIdx, toIdx) {
        var items = this.items,
            item;
        item = items.removeAt(fromIdx);
        if (item === false) {
            return false;
        }
        items.insert(toIdx, item);
        this.doLayout();
        return item;
    },

    
    onBeforeAdd : function(item) {
        var me = this;
        
        if (item.ownerCt) {
            item.ownerCt.remove(item, false);
        }

        if (me.border === false || me.border === 0) {
            item.border = (item.border === true);
        }
    },

    
    remove : function(comp, autoDestroy) {
        var me = this,
            c = me.getComponent(comp);
            if (Ext.isDefined(Ext.global.console) && !c) {
                console.warn("Attempted to remove a component that does not exist. Ext.container.Container: remove takes an argument of the component to remove. cmp.remove() is incorrect usage.");
            }

        if (c && me.fireEvent('beforeremove', me, c) !== false) {
            me.doRemove(c, autoDestroy);
            me.fireEvent('remove', me, c);
        }

        return c;
    },

    
    doRemove : function(component, autoDestroy) {
        var me = this,
            layout = me.layout,
            hasLayout = layout && me.rendered;

        me.items.remove(component);
        component.onRemoved();

        if (hasLayout) {
            layout.onRemove(component);
        }

        me.onRemove(component, autoDestroy);

        if (autoDestroy === true || (autoDestroy !== false && me.autoDestroy)) {
            component.destroy();
        }

        if (hasLayout && !autoDestroy) {
            layout.afterRemove(component);
        }

        if (!me.destroying) {
            me.doLayout();
        }
    },

    
    removeAll : function(autoDestroy) {
        var me = this,
            removeItems = me.items.items.slice(),
            items = [],
            i = 0,
            len = removeItems.length,
            item;

        
        me.suspendLayout = true;
        for (; i < len; i++) {
            item = removeItems[i];
            me.remove(item, autoDestroy);

            if (item.ownerCt !== me) {
                items.push(item);
            }
        }

        
        me.suspendLayout = false;
        me.doLayout();
        return items;
    },

    
    
    
    
    
    
    
    
    getRefItems : function(deep) {
        var me = this,
            items = me.items.items,
            len = items.length,
            i = 0,
            item,
            result = [];

        for (; i < len; i++) {
            item = items[i];
            result.push(item);
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }

        
        
        if (me.floatingItems && me.floatingItems.accessList) {
            result.push.apply(result, me.floatingItems.accessList);
        }

        return result;
    },

    
    cascade : function(fn, scope, origArgs){
        var me = this,
            cs = me.items ? me.items.items : [],
            len = cs.length,
            i = 0,
            c,
            args = origArgs ? origArgs.concat(me) : [me],
            componentIndex = args.length - 1;

        if (fn.apply(scope || me, args) !== false) {
            for(; i < len; i++){
                c = cs[i];
                if (c.cascade) {
                    c.cascade(fn, scope, origArgs);
                } else {
                    args[componentIndex] = c;
                    fn.apply(scope || cs, args);
                }
            }
        }
        return this;
    },

    
    getComponent : function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }

        return this.items.get(comp);
    },

    
    query : function(selector) {
        return Ext.ComponentQuery.query(selector, this);
    },

    
    child : function(selector) {
        return this.query('> ' + selector)[0] || null;
    },

    
    down : function(selector) {
        return this.query(selector)[0] || null;
    },

    
    show : function() {
        this.callParent(arguments);
        this.performDeferredLayouts();
        return this;
    },

    
    
    performDeferredLayouts: function() {
        var layoutCollection = this.layoutOnShow,
            ln = layoutCollection.getCount(),
            i = 0,
            needsLayout,
            item;

        for (; i < ln; i++) {
            item = layoutCollection.get(i);
            needsLayout = item.needsLayout;

            if (Ext.isObject(needsLayout)) {
                item.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize, needsLayout.ownerCt);
            }
        }
        layoutCollection.clear();
    },    
    
    //@private

    
    onEnable: function() {
        Ext.Array.each(this.query('[isFormField]'), function(item) {
            if (item.resetDisable) {
                item.enable();
                delete item.resetDisable;             
            }
        });
        this.callParent();
    },
    
    
    
    onDisable: function() {
        Ext.Array.each(this.query('[isFormField]'), function(item) {
            if (item.resetDisable !== false && !item.disabled) {
                item.disable();
                item.resetDisable = true;
            }
        });
        this.callParent();
    },

    
    beforeLayout: function() {
        return true;
    },

    
    beforeDestroy : function() {
        var me = this,
            items = me.items,
            c;

        if (items) {
            while ((c = items.first())) {
                me.doRemove(c, true);
            }
        }

        Ext.destroy(
            me.layout,
            me.floatingItems
        );
        me.callParent();
    }
});

Ext.define('Ext.container.Container', {
    extend: 'Ext.container.AbstractContainer',
    alias: 'widget.container',
    alternateClassName: 'Ext.Container',

    
    getChildByElement: function(el) {
        var item,
            itemEl,
            i = 0,
            it = this.items.items,
            ln = it.length;

        el = Ext.getDom(el);
        for (; i < ln; i++) {
            item = it[i];
            itemEl = item.getEl();
            if ((itemEl.dom === el) || itemEl.contains(el)) {
                return item;
            }
        }
        return null;
    }
});


Ext.define('Ext.toolbar.Fill', {
    extend: 'Ext.Component',
    alias: 'widget.tbfill',
    alternateClassName: 'Ext.Toolbar.Fill',
    isFill : true,
    flex: 1
});

Ext.define('Ext.toolbar.Item', {
    extend: 'Ext.Component',
    alias: 'widget.tbitem',
    alternateClassName: 'Ext.Toolbar.Item',
    enable:Ext.emptyFn,
    disable:Ext.emptyFn,
    focus:Ext.emptyFn
    
});

Ext.define('Ext.toolbar.Separator', {
    extend: 'Ext.toolbar.Item',
    alias: 'widget.tbseparator',
    alternateClassName: 'Ext.Toolbar.Separator',
    baseCls: Ext.baseCSSPrefix + 'toolbar-separator',
    focusable: false
});

Ext.define('Ext.menu.Manager', {
    singleton: true,
    requires: [
        'Ext.util.MixedCollection',
        'Ext.util.KeyMap'
    ],
    alternateClassName: 'Ext.menu.MenuMgr',

    uses: ['Ext.menu.Menu'],

    menus: {},
    groups: {},
    attached: false,
    lastShow: new Date(),

    init: function() {
        var me = this;
        
        me.active = Ext.create('Ext.util.MixedCollection');
        Ext.getDoc().addKeyListener(27, function() {
            if (me.active.length > 0) {
                me.hideAll();
            }
        }, me);
    },

    
    hideAll: function() {
        var active = this.active,
            c;
        if (active && active.length > 0) {
            c = active.clone();
            c.each(function(m) {
                m.hide();
            });
            return true;
        }
        return false;
    },

    onHide: function(m) {
        var me = this,
            active = me.active;
        active.remove(m);
        if (active.length < 1) {
            Ext.getDoc().un('mousedown', me.onMouseDown, me);
            me.attached = false;
        }
    },

    onShow: function(m) {
        var me = this,
            active   = me.active,
            last     = active.last(),
            attached = me.attached,
            menuEl   = m.getEl(),
            zIndex;

        me.lastShow = new Date();
        active.add(m);
        if (!attached) {
            Ext.getDoc().on('mousedown', me.onMouseDown, me);
            me.attached = true;
        }
        m.toFront();
    },

    onBeforeHide: function(m) {
        if (m.activeChild) {
            m.activeChild.hide();
        }
        if (m.autoHideTimer) {
            clearTimeout(m.autoHideTimer);
            delete m.autoHideTimer;
        }
    },

    onBeforeShow: function(m) {
        var active = this.active,
            parentMenu = m.parentMenu;
            
        active.remove(m);
        if (!parentMenu && !m.allowOtherMenus) {
            this.hideAll();
        }
        else if (parentMenu && parentMenu.activeChild && m != parentMenu.activeChild) {
            parentMenu.activeChild.hide();
        }
    },

    
    onMouseDown: function(e) {
        var me = this,
            active = me.active,
            lastShow = me.lastShow;

        if (Ext.Date.getElapsed(lastShow) > 50 && active.length > 0 && !e.getTarget('.' + Ext.baseCSSPrefix + 'menu')) {
            me.hideAll();
        }
    },

    
    register: function(menu) {
        var me = this;

        if (!me.active) {
            me.init();
        }

        if (menu.floating) {
            me.menus[menu.id] = menu;
            menu.on({
                beforehide: me.onBeforeHide,
                hide: me.onHide,
                beforeshow: me.onBeforeShow,
                show: me.onShow,
                scope: me
            });
        }
    },

    
    get: function(menu) {
        var menus = this.menus;
        
        if (typeof menu == 'string') { 
            if (!menus) {  
                return null;
            }
            return menus[menu];
        } else if (menu.isMenu) {  
            return menu;
        } else if (Ext.isArray(menu)) { 
            return Ext.create('Ext.menu.Menu', {items:menu});
        } else { 
            return Ext.ComponentManager.create(menu, 'menu');
        }
    },

    
    unregister: function(menu) {
        var me = this,
            menus = me.menus,
            active = me.active;

        delete menus[menu.id];
        active.remove(menu);
        menu.un({
            beforehide: me.onBeforeHide,
            hide: me.onHide,
            beforeshow: me.onBeforeShow,
            show: me.onShow,
            scope: me
        });
    },

    
    registerCheckable: function(menuItem) {
        var groups  = this.groups,
            groupId = menuItem.group;

        if (groupId) {
            if (!groups[groupId]) {
                groups[groupId] = [];
            }

            groups[groupId].push(menuItem);
        }
    },

    
    unregisterCheckable: function(menuItem) {
        var groups  = this.groups,
            groupId = menuItem.group;

        if (groupId) {
            Ext.Array.remove(groups[groupId], menuItem);
        }
    },

    onCheckChange: function(menuItem, state) {
        var groups  = this.groups,
            groupId = menuItem.group,
            i       = 0,
            group, ln, curr;

        if (groupId && state) {
            group = groups[groupId];
            ln = group.length;
            for (; i < ln; i++) {
                curr = group[i];
                if (curr != menuItem) {
                    curr.setChecked(false);
                }
            }
        }
    }
});

Ext.define('Ext.button.Button', {

    
    alias: 'widget.button',
    extend: 'Ext.Component',

    requires: [
        'Ext.menu.Manager',
        'Ext.util.ClickRepeater',
        'Ext.layout.component.Button',
        'Ext.util.TextMetrics',
        'Ext.util.KeyMap'
    ],

    alternateClassName: 'Ext.Button',
    

    isButton: true,
    componentLayout: 'button',

    
    hidden: false,

    
    disabled: false,

    
    pressed: false,

    

    

    

    

    

    

    

    

    

    

    

    

    
    enableToggle: false,

    

    

    
    menuAlign: 'tl-bl?',

    

    

    
    type: 'button',

    
    clickEvent: 'click',
    
    
    preventDefault: true,

    
    handleMouseEvents: true,

    
    tooltipType: 'qtip',

    
    baseCls: Ext.baseCSSPrefix + 'btn',

    
    pressedCls: 'pressed',
    
    
    overCls: 'over',
    
    
    focusCls: 'focus',
    
    
    menuActiveCls: 'menu-active',
    
    
    
    

    ariaRole: 'button',

    
    renderTpl:
        '<em class="{splitCls}">' +
            '<tpl if="href">' +
                '<a href="{href}" target="{target}"<tpl if="tabIndex"> tabIndex="{tabIndex}"</tpl> role="link">' +
                    '<span class="{baseCls}-inner">{text}</span>' +
                '</a>' +
            '</tpl>' +
            '<tpl if="!href">' +
                '<button type="{type}" hidefocus="true"' +
                    
                    
                    '<tpl if="tabIndex"> tabIndex="{tabIndex}"</tpl> role="button" autocomplete="off">' +
                    '<span class="{baseCls}-inner" style="{innerSpanStyle}">{text}</span>' +
                '</button>' +
            '</tpl>' +
        '</em>' ,

    
    scale: 'small',
    
    
    allowedScales: ['small', 'medium', 'large'],
    
    

    
    iconAlign: 'left',

    
    arrowAlign: 'right',

    
    arrowCls: 'arrow',

    

    

    

    
     
    maskOnDisable: false,

    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);

        me.addEvents(
            
            'click',

            
            'toggle',

            
            'mouseover',

            
            'mouseout',

            
            'menushow',

            
            'menuhide',

            
            'menutriggerover',

            
            'menutriggerout'
        );

        if (me.menu) {
            
            me.split = true;

            
            me.menu = Ext.menu.Manager.get(me.menu);
            me.menu.ownerCt = me;
        }

        
        if (me.url) {
            me.href = me.url;
        }

        
        if (me.href && !me.hasOwnProperty('preventDefault')) {
            me.preventDefault = false;
        }

        if (Ext.isString(me.toggleGroup)) {
            me.enableToggle = true;
        }

    },

    
    initAria: function() {
        this.callParent();
        var actionEl = this.getActionEl();
        if (this.menu) {
            actionEl.dom.setAttribute('aria-haspopup', true);
        }
    },

    
    getActionEl: function() {
        return this.btnEl;
    },

    
    getFocusEl: function() {
        return this.btnEl;
    },

    
    setButtonCls: function() {
        var me = this,
            el = me.el,
            cls = [];

        if (me.useSetClass) {
            if (!Ext.isEmpty(me.oldCls)) {
                me.removeClsWithUI(me.oldCls);
                me.removeClsWithUI(me.pressedCls);
            }
            
            
            if (me.iconCls || me.icon) {
                if (me.text) {
                    cls.push('icon-text-' + me.iconAlign);
                } else {
                    cls.push('icon');
                }
            } else if (me.text) {
                cls.push('noicon');
            }
            
            me.oldCls = cls;
            me.addClsWithUI(cls);
            me.addClsWithUI(me.pressed ? me.pressedCls : null);
        }
    },
    
    
    onRender: function(ct, position) {
        
        var me = this,
            repeater, btn;
            
        
        Ext.applyIf(me.renderData, me.getTemplateArgs());

        
        Ext.applyIf(me.renderSelectors, {
            btnEl  : me.href ? 'a' : 'button',
            btnWrap: 'em',
            btnInnerEl: '.' + me.baseCls + '-inner'
        });
        
        if (me.scale) {
            me.ui = me.ui + '-' + me.scale;
        }

        
        me.callParent(arguments);

        
        if (me.split && me.arrowTooltip) {
            me.arrowEl.dom[me.tooltipType] = me.arrowTooltip;
        }

        
        me.mon(me.btnEl, {
            scope: me,
            focus: me.onFocus,
            blur : me.onBlur
        });

        
        btn = me.el;

        if (me.icon) {
            me.setIcon(me.icon);
        }

        if (me.iconCls) {
            me.setIconCls(me.iconCls);
        }

        if (me.tooltip) {
            me.setTooltip(me.tooltip, true);
        }

        
        if (me.handleMouseEvents) {
            me.mon(btn, {
                scope: me,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousedown: me.onMouseDown
            });

            if (me.split) {
                me.mon(btn, {
                    mousemove: me.onMouseMove,
                    scope: me
                });
            }
        }

        
        if (me.menu) {
            me.mon(me.menu, {
                scope: me,
                show: me.onMenuShow,
                hide: me.onMenuHide
            });

            me.keyMap = Ext.create('Ext.util.KeyMap', me.el, {
                key: Ext.EventObject.DOWN,
                handler: me.onDownKey,
                scope: me
            });
        }

        
        if (me.repeat) {
            repeater = Ext.create('Ext.util.ClickRepeater', btn, Ext.isObject(me.repeat) ? me.repeat: {});
            me.mon(repeater, 'click', me.onRepeatClick, me);
        } else {
            me.mon(btn, me.clickEvent, me.onClick, me);
        }

        
        Ext.ButtonToggleManager.register(me);
    },

    
    getTemplateArgs: function() {
        var me = this,
            persistentPadding = me.getPersistentBtnPadding(),
            innerSpanStyle = '';

        
        if (Math.max.apply(Math, persistentPadding) > 0) {
            innerSpanStyle = 'margin:' + Ext.Array.map(persistentPadding, function(pad) {
                return -pad + 'px';
            }).join(' ');
        }

        return {
            href     : me.getHref(),
            target   : me.target || '_blank',
            type     : me.type,
            splitCls : me.getSplitCls(),
            cls      : me.cls,
            text     : me.text || '&#160;',
            tabIndex : me.tabIndex,
            innerSpanStyle: innerSpanStyle
        };
    },

    
    getHref: function() {
        var me = this,
            params = Ext.apply({}, me.baseParams);
            
        
        params = Ext.apply(params, me.params);
        return me.href ? Ext.urlAppend(me.href, Ext.Object.toQueryString(params)) : false;
    },

    
    setParams: function(params) {
        this.params = params;
        this.btnEl.dom.href = this.getHref();
    },

    getSplitCls: function() {
        var me = this;
        return me.split ? (me.baseCls + '-' + me.arrowCls) + ' ' + (me.baseCls + '-' + me.arrowCls + '-' + me.arrowAlign) : '';
    },

    
    afterRender: function() {
        var me = this;
        me.useSetClass = true;
        me.setButtonCls();
        me.doc = Ext.getDoc();
        this.callParent(arguments);
    },

    
    setIconCls: function(cls) {
        var me = this,
            btnInnerEl = me.btnInnerEl;
        if (btnInnerEl) {
            
            btnInnerEl.removeCls(me.iconCls);
            btnInnerEl.addCls(cls || '');
            me.setButtonCls();
        }
        me.iconCls = cls;
        return me;
    },

    
    setTooltip: function(tooltip, initial) {
        var me = this;

        if (me.rendered) {
            if (!initial) {
                me.clearTip();
            }
            if (Ext.isObject(tooltip)) {
                Ext.tip.QuickTipManager.register(Ext.apply({
                    target: me.btnEl.id
                },
                tooltip));
                me.tooltip = tooltip;
            } else {
                me.btnEl.dom.setAttribute('data-' + this.tooltipType, tooltip);
            }
        } else {
            me.tooltip = tooltip;
        }
        return me;
    },

    
    getRefItems: function(deep){
        var menu = this.menu,
            items;

        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }
        return items || [];
    },

    
    clearTip: function() {
        if (Ext.isObject(this.tooltip)) {
            Ext.tip.QuickTipManager.unregister(this.btnEl);
        }
    },

    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.clearTip();
        }
        if (me.menu && me.destroyMenu !== false) {
            Ext.destroy(me.btnEl, me.btnInnerEl, me.menu);
        }
        Ext.destroy(me.repeater);
    },

    
    onDestroy: function() {
        var me = this;
        if (me.rendered) {
            me.doc.un('mouseover', me.monitorMouseOver, me);
            me.doc.un('mouseup', me.onMouseUp, me);
            delete me.doc;
            delete me.btnEl;
            delete me.btnInnerEl;
            Ext.ButtonToggleManager.unregister(me);
            
            Ext.destroy(me.keyMap);
            delete me.keyMap;
        }
        me.callParent();
    },

    
    setHandler: function(handler, scope) {
        this.handler = handler;
        this.scope = scope;
        return this;
    },

    
    setText: function(text) {
        var me = this;
        me.text = text;
        if (me.el) {
            me.btnInnerEl.update(text || '&#160;');
            me.setButtonCls();
        }
        me.doComponentLayout();
        return me;
    },

    
    setIcon: function(icon) {
        var me = this,
            btnInnerEl = me.btnInnerEl;
        me.icon = icon;
        if (btnInnerEl) {
            btnInnerEl.setStyle('background-image', icon ? 'url(' + icon + ')': '');
            me.setButtonCls();
        }
        return me;
    },

    
    getText: function() {
        return this.text;
    },

    
    toggle: function(state, suppressEvent) {
        var me = this;
        state = state === undefined ? !me.pressed: !!state;
        if (state !== me.pressed) {
            if (me.rendered) {
                me[state ? 'addClsWithUI': 'removeClsWithUI'](me.pressedCls);
            }
            me.btnEl.dom.setAttribute('aria-pressed', state);
            me.pressed = state;
            if (!suppressEvent) {
                me.fireEvent('toggle', me, state);
                Ext.callback(me.toggleHandler, me.scope || me, [me, state]);
            }
        }
        return me;
    },

    
    showMenu: function() {
        var me = this;
        if (me.rendered && me.menu) {
            if (me.tooltip) {
                Ext.tip.QuickTipManager.getQuickTip().cancelShow(me.btnEl);
            }
            if (me.menu.isVisible()) {
                me.menu.hide();
            }

            me.menu.showBy(me.el, me.menuAlign);
        }
        return me;
    },

    
    hideMenu: function() {
        if (this.hasVisibleMenu()) {
            this.menu.hide();
        }
        return this;
    },

    
    hasVisibleMenu: function() {
        var menu = this.menu;
        return menu && menu.rendered && menu.isVisible();
    },

    
    onRepeatClick: function(repeat, e) {
        this.onClick(e);
    },

    
    onClick: function(e) {
        var me = this;
        if (me.preventDefault || (me.disabled && me.getHref()) && e) {
            e.preventDefault();
        }
        if (e.button !== 0) {
            return;
        }
        if (!me.disabled) {
            if (me.enableToggle && (me.allowDepress !== false || !me.pressed)) {
                me.toggle();
            }
            if (me.menu && !me.hasVisibleMenu() && !me.ignoreNextClick) {
                me.showMenu();
            }
            me.fireEvent('click', me, e);
            if (me.handler) {
                me.handler.call(me.scope || me, me, e);
            }
            me.onBlur();
        }
    },

    
    onMouseOver: function(e) {
        var me = this;
        if (!me.disabled && !e.within(me.el, true, true)) {
            me.onMouseEnter(e);
        }
    },

    
    onMouseOut: function(e) {
        var me = this;
        if (!e.within(me.el, true, true)) {
            if (me.overMenuTrigger) {
                me.onMenuTriggerOut(e);
            }
            me.onMouseLeave(e);
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            el = me.el,
            over = me.overMenuTrigger,
            overlap, btnSize;

        if (me.split) {
            if (me.arrowAlign === 'right') {
                overlap = e.getX() - el.getX();
                btnSize = el.getWidth();
            } else {
                overlap = e.getY() - el.getY();
                btnSize = el.getHeight();
            }

            if (overlap > (btnSize - me.getTriggerSize())) {
                if (!over) {
                    me.onMenuTriggerOver(e);
                }
            } else {
                if (over) {
                    me.onMenuTriggerOut(e);
                }
            }
        }
    },

    
    getTriggerSize: function() {
        var me = this,
            size = me.triggerSize,
            side, sideFirstLetter, undef;
            
        if (size === undef) {
            side = me.arrowAlign;
            sideFirstLetter = side.charAt(0);
            size = me.triggerSize = me.el.getFrameWidth(sideFirstLetter) + me.btnWrap.getFrameWidth(sideFirstLetter) + (me.frameSize && me.frameSize[side] || 0);
        }
        return size;
    },

    
    onMouseEnter: function(e) {
        var me = this;
        me.addClsWithUI(me.overCls);
        me.fireEvent('mouseover', me, e);
    },

    
    onMouseLeave: function(e) {
        var me = this;
        me.removeClsWithUI(me.overCls);
        me.fireEvent('mouseout', me, e);
    },

    
    onMenuTriggerOver: function(e) {
        var me = this;
        me.overMenuTrigger = true;
        me.fireEvent('menutriggerover', me, me.menu, e);
    },

    
    onMenuTriggerOut: function(e) {
        var me = this;
        delete me.overMenuTrigger;
        me.fireEvent('menutriggerout', me, me.menu, e);
    },
    
    
    enable : function(silent) {
        var me = this;

        me.callParent(arguments);
        
        me.removeClsWithUI('disabled');

        return me;
    },

    
    disable : function(silent) {
        var me = this;
        
        me.callParent(arguments);
        
        me.addClsWithUI('disabled');

        return me;
    },
    
    
    setScale: function(scale) {
        var me = this,
            ui = me.ui.replace('-' + me.scale, '');
        
        
        if (!Ext.Array.contains(me.allowedScales, scale)) {
            throw('#setScale: scale must be an allowed scale (' + me.allowedScales.join(', ') + ')');
        }
        
        me.scale = scale;
        me.setUI(ui);
    },
    
    
    setUI: function(ui) {
        var me = this;
        
        
        if (me.scale && !ui.match(me.scale)) {
            ui = ui + '-' + me.scale;
        }
        
        me.callParent([ui]);
        
        
        
    },
    
    
    onFocus: function(e) {
        var me = this;
        if (!me.disabled) {
            me.addClsWithUI(me.focusCls);
        }
    },

    
    onBlur: function(e) {
        var me = this;
        me.removeClsWithUI(me.focusCls);
    },

    
    onMouseDown: function(e) {
        var me = this;
        if (!me.disabled && e.button === 0) {
            me.addClsWithUI(me.pressedCls);
            me.doc.on('mouseup', me.onMouseUp, me);
        }
    },
    
    onMouseUp: function(e) {
        var me = this;
        if (e.button === 0) {
            if (!me.pressed) {
                me.removeClsWithUI(me.pressedCls);
            }
            me.doc.un('mouseup', me.onMouseUp, me);
        }
    },
    
    onMenuShow: function(e) {
        var me = this;
        me.ignoreNextClick = 0;
        me.addClsWithUI(me.menuActiveCls);
        me.fireEvent('menushow', me, me.menu);
    },

    
    onMenuHide: function(e) {
        var me = this;
        me.removeClsWithUI(me.menuActiveCls);
        me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me);
        me.fireEvent('menuhide', me, me.menu);
    },

    
    restoreClick: function() {
        this.ignoreNextClick = 0;
    },

    
    onDownKey: function() {
        var me = this;

        if (!me.disabled) {
            if (me.menu) {
                me.showMenu();
            }
        }
    },

    
    getPersistentBtnPadding: function() {
        var cls = Ext.button.Button,
            padding = cls.persistentPadding,
            btn, leftTop, btnEl, btnInnerEl;

        if (!padding) {
            padding = cls.persistentPadding = [0, 0, 0, 0]; 

            if (!Ext.isIE) { 
                
                btn = Ext.create('Ext.button.Button', {
                    renderTo: Ext.getBody(),
                    text: 'test',
                    style: 'position:absolute;top:-999px;'
                });
                btnEl = btn.btnEl;
                btnInnerEl = btn.btnInnerEl;
                btnEl.setSize(null, null); 

                leftTop = btnInnerEl.getOffsetsTo(btnEl);
                padding[0] = leftTop[1];
                padding[1] = btnEl.getWidth() - btnInnerEl.getWidth() - leftTop[0];
                padding[2] = btnEl.getHeight() - btnInnerEl.getHeight() - leftTop[1];
                padding[3] = leftTop[0];

                btn.destroy();
            }
        }

        return padding;
    }

}, function() {
    var groups = {},
        g, i, l;

    function toggleGroup(btn, state) {
        if (state) {
            g = groups[btn.toggleGroup];
            for (i = 0, l = g.length; i < l; i++) {
                if (g[i] !== btn) {
                    g[i].toggle(false);
                }
            }
        }
    }
    
    Ext.ButtonToggleManager = {
        register: function(btn) {
            if (!btn.toggleGroup) {
                return;
            }
            var group = groups[btn.toggleGroup];
            if (!group) {
                group = groups[btn.toggleGroup] = [];
            }
            group.push(btn);
            btn.on('toggle', toggleGroup);
        },

        unregister: function(btn) {
            if (!btn.toggleGroup) {
                return;
            }
            var group = groups[btn.toggleGroup];
            if (group) {
                Ext.Array.remove(group, btn);
                btn.un('toggle', toggleGroup);
            }
        },

        
        getPressed: function(group) {
            var g = groups[group],
                i = 0,
                len;
            if (g) {
                for (len = g.length; i < len; i++) {
                    if (g[i].pressed === true) {
                        return g[i];
                    }
                }
            }
            return null;
        }
    };
});


Ext.define('Ext.layout.container.boxOverflow.Menu', {

    

    extend: 'Ext.layout.container.boxOverflow.None',
    requires: ['Ext.toolbar.Separator', 'Ext.button.Button'],
    alternateClassName: 'Ext.layout.boxOverflow.Menu',
    
    

    

    
    noItemsMenuText : '<div class="' + Ext.baseCSSPrefix + 'toolbar-no-items">(None)</div>',

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

        me.callParent(arguments);

        
        layout.beforeLayout = Ext.Function.createInterceptor(layout.beforeLayout, this.clearOverflow, this);

        me.afterCtCls = me.afterCtCls || Ext.baseCSSPrefix + 'box-menu-' + layout.parallelAfter;
        
        me.menuItems = [];
    },
    
    onRemove: function(comp){
        Ext.Array.remove(this.menuItems, comp);
    },

    handleOverflow: function(calculations, targetSize) {
        var me = this,
            layout = me.layout,
            methodName = 'get' + layout.parallelPrefixCap,
            newSize = {},
            posArgs = [null, null];

        me.callParent(arguments);
        this.createMenu(calculations, targetSize);
        newSize[layout.perpendicularPrefix] = targetSize[layout.perpendicularPrefix];
        newSize[layout.parallelPrefix] = targetSize[layout.parallelPrefix] - me.afterCt[methodName]();

        
        
        posArgs[layout.perpendicularSizeIndex] = (calculations.meta.maxSize - me.menuTrigger['get' + layout.perpendicularPrefixCap]()) / 2;
        me.menuTrigger.setPosition.apply(me.menuTrigger, posArgs);

        return { targetSize: newSize };
    },

    
    clearOverflow: function(calculations, targetSize) {
        var me = this,
            newWidth = targetSize ? targetSize.width + (me.afterCt ? me.afterCt.getWidth() : 0) : 0,
            items = me.menuItems,
            i = 0,
            length = items.length,
            item;

        me.hideTrigger();
        for (; i < length; i++) {
            items[i].show();
        }
        items.length = 0;

        return targetSize ? {
            targetSize: {
                height: targetSize.height,
                width : newWidth
            }
        } : null;
    },

    
    showTrigger: function() {
        this.menuTrigger.show();
    },

    
    hideTrigger: function() {
        if (this.menuTrigger !== undefined) {
            this.menuTrigger.hide();
        }
    },

    
    beforeMenuShow: function(menu) {
        var me = this,
            items = me.menuItems,
            i = 0,
            len   = items.length,
            item,
            prev;

        var needsSep = function(group, prev){
            return group.isXType('buttongroup') && !(prev instanceof Ext.toolbar.Separator);
        };

        me.clearMenu();
        menu.removeAll();

        for (; i < len; i++) {
            item = items[i];

            
            if (!i && (item instanceof Ext.toolbar.Separator)) {
                continue;
            }
            if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
                menu.add('-');
            }

            me.addComponentToMenu(menu, item);
            prev = item;
        }

        
        if (menu.items.length < 1) {
            menu.add(me.noItemsMenuText);
        }
    },
    
    
    createMenuConfig : function(component, hideOnClick) {
        var config = Ext.apply({}, component.initialConfig),
            group  = component.toggleGroup;

        Ext.copyTo(config, component, [
            'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
        ]);

        Ext.apply(config, {
            text       : component.overflowText || component.text,
            hideOnClick: hideOnClick,
            destroyMenu: false
        });

        if (group || component.enableToggle) {
            Ext.apply(config, {
                group  : group,
                checked: component.pressed,
                listeners: {
                    checkchange: function(item, checked){
                        component.toggle(checked);
                    }
                }
            });
        }

        delete config.ownerCt;
        delete config.xtype;
        delete config.id;
        return config;
    },

    
    addComponentToMenu : function(menu, component) {
        var me = this;
        if (component instanceof Ext.toolbar.Separator) {
            menu.add('-');
        } else if (component.isComponent) {
            if (component.isXType('splitbutton')) {
                menu.add(me.createMenuConfig(component, true));

            } else if (component.isXType('button')) {
                menu.add(me.createMenuConfig(component, !component.menu));

            } else if (component.isXType('buttongroup')) {
                component.items.each(function(item){
                     me.addComponentToMenu(menu, item);
                });
            } else {
                menu.add(Ext.create(Ext.getClassName(component), me.createMenuConfig(component)));
            }
        }
    },

    
    clearMenu : function() {
        var menu = this.moreMenu;
        if (menu && menu.items) {
            menu.items.each(function(item) {
                if (item.menu) {
                    delete item.menu;
                }
            });
        }
    },

    
    createMenu: function(calculations, targetSize) {
        var me = this,
            layout = me.layout,
            startProp = layout.parallelBefore,
            sizeProp = layout.parallelPrefix,
            available = targetSize[sizeProp],
            boxes = calculations.boxes,
            i = 0,
            len = boxes.length,
            box;

        if (!me.menuTrigger) {
            me.createInnerElements();

            
            me.menu = Ext.create('Ext.menu.Menu', {
                hideMode: 'offsets',
                listeners: {
                    scope: me,
                    beforeshow: me.beforeMenuShow
                }
            });

            
            me.menuTrigger = Ext.create('Ext.button.Button', {
                ownerCt : me.layout.owner, 
                iconCls : Ext.baseCSSPrefix + layout.owner.getXType() + '-more-icon',
                ui      : layout.owner instanceof Ext.toolbar.Toolbar ? 'default-toolbar' : 'default',
                menu    : me.menu,
                getSplitCls: function() { return '';},
                renderTo: me.afterCt
            });
        }
        me.showTrigger();
        available -= me.afterCt.getWidth();

        
        
        me.menuItems.length = 0;
        for (; i < len; i++) {
            box = boxes[i];
            if (box[startProp] + box[sizeProp] > available) {
                me.menuItems.push(box.component);
                box.component.hide();
            }
        }
    },

    
    createInnerElements: function() {
        var me = this,
            target = me.layout.getRenderTarget();

        if (!this.afterCt) {
            target.addCls(Ext.baseCSSPrefix + me.layout.direction + '-box-overflow-body');
            this.afterCt  = target.insertSibling({cls: Ext.layout.container.Box.prototype.innerCls + ' ' + this.afterCtCls}, 'before');
        }
    },

    
    destroy: function() {
        Ext.destroy(this.menu, this.menuTrigger);
    }
});


Ext.define('Ext.util.Region', {

    

    requires: ['Ext.util.Offset'],

    statics: {
        
        getRegion: function(el) {
            return Ext.fly(el).getPageBox(true);
        },

        
        from: function(o) {
            return new this(o.top, o.right, o.bottom, o.left);
        }
    },

    

    
    constructor : function(t, r, b, l) {
        var me = this;
        me.y = me.top = me[1] = t;
        me.right = r;
        me.bottom = b;
        me.x = me.left = me[0] = l;
    },

    
    contains : function(region) {
        var me = this;
        return (region.x >= me.x &&
                region.right <= me.right &&
                region.y >= me.y &&
                region.bottom <= me.bottom);

    },

    
    intersect : function(region) {
        var me = this,
            t = Math.max(me.y, region.y),
            r = Math.min(me.right, region.right),
            b = Math.min(me.bottom, region.bottom),
            l = Math.max(me.x, region.x);

        if (b > t && r > l) {
            return new this.self(t, r, b, l);
        }
        else {
            return false;
        }
    },

    
    union : function(region) {
        var me = this,
            t = Math.min(me.y, region.y),
            r = Math.max(me.right, region.right),
            b = Math.max(me.bottom, region.bottom),
            l = Math.min(me.x, region.x);

        return new this.self(t, r, b, l);
    },

    
    constrainTo : function(r) {
        var me = this,
            constrain = Ext.Number.constrain;
        me.top = me.y = constrain(me.top, r.y, r.bottom);
        me.bottom = constrain(me.bottom, r.y, r.bottom);
        me.left = me.x = constrain(me.left, r.x, r.right);
        me.right = constrain(me.right, r.x, r.right);
        return me;
    },

    
    adjust : function(t, r, b, l) {
        var me = this;
        me.top = me.y += t;
        me.left = me.x += l;
        me.right += r;
        me.bottom += b;
        return me;
    },

    
    getOutOfBoundOffset: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.getOutOfBoundOffsetX(p);
            } else {
                return this.getOutOfBoundOffsetY(p);
            }
        } else {
            p = axis;
            var d = Ext.create('Ext.util.Offset');
            d.x = this.getOutOfBoundOffsetX(p.x);
            d.y = this.getOutOfBoundOffsetY(p.y);
            return d;
        }

    },

    
    getOutOfBoundOffsetX: function(p) {
        if (p <= this.x) {
            return this.x - p;
        } else if (p >= this.right) {
            return this.right - p;
        }

        return 0;
    },

    
    getOutOfBoundOffsetY: function(p) {
        if (p <= this.y) {
            return this.y - p;
        } else if (p >= this.bottom) {
            return this.bottom - p;
        }

        return 0;
    },

    
    isOutOfBound: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.isOutOfBoundX(p);
            } else {
                return this.isOutOfBoundY(p);
            }
        } else {
            p = axis;
            return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
        }
    },

    
    isOutOfBoundX: function(p) {
        return (p < this.x || p > this.right);
    },

    
    isOutOfBoundY: function(p) {
        return (p < this.y || p > this.bottom);
    },

    
    restrict: function(axis, p, factor) {
        if (Ext.isObject(axis)) {
            var newP;

            factor = p;
            p = axis;

            if (p.copy) {
                newP = p.copy();
            }
            else {
                newP = {
                    x: p.x,
                    y: p.y
                };
            }

            newP.x = this.restrictX(p.x, factor);
            newP.y = this.restrictY(p.y, factor);
            return newP;
        } else {
            if (axis == 'x') {
                return this.restrictX(p, factor);
            } else {
                return this.restrictY(p, factor);
            }
        }
    },

    
    restrictX : function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.x) {
            p -= (p - this.x) * factor;
        }
        else if (p >= this.right) {
            p -= (p - this.right) * factor;
        }
        return p;
    },

    
    restrictY : function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.y) {
            p -= (p - this.y) * factor;
        }
        else if (p >= this.bottom) {
            p -= (p - this.bottom) * factor;
        }
        return p;
    },

    
    getSize: function() {
        return {
            width: this.right - this.x,
            height: this.bottom - this.y
        };
    },

    
    copy: function() {
        return new this.self(this.y, this.right, this.bottom, this.x);
    },

    
    copyFrom: function(p) {
        var me = this;
        me.top = me.y = me[1] = p.y;
        me.right = p.right;
        me.bottom = p.bottom;
        me.left = me.x = me[0] = p.x;

        return this;
    },

    
    toString: function() {
        return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
    },


    
    translateBy: function(x, y) {
        if (arguments.length == 1) {
            y = x.y;
            x = x.x;
        }
        var me = this;
        me.top = me.y += y;
        me.right += x;
        me.bottom += y;
        me.left = me.x += x;

        return me;
    },

    
    round: function() {
        var me = this;
        me.top = me.y = Math.round(me.y);
        me.right = Math.round(me.right);
        me.bottom = Math.round(me.bottom);
        me.left = me.x = Math.round(me.x);

        return me;
    },

    
    equals: function(region) {
        return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left);
    }
});





Ext.define('Ext.dd.DragDropManager', {
    singleton: true,

    requires: ['Ext.util.Region'],

    uses: ['Ext.tip.QuickTipManager'],

    
    alternateClassName: ['Ext.dd.DragDropMgr', 'Ext.dd.DDM'],
    
    
    ids: {},

    
    handleIds: {},

    
    dragCurrent: null,

    
    dragOvers: {},

    
    deltaX: 0,

    
    deltaY: 0,

    
    preventDefault: true,

    
    stopPropagation: true,

    
    initialized: false,

    
    locked: false,

    
    init: function() {
        this.initialized = true;
    },

    
    POINT: 0,

    
    INTERSECT: 1,

    
    mode: 0,

    
    _execOnAll: function(sMethod, args) {
        for (var i in this.ids) {
            for (var j in this.ids[i]) {
                var oDD = this.ids[i][j];
                if (! this.isTypeOfDD(oDD)) {
                    continue;
                }
                oDD[sMethod].apply(oDD, args);
            }
        }
    },

    
    _onLoad: function() {

        this.init();

        var Event = Ext.EventManager;
        Event.on(document, "mouseup",   this.handleMouseUp, this, true);
        Event.on(document, "mousemove", this.handleMouseMove, this, true);
        Event.on(window,   "unload",    this._onUnload, this, true);
        Event.on(window,   "resize",    this._onResize, this, true);
        

    },

    
    _onResize: function(e) {
        this._execOnAll("resetConstraints", []);
    },

    
    lock: function() { this.locked = true; },

    
    unlock: function() { this.locked = false; },

    
    isLocked: function() { return this.locked; },

    
    locationCache: {},

    
    useCache: true,

    
    clickPixelThresh: 3,

    
    clickTimeThresh: 350,

    
    dragThreshMet: false,

    
    clickTimeout: null,

    
    startX: 0,

    
    startY: 0,

    
    regDragDrop: function(oDD, sGroup) {
        if (!this.initialized) { this.init(); }

        if (!this.ids[sGroup]) {
            this.ids[sGroup] = {};
        }
        this.ids[sGroup][oDD.id] = oDD;
    },

    
    removeDDFromGroup: function(oDD, sGroup) {
        if (!this.ids[sGroup]) {
            this.ids[sGroup] = {};
        }

        var obj = this.ids[sGroup];
        if (obj && obj[oDD.id]) {
            delete obj[oDD.id];
        }
    },

    
    _remove: function(oDD) {
        for (var g in oDD.groups) {
            if (g && this.ids[g] && this.ids[g][oDD.id]) {
                delete this.ids[g][oDD.id];
            }
        }
        delete this.handleIds[oDD.id];
    },

    
    regHandle: function(sDDId, sHandleId) {
        if (!this.handleIds[sDDId]) {
            this.handleIds[sDDId] = {};
        }
        this.handleIds[sDDId][sHandleId] = sHandleId;
    },

    
    isDragDrop: function(id) {
        return ( this.getDDById(id) ) ? true : false;
    },

    
    getRelated: function(p_oDD, bTargetsOnly) {
        var oDDs = [];
        for (var i in p_oDD.groups) {
            for (var j in this.ids[i]) {
                var dd = this.ids[i][j];
                if (! this.isTypeOfDD(dd)) {
                    continue;
                }
                if (!bTargetsOnly || dd.isTarget) {
                    oDDs[oDDs.length] = dd;
                }
            }
        }

        return oDDs;
    },

    
    isLegalTarget: function (oDD, oTargetDD) {
        var targets = this.getRelated(oDD, true);
        for (var i=0, len=targets.length;i<len;++i) {
            if (targets[i].id == oTargetDD.id) {
                return true;
            }
        }

        return false;
    },

    
    isTypeOfDD: function (oDD) {
        return (oDD && oDD.__ygDragDrop);
    },

    
    isHandle: function(sDDId, sHandleId) {
        return ( this.handleIds[sDDId] &&
                        this.handleIds[sDDId][sHandleId] );
    },

    
    getDDById: function(id) {
        for (var i in this.ids) {
            if (this.ids[i][id]) {
                return this.ids[i][id];
            }
        }
        return null;
    },

    
    handleMouseDown: function(e, oDD) {
        if(Ext.tip.QuickTipManager){
            Ext.tip.QuickTipManager.ddDisable();
        }
        if(this.dragCurrent){
            
            
            this.handleMouseUp(e);
        }
        
        this.currentTarget = e.getTarget();
        this.dragCurrent = oDD;

        var el = oDD.getEl();

        
        this.startX = e.getPageX();
        this.startY = e.getPageY();

        this.deltaX = this.startX - el.offsetLeft;
        this.deltaY = this.startY - el.offsetTop;

        this.dragThreshMet = false;

        this.clickTimeout = setTimeout(
                function() {
                    var DDM = Ext.dd.DragDropManager;
                    DDM.startDrag(DDM.startX, DDM.startY);
                },
                this.clickTimeThresh );
    },

    
    startDrag: function(x, y) {
        clearTimeout(this.clickTimeout);
        if (this.dragCurrent) {
            this.dragCurrent.b4StartDrag(x, y);
            this.dragCurrent.startDrag(x, y);
        }
        this.dragThreshMet = true;
    },

    
    handleMouseUp: function(e) {

        if(Ext.tip.QuickTipManager){
            Ext.tip.QuickTipManager.ddEnable();
        }
        if (! this.dragCurrent) {
            return;
        }

        clearTimeout(this.clickTimeout);

        if (this.dragThreshMet) {
            this.fireEvents(e, true);
        } else {
        }

        this.stopDrag(e);

        this.stopEvent(e);
    },

    
    stopEvent: function(e){
        if(this.stopPropagation) {
            e.stopPropagation();
        }

        if (this.preventDefault) {
            e.preventDefault();
        }
    },

    
    stopDrag: function(e) {
        
        if (this.dragCurrent) {
            if (this.dragThreshMet) {
                this.dragCurrent.b4EndDrag(e);
                this.dragCurrent.endDrag(e);
            }

            this.dragCurrent.onMouseUp(e);
        }

        this.dragCurrent = null;
        this.dragOvers = {};
    },

    
    handleMouseMove: function(e) {
        if (! this.dragCurrent) {
            return true;
        }
        

        
        if (Ext.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
            this.stopEvent(e);
            return this.handleMouseUp(e);
        }

        if (!this.dragThreshMet) {
            var diffX = Math.abs(this.startX - e.getPageX());
            var diffY = Math.abs(this.startY - e.getPageY());
            if (diffX > this.clickPixelThresh ||
                        diffY > this.clickPixelThresh) {
                this.startDrag(this.startX, this.startY);
            }
        }

        if (this.dragThreshMet) {
            this.dragCurrent.b4Drag(e);
            this.dragCurrent.onDrag(e);
            if(!this.dragCurrent.moveOnly){
                this.fireEvents(e, false);
            }
        }

        this.stopEvent(e);

        return true;
    },

    
    fireEvents: function(e, isDrop) {
        var dc = this.dragCurrent;

        
        
        if (!dc || dc.isLocked()) {
            return;
        }

        var pt = e.getPoint();

        
        var oldOvers = [];

        var outEvts   = [];
        var overEvts  = [];
        var dropEvts  = [];
        var enterEvts = [];

        
        
        for (var i in this.dragOvers) {

            var ddo = this.dragOvers[i];

            if (! this.isTypeOfDD(ddo)) {
                continue;
            }

            if (! this.isOverTarget(pt, ddo, this.mode)) {
                outEvts.push( ddo );
            }

            oldOvers[i] = true;
            delete this.dragOvers[i];
        }

        for (var sGroup in dc.groups) {

            if ("string" != typeof sGroup) {
                continue;
            }

            for (i in this.ids[sGroup]) {
                var oDD = this.ids[sGroup][i];
                if (! this.isTypeOfDD(oDD)) {
                    continue;
                }

                if (oDD.isTarget && !oDD.isLocked() && ((oDD != dc) || (dc.ignoreSelf === false))) {
                    if (this.isOverTarget(pt, oDD, this.mode)) {
                        
                        if (isDrop) {
                            dropEvts.push( oDD );
                        
                        } else {

                            
                            if (!oldOvers[oDD.id]) {
                                enterEvts.push( oDD );
                            
                            } else {
                                overEvts.push( oDD );
                            }

                            this.dragOvers[oDD.id] = oDD;
                        }
                    }
                }
            }
        }

        if (this.mode) {
            if (outEvts.length) {
                dc.b4DragOut(e, outEvts);
                dc.onDragOut(e, outEvts);
            }

            if (enterEvts.length) {
                dc.onDragEnter(e, enterEvts);
            }

            if (overEvts.length) {
                dc.b4DragOver(e, overEvts);
                dc.onDragOver(e, overEvts);
            }

            if (dropEvts.length) {
                dc.b4DragDrop(e, dropEvts);
                dc.onDragDrop(e, dropEvts);
            }

        } else {
            
            var len = 0;
            for (i=0, len=outEvts.length; i<len; ++i) {
                dc.b4DragOut(e, outEvts[i].id);
                dc.onDragOut(e, outEvts[i].id);
            }

            
            for (i=0,len=enterEvts.length; i<len; ++i) {
                
                dc.onDragEnter(e, enterEvts[i].id);
            }

            
            for (i=0,len=overEvts.length; i<len; ++i) {
                dc.b4DragOver(e, overEvts[i].id);
                dc.onDragOver(e, overEvts[i].id);
            }

            
            for (i=0, len=dropEvts.length; i<len; ++i) {
                dc.b4DragDrop(e, dropEvts[i].id);
                dc.onDragDrop(e, dropEvts[i].id);
            }

        }

        
        if (isDrop && !dropEvts.length) {
            dc.onInvalidDrop(e);
        }

    },

    
    getBestMatch: function(dds) {
        var winner = null;
        
        
           
        
        

        var len = dds.length;

        if (len == 1) {
            winner = dds[0];
        } else {
            
            for (var i=0; i<len; ++i) {
                var dd = dds[i];
                
                
                
                if (dd.cursorIsOver) {
                    winner = dd;
                    break;
                
                } else {
                    if (!winner ||
                        winner.overlap.getArea() < dd.overlap.getArea()) {
                        winner = dd;
                    }
                }
            }
        }

        return winner;
    },

    
    refreshCache: function(groups) {
        for (var sGroup in groups) {
            if ("string" != typeof sGroup) {
                continue;
            }
            for (var i in this.ids[sGroup]) {
                var oDD = this.ids[sGroup][i];

                if (this.isTypeOfDD(oDD)) {
                
                    var loc = this.getLocation(oDD);
                    if (loc) {
                        this.locationCache[oDD.id] = loc;
                    } else {
                        delete this.locationCache[oDD.id];
                        
                        
                        
                    }
                }
            }
        }
    },

    
    verifyEl: function(el) {
        if (el) {
            var parent;
            if(Ext.isIE){
                try{
                    parent = el.offsetParent;
                }catch(e){}
            }else{
                parent = el.offsetParent;
            }
            if (parent) {
                return true;
            }
        }

        return false;
    },

    
    getLocation: function(oDD) {
        if (! this.isTypeOfDD(oDD)) {
            return null;
        }

        
        
        if (oDD.getRegion) {
            return oDD.getRegion();
        }

        var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;

        try {
            pos= Ext.core.Element.getXY(el);
        } catch (e) { }

        if (!pos) {
            return null;
        }

        x1 = pos[0];
        x2 = x1 + el.offsetWidth;
        y1 = pos[1];
        y2 = y1 + el.offsetHeight;

        t = y1 - oDD.padding[0];
        r = x2 + oDD.padding[1];
        b = y2 + oDD.padding[2];
        l = x1 - oDD.padding[3];

        return Ext.create('Ext.util.Region', t, r, b, l);
    },

    
    isOverTarget: function(pt, oTarget, intersect) {
        
        var loc = this.locationCache[oTarget.id];
        if (!loc || !this.useCache) {
            loc = this.getLocation(oTarget);
            this.locationCache[oTarget.id] = loc;

        }

        if (!loc) {
            return false;
        }

        oTarget.cursorIsOver = loc.contains( pt );

        
        
        
        
        
        var dc = this.dragCurrent;
        if (!dc || !dc.getTargetCoord ||
                (!intersect && !dc.constrainX && !dc.constrainY)) {
            return oTarget.cursorIsOver;
        }

        oTarget.overlap = null;

        
        
        
        
        var pos = dc.getTargetCoord(pt.x, pt.y);

        var el = dc.getDragEl();
        var curRegion = Ext.create('Ext.util.Region', pos.y,
                                               pos.x + el.offsetWidth,
                                               pos.y + el.offsetHeight,
                                               pos.x );

        var overlap = curRegion.intersect(loc);

        if (overlap) {
            oTarget.overlap = overlap;
            return (intersect) ? true : oTarget.cursorIsOver;
        } else {
            return false;
        }
    },

    
    _onUnload: function(e, me) {
        Ext.dd.DragDropManager.unregAll();
    },

    
    unregAll: function() {

        if (this.dragCurrent) {
            this.stopDrag();
            this.dragCurrent = null;
        }

        this._execOnAll("unreg", []);

        for (var i in this.elementCache) {
            delete this.elementCache[i];
        }

        this.elementCache = {};
        this.ids = {};
    },

    
    elementCache: {},

    
    getElWrapper: function(id) {
        var oWrapper = this.elementCache[id];
        if (!oWrapper || !oWrapper.el) {
            oWrapper = this.elementCache[id] =
                new this.ElementWrapper(Ext.getDom(id));
        }
        return oWrapper;
    },

    
    getElement: function(id) {
        return Ext.getDom(id);
    },

    
    getCss: function(id) {
        var el = Ext.getDom(id);
        return (el) ? el.style : null;
    },

    
    ElementWrapper: function(el) {
            
            this.el = el || null;
            
            this.id = this.el && el.id;
            
            this.css = this.el && el.style;
        },

    
    getPosX: function(el) {
        return Ext.core.Element.getX(el);
    },

    
    getPosY: function(el) {
        return Ext.core.Element.getY(el);
    },

    
    swapNode: function(n1, n2) {
        if (n1.swapNode) {
            n1.swapNode(n2);
        } else {
            var p = n2.parentNode;
            var s = n2.nextSibling;

            if (s == n1) {
                p.insertBefore(n1, n2);
            } else if (n2 == n1.nextSibling) {
                p.insertBefore(n2, n1);
            } else {
                n1.parentNode.replaceChild(n2, n1);
                p.insertBefore(n1, s);
            }
        }
    },

    
    getScroll: function () {
        var doc   = window.document,
            docEl = doc.documentElement,
            body  = doc.body,
            top   = 0,
            left  = 0;
            
        if (Ext.isGecko4) {
            top  = window.scrollYOffset;
            left = window.scrollXOffset;
        } else {
            if (docEl && (docEl.scrollTop || docEl.scrollLeft)) {
                top  = docEl.scrollTop;
                left = docEl.scrollLeft;
            } else if (body) {
                top  = body.scrollTop;
                left = body.scrollLeft;
            } 
        }
        return {
            top: top,
            left: left
        };
    },

    
    getStyle: function(el, styleProp) {
        return Ext.fly(el).getStyle(styleProp);
    },

    
    getScrollTop: function () {
        return this.getScroll().top;
    },

    
    getScrollLeft: function () {
        return this.getScroll().left;
    },

    
    moveToEl: function (moveEl, targetEl) {
        var aCoord = Ext.core.Element.getXY(targetEl);
        Ext.core.Element.setXY(moveEl, aCoord);
    },

    
    numericSort: function(a, b) {
        return (a - b);
    },

    
    _timeoutCount: 0,

    
    _addListeners: function() {
        if ( document ) {
            this._onLoad();
        } else {
            if (this._timeoutCount > 2000) {
            } else {
                setTimeout(this._addListeners, 10);
                if (document && document.body) {
                    this._timeoutCount += 1;
                }
            }
        }
    },

    
    handleWasClicked: function(node, id) {
        if (this.isHandle(id, node.id)) {
            return true;
        } else {
            
            var p = node.parentNode;

            while (p) {
                if (this.isHandle(id, p.id)) {
                    return true;
                } else {
                    p = p.parentNode;
                }
            }
        }

        return false;
    }
}, function() {
    this._addListeners();
});



Ext.define('Ext.layout.container.Box', {

    

    alias: ['layout.box'],
    extend: 'Ext.layout.container.Container',
    alternateClassName: 'Ext.layout.BoxLayout',
    
    requires: [
        'Ext.layout.container.boxOverflow.None',
        'Ext.layout.container.boxOverflow.Menu',
        'Ext.layout.container.boxOverflow.Scroller',
        'Ext.util.Format',
        'Ext.dd.DragDropManager'
    ],

    

    

    
    defaultMargins: {
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    },

    
    padding: '0',
    
    pack: 'start',

    
    

    type: 'box',
    scrollOffset: 0,
    itemCls: Ext.baseCSSPrefix + 'box-item',
    targetCls: Ext.baseCSSPrefix + 'box-layout-ct',
    innerCls: Ext.baseCSSPrefix + 'box-inner',

    bindToOwnerCtContainer: true,

    fixedLayout: false,
    
    
    
    availableSpaceOffset: 0,
    
    
    reserveOffset: true,
    
    
    clearInnerCtOnLayout: false,

    flexSortFn: function (a, b) {
        var maxParallelPrefix = 'max' + this.parallelPrefixCap,
            infiniteValue = Infinity;
        a = a.component[maxParallelPrefix] || infiniteValue;
        b = b.component[maxParallelPrefix] || infiniteValue;
        
        if (!isFinite(a) && !isFinite(b)) {
            return false;
        }
        return a - b;
    },

    
    minSizeSortFn: function(a, b) {
        return b.available - a.available;
    },

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

        me.callParent(arguments);

        
        me.flexSortFn = Ext.Function.bind(me.flexSortFn, me);

        me.initOverflowHandler();
    },

    
    getChildBox: function(child) {
        child = child.el || this.owner.getComponent(child).el;
        return {
            left: child.getLeft(true),
            top: child.getTop(true),
            width: child.getWidth(),
            height: child.getHeight()
        };
    },

    
    calculateChildBox: function(child) {
        var me = this,
            boxes = me.calculateChildBoxes(me.getVisibleItems(), me.getLayoutTargetSize()).boxes,
            ln = boxes.length,
            i = 0;

        child = me.owner.getComponent(child);
        for (; i < ln; i++) {
            if (boxes[i].component === child) {
                return boxes[i];
            }
        }
    },

    
    calculateChildBoxes: function(visibleItems, targetSize) {
        var me = this,
            math = Math,
            mmax = math.max,
            infiniteValue = Infinity,
            undefinedValue,

            parallelPrefix = me.parallelPrefix,
            parallelPrefixCap = me.parallelPrefixCap,
            perpendicularPrefix = me.perpendicularPrefix,
            perpendicularPrefixCap = me.perpendicularPrefixCap,
            parallelMinString = 'min' + parallelPrefixCap,
            perpendicularMinString = 'min' + perpendicularPrefixCap,
            perpendicularMaxString = 'max' + perpendicularPrefixCap,

            parallelSize = targetSize[parallelPrefix] - me.scrollOffset,
            perpendicularSize = targetSize[perpendicularPrefix],
            padding = me.padding,
            parallelOffset = padding[me.parallelBefore],
            paddingParallel = parallelOffset + padding[me.parallelAfter],
            perpendicularOffset = padding[me.perpendicularLeftTop],
            paddingPerpendicular =  perpendicularOffset + padding[me.perpendicularRightBottom],
            availPerpendicularSize = mmax(0, perpendicularSize - paddingPerpendicular),

            isStart = me.pack == 'start',
            isCenter = me.pack == 'center',
            isEnd = me.pack == 'end',

            constrain = Ext.Number.constrain,
            visibleCount = visibleItems.length,
            nonFlexSize = 0,
            totalFlex = 0,
            desiredSize = 0,
            minimumSize = 0,
            maxSize = 0,
            boxes = [],
            minSizes = [],
            calculatedWidth,

            i, child, childParallel, childPerpendicular, childMargins, childSize, minParallel, tmpObj, shortfall, 
            tooNarrow, availableSpace, minSize, item, length, itemIndex, box, oldSize, newSize, reduction, diff, 
            flexedBoxes, remainingSpace, remainingFlex, flexedSize, parallelMargins, calcs, offset, 
            perpendicularMargins, stretchSize;

        
        for (i = 0; i < visibleCount; i++) {
            child = visibleItems[i];
            childPerpendicular = child[perpendicularPrefix];
            me.layoutItem(child);
            childMargins = child.margins;
            parallelMargins = childMargins[me.parallelBefore] + childMargins[me.parallelAfter];

            
            tmpObj = {
                component: child,
                margins: childMargins
            };

            
            if (child.flex) {
                totalFlex += child.flex;
                childParallel = undefinedValue;
            }
            
            else {
                if (!(child[parallelPrefix] && childPerpendicular)) {
                    childSize = child.getSize();
                }
                childParallel = child[parallelPrefix] || childSize[parallelPrefix];
                childPerpendicular = childPerpendicular || childSize[perpendicularPrefix];
            }

            nonFlexSize += parallelMargins + (childParallel || 0);
            desiredSize += parallelMargins + (child.flex ? child[parallelMinString] || 0 : childParallel);
            minimumSize += parallelMargins + (child[parallelMinString] || childParallel || 0);

            
            if (typeof childPerpendicular != 'number') {
                
                
                childPerpendicular = child['get' + perpendicularPrefixCap]();
            }

            
            maxSize = mmax(maxSize, childPerpendicular + childMargins[me.perpendicularLeftTop] + childMargins[me.perpendicularRightBottom]);

            tmpObj[parallelPrefix] = childParallel || undefinedValue;
            tmpObj[perpendicularPrefix] = childPerpendicular || undefinedValue;
            boxes.push(tmpObj);
        }
        shortfall = desiredSize - parallelSize;
        tooNarrow = minimumSize > parallelSize;

        
        availableSpace = mmax(0, parallelSize - nonFlexSize - paddingParallel - (me.reserveOffset ? me.availableSpaceOffset : 0));

        if (tooNarrow) {
            for (i = 0; i < visibleCount; i++) {
                box = boxes[i];
                minSize = visibleItems[i][parallelMinString] || visibleItems[i][parallelPrefix] || box[parallelPrefix];
                box.dirtySize = box.dirtySize || box[parallelPrefix] != minSize;
                box[parallelPrefix] = minSize;
            }
        }
        else {
            
            
            if (shortfall > 0) {
                
                for (i = 0; i < visibleCount; i++) {
                    item = visibleItems[i];
                    minSize = item[parallelMinString] || 0;

                    
                    
                    if (item.flex) {
                        box = boxes[i];
                        box.dirtySize = box.dirtySize || box[parallelPrefix] != minSize;
                        box[parallelPrefix] = minSize;
                    }
                    else {
                        minSizes.push({
                            minSize: minSize,
                            available: boxes[i][parallelPrefix] - minSize,
                            index: i
                        });
                    }
                }

                
                Ext.Array.sort(minSizes, me.minSizeSortFn);

                
                for (i = 0, length = minSizes.length; i < length; i++) {
                    itemIndex = minSizes[i].index;

                    if (itemIndex == undefinedValue) {
                        continue;
                    }
                    item = visibleItems[itemIndex];
                    minSize = minSizes[i].minSize;

                    box = boxes[itemIndex];
                    oldSize = box[parallelPrefix];
                    newSize = mmax(minSize, oldSize - math.ceil(shortfall / (length - i)));
                    reduction = oldSize - newSize;

                    box.dirtySize = box.dirtySize || box[parallelPrefix] != newSize;
                    box[parallelPrefix] = newSize;
                    shortfall -= reduction;
                }
            }
            else {
                remainingSpace = availableSpace;
                remainingFlex = totalFlex;
                flexedBoxes = [];

                
                for (i = 0; i < visibleCount; i++) {
                    child = visibleItems[i];
                    if (isStart && child.flex) {
                        flexedBoxes.push(boxes[Ext.Array.indexOf(visibleItems, child)]);
                    }
                }
                
                
                
                Ext.Array.sort(flexedBoxes, me.flexSortFn);

                
                for (i = 0; i < flexedBoxes.length; i++) {
                    calcs = flexedBoxes[i];
                    child = calcs.component;
                    childMargins = calcs.margins;

                    flexedSize = math.ceil((child.flex / remainingFlex) * remainingSpace);

                    
                    flexedSize = Math.max(child['min' + parallelPrefixCap] || 0, math.min(child['max' + parallelPrefixCap] || infiniteValue, flexedSize));

                    
                    remainingSpace -= flexedSize;
                    remainingFlex -= child.flex;

                    calcs.dirtySize = calcs.dirtySize || calcs[parallelPrefix] != flexedSize;
                    calcs[parallelPrefix] = flexedSize;
                }
            }
        }

        if (isCenter) {
            parallelOffset += availableSpace / 2;
        }
        else if (isEnd) {
            parallelOffset += availableSpace;
        }

        
        
        
        
        if (me.owner.dock && (Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks) && !me.owner.width && me.direction == 'vertical') {

            calculatedWidth = maxSize + me.owner.el.getPadding('lr') + me.owner.el.getBorderWidth('lr');
            if (me.owner.frameSize) {
                calculatedWidth += me.owner.frameSize.left + me.owner.frameSize.right;
            }
            
            availPerpendicularSize = Math.min(availPerpendicularSize, targetSize.width = maxSize + padding.left + padding.right);
        }

        
        for (i = 0; i < visibleCount; i++) {
            child = visibleItems[i];
            calcs = boxes[i];

            childMargins = calcs.margins;

            perpendicularMargins = childMargins[me.perpendicularLeftTop] + childMargins[me.perpendicularRightBottom];

            
            parallelOffset += childMargins[me.parallelBefore];

            calcs[me.parallelBefore] = parallelOffset;
            calcs[me.perpendicularLeftTop] = perpendicularOffset + childMargins[me.perpendicularLeftTop];

            if (me.align == 'stretch') {
                stretchSize = constrain(availPerpendicularSize - perpendicularMargins, child[perpendicularMinString] || 0, child[perpendicularMaxString] || infiniteValue);
                calcs.dirtySize = calcs.dirtySize || calcs[perpendicularPrefix] != stretchSize;
                calcs[perpendicularPrefix] = stretchSize;
            }
            else if (me.align == 'stretchmax') {
                stretchSize = constrain(maxSize - perpendicularMargins, child[perpendicularMinString] || 0, child[perpendicularMaxString] || infiniteValue);
                calcs.dirtySize = calcs.dirtySize || calcs[perpendicularPrefix] != stretchSize;
                calcs[perpendicularPrefix] = stretchSize;
            }
            else if (me.align == me.alignCenteringString) {
                
                
                
                diff = mmax(availPerpendicularSize, maxSize) - me.innerCt.getBorderWidth(me.perpendicularLT + me.perpendicularRB) - calcs[perpendicularPrefix];
                if (diff > 0) {
                    calcs[me.perpendicularLeftTop] = perpendicularOffset + Math.round(diff / 2);
                }
            }

            
            parallelOffset += (calcs[parallelPrefix] || 0) + childMargins[me.parallelAfter];
        }

        return {
            boxes: boxes,
            meta : {
                calculatedWidth: calculatedWidth,
                maxSize: maxSize,
                nonFlexSize: nonFlexSize,
                desiredSize: desiredSize,
                minimumSize: minimumSize,
                shortfall: shortfall,
                tooNarrow: tooNarrow
            }
        };
    },
    
    onRemove: function(comp){
        this.callParent(arguments);
        if (this.overflowHandler) {
            this.overflowHandler.onRemove(comp);
        }
    },

    
    initOverflowHandler: function() {
        var handler = this.overflowHandler;

        if (typeof handler == 'string') {
            handler = {
                type: handler
            };
        }

        var handlerType = 'None';
        if (handler && handler.type !== undefined) {
            handlerType = handler.type;
        }

        var constructor = Ext.layout.container.boxOverflow[handlerType];
        if (constructor[this.type]) {
            constructor = constructor[this.type];
        }

        this.overflowHandler = Ext.create('Ext.layout.container.boxOverflow.' + handlerType, this, handler);
    },

    
    onLayout: function() {
        this.callParent();
        
        if (this.clearInnerCtOnLayout === true && this.adjustmentPass !== true) {
            this.innerCt.setSize(null, null);
        }

        var me = this,
            targetSize = me.getLayoutTargetSize(),
            items = me.getVisibleItems(),
            calcs = me.calculateChildBoxes(items, targetSize),
            boxes = calcs.boxes,
            meta = calcs.meta,
            handler, method, results;

        if (me.autoSize && calcs.meta.desiredSize) {
            targetSize[me.parallelPrefix] = calcs.meta.desiredSize;
        }

        
        if (meta.shortfall > 0) {
            handler = me.overflowHandler;
            method = meta.tooNarrow ? 'handleOverflow': 'clearOverflow';

            results = handler[method](calcs, targetSize);

            if (results) {
                if (results.targetSize) {
                    targetSize = results.targetSize;
                }

                if (results.recalculate) {
                    items = me.getVisibleItems(owner);
                    calcs = me.calculateChildBoxes(items, targetSize);
                    boxes = calcs.boxes;
                }
            }
        } else {
            me.overflowHandler.clearOverflow();
        }

        
        me.layoutTargetLastSize = targetSize;

        
        me.childBoxCache = calcs;

        me.updateInnerCtSize(targetSize, calcs);
        me.updateChildBoxes(boxes);
        me.handleTargetOverflow(targetSize);
    },

    
    updateChildBoxes: function(boxes) {
        var me = this,
            i = 0,
            length = boxes.length,
            animQueue = [],
            dd = Ext.dd.DDM.getDDById(me.innerCt.id), 
            oldBox, newBox, changed, comp, boxAnim, animCallback;

        for (; i < length; i++) {
            newBox = boxes[i];
            comp = newBox.component;

            
            
            if (dd && (dd.getDragEl() === comp.el.dom)) {
                continue;
            }

            changed = false;

            oldBox = me.getChildBox(comp);

            
            
            
            if (me.animate) {
                
                animCallback = me.animate.callback || me.animate;
                boxAnim = {
                    layoutAnimation: true,  
                    target: comp,
                    from: {},
                    to: {},
                    listeners: {}
                };
                
                
                
                
                if (!isNaN(newBox.width) && (newBox.width != oldBox.width)) {
                    changed = true;
                    
                    boxAnim.to.width = newBox.width;
                }
                if (!isNaN(newBox.height) && (newBox.height != oldBox.height)) {
                    changed = true;
                    
                    boxAnim.to.height = newBox.height;
                }
                if (!isNaN(newBox.left) && (newBox.left != oldBox.left)) {
                    changed = true;
                    
                    boxAnim.to.left = newBox.left;
                }
                if (!isNaN(newBox.top) && (newBox.top != oldBox.top)) {
                    changed = true;
                    
                    boxAnim.to.top = newBox.top;
                }
                if (changed) {
                    animQueue.push(boxAnim);
                }
            } else {
                if (newBox.dirtySize) {
                    if (newBox.width !== oldBox.width || newBox.height !== oldBox.height) {
                        me.setItemSize(comp, newBox.width, newBox.height);
                    }
                }
                
                if (isNaN(newBox.left) || isNaN(newBox.top)) {
                    continue;
                }
                comp.setPosition(newBox.left, newBox.top);
            }
        }

        
        length = animQueue.length;
        if (length) {

            
            
            var afterAnimate = function(anim) {
                
                length -= 1;
                if (!length) {
                    me.layoutBusy = false;
                    if (Ext.isFunction(animCallback)) {
                        animCallback();
                    }
                }
            };

            var beforeAnimate = function() {
                me.layoutBusy = true;
            };

            
            for (i = 0, length = animQueue.length; i < length; i++) {
                boxAnim = animQueue[i];

                
                boxAnim.listeners.afteranimate = afterAnimate;

                
                if (!i) {
                    boxAnim.listeners.beforeanimate = beforeAnimate;
                }
                if (me.animate.duration) {
                    boxAnim.duration = me.animate.duration;
                }
                comp = boxAnim.target;
                delete boxAnim.target;
                
                comp.stopAnimation();
                comp.animate(boxAnim);
            }
        }
    },

    
    updateInnerCtSize: function(tSize, calcs) {
        var me = this,
            mmax = Math.max,
            align = me.align,
            padding = me.padding,
            width = tSize.width,
            height = tSize.height,
            meta = calcs.meta,
            innerCtWidth,
            innerCtHeight;

        if (me.direction == 'horizontal') {
            innerCtWidth = width;
            innerCtHeight = meta.maxSize + padding.top + padding.bottom + me.innerCt.getBorderWidth('tb');

            if (align == 'stretch') {
                innerCtHeight = height;
            }
            else if (align == 'middle') {
                innerCtHeight = mmax(height, innerCtHeight);
            }
        } else {
            innerCtHeight = height;
            innerCtWidth = meta.maxSize + padding.left + padding.right + me.innerCt.getBorderWidth('lr');

            if (align == 'stretch') {
                innerCtWidth = width;
            }
            else if (align == 'center') {
                innerCtWidth = mmax(width, innerCtWidth);
            }
        }
        me.getRenderTarget().setSize(innerCtWidth || undefined, innerCtHeight || undefined);

        
        
        if (meta.calculatedWidth && me.owner.el.getWidth() > meta.calculatedWidth) {
            me.owner.el.setWidth(meta.calculatedWidth);
        }

        if (me.innerCt.dom.scrollTop) {
            me.innerCt.dom.scrollTop = 0;
        }
    },

    
    handleTargetOverflow: function(previousTargetSize) {
        var target = this.getTarget(),
            overflow = target.getStyle('overflow'),
            newTargetSize;

        if (overflow && overflow != 'hidden' && !this.adjustmentPass) {
            newTargetSize = this.getLayoutTargetSize();
            if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height) {
                this.adjustmentPass = true;
                this.onLayout();
                return true;
            }
        }

        delete this.adjustmentPass;
    },

    
    isValidParent : function(item, target, position) {
        
        
        var itemEl = item.el ? item.el.dom : Ext.getDom(item);
        return (itemEl && this.innerCt && itemEl.parentNode === this.innerCt.dom) || false;
    },

    
    
    getRenderTarget: function() {
        if (!this.innerCt) {
            
            this.innerCt = this.getTarget().createChild({
                cls: this.innerCls,
                role: 'presentation'
            });
            this.padding = Ext.util.Format.parseBox(this.padding);
        }
        return this.innerCt;
    },

    
    renderItem: function(item, target) {
        this.callParent(arguments);
        var me = this,
            itemEl = item.getEl(),
            style = itemEl.dom.style,
            margins = item.margins || item.margin;

        
        if (margins) {
            if (Ext.isString(margins) || Ext.isNumber(margins)) {
                margins = Ext.util.Format.parseBox(margins);
            } else {
                Ext.applyIf(margins, {top: 0, right: 0, bottom: 0, left: 0});
            }
        } else {
            margins = Ext.apply({}, me.defaultMargins);
        }

        
        margins.top    += itemEl.getMargin('t');
        margins.right  += itemEl.getMargin('r');
        margins.bottom += itemEl.getMargin('b');
        margins.left   += itemEl.getMargin('l');
        style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = '0';

        
        item.margins = margins;
    },

    
    destroy: function() {
        Ext.destroy(this.overflowHandler);
        this.callParent(arguments);
    }
});

Ext.define('Ext.layout.container.HBox', {

    

    alias: ['layout.hbox'],
    extend: 'Ext.layout.container.Box',
    alternateClassName: 'Ext.layout.HBoxLayout',
    
    

    
    align: 'top', 

    //@private

    alignCenteringString: 'middle',

    type : 'hbox',

    direction: 'horizontal',

    
    parallelSizeIndex: 0,
    perpendicularSizeIndex: 1,

    parallelPrefix: 'width',
    parallelPrefixCap: 'Width',
    parallelLT: 'l',
    parallelRB: 'r',
    parallelBefore: 'left',
    parallelBeforeCap: 'Left',
    parallelAfter: 'right',
    parallelPosition: 'x',

    perpendicularPrefix: 'height',
    perpendicularPrefixCap: 'Height',
    perpendicularLT: 't',
    perpendicularRB: 'b',
    perpendicularLeftTop: 'top',
    perpendicularRightBottom: 'bottom',
    perpendicularPosition: 'y'
});

Ext.define('Ext.layout.container.VBox', {

    

    alias: ['layout.vbox'],
    extend: 'Ext.layout.container.Box',
    alternateClassName: 'Ext.layout.VBoxLayout',
    
    

    
    align : 'left', 

    //@private

    alignCenteringString: 'center',

    type: 'vbox',

    direction: 'vertical',

    
    parallelSizeIndex: 1,
    perpendicularSizeIndex: 0,

    parallelPrefix: 'height',
    parallelPrefixCap: 'Height',
    parallelLT: 't',
    parallelRB: 'b',
    parallelBefore: 'top',
    parallelBeforeCap: 'Top',
    parallelAfter: 'bottom',
    parallelPosition: 'y',

    perpendicularPrefix: 'width',
    perpendicularPrefixCap: 'Width',
    perpendicularLT: 'l',
    perpendicularRB: 'r',
    perpendicularLeftTop: 'left',
    perpendicularRightBottom: 'right',
    perpendicularPosition: 'x'
});

Ext.define('Ext.FocusManager', {
    singleton: true,
    alternateClassName: 'Ext.FocusMgr',

    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: [
        'Ext.ComponentManager',
        'Ext.ComponentQuery',
        'Ext.util.HashMap',
        'Ext.util.KeyNav'
    ],

    
    enabled: false,

    

    focusElementCls: Ext.baseCSSPrefix + 'focus-element',

    focusFrameCls: Ext.baseCSSPrefix + 'focus-frame',

    
    whitelist: [
        'textfield'
    ],

    tabIndexWhitelist: [
        'a',
        'button',
        'embed',
        'frame',
        'iframe',
        'img',
        'input',
        'object',
        'select',
        'textarea'
    ],

    constructor: function() {
        var me = this,
            CQ = Ext.ComponentQuery;

        me.addEvents(
            
            'beforecomponentfocus',

            
            'componentfocus',

            
            'disable',

            
            'enable'
        );

        
        
        me.keyNav = Ext.create('Ext.util.KeyNav', Ext.getDoc(), {
            disabled: true,
            scope: me,

            backspace: me.focusLast,
            enter: me.navigateIn,
            esc: me.navigateOut,
            tab: me.navigateSiblings

            
            
            
            
            
            
        });

        me.focusData = {};
        me.subscribers = Ext.create('Ext.util.HashMap');
        me.focusChain = {};

        
        Ext.apply(CQ.pseudos, {
            focusable: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c,

                    isFocusable = function(x) {
                        return x && x.focusable !== false && CQ.is(x, '[rendered]:not([destroying]):not([isDestroyed]):not([disabled]){isVisible(true)}{el && c.el.dom && c.el.isVisible()}');
                    };

                for (; i < len; i++) {
                    c = cmps[i];
                    if (isFocusable(c)) {
                        results.push(c);
                    }
                }

                return results;
            },

            nextFocus: function(cmps, idx, step) {
                step = step || 1;
                idx = parseInt(idx, 10);

                var len = cmps.length,
                    i = idx + step,
                    c;

                for (; i != idx; i += step) {
                    if (i >= len) {
                        i = 0;
                    } else if (i < 0) {
                        i = len - 1;
                    }

                    c = cmps[i];
                    if (CQ.is(c, ':focusable')) {
                        return [c];
                    } else if (c.placeholder && CQ.is(c.placeholder, ':focusable')) {
                        return [c.placeholder];
                    }
                }

                return [];
            },

            prevFocus: function(cmps, idx) {
                return this.nextFocus(cmps, idx, -1);
            },

            root: function(cmps) {
                var len = cmps.length,
                    results = [],
                    i = 0,
                    c;

                for (; i < len; i++) {
                    c = cmps[i];
                    if (!c.ownerCt) {
                        results.push(c);
                    }
                }

                return results;
            }
        });
    },

    
    addXTypeToWhitelist: function(xtype) {
        var me = this;

        if (Ext.isArray(xtype)) {
            Ext.Array.forEach(xtype, me.addXTypeToWhitelist, me);
            return;
        }

        if (!Ext.Array.contains(me.whitelist, xtype)) {
            me.whitelist.push(xtype);
        }
    },

    clearComponent: function(cmp) {
        clearTimeout(this.cmpFocusDelay);
        if (!cmp.isDestroyed) {
            cmp.blur();
        }
    },

    
    disable: function() {
        var me = this;

        if (!me.enabled) {
            return;
        }

        delete me.options;
        me.enabled = false;

        Ext.ComponentManager.all.un('add', me.onComponentCreated, me);

        me.removeDOM();

        
        me.keyNav.disable();

        
        me.setFocusAll(false);

        me.fireEvent('disable', me);
    },

    
    enable: function(options) {
        var me = this;

        if (options === true) {
            options = { focusFrame: true };
        }
        me.options = options = options || {};

        if (me.enabled) {
            return;
        }

        
        Ext.ComponentManager.all.on('add', me.onComponentCreated, me);

        me.initDOM(options);

        
        me.keyNav.enable();

        
        me.setFocusAll(true, options);

        
        me.focusEl.focus();
        delete me.focusedCmp;

        me.enabled = true;
        me.fireEvent('enable', me);
    },

    focusLast: function(e) {
        var me = this;

        if (me.isWhitelisted(me.focusedCmp)) {
            return true;
        }

        
        if (me.previousFocusedCmp) {
            me.previousFocusedCmp.focus();
        }
    },

    getRootComponents: function() {
        var me = this,
            CQ = Ext.ComponentQuery,
            inline = CQ.query(':focusable:root:not([floating])'),
            floating = CQ.query(':focusable:root[floating]');

        
        
        floating.sort(function(a, b) {
            return a.el.getZIndex() > b.el.getZIndex();
        });

        return floating.concat(inline);
    },

    initDOM: function(options) {
        var me = this,
            sp = '&#160',
            cls = me.focusFrameCls;

        if (!Ext.isReady) {
            Ext.onReady(me.initDOM, me);
            return;
        }

        
        if (!me.focusEl) {
            me.focusEl = Ext.getBody().createChild({
                tabIndex: '-1',
                cls: me.focusElementCls,
                html: sp
            });
        }

        
        if (!me.focusFrame && options.focusFrame) {
            me.focusFrame = Ext.getBody().createChild({
                cls: cls,
                children: [
                    { cls: cls + '-top' },
                    { cls: cls + '-bottom' },
                    { cls: cls + '-left' },
                    { cls: cls + '-right' }
                ],
                style: 'top: -100px; left: -100px;'
            });
            me.focusFrame.setVisibilityMode(Ext.core.Element.DISPLAY);
            me.focusFrameWidth = me.focusFrame.child('.' + cls + '-top').getHeight();
            me.focusFrame.hide().setLeftTop(0, 0);
        }
    },

    isWhitelisted: function(cmp) {
        return cmp && Ext.Array.some(this.whitelist, function(x) {
            return cmp.isXType(x);
        });
    },

    navigateIn: function(e) {
        var me = this,
            focusedCmp = me.focusedCmp,
            rootCmps,
            firstChild;

        if (!focusedCmp) {
            
            rootCmps = me.getRootComponents();
            if (rootCmps.length) {
                rootCmps[0].focus();
            }
        } else {
            
            
            firstChild = Ext.ComponentQuery.query('>:focusable', focusedCmp)[0];
            if (firstChild) {
                firstChild.focus();
            } else {
                
                if (Ext.isFunction(focusedCmp.onClick)) {
                    e.button = 0;
                    focusedCmp.onClick(e);
                    focusedCmp.focus();
                }
            }
        }
    },

    navigateOut: function(e) {
        var me = this,
            parent;

        if (!me.focusedCmp || !(parent = me.focusedCmp.up(':focusable'))) {
            me.focusEl.focus();
            return;
        }

        parent.focus();
    },

    navigateSiblings: function(e, source, parent) {
        var me = this,
            src = source || me,
            key = e.getKey(),
            EO = Ext.EventObject,
            goBack = e.shiftKey || key == EO.LEFT || key == EO.UP,
            checkWhitelist = key == EO.LEFT || key == EO.RIGHT || key == EO.UP || key == EO.DOWN,
            nextSelector = goBack ? 'prev' : 'next',
            idx, next, focusedCmp;

        focusedCmp = (src.focusedCmp && src.focusedCmp.comp) || src.focusedCmp;
        if (!focusedCmp && !parent) {
            return;
        }

        if (checkWhitelist && me.isWhitelisted(focusedCmp)) {
            return true;
        }

        parent = parent || focusedCmp.up();
        if (parent) {
            idx = focusedCmp ? Ext.Array.indexOf(parent.getRefItems(), focusedCmp) : -1;
            next = Ext.ComponentQuery.query('>:' + nextSelector + 'Focus(' + idx + ')', parent)[0];
            if (next && focusedCmp !== next) {
                next.focus();
                return next;
            }
        }
    },

    onComponentBlur: function(cmp, e) {
        var me = this;

        if (me.focusedCmp === cmp) {
            me.previousFocusedCmp = cmp;
            delete me.focusedCmp;
        }

        if (me.focusFrame) {
            me.focusFrame.hide();
        }
    },

    onComponentCreated: function(hash, id, cmp) {
        this.setFocus(cmp, true, this.options);
    },

    onComponentDestroy: function(cmp) {
        this.setFocus(cmp, false);
    },

    onComponentFocus: function(cmp, e) {
        var me = this,
            chain = me.focusChain;

        if (!Ext.ComponentQuery.is(cmp, ':focusable')) {
            me.clearComponent(cmp);

            
            
            
            
            if (chain[cmp.id]) {
                return;
            }

            
            var parent = cmp.up();
            if (parent) {
                
                
                
                chain[cmp.id] = true;
                parent.focus();
            }

            return;
        }

        
        me.focusChain = {};

        
        
        clearTimeout(me.cmpFocusDelay);
        if (arguments.length !== 2) {
            me.cmpFocusDelay = Ext.defer(me.onComponentFocus, 90, me, [cmp, e]);
            return;
        }

        if (me.fireEvent('beforecomponentfocus', me, cmp, me.previousFocusedCmp) === false) {
            me.clearComponent(cmp);
            return;
        }

        me.focusedCmp = cmp;

        
        if (me.shouldShowFocusFrame(cmp)) {
            var cls = '.' + me.focusFrameCls + '-',
                ff = me.focusFrame,
                fw = me.focusFrameWidth,
                box = cmp.el.getPageBox(),

            
            
            
                bt = box.top,
                bl = box.left,
                bw = box.width,
                bh = box.height,
                ft = ff.child(cls + 'top'),
                fb = ff.child(cls + 'bottom'),
                fl = ff.child(cls + 'left'),
                fr = ff.child(cls + 'right');

            ft.setWidth(bw - 2).setLeftTop(bl + 1, bt);
            fb.setWidth(bw - 2).setLeftTop(bl + 1, bt + bh - fw);
            fl.setHeight(bh - 2).setLeftTop(bl, bt + 1);
            fr.setHeight(bh - 2).setLeftTop(bl + bw - fw, bt + 1);

            ff.show();
        }

        me.fireEvent('componentfocus', me, cmp, me.previousFocusedCmp);
    },

    onComponentHide: function(cmp) {
        var me = this,
            CQ = Ext.ComponentQuery,
            cmpHadFocus = false,
            focusedCmp,
            parent;

        if (me.focusedCmp) {
            focusedCmp = CQ.query('[id=' + me.focusedCmp.id + ']', cmp)[0];
            cmpHadFocus = me.focusedCmp.id === cmp.id || focusedCmp;

            if (focusedCmp) {
                me.clearComponent(focusedCmp);
            }
        }

        me.clearComponent(cmp);

        if (cmpHadFocus) {
            parent = CQ.query('^:focusable', cmp)[0];
            if (parent) {
                parent.focus();
            }
        }
    },

    removeDOM: function() {
        var me = this;

        
        
        if (me.enabled || me.subscribers.length) {
            return;
        }

        Ext.destroy(
            me.focusEl,
            me.focusFrame
        );
        delete me.focusEl;
        delete me.focusFrame;
        delete me.focusFrameWidth;
    },

    
    removeXTypeFromWhitelist: function(xtype) {
        var me = this;

        if (Ext.isArray(xtype)) {
            Ext.Array.forEach(xtype, me.removeXTypeFromWhitelist, me);
            return;
        }

        Ext.Array.remove(me.whitelist, xtype);
    },

    setFocus: function(cmp, focusable, options) {
        var me = this,
            el, dom, data,

            needsTabIndex = function(n) {
                return !Ext.Array.contains(me.tabIndexWhitelist, n.tagName.toLowerCase())
                    && n.tabIndex <= 0;
            };

        options = options || {};

        
        if (!cmp.rendered) {
            cmp.on('afterrender', Ext.pass(me.setFocus, arguments, me), me, { single: true });
            return;
        }

        el = cmp.getFocusEl();
        dom = el.dom;

        
        if ((focusable && !me.focusData[cmp.id]) || (!focusable && me.focusData[cmp.id])) {
            if (focusable) {
                data = {
                    focusFrame: options.focusFrame
                };

                
                
                
                
                if (needsTabIndex(dom)) {
                    data.tabIndex = dom.tabIndex;
                    dom.tabIndex = -1;
                }

                el.on({
                    focus: data.focusFn = Ext.bind(me.onComponentFocus, me, [cmp], 0),
                    blur: data.blurFn = Ext.bind(me.onComponentBlur, me, [cmp], 0),
                    scope: me
                });
                cmp.on({
                    hide: me.onComponentHide,
                    close: me.onComponentHide,
                    beforedestroy: me.onComponentDestroy,
                    scope: me
                });

                me.focusData[cmp.id] = data;
            } else {
                data = me.focusData[cmp.id];
                if ('tabIndex' in data) {
                    dom.tabIndex = data.tabIndex;
                }
                el.un('focus', data.focusFn, me);
                el.un('blur', data.blurFn, me);
                cmp.un('hide', me.onComponentHide, me);
                cmp.un('close', me.onComponentHide, me);
                cmp.un('beforedestroy', me.onComponentDestroy, me);

                delete me.focusData[cmp.id];
            }
        }
    },

    setFocusAll: function(focusable, options) {
        var me = this,
            cmps = Ext.ComponentManager.all.getArray(),
            len = cmps.length,
            cmp,
            i = 0;

        for (; i < len; i++) {
            me.setFocus(cmps[i], focusable, options);
        }
    },

    setupSubscriberKeys: function(container, keys) {
        var me = this,
            el = container.getFocusEl(),
            scope = keys.scope,
            handlers = {
                backspace: me.focusLast,
                enter: me.navigateIn,
                esc: me.navigateOut,
                scope: me
            },

            navSiblings = function(e) {
                if (me.focusedCmp === container) {
                    
                    
                    
                    return me.navigateSiblings(e, me, container);
                } else {
                    return me.navigateSiblings(e);
                }
            };

        Ext.iterate(keys, function(key, cb) {
            handlers[key] = function(e) {
                var ret = navSiblings(e);

                if (Ext.isFunction(cb) && cb.call(scope || container, e, ret) === true) {
                    return true;
                }

                return ret;
            };
        }, me);

        return Ext.create('Ext.util.KeyNav', el, handlers);
    },

    shouldShowFocusFrame: function(cmp) {
        var me = this,
            opts = me.options || {};

        if (!me.focusFrame || !cmp) {
            return false;
        }

        
        if (opts.focusFrame) {
            return true;
        }

        if (me.focusData[cmp.id].focusFrame) {
            return true;
        }

        return false;
    },

    
    subscribe: function(container, options) {
        var me = this,
            EA = Ext.Array,
            data = {},
            subs = me.subscribers,

            
            
            
            safeSetFocus = function(cmp) {
                if (cmp.isContainer && !subs.containsKey(cmp.id)) {
                    EA.forEach(cmp.query('>'), safeSetFocus);
                    me.setFocus(cmp, true, options);
                    cmp.on('add', data.onAdd, me);
                } else if (!cmp.isContainer) {
                    me.setFocus(cmp, true, options);
                }
            };

        
        if (!container || !container.isContainer) {
            return;
        }

        if (!container.rendered) {
            container.on('afterrender', Ext.pass(me.subscribe, arguments, me), me, { single: true });
            return;
        }

        
        me.initDOM(options);

        
        data.keyNav = me.setupSubscriberKeys(container, options.keys);

        
        
        
        
        data.onAdd = function(ct, cmp, idx) {
            safeSetFocus(cmp);
        };
        container.on('beforedestroy', me.unsubscribe, me);

        
        safeSetFocus(container);

        
        subs.add(container.id, data);
    },

    
    unsubscribe: function(container) {
        var me = this,
            EA = Ext.Array,
            subs = me.subscribers,
            data,

            
            
            
            safeSetFocus = function(cmp) {
                if (cmp.isContainer && !subs.containsKey(cmp.id)) {
                    EA.forEach(cmp.query('>'), safeSetFocus);
                    me.setFocus(cmp, false);
                    cmp.un('add', data.onAdd, me);
                } else if (!cmp.isContainer) {
                    me.setFocus(cmp, false);
                }
            };

        if (!container || !subs.containsKey(container.id)) {
            return;
        }

        data = subs.get(container.id);
        data.keyNav.destroy();
        container.un('beforedestroy', me.unsubscribe, me);
        subs.removeAtKey(container.id);
        safeSetFocus(container);
        me.removeDOM();
    }
});

Ext.define('Ext.toolbar.Toolbar', {
    extend: 'Ext.container.Container',
    requires: [
        'Ext.toolbar.Fill',
        'Ext.layout.container.HBox',
        'Ext.layout.container.VBox',
        'Ext.FocusManager'
    ],
    uses: [
        'Ext.toolbar.Separator'
    ],
    alias: 'widget.toolbar',
    alternateClassName: 'Ext.Toolbar',
    
    isToolbar: true,
    baseCls  : Ext.baseCSSPrefix + 'toolbar',
    ariaRole : 'toolbar',
    
    defaultType: 'button',
    
    
    vertical: false,

    

    
    enableOverflow: false,
    
    
    trackMenus: true,
    
    itemCls: Ext.baseCSSPrefix + 'toolbar-item',
    
    initComponent: function() {
        var me = this,
            keys;

        
        if (!me.layout && me.enableOverflow) {
            me.layout = { overflowHandler: 'Menu' };
        }
        
        if (me.dock === 'right' || me.dock === 'left') {
            me.vertical = true;
        }

        me.layout = Ext.applyIf(Ext.isString(me.layout) ? {
            type: me.layout
        } : me.layout || {}, {
            type: me.vertical ? 'vbox' : 'hbox',
            align: me.vertical ? 'stretchmax' : 'middle'
        });
        
        if (me.vertical) {
            me.addClsWithUI('vertical');
        }
        
        
        if (me.ui === 'footer') {
            me.ignoreBorderManagement = true;
        }
        
        me.callParent();

        
        me.addEvents('overflowchange');
        
        
        keys = me.vertical ? ['up', 'down'] : ['left', 'right'];
        Ext.FocusManager.subscribe(me, {
            keys: keys
        });
    },

    

    
    lookupComponent: function(c) {
        if (Ext.isString(c)) {
            var shortcut = Ext.toolbar.Toolbar.shortcuts[c];
            if (shortcut) {
                c = {
                    xtype: shortcut
                };
            } else {
                c = {
                    xtype: 'tbtext',
                    text: c
                };
            }
            this.applyDefaults(c);
        }
        return this.callParent(arguments);
    },

    
    applyDefaults: function(c) {
        if (!Ext.isString(c)) {
            c = this.callParent(arguments);
            var d = this.internalDefaults;
            if (c.events) {
                Ext.applyIf(c.initialConfig, d);
                Ext.apply(c, d);
            } else {
                Ext.applyIf(c, d);
            }
        }
        return c;
    },

    
    trackMenu: function(item, remove) {
        if (this.trackMenus && item.menu) {
            var method = remove ? 'mun' : 'mon',
                me = this;

            me[method](item, 'menutriggerover', me.onButtonTriggerOver, me);
            me[method](item, 'menushow', me.onButtonMenuShow, me);
            me[method](item, 'menuhide', me.onButtonMenuHide, me);
        }
    },

    
    constructButton: function(item) {
        return item.events ? item : this.createComponent(item, item.split ? 'splitbutton' : this.defaultType);
    },

    
    onBeforeAdd: function(component) {
        if (component.is('field') || (component.is('button') && this.ui != 'footer')) {
            component.ui = component.ui + '-toolbar';
        }
        
        
        if (component instanceof Ext.toolbar.Separator) {
            component.setUI((this.vertical) ? 'vertical' : 'horizontal');
        }
        
        this.callParent(arguments);
    },

    
    onAdd: function(component) {
        this.callParent(arguments);

        this.trackMenu(component);
        if (this.disabled) {
            component.disable();
        }
    },

    
    onRemove: function(c) {
        this.callParent(arguments);
        this.trackMenu(c, true);
    },

    
    onButtonTriggerOver: function(btn){
        if (this.activeMenuBtn && this.activeMenuBtn != btn) {
            this.activeMenuBtn.hideMenu();
            btn.showMenu();
            this.activeMenuBtn = btn;
        }
    },

    
    onButtonMenuShow: function(btn) {
        this.activeMenuBtn = btn;
    },

    
    onButtonMenuHide: function(btn) {
        delete this.activeMenuBtn;
    }
}, function() {
    this.shortcuts = {
        '-' : 'tbseparator',
        ' ' : 'tbspacer',
        '->': 'tbfill'
    };
});

Ext.define('Ext.panel.AbstractPanel', {

    

    extend: 'Ext.container.Container',

    requires: ['Ext.util.MixedCollection', 'Ext.core.Element', 'Ext.toolbar.Toolbar'],

    

    
    baseCls : Ext.baseCSSPrefix + 'panel',

    

    
    
    
    
    

    isPanel: true,

    componentLayout: 'dock',

    renderTpl: ['<div class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl> {baseCls}-body-{ui}<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl></tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>></div>'],

    
    
     
    border: true,

    initComponent : function() {
        var me = this;
        
        me.addEvents(
            
            'bodyresize'
            
            
            
            
        );

        Ext.applyIf(me.renderSelectors, {
            body: '.' + me.baseCls + '-body'
        });
        
        
        
        
        if (me.frame && me.border && me.bodyBorder === undefined) {
            me.bodyBorder = false;
        }
        if (me.frame && me.border && (me.bodyBorder === false || me.bodyBorder === 0)) {
            me.manageBodyBorders = true;
        }
        
        me.callParent();
    },

    
    initItems : function() {
        var me = this,
            items = me.dockedItems;
            
        me.callParent();
        me.dockedItems = Ext.create('Ext.util.MixedCollection', false, me.getComponentId);
        if (items) {
            me.addDocked(items);
        }
    },

    
    getDockedComponent: function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }
        return this.dockedItems.get(comp);
    },

    
    getComponent: function(comp) {
        var component = this.callParent(arguments);
        if (component === undefined && !Ext.isNumber(comp)) {
            
            component = this.getDockedComponent(comp);
        }
        return component;
    },

    
    initBodyStyles: function() {
        var me = this,
            bodyStyle = me.bodyStyle,
            styles = [],
            Element = Ext.core.Element,
            prop;

        if (Ext.isFunction(bodyStyle)) {
            bodyStyle = bodyStyle();
        }
        if (Ext.isString(bodyStyle)) {
            styles = bodyStyle.split(';');
        } else {
            for (prop in bodyStyle) {
                if (bodyStyle.hasOwnProperty(prop)) {
                    styles.push(prop + ':' + bodyStyle[prop]);
                }
            }
        }

        if (me.bodyPadding !== undefined) {
            styles.push('padding: ' + Element.unitizeBox((me.bodyPadding === true) ? 5 : me.bodyPadding));
        }
        if (me.frame && me.bodyBorder) {
            if (!Ext.isNumber(me.bodyBorder)) {
                me.bodyBorder = 1;
            }
            styles.push('border-width: ' + Element.unitizeBox(me.bodyBorder));
        }
        delete me.bodyStyle;
        return styles.length ? styles.join(';') : undefined;
    },
    
    
    initBodyCls: function() {
        var me = this,
            cls = '',
            bodyCls = me.bodyCls;
        
        if (bodyCls) {
            Ext.each(bodyCls, function(v) {
                cls += " " + v;
            });
            delete me.bodyCls;
        }
        return cls.length > 0 ? cls : undefined;
    },
    
    
    initRenderData: function() {
        return Ext.applyIf(this.callParent(), {
            bodyStyle: this.initBodyStyles(),
            bodyCls: this.initBodyCls()
        });
    },

    
    addDocked : function(items, pos) {
        var me = this,
            i = 0,
            item, length;

        items = me.prepareItems(items);
        length = items.length;

        for (; i < length; i++) {
            item = items[i];
            item.dock = item.dock || 'top';

            
            if (me.border === false) {
                
            }

            if (pos !== undefined) {
                me.dockedItems.insert(pos + i, item);
            }
            else {
                me.dockedItems.add(item);
            }
            item.onAdded(me, i);
            me.onDockedAdd(item);
        }
        if (me.rendered && !me.suspendLayout) {
            me.doComponentLayout();
        }
        return items;
    },

    
    onDockedAdd : Ext.emptyFn,
    onDockedRemove : Ext.emptyFn,

    
    insertDocked : function(pos, items) {
        this.addDocked(items, pos);
    },

    
    removeDocked : function(item, autoDestroy) {
        var me = this,
            layout,
            hasLayout;
            
        if (!me.dockedItems.contains(item)) {
            return item;
        }

        layout = me.componentLayout;
        hasLayout = layout && me.rendered;

        if (hasLayout) {
            layout.onRemove(item);
        }

        me.dockedItems.remove(item);
        item.onRemoved();
        me.onDockedRemove(item);

        if (autoDestroy === true || (autoDestroy !== false && me.autoDestroy)) {
            item.destroy();
        }

        if (hasLayout && !autoDestroy) {
            layout.afterRemove(item);
        }
        
        if (!this.destroying) {
            me.doComponentLayout();
        }

        return item;
    },

    
    getDockedItems : function(cqSelector) {
        var me = this,
            
            
            defaultWeight = { top: 1, left: 3, right: 5, bottom: 7 },
            dockedItems;

        if (me.dockedItems && me.dockedItems.items.length) {
            
            if (cqSelector) {
                dockedItems = Ext.ComponentQuery.query(cqSelector, me.dockedItems.items);
            } else {
                dockedItems = me.dockedItems.items.slice();
            }

            Ext.Array.sort(dockedItems, function(a, b) {
                
                
                var aw = a.weight || defaultWeight[a.dock],
                    bw = b.weight || defaultWeight[b.dock];
                if (Ext.isNumber(aw) && Ext.isNumber(bw)) {
                    return aw - bw;
                }
                return 0;
            });
            
            return dockedItems;
        }
        return [];
    },
    
    
    addUIClsToElement: function(cls, force) {
        var me = this;
        
        me.callParent(arguments);
        
        if (!force && me.rendered) {
            me.body.addCls(Ext.baseCSSPrefix + cls);
            me.body.addCls(me.baseCls + '-body-' + cls);
            me.body.addCls(me.baseCls + '-body-' + me.ui + '-' + cls);
        }
    },
    
    
    removeUIClsFromElement: function(cls, force) {
        var me = this;
        
        me.callParent(arguments);
        
        if (!force && me.rendered) {
            me.body.removeCls(Ext.baseCSSPrefix + cls);
            me.body.removeCls(me.baseCls + '-body-' + cls);
            me.body.removeCls(me.baseCls + '-body-' + me.ui + '-' + cls);
        }
    },
    
    
    addUIToElement: function(force) {
        var me = this;
        
        me.callParent(arguments);
        
        if (!force && me.rendered) {
            me.body.addCls(me.baseCls + '-body-' + me.ui);
        }
    },
    
    
    removeUIFromElement: function() {
        var me = this;
        
        me.callParent(arguments);
        
        if (me.rendered) {
            me.body.removeCls(me.baseCls + '-body-' + me.ui);
        }
    },

    
    getTargetEl : function() {
        return this.body;
    },

    getRefItems: function(deep) {
        var items = this.callParent(arguments),
            
            dockedItems = this.getDockedItems(deep ? '*,* *' : undefined),
            ln = dockedItems.length,
            i = 0,
            item;
        
        
        for (; i < ln; i++) {
            item = dockedItems[i];
            if (item.dock === 'right' || item.dock === 'bottom') {
                break;
            }
        }
        
        
        
        
        return dockedItems.splice(0, i).concat(items).concat(dockedItems);
    },

    beforeDestroy: function(){
        var docked = this.dockedItems,
            c;

        if (docked) {
            while ((c = docked.first())) {
                this.removeDocked(c, true);
            }
        }
        this.callParent();
    },
    
    setBorder: function(border) {
        var me = this;
        me.border = (border !== undefined) ? border : true;
        if (me.rendered) {
            me.doComponentLayout();
        }
    }
});

Ext.define('Ext.panel.Header', {
    extend: 'Ext.container.Container',
    uses: ['Ext.panel.Tool', 'Ext.draw.Component', 'Ext.util.CSS'],
    alias: 'widget.header',

    isHeader       : true,
    defaultType    : 'tool',
    indicateDrag   : false,
    weight         : -1,

    renderTpl: ['<div class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl><tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl></tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>></div>'],

    initComponent: function() {
        var me = this,
            rule,
            style,
            titleTextEl,
            ui;

        me.indicateDragCls = me.baseCls + '-draggable';
        me.title = me.title || '&#160;';
        me.tools = me.tools || [];
        me.items = me.items || [];
        me.orientation = me.orientation || 'horizontal';
        me.dock = (me.dock) ? me.dock : (me.orientation == 'horizontal') ? 'top' : 'left';

        
        
        me.addClsWithUI(me.orientation);
        me.addClsWithUI(me.dock);

        Ext.applyIf(me.renderSelectors, {
            body: '.' + me.baseCls + '-body'
        });

        
        if (!Ext.isEmpty(me.iconCls)) {
            me.initIconCmp();
            me.items.push(me.iconCmp);
        }

        
        if (me.orientation == 'vertical') {
            
            if (Ext.isIE6 || Ext.isIE7) {
                me.width = this.width || 24;
            } else if (Ext.isIEQuirks) {
                me.width = this.width || 25;
            }

            me.layout = {
                type : 'vbox',
                align: 'center',
                clearInnerCtOnLayout: true,
                bindToOwnerCtContainer: false
            };
            me.textConfig = {
                cls: me.baseCls + '-text',
                type: 'text',
                text: me.title,
                rotate: {
                    degrees: 90
                }
            };
            ui = me.ui;
            if (Ext.isArray(ui)) {
                ui = ui[0];
            }
            rule = Ext.util.CSS.getRule('.' + me.baseCls + '-text-' + ui);
            if (rule) {
                style = rule.style;
            }
            if (style) {
                Ext.apply(me.textConfig, {
                    'font-family': style.fontFamily,
                    'font-weight': style.fontWeight,
                    'font-size': style.fontSize,
                    fill: style.color
                });
            }
            me.titleCmp = Ext.create('Ext.draw.Component', {
                ariaRole  : 'heading',
                focusable: false,
                viewBox: false,
                autoSize: true,
                margins: '5 0 0 0',
                items: [ me.textConfig ],
                renderSelectors: {
                    textEl: '.' + me.baseCls + '-text'
                }
            });
        } else {
            me.layout = {
                type : 'hbox',
                align: 'middle',
                clearInnerCtOnLayout: true,
                bindToOwnerCtContainer: false
            };
            me.titleCmp = Ext.create('Ext.Component', {
                xtype     : 'component',
                ariaRole  : 'heading',
                focusable: false,
                renderTpl : ['<span class="{cls}-text {cls}-text-{ui}">{title}</span>'],
                renderData: {
                    title: me.title,
                    cls  : me.baseCls,
                    ui   : me.ui
                },
                renderSelectors: {
                    textEl: '.' + me.baseCls + '-text'
                }
            });
        }
        me.items.push(me.titleCmp);

        
        me.items.push({
            xtype: 'component',
            html : '&nbsp;',
            flex : 1,
            focusable: false
        });

        
        me.items = me.items.concat(me.tools);
        this.callParent();
    },

    initIconCmp: function() {
        this.iconCmp = Ext.create('Ext.Component', {
            focusable: false,
            renderTpl : ['<img alt="" src="{blank}" class="{cls}-icon {iconCls}"/>'],
            renderData: {
                blank  : Ext.BLANK_IMAGE_URL,
                cls    : this.baseCls,
                iconCls: this.iconCls,
                orientation: this.orientation
            },
            renderSelectors: {
                iconEl: '.' + this.baseCls + '-icon'
            },
            iconCls: this.iconCls
        });
    },

    afterRender: function() {
        var me = this;

        me.el.unselectable();
        if (me.indicateDrag) {
            me.el.addCls(me.indicateDragCls);
        }
        me.mon(me.el, {
            click: me.onClick,
            scope: me
        });
        me.callParent();
    },

    afterLayout: function() {
        var me = this;
        me.callParent(arguments);

        
        if (Ext.isIE7) {
            me.el.repaint();
        }
    },

    
    addUIClsToElement: function(cls, force) {
        var me = this;

        me.callParent(arguments);

        if (!force && me.rendered) {
            me.body.addCls(me.baseCls + '-body-' + cls);
            me.body.addCls(me.baseCls + '-body-' + me.ui + '-' + cls);
        }
    },

    
    removeUIClsFromElement: function(cls, force) {
        var me = this;

        me.callParent(arguments);

        if (!force && me.rendered) {
            me.body.removeCls(me.baseCls + '-body-' + cls);
            me.body.removeCls(me.baseCls + '-body-' + me.ui + '-' + cls);
        }
    },

    
    addUIToElement: function(force) {
        var me = this;

        me.callParent(arguments);

        if (!force && me.rendered) {
            me.body.addCls(me.baseCls + '-body-' + me.ui);
        }

        if (!force && me.titleCmp && me.titleCmp.rendered && me.titleCmp.textEl) {
            me.titleCmp.textEl.addCls(me.baseCls + '-text-' + me.ui);
        }
    },

    
    removeUIFromElement: function() {
        var me = this;

        me.callParent(arguments);

        if (me.rendered) {
            me.body.removeCls(me.baseCls + '-body-' + me.ui);
        }

        if (me.titleCmp && me.titleCmp.rendered && me.titleCmp.textEl) {
            me.titleCmp.textEl.removeCls(me.baseCls + '-text-' + me.ui);
        }
    },

    onClick: function(e) {
        if (!e.getTarget(Ext.baseCSSPrefix + 'tool')) {
            this.fireEvent('click', e);
        }
    },

    getTargetEl: function() {
        return this.body || this.frameBody || this.el;
    },

    
    setTitle: function(title) {
        var me = this;
        if (me.rendered) {
            if (me.titleCmp.rendered) {
                if (me.titleCmp.surface) {
                    me.title = title || '';
                    var sprite = me.titleCmp.surface.items.items[0],
                        surface = me.titleCmp.surface;

                    surface.remove(sprite);
                    me.textConfig.type = 'text';
                    me.textConfig.text = title;
                    sprite = surface.add(me.textConfig);
                    sprite.setAttributes({
                        rotate: {
                            degrees: 90
                        }
                    }, true);
                    me.titleCmp.autoSizeSurface();
                } else {
                    me.title = title || '&#160;';
                    me.titleCmp.textEl.update(me.title);
                }
            } else {
                me.titleCmp.on({
                    render: function() {
                        me.setTitle(title);
                    },
                    single: true
                });
            }
        } else {
            me.on({
                render: function() {
                    me.layout.layout();
                    me.setTitle(title);
                },
                single: true
            });
        }
    },

    
    setIconCls: function(cls) {
        this.iconCls = cls;
        if (!this.iconCmp) {
            this.initIconCmp();
            this.insert(0, this.iconCmp);
        }
        else {
            if (!cls || !cls.length) {
                this.iconCmp.destroy();
            }
            else {
                var iconCmp = this.iconCmp,
                    el      = iconCmp.iconEl;

                el.removeCls(iconCmp.iconCls);
                el.addCls(cls);
                iconCmp.iconCls = cls;
            }
        }
    },

    
    addTool: function(tool) {
        this.tools.push(this.add(tool));
    },

    
    onAdd: function(component, index) {
        this.callParent([arguments]);
        if (component instanceof Ext.panel.Tool) {
            component.bindTo(this.ownerCt);
            this.tools[component.type] = component;
        }
    }
});


Ext.define('Ext.fx.target.Element', {

    
    
    extend: 'Ext.fx.target.Target',
    
    

    type: 'element',

    getElVal: function(el, attr, val) {
        if (val == undefined) {
            if (attr === 'x') {
                val = el.getX();
            }
            else if (attr === 'y') {
                val = el.getY();
            }
            else if (attr === 'scrollTop') {
                val = el.getScroll().top;
            }
            else if (attr === 'scrollLeft') {
                val = el.getScroll().left;
            }
            else if (attr === 'height') {
                val = el.getHeight();
            }
            else if (attr === 'width') {
                val = el.getWidth();
            }
            else {
                val = el.getStyle(attr);
            }
        }
        return val;
    },

    getAttr: function(attr, val) {
        var el = this.target;
        return [[ el, this.getElVal(el, attr, val)]];
    },

    setAttr: function(targetData) {
        var target = this.target,
            ln = targetData.length,
            attrs, attr, o, i, j, ln2, element, value;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                if (attrs.hasOwnProperty(attr)) {
                    ln2 = attrs[attr].length;
                    for (j = 0; j < ln2; j++) {
                        o = attrs[attr][j];
                        element = o[0];
                        value = o[1];
                        if (attr === 'x') {
                            element.setX(value);
                        }
                        else if (attr === 'y') {
                            element.setY(value);
                        }
                        else if (attr === 'scrollTop') {
                            element.scrollTo('top', value);
                        }
                        else if (attr === 'scrollLeft') {
                            element.scrollTo('left',value);
                        }
                        else {
                            element.setStyle(attr, value);
                        }
                    }
                }
            }
        }
    }
});


Ext.define('Ext.fx.target.CompositeElement', {

    

    extend: 'Ext.fx.target.Element',

    

    isComposite: true,
    
    constructor: function(target) {
        target.id = target.id || Ext.id(null, 'ext-composite-');
        this.callParent([target]);
    },

    getAttr: function(attr, val) {
        var out = [],
            target = this.target;
        target.each(function(el) {
            out.push([el, this.getElVal(el, attr, val)]);
        }, this);
        return out;
    }
});



Ext.define('Ext.fx.Manager', {

    

    singleton: true,

    requires: ['Ext.util.MixedCollection',
               'Ext.fx.target.Element',
               'Ext.fx.target.CompositeElement',
               'Ext.fx.target.Sprite',
               'Ext.fx.target.CompositeSprite',
               'Ext.fx.target.Component'],

    mixins: {
        queue: 'Ext.fx.Queue'
    },

    

    constructor: function() {
        this.items = Ext.create('Ext.util.MixedCollection');
        this.mixins.queue.constructor.call(this);

        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    },

    
    interval: 16,

    
    forceJS: true,

    
    createTarget: function(target) {
        var me = this,
            useCSS3 = !me.forceJS && Ext.supports.Transitions,
            targetObj;

        me.useCSS3 = useCSS3;

        
        if (Ext.isString(target)) {
            target = Ext.get(target);
        }
        
        if (target && target.tagName) {
            target = Ext.get(target);
            targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
            me.targets.add(targetObj);
            return targetObj;
        }
        if (Ext.isObject(target)) {
            
            if (target.dom) {
                targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
            }
            
            else if (target.isComposite) {
                targetObj = Ext.create('Ext.fx.target.' + 'CompositeElement' + (useCSS3 ? 'CSS' : ''), target);
            }
            
            else if (target.isSprite) {
                targetObj = Ext.create('Ext.fx.target.Sprite', target);
            }
            
            else if (target.isCompositeSprite) {
                targetObj = Ext.create('Ext.fx.target.CompositeSprite', target);
            }
            
            else if (target.isComponent) {
                targetObj = Ext.create('Ext.fx.target.Component', target);
            }
            else if (target.isAnimTarget) {
                return target;
            }
            else {
                return null;
            }
            me.targets.add(targetObj);
            return targetObj;
        }
        else {
            return null;
        }
    },

    
    addAnim: function(anim) {
        var items = this.items,
            task = this.task;
        
        
        
        
        
        
        
        
        
        
        
        

        items.add(anim);

        
        if (!task && items.length) {
            task = this.task = {
                run: this.runner,
                interval: this.interval,
                scope: this
            };
            Ext.TaskManager.start(task);
        }

        
        
        
        
    },

    
    removeAnim: function(anim) {
        
        var items = this.items,
            task = this.task;
        items.remove(anim);
        
        if (task && !items.length) {
            Ext.TaskManager.stop(task);
            delete this.task;
        }
    },

    
    startingFilter: function(o) {
        return o.paused === false && o.running === false && o.iterations > 0;
    },

    
    runningFilter: function(o) {
        return o.paused === false && o.running === true && o.isAnimator !== true;
    },

    
    runner: function() {
        var me = this,
            items = me.items;

        me.targetData = {};
        me.targetArr = {};

        
        me.timestamp = new Date();

        
        items.filterBy(me.startingFilter).each(me.startAnim, me);

        
        items.filterBy(me.runningFilter).each(me.runAnim, me);

        
        me.applyPendingAttrs();
    },

    
    startAnim: function(anim) {
        anim.start(this.timestamp);
    },

    
    runAnim: function(anim) {
        if (!anim) {
            return;
        }
        var me = this,
            targetId = anim.target.getId(),
            useCSS3 = me.useCSS3 && anim.target.type == 'element',
            elapsedTime = me.timestamp - anim.startTime,
            target, o;

        this.collectTargetData(anim, elapsedTime, useCSS3);

        
        
        if (useCSS3) {
            
            anim.target.setAttr(me.targetData[targetId], true);

            
            me.targetData[targetId] = [];
            me.collectTargetData(anim, anim.duration, useCSS3);

            
            anim.paused = true;

            target = anim.target.target;
            
            if (anim.target.isComposite) {
                target = anim.target.target.last();
            }

            
            o = {};
            o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
            o.scope = anim;
            o.single = true;
            target.on(o);
        }
        
        else if (elapsedTime >= anim.duration) {
            me.applyPendingAttrs(true);
            delete me.targetData[targetId];
            delete me.targetArr[targetId];
            anim.lastFrame();
        }
    },

    
    collectTargetData: function(anim, elapsedTime, useCSS3) {
        var targetId = anim.target.getId(),
            targetData = this.targetData[targetId],
            data;
        
        if (!targetData) {
            targetData = this.targetData[targetId] = [];
            this.targetArr[targetId] = anim.target;
        }

        data = {
            duration: anim.duration,
            easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
            attrs: {}
        };
        Ext.apply(data.attrs, anim.runAnim(elapsedTime));
        targetData.push(data);
    },

    
    applyPendingAttrs: function(isLastFrame) {
        var targetData = this.targetData,
            targetArr = this.targetArr,
            targetId;
        for (targetId in targetData) {
            if (targetData.hasOwnProperty(targetId)) {
                targetArr[targetId].setAttr(targetData[targetId], false, isLastFrame);
            }
        }
    }
});


Ext.define('Ext.fx.Animator', {

    

    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: ['Ext.fx.Manager'],

    

    isAnimator: true,

    
    duration: 250,

    
    delay: 0,

    
    delayStart: 0,

    
    dynamic: false,

    
    easing: 'ease',

    
    running: false,

    
    paused: false,

    
    damper: 1,

    
    iterations: 1,

    
    currentIteration: 0,

    
    keyframeStep: 0,

    
    animKeyFramesRE: /^(from|to|\d+%?)$/,

    

     
    constructor: function(config) {
        var me = this;
        config = Ext.apply(me, config || {});
        me.config = config;
        me.id = Ext.id(null, 'ext-animator-');
        me.addEvents(
            
            'beforeanimate',
            
            'keyframe',
            
            'afteranimate'
        );
        me.mixins.observable.constructor.call(me, config);
        me.timeline = [];
        me.createTimeline(me.keyframes);
        if (me.target) {
            me.applyAnimator(me.target);
            Ext.fx.Manager.addAnim(me);
        }
    },

    
    sorter: function (a, b) {
        return a.pct - b.pct;
    },

    
    createTimeline: function(keyframes) {
        var me = this,
            attrs = [],
            to = me.to || {},
            duration = me.duration,
            prevMs, ms, i, ln, pct, anim, nextAnim, attr;

        for (pct in keyframes) {
            if (keyframes.hasOwnProperty(pct) && me.animKeyFramesRE.test(pct)) {
                attr = {attrs: Ext.apply(keyframes[pct], to)};
                
                if (pct == "from") {
                    pct = 0;
                }
                else if (pct == "to") {
                    pct = 100;
                }
                
                attr.pct = parseInt(pct, 10);
                attrs.push(attr);
            }
        }
        
        Ext.Array.sort(attrs, me.sorter);
        
        
        
        

        ln = attrs.length;
        for (i = 0; i < ln; i++) {
            prevMs = (attrs[i - 1]) ? duration * (attrs[i - 1].pct / 100) : 0;
            ms = duration * (attrs[i].pct / 100);
            me.timeline.push({
                duration: ms - prevMs,
                attrs: attrs[i].attrs
            });
        }
    },

    
    applyAnimator: function(target) {
        var me = this,
            anims = [],
            timeline = me.timeline,
            reverse = me.reverse,
            ln = timeline.length,
            anim, easing, damper, initial, attrs, lastAttrs, i;

        if (me.fireEvent('beforeanimate', me) !== false) {
            for (i = 0; i < ln; i++) {
                anim = timeline[i];
                attrs = anim.attrs;
                easing = attrs.easing || me.easing;
                damper = attrs.damper || me.damper;
                delete attrs.easing;
                delete attrs.damper;
                anim = Ext.create('Ext.fx.Anim', {
                    target: target,
                    easing: easing,
                    damper: damper,
                    duration: anim.duration,
                    paused: true,
                    to: attrs
                });
                anims.push(anim);
            }
            me.animations = anims;
            me.target = anim.target;
            for (i = 0; i < ln - 1; i++) {
                anim = anims[i];
                anim.nextAnim = anims[i + 1];
                anim.on('afteranimate', function() {
                    this.nextAnim.paused = false;
                });
                anim.on('afteranimate', function() {
                    this.fireEvent('keyframe', this, ++this.keyframeStep);
                }, me);
            }
            anims[ln - 1].on('afteranimate', function() {
                this.lastFrame();
            }, me);
        }
    },

    
    start: function(startTime) {
        var me = this,
            delay = me.delay,
            delayStart = me.delayStart,
            delayDelta;
        if (delay) {
            if (!delayStart) {
                me.delayStart = startTime;
                return;
            }
            else {
                delayDelta = startTime - delayStart;
                if (delayDelta < delay) {
                    return;
                }
                else {
                    
                    startTime = new Date(delayStart.getTime() + delay);
                }
            }
        }
        if (me.fireEvent('beforeanimate', me) !== false) {
            me.startTime = startTime;
            me.running = true;
            me.animations[me.keyframeStep].paused = false;
        }
    },

    
    lastFrame: function() {
        var me = this,
            iter = me.iterations,
            iterCount = me.currentIteration;

        iterCount++;
        if (iterCount < iter) {
            me.startTime = new Date();
            me.currentIteration = iterCount;
            me.keyframeStep = 0;
            me.applyAnimator(me.target);
            me.animations[me.keyframeStep].paused = false;
        }
        else {
            me.currentIteration = 0;
            me.end();
        }
    },

    
    end: function() {
        var me = this;
        me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime);
    }
});

Ext.ns('Ext.fx');

Ext.require('Ext.fx.CubicBezier', function() {
    var math = Math,
        pi = math.PI,
        pow = math.pow,
        sin = math.sin,
        sqrt = math.sqrt,
        abs = math.abs,
        backInSeed = 1.70158;
    Ext.fx.Easing = {
        
        
        
        
        
        
        
        
    };

    Ext.apply(Ext.fx.Easing, {
        linear: function(n) {
            return n;
        },
        ease: function(n) {
            var q = 0.07813 - n / 2,
                alpha = -0.25,
                Q = sqrt(0.0066 + q * q),
                x = Q - q,
                X = pow(abs(x), 1/3) * (x < 0 ? -1 : 1),
                y = -Q - q,
                Y = pow(abs(y), 1/3) * (y < 0 ? -1 : 1),
                t = X + Y + 0.25;
            return pow(1 - t, 2) * 3 * t * 0.1 + (1 - t) * 3 * t * t + t * t * t;
        },
        easeIn: function (n) {
            return pow(n, 1.7);
        },
        easeOut: function (n) {
            return pow(n, 0.48);
        },
        easeInOut: function(n) {
            var q = 0.48 - n / 1.04,
                Q = sqrt(0.1734 + q * q),
                x = Q - q,
                X = pow(abs(x), 1/3) * (x < 0 ? -1 : 1),
                y = -Q - q,
                Y = pow(abs(y), 1/3) * (y < 0 ? -1 : 1),
                t = X + Y + 0.5;
            return (1 - t) * 3 * t * t + t * t * t;
        },
        backIn: function (n) {
            return n * n * ((backInSeed + 1) * n - backInSeed);
        },
        backOut: function (n) {
            n = n - 1;
            return n * n * ((backInSeed + 1) * n + backInSeed) + 1;
        },
        elasticIn: function (n) {
            if (n === 0 || n === 1) {
                return n;
            }
            var p = 0.3,
                s = p / 4;
            return pow(2, -10 * n) * sin((n - s) * (2 * pi) / p) + 1;
        },
        elasticOut: function (n) {
            return 1 - Ext.fx.Easing.elasticIn(1 - n);
        },
        bounceIn: function (n) {
            return 1 - Ext.fx.Easing.bounceOut(1 - n);
        },
        bounceOut: function (n) {
            var s = 7.5625,
                p = 2.75,
                l;
            if (n < (1 / p)) {
                l = s * n * n;
            } else {
                if (n < (2 / p)) {
                    n -= (1.5 / p);
                    l = s * n * n + 0.75;
                } else {
                    if (n < (2.5 / p)) {
                        n -= (2.25 / p);
                        l = s * n * n + 0.9375;
                    } else {
                        n -= (2.625 / p);
                        l = s * n * n + 0.984375;
                    }
                }
            }
            return l;
        }
    });
    Ext.apply(Ext.fx.Easing, {
        'back-in': Ext.fx.Easing.backIn,
        'back-out': Ext.fx.Easing.backOut,
        'ease-in': Ext.fx.Easing.easeIn,
        'ease-out': Ext.fx.Easing.easeOut,
        'elastic-in': Ext.fx.Easing.elasticIn,
        'elastic-out': Ext.fx.Easing.elasticIn,
        'bounce-in': Ext.fx.Easing.bounceIn,
        'bounce-out': Ext.fx.Easing.bounceOut,
        'ease-in-out': Ext.fx.Easing.easeInOut
    });
});


Ext.define('Ext.draw.Draw', {
    

    singleton: true,

    requires: ['Ext.draw.Color'],

    

    pathToStringRE: /,?([achlmqrstvxz]),?/gi,
    pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
    pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
    stopsRE: /^(\d+%?)$/,
    radian: Math.PI / 180,

    availableAnimAttrs: {
        along: "along",
        blur: null,
        "clip-rect": "csv",
        cx: null,
        cy: null,
        fill: "color",
        "fill-opacity": null,
        "font-size": null,
        height: null,
        opacity: null,
        path: "path",
        r: null,
        rotation: "csv",
        rx: null,
        ry: null,
        scale: "csv",
        stroke: "color",
        "stroke-opacity": null,
        "stroke-width": null,
        translation: "csv",
        width: null,
        x: null,
        y: null
    },

    is: function(o, type) {
        type = String(type).toLowerCase();
        return (type == "object" && o === Object(o)) ||
            (type == "undefined" && typeof o == type) ||
            (type == "null" && o === null) ||
            (type == "array" && Array.isArray && Array.isArray(o)) ||
            (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
    },

    ellipsePath: function(sprite) {
        var attr = sprite.attr;
        return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z", attr.x, attr.y - attr.ry, attr.rx, attr.ry, attr.y + attr.ry);
    },

    rectPath: function(sprite) {
        var attr = sprite.attr;
        if (attr.radius) {
            return Ext.String.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", attr.x + attr.radius, attr.y, attr.width - attr.radius * 2, attr.radius, -attr.radius, attr.height - attr.radius * 2, attr.radius * 2 - attr.width, attr.radius * 2 - attr.height);
        }
        else {
            return Ext.String.format("M{0},{1}l{2},0,0,{3},{4},0z", attr.x, attr.y, attr.width, attr.height, -attr.width);
        }
    },

    
    path2string: function () {
        return this.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
    },

    
    pathToString: function(arrayPath) {
        return arrayPath.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");
    },

    parsePathString: function (pathString) {
        if (!pathString) {
            return null;
        }
        var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
            data = [],
            me = this;
        if (me.is(pathString, "array") && me.is(pathString[0], "array")) { 
            data = me.pathClone(pathString);
        }
        if (!data.length) {
            String(pathString).replace(me.pathCommandRE, function (a, b, c) {
                var params = [],
                    name = b.toLowerCase();
                c.replace(me.pathValuesRE, function (a, b) {
                    b && params.push(+b);
                });
                if (name == "m" && params.length > 2) {
                    data.push([b].concat(params.splice(0, 2)));
                    name = "l";
                    b = (b == "m") ? "l" : "L";
                }
                while (params.length >= paramCounts[name]) {
                    data.push([b].concat(params.splice(0, paramCounts[name])));
                    if (!paramCounts[name]) {
                        break;
                    }
                }
            });
        }
        data.toString = me.path2string;
        return data;
    },

    mapPath: function (path, matrix) {
        if (!matrix) {
            return path;
        }
        var x, y, i, ii, j, jj, pathi;
        path = this.path2curve(path);
        for (i = 0, ii = path.length; i < ii; i++) {
            pathi = path[i];
            for (j = 1, jj = pathi.length; j < jj-1; j += 2) {
                x = matrix.x(pathi[j], pathi[j + 1]);
                y = matrix.y(pathi[j], pathi[j + 1]);
                pathi[j] = x;
                pathi[j + 1] = y;
            }
        }
        return path;
    },

    pathClone: function(pathArray) {
        var res = [],
            j, jj, i, ii;
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { 
            pathArray = this.parsePathString(pathArray);
        }
        for (i = 0, ii = pathArray.length; i < ii; i++) {
            res[i] = [];
            for (j = 0, jj = pathArray[i].length; j < jj; j++) {
                res[i][j] = pathArray[i][j];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    pathToAbsolute: function (pathArray) {
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { 
            pathArray = this.parsePathString(pathArray);
        }
        var res = [],
            x = 0,
            y = 0,
            mx = 0,
            my = 0,
            i = 0,
            ln = pathArray.length,
            r, pathSegment, j, ln2;
        
        if (ln && pathArray[0][0] == "M") {
            x = +pathArray[0][1];
            y = +pathArray[0][2];
            mx = x;
            my = y;
            i++;
            res[0] = ["M", x, y];
        }
        for (; i < ln; i++) {
            r = res[i] = [];
            pathSegment = pathArray[i];
            if (pathSegment[0] != pathSegment[0].toUpperCase()) {
                r[0] = pathSegment[0].toUpperCase();
                switch (r[0]) {
                    
                    case "A":
                        r[1] = pathSegment[1];
                        r[2] = pathSegment[2];
                        r[3] = pathSegment[3];
                        r[4] = pathSegment[4];
                        r[5] = pathSegment[5];
                        r[6] = +(pathSegment[6] + x);
                        r[7] = +(pathSegment[7] + y);
                        break;
                    
                    case "V":
                        r[1] = +pathSegment[1] + y;
                        break;
                    
                    case "H":
                        r[1] = +pathSegment[1] + x;
                        break;
                    case "M":
                    
                        mx = +pathSegment[1] + x;
                        my = +pathSegment[2] + y;
                    default:
                        j = 1;
                        ln2 = pathSegment.length;
                        for (; j < ln2; j++) {
                            r[j] = +pathSegment[j] + ((j % 2) ? x : y);
                        }
                }
            }
            else {
                j = 0;
                ln2 = pathSegment.length;
                for (; j < ln2; j++) {
                    res[i][j] = pathSegment[j];
                }
            }
            switch (r[0]) {
                
                case "Z":
                    x = mx;
                    y = my;
                    break;
                
                case "H":
                    x = r[1];
                    break;
                
                case "V":
                    y = r[1];
                    break;
                
                case "M":
                    pathSegment = res[i];
                    ln2 = pathSegment.length;
                    mx = pathSegment[ln2 - 2];
                    my = pathSegment[ln2 - 1];
                default:
                    pathSegment = res[i];
                    ln2 = pathSegment.length;
                    x = pathSegment[ln2 - 2];
                    y = pathSegment[ln2 - 1];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    
    pathToRelative: function (pathArray) {
        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) {
            pathArray = this.parsePathString(pathArray);
        }
        var res = [],
            x = 0,
            y = 0,
            mx = 0,
            my = 0,
            start = 0;
        if (pathArray[0][0] == "M") {
            x = pathArray[0][1];
            y = pathArray[0][2];
            mx = x;
            my = y;
            start++;
            res.push(["M", x, y]);
        }
        for (var i = start, ii = pathArray.length; i < ii; i++) {
            var r = res[i] = [],
                pa = pathArray[i];
            if (pa[0] != pa[0].toLowerCase()) {
                r[0] = pa[0].toLowerCase();
                switch (r[0]) {
                    case "a":
                        r[1] = pa[1];
                        r[2] = pa[2];
                        r[3] = pa[3];
                        r[4] = pa[4];
                        r[5] = pa[5];
                        r[6] = +(pa[6] - x).toFixed(3);
                        r[7] = +(pa[7] - y).toFixed(3);
                        break;
                    case "v":
                        r[1] = +(pa[1] - y).toFixed(3);
                        break;
                    case "m":
                        mx = pa[1];
                        my = pa[2];
                    default:
                        for (var j = 1, jj = pa.length; j < jj; j++) {
                            r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
                        }
                }
            } else {
                r = res[i] = [];
                if (pa[0] == "m") {
                    mx = pa[1] + x;
                    my = pa[2] + y;
                }
                for (var k = 0, kk = pa.length; k < kk; k++) {
                    res[i][k] = pa[k];
                }
            }
            var len = res[i].length;
            switch (res[i][0]) {
                case "z":
                    x = mx;
                    y = my;
                    break;
                case "h":
                    x += +res[i][len - 1];
                    break;
                case "v":
                    y += +res[i][len - 1];
                    break;
                default:
                    x += +res[i][len - 2];
                    y += +res[i][len - 1];
            }
        }
        res.toString = this.path2string;
        return res;
    },

    
    path2curve: function (path) {
        var me = this,
            points = me.pathToAbsolute(path),
            ln = points.length,
            attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            i, seg, segLn, point;
            
        for (i = 0; i < ln; i++) {
            points[i] = me.command2curve(points[i], attrs);
            if (points[i].length > 7) {
                    points[i].shift();
                    point = points[i];
                    while (point.length) {
                        points.splice(i++, 0, ["C"].concat(point.splice(0, 6)));
                    }
                    points.splice(i, 1);
                    ln = points.length;
                }
            seg = points[i];
            segLn = seg.length;
            attrs.x = seg[segLn - 2];
            attrs.y = seg[segLn - 1];
            attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x;
            attrs.by = parseFloat(seg[segLn - 3]) || attrs.y;
        }
        return points;
    },
    
    interpolatePaths: function (path, path2) {
        var me = this,
            p = me.pathToAbsolute(path),
            p2 = me.pathToAbsolute(path2),
            attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
            fixArc = function (pp, i) {
                if (pp[i].length > 7) {
                    pp[i].shift();
                    var pi = pp[i];
                    while (pi.length) {
                        pp.splice(i++, 0, ["C"].concat(pi.splice(0, 6)));
                    }
                    pp.splice(i, 1);
                    ii = Math.max(p.length, p2.length || 0);
                }
            },
            fixM = function (path1, path2, a1, a2, i) {
                if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
                    path2.splice(i, 0, ["M", a2.x, a2.y]);
                    a1.bx = 0;
                    a1.by = 0;
                    a1.x = path1[i][1];
                    a1.y = path1[i][2];
                    ii = Math.max(p.length, p2.length || 0);
                }
            };
        for (var i = 0, ii = Math.max(p.length, p2.length || 0); i < ii; i++) {
            p[i] = me.command2curve(p[i], attrs);
            fixArc(p, i);
            (p2[i] = me.command2curve(p2[i], attrs2));
            fixArc(p2, i);
            fixM(p, p2, attrs, attrs2, i);
            fixM(p2, p, attrs2, attrs, i);
            var seg = p[i],
                seg2 = p2[i],
                seglen = seg.length,
                seg2len = seg2.length;
            attrs.x = seg[seglen - 2];
            attrs.y = seg[seglen - 1];
            attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
            attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
            attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
            attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
            attrs2.x = seg2[seg2len - 2];
            attrs2.y = seg2[seg2len - 1];
        }
        return [p, p2];
    },
    
    
    command2curve: function (pathCommand, d) {
        var me = this;
        if (!pathCommand) {
            return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
        }
        if (pathCommand[0] != "T" && pathCommand[0] != "Q") {
            d.qx = d.qy = null;
        }
        switch (pathCommand[0]) {
            case "M":
                d.X = pathCommand[1];
                d.Y = pathCommand[2];
                break;
            case "A":
                pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));
                break;
            case "S":
                pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));
                break;
            case "T":
                d.qx = d.x + (d.x - (d.qx || d.x));
                d.qy = d.y + (d.y - (d.qy || d.y));
                pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
                break;
            case "Q":
                d.qx = pathCommand[1];
                d.qy = pathCommand[2];
                pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
                break;
            case "L":
                pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
                break;
            case "H":
                pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
                break;
            case "V":
                pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
                break;
            case "Z":
                pathCommand = ["C"].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
                break;
        }
        return pathCommand;
    },

    quadratic2curve: function (x1, y1, ax, ay, x2, y2) {
        var _13 = 1 / 3,
            _23 = 2 / 3;
        return [
                _13 * x1 + _23 * ax,
                _13 * y1 + _23 * ay,
                _13 * x2 + _23 * ax,
                _13 * y2 + _23 * ay,
                x2,
                y2
            ];
    },
    
    rotate: function (x, y, rad) {
        var cos = Math.cos(rad),
            sin = Math.sin(rad),
            X = x * cos - y * sin,
            Y = x * sin + y * cos;
        return {x: X, y: Y};
    },

    arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
        
        
        var me = this,
            PI = Math.PI,
            radian = me.radian,
            _120 = PI * 120 / 180,
            rad = radian * (+angle || 0),
            res = [],
            math = Math,
            mcos = math.cos,
            msin = math.sin,
            msqrt = math.sqrt,
            mabs = math.abs,
            masin = math.asin,
            xy, cos, sin, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,
            t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
        if (!recursive) {
            xy = me.rotate(x1, y1, -rad);
            x1 = xy.x;
            y1 = xy.y;
            xy = me.rotate(x2, y2, -rad);
            x2 = xy.x;
            y2 = xy.y;
            cos = mcos(radian * angle);
            sin = msin(radian * angle);
            x = (x1 - x2) / 2;
            y = (y1 - y2) / 2;
            h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
            if (h > 1) {
                h = msqrt(h);
                rx = h * rx;
                ry = h * ry;
            }
            rx2 = rx * rx;
            ry2 = ry * ry;
            k = (large_arc_flag == sweep_flag ? -1 : 1) *
                    msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
            cx = k * rx * y / ry + (x1 + x2) / 2;
            cy = k * -ry * x / rx + (y1 + y2) / 2;
            f1 = masin(((y1 - cy) / ry).toFixed(7));
            f2 = masin(((y2 - cy) / ry).toFixed(7));

            f1 = x1 < cx ? PI - f1 : f1;
            f2 = x2 < cx ? PI - f2 : f2;
            if (f1 < 0) {
                f1 = PI * 2 + f1;
            }
            if (f2 < 0) {
                f2 = PI * 2 + f2;
            }
            if (sweep_flag && f1 > f2) {
                f1 = f1 - PI * 2;
            }
            if (!sweep_flag && f2 > f1) {
                f2 = f2 - PI * 2;
            }
        }
        else {
            f1 = recursive[0];
            f2 = recursive[1];
            cx = recursive[2];
            cy = recursive[3];
        }
        df = f2 - f1;
        if (mabs(df) > _120) {
            f2old = f2;
            x2old = x2;
            y2old = y2;
            f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
            x2 = cx + rx * mcos(f2);
            y2 = cy + ry * msin(f2);
            res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
        }
        df = f2 - f1;
        c1 = mcos(f1);
        s1 = msin(f1);
        c2 = mcos(f2);
        s2 = msin(f2);
        t = math.tan(df / 4);
        hx = 4 / 3 * rx * t;
        hy = 4 / 3 * ry * t;
        m1 = [x1, y1];
        m2 = [x1 + hx * s1, y1 - hy * c1];
        m3 = [x2 + hx * s2, y2 - hy * c2];
        m4 = [x2, y2];
        m2[0] = 2 * m1[0] - m2[0];
        m2[1] = 2 * m1[1] - m2[1];
        if (recursive) {
            return [m2, m3, m4].concat(res);
        }
        else {
            res = [m2, m3, m4].concat(res).join().split(",");
            newres = [];
            ln = res.length;
            for (i = 0;  i < ln; i++) {
                newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
            }
            return newres;
        }
    },

    
    rotateAndTranslatePath: function (sprite) {
        var alpha = sprite.rotation.degrees,
            cx = sprite.rotation.x,
            cy = sprite.rotation.y,
            dx = sprite.translation.x,
            dy = sprite.translation.y,
            path,
            i,
            p,
            xy,
            j,
            res = [];
        if (!alpha && !dx && !dy) {
            return this.pathToAbsolute(sprite.attr.path);
        }
        dx = dx || 0;
        dy = dy || 0;
        path = this.pathToAbsolute(sprite.attr.path);
        for (i = path.length; i--;) {
            p = res[i] = path[i].slice();
            if (p[0] == "A") {
                xy = this.rotatePoint(p[6], p[7], alpha, cx, cy);
                p[6] = xy.x + dx;
                p[7] = xy.y + dy;
            } else {
                j = 1;
                while (p[j + 1] != null) {
                    xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy);
                    p[j] = xy.x + dx;
                    p[j + 1] = xy.y + dy;
                    j += 2;
                }
            }
        }
        return res;
    },

    
    rotatePoint: function (x, y, alpha, cx, cy) {
        if (!alpha) {
            return {
                x: x,
                y: y
            };
        }
        cx = cx || 0;
        cy = cy || 0;
        x = x - cx;
        y = y - cy;
        alpha = alpha * this.radian;
        var cos = Math.cos(alpha),
            sin = Math.sin(alpha);
        return {
            x: x * cos - y * sin + cx,
            y: x * sin + y * cos + cy
        };
    },

    pathDimensions: function (path) {
        if (!path || !(path + "")) {
            return {x: 0, y: 0, width: 0, height: 0};
        }
        path = this.path2curve(path);
        var x = 0, 
            y = 0,
            X = [],
            Y = [],
            i = 0,
            ln = path.length,
            p, xmin, ymin, dim;
        for (; i < ln; i++) {
            p = path[i];
            if (p[0] == "M") {
                x = p[1];
                y = p[2];
                X.push(x);
                Y.push(y);
            }
            else {
                dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                X = X.concat(dim.min.x, dim.max.x);
                Y = Y.concat(dim.min.y, dim.max.y);
                x = p[5];
                y = p[6];
            }
        }
        xmin = Math.min.apply(0, X);
        ymin = Math.min.apply(0, Y);
        return {
            x: xmin,
            y: ymin,
            path: path,
            width: Math.max.apply(0, X) - xmin,
            height: Math.max.apply(0, Y) - ymin
        };
    },

    intersectInside: function(path, cp1, cp2) {
        return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) > (cp2[1] - cp1[1]) * (path[0] - cp1[0]);
    },

    intersectIntersection: function(s, e, cp1, cp2) {
        var p = [],
            dcx = cp1[0] - cp2[0],
            dcy = cp1[1] - cp2[1],
            dpx = s[0] - e[0],
            dpy = s[1] - e[1],
            n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
            n2 = s[0] * e[1] - s[1] * e[0],
            n3 = 1 / (dcx * dpy - dcy * dpx);

        p[0] = (n1 * dpx - n2 * dcx) * n3;
        p[1] = (n1 * dpy - n2 * dcy) * n3;
        return p;
    },

    intersect: function(subjectPolygon, clipPolygon) {
        var me = this,
            i = 0,
            ln = clipPolygon.length,
            cp1 = clipPolygon[ln - 1],
            outputList = subjectPolygon,
            cp2, s, e, point, ln2, inputList, j;
        for (; i < ln; ++i) {
            cp2 = clipPolygon[i];
            inputList = outputList;
            outputList = [];
            s = inputList[inputList.length - 1];
            j = 0;
            ln2 = inputList.length;
            for (; j < ln2; j++) {
                e = inputList[j];
                if (me.intersectInside(e, cp1, cp2)) {
                    if (!me.intersectInside(s, cp1, cp2)) {
                        outputList.push(me.intersectIntersection(s, e, cp1, cp2));
                    }
                    outputList.push(e);
                }
                else if (me.intersectInside(s, cp1, cp2)) {
                    outputList.push(me.intersectIntersection(s, e, cp1, cp2));
                }
                s = e;
            }
            cp1 = cp2;
        }
        return outputList;
    },

    curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
        var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
            b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
            c = p1x - c1x,
            t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
            t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
            y = [p1y, p2y],
            x = [p1x, p2x],
            dot;
        if (Math.abs(t1) > 1e12) {
            t1 = 0.5;
        }
        if (Math.abs(t2) > 1e12) {
            t2 = 0.5;
        }
        if (t1 > 0 && t1 < 1) {
            dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
            x.push(dot.x);
            y.push(dot.y);
        }
        if (t2 > 0 && t2 < 1) {
            dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
            x.push(dot.x);
            y.push(dot.y);
        }
        a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
        b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
        c = p1y - c1y;
        t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
        t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
        if (Math.abs(t1) > 1e12) {
            t1 = 0.5;
        }
        if (Math.abs(t2) > 1e12) {
            t2 = 0.5;
        }
        if (t1 > 0 && t1 < 1) {
            dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
            x.push(dot.x);
            y.push(dot.y);
        }
        if (t2 > 0 && t2 < 1) {
            dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
            x.push(dot.x);
            y.push(dot.y);
        }
        return {
            min: {x: Math.min.apply(0, x), y: Math.min.apply(0, y)},
            max: {x: Math.max.apply(0, x), y: Math.max.apply(0, y)}
        };
    },

    getAnchors: function (p1x, p1y, p2x, p2y, p3x, p3y, value) {
        value = value || 4;
        var l = Math.min(Math.sqrt(Math.pow(p1x - p2x, 2) + Math.pow(p1y - p2y, 2)) / value, Math.sqrt(Math.pow(p3x - p2x, 2) + Math.pow(p3y - p2y, 2)) / value),
            a = Math.atan((p2x - p1x) / Math.abs(p2y - p1y)),
            b = Math.atan((p3x - p2x) / Math.abs(p2y - p3y)),
            pi = Math.PI;
        a = p1y < p2y ? pi - a : a;
        b = p3y < p2y ? pi - b : b;
        var alpha = pi / 2 - ((a + b) % (pi * 2)) / 2;
        alpha > pi / 2 && (alpha -= pi);
        var dx1 = l * Math.sin(alpha + a),
            dy1 = l * Math.cos(alpha + a),
            dx2 = l * Math.sin(alpha + b),
            dy2 = l * Math.cos(alpha + b),
            out = {
                x1: p2x - dx1,
                y1: p2y + dy1,
                x2: p2x + dx2,
                y2: p2y + dy2
            };
        return out;
    },

    
    smooth: function (originalPath, value) {
        var path = this.path2curve(originalPath),
            newp = [path[0]],
            x = path[0][1],
            y = path[0][2],
            j,
            points,
            i = 1,
            ii = path.length,
            beg = 1,
            mx = x,
            my = y,
            cx = 0,
            cy = 0;
        for (; i < ii; i++) {
            var pathi = path[i],
                pathil = pathi.length,
                pathim = path[i - 1],
                pathiml = pathim.length,
                pathip = path[i + 1],
                pathipl = pathip && pathip.length;
            if (pathi[0] == "M") {
                mx = pathi[1];
                my = pathi[2];
                j = i + 1;
                while (path[j][0] != "C") {
                    j++;
                }
                cx = path[j][5];
                cy = path[j][6];
                newp.push(["M", mx, my]);
                beg = newp.length;
                x = mx;
                y = my;
                continue;
            }
            if (pathi[pathil - 2] == mx && pathi[pathil - 1] == my && (!pathip || pathip[0] == "M")) {
                var begl = newp[beg].length;
                points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value);
                newp[beg][1] = points.x2;
                newp[beg][2] = points.y2;
            }
            else if (!pathip || pathip[0] == "M") {
                points = {
                    x1: pathi[pathil - 2],
                    y1: pathi[pathil - 1]
                };
            } else {
                points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value);
            }
            newp.push(["C", x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]);
            x = points.x2;
            y = points.y2;
        }
        return newp;
    },

    findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
        var t1 = 1 - t;
        return {
            x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x,
            y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y
        };
    },

    snapEnds: function (from, to, stepsMax) {
        var step = (to - from) / stepsMax,
            level = Math.floor(Math.log(step) / Math.LN10) + 1,
            m = Math.pow(10, level),
            cur,
            modulo = Math.round((step % m) * Math.pow(10, 2 - level)),
            interval = [[0, 15], [20, 4], [30, 2], [40, 4], [50, 9], [60, 4], [70, 2], [80, 4], [100, 15]],
            stepCount = 0,
            value,
            weight,
            i,
            topValue,
            topWeight = 1e9,
            ln = interval.length;
        cur = from = Math.floor(from / m) * m;
        for (i = 0; i < ln; i++) {
            value = interval[i][0];
            weight = (value - modulo) < 0 ? 1e6 : (value - modulo) / interval[i][1];
            if (weight < topWeight) {
                topValue = value;
                topWeight = weight;
            }
        }
        step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2);
        while (cur < to) {
            cur += step;
            stepCount++;
        }
        to = +cur.toFixed(10);
        return {
            from: from,
            to: to,
            power: level,
            step: step,
            steps: stepCount
        };
    },

    sorter: function (a, b) {
        return a.offset - b.offset;
    },

    rad: function(degrees) {
        return degrees % 360 * Math.PI / 180;
    },

    degrees: function(radian) {
        return radian * 180 / Math.PI % 360;
    },

    withinBox: function(x, y, bbox) {
        bbox = bbox || {};
        return (x >= bbox.x && x <= (bbox.x + bbox.width) && y >= bbox.y && y <= (bbox.y + bbox.height));
    },

    parseGradient: function(gradient) {
        var me = this,
            type = gradient.type || 'linear',
            angle = gradient.angle || 0,
            radian = me.radian,
            stops = gradient.stops,
            stopsArr = [],
            stop,
            vector,
            max,
            stopObj;

        if (type == 'linear') {
            vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)];
            max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
            vector[2] *= max;
            vector[3] *= max;
            if (vector[2] < 0) {
                vector[0] = -vector[2];
                vector[2] = 0;
            }
            if (vector[3] < 0) {
                vector[1] = -vector[3];
                vector[3] = 0;
            }
        }

        for (stop in stops) {
            if (stops.hasOwnProperty(stop) && me.stopsRE.test(stop)) {
                stopObj = {
                    offset: parseInt(stop, 10),
                    color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff',
                    opacity: stops[stop].opacity || 1
                };
                stopsArr.push(stopObj);
            }
        }
        
        Ext.Array.sort(stopsArr, me.sorter);
        if (type == 'linear') {
            return {
                id: gradient.id,
                type: type,
                vector: vector,
                stops: stopsArr
            };
        }
        else {
            return {
                id: gradient.id,
                type: type,
                centerX: gradient.centerX,
                centerY: gradient.centerY,
                focalX: gradient.focalX,
                focalY: gradient.focalY,
                radius: gradient.radius,
                vector: vector,
                stops: stopsArr
            };
        }
    }
});


Ext.define('Ext.fx.PropertyHandler', {

    

    requires: ['Ext.draw.Draw'],

    statics: {
        defaultHandler: {
            pixelDefaults: ['width', 'height', 'top', 'left'],
            unitRE: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/,

            computeDelta: function(from, end, damper, initial, attr) {
                damper = (typeof damper == 'number') ? damper : 1;
                var match = this.unitRE.exec(from),
                    start, units;
                if (match) {
                    from = match[1];
                    units = match[2];
                    if (!units && Ext.Array.contains(this.pixelDefaults, attr)) {
                        units = 'px';
                    }
                }
                from = +from || 0;

                match = this.unitRE.exec(end);
                if (match) {
                    end = match[1];
                    units = match[2] || units;
                }
                end = +end || 0;
                start = (initial != null) ? initial : from;
                return {
                    from: from,
                    delta: (end - start) * damper,
                    units: units
                };
            },

            get: function(from, end, damper, initialFrom, attr) {
                var ln = from.length,
                    out = [],
                    i, initial, res, j, len;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    if (Ext.isArray(from[i][1]) && Ext.isArray(end)) {
                        res = [];
                        j = 0;
                        len = from[i][1].length;
                        for (; j < len; j++) {
                            res.push(this.computeDelta(from[i][1][j], end[j], damper, initial, attr));
                        }
                        out.push([from[i][0], res]);
                    }
                    else {
                        out.push([from[i][0], this.computeDelta(from[i][1], end, damper, initial, attr)]);
                    }
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, res, len, j;
                for (i = 0; i < ln; i++) {
                    val  = values[i][1];
                    if (Ext.isArray(val)) {
                        res = [];
                        j = 0;
                        len = val.length;
                        for (; j < len; j++) {
                            res.push(val[j].from + (val[j].delta * easing) + (val[j].units || 0));
                        }
                        out.push([values[i][0], res]);
                    } else {
                        out.push([values[i][0], val.from + (val.delta * easing) + (val.units || 0)]);
                    }
                }
                return out;
            }
        },
        color: {
            rgbRE: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
            hexRE: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
            hex3RE: /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i,

            parseColor : function(color, damper) {
                damper = (typeof damper == 'number') ? damper : 1;
                var base,
                    out = false,
                    match;

                Ext.each([this.hexRE, this.rgbRE, this.hex3RE], function(re, idx) {
                    base = (idx % 2 == 0) ? 16 : 10;
                    match = re.exec(color);
                    if (match && match.length == 4) {
                        if (idx == 2) {
                            match[1] += match[1];
                            match[2] += match[2];
                            match[3] += match[3];
                        }
                        out = {
                            red: parseInt(match[1], base),
                            green: parseInt(match[2], base),
                            blue: parseInt(match[3], base)
                        };
                        return false;
                    }
                });
                return out || color;
            },

            computeDelta: function(from, end, damper, initial) {
                from = this.parseColor(from);
                end = this.parseColor(end, damper);
                var start = initial ? initial : from,
                    tfrom = typeof start,
                    tend = typeof end;
                
                if (tfrom == 'string' ||  tfrom == 'undefined' 
                  || tend == 'string' || tend == 'undefined') {
                    return end || start;
                }
                return {
                    from:  from,
                    delta: {
                        red: Math.round((end.red - start.red) * damper),
                        green: Math.round((end.green - start.green) * damper),
                        blue: Math.round((end.blue - start.blue) * damper)
                    }
                };
            },

            get: function(start, end, damper, initialFrom) {
                var ln = start.length,
                    out = [],
                    i, initial;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, val, parsedString, from, delta;
                for (i = 0; i < ln; i++) {
                    val = values[i][1];
                    if (val) {
                        from = val.from;
                        delta = val.delta;
                        
                        val = (typeof val == 'object' && 'red' in val)? 
                                'rgb(' + val.red + ', ' + val.green + ', ' + val.blue + ')' : val;
                        val = (typeof val == 'object' && val.length)? val[0] : val;
                        if (typeof val == 'undefined') {
                            return [];
                        }
                        parsedString = typeof val == 'string'? val :
                            'rgb(' + [
                                  (from.red + Math.round(delta.red * easing)) % 256,
                                  (from.green + Math.round(delta.green * easing)) % 256,
                                  (from.blue + Math.round(delta.blue * easing)) % 256
                              ].join(',') + ')';
                        out.push([
                            values[i][0],
                            parsedString
                        ]);
                    }
                }
                return out;
            }
        },
        object: {
            interpolate: function(prop, damper) {
                damper = (typeof damper == 'number') ? damper : 1;
                var out = {},
                    p;
                for(p in prop) {
                    out[p] = parseInt(prop[p], 10) * damper;
                }
                return out;
            },

            computeDelta: function(from, end, damper, initial) {
                from = this.interpolate(from);
                end = this.interpolate(end, damper);
                var start = initial ? initial : from,
                    delta = {},
                    p;

                for(p in end) {
                    delta[p] = end[p] - start[p];
                }
                return {
                    from:  from,
                    delta: delta
                };
            },

            get: function(start, end, damper, initialFrom) {
                var ln = start.length,
                    out = [],
                    i, initial;
                for (i = 0; i < ln; i++) {
                    if (initialFrom) {
                        initial = initialFrom[i][1].from;
                    }
                    out.push([start[i][0], this.computeDelta(start[i][1], end, damper, initial)]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    outObject = {},
                    i, from, delta, val, p;
                for (i = 0; i < ln; i++) {
                    val  = values[i][1];
                    from = val.from;
                    delta = val.delta;
                    for (p in from) {
                        outObject[p] = Math.round(from[p] + delta[p] * easing);
                    }
                    out.push([
                        values[i][0],
                        outObject
                    ]);
                }
                return out;
            }
        },

        path: {
            computeDelta: function(from, end, damper, initial) {
                damper = (typeof damper == 'number') ? damper : 1;
                var start;
                from = +from || 0;
                end = +end || 0;
                start = (initial != null) ? initial : from;
                return {
                    from: from,
                    delta: (end - start) * damper
                };
            },

            forcePath: function(path) {
                if (!Ext.isArray(path) && !Ext.isArray(path[0])) {
                    path = Ext.draw.Draw.parsePathString(path);
                }
                return path;
            },

            get: function(start, end, damper, initialFrom) {
                var endPath = this.forcePath(end),
                    out = [],
                    startLn = start.length,
                    startPathLn, pointsLn, i, deltaPath, initial, j, k, path, startPath;
                for (i = 0; i < startLn; i++) {
                    startPath = this.forcePath(start[i][1]);

                    deltaPath = Ext.draw.Draw.interpolatePaths(startPath, endPath);
                    startPath = deltaPath[0];
                    endPath = deltaPath[1];

                    startPathLn = startPath.length;
                    path = [];
                    for (j = 0; j < startPathLn; j++) {
                        deltaPath = [startPath[j][0]];
                        pointsLn = startPath[j].length;
                        for (k = 1; k < pointsLn; k++) {
                            initial = initialFrom && initialFrom[0][1][j][k].from;
                            deltaPath.push(this.computeDelta(startPath[j][k], endPath[j][k], damper, initial));
                        }
                        path.push(deltaPath);
                    }
                    out.push([start[i][0], path]);
                }
                return out;
            },

            set: function(values, easing) {
                var ln = values.length,
                    out = [],
                    i, j, k, newPath, calcPath, deltaPath, deltaPathLn, pointsLn;
                for (i = 0; i < ln; i++) {
                    deltaPath = values[i][1];
                    newPath = [];
                    deltaPathLn = deltaPath.length;
                    for (j = 0; j < deltaPathLn; j++) {
                        calcPath = [deltaPath[j][0]];
                        pointsLn = deltaPath[j].length;
                        for (k = 1; k < pointsLn; k++) {
                            calcPath.push(deltaPath[j][k].from + deltaPath[j][k].delta * easing);
                        }
                        newPath.push(calcPath.join(','));
                    }
                    out.push([values[i][0], newPath.join(',')]);
                }
                return out;
            }
        }
        
    }
}, function() {
    Ext.each([
        'outlineColor',
        'backgroundColor',
        'borderColor',
        'borderTopColor',
        'borderRightColor', 
        'borderBottomColor', 
        'borderLeftColor',
        'fill',
        'stroke'
    ], function(prop) {
        this[prop] = this.color;
    }, this);
});

Ext.define('Ext.fx.Anim', {

    

    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: ['Ext.fx.Manager', 'Ext.fx.Animator', 'Ext.fx.Easing', 'Ext.fx.CubicBezier', 'Ext.fx.PropertyHandler'],

    

    isAnimation: true,
    
    duration: 250,

    
    delay: 0,

    
    delayStart: 0,

    
    dynamic: false,

    
    easing: 'ease',

     

    
    damper: 1,

    
    bezierRE: /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,

    
    reverse: false,

    
    running: false,

    
    paused: false,

    
    iterations: 1,

    
    alternate: false,

    
    currentIteration: 0,

    
    startTime: 0,

    

    

    

    

    
    constructor: function(config) {
        var me = this;
        config = config || {};
        
        if (config.keyframes) {
            return Ext.create('Ext.fx.Animator', config);
        }
        config = Ext.apply(me, config);
        if (me.from === undefined) {
            me.from = {};
        }
        me.propHandlers = {};
        me.config = config;
        me.target = Ext.fx.Manager.createTarget(me.target);
        me.easingFn = Ext.fx.Easing[me.easing];
        me.target.dynamic = me.dynamic;

        
        if (!me.easingFn) {
            me.easingFn = String(me.easing).match(me.bezierRE);
            if (me.easingFn && me.easingFn.length == 5) {
                var curve = me.easingFn;
                me.easingFn = Ext.fx.cubicBezier(+curve[1], +curve[2], +curve[3], +curve[4]);
            }
        }
        me.id = Ext.id(null, 'ext-anim-');
        Ext.fx.Manager.addAnim(me);
        me.addEvents(
            
            'beforeanimate',
             
            'afteranimate',
             
            'lastframe'
        );
        me.mixins.observable.constructor.call(me, config);
        if (config.callback) {
            me.on('afteranimate', config.callback, config.scope);
        }
        return me;
    },

    
    setAttr: function(attr, value) {
        return Ext.fx.Manager.items.get(this.id).setAttr(this.target, attr, value);
    },

    
    initAttrs: function() {
        var me = this,
            from = me.from,
            to = me.to,
            initialFrom = me.initialFrom || {},
            out = {},
            start, end, propHandler, attr;

        for (attr in to) {
            if (to.hasOwnProperty(attr)) {
                start = me.target.getAttr(attr, from[attr]);
                end = to[attr];
                
                if (!Ext.fx.PropertyHandler[attr]) {
                    if (Ext.isObject(end)) {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.object;
                    } else {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.defaultHandler;
                    }
                }
                
                else {
                    propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler[attr];
                }
                out[attr] = propHandler.get(start, end, me.damper, initialFrom[attr], attr);
            }
        }
        me.currentAttrs = out;
    },

    
    start: function(startTime) {
        var me = this,
            delay = me.delay,
            delayStart = me.delayStart,
            delayDelta;
        if (delay) {
            if (!delayStart) {
                me.delayStart = startTime;
                return;
            }
            else {
                delayDelta = startTime - delayStart;
                if (delayDelta < delay) {
                    return;
                }
                else {
                    
                    startTime = new Date(delayStart.getTime() + delay);
                }
            }
        }
        if (me.fireEvent('beforeanimate', me) !== false) {
            me.startTime = startTime;
            if (!me.paused && !me.currentAttrs) {
                me.initAttrs();
            }
            me.running = true;
        }
    },

    
    runAnim: function(elapsedTime) {
        var me = this,
            attrs = me.currentAttrs,
            duration = me.duration,
            easingFn = me.easingFn,
            propHandlers = me.propHandlers,
            ret = {},
            easing, values, attr, lastFrame;

        if (elapsedTime >= duration) {
            elapsedTime = duration;
            lastFrame = true;
        }
        if (me.reverse) {
            elapsedTime = duration - elapsedTime;
        }

        for (attr in attrs) {
            if (attrs.hasOwnProperty(attr)) {
                values = attrs[attr];
                easing = lastFrame ? 1 : easingFn(elapsedTime / duration);
                ret[attr] = propHandlers[attr].set(values, easing);
            }
        }
        return ret;
    },

    
    lastFrame: function() {
        var me = this,
            iter = me.iterations,
            iterCount = me.currentIteration;

        iterCount++;
        if (iterCount < iter) {
            if (me.alternate) {
                me.reverse = !me.reverse;
            }
            me.startTime = new Date();
            me.currentIteration = iterCount;
            
            me.paused = false;
        }
        else {
            me.currentIteration = 0;
            me.end();
            me.fireEvent('lastframe', me, me.startTime);
        }
    },

    
    end: function() {
        var me = this;
        me.startTime = 0;
        me.paused = false;
        me.running = false;
        Ext.fx.Manager.removeAnim(me);
        me.fireEvent('afteranimate', me, me.startTime);
    }
});

Ext.enableFx = true;






Ext.define('Ext.dd.DragDrop', {
    requires: ['Ext.dd.DragDropManager'],
    constructor: function(id, sGroup, config) {
        if(id) {
            this.init(id, sGroup, config);
        }
    },
    
    

    
    id: null,

    
    config: null,

    
    dragElId: null,

    
    handleElId: null,

    
    invalidHandleTypes: null,

    
    invalidHandleIds: null,

    
    invalidHandleClasses: null,

    
    startPageX: 0,

    
    startPageY: 0,

    
    groups: null,

    
    locked: false,

    
    lock: function() {
        this.locked = true;
    },

    
    moveOnly: false,

    
    unlock: function() {
        this.locked = false;
    },

    
    isTarget: true,

    
    padding: null,

    
    _domRef: null,

    
    __ygDragDrop: true,

    
    constrainX: false,

    
    constrainY: false,

    
    minX: 0,

    
    maxX: 0,

    
    minY: 0,

    
    maxY: 0,

    
    maintainOffset: false,

    
    xTicks: null,

    
    yTicks: null,

    
    primaryButtonOnly: true,

    
    available: false,

    
    hasOuterHandles: false,

    
    b4StartDrag: function(x, y) { },

    
    startDrag: function(x, y) {  },

    
    b4Drag: function(e) { },

    
    onDrag: function(e) {  },

    
    onDragEnter: function(e, id) {  },

    
    b4DragOver: function(e) { },

    
    onDragOver: function(e, id) {  },

    
    b4DragOut: function(e) { },

    
    onDragOut: function(e, id) {  },

    
    b4DragDrop: function(e) { },

    
    onDragDrop: function(e, id) {  },

    
    onInvalidDrop: function(e) {  },

    
    b4EndDrag: function(e) { },

    
    endDrag: function(e) {  },

    
    b4MouseDown: function(e) {  },

    
    onMouseDown: function(e) {  },

    
    onMouseUp: function(e) {  },

    
    onAvailable: function () {
    },

    
    defaultPadding: {
        left: 0,
        right: 0,
        top: 0,
        bottom: 0
    },

    
    constrainTo : function(constrainTo, pad, inContent){
        if(Ext.isNumber(pad)){
            pad = {left: pad, right:pad, top:pad, bottom:pad};
        }
        pad = pad || this.defaultPadding;
        var b = Ext.get(this.getEl()).getBox(),
            ce = Ext.get(constrainTo),
            s = ce.getScroll(),
            c, 
            cd = ce.dom;
        if(cd == document.body){
            c = { x: s.left, y: s.top, width: Ext.core.Element.getViewWidth(), height: Ext.core.Element.getViewHeight()};
        }else{
            var xy = ce.getXY();
            c = {x : xy[0], y: xy[1], width: cd.clientWidth, height: cd.clientHeight};
        }


        var topSpace = b.y - c.y,
            leftSpace = b.x - c.x;

        this.resetConstraints();
        this.setXConstraint(leftSpace - (pad.left||0), 
                c.width - leftSpace - b.width - (pad.right||0), 
				this.xTickSize
        );
        this.setYConstraint(topSpace - (pad.top||0), 
                c.height - topSpace - b.height - (pad.bottom||0), 
				this.yTickSize
        );
    },

    
    getEl: function() {
        if (!this._domRef) {
            this._domRef = Ext.getDom(this.id);
        }

        return this._domRef;
    },

    
    getDragEl: function() {
        return Ext.getDom(this.dragElId);
    },

    
    init: function(id, sGroup, config) {
        this.initTarget(id, sGroup, config);
        Ext.EventManager.on(this.id, "mousedown", this.handleMouseDown, this);
        
    },

    
    initTarget: function(id, sGroup, config) {

        
        this.config = config || {};

        
        this.DDMInstance = Ext.dd.DragDropManager;
        
        this.groups = {};

        
        
        if (typeof id !== "string") {
            id = Ext.id(id);
        }

        
        this.id = id;

        
        this.addToGroup((sGroup) ? sGroup : "default");

        
        
        this.handleElId = id;

        
        this.setDragElId(id);

        
        this.invalidHandleTypes = { A: "A" };
        this.invalidHandleIds = {};
        this.invalidHandleClasses = [];

        this.applyConfig();

        this.handleOnAvailable();
    },

    
    applyConfig: function() {

        
        
        this.padding           = this.config.padding || [0, 0, 0, 0];
        this.isTarget          = (this.config.isTarget !== false);
        this.maintainOffset    = (this.config.maintainOffset);
        this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);

    },

    
    handleOnAvailable: function() {
        this.available = true;
        this.resetConstraints();
        this.onAvailable();
    },

     
    setPadding: function(iTop, iRight, iBot, iLeft) {
        
        if (!iRight && 0 !== iRight) {
            this.padding = [iTop, iTop, iTop, iTop];
        } else if (!iBot && 0 !== iBot) {
            this.padding = [iTop, iRight, iTop, iRight];
        } else {
            this.padding = [iTop, iRight, iBot, iLeft];
        }
    },

    
    setInitPosition: function(diffX, diffY) {
        var el = this.getEl();

        if (!this.DDMInstance.verifyEl(el)) {
            return;
        }

        var dx = diffX || 0;
        var dy = diffY || 0;

        var p = Ext.core.Element.getXY( el );

        this.initPageX = p[0] - dx;
        this.initPageY = p[1] - dy;

        this.lastPageX = p[0];
        this.lastPageY = p[1];

        this.setStartPosition(p);
    },

    
    setStartPosition: function(pos) {
        var p = pos || Ext.core.Element.getXY( this.getEl() );
        this.deltaSetXY = null;

        this.startPageX = p[0];
        this.startPageY = p[1];
    },

    
    addToGroup: function(sGroup) {
        this.groups[sGroup] = true;
        this.DDMInstance.regDragDrop(this, sGroup);
    },

    
    removeFromGroup: function(sGroup) {
        if (this.groups[sGroup]) {
            delete this.groups[sGroup];
        }

        this.DDMInstance.removeDDFromGroup(this, sGroup);
    },

    
    setDragElId: function(id) {
        this.dragElId = id;
    },

    
    setHandleElId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        this.handleElId = id;
        this.DDMInstance.regHandle(this.id, id);
    },

    
    setOuterHandleElId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        Ext.EventManager.on(id, "mousedown", this.handleMouseDown, this);
        this.setHandleElId(id);

        this.hasOuterHandles = true;
    },

    
    unreg: function() {
        Ext.EventManager.un(this.id, "mousedown", this.handleMouseDown, this);
        this._domRef = null;
        this.DDMInstance._remove(this);
    },

    destroy : function(){
        this.unreg();
    },

    
    isLocked: function() {
        return (this.DDMInstance.isLocked() || this.locked);
    },

    
    handleMouseDown: function(e, oDD){
        if (this.primaryButtonOnly && e.button != 0) {
            return;
        }

        if (this.isLocked()) {
            return;
        }

        this.DDMInstance.refreshCache(this.groups);

        var pt = e.getPoint();
        if (!this.hasOuterHandles && !this.DDMInstance.isOverTarget(pt, this) )  {
        } else {
            if (this.clickValidator(e)) {
                
                this.setStartPosition();
                this.b4MouseDown(e);
                this.onMouseDown(e);

                this.DDMInstance.handleMouseDown(e, this);

                this.DDMInstance.stopEvent(e);
            } else {


            }
        }
    },

    clickValidator: function(e) {
        var target = e.getTarget();
        return ( this.isValidHandleChild(target) &&
                    (this.id == this.handleElId ||
                        this.DDMInstance.handleWasClicked(target, this.id)) );
    },

    
    addInvalidHandleType: function(tagName) {
        var type = tagName.toUpperCase();
        this.invalidHandleTypes[type] = type;
    },

    
    addInvalidHandleId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        this.invalidHandleIds[id] = id;
    },

    
    addInvalidHandleClass: function(cssClass) {
        this.invalidHandleClasses.push(cssClass);
    },

    
    removeInvalidHandleType: function(tagName) {
        var type = tagName.toUpperCase();
        
        delete this.invalidHandleTypes[type];
    },

    
    removeInvalidHandleId: function(id) {
        if (typeof id !== "string") {
            id = Ext.id(id);
        }
        delete this.invalidHandleIds[id];
    },

    
    removeInvalidHandleClass: function(cssClass) {
        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
            if (this.invalidHandleClasses[i] == cssClass) {
                delete this.invalidHandleClasses[i];
            }
        }
    },

    
    isValidHandleChild: function(node) {

        var valid = true;
        
        var nodeName;
        try {
            nodeName = node.nodeName.toUpperCase();
        } catch(e) {
            nodeName = node.nodeName;
        }
        valid = valid && !this.invalidHandleTypes[nodeName];
        valid = valid && !this.invalidHandleIds[node.id];

        for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
            valid = !Ext.fly(node).hasCls(this.invalidHandleClasses[i]);
        }


        return valid;

    },

    
    setXTicks: function(iStartX, iTickSize) {
        this.xTicks = [];
        this.xTickSize = iTickSize;

        var tickMap = {};

        for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
            if (!tickMap[i]) {
                this.xTicks[this.xTicks.length] = i;
                tickMap[i] = true;
            }
        }

        for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
            if (!tickMap[i]) {
                this.xTicks[this.xTicks.length] = i;
                tickMap[i] = true;
            }
        }

        Ext.Array.sort(this.xTicks, this.DDMInstance.numericSort);
    },

    
    setYTicks: function(iStartY, iTickSize) {
        this.yTicks = [];
        this.yTickSize = iTickSize;

        var tickMap = {};

        for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
            if (!tickMap[i]) {
                this.yTicks[this.yTicks.length] = i;
                tickMap[i] = true;
            }
        }

        for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
            if (!tickMap[i]) {
                this.yTicks[this.yTicks.length] = i;
                tickMap[i] = true;
            }
        }

        Ext.Array.sort(this.yTicks, this.DDMInstance.numericSort);
    },

    
    setXConstraint: function(iLeft, iRight, iTickSize) {
        this.leftConstraint = iLeft;
        this.rightConstraint = iRight;

        this.minX = this.initPageX - iLeft;
        this.maxX = this.initPageX + iRight;
        if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }

        this.constrainX = true;
    },

    
    clearConstraints: function() {
        this.constrainX = false;
        this.constrainY = false;
        this.clearTicks();
    },

    
    clearTicks: function() {
        this.xTicks = null;
        this.yTicks = null;
        this.xTickSize = 0;
        this.yTickSize = 0;
    },

    
    setYConstraint: function(iUp, iDown, iTickSize) {
        this.topConstraint = iUp;
        this.bottomConstraint = iDown;

        this.minY = this.initPageY - iUp;
        this.maxY = this.initPageY + iDown;
        if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }

        this.constrainY = true;

    },

    
    resetConstraints: function() {
        
        if (this.initPageX || this.initPageX === 0) {
            
            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
            var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;

            this.setInitPosition(dx, dy);

        
        } else {
            this.setInitPosition();
        }

        if (this.constrainX) {
            this.setXConstraint( this.leftConstraint,
                                 this.rightConstraint,
                                 this.xTickSize        );
        }

        if (this.constrainY) {
            this.setYConstraint( this.topConstraint,
                                 this.bottomConstraint,
                                 this.yTickSize         );
        }
    },

    
    getTick: function(val, tickArray) {
        if (!tickArray) {
            
            
            return val;
        } else if (tickArray[0] >= val) {
            
            
            return tickArray[0];
        } else {
            for (var i=0, len=tickArray.length; i<len; ++i) {
                var next = i + 1;
                if (tickArray[next] && tickArray[next] >= val) {
                    var diff1 = val - tickArray[i];
                    var diff2 = tickArray[next] - val;
                    return (diff2 > diff1) ? tickArray[i] : tickArray[next];
                }
            }

            
            
            return tickArray[tickArray.length - 1];
        }
    },

    
    toString: function() {
        return ("DragDrop " + this.id);
    }

});





Ext.define('Ext.dd.DD', {
    extend: 'Ext.dd.DragDrop',
    requires: ['Ext.dd.DragDropManager'],
    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
        }
    },

    
    scroll: true,

    
    autoOffset: function(iPageX, iPageY) {
        var x = iPageX - this.startPageX;
        var y = iPageY - this.startPageY;
        this.setDelta(x, y);
    },

    
    setDelta: function(iDeltaX, iDeltaY) {
        this.deltaX = iDeltaX;
        this.deltaY = iDeltaY;
    },

    
    setDragElPos: function(iPageX, iPageY) {
        
        

        var el = this.getDragEl();
        this.alignElWithMouse(el, iPageX, iPageY);
    },

    
    alignElWithMouse: function(el, iPageX, iPageY) {
        var oCoord = this.getTargetCoord(iPageX, iPageY),
            fly = el.dom ? el : Ext.fly(el, '_dd'),
            elSize = fly.getSize(),
            EL = Ext.core.Element,
            vpSize;

        if (!this.deltaSetXY) {
            vpSize = this.cachedViewportSize = { width: EL.getDocumentWidth(), height: EL.getDocumentHeight() };
            var aCoord = [
                Math.max(0, Math.min(oCoord.x, vpSize.width - elSize.width)),
                Math.max(0, Math.min(oCoord.y, vpSize.height - elSize.height))
            ];
            fly.setXY(aCoord);
            var newLeft = fly.getLeft(true);
            var newTop  = fly.getTop(true);
            this.deltaSetXY = [newLeft - oCoord.x, newTop - oCoord.y];
        } else {
            vpSize = this.cachedViewportSize;
            fly.setLeftTop(
                Math.max(0, Math.min(oCoord.x + this.deltaSetXY[0], vpSize.width - elSize.width)),
                Math.max(0, Math.min(oCoord.y + this.deltaSetXY[1], vpSize.height - elSize.height))
            );
        }

        this.cachePosition(oCoord.x, oCoord.y);
        this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
        return oCoord;
    },

    
    cachePosition: function(iPageX, iPageY) {
        if (iPageX) {
            this.lastPageX = iPageX;
            this.lastPageY = iPageY;
        } else {
            var aCoord = Ext.core.Element.getXY(this.getEl());
            this.lastPageX = aCoord[0];
            this.lastPageY = aCoord[1];
        }
    },

    
    autoScroll: function(x, y, h, w) {

        if (this.scroll) {
            
            var clientH = Ext.core.Element.getViewHeight();

            
            var clientW = Ext.core.Element.getViewWidth();

            
            var st = this.DDMInstance.getScrollTop();

            
            var sl = this.DDMInstance.getScrollLeft();

            
            var bot = h + y;

            
            var right = w + x;

            
            
            
            var toBot = (clientH + st - y - this.deltaY);

            
            var toRight = (clientW + sl - x - this.deltaX);


            
            
            var thresh = 40;

            
            
            
            var scrAmt = (document.all) ? 80 : 30;

            
            
            if ( bot > clientH && toBot < thresh ) {
                window.scrollTo(sl, st + scrAmt);
            }

            
            
            if ( y < st && st > 0 && y - st < thresh ) {
                window.scrollTo(sl, st - scrAmt);
            }

            
            
            if ( right > clientW && toRight < thresh ) {
                window.scrollTo(sl + scrAmt, st);
            }

            
            
            if ( x < sl && sl > 0 && x - sl < thresh ) {
                window.scrollTo(sl - scrAmt, st);
            }
        }
    },

    
    getTargetCoord: function(iPageX, iPageY) {
        var x = iPageX - this.deltaX;
        var y = iPageY - this.deltaY;

        if (this.constrainX) {
            if (x < this.minX) {
                x = this.minX;
            }
            if (x > this.maxX) {
                x = this.maxX;
            }
        }

        if (this.constrainY) {
            if (y < this.minY) {
                y = this.minY;
            }
            if (y > this.maxY) {
                y = this.maxY;
            }
        }

        x = this.getTick(x, this.xTicks);
        y = this.getTick(y, this.yTicks);


        return {x: x, y: y};
    },

    
    applyConfig: function() {
        this.callParent();
        this.scroll = (this.config.scroll !== false);
    },

    
    b4MouseDown: function(e) {
        
        this.autoOffset(e.getPageX(), e.getPageY());
    },

    
    b4Drag: function(e) {
        this.setDragElPos(e.getPageX(), e.getPageY());
    },

    toString: function() {
        return ("DD " + this.id);
    }

    
    
    
    

});




Ext.define('Ext.dd.DDProxy', {
    extend: 'Ext.dd.DD',

    statics: {
        
        dragElId: "ygddfdiv"
    },

    constructor: function(id, sGroup, config) {
        if (id) {
            this.init(id, sGroup, config);
            this.initFrame();
        }
    },

    
    resizeFrame: true,

    
    centerFrame: false,

    
    createFrame: function() {
        var self = this;
        var body = document.body;

        if (!body || !body.firstChild) {
            setTimeout( function() { self.createFrame(); }, 50 );
            return;
        }

        var div = this.getDragEl();

        if (!div) {
            div    = document.createElement("div");
            div.id = this.dragElId;
            var s  = div.style;

            s.position   = "absolute";
            s.visibility = "hidden";
            s.cursor     = "move";
            s.border     = "2px solid #aaa";
            s.zIndex     = 999;

            
            
            
            body.insertBefore(div, body.firstChild);
        }
    },

    
    initFrame: function() {
        this.createFrame();
    },

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

        this.resizeFrame = (this.config.resizeFrame !== false);
        this.centerFrame = (this.config.centerFrame);
        this.setDragElId(this.config.dragElId || Ext.dd.DDProxy.dragElId);
    },

    
    showFrame: function(iPageX, iPageY) {
        var el = this.getEl();
        var dragEl = this.getDragEl();
        var s = dragEl.style;

        this._resizeProxy();

        if (this.centerFrame) {
            this.setDelta( Math.round(parseInt(s.width,  10)/2),
                           Math.round(parseInt(s.height, 10)/2) );
        }

        this.setDragElPos(iPageX, iPageY);

        Ext.fly(dragEl).show();
    },

    
    _resizeProxy: function() {
        if (this.resizeFrame) {
            var el = this.getEl();
            Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
        }
    },

    
    b4MouseDown: function(e) {
        var x = e.getPageX();
        var y = e.getPageY();
        this.autoOffset(x, y);
        this.setDragElPos(x, y);
    },

    
    b4StartDrag: function(x, y) {
        
        this.showFrame(x, y);
    },

    
    b4EndDrag: function(e) {
        Ext.fly(this.getDragEl()).hide();
    },

    
    
    
    endDrag: function(e) {

        var lel = this.getEl();
        var del = this.getDragEl();

        
        del.style.visibility = "";

        this.beforeMove();
        
        
        lel.style.visibility = "hidden";
        Ext.dd.DDM.moveToEl(lel, del);
        del.style.visibility = "hidden";
        lel.style.visibility = "";

        this.afterDrag();
    },

    beforeMove : function(){

    },

    afterDrag : function(){

    },

    toString: function() {
        return ("DDProxy " + this.id);
    }

});


Ext.define('Ext.dd.DragSource', {
    extend: 'Ext.dd.DDProxy',
    requires: [
        'Ext.dd.StatusProxy',
        'Ext.dd.DragDropManager'
    ],

    

    

    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    animRepair: true,

    
    repairHighlightColor: 'c3daf9',

    constructor: function(el, config) {
        this.el = Ext.get(el);
        if(!this.dragData){
            this.dragData = {};
        }

        Ext.apply(this, config);

        if(!this.proxy){
            this.proxy = Ext.create('Ext.dd.StatusProxy', {
                animRepair: this.animRepair
            });
        }
        this.callParent([this.el.dom, this.ddGroup || this.group,
              {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true}]);

        this.dragging = false;
    },

    
    getDragData : function(e){
        return this.dragData;
    },

    
    onDragEnter : function(e, id){
        var target = Ext.dd.DragDropManager.getDDById(id);
        this.cachedTarget = target;
        if (this.beforeDragEnter(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                var status = target.notifyEnter(this, e, this.dragData);
                this.proxy.setStatus(status);
            } else {
                this.proxy.setStatus(this.dropAllowed);
            }

            if (this.afterDragEnter) {
                
                this.afterDragEnter(target, e, id);
            }
        }
    },

    
    beforeDragEnter: function(target, e, id) {
        return true;
    },

    
    alignElWithMouse: function() {
        this.callParent(arguments);
        this.proxy.sync();
    },

    
    onDragOver: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragOver(target, e, id) !== false) {
            if(target.isNotifyTarget){
                var status = target.notifyOver(this, e, this.dragData);
                this.proxy.setStatus(status);
            }

            if (this.afterDragOver) {
                
                this.afterDragOver(target, e, id);
            }
        }
    },

    
    beforeDragOver: function(target, e, id) {
        return true;
    },

    
    onDragOut: function(e, id) {
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragOut(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                target.notifyOut(this, e, this.dragData);
            }
            this.proxy.reset();
            if (this.afterDragOut) {
                
                this.afterDragOut(target, e, id);
            }
        }
        this.cachedTarget = null;
    },

    
    beforeDragOut: function(target, e, id){
        return true;
    },

    
    onDragDrop: function(e, id){
        var target = this.cachedTarget || Ext.dd.DragDropManager.getDDById(id);
        if (this.beforeDragDrop(target, e, id) !== false) {
            if (target.isNotifyTarget) {
                if (target.notifyDrop(this, e, this.dragData) !== false) { 
                    this.onValidDrop(target, e, id);
                } else {
                    this.onInvalidDrop(target, e, id);
                }
            } else {
                this.onValidDrop(target, e, id);
            }

            if (this.afterDragDrop) {
                
                this.afterDragDrop(target, e, id);
            }
        }
        delete this.cachedTarget;
    },

    
    beforeDragDrop: function(target, e, id){
        return true;
    },

    
    onValidDrop: function(target, e, id){
        this.hideProxy();
        if(this.afterValidDrop){
            
            this.afterValidDrop(target, e, id);
        }
    },

    
    getRepairXY: function(e, data){
        return this.el.getXY();
    },

    
    onInvalidDrop: function(target, e, id) {
        this.beforeInvalidDrop(target, e, id);
        if (this.cachedTarget) {
            if(this.cachedTarget.isNotifyTarget){
                this.cachedTarget.notifyOut(this, e, this.dragData);
            }
            this.cacheTarget = null;
        }
        this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);

        if (this.afterInvalidDrop) {
            
            this.afterInvalidDrop(e, id);
        }
    },

    
    afterRepair: function() {
        var me = this;
        if (Ext.enableFx) {
            me.el.highlight(me.repairHighlightColor);
        }
        me.dragging = false;
    },

    
    beforeInvalidDrop: function(target, e, id) {
        return true;
    },

    
    handleMouseDown: function(e) {
        if (this.dragging) {
            return;
        }
        var data = this.getDragData(e);
        if (data && this.onBeforeDrag(data, e) !== false) {
            this.dragData = data;
            this.proxy.stop();
            this.callParent(arguments);
        }
    },

    
    onBeforeDrag: function(data, e){
        return true;
    },

    
    onStartDrag: Ext.emptyFn,

    
    startDrag: function(x, y) {
        this.proxy.reset();
        this.dragging = true;
        this.proxy.update("");
        this.onInitDrag(x, y);
        this.proxy.show();
    },

    
    onInitDrag: function(x, y) {
        var clone = this.el.dom.cloneNode(true);
        clone.id = Ext.id(); 
        this.proxy.update(clone);
        this.onStartDrag(x, y);
        return true;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    hideProxy: function() {
        this.proxy.hide();
        this.proxy.reset(true);
        this.dragging = false;
    },

    
    triggerCacheRefresh: function() {
        Ext.dd.DDM.refreshCache(this.groups);
    },

    
    b4EndDrag: function(e) {
    },

    
    endDrag : function(e){
        this.onEndDrag(this.dragData, e);
    },

    
    onEndDrag : function(data, e){
    },

    
    autoOffset : function(x, y) {
        this.setDelta(-12, -20);
    },

    destroy: function(){
        this.callParent();
        Ext.destroy(this.proxy);
    }
});


Ext.define('Ext.panel.DD', {
    extend: 'Ext.dd.DragSource',
    requires: ['Ext.panel.Proxy'],

    constructor : function(panel, cfg){
        this.panel = panel;
        this.dragData = {panel: panel};
        this.proxy = Ext.create('Ext.panel.Proxy', panel, cfg);

        this.callParent([panel.el, cfg]);

        Ext.defer(function() {
            var header = panel.header,
                el = panel.body;

            if(header){
                this.setHandleElId(header.id);
                el = header.el;
            }
            el.setStyle('cursor', 'move');
            this.scroll = false;
        }, 200, this);
    },

    showFrame: Ext.emptyFn,
    startDrag: Ext.emptyFn,
    b4StartDrag: function(x, y) {
        this.proxy.show();
    },
    b4MouseDown: function(e) {
        var x = e.getPageX(),
            y = e.getPageY();
        this.autoOffset(x, y);
    },
    onInitDrag : function(x, y){
        this.onStartDrag(x, y);
        return true;
    },
    createFrame : Ext.emptyFn,
    getDragEl : function(e){
        return this.proxy.ghost.el.dom;
    },
    endDrag : function(e){
        this.proxy.hide();
        this.panel.saveState();
    },

    autoOffset : function(x, y) {
        x -= this.startPageX;
        y -= this.startPageY;
        this.setDelta(x, y);
    }
});


Ext.define('Ext.layout.component.Dock', {

    

    alias: ['layout.dock'],

    extend: 'Ext.layout.component.AbstractDock'

    

});

Ext.define('Ext.panel.Panel', {
    extend: 'Ext.panel.AbstractPanel',
    requires: [
        'Ext.panel.Header',
        'Ext.fx.Anim',
        'Ext.util.KeyMap',
        'Ext.panel.DD',
        'Ext.XTemplate',
        'Ext.layout.component.Dock'
    ],
    alias: 'widget.panel',
    alternateClassName: 'Ext.Panel',

    
    collapsedCls: 'collapsed',

    
    animCollapse: Ext.enableFx,

    
    minButtonWidth: 75,

    
    collapsed: false,

    
    collapseFirst: true,

    
    hideCollapseTool: false,

    
    titleCollapse: false,

    

    

    
    floatable: true,
    
    
    
    
    collapsible: false,

    

    
    closable: false,

    
    closeAction: 'destroy',

    

    
    preventHeader: false,

     
    headerPosition: 'top',

     
    frame: false,

    
    frameHeader: true,

    


    initComponent: function() {
        var me = this,
            cls;

        me.addEvents(
        
            'titlechange',
        
            'iconchange'
        );

        if (me.unstyled) {
            me.setUI('plain');
        }

        if (me.frame) {
            me.setUI('default-framed');
        }

        me.callParent();

        me.collapseDirection = me.collapseDirection || me.headerPosition || Ext.Component.DIRECTION_TOP;

        
        me.bridgeToolbars();
    },

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

    beforeDestroy: function() {
        Ext.destroy(
            this.ghostPanel,
            this.dd
        );
        this.callParent();
    },

    initAria: function() {
        this.callParent();
        this.initHeaderAria();
    },

    initHeaderAria: function() {
        var me = this,
            el = me.el,
            header = me.header;
        if (el && header) {
            el.dom.setAttribute('aria-labelledby', header.titleCmp.id);
        }
    },

    getHeader: function() {
        return this.header;
    },

    
    setTitle: function(newTitle) {
        var me = this,
        oldTitle = this.title;

        me.title = newTitle;
        if (me.header) {
            me.header.setTitle(newTitle);
        } else {
            me.updateHeader();
        }

        if (me.reExpander) {
            me.reExpander.setTitle(newTitle);
        }
        me.fireEvent('titlechange', me, newTitle, oldTitle);
    },

    
    setIconCls: function(newIconCls) {
        var me = this,
            oldIconCls = me.iconCls;

        me.iconCls = newIconCls;
        var header = me.header;
        if (header) {
            header.setIconCls(newIconCls);
        }
        me.fireEvent('iconchange', me, newIconCls, oldIconCls);
    },

    bridgeToolbars: function() {
        var me = this,
            fbar,
            fbarDefaults,
            minButtonWidth = me.minButtonWidth;

        function initToolbar (toolbar, pos) {
            if (Ext.isArray(toolbar)) {
                toolbar = {
                    xtype: 'toolbar',
                    items: toolbar
                };
            }
            else if (!toolbar.xtype) {
                toolbar.xtype = 'toolbar';
            }
            toolbar.dock = pos;
            if (pos == 'left' || pos == 'right') {
                toolbar.vertical = true;
            }
            return toolbar;
        }

        

        
        if (me.tbar) {
            me.addDocked(initToolbar(me.tbar, 'top'));
            me.tbar = null;
        }

        
        if (me.bbar) {
            me.addDocked(initToolbar(me.bbar, 'bottom'));
            me.bbar = null;
        }

        
        if (me.buttons) {
            me.fbar = me.buttons;
            me.buttons = null;
        }

        
        if (me.fbar) {
            fbar = initToolbar(me.fbar, 'bottom');
            fbar.ui = 'footer';

            
            if (minButtonWidth) {
                fbarDefaults = fbar.defaults;
                fbar.defaults = function(config) {
                    var defaults = fbarDefaults || {};
                    if ((!config.xtype || config.xtype === 'button' || (config.isComponent && config.isXType('button'))) &&
                            !('minWidth' in defaults)) {
                        defaults = Ext.apply({minWidth: minButtonWidth}, defaults);
                    }
                    return defaults;
                };
            }

            fbar = me.addDocked(fbar)[0];
            fbar.insert(0, {
                flex: 1,
                xtype: 'component',
                focusable: false
            });
            me.fbar = null;
        }

        
        if (me.lbar) {
            me.addDocked(initToolbar(me.lbar, 'left'));
            me.lbar = null;
        }

        
        if (me.rbar) {
            me.addDocked(initToolbar(me.rbar, 'right'));
            me.rbar = null;
        }
    },

    
    initTools: function() {
        var me = this;

        me.tools = me.tools || [];

        
        
        if (me.collapsible && !(me.hideCollapseTool || me.header === false)) {
            me.collapseDirection = me.collapseDirection || me.headerPosition || 'top';
            me.collapseTool = me.expandTool = me.createComponent({
                xtype: 'tool',
                type: 'collapse-' + me.collapseDirection,
                expandType: me.getOppositeDirection(me.collapseDirection),
                handler: me.toggleCollapse,
                scope: me
            });

            
            if (me.collapseFirst) {
                me.tools.unshift(me.collapseTool);
            }
        }

        
        me.addTools();

        
        if (me.closable) {
            me.addClsWithUI('closable');
            me.addTool({
                type: 'close',
                handler: Ext.Function.bind(me.close, this, [])
            });
        }

        
        if (me.collapseTool && !me.collapseFirst) {
            me.tools.push(me.collapseTool);
        }
    },

    
    addTools: Ext.emptyFn,

    
    close: function() {
        if (this.fireEvent('beforeclose', this) !== false) {
            this.doClose();
        }
    },

    
    doClose: function() {
        this.fireEvent('close', this);
        this[this.closeAction]();
    },

    onRender: function(ct, position) {
        var me = this,
            topContainer;

        
        
        me.initTools();

        
        me.updateHeader();

        
        
        if (me.collapsed) {
            me.collapsed = false;
            topContainer = me.findLayoutController();
            if (!me.hidden && topContainer) {
                topContainer.on({
                    afterlayout: function() {
                        me.collapse(null, false, true);
                    },
                    single: true
                });
            } else {
                me.afterComponentLayout = function() {
                    delete me.afterComponentLayout;
                    Ext.getClass(me).prototype.afterComponentLayout.apply(me, arguments);
                    me.collapse(null, false, true);
                };
            }
        }

        
        me.callParent(arguments);
    },

    
    updateHeader: function(force) {
        var me = this,
            header = me.header,
            title = me.title,
            tools = me.tools;

        if (!me.preventHeader && (force || title || (tools && tools.length))) {
            if (!header) {
                header = me.header = Ext.create('Ext.panel.Header', {
                    title       : title,
                    orientation : (me.headerPosition == 'left' || me.headerPosition == 'right') ? 'vertical' : 'horizontal',
                    dock        : me.headerPosition || 'top',
                    textCls     : me.headerTextCls,
                    iconCls     : me.iconCls,
                    baseCls     : me.baseCls + '-header',
                    tools       : tools,
                    ui          : me.ui,
                    indicateDrag: me.draggable,
                    border      : me.border,
                    frame       : me.frame && me.frameHeader,
                    ignoreParentFrame : me.frame || me.overlapHeader,
                    ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
                    listeners   : me.collapsible && me.titleCollapse ? {
                        click: me.toggleCollapse,
                        scope: me
                    } : null
                });
                me.addDocked(header, 0);

                
                
                me.tools = header.tools;
            }
            header.show();
            me.initHeaderAria();
        } else if (header) {
            header.hide();
        }
    },

    
    setUI: function(ui) {
        var me = this;

        me.callParent(arguments);

        if (me.header) {
            me.header.setUI(ui);
        }
    },

    
    getContentTarget: function() {
        return this.body;
    },

    getTargetEl: function() {
        return this.body || this.frameBody || this.el;
    },

    addTool: function(tool) {
        this.tools.push(tool);
        var header = this.header;
        if (header) {
            header.addTool(tool);
        }
        this.updateHeader();
    },

    getOppositeDirection: function(d) {
        var c = Ext.Component;
        switch (d) {
            case c.DIRECTION_TOP:
                return c.DIRECTION_BOTTOM;
            case c.DIRECTION_RIGHT:
                return c.DIRECTION_LEFT;
            case c.DIRECTION_BOTTOM:
                return c.DIRECTION_TOP;
            case c.DIRECTION_LEFT:
                return c.DIRECTION_RIGHT;
        }
    },

    
    collapse: function(direction, animate,  internal) {
        var me = this,
            c = Ext.Component,
            height = me.getHeight(),
            width = me.getWidth(),
            frameInfo,
            newSize = 0,
            dockedItems = me.dockedItems.items,
            dockedItemCount = dockedItems.length,
            i = 0,
            comp,
            pos,
            anim = {
                from: {
                    height: height,
                    width: width
                },
                to: {
                    height: height,
                    width: width
                },
                listeners: {
                    afteranimate: me.afterCollapse,
                    scope: me
                },
                duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration)
            },
            reExpander,
            reExpanderOrientation,
            reExpanderDock,
            getDimension,
            setDimension,
            collapseDimension;

        if (!direction) {
            direction = me.collapseDirection;
        }

        
        if (internal) {
            animate = false;
        } else if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) {
            return false;
        }

        reExpanderDock = direction;
        me.expandDirection = me.getOppositeDirection(direction);

        
        me.hiddenDocked = [];

        switch (direction) {
            case c.DIRECTION_TOP:
            case c.DIRECTION_BOTTOM:
                me.expandedSize = me.getHeight();
                reExpanderOrientation = 'horizontal';
                collapseDimension = 'height';
                getDimension = 'getHeight';
                setDimension = 'setHeight';

                
                
                
                for (; i < dockedItemCount; i++) {
                    comp = dockedItems[i];
                    if (comp.isVisible()) {
                        if (comp.isHeader && (!comp.dock || comp.dock == 'top' || comp.dock == 'bottom')) {
                            reExpander = comp;
                        } else {
                            me.hiddenDocked.push(comp);
                        }
                    }
                }

                if (direction == Ext.Component.DIRECTION_BOTTOM) {
                    pos = me.getPosition()[1] - Ext.fly(me.el.dom.offsetParent).getRegion().top;
                    anim.from.top = pos;
                }
                break;

            case c.DIRECTION_LEFT:
            case c.DIRECTION_RIGHT:
                me.expandedSize = me.getWidth();
                reExpanderOrientation = 'vertical';
                collapseDimension = 'width';
                getDimension = 'getWidth';
                setDimension = 'setWidth';

                
                
                
                for (; i < dockedItemCount; i++) {
                    comp = dockedItems[i];
                    if (comp.isVisible()) {
                        if (comp.isHeader && (comp.dock == 'left' || comp.dock == 'right')) {
                            reExpander = comp;
                        } else {
                            me.hiddenDocked.push(comp);
                        }
                    }
                }

                if (direction == Ext.Component.DIRECTION_RIGHT) {
                    pos = me.getPosition()[0] - Ext.fly(me.el.dom.offsetParent).getRegion().left;
                    anim.from.left = pos;
                }
                break;

            default:
                throw('Panel collapse must be passed a valid Component collapse direction');
        }

        
        
        me.setAutoScroll(false);
        me.suspendLayout = true;
        me.body.setVisibilityMode(Ext.core.Element.DISPLAY);

        
        if (animate && me.collapseTool) {
            me.collapseTool.disable();
        }

        
        me.addClsWithUI(me.collapsedCls);
        
        
        

        
        if (reExpander) {
            
            reExpander.addClsWithUI(me.collapsedCls);
            reExpander.addClsWithUI(me.collapsedCls + '-' + reExpander.dock);
            if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
                reExpander.addClsWithUI(me.collapsedCls + '-border-' + reExpander.dock);
            }

            frameInfo = reExpander.getFrameInfo();
                        
            
            newSize = reExpander[getDimension]() + (frameInfo ? frameInfo[direction] : 0);

            
            reExpander.removeClsWithUI(me.collapsedCls);
            reExpander.removeClsWithUI(me.collapsedCls + '-' + reExpander.dock);              
            if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
                reExpander.removeClsWithUI(me.collapsedCls + '-border-' + reExpander.dock);
            }
        }
        
        else {
            reExpander = {
                hideMode: 'offsets',
                temporary: true,
                title: me.title,
                orientation: reExpanderOrientation,
                dock: reExpanderDock,
                textCls: me.headerTextCls,
                iconCls: me.iconCls,
                baseCls: me.baseCls + '-header',
                ui: me.ui,
                frame: me.frame && me.frameHeader,
                ignoreParentFrame: me.frame || me.overlapHeader,
                indicateDrag: me.draggable,
                cls: me.baseCls + '-collapsed-placeholder ' + ' ' + Ext.baseCSSPrefix + 'docked ' + me.baseCls + '-' + me.ui + '-collapsed',
                renderTo: me.el
            };
            if (!me.hideCollapseTool) {
                reExpander[(reExpander.orientation == 'horizontal') ? 'tools' : 'items'] = [{
                    xtype: 'tool',
                    type: 'expand-' + me.expandDirection,
                    handler: me.toggleCollapse,
                    scope: me
                }];
            }

            
            
            reExpander = me.reExpander = Ext.create('Ext.panel.Header', reExpander);
            newSize = reExpander[getDimension]() + ((reExpander.frame) ? reExpander.frameSize[direction] : 0);
            reExpander.hide();

            
            me.insertDocked(0, reExpander);
        }

        me.reExpander = reExpander;
        me.reExpander.addClsWithUI(me.collapsedCls);
        me.reExpander.addClsWithUI(me.collapsedCls + '-' + reExpander.dock);
        if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
            me.reExpander.addClsWithUI(me.collapsedCls + '-border-' + me.reExpander.dock);
        }

        
        if (direction == Ext.Component.DIRECTION_RIGHT) {
            anim.to.left = pos + (width - newSize);
        } else if (direction == Ext.Component.DIRECTION_BOTTOM) {
            anim.to.top = pos + (height - newSize);
        }

        
        anim.to[collapseDimension] = newSize;

        
        me.savedFlex = me.flex;
        me.savedMinWidth = me.minWidth;
        me.savedMinHeight = me.minHeight;
        me.minWidth = 0;
        me.minHeight = 0;
        delete me.flex;

        if (animate) {
            me.animate(anim);
        } else {
            
            
            me.uncollapsedSize = { width: me.width, height: me.height };

            me.setSize(anim.to.width, anim.to.height);
            if (Ext.isDefined(anim.to.left) || Ext.isDefined(anim.to.top)) {
                me.setPosition(anim.to.left, anim.to.top);
            }
            me.afterCollapse(false, internal);
        }
        return me;
    },

    afterCollapse: function(animated, internal) {
        var me = this,
            i = 0,
            l = me.hiddenDocked.length;

        me.minWidth = me.savedMinWidth;
        me.minHeight = me.savedMinHeight;

        me.body.hide();
        for (; i < l; i++) {
            me.hiddenDocked[i].hide();
        }
        if (me.reExpander) {
            me.reExpander.updateFrame();
            me.reExpander.show();
        }
        me.collapsed = true;

        if (!internal) {
            me.doComponentLayout();
        }

        if (me.resizer) {
            me.resizer.disable();
        }

        
        if (me.collapseTool) {
            me.collapseTool.setType('expand-' + me.expandDirection);
        }
        if (!internal) {
            me.fireEvent('collapse', me);
        }

        
        if (animated && me.collapseTool) {
            me.collapseTool.enable();
        }
    },

    
    expand: function(animate) {
        if (!this.collapsed || this.fireEvent('beforeexpand', this, animate) === false) {
            return false;
        }

        
        if (this.uncollapsedSize) {
            Ext.Object.each(this.uncollapsedSize, function (name, value) {
                if (Ext.isDefined(value)) {
                    this[name] = value;
                } else {
                    delete this[name];
                }
            }, this);
            delete this.uncollapsedSize;
        }

        var me = this,
            i = 0,
            l = me.hiddenDocked.length,
            direction = me.expandDirection,
            height = me.getHeight(),
            width = me.getWidth(),
            pos, anim, satisfyJSLint;

        
        if (animate && me.collapseTool) {
            me.collapseTool.disable();
        }

        
        
        for (; i < l; i++) {
            me.hiddenDocked[i].hidden = false;
            me.hiddenDocked[i].el.show();
        }
        if (me.reExpander) {
            if (me.reExpander.temporary) {
                me.reExpander.hide();
            } else {
                me.reExpander.removeClsWithUI(me.collapsedCls);
                me.reExpander.removeClsWithUI(me.collapsedCls + '-' + me.reExpander.dock);
                if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
                    me.reExpander.removeClsWithUI(me.collapsedCls + '-border-' + me.reExpander.dock);
                }
                me.reExpander.updateFrame();
            }
        }

        
        if (me.collapseTool) {
            me.collapseTool.setType('collapse-' + me.collapseDirection);
        }

        
        me.collapsed = false;

        
        me.body.show();

        
        me.removeClsWithUI(me.collapsedCls);
        
        
        

        anim = {
            to: {
            },
            from: {
                height: height,
                width: width
            },
            listeners: {
                afteranimate: me.afterExpand,
                scope: me
            }
        };

        if ((direction == Ext.Component.DIRECTION_TOP) || (direction == Ext.Component.DIRECTION_BOTTOM)) {

            
            if (me.autoHeight) {
                me.setCalculatedSize(me.width, null);
                anim.to.height = me.getHeight();

                
                me.setCalculatedSize(me.width, anim.from.height);
            }
            
            
            else if (me.savedFlex) {
                me.flex = me.savedFlex;
                anim.to.height = me.ownerCt.layout.calculateChildBox(me).height;
                delete me.flex;
            }
            
            else {
                anim.to.height = me.expandedSize;
            }

            
            if (direction == Ext.Component.DIRECTION_TOP) {
                pos = me.getPosition()[1] - Ext.fly(me.el.dom.offsetParent).getRegion().top;
                anim.from.top = pos;
                anim.to.top = pos - (anim.to.height - height);
            }
        } else if ((direction == Ext.Component.DIRECTION_LEFT) || (direction == Ext.Component.DIRECTION_RIGHT)) {

            
            if (me.autoWidth) {
                me.setCalculatedSize(null, me.height);
                anim.to.width = me.getWidth();

                
                me.setCalculatedSize(anim.from.width, me.height);
            }
            
            
            else if (me.savedFlex) {
                me.flex = me.savedFlex;
                anim.to.width = me.ownerCt.layout.calculateChildBox(me).width;
                delete me.flex;
            }
            
            else {
                anim.to.width = me.expandedSize;
            }

            
            if (direction == Ext.Component.DIRECTION_LEFT) {
                pos = me.getPosition()[0] - Ext.fly(me.el.dom.offsetParent).getRegion().left;
                anim.from.left = pos;
                anim.to.left = pos - (anim.to.width - width);
            }
        }

        if (animate) {
            me.animate(anim);
        } else {
            me.setSize(anim.to.width, anim.to.height);
            if (anim.to.x) {
                me.setLeft(anim.to.x);
            }
            if (anim.to.y) {
                me.setTop(anim.to.y);
            }
            me.afterExpand(false);
        }

        return me;
    },

    afterExpand: function(animated) {
        var me = this;
        me.setAutoScroll(me.initialConfig.autoScroll);

        
        if (me.savedFlex) {
            me.flex = me.savedFlex;
            delete me.savedFlex;
            delete me.width;
            delete me.height;
        }

        
        delete me.suspendLayout;
        if (animated && me.ownerCt) {
            me.ownerCt.doLayout();
        }

        if (me.resizer) {
            me.resizer.enable();
        }

        me.fireEvent('expand', me);

        
        if (animated && me.collapseTool) {
            me.collapseTool.enable();
        }
    },

    
    toggleCollapse: function() {
        if (this.collapsed) {
            this.expand(this.animCollapse);
        } else {
            this.collapse(this.collapseDirection, this.animCollapse);
        }
        return this;
    },

    
    getKeyMap : function(){
        if(!this.keyMap){
            this.keyMap = Ext.create('Ext.util.KeyMap', this.el, this.keys);
        }
        return this.keyMap;
    },

    
    initDraggable : function(){
        
        this.dd = Ext.create('Ext.panel.DD', this, Ext.isBoolean(this.draggable) ? null : this.draggable);
    },

    
    ghostTools : function() {
        var tools = [],
            origTools = this.initialConfig.tools;

        if (origTools) {
            Ext.each(origTools, function(tool) {
                
                
                
                
                tools.push({
                    type: tool.type
                });
            });
        }
        else {
            tools = [{
                type: 'placeholder'
            }];
        }
        return tools;
    },

    
    ghost: function(cls) {
        var me = this,
            ghostPanel = me.ghostPanel,
            box = me.getBox();

        if (!ghostPanel) {
            ghostPanel = Ext.create('Ext.panel.Panel', {
                renderTo: document.body,
                floating: {
                    shadow: false
                },
                frame: Ext.supports.CSS3BorderRadius ? me.frame : false,
                title: me.title,
                overlapHeader: me.overlapHeader,
                headerPosition: me.headerPosition,
                width: me.getWidth(),
                height: me.getHeight(),
                iconCls: me.iconCls,
                baseCls: me.baseCls,
                tools: me.ghostTools(),
                cls: me.baseCls + '-ghost ' + (cls ||'')
            });
            me.ghostPanel = ghostPanel;
        }
        ghostPanel.floatParent = me.floatParent;
        if (me.floating) {
            ghostPanel.setZIndex(Ext.Number.from(me.el.getStyle('zIndex'), 0));
        } else {
            ghostPanel.toFront();
        }
        ghostPanel.el.show();
        ghostPanel.setPosition(box.x, box.y);
        ghostPanel.setSize(box.width, box.height);
        me.el.hide();
        if (me.floatingItems) {
            me.floatingItems.hide();
        }
        return ghostPanel;
    },

    
    unghost: function(show, matchPosition) {
        var me = this;
        if (!me.ghostPanel) {
            return;
        }
        if (show !== false) {
            me.el.show();
            if (matchPosition !== false) {
                me.setPosition(me.ghostPanel.getPosition());
            }
            if (me.floatingItems) {
                me.floatingItems.show();
            }
            Ext.defer(me.focus, 10, me);
        }
        me.ghostPanel.el.hide();
    },

    initResizable: function(resizable) {
        if (this.collapsed) {
            resizable.disabled = true;
        }
        this.callParent([resizable]);
    }
});



Ext.define('Ext.layout.component.Tip', {

    

    alias: ['layout.tip'],

    extend: 'Ext.layout.component.Dock',

    

    type: 'tip',
    
    onLayout: function(width, height) {
        var me = this,
            owner = me.owner,
            el = owner.el,
            minWidth,
            maxWidth,
            naturalWidth,
            constrainedWidth,
            xy = el.getXY();

        
        el.setXY([-9999,-9999]);

        
        this.callParent(arguments);

        
        if (!Ext.isNumber(width)) {
            minWidth = owner.minWidth;
            maxWidth = owner.maxWidth;
            
            if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
                constrainedWidth = me.doAutoWidth();
            } else {
                naturalWidth = el.getWidth();
            }
            if (naturalWidth < minWidth) {
                constrainedWidth = minWidth;
            }
            else if (naturalWidth > maxWidth) {
                constrainedWidth = maxWidth;
            }
            if (constrainedWidth) {
                this.callParent([constrainedWidth, height]);
            }
        }

        
        el.setXY(xy);
    },
    
    doAutoWidth: function(){
        var me = this,
            owner = me.owner,
            body = owner.body,
            width = body.getTextWidth();
            
        if (owner.header) {
            width = Math.max(width, owner.header.getWidth());
        }
        if (!Ext.isDefined(me.frameWidth)) {
            me.frameWidth = owner.el.getWidth() - body.getWidth();
        }
        width += me.frameWidth + body.getPadding('lr');
        return width;
    }
});


Ext.define('Ext.tip.Tip', {
    extend: 'Ext.panel.Panel',
    requires: [ 'Ext.layout.component.Tip' ],
    alternateClassName: 'Ext.Tip',
    
    
    
    minWidth : 40,
    
    maxWidth : 300,
    
    shadow : "sides",

    
    defaultAlign : "tl-bl?",
    
    constrainPosition : true,

    
    frame: false,

    
    autoRender: true,
    hidden: true,
    baseCls: Ext.baseCSSPrefix + 'tip',
    floating: {
        shadow: true,
        shim: true,
        constrain: true
    },
    focusOnToFront: false,
    componentLayout: 'tip',

    closeAction: 'hide',

    ariaRole: 'tooltip',

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

        
        this.constrain = this.constrain || this.constrainPosition;
    },

    
    showAt : function(xy){
        var me = this;
        this.callParent();
        
        if (me.isVisible()) {
            me.setPagePosition(xy[0], xy[1]);
            if (me.constrainPosition || me.constrain) {
                me.doConstrain();
            }
            me.toFront(true);
        }
    },

    
    showBy : function(el, pos) {
        this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));
    },

    
    initDraggable : function(){
        var me = this;
        me.draggable = {
            el: me.getDragEl(),
            delegate: me.header.el,
            constrain: me,
            constrainTo: me.el.dom.parentNode
        };
        
        Ext.Component.prototype.initDraggable.call(me);
    },

    
    ghost: undefined,
    unghost: undefined
});


Ext.define('Ext.tip.ToolTip', {
    extend: 'Ext.tip.Tip',
    alias: 'widget.tooltip',
    alternateClassName: 'Ext.ToolTip',
    
    
    
    
    showDelay: 500,
    
    hideDelay: 200,
    
    dismissDelay: 5000,
    
    
    trackMouse: false,
    
    
    anchorToTarget: true,
    
    anchorOffset: 0,
    

    
    targetCounter: 0,
    quickShowInterval: 250,

    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);
        me.lastActive = new Date();
        me.setTarget(me.target);
        me.origAnchor = me.anchor;
    },

    
    onRender: function(ct, position) {
        var me = this;
        me.callParent(arguments);
        me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
        me.anchorEl = me.el.createChild({
            cls: Ext.baseCSSPrefix + 'tip-anchor ' + me.anchorCls
        });
    },

    
    afterRender: function() {
        var me = this,
            zIndex;

        me.callParent(arguments);
        zIndex = parseInt(me.el.getZIndex(), 10) || 0;
        me.anchorEl.setStyle('z-index', zIndex + 1).setVisibilityMode(Ext.core.Element.DISPLAY);
    },

    
    setTarget: function(target) {
        var me = this,
            t = Ext.get(target),
            tg;

        if (me.target) {
            tg = Ext.get(me.target);
            me.mun(tg, 'mouseover', me.onTargetOver, me);
            me.mun(tg, 'mouseout', me.onTargetOut, me);
            me.mun(tg, 'mousemove', me.onMouseMove, me);
        }
        
        me.target = t;
        if (t) {
            
            me.mon(t, {
                
                
                freezeEvent: true,

                mouseover: me.onTargetOver,
                mouseout: me.onTargetOut,
                mousemove: me.onMouseMove,
                scope: me
            });
        }
        if (me.anchor) {
            me.anchorTarget = me.target;
        }
    },

    
    onMouseMove: function(e) {
        var me = this,
            t = me.delegate ? e.getTarget(me.delegate) : me.triggerElement = true,
            xy;
        if (t) {
            me.targetXY = e.getXY();
            if (t === me.triggerElement) {
                if (!me.hidden && me.trackMouse) {
                    xy = me.getTargetXY();
                    if (me.constrainPosition) {
                        xy = me.el.adjustForConstraints(xy, me.el.dom.parentNode);
                    }
                    me.setPagePosition(xy);
                }
            } else {
                me.hide();
                me.lastActive = new Date(0);
                me.onTargetOver(e);
            }
        } else if ((!me.closable && me.isVisible()) && me.autoHide !== false) {
            me.hide();
        }
    },

    
    getTargetXY: function() {
        var me = this,
            mouseOffset;
        if (me.delegate) {
            me.anchorTarget = me.triggerElement;
        }
        if (me.anchor) {
            me.targetCounter++;
                var offsets = me.getOffsets(),
                    xy = (me.anchorToTarget && !me.trackMouse) ? me.el.getAlignToXY(me.anchorTarget, me.getAnchorAlign()) : me.targetXY,
                    dw = Ext.core.Element.getViewWidth() - 5,
                    dh = Ext.core.Element.getViewHeight() - 5,
                    de = document.documentElement,
                    bd = document.body,
                    scrollX = (de.scrollLeft || bd.scrollLeft || 0) + 5,
                    scrollY = (de.scrollTop || bd.scrollTop || 0) + 5,
                    axy = [xy[0] + offsets[0], xy[1] + offsets[1]],
                    sz = me.getSize(),
                    constrainPosition = me.constrainPosition;

            me.anchorEl.removeCls(me.anchorCls);

            if (me.targetCounter < 2 && constrainPosition) {
                if (axy[0] < scrollX) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'l-r';
                        if (me.mouseOffset) {
                            me.mouseOffset[0] *= -1;
                        }
                    }
                    me.anchor = 'left';
                    return me.getTargetXY();
                }
                if (axy[0] + sz.width > dw) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'r-l';
                        if (me.mouseOffset) {
                            me.mouseOffset[0] *= -1;
                        }
                    }
                    me.anchor = 'right';
                    return me.getTargetXY();
                }
                if (axy[1] < scrollY) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 't-b';
                        if (me.mouseOffset) {
                            me.mouseOffset[1] *= -1;
                        }
                    }
                    me.anchor = 'top';
                    return me.getTargetXY();
                }
                if (axy[1] + sz.height > dh) {
                    if (me.anchorToTarget) {
                        me.defaultAlign = 'b-t';
                        if (me.mouseOffset) {
                            me.mouseOffset[1] *= -1;
                        }
                    }
                    me.anchor = 'bottom';
                    return me.getTargetXY();
                }
            }

            me.anchorCls = Ext.baseCSSPrefix + 'tip-anchor-' + me.getAnchorPosition();
            me.anchorEl.addCls(me.anchorCls);
            me.targetCounter = 0;
            return axy;
        } else {
            mouseOffset = me.getMouseOffset();
            return (me.targetXY) ? [me.targetXY[0] + mouseOffset[0], me.targetXY[1] + mouseOffset[1]] : mouseOffset;
        }
    },

    getMouseOffset: function() {
        var me = this,
        offset = me.anchor ? [0, 0] : [15, 18];
        if (me.mouseOffset) {
            offset[0] += me.mouseOffset[0];
            offset[1] += me.mouseOffset[1];
        }
        return offset;
    },

    
    getAnchorPosition: function() {
        var me = this,
            m;
        if (me.anchor) {
            me.tipAnchor = me.anchor.charAt(0);
        } else {
            m = me.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
            if (!m) {
                Ext.Error.raise('The AnchorTip.defaultAlign value "' + me.defaultAlign + '" is invalid.');
            }
            me.tipAnchor = m[1].charAt(0);
        }

        switch (me.tipAnchor) {
        case 't':
            return 'top';
        case 'b':
            return 'bottom';
        case 'r':
            return 'right';
        }
        return 'left';
    },

    
    getAnchorAlign: function() {
        switch (this.anchor) {
        case 'top':
            return 'tl-bl';
        case 'left':
            return 'tl-tr';
        case 'right':
            return 'tr-tl';
        default:
            return 'bl-tl';
        }
    },

    
    getOffsets: function() {
        var me = this,
            mouseOffset,
            offsets,
            ap = me.getAnchorPosition().charAt(0);
        if (me.anchorToTarget && !me.trackMouse) {
            switch (ap) {
            case 't':
                offsets = [0, 9];
                break;
            case 'b':
                offsets = [0, -13];
                break;
            case 'r':
                offsets = [ - 13, 0];
                break;
            default:
                offsets = [9, 0];
                break;
            }
        } else {
            switch (ap) {
            case 't':
                offsets = [ - 15 - me.anchorOffset, 30];
                break;
            case 'b':
                offsets = [ - 19 - me.anchorOffset, -13 - me.el.dom.offsetHeight];
                break;
            case 'r':
                offsets = [ - 15 - me.el.dom.offsetWidth, -13 - me.anchorOffset];
                break;
            default:
                offsets = [25, -13 - me.anchorOffset];
                break;
            }
        }
        mouseOffset = me.getMouseOffset();
        offsets[0] += mouseOffset[0];
        offsets[1] += mouseOffset[1];

        return offsets;
    },

    
    onTargetOver: function(e) {
        var me = this,
            t;

        if (me.disabled || e.within(me.target.dom, true)) {
            return;
        }
        t = e.getTarget(me.delegate);
        if (t) {
            me.triggerElement = t;
            me.clearTimer('hide');
            me.targetXY = e.getXY();
            me.delayShow();
        }
    },

    
    delayShow: function() {
        var me = this;
        if (me.hidden && !me.showTimer) {
            if (Ext.Date.getElapsed(me.lastActive) < me.quickShowInterval) {
                me.show();
            } else {
                me.showTimer = Ext.defer(me.show, me.showDelay, me);
            }
        }
        else if (!me.hidden && me.autoHide !== false) {
            me.show();
        }
    },

    
    onTargetOut: function(e) {
        var me = this;
        if (me.disabled || e.within(me.target.dom, true)) {
            return;
        }
        me.clearTimer('show');
        if (me.autoHide !== false) {
            me.delayHide();
        }
    },

    
    delayHide: function() {
        var me = this;
        if (!me.hidden && !me.hideTimer) {
            me.hideTimer = Ext.defer(me.hide, me.hideDelay, me);
        }
    },

    
    hide: function() {
        var me = this;
        me.clearTimer('dismiss');
        me.lastActive = new Date();
        if (me.anchorEl) {
            me.anchorEl.hide();
        }
        me.callParent(arguments);
        delete me.triggerElement;
    },

    
    show: function() {
        var me = this;

        
        
        this.callParent();
        if (this.hidden === false) {
            me.setPagePosition(-10000, -10000);

            if (me.anchor) {
                me.anchor = me.origAnchor;
            }
            me.showAt(me.getTargetXY());

            if (me.anchor) {
                me.syncAnchor();
                me.anchorEl.show();
            } else {
                me.anchorEl.hide();
            }
        }
    },

    
    showAt: function(xy) {
        var me = this;
        me.lastActive = new Date();
        me.clearTimers();

        
        if (!me.isVisible()) {
            this.callParent(arguments);
        }

        
        if (me.isVisible()) {
            me.setPagePosition(xy[0], xy[1]);
            if (me.constrainPosition || me.constrain) {
                me.doConstrain();
            }
            me.toFront(true);
        }

        if (me.dismissDelay && me.autoHide !== false) {
            me.dismissTimer = Ext.defer(me.hide, me.dismissDelay, me);
        }
        if (me.anchor) {
            me.syncAnchor();
            if (!me.anchorEl.isVisible()) {
                me.anchorEl.show();
            }
        } else {
            me.anchorEl.hide();
        }
    },

    
    syncAnchor: function() {
        var me = this,
            anchorPos,
            targetPos,
            offset;
        switch (me.tipAnchor.charAt(0)) {
        case 't':
            anchorPos = 'b';
            targetPos = 'tl';
            offset = [20 + me.anchorOffset, 1];
            break;
        case 'r':
            anchorPos = 'l';
            targetPos = 'tr';
            offset = [ - 1, 12 + me.anchorOffset];
            break;
        case 'b':
            anchorPos = 't';
            targetPos = 'bl';
            offset = [20 + me.anchorOffset, -1];
            break;
        default:
            anchorPos = 'r';
            targetPos = 'tl';
            offset = [1, 12 + me.anchorOffset];
            break;
        }
        me.anchorEl.alignTo(me.el, anchorPos + '-' + targetPos, offset);
    },

    
    setPagePosition: function(x, y) {
        var me = this;
        me.callParent(arguments);
        if (me.anchor) {
            me.syncAnchor();
        }
    },

    
    clearTimer: function(name) {
        name = name + 'Timer';
        clearTimeout(this[name]);
        delete this[name];
    },

    
    clearTimers: function() {
        var me = this;
        me.clearTimer('show');
        me.clearTimer('dismiss');
        me.clearTimer('hide');
    },

    
    onShow: function() {
        var me = this;
        me.callParent();
        me.mon(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
    },

    
    onHide: function() {
        var me = this;
        me.callParent();
        me.mun(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
    },

    
    onDocMouseDown: function(e) {
        var me = this;
        if (me.autoHide !== true && !me.closable && !e.within(me.el.dom)) {
            me.disable();
            Ext.defer(me.doEnable, 100, me);
        }
    },

    
    doEnable: function() {
        if (!this.isDestroyed) {
            this.enable();
        }
    },

    
    onDisable: function() {
        this.callParent();
        this.clearTimers();
        this.hide();
    },

    beforeDestroy: function() {
        var me = this;
        me.clearTimers();
        Ext.destroy(me.anchorEl);
        delete me.anchorEl;
        delete me.target;
        delete me.anchorTarget;
        delete me.triggerElement;
        me.callParent();
    },

    
    onDestroy: function() {
        Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
        this.callParent();
    }
});


Ext.define('Ext.tip.QuickTip', {
    extend: 'Ext.tip.ToolTip',
    alternateClassName: 'Ext.QuickTip',
    
    
    interceptTitles : false,

    
    title: '&#160;',

    
    tagConfig : {
        namespace : "data-",
        attribute : "qtip",
        width : "qwidth",
        target : "target",
        title : "qtitle",
        hide : "hide",
        cls : "qclass",
        align : "qalign",
        anchor : "anchor"
    },

    
    initComponent : function(){
        var me = this;
        
        me.target = me.target || Ext.getDoc();
        me.targets = me.targets || {};
        me.callParent();
    },

    
    register : function(config){
        var configs = Ext.isArray(config) ? config : arguments,
            i = 0,
            len = configs.length,
            target, j, targetLen;
            
        for (; i < len; i++) {
            config = configs[i];
            target = config.target;
            if (target) {
                if (Ext.isArray(target)) {
                    for (j = 0, targetLen = target.length; j < targetLen; j++) {
                        this.targets[Ext.id(target[j])] = config;
                    }
                } else{
                    this.targets[Ext.id(target)] = config;
                }
            }
        }
    },

    
    unregister : function(el){
        delete this.targets[Ext.id(el)];
    },
    
    
    cancelShow: function(el){
        var me = this,
            activeTarget = me.activeTarget;
            
        el = Ext.get(el).dom;
        if (me.isVisible()) {
            if (activeTarget && activeTarget.el == el) {
                me.hide();
            }
        } else if (activeTarget && activeTarget.el == el) {
            me.clearTimer('show');
        }
    },
    
    getTipCfg: function(e) {
        var t = e.getTarget(),
            ttp, 
            cfg;
        
        if(this.interceptTitles && t.title && Ext.isString(t.title)){
            ttp = t.title;
            t.qtip = ttp;
            t.removeAttribute("title");
            e.preventDefault();
        } 
        else {            
            cfg = this.tagConfig;
            t = e.getTarget('[' + cfg.namespace + cfg.attribute + ']');
            if (t) {
                ttp = t.getAttribute(cfg.namespace + cfg.attribute);
            }
        }
        return ttp;
    },

    
    onTargetOver : function(e){
        var me = this,
            target = e.getTarget(),
            elTarget,
            cfg,
            ns,
            ttp,
            autoHide;
        
        if (me.disabled) {
            return;
        }

        
        
        
        me.targetXY = e.getXY();

        if(!target || target.nodeType !== 1 || target == document || target == document.body){
            return;
        }
        
        if (me.activeTarget && ((target == me.activeTarget.el) || Ext.fly(me.activeTarget.el).contains(target))) {
            me.clearTimer('hide');
            me.show();
            return;
        }
        
        if (target) {
            Ext.Object.each(me.targets, function(key, value) {
                var targetEl = Ext.fly(value.target);
                if (targetEl && (targetEl.dom === target || targetEl.contains(target))) {
                    elTarget = targetEl.dom;
                    return false;
                }
            });
            if (elTarget) {
                me.activeTarget = me.targets[elTarget.id];
                me.activeTarget.el = target;
                me.anchor = me.activeTarget.anchor;
                if (me.anchor) {
                    me.anchorTarget = target;
                }
                me.delayShow();
                return;
            }
        }

        elTarget = Ext.get(target);
        cfg = me.tagConfig;
        ns = cfg.namespace; 
        ttp = me.getTipCfg(e);
        
        if (ttp) {
            autoHide = elTarget.getAttribute(ns + cfg.hide);
                 
            me.activeTarget = {
                el: target,
                text: ttp,
                width: +elTarget.getAttribute(ns + cfg.width) || null,
                autoHide: autoHide != "user" && autoHide !== 'false',
                title: elTarget.getAttribute(ns + cfg.title),
                cls: elTarget.getAttribute(ns + cfg.cls),
                align: elTarget.getAttribute(ns + cfg.align)
                
            };
            me.anchor = elTarget.getAttribute(ns + cfg.anchor);
            if (me.anchor) {
                me.anchorTarget = target;
            }
            me.delayShow();
        }
    },

    
    onTargetOut : function(e){
        var me = this;
        
        
        if (me.activeTarget && e.within(me.activeTarget.el) && !me.getTipCfg(e)) {
            return;
        }

        me.clearTimer('show');
        if (me.autoHide !== false) {
            me.delayHide();
        }
    },

    
    showAt : function(xy){
        var me = this,
            target = me.activeTarget;
        
        if (target) {
            if (!me.rendered) {
                me.render(Ext.getBody());
                me.activeTarget = target;
            }
            if (target.title) {
                me.setTitle(target.title || '');
                me.header.show();
            } else {
                me.header.hide();
            }
            me.body.update(target.text);
            me.autoHide = target.autoHide;
            me.dismissDelay = target.dismissDelay || me.dismissDelay;
            if (me.lastCls) {
                me.el.removeCls(me.lastCls);
                delete me.lastCls;
            }
            if (target.cls) {
                me.el.addCls(target.cls);
                me.lastCls = target.cls;
            }

            me.setWidth(target.width);
            
            if (me.anchor) {
                me.constrainPosition = false;
            } else if (target.align) { 
                xy = me.el.getAlignToXY(target.el, target.align);
                me.constrainPosition = false;
            }else{
                me.constrainPosition = true;
            }
        }
        me.callParent([xy]);
    },

    
    hide: function(){
        delete this.activeTarget;
        this.callParent();
    }
});


Ext.define('Ext.tip.QuickTipManager', function() {
    var tip,
        disabled = false;

    return {
        requires: ['Ext.tip.QuickTip'],
        singleton: true,
        alternateClassName: 'Ext.QuickTips',

        
        init : function (autoRender, config) {
            if (!tip) {
                if (!Ext.isReady) {
                    Ext.onReady(function(){
                        Ext.tip.QuickTipManager.init(autoRender);
                    });
                    return;
                }

                var tipConfig = Ext.apply({ disabled: disabled }, config),
                    className = tipConfig.className,
                    xtype = tipConfig.xtype;

                if (className) {
                    delete tipConfig.className;
                } else if (xtype) {
                    className = 'widget.' + xtype;
                    delete tipConfig.xtype;
                }

                if (autoRender !== false) {
                    tipConfig.renderTo = document.body;

                    if (tipConfig.renderTo.tagName != 'BODY') { 
                        Ext.Error.raise({
                            sourceClass: 'Ext.tip.QuickTipManager',
                            sourceMethod: 'init',
                            msg: 'Cannot init QuickTipManager: no document body'
                        });
                    }
                }

                tip = Ext.create(className || 'Ext.tip.QuickTip', tipConfig);
            }
        },

        
        destroy: function() {
            if (tip) {
                var undef;
                tip.destroy();
                tip = undef;
            }
        },

        
        ddDisable : function(){
            
            if(tip && !disabled){
                tip.disable();
            }
        },

        
        ddEnable : function(){
            
            if(tip && !disabled){
                tip.enable();
            }
        },

        
        enable : function(){
            if(tip){
                tip.enable();
            }
            disabled = false;
        },

        
        disable : function(){
            if(tip){
                tip.disable();
            }
            disabled = true;
        },

        
        isEnabled : function(){
            return tip !== undefined && !tip.disabled;
        },

        
        getQuickTip : function(){
            return tip;
        },

        
        register : function(){
            tip.register.apply(tip, arguments);
        },

        
        unregister : function(){
            tip.unregister.apply(tip, arguments);
        },

        
        tips : function(){
            tip.register.apply(tip, arguments);
        }
    };
}());

Ext.define('Ext.app.Application', {
    extend: 'Ext.app.Controller',

    requires: [
        'Ext.ModelManager',
        'Ext.data.Model',
        'Ext.data.StoreManager',
        'Ext.tip.QuickTipManager',
        'Ext.ComponentManager',
        'Ext.app.EventBus'
    ],

    

    
    scope: undefined,

    
    enableQuickTips: true,

    

    
    appFolder: 'app',

    
    autoCreateViewport: false,

    constructor: function(config) {
        config = config || {};
        Ext.apply(this, config);

        var requires = config.requires || [];

        Ext.Loader.setPath(this.name, this.appFolder);

        if (this.paths) {
            Ext.Object.each(this.paths, function(key, value) {
                Ext.Loader.setPath(key, value);
            });
        }

        this.callParent(arguments);

        this.eventbus = Ext.create('Ext.app.EventBus');

        var controllers = Ext.Array.from(this.controllers),
            ln = controllers && controllers.length,
            i, controller;

        this.controllers = Ext.create('Ext.util.MixedCollection');

        if (this.autoCreateViewport) {
            requires.push(this.getModuleClassName('Viewport', 'view'));
        }

        for (i = 0; i < ln; i++) {
            requires.push(this.getModuleClassName(controllers[i], 'controller'));
        }

        Ext.require(requires);

        Ext.onReady(function() {
            for (i = 0; i < ln; i++) {
                controller = this.getController(controllers[i]);
                controller.init(this);
            }

            this.onBeforeLaunch.call(this);
        }, this);
    },

    control: function(selectors, listeners, controller) {
        this.eventbus.control(selectors, listeners, controller);
    },

    
    launch: Ext.emptyFn,

    
    onBeforeLaunch: function() {
        if (this.enableQuickTips) {
            Ext.tip.QuickTipManager.init();
        }

        if (this.autoCreateViewport) {
            this.getView('Viewport').create();
        }

        this.launch.call(this.scope || this);
        this.launched = true;
        this.fireEvent('launch', this);

        this.controllers.each(function(controller) {
            controller.onLaunch(this);
        }, this);
    },

    getModuleClassName: function(name, type) {
        var namespace = Ext.Loader.getPrefix(name);

        if (namespace.length > 0 && namespace !== name) {
            return name;
        }

        return this.name + '.' + type + '.' + name;
    },

    getController: function(name) {
        var controller = this.controllers.get(name);

        if (!controller) {
            controller = Ext.create(this.getModuleClassName(name, 'controller'), {
                application: this,
                id: name
            });

            this.controllers.add(controller);
        }

        return controller;
    },

    getStore: function(name) {
        var store = Ext.StoreManager.get(name);

        if (!store) {
            store = Ext.create(this.getModuleClassName(name, 'store'), {
                storeId: name
            });
        }

        return store;
    },

    getModel: function(model) {
        model = this.getModuleClassName(model, 'model');

        return Ext.ModelManager.getModel(model);
    },

    getView: function(view) {
        view = this.getModuleClassName(view, 'view');

        return Ext.ClassManager.get(view);
    }
});


Ext.define('Ext.chart.Callout', {

    

    

    constructor: function(config) {
        if (config.callouts) {
            config.callouts.styles = Ext.applyIf(config.callouts.styles || {}, {
                color: "#000",
                font: "11px Helvetica, sans-serif"
            });
            this.callouts = Ext.apply(this.callouts || {}, config.callouts);
            this.calloutsArray = [];
        }
    },

    renderCallouts: function() {
        if (!this.callouts) {
            return;
        }

        var me = this,
            items = me.items,
            animate = me.chart.animate,
            config = me.callouts,
            styles = config.styles,
            group = me.calloutsArray,
            store = me.chart.store,
            len = store.getCount(),
            ratio = items.length / len,
            previouslyPlacedCallouts = [],
            i,
            count,
            j,
            p;
            
        for (i = 0, count = 0; i < len; i++) {
            for (j = 0; j < ratio; j++) {
                var item = items[count],
                    label = group[count],
                    storeItem = store.getAt(i),
                    display;
                
                display = config.filter(storeItem);
                
                if (!display && !label) {
                    count++;
                    continue;               
                }
                
                if (!label) {
                    group[count] = label = me.onCreateCallout(storeItem, item, i, display, j, count);
                }
                for (p in label) {
                    if (label[p] && label[p].setAttributes) {
                        label[p].setAttributes(styles, true);
                    }
                }
                if (!display) {
                    for (p in label) {
                        if (label[p]) {
                            if (label[p].setAttributes) {
                                label[p].setAttributes({
                                    hidden: true
                                }, true);
                            } else if(label[p].setVisible) {
                                label[p].setVisible(false);
                            }
                        }
                    }
                }
                config.renderer(label, storeItem);
                me.onPlaceCallout(label, storeItem, item, i, display, animate,
                                  j, count, previouslyPlacedCallouts);
                previouslyPlacedCallouts.push(label);
                count++;
            }
        }
        this.hideCallouts(count);
    },

    onCreateCallout: function(storeItem, item, i, display) {
        var me = this,
            group = me.calloutsGroup,
            config = me.callouts,
            styles = config.styles,
            width = styles.width,
            height = styles.height,
            chart = me.chart,
            surface = chart.surface,
            calloutObj = {
                
                
                lines: false
            };

        calloutObj.lines = surface.add(Ext.apply({},
        {
            type: 'path',
            path: 'M0,0',
            stroke: me.getLegendColor() || '#555'
        },
        styles));

        if (config.items) {
            calloutObj.panel = Ext.create('widget.panel', {
                style: "position: absolute;",    
                width: width,
                height: height,
                items: config.items,
                renderTo: chart.el
            });
        }

        return calloutObj;
    },

    hideCallouts: function(index) {
        var calloutsArray = this.calloutsArray,
            len = calloutsArray.length,
            co,
            p;
        while (len-->index) {
            co = calloutsArray[len];
            for (p in co) {
                if (co[p]) {
                    co[p].hide(true);
                }
            }
        }
    }
});


Ext.define('Ext.draw.CompositeSprite', {

    

    extend: 'Ext.util.MixedCollection',
    mixins: {
        animate: 'Ext.util.Animate'
    },

    
    isCompositeSprite: true,
    constructor: function(config) {
        var me = this;
        
        config = config || {};
        Ext.apply(me, config);

        me.addEvents(
            'mousedown',
            'mouseup',
            'mouseover',
            'mouseout',
            'click'
        );
        me.id = Ext.id(null, 'ext-sprite-group-');
        me.callParent();
    },

    
    onClick: function(e) {
        this.fireEvent('click', e);
    },

    
    onMouseUp: function(e) {
        this.fireEvent('mouseup', e);
    },

    
    onMouseDown: function(e) {
        this.fireEvent('mousedown', e);
    },

    
    onMouseOver: function(e) {
        this.fireEvent('mouseover', e);
    },

    
    onMouseOut: function(e) {
        this.fireEvent('mouseout', e);
    },

    attachEvents: function(o) {
        var me = this;
        
        o.on({
            scope: me,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            click: me.onClick
        });
    },

    
    add: function(key, o) {
        var result = this.callParent(arguments);
        this.attachEvents(result);
        return result;
    },

    insert: function(index, key, o) {
        return this.callParent(arguments);
    },

    
    remove: function(o) {
        var me = this;
        
        o.un({
            scope: me,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            click: me.onClick
        });
        me.callParent(arguments);
    },
    
    
    getBBox: function() {
        var i = 0,
            sprite,
            bb,
            items = this.items,
            len = this.length,
            infinity = Infinity,
            minX = infinity,
            maxHeight = -infinity,
            minY = infinity,
            maxWidth = -infinity,
            maxWidthBBox, maxHeightBBox;
        
        for (; i < len; i++) {
            sprite = items[i];
            if (sprite.el) {
                bb = sprite.getBBox();
                minX = Math.min(minX, bb.x);
                minY = Math.min(minY, bb.y);
                maxHeight = Math.max(maxHeight, bb.height + bb.y);
                maxWidth = Math.max(maxWidth, bb.width + bb.x);
            }
        }
        
        return {
            x: minX,
            y: minY,
            height: maxHeight - minY,
            width: maxWidth - minX
        };
    },

    
    setAttributes: function(attrs, redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].setAttributes(attrs, redraw);
        }
        return this;
    },

    
    hide: function(redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].hide(redraw);
        }
        return this;
    },

    
    show: function(redraw) {
        var i = 0,
            items = this.items,
            len = this.length;
            
        for (; i < len; i++) {
            items[i].show(redraw);
        }
        return this;
    },

    redraw: function() {
        var me = this,
            i = 0,
            items = me.items,
            surface = me.getSurface(),
            len = me.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.renderItem(items[i]);
            }
        }
        return me;
    },

    setStyle: function(obj) {
        var i = 0,
            items = this.items,
            len = this.length,
            item, el;
            
        for (; i < len; i++) {
            item = items[i];
            el = item.el;
            if (el) {
                el.setStyle(obj);
            }
        }
    },

    addCls: function(obj) {
        var i = 0,
            items = this.items,
            surface = this.getSurface(),
            len = this.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.addCls(items[i], obj);
            }
        }
    },

    removeCls: function(obj) {
        var i = 0,
            items = this.items,
            surface = this.getSurface(),
            len = this.length;
        
        if (surface) {
            for (; i < len; i++) {
                surface.removeCls(items[i], obj);
            }
        }
    },
    
    
    getSurface: function(){
        var first = this.first();
        if (first) {
            return first.surface;
        }
        return null;
    },
    
    
    destroy: function(){
        var me = this,
            surface = me.getSurface(),
            item;
            
        if (surface) {
            while (me.getCount() > 0) {
                item = me.first();
                me.remove(item);
                surface.remove(item);
            }
        }
        me.clearListeners();
    }
});



Ext.define('Ext.layout.component.Draw', {

    

    alias: 'layout.draw',

    extend: 'Ext.layout.component.Auto',

    

    type: 'draw',

    onLayout : function(width, height) {
        this.owner.surface.setSize(width, height);
        this.callParent(arguments);
    }
});

Ext.define('Ext.chart.theme.Theme', {

    

    requires: ['Ext.draw.Color'],

    

    theme: 'Base',
    themeAttrs: false,
    
    initTheme: function(theme) {
        var me = this,
            themes = Ext.chart.theme,
            key, gradients;
        if (theme) {
            theme = theme.split(':');
            for (key in themes) {
                if (key == theme[0]) {
                    gradients = theme[1] == 'gradients';
                    me.themeAttrs = new themes[key]({
                        useGradients: gradients
                    });
                    if (gradients) {
                        me.gradients = me.themeAttrs.gradients;
                    }
                    if (me.themeAttrs.background) {
                        me.background = me.themeAttrs.background;
                    }
                    return;
                }
            }
            Ext.Error.raise('No theme found named "' + theme + '"');
        }
    }
}, 

function() {
   

(function() {
    Ext.chart.theme = function(config, base) {
        config = config || {};
        var i = 0, l, colors, color,
            seriesThemes, markerThemes,
            seriesTheme, markerTheme, 
            key, gradients = [],
            midColor, midL;
        
        if (config.baseColor) {
            midColor = Ext.draw.Color.fromString(config.baseColor);
            midL = midColor.getHSL()[2];
            if (midL < 0.15) {
                midColor = midColor.getLighter(0.3);
            } else if (midL < 0.3) {
                midColor = midColor.getLighter(0.15);
            } else if (midL > 0.85) {
                midColor = midColor.getDarker(0.3);
            } else if (midL > 0.7) {
                midColor = midColor.getDarker(0.15);
            }
            config.colors = [ midColor.getDarker(0.3).toString(),
                              midColor.getDarker(0.15).toString(),
                              midColor.toString(),
                              midColor.getLighter(0.15).toString(),
                              midColor.getLighter(0.3).toString()];

            delete config.baseColor;
        }
        if (config.colors) {
            colors = config.colors.slice();
            markerThemes = base.markerThemes;
            seriesThemes = base.seriesThemes;
            l = colors.length;
            base.colors = colors;
            for (; i < l; i++) {
                color = colors[i];
                markerTheme = markerThemes[i] || {};
                seriesTheme = seriesThemes[i] || {};
                markerTheme.fill = seriesTheme.fill = markerTheme.stroke = seriesTheme.stroke = color;
                markerThemes[i] = markerTheme;
                seriesThemes[i] = seriesTheme;
            }
            base.markerThemes = markerThemes.slice(0, l);
            base.seriesThemes = seriesThemes.slice(0, l);
        
        }
        for (key in base) {
            if (key in config) {
                if (Ext.isObject(config[key]) && Ext.isObject(base[key])) {
                    Ext.apply(base[key], config[key]);
                } else {
                    base[key] = config[key];
                }
            }
        }
        if (config.useGradients) {
            colors = base.colors || (function () {
                var ans = [];
                for (i = 0, seriesThemes = base.seriesThemes, l = seriesThemes.length; i < l; i++) {
                    ans.push(seriesThemes[i].fill || seriesThemes[i].stroke);
                }
                return ans;
            })();
            for (i = 0, l = colors.length; i < l; i++) {
                midColor = Ext.draw.Color.fromString(colors[i]);
                if (midColor) {
                    color = midColor.getDarker(0.1).toString();
                    midColor = midColor.toString();
                    key = 'theme-' + midColor.substr(1) + '-' + color.substr(1);
                    gradients.push({
                        id: key,
                        angle: 45,
                        stops: {
                            0: {
                                color: midColor.toString()
                            },
                            100: {
                                color: color.toString()
                            }
                        }
                    });
                    colors[i] = 'url(#' + key + ')'; 
                }
            }
            base.gradients = gradients;
            base.colors = colors;
        }
        
        Ext.apply(this, base);
    };
})();
});


Ext.define('Ext.chart.Mask', {
    constructor: function(config) {
        var me = this;

        me.addEvents('select');

        if (config) {
            Ext.apply(me, config);
        }
        if (me.mask) {
            me.on('afterrender', function() {
                
                var comp = Ext.create('Ext.chart.MaskLayer', {
                    renderTo: me.el
                });
                comp.el.on({
                    'mousemove': function(e) {
                        me.onMouseMove(e);
                    },
                    'mouseup': function(e) {
                        me.resized(e);
                    }
                });
                
                var resizeHandler = Ext.create('Ext.resizer.Resizer', {
                    el: comp.el,
                    handles: 'all',
                    pinned: true
                });
                resizeHandler.on({
                    'resize': function(e) {
                        me.resized(e);    
                    }    
                });
                comp.initDraggable();
                me.maskType = me.mask;
                me.mask = comp;
                me.maskSprite = me.surface.add({
                    type: 'path',
                    path: ['M', 0, 0],
                    zIndex: 1001,
                    opacity: 0.7,
                    hidden: true,
                    stroke: '#444'
                });
            }, me, { single: true });
        }
    },
    
    resized: function(e) {
        var me = this,
            bbox = me.bbox || me.chartBBox,
            x = bbox.x,
            y = bbox.y,
            width = bbox.width,
            height = bbox.height,
            box = me.mask.getBox(true),
            max = Math.max,
            min = Math.min,
            staticX = box.x - x,
            staticY = box.y - y;
        
        staticX = max(staticX, x);
        staticY = max(staticY, y);
        staticX = min(staticX, width);
        staticY = min(staticY, height);
        box.x = staticX;
        box.y = staticY;
        me.fireEvent('select', me, box);
    },

    onMouseUp: function(e) {
        var me = this,
            bbox = me.bbox || me.chartBBox,
            sel = me.maskSelection;
        me.maskMouseDown = false;
        me.mouseDown = false;
        if (me.mouseMoved) {
            me.onMouseMove(e);
            me.mouseMoved = false;
            me.fireEvent('select', me, {
                x: sel.x - bbox.x,
                y: sel.y - bbox.y,
                width: sel.width,
                height: sel.height
            });
        }
    },

    onMouseDown: function(e) {
        var me = this;
        me.mouseDown = true;
        me.mouseMoved = false;
        me.maskMouseDown = {
            x: e.getPageX() - me.el.getX(),
            y: e.getPageY() - me.el.getY()
        };
    },

    onMouseMove: function(e) {
        var me = this,
            mask = me.maskType,
            bbox = me.bbox || me.chartBBox,
            x = bbox.x,
            y = bbox.y,
            math = Math,
            floor = math.floor,
            abs = math.abs,
            min = math.min,
            max = math.max,
            height = floor(y + bbox.height),
            width = floor(x + bbox.width),
            posX = e.getPageX(),
            posY = e.getPageY(),
            staticX = posX - me.el.getX(),
            staticY = posY - me.el.getY(),
            maskMouseDown = me.maskMouseDown,
            path;
        
        me.mouseMoved = me.mouseDown;
        staticX = max(staticX, x);
        staticY = max(staticY, y);
        staticX = min(staticX, width);
        staticY = min(staticY, height);
        if (maskMouseDown && me.mouseDown) {
            if (mask == 'horizontal') {
                staticY = y;
                maskMouseDown.y = height;
                posY = me.el.getY() + bbox.height + me.insetPadding;
            }
            else if (mask == 'vertical') {
                staticX = x;
                maskMouseDown.x = width;
            }
            width = maskMouseDown.x - staticX;
            height = maskMouseDown.y - staticY;
            path = ['M', staticX, staticY, 'l', width, 0, 0, height, -width, 0, 'z'];
            me.maskSelection = {
                x: width > 0 ? staticX : staticX + width,
                y: height > 0 ? staticY : staticY + height,
                width: abs(width),
                height: abs(height)
            };
            me.mask.updateBox({
                x: posX - abs(width),
                y: posY - abs(height),
                width: abs(width),
                height: abs(height)
            });
            me.mask.show();
            me.maskSprite.setAttributes({
                hidden: true    
            }, true);
        }
        else {
            if (mask == 'horizontal') {
                path = ['M', staticX, y, 'L', staticX, height];
            }
            else if (mask == 'vertical') {
                path = ['M', x, staticY, 'L', width, staticY];
            }
            else {
                path = ['M', staticX, y, 'L', staticX, height, 'M', x, staticY, 'L', width, staticY];
            }
            me.maskSprite.setAttributes({
                path: path,
                fill: me.maskMouseDown ? me.maskSprite.stroke : false,
                'stroke-width': mask === true ? 1 : 3,
                hidden: false
            }, true);
        }
    },

    onMouseLeave: function(e) {
        var me = this;
        me.mouseMoved = false;
        me.mouseDown = false;
        me.maskMouseDown = false;
        me.mask.hide();
        me.maskSprite.hide(true);
    }
});
    

Ext.define('Ext.chart.Navigation', {

    constructor: function() {
        this.originalStore = this.store;
    },
    
    
    setZoom: function(zoomConfig) {
        var me = this,
            store = me.substore || me.store,
            bbox = me.chartBBox,
            len = store.getCount(),
            from = (zoomConfig.x / bbox.width * len) >> 0,
            to = Math.ceil(((zoomConfig.x + zoomConfig.width) / bbox.width * len)),
            recFieldsLen, recFields = [], curField, json = [], obj;
        
        store.each(function(rec, i) {
            if (i < from || i > to) {
                return;
            }
            obj = {};
            
            if (!recFields.length) {
                rec.fields.each(function(f) {
                    recFields.push(f.name);
                });
                recFieldsLen = recFields.length;
            }
            
            for (i = 0; i < recFieldsLen; i++) {
                curField = recFields[i];
                obj[curField] = rec.get(curField);
            }
            json.push(obj);
        });
        me.store = me.substore = Ext.create('Ext.data.JsonStore', {
            fields: recFields,
            data: json
        });
        me.redraw(true);
    },

    restoreZoom: function() {
        this.store = this.substore = this.originalStore;
        this.redraw(true);
    }
    
});

Ext.define('Ext.chart.Shape', {

    

    singleton: true,

    

    circle: function (surface, opts) {
        return surface.add(Ext.apply({
            type: 'circle',
            x: opts.x,
            y: opts.y,
            stroke: null,
            radius: opts.radius
        }, opts));
    },
    line: function (surface, opts) {
        return surface.add(Ext.apply({
            type: 'rect',
            x: opts.x - opts.radius,
            y: opts.y - opts.radius,
            height: 2 * opts.radius,
            width: 2 * opts.radius / 5
        }, opts));
    },
    square: function (surface, opts) {
        return surface.add(Ext.applyIf({
            type: 'rect',
            x: opts.x - opts.radius,
            y: opts.y - opts.radius,
            height: 2 * opts.radius,
            width: 2 * opts.radius,
            radius: null
        }, opts));
    },
    triangle: function (surface, opts) {
        opts.radius *= 1.75;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x, ",", opts.y, "m0-", opts.radius * 0.58, "l", opts.radius * 0.5, ",", opts.radius * 0.87, "-", opts.radius, ",0z")
        }, opts));
    },
    diamond: function (surface, opts) {
        var r = opts.radius;
        r *= 1.5;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: ["M", opts.x, opts.y - r, "l", r, r, -r, r, -r, -r, r, -r, "z"]
        }, opts));
    },
    cross: function (surface, opts) {
        var r = opts.radius;
        r = r / 1.7;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x - r, ",", opts.y, "l", [-r, -r, r, -r, r, r, r, -r, r, r, -r, r, r, r, -r, r, -r, -r, -r, r, -r, -r, "z"])
        }, opts));
    },
    plus: function (surface, opts) {
        var r = opts.radius / 1.3;
        return surface.add(Ext.apply({
            type: 'path',
            stroke: null,
            path: "M".concat(opts.x - r / 2, ",", opts.y - r / 2, "l", [0, -r, r, 0, 0, r, r, 0, 0, r, -r, 0, 0, r, -r, 0, 0, -r, -r, 0, 0, -r, "z"])
        }, opts));
    },
    arrow: function (surface, opts) {
        var r = opts.radius;
        return surface.add(Ext.apply({
            type: 'path',
            path: "M".concat(opts.x - r * 0.7, ",", opts.y - r * 0.4, "l", [r * 0.6, 0, 0, -r * 0.4, r, r * 0.8, -r, r * 0.8, 0, -r * 0.4, -r * 0.6, 0], "z")
        }, opts));
    },
    drop: function (surface, x, y, text, size, angle) {
        size = size || 30;
        angle = angle || 0;
        surface.add({
            type: 'path',
            path: ['M', x, y, 'l', size, 0, 'A', size * 0.4, size * 0.4, 0, 1, 0, x + size * 0.7, y - size * 0.7, 'z'],
            fill: '#000',
            stroke: 'none',
            rotate: {
                degrees: 22.5 - angle,
                x: x,
                y: y
            }
        });
        angle = (angle + 90) * Math.PI / 180;
        surface.add({
            type: 'text',
            x: x + size * Math.sin(angle) - 10, 
            y: y + size * Math.cos(angle) + 5,
            text:  text,
            'font-size': size * 12 / 40,
            stroke: 'none',
            fill: '#fff'
        });
    }
});

Ext.define('Ext.draw.Surface', {

    

    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: ['Ext.draw.CompositeSprite'],
    uses: ['Ext.draw.engine.Svg', 'Ext.draw.engine.Vml'],

    separatorRe: /[, ]+/,

    statics: {
        
        create: function(config, enginePriority) {
            enginePriority = enginePriority || ['Svg', 'Vml'];

            var i = 0,
                len = enginePriority.length,
                surfaceClass;

            for (; i < len; i++) {
                if (Ext.supports[enginePriority[i]]) {
                    return Ext.create('Ext.draw.engine.' + enginePriority[i], config);
                }
            }
            return false;
        }
    },

    

    
    availableAttrs: {
        blur: 0,
        "clip-rect": "0 0 1e9 1e9",
        cursor: "default",
        cx: 0,
        cy: 0,
        'dominant-baseline': 'auto',
        fill: "none",
        "fill-opacity": 1,
        font: '10px "Arial"',
        "font-family": '"Arial"',
        "font-size": "10",
        "font-style": "normal",
        "font-weight": 400,
        gradient: "",
        height: 0,
        hidden: false,
        href: "http://sencha.com/",
        opacity: 1,
        path: "M0,0",
        radius: 0,
        rx: 0,
        ry: 0,
        scale: "1 1",
        src: "",
        stroke: "#000",
        "stroke-dasharray": "",
        "stroke-linecap": "butt",
        "stroke-linejoin": "butt",
        "stroke-miterlimit": 0,
        "stroke-opacity": 1,
        "stroke-width": 1,
        target: "_blank",
        text: "",
        "text-anchor": "middle",
        title: "Ext Draw",
        width: 0,
        x: 0,
        y: 0,
        zIndex: 0
    },

 
 
    container: undefined,
    height: 352,
    width: 512,
    x: 0,
    y: 0,

    constructor: function(config) {
        var me = this;
        config = config || {};
        Ext.apply(me, config);

        me.domRef = Ext.getDoc().dom;
        
        me.customAttributes = {};

        me.addEvents(
            'mousedown',
            'mouseup',
            'mouseover',
            'mouseout',
            'mousemove',
            'mouseenter',
            'mouseleave',
            'click'
        );

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

        me.getId();
        me.initGradients();
        me.initItems();
        if (me.renderTo) {
            me.render(me.renderTo);
            delete me.renderTo;
        }
        me.initBackground(config.background);
    },

    
    
    initSurface: Ext.emptyFn,

    
    
    renderItem: Ext.emptyFn,

    
    renderItems: Ext.emptyFn,

    
    setViewBox: Ext.emptyFn,

    
    addCls: Ext.emptyFn,

    
    removeCls: Ext.emptyFn,

    
    setStyle: Ext.emptyFn,

    
    initGradients: function() {
        var gradients = this.gradients;
        if (gradients) {
            Ext.each(gradients, this.addGradient, this);
        }
    },

    
    initItems: function() {
        var items = this.items;
        this.items = Ext.create('Ext.draw.CompositeSprite');
        this.groups = Ext.create('Ext.draw.CompositeSprite');
        if (items) {
            this.add(items);
        }
    },
    
    
    initBackground: function(config) {
        var me = this,
            width = me.width,
            height = me.height,
            gradientId, gradient, backgroundSprite;
        if (config) {
            if (config.gradient) {
                gradient = config.gradient;
                gradientId = gradient.id;
                me.addGradient(gradient);
                me.background = me.add({
                    type: 'rect',
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    fill: 'url(#' + gradientId + ')'
                });
            } else if (config.fill) {
                me.background = me.add({
                    type: 'rect',
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    fill: config.fill
                });
            } else if (config.image) {
                me.background = me.add({
                    type: 'image',
                    x: 0,
                    y: 0,
                    width: width,
                    height: height,
                    src: config.image
                });
            }
        }
    },
    
    
    setSize: function(w, h) {
        if (this.background) {
            this.background.setAttributes({
                width: w,
                height: h,
                hidden: false
            }, true);
        }
    },

    
    scrubAttrs: function(sprite) {
        var i,
            attrs = {},
            exclude = {},
            sattr = sprite.attr;
        for (i in sattr) {    
            
            if (this.translateAttrs.hasOwnProperty(i)) {
                
                attrs[this.translateAttrs[i]] = sattr[i];
                exclude[this.translateAttrs[i]] = true;
            }
            else if (this.availableAttrs.hasOwnProperty(i) && !exclude[i]) {
                
                attrs[i] = sattr[i];
            }
        }
        return attrs;
    },

    
    onClick: function(e) {
        this.processEvent('click', e);
    },

    
    onMouseUp: function(e) {
        this.processEvent('mouseup', e);
    },

    
    onMouseDown: function(e) {
        this.processEvent('mousedown', e);
    },

    
    onMouseOver: function(e) {
        this.processEvent('mouseover', e);
    },

    
    onMouseOut: function(e) {
        this.processEvent('mouseout', e);
    },

    
    onMouseMove: function(e) {
        this.fireEvent('mousemove', e);
    },

    
    onMouseEnter: Ext.emptyFn,

    
    onMouseLeave: Ext.emptyFn,

    
    addGradient: Ext.emptyFn,

    
    add: function() {
        var args = Array.prototype.slice.call(arguments),
            sprite,
            index;

        var hasMultipleArgs = args.length > 1;
        if (hasMultipleArgs || Ext.isArray(args[0])) {
            var items = hasMultipleArgs ? args : args[0],
                results = [],
                i, ln, item;

            for (i = 0, ln = items.length; i < ln; i++) {
                item = items[i];
                item = this.add(item);
                results.push(item);
            }

            return results;
        }
        sprite = this.prepareItems(args[0], true)[0];
        this.normalizeSpriteCollection(sprite);
        this.onAdd(sprite);
        return sprite;
    },

    
    normalizeSpriteCollection: function(sprite) {
        var items = this.items,
            zIndex = sprite.attr.zIndex,
            idx = items.indexOf(sprite);

        if (idx < 0 || (idx > 0 && items.getAt(idx - 1).attr.zIndex > zIndex) ||
                (idx < items.length - 1 && items.getAt(idx + 1).attr.zIndex < zIndex)) {
            items.removeAt(idx);
            idx = items.findIndexBy(function(otherSprite) {
                return otherSprite.attr.zIndex > zIndex;
            });
            if (idx < 0) {
                idx = items.length;
            }
            items.insert(idx, sprite);
        }
        return idx;
    },

    onAdd: function(sprite) {
        var group = sprite.group,
            draggable = sprite.draggable,
            groups, ln, i;
        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                this.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        if (draggable) {
            sprite.initDraggable();
        }
    },

    
    remove: function(sprite, destroySprite) {
        if (sprite) {
            this.items.remove(sprite);
            this.groups.each(function(item) {
                item.remove(sprite);
            });
            sprite.onRemove();
            if (destroySprite === true) {
                sprite.destroy();
            }
        }
    },

    
    removeAll: function(destroySprites) {
        var items = this.items.items,
            ln = items.length,
            i;
        for (i = ln - 1; i > -1; i--) {
            this.remove(items[i], destroySprites);
        }
    },

    onRemove: Ext.emptyFn,

    onDestroy: Ext.emptyFn,

    
    applyTransformations: function(sprite) {
            sprite.bbox.transform = 0;
            this.transform(sprite);

        var me = this,
            dirty = false,
            attr = sprite.attr;

        if (attr.translation.x != null || attr.translation.y != null) {
            me.translate(sprite);
            dirty = true;
        }
        if (attr.scaling.x != null || attr.scaling.y != null) {
            me.scale(sprite);
            dirty = true;
        }
        if (attr.rotation.degrees != null) {
            me.rotate(sprite);
            dirty = true;
        }
        if (dirty) {
            sprite.bbox.transform = 0;
            this.transform(sprite);
            sprite.transformations = [];
        }
    },

    
    rotate: function (sprite) {
        var bbox,
            deg = sprite.attr.rotation.degrees,
            centerX = sprite.attr.rotation.x,
            centerY = sprite.attr.rotation.y;
        if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
            bbox = this.getBBox(sprite);
            centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
            centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
        }
        sprite.transformations.push({
            type: "rotate",
            degrees: deg,
            x: centerX,
            y: centerY
        });
    },

    
    translate: function(sprite) {
        var x = sprite.attr.translation.x || 0,
            y = sprite.attr.translation.y || 0;
        sprite.transformations.push({
            type: "translate",
            x: x,
            y: y
        });
    },

    
    scale: function(sprite) {
        var bbox,
            x = sprite.attr.scaling.x || 1,
            y = sprite.attr.scaling.y || 1,
            centerX = sprite.attr.scaling.centerX,
            centerY = sprite.attr.scaling.centerY;

        if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
            bbox = this.getBBox(sprite);
            centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
            centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
        }
        sprite.transformations.push({
            type: "scale",
            x: x,
            y: y,
            centerX: centerX,
            centerY: centerY
        });
    },

    
    rectPath: function (x, y, w, h, r) {
        if (r) {
            return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
        }
        return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
    },

    
    ellipsePath: function (x, y, rx, ry) {
        if (ry == null) {
            ry = rx;
        }
        return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
    },

    
    getPathpath: function (el) {
        return el.attr.path;
    },

    
    getPathcircle: function (el) {
        var a = el.attr;
        return this.ellipsePath(a.x, a.y, a.radius, a.radius);
    },

    
    getPathellipse: function (el) {
        var a = el.attr;
        return this.ellipsePath(a.x, a.y,
                                a.radiusX || (a.width / 2) || 0,
                                a.radiusY || (a.height / 2) || 0);
    },

    
    getPathrect: function (el) {
        var a = el.attr;
        return this.rectPath(a.x, a.y, a.width, a.height, a.r);
    },

    
    getPathimage: function (el) {
        var a = el.attr;
        return this.rectPath(a.x || 0, a.y || 0, a.width, a.height);
    },

    
    getPathtext: function (el) {
        var bbox = this.getBBoxText(el);
        return this.rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
    },

    createGroup: function(id) {
        var group = this.groups.get(id);
        if (!group) {
            group = Ext.create('Ext.draw.CompositeSprite', {
                surface: this
            });
            group.id = id || Ext.id(null, 'ext-surface-group-');
            this.groups.add(group);
        }
        return group;
    },

    
    getGroup: function(id) {
        if (typeof id == "string") {
            var group = this.groups.get(id);
            if (!group) {
                group = this.createGroup(id);
            }
        } else {
            group = id;
        }
        return group;
    },

    
    prepareItems: function(items, applyDefaults) {
        items = [].concat(items);
        
        var item, i, ln;
        for (i = 0, ln = items.length; i < ln; i++) {
            item = items[i];
            if (!(item instanceof Ext.draw.Sprite)) {
                
                item.surface = this;
                items[i] = this.createItem(item);
            } else {
                item.surface = this;
            }
        }
        return items;
    },
    
    
    setText: Ext.emptyFn,
    
    //@private Creates an item and appends it to the surface. Called

    
    createItem: Ext.emptyFn,

    
    getId: function() {
        return this.id || (this.id = Ext.id(null, 'ext-surface-'));
    },

    
    destroy: function() {
        delete this.domRef;
        this.removeAll();
    }
});

Ext.define('Ext.draw.Component', {

    

    alias: 'widget.draw',

    extend: 'Ext.Component',

    requires: [
        'Ext.draw.Surface',
        'Ext.layout.component.Draw'
    ],

    

    
    enginePriority: ['Svg', 'Vml'],

    baseCls: Ext.baseCSSPrefix + 'surface',

    componentLayout: 'draw',

    
    viewBox: true,

    
    autoSize: false,
    
    

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

        this.addEvents(
            'mousedown',
            'mouseup',
            'mousemove',
            'mouseenter',
            'mouseleave',
            'click'
        );
    },

    
    onRender: function() {
        var me = this,
            viewBox = me.viewBox,
            autoSize = me.autoSize,
            bbox, items, width, height, x, y;
        me.callParent(arguments);

        me.createSurface();

        items = me.surface.items;

        if (viewBox || autoSize) {
            bbox = items.getBBox();
            width = bbox.width;
            height = bbox.height;
            x = bbox.x;
            y = bbox.y;
            if (me.viewBox) {
                me.surface.setViewBox(x, y, width, height);
            }
            else {
                
                me.autoSizeSurface();
            }
        }
    },

    //@private

    autoSizeSurface: function() {
        var me = this,
            items = me.surface.items,
            bbox = items.getBBox(),
            width = bbox.width,
            height = bbox.height;
        items.setAttributes({
            translate: {
                x: -bbox.x,
                
                y: -bbox.y + (+Ext.isOpera)
            }
        }, true);
        if (me.rendered) {
            me.setSize(width, height);
            me.surface.setSize(width, height);
        }
        else {
            me.surface.setSize(width, height);
        }
        me.el.setSize(width, height);
    },

    
    createSurface: function() {
        var surface = Ext.draw.Surface.create(Ext.apply({}, {
                width: this.width,
                height: this.height,
                renderTo: this.el
            }, this.initialConfig));
        this.surface = surface;

        function refire(eventName) {
            return function(e) {
                this.fireEvent(eventName, e);
            };
        }

        surface.on({
            scope: this,
            mouseup: refire('mouseup'),
            mousedown: refire('mousedown'),
            mousemove: refire('mousemove'),
            mouseenter: refire('mouseenter'),
            mouseleave: refire('mouseleave'),
            click: refire('click')
        });
    },


    
    onDestroy: function() {
        var surface = this.surface;
        if (surface) {
            surface.destroy();
        }
        this.callParent(arguments);
    }

});


Ext.define('Ext.chart.LegendItem', {

    

    extend: 'Ext.draw.CompositeSprite',

    requires: ['Ext.chart.Shape'],

    

    
    x: 0,
    y: 0,
    zIndex: 500,

    constructor: function(config) {
        this.callParent(arguments);
        this.createLegend(config);
    },

    
    createLegend: function(config) {
        var me = this,
            index = config.yFieldIndex,
            series = me.series,
            seriesType = series.type,
            idx = me.yFieldIndex,
            legend = me.legend,
            surface = me.surface,
            refX = legend.x + me.x,
            refY = legend.y + me.y,
            bbox, z = me.zIndex,
            markerConfig, label, mask,
            radius, toggle = false,
            seriesStyle = Ext.apply(series.seriesStyle, series.style);

        function getSeriesProp(name) {
            var val = series[name];
            return (Ext.isArray(val) ? val[idx] : val);
        }
        
        label = me.add('label', surface.add({
            type: 'text',
            x: 20,
            y: 0,
            zIndex: z || 0,
            font: legend.labelFont,
            text: getSeriesProp('title') || getSeriesProp('yField')
        }));

        
        if (seriesType === 'line' || seriesType === 'scatter') {
            if(seriesType === 'line') {
                me.add('line', surface.add({
                    type: 'path',
                    path: 'M0.5,0.5L16.5,0.5',
                    zIndex: z,
                    "stroke-width": series.lineWidth,
                    "stroke-linejoin": "round",
                    "stroke-dasharray": series.dash,
                    stroke: seriesStyle.stroke || '#000',
                    style: {
                        cursor: 'pointer'
                    }
                }));
            }
            if (series.showMarkers || seriesType === 'scatter') {
                markerConfig = Ext.apply(series.markerStyle, series.markerConfig || {});
                me.add('marker', Ext.chart.Shape[markerConfig.type](surface, {
                    fill: markerConfig.fill,
                    x: 8.5,
                    y: 0.5,
                    zIndex: z,
                    radius: markerConfig.radius || markerConfig.size,
                    style: {
                        cursor: 'pointer'
                    }
                }));
            }
        }
        
        else {
            me.add('box', surface.add({
                type: 'rect',
                zIndex: z,
                x: 0,
                y: 0,
                width: 12,
                height: 12,
                fill: series.getLegendColor(index),
                style: {
                    cursor: 'pointer'
                }
            }));
        }
        
        me.setAttributes({
            hidden: false
        }, true);
        
        bbox = me.getBBox();
        
        mask = me.add('mask', surface.add({
            type: 'rect',
            x: bbox.x,
            y: bbox.y,
            width: bbox.width || 20,
            height: bbox.height || 20,
            zIndex: (z || 0) + 1000,
            fill: '#f00',
            opacity: 0,
            style: {
                'cursor': 'pointer'
            }
        }));

        
        me.on('mouseover', function() {
            label.setStyle({
                'font-weight': 'bold'
            });
            mask.setStyle({
                'cursor': 'pointer'
            });
            series._index = index;
            series.highlightItem();
        }, me);

        me.on('mouseout', function() {
            label.setStyle({
                'font-weight': 'normal'
            });
            series._index = index;
            series.unHighlightItem();
        }, me);
        
        if (!series.visibleInLegend(index)) {
            toggle = true;
            label.setAttributes({
               opacity: 0.5
            }, true);
        }

        me.on('mousedown', function() {
            if (!toggle) {
                series.hideAll();
                label.setAttributes({
                    opacity: 0.5
                }, true);
            } else {
                series.showAll();
                label.setAttributes({
                    opacity: 1
                }, true);
            }
            toggle = !toggle;
        }, me);
        me.updatePosition({x:0, y:0}); 
    },

    
    updatePosition: function(relativeTo) {
        var me = this,
            items = me.items,
            ln = items.length,
            i = 0,
            item;
        if (!relativeTo) {
            relativeTo = me.legend;
        }
        for (; i < ln; i++) {
            item = items[i];
            switch (item.type) {
                case 'text':
                    item.setAttributes({
                        x: 20 + relativeTo.x + me.x,
                        y: relativeTo.y + me.y
                    }, true);
                    break;
                case 'rect':
                    item.setAttributes({
                        translate: {
                            x: relativeTo.x + me.x,
                            y: relativeTo.y + me.y - 6
                        }
                    }, true);
                    break;
                default:
                    item.setAttributes({
                        translate: {
                            x: relativeTo.x + me.x,
                            y: relativeTo.y + me.y
                        }
                    }, true);
            }
        }
    }
});

Ext.define('Ext.chart.Legend', {

    

    requires: ['Ext.chart.LegendItem'],

    

    
    visible: true,

    
    position: 'bottom',

    
    x: 0,

    
    y: 0,

    
    labelFont: '12px Helvetica, sans-serif',

    
    boxStroke: '#000',

    
    boxStrokeWidth: 1,

    
    boxFill: '#FFF',

    
    itemSpacing: 10,

    
    padding: 5,

    
    width: 0,
    
    height: 0,

    
    boxZIndex: 100,

    constructor: function(config) {
        var me = this;
        if (config) {
            Ext.apply(me, config);
        }
        me.items = [];
        
        me.isVertical = ("left|right|float".indexOf(me.position) !== -1);
        
        
        me.origX = me.x;
        me.origY = me.y;
    },

    
    create: function() {
        var me = this;
        me.createItems();
        if (!me.created && me.isDisplayed()) {
            me.createBox();
            me.created = true;

            
            me.chart.series.each(function(series) {
                series.on('titlechange', function() {
                    me.create();
                    me.updatePosition();
                });
            });
        }
    },

    
    isDisplayed: function() {
        return this.visible && this.chart.series.findIndex('showInLegend', true) !== -1;
    },

    
    createItems: function() {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            items = me.items,
            padding = me.padding,
            itemSpacing = me.itemSpacing,
            spacingOffset = 2,
            maxWidth = 0,
            maxHeight = 0,
            totalWidth = 0,
            totalHeight = 0,
            vertical = me.isVertical,
            math = Math,
            mfloor = math.floor,
            mmax = math.max,
            index = 0, 
            i = 0, 
            len = items ? items.length : 0,
            x, y, spacing, item, bbox, height, width;

        
        if (len) {
            for (; i < len; i++) {
                items[i].destroy();
            }
        }
        
        items.length = [];
        
        
        chart.series.each(function(series, i) {
            if (series.showInLegend) {
                Ext.each([].concat(series.yField), function(field, j) {
                    item = Ext.create('Ext.chart.LegendItem', {
                        legend: this,
                        series: series,
                        surface: chart.surface,
                        yFieldIndex: j
                    });
                    bbox = item.getBBox();

                    
                    width = bbox.width; 
                    height = bbox.height;

                    if (i + j === 0) {
                        spacing = vertical ? padding + height / 2 : padding;
                    }
                    else {
                        spacing = itemSpacing / (vertical ? 2 : 1);
                    }
                    
                    item.x = mfloor(vertical ? padding : totalWidth + spacing);
                    item.y = mfloor(vertical ? totalHeight + spacing : padding + height / 2);

                    
                    totalWidth += width + spacing;
                    totalHeight += height + spacing;
                    maxWidth = mmax(maxWidth, width);
                    maxHeight = mmax(maxHeight, height);

                    items.push(item);
                }, this);
            }
        }, me);

        
        me.width = mfloor((vertical ? maxWidth : totalWidth) + padding * 2);
        if (vertical && items.length === 1) {
            spacingOffset = 1;
        }
        me.height = mfloor((vertical ? totalHeight - spacingOffset * spacing : maxHeight) + (padding * 2));
        me.itemHeight = maxHeight;
    },

    
    getBBox: function() {
        var me = this;
        return {
            x: Math.round(me.x) - me.boxStrokeWidth / 2,
            y: Math.round(me.y) - me.boxStrokeWidth / 2,
            width: me.width,
            height: me.height
        };
    },

    
    createBox: function() {
        var me = this,
            box = me.boxSprite = me.chart.surface.add(Ext.apply({
                type: 'rect',
                stroke: me.boxStroke,
                "stroke-width": me.boxStrokeWidth,
                fill: me.boxFill,
                zIndex: me.boxZIndex
            }, me.getBBox()));
        box.redraw();
    },

    
    updatePosition: function() {
        var me = this,
            x, y,
            legendWidth = me.width,
            legendHeight = me.height,
            padding = me.padding,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            insets = chart.insetPadding,
            chartWidth = chartBBox.width - (insets * 2),
            chartHeight = chartBBox.height - (insets * 2),
            chartX = chartBBox.x + insets,
            chartY = chartBBox.y + insets,
            surface = chart.surface,
            mfloor = Math.floor;
        
        if (me.isDisplayed()) {
            
            switch(me.position) {
                case "left":
                    x = insets;
                    y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
                    break;
                case "right":
                    x = mfloor(surface.width - legendWidth) - insets;
                    y = mfloor(chartY + chartHeight / 2 - legendHeight / 2);
                    break;
                case "top":
                    x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
                    y = insets;
                    break;
                case "bottom":
                    x = mfloor(chartX + chartWidth / 2 - legendWidth / 2);
                    y = mfloor(surface.height - legendHeight) - insets;
                    break;
                default:
                    x = mfloor(me.origX) + insets;
                    y = mfloor(me.origY) + insets;
            }
            me.x = x;
            me.y = y;

            
            Ext.each(me.items, function(item) {
                item.updatePosition();
            });
            
            me.boxSprite.setAttributes(me.getBBox(), true);
        }
    }
});


Ext.define('Ext.chart.Chart', {

    

    alias: 'widget.chart',

    extend: 'Ext.draw.Component',
    
    mixins: {
        themeManager: 'Ext.chart.theme.Theme',
        mask: 'Ext.chart.Mask',
        navigation: 'Ext.chart.Navigation'
    },

    requires: [
        'Ext.util.MixedCollection',
        'Ext.data.StoreManager',
        'Ext.chart.Legend',
        'Ext.util.DelayedTask'
    ],

    

    
    viewBox: false,

    

    
    animate: false,

    
    legend: false,

    
    insetPadding: 10,

    
    enginePriority: ['Svg', 'Vml'],

    
    background: false,

    


    constructor: function(config) {
        var me = this,
            defaultAnim;
        me.initTheme(config.theme || me.theme);
        if (me.gradients) {
            Ext.apply(config, { gradients: me.gradients });
        }
        if (me.background) {
            Ext.apply(config, { background: me.background });
        }
        if (config.animate) {
            defaultAnim = {
                easing: 'ease',
                duration: 500
            };
            if (Ext.isObject(config.animate)) {
                config.animate = Ext.applyIf(config.animate, defaultAnim);
            }
            else {
                config.animate = defaultAnim;
            }
        }
        me.mixins.mask.constructor.call(me, config);
        me.mixins.navigation.constructor.call(me, config);
        me.callParent([config]);
    },

    initComponent: function() {
        var me = this,
            axes,
            series;
        me.callParent();
        me.addEvents(
            'itemmousedown',
            'itemmouseup',
            'itemmouseover',
            'itemmouseout',
            'itemclick',
            'itemdoubleclick',
            'itemdragstart',
            'itemdrag',
            'itemdragend',
            
            'beforerefresh',
            
            'refresh'
        );
        Ext.applyIf(me, {
            zoom: {
                width: 1,
                height: 1,
                x: 0,
                y: 0
            }
        });
        me.maxGutter = [0, 0];
        me.store = Ext.data.StoreManager.lookup(me.store);
        axes = me.axes;
        me.axes = Ext.create('Ext.util.MixedCollection', false, function(a) { return a.position; });
        if (axes) {
            me.axes.addAll(axes);
        }
        series = me.series;
        me.series = Ext.create('Ext.util.MixedCollection', false, function(a) { return a.seriesId || (a.seriesId = Ext.id(null, 'ext-chart-series-')); });
        if (series) {
            me.series.addAll(series);
        }
        if (me.legend !== false) {
            me.legend = Ext.create('Ext.chart.Legend', Ext.applyIf({chart:me}, me.legend));
        }

        me.on({
            mousemove: me.onMouseMove,
            mouseleave: me.onMouseLeave,
            mousedown: me.onMouseDown,
            mouseup: me.onMouseUp,
            scope: me
        });
    },

    
    afterComponentLayout: function(width, height) {
        var me = this;
        if (Ext.isNumber(width) && Ext.isNumber(height)) {
            me.curWidth = width;
            me.curHeight = height;
            me.redraw(true);
        }
        this.callParent(arguments);
    },

    
    redraw: function(resize) {
        var me = this,
            chartBBox = me.chartBBox = {
                x: 0,
                y: 0,
                height: me.curHeight,
                width: me.curWidth
            },
            legend = me.legend;
        me.surface.setSize(chartBBox.width, chartBBox.height);
        
        me.series.each(me.initializeSeries, me);
        me.axes.each(me.initializeAxis, me);
        
        
        me.axes.each(function(axis) {
            axis.processView();
        });
        me.axes.each(function(axis) {
            axis.drawAxis(true);
        });

        
        if (legend !== false) {
            legend.create();
        }

        
        me.alignAxes();

        
        if (me.legend !== false) {
            legend.updatePosition();
        }

        
        me.getMaxGutter();

        
        me.resizing = !!resize;

        me.axes.each(me.drawAxis, me);
        me.series.each(me.drawCharts, me);
        me.resizing = false;
    },

    
    afterRender: function() {
        var ref,
            me = this;
        this.callParent();

        if (me.categoryNames) {
            me.setCategoryNames(me.categoryNames);
        }

        if (me.tipRenderer) {
            ref = me.getFunctionRef(me.tipRenderer);
            me.setTipRenderer(ref.fn, ref.scope);
        }
        me.bindStore(me.store, true);
        me.refresh();
    },

    
    getEventXY: function(e) {
        var me = this,
            box = this.surface.getRegion(),
            pageXY = e.getXY(),
            x = pageXY[0] - box.left,
            y = pageXY[1] - box.top;
        return [x, y];
    },

    
    onClick: function(e) {
        var me = this,
            position = me.getEventXY(e),
            item;

        
        
        me.series.each(function(series) {
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent('itemclick', item);
                    }
                }
            }
        }, me);
    },

    
    onMouseDown: function(e) {
        var me = this,
            position = me.getEventXY(e),
            item;

        if (me.mask) {
            me.mixins.mask.onMouseDown.call(me, e);
        }
        
        
        me.series.each(function(series) {
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent('itemmousedown', item);
                    }
                }
            }
        }, me);
    },

    
    onMouseUp: function(e) {
        var me = this,
            position = me.getEventXY(e),
            item;

        if (me.mask) {
            me.mixins.mask.onMouseUp.call(me, e);
        }
        
        
        me.series.each(function(series) {
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    if (item) {
                        series.fireEvent('itemmouseup', item);
                    }
                }
            }
        }, me);
    },

    
    onMouseMove: function(e) {
        var me = this,
            position = me.getEventXY(e),
            item, last, storeItem, storeField;

        if (me.mask) {
            me.mixins.mask.onMouseMove.call(me, e);
        }
        
        
        me.series.each(function(series) {
            if (Ext.draw.Draw.withinBox(position[0], position[1], series.bbox)) {
                if (series.getItemForPoint) {
                    item = series.getItemForPoint(position[0], position[1]);
                    last = series._lastItemForPoint;
                    storeItem = series._lastStoreItem;
                    storeField = series._lastStoreField;


                    if (item !== last || item && (item.storeItem != storeItem || item.storeField != storeField)) {
                        if (last) {
                            series.fireEvent('itemmouseout', last);
                            delete series._lastItemForPoint;
                            delete series._lastStoreField;
                            delete series._lastStoreItem;
                        }
                        if (item) {
                            series.fireEvent('itemmouseover', item);
                            series._lastItemForPoint = item;
                            series._lastStoreItem = item.storeItem;
                            series._lastStoreField = item.storeField;
                        }
                    }
                }
            } else {
                last = series._lastItemForPoint;
                if (last) {
                    series.fireEvent('itemmouseout', last);
                    delete series._lastItemForPoint;
                    delete series._lastStoreField;
                    delete series._lastStoreItem;
                }
            }
        }, me);
    },

    
    onMouseLeave: function(e) {
        var me = this;
        if (me.mask) {
            me.mixins.mask.onMouseLeave.call(me, e);
        }
        me.series.each(function(series) {
            delete series._lastItemForPoint;
        });
    },

    
    delayRefresh: function() {
        var me = this;
        if (!me.refreshTask) {
            me.refreshTask = Ext.create('Ext.util.DelayedTask', me.refresh, me);
        }
        me.refreshTask.delay(me.refreshBuffer);
    },

    
    refresh: function() {
        var me = this;
        if (me.rendered && me.curWidth != undefined && me.curHeight != undefined) {
            if (me.fireEvent('beforerefresh', me) !== false) {
                me.redraw();
                me.fireEvent('refresh', me);
            }
        }
    },

    
    bindStore: function(store, initial) {
        var me = this;
        if (!initial && me.store) {
            if (store !== me.store && me.store.autoDestroy) {
                me.store.destroy();
            }
            else {
                me.store.un('datachanged', me.refresh, me);
                me.store.un('add', me.delayRefresh, me);
                me.store.un('remove', me.delayRefresh, me);
                me.store.un('update', me.delayRefresh, me);
                me.store.un('clear', me.refresh, me);
            }
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            store.on({
                scope: me,
                datachanged: me.refresh,
                add: me.delayRefresh,
                remove: me.delayRefresh,
                update: me.delayRefresh,
                clear: me.refresh
            });
        }
        me.store = store;
        if (store && !initial) {
            me.refresh();
        }
    },

    
    initializeAxis: function(axis) {
        var me = this,
            chartBBox = me.chartBBox,
            w = chartBBox.width,
            h = chartBBox.height,
            x = chartBBox.x,
            y = chartBBox.y,
            themeAttrs = me.themeAttrs,
            config = {
                chart: me
            };
        if (themeAttrs) {
            config.axisStyle = Ext.apply({}, themeAttrs.axis);
            config.axisLabelLeftStyle = Ext.apply({}, themeAttrs.axisLabelLeft);
            config.axisLabelRightStyle = Ext.apply({}, themeAttrs.axisLabelRight);
            config.axisLabelTopStyle = Ext.apply({}, themeAttrs.axisLabelTop);
            config.axisLabelBottomStyle = Ext.apply({}, themeAttrs.axisLabelBottom);
            config.axisTitleLeftStyle = Ext.apply({}, themeAttrs.axisTitleLeft);
            config.axisTitleRightStyle = Ext.apply({}, themeAttrs.axisTitleRight);
            config.axisTitleTopStyle = Ext.apply({}, themeAttrs.axisTitleTop);
            config.axisTitleBottomStyle = Ext.apply({}, themeAttrs.axisTitleBottom);
        }
        switch (axis.position) {
            case 'top':
                Ext.apply(config, {
                    length: w,
                    width: h,
                    x: x,
                    y: y
                });
            break;
            case 'bottom':
                Ext.apply(config, {
                    length: w,
                    width: h,
                    x: x,
                    y: h
                });
            break;
            case 'left':
                Ext.apply(config, {
                    length: h,
                    width: w,
                    x: x,
                    y: h
                });
            break;
            case 'right':
                Ext.apply(config, {
                    length: h,
                    width: w,
                    x: w,
                    y: h
                });
            break;
        }
        if (!axis.chart) {
            Ext.apply(config, axis);
            axis = me.axes.replace(Ext.createByAlias('axis.' + axis.type.toLowerCase(), config));
        }
        else {
            Ext.apply(axis, config);
        }
    },


    
    alignAxes: function() {
        var me = this,
            axes = me.axes,
            legend = me.legend,
            edges = ['top', 'right', 'bottom', 'left'],
            chartBBox,
            insetPadding = me.insetPadding,
            insets = {
                top: insetPadding,
                right: insetPadding,
                bottom: insetPadding,
                left: insetPadding
            };

        function getAxis(edge) {
            var i = axes.findIndex('position', edge);
            return (i < 0) ? null : axes.getAt(i);
        }

        
        Ext.each(edges, function(edge) {
            var isVertical = (edge === 'left' || edge === 'right'),
                axis = getAxis(edge),
                bbox;

            
            if (legend !== false) {
                if (legend.position === edge) {
                    bbox = legend.getBBox();
                    insets[edge] += (isVertical ? bbox.width : bbox.height) + insets[edge];
                }
            }

            
            
            if (axis && axis.bbox) {
                bbox = axis.bbox;
                insets[edge] += (isVertical ? bbox.width : bbox.height);
            }
        });
        
        chartBBox = {
            x: insets.left,
            y: insets.top,
            width: me.curWidth - insets.left - insets.right,
            height: me.curHeight - insets.top - insets.bottom
        };
        me.chartBBox = chartBBox;

        
        
        axes.each(function(axis) {
            var pos = axis.position,
                isVertical = (pos === 'left' || pos === 'right');

            axis.x = (pos === 'right' ? chartBBox.x + chartBBox.width : chartBBox.x);
            axis.y = (pos === 'top' ? chartBBox.y : chartBBox.y + chartBBox.height);
            axis.width = (isVertical ? chartBBox.width : chartBBox.height);
            axis.length = (isVertical ? chartBBox.height : chartBBox.width);
        });
    },

    
    initializeSeries: function(series, idx) {
        var me = this,
            themeAttrs = me.themeAttrs,
            seriesObj, markerObj, seriesThemes, st,
            markerThemes, colorArrayStyle = [],
            i = 0, l,
            config = {
                chart: me,
                seriesId: series.seriesId
            };
        if (themeAttrs) {
            seriesThemes = themeAttrs.seriesThemes;
            markerThemes = themeAttrs.markerThemes;
            seriesObj = Ext.apply({}, themeAttrs.series);
            markerObj = Ext.apply({}, themeAttrs.marker);
            config.seriesStyle = Ext.apply(seriesObj, seriesThemes[idx % seriesThemes.length]);
            config.seriesLabelStyle = Ext.apply({}, themeAttrs.seriesLabel);
            config.markerStyle = Ext.apply(markerObj, markerThemes[idx % markerThemes.length]);
            if (themeAttrs.colors) {
                config.colorArrayStyle = themeAttrs.colors;
            } else {
                colorArrayStyle = [];
                for (l = seriesThemes.length; i < l; i++) {
                    st = seriesThemes[i];
                    if (st.fill || st.stroke) {
                        colorArrayStyle.push(st.fill || st.stroke);
                    }
                }
                if (colorArrayStyle.length) {
                    config.colorArrayStyle = colorArrayStyle;
                }
            }
            config.seriesIdx = idx;
        }
        if (series instanceof Ext.chart.series.Series) {
            Ext.apply(series, config);
        } else {
            Ext.applyIf(config, series);
            series = me.series.replace(Ext.createByAlias('series.' + series.type.toLowerCase(), config));
        }
        if (series.initialize) {
            series.initialize();
        }
    },

    
    getMaxGutter: function() {
        var me = this,
            maxGutter = [0, 0];
        me.series.each(function(s) {
            var gutter = s.getGutters && s.getGutters() || [0, 0];
            maxGutter[0] = Math.max(maxGutter[0], gutter[0]);
            maxGutter[1] = Math.max(maxGutter[1], gutter[1]);
        });
        me.maxGutter = maxGutter;
    },

    
    drawAxis: function(axis) {
        axis.drawAxis();
    },

    
    drawCharts: function(series) {
        series.triggerafterrender = false;
        series.drawSeries();
        if (!this.animate) {
            series.fireEvent('afterrender');
        }
    },

    
    destroy: function() {
        this.surface.destroy();
        this.bindStore(null);
        this.callParent(arguments);
    }
});


Ext.define('Ext.chart.Highlight', {

    

    requires: ['Ext.fx.Anim'],

    

    
    highlight: false,

    highlightCfg : null,

    constructor: function(config) {
        if (config.highlight) {
            if (config.highlight !== true) { 
                this.highlightCfg = Ext.apply({}, config.highlight);
            }
            else {
                this.highlightCfg = {
                    fill: '#fdd',
                    radius: 20,
                    lineWidth: 5,
                    stroke: '#f55'
                };
            }
        }
    },

    
    highlightItem: function(item) {
        if (!item) {
            return;
        }
        
        var me = this,
            sprite = item.sprite,
            opts = me.highlightCfg,
            surface = me.chart.surface,
            animate = me.chart.animate,
            p,
            from,
            to,
            pi;

        if (!me.highlight || !sprite || sprite._highlighted) {
            return;
        }
        if (sprite._anim) {
            sprite._anim.paused = true;
        }
        sprite._highlighted = true;
        if (!sprite._defaults) {
            sprite._defaults = Ext.apply(sprite._defaults || {},
            sprite.attr);
            from = {};
            to = {};
            for (p in opts) {
                if (! (p in sprite._defaults)) {
                    sprite._defaults[p] = surface.availableAttrs[p];
                }
                from[p] = sprite._defaults[p];
                to[p] = opts[p];
                if (Ext.isObject(opts[p])) {
                    from[p] = {};
                    to[p] = {};
                    Ext.apply(sprite._defaults[p], sprite.attr[p]);
                    Ext.apply(from[p], sprite._defaults[p]);
                    for (pi in sprite._defaults[p]) {
                        if (! (pi in opts[p])) {
                            to[p][pi] = from[p][pi];
                        } else {
                            to[p][pi] = opts[p][pi];
                        }
                    }
                    for (pi in opts[p]) {
                        if (! (pi in to[p])) {
                            to[p][pi] = opts[p][pi];
                        }
                    }
                }
            }
            sprite._from = from;
            sprite._to = to;
        }
        if (animate) {
            sprite._anim = Ext.create('Ext.fx.Anim', {
                target: sprite,
                from: sprite._from,
                to: sprite._to,
                duration: 150
            });
        } else {
            sprite.setAttributes(sprite._to, true);
        }
    },

    
    unHighlightItem: function() {
        if (!this.highlight || !this.items) {
            return;
        }

        var me = this,
            items = me.items,
            len = items.length,
            opts = me.highlightCfg,
            animate = me.chart.animate,
            i = 0,
            obj,
            p,
            sprite;

        for (; i < len; i++) {
            if (!items[i]) {
                continue;
            }
            sprite = items[i].sprite;
            if (sprite && sprite._highlighted) {
                if (sprite._anim) {
                    sprite._anim.paused = true;
                }
                obj = {};
                for (p in opts) {
                    if (Ext.isObject(sprite._defaults[p])) {
                        obj[p] = {};
                        Ext.apply(obj[p], sprite._defaults[p]);
                    }
                    else {
                        obj[p] = sprite._defaults[p];
                    }
                }
                if (animate) {
                    sprite._anim = Ext.create('Ext.fx.Anim', {
                        target: sprite,
                        to: obj,
                        duration: 150
                    });
                }
                else {
                    sprite.setAttributes(obj, true);
                }
                delete sprite._highlighted;
                
            }
        }
    },

    cleanHighlights: function() {
        if (!this.highlight) {
            return;
        }

        var group = this.group,
            markerGroup = this.markerGroup,
            i = 0,
            l;
        for (l = group.getCount(); i < l; i++) {
            delete group.getAt(i)._defaults;
        }
        if (markerGroup) {
            for (l = markerGroup.getCount(); i < l; i++) {
                delete markerGroup.getAt(i)._defaults;
            }
        }
    }
});

Ext.define('Ext.chart.Label', {

    

    requires: ['Ext.draw.Color'],
    
    

    

    

    

    

    

    

    

    //@private a regex to parse url type colors.

    colorStringRe: /url\s*\(\s*#([^\/)]+)\s*\)/,
    
    //@private the mixin constructor. Used internally by Series.

    constructor: function(config) {
        var me = this;
        me.label = Ext.applyIf(me.label || {},
        {
            display: "none",
            color: "#000",
            field: "name",
            minMargin: 50,
            font: "11px Helvetica, sans-serif",
            orientation: "horizontal",
            renderer: function(v) {
                return v;
            }
        });

        if (me.label.display !== 'none') {
            me.labelsGroup = me.chart.surface.getGroup(me.seriesId + '-labels');
        }
    },

    //@private a method to render all labels in the labelGroup

    renderLabels: function() {
        var me = this,
            chart = me.chart,
            gradients = chart.gradients,
            gradient,
            items = me.items,
            animate = chart.animate,
            config = me.label,
            display = config.display,
            color = config.color,
            field = [].concat(config.field),
            group = me.labelsGroup,
            store = me.chart.store,
            len = store.getCount(),
            ratio = items.length / len,
            i, count, index, j, 
            k, gradientsCount = (gradients || 0) && gradients.length,
            colorStopTotal, colorStopIndex, colorStop,
            item, label, storeItem,
            sprite, spriteColor, spriteBrightness, labelColor,
            Color = Ext.draw.Color,
            colorString;

        if (display == 'none') {
            return;
        }

        for (i = 0, count = 0; i < len; i++) {
            index = 0;
            for (j = 0; j < ratio; j++) {
                item = items[count];
                label = group.getAt(count);
                storeItem = store.getAt(i);
                
                
                while(this.__excludes && this.__excludes[index]) {
                    index++;
                }

                if (!item && label) {
                    label.hide(true);
                }

                if (item && field[j]) {
                    if (!label) {
                        label = me.onCreateLabel(storeItem, item, i, display, j, index);
                    }
                    me.onPlaceLabel(label, storeItem, item, i, display, animate, j, index);

                    
                    if (config.contrast && item.sprite) {
                        sprite = item.sprite;
                        colorString = sprite._to && sprite._to.fill || sprite.attr.fill;
                        spriteColor = Color.fromString(colorString);
                        
                        if (colorString && !spriteColor) {
                            colorString = colorString.match(me.colorStringRe)[1];
                            for (k = 0; k < gradientsCount; k++) {
                                gradient = gradients[k];
                                if (gradient.id == colorString) {
                                    
                                    colorStop = 0; colorStopTotal = 0;
                                    for (colorStopIndex in gradient.stops) {
                                        colorStop++;
                                        colorStopTotal += Color.fromString(gradient.stops[colorStopIndex].color).getGrayscale();
                                    }
                                    spriteBrightness = (colorStopTotal / colorStop) / 255;
                                    break;
                                }
                            }
                        }
                        else {
                            spriteBrightness = spriteColor.getGrayscale() / 255;
                        }
                        labelColor = Color.fromString(label.attr.color || label.attr.fill).getHSL();
                        
                        labelColor[2] = spriteBrightness > 0.5? 0.2 : 0.8;
                        label.setAttributes({
                            fill: String(Color.fromHSL.apply({}, labelColor))
                        }, true);
                    }
                }
                count++;
                index++;
            }
        }
        me.hideLabels(count);
    },

    //@private a method to hide labels.

    hideLabels: function(index) {
        var labelsGroup = this.labelsGroup, len;
        if (labelsGroup) {
            len = labelsGroup.getCount();
            while (len-->index) {
                labelsGroup.getAt(len).hide(true);
            }
        }
    }
});
Ext.define('Ext.chart.MaskLayer', {
    extend: 'Ext.Component',
    
    constructor: function(config) {
        config = Ext.apply(config || {}, {
            style: 'position:absolute;background-color:#888;cursor:move;opacity:0.6;border:1px solid #222;'
        });
        this.callParent([config]);    
    },
    
    initComponent: function() {
        var me = this;
        me.callParent(arguments);
        me.addEvents(
            'mousedown',
            'mouseup',
            'mousemove',
            'mouseenter',
            'mouseleave'
        );
    },

    initDraggable: function() {
        this.callParent(arguments);
        this.dd.onStart = function (e) {
            var me = this,
                comp = me.comp;
    
            
            this.startPosition = comp.getPosition(true);
    
            
            
            if (comp.ghost && !comp.liveDrag) {
                 me.proxy = comp.ghost();
                 me.dragTarget = me.proxy.header.el;
            }
    
            
            if (me.constrain || me.constrainDelegate) {
                me.constrainTo = me.calculateConstrainRegion();
            }
        };
    }
});

Ext.define('Ext.chart.TipSurface', {

    

    extend: 'Ext.draw.Component',

    

    spriteArray: false,
    renderFirst: true,

    constructor: function(config) {
        this.callParent([config]);
        if (config.sprites) {
            this.spriteArray = [].concat(config.sprites);
            delete config.sprites;
        }
    },

    onRender: function() {
        var me = this,
            i = 0,
            l = 0,
            sp,
            sprites;
            this.callParent(arguments);
        sprites = me.spriteArray;
        if (me.renderFirst && sprites) {
            me.renderFirst = false;
            for (l = sprites.length; i < l; i++) {
                sp = me.surface.add(sprites[i]);
                sp.setAttributes({
                    hidden: false
                },
                true);
            }
        }
    }
});


Ext.define('Ext.chart.Tip', {

    

    requires: ['Ext.tip.ToolTip', 'Ext.chart.TipSurface'],

    

    constructor: function(config) {
        var me = this,
            surface,
            sprites,
            tipSurface;
        if (config.tips) {
            me.tipTimeout = null;
            me.tipConfig = Ext.apply({}, config.tips, {
                renderer: Ext.emptyFn,
                constrainPosition: false
            });
            me.tooltip = Ext.create('Ext.tip.ToolTip', me.tipConfig);
            Ext.getBody().on('mousemove', me.tooltip.onMouseMove, me.tooltip);
            if (me.tipConfig.surface) {
                
                surface = me.tipConfig.surface;
                sprites = surface.sprites;
                tipSurface = Ext.create('Ext.chart.TipSurface', {
                    id: 'tipSurfaceComponent',
                    sprites: sprites
                });
                if (surface.width && surface.height) {
                    tipSurface.setSize(surface.width, surface.height);
                }
                me.tooltip.add(tipSurface);
                me.spriteTip = tipSurface;
            }
        }
    },

    showTip: function(item) {
        var me = this;
        if (!me.tooltip) {
            return;
        }
        clearTimeout(me.tipTimeout);
        var tooltip = me.tooltip,
            spriteTip = me.spriteTip,
            tipConfig = me.tipConfig,
            trackMouse = tooltip.trackMouse,
            sprite, surface, surfaceExt, pos, x, y;
        if (!trackMouse) {
            tooltip.trackMouse = true;
            sprite = item.sprite;
            surface = sprite.surface;
            surfaceExt = Ext.get(surface.getId());
            if (surfaceExt) {
                pos = surfaceExt.getXY();
                x = pos[0] + (sprite.attr.x || 0) + (sprite.attr.translation && sprite.attr.translation.x || 0);
                y = pos[1] + (sprite.attr.y || 0) + (sprite.attr.translation && sprite.attr.translation.y || 0);
                tooltip.targetXY = [x, y];
            }
        }
        if (spriteTip) {
            tipConfig.renderer.call(tooltip, item.storeItem, item, spriteTip.surface);
        } else {
            tipConfig.renderer.call(tooltip, item.storeItem, item);
        }
        tooltip.show();
        tooltip.trackMouse = trackMouse;
    },

    hideTip: function(item) {
        var tooltip = this.tooltip;
        if (!tooltip) {
            return;
        }
        clearTimeout(this.tipTimeout);
        this.tipTimeout = setTimeout(function() {
            tooltip.hide();
        }, 0);
    }
});

Ext.define('Ext.chart.axis.Abstract', {

    

    requires: ['Ext.chart.Chart'],

    

    constructor: function(config) {
        config = config || {};

        var me = this,
            pos = config.position || 'left';

        pos = pos.charAt(0).toUpperCase() + pos.substring(1);
        
        config.label = Ext.apply(config['axisLabel' + pos + 'Style'] || {}, config.label || {});
        config.axisTitleStyle = Ext.apply(config['axisTitle' + pos + 'Style'] || {}, config.labelTitle || {});
        Ext.apply(me, config);
        me.fields = [].concat(me.fields);
        this.callParent();
        me.labels = [];
        me.getId();
        me.labelGroup = me.chart.surface.getGroup(me.axisId + "-labels");
    },

    alignment: null,
    grid: false,
    steps: 10,
    x: 0,
    y: 0,
    minValue: 0,
    maxValue: 0,

    getId: function() {
        return this.axisId || (this.axisId = Ext.id(null, 'ext-axis-'));
    },

    
    processView: Ext.emptyFn,

    drawAxis: Ext.emptyFn,
    addDisplayAndLabels: Ext.emptyFn
});


Ext.define('Ext.chart.axis.Axis', {

    

    extend: 'Ext.chart.axis.Abstract',

    alternateClassName: 'Ext.chart.Axis',

    requires: ['Ext.draw.Draw'],

    

    

    
    
    //@private force min/max values from store

    forceMinMax: false,
    
    
    dashSize: 3,
    
    
    position: 'bottom',
    
    
    skipFirst: false,
    
    
    length: 0,
    
    
    width: 0,
    
    majorTickSteps: false,

    
    applyData: Ext.emptyFn,

    
    calcEnds: function() {
        var me = this,
            math = Math,
            mmax = math.max,
            mmin = math.min,
            store = me.chart.substore || me.chart.store,
            series = me.chart.series.items,
            fields = me.fields,
            ln = fields.length,
            min = isNaN(me.minimum) ? Infinity : me.minimum,
            max = isNaN(me.maximum) ? -Infinity : me.maximum,
            prevMin = me.prevMin,
            prevMax = me.prevMax,
            aggregate = false,
            total = 0,
            excludes = [],
            outfrom, outto,
            i, l, values, rec, out;

        
        
        for (i = 0, l = series.length; !aggregate && i < l; i++) {
            aggregate = aggregate || series[i].stacked;
            excludes = series[i].__excludes || excludes;
        }
        store.each(function(record) {
            if (aggregate) {
                if (!isFinite(min)) {
                    min = 0;
                }
                for (values = [0, 0], i = 0; i < ln; i++) {
                    if (excludes[i]) {
                        continue;
                    }
                    rec = record.get(fields[i]);
                    values[+(rec > 0)] += math.abs(rec);
                }
                max = mmax(max, -values[0], values[1]);
                min = mmin(min, -values[0], values[1]);
            }
            else {
                for (i = 0; i < ln; i++) {
                    if (excludes[i]) {
                        continue;
                    }
                    value = record.get(fields[i]);
                    max = mmax(max, value);
                    min = mmin(min, value);
                }
            }
        });
        if (!isFinite(max)) {
            max = me.prevMax || 0;
        }
        if (!isFinite(min)) {
            min = me.prevMin || 0;
        }
        
        if (min != max && (max != (max >> 0))) {
            max = (max >> 0) + 1;
        }
        out = Ext.draw.Draw.snapEnds(min, max, me.majorTickSteps !== false ?  (me.majorTickSteps +1) : me.steps);
        outfrom = out.from;
        outto = out.to;
        if (me.forceMinMax) {
            if (!isNaN(max)) {
                out.to = max;
            }
            if (!isNaN(min)) {
                out.from = min;
            }
        }
        if (!isNaN(me.maximum)) {
            
            
            out.to = me.maximum;
        }
        if (!isNaN(me.minimum)) {
            
            
            out.from = me.minimum;
        }
        
        
        out.step = (out.to - out.from) / (outto - outfrom) * out.step;
        
        if (me.adjustMaximumByMajorUnit) {
            out.to += out.step;
        }
        if (me.adjustMinimumByMajorUnit) {
            out.from -= out.step;
        }
        me.prevMin = min == max? 0 : min;
        me.prevMax = max;
        return out;
    },

    
    drawAxis: function (init) {
        var me = this,
            i, j,
            x = me.x,
            y = me.y,
            gutterX = me.chart.maxGutter[0],
            gutterY = me.chart.maxGutter[1],
            dashSize = me.dashSize,
            subDashesX = me.minorTickSteps || 0,
            subDashesY = me.minorTickSteps || 0,
            length = me.length,
            position = me.position,
            inflections = [],
            calcLabels = false,
            stepCalcs = me.applyData(),
            step = stepCalcs.step,
            steps = stepCalcs.steps,
            from = stepCalcs.from,
            to = stepCalcs.to,
            trueLength,
            currentX,
            currentY,
            path,
            prev,
            dashesX,
            dashesY,
            delta;
        
        
        
        
        if (me.hidden || isNaN(step) || (from == to)) {
            return;
        }

        me.from = stepCalcs.from;
        me.to = stepCalcs.to;
        if (position == 'left' || position == 'right') {
            currentX = Math.floor(x) + 0.5;
            path = ["M", currentX, y, "l", 0, -length];
            trueLength = length - (gutterY * 2);
        }
        else {
            currentY = Math.floor(y) + 0.5;
            path = ["M", x, currentY, "l", length, 0];
            trueLength = length - (gutterX * 2);
        }
        
        delta = trueLength / (steps || 1);
        dashesX = Math.max(subDashesX +1, 0);
        dashesY = Math.max(subDashesY +1, 0);
        if (me.type == 'Numeric') {
            calcLabels = true;
            me.labels = [stepCalcs.from];
        }
        if (position == 'right' || position == 'left') {
            currentY = y - gutterY;
            currentX = x - ((position == 'left') * dashSize * 2);
            while (currentY >= y - gutterY - trueLength) {
                path.push("M", currentX, Math.floor(currentY) + 0.5, "l", dashSize * 2 + 1, 0);
                if (currentY != y - gutterY) {
                    for (i = 1; i < dashesY; i++) {
                        path.push("M", currentX + dashSize, Math.floor(currentY + delta * i / dashesY) + 0.5, "l", dashSize + 1, 0);
                    }
                }
                inflections.push([ Math.floor(x), Math.floor(currentY) ]);
                currentY -= delta;
                if (calcLabels) {
                    me.labels.push(me.labels[me.labels.length -1] + step);
                }
                if (delta === 0) {
                    break;
                }
            }
            if (Math.round(currentY + delta - (y - gutterY - trueLength))) {
                path.push("M", currentX, Math.floor(y - length + gutterY) + 0.5, "l", dashSize * 2 + 1, 0);
                for (i = 1; i < dashesY; i++) {
                    path.push("M", currentX + dashSize, Math.floor(y - length + gutterY + delta * i / dashesY) + 0.5, "l", dashSize + 1, 0);
                }
                inflections.push([ Math.floor(x), Math.floor(currentY) ]);
                if (calcLabels) {
                    me.labels.push(me.labels[me.labels.length -1] + step);
                }
            }
        } else {
            currentX = x + gutterX;
            currentY = y - ((position == 'top') * dashSize * 2);
            while (currentX <= x + gutterX + trueLength) {
                path.push("M", Math.floor(currentX) + 0.5, currentY, "l", 0, dashSize * 2 + 1);
                if (currentX != x + gutterX) {
                    for (i = 1; i < dashesX; i++) {
                        path.push("M", Math.floor(currentX - delta * i / dashesX) + 0.5, currentY, "l", 0, dashSize + 1);
                    }
                }
                inflections.push([ Math.floor(currentX), Math.floor(y) ]);
                currentX += delta;
                if (calcLabels) {
                    me.labels.push(me.labels[me.labels.length -1] + step);
                }
                if (delta === 0) {
                    break;
                }
            }
            if (Math.round(currentX - delta - (x + gutterX + trueLength))) {
                path.push("M", Math.floor(x + length - gutterX) + 0.5, currentY, "l", 0, dashSize * 2 + 1);
                for (i = 1; i < dashesX; i++) {
                    path.push("M", Math.floor(x + length - gutterX - delta * i / dashesX) + 0.5, currentY, "l", 0, dashSize + 1);
                }
                inflections.push([ Math.floor(currentX), Math.floor(y) ]);
                if (calcLabels) {
                    me.labels.push(me.labels[me.labels.length -1] + step);
                }
            }
        }
        if (!me.axis) {
            me.axis = me.chart.surface.add(Ext.apply({
                type: 'path',
                path: path
            }, me.axisStyle));
        }
        me.axis.setAttributes({
            path: path
        }, true);
        me.inflections = inflections;
        if (!init && me.grid) {
            me.drawGrid();
        }
        me.axisBBox = me.axis.getBBox();
        me.drawLabel();
    },

    
    drawGrid: function() {
        var me = this,
            surface = me.chart.surface, 
            grid = me.grid,
            odd = grid.odd,
            even = grid.even,
            inflections = me.inflections,
            ln = inflections.length - ((odd || even)? 0 : 1),
            position = me.position,
            gutter = me.chart.maxGutter,
            width = me.width - 2,
            vert = false,
            point, prevPoint,
            i = 1,
            path = [], styles, lineWidth, dlineWidth,
            oddPath = [], evenPath = [];
        
        if ((gutter[1] !== 0 && (position == 'left' || position == 'right')) ||
            (gutter[0] !== 0 && (position == 'top' || position == 'bottom'))) {
            i = 0;
            ln++;
        }
        for (; i < ln; i++) {
            point = inflections[i];
            prevPoint = inflections[i - 1];
            if (odd || even) {
                path = (i % 2)? oddPath : evenPath;
                styles = ((i % 2)? odd : even) || {};
                lineWidth = (styles.lineWidth || styles['stroke-width'] || 0) / 2;
                dlineWidth = 2 * lineWidth;
                if (position == 'left') {
                    path.push("M", prevPoint[0] + 1 + lineWidth, prevPoint[1] + 0.5 - lineWidth, 
                              "L", prevPoint[0] + 1 + width - lineWidth, prevPoint[1] + 0.5 - lineWidth,
                              "L", point[0] + 1 + width - lineWidth, point[1] + 0.5 + lineWidth,
                              "L", point[0] + 1 + lineWidth, point[1] + 0.5 + lineWidth, "Z");
                }
                else if (position == 'right') {
                    path.push("M", prevPoint[0] - lineWidth, prevPoint[1] + 0.5 - lineWidth, 
                              "L", prevPoint[0] - width + lineWidth, prevPoint[1] + 0.5 - lineWidth,
                              "L", point[0] - width + lineWidth, point[1] + 0.5 + lineWidth,
                              "L", point[0] - lineWidth, point[1] + 0.5 + lineWidth, "Z");
                }
                else if (position == 'top') {
                    path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + lineWidth, 
                              "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] + 1 + width - lineWidth,
                              "L", point[0] + 0.5 - lineWidth, point[1] + 1 + width - lineWidth,
                              "L", point[0] + 0.5 - lineWidth, point[1] + 1 + lineWidth, "Z");
                }
                else {
                    path.push("M", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - lineWidth, 
                            "L", prevPoint[0] + 0.5 + lineWidth, prevPoint[1] - width + lineWidth,
                            "L", point[0] + 0.5 - lineWidth, point[1] - width + lineWidth,
                            "L", point[0] + 0.5 - lineWidth, point[1] - lineWidth, "Z");
                }
            } else {
                if (position == 'left') {
                    path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", width, 0]);
                }
                else if (position == 'right') {
                    path = path.concat(["M", point[0] - 0.5, point[1] + 0.5, "l", -width, 0]);
                }
                else if (position == 'top') {
                    path = path.concat(["M", point[0] + 0.5, point[1] + 0.5, "l", 0, width]);
                }
                else {
                    path = path.concat(["M", point[0] + 0.5, point[1] - 0.5, "l", 0, -width]);
                }
            }
        }
        if (odd || even) {
            if (oddPath.length) {
                if (!me.gridOdd && oddPath.length) {
                    me.gridOdd = surface.add({
                        type: 'path',
                        path: oddPath
                    });
                }
                me.gridOdd.setAttributes(Ext.apply({
                    path: oddPath,
                    hidden: false
                }, odd || {}), true);
            }
            if (evenPath.length) {
                if (!me.gridEven) {
                    me.gridEven = surface.add({
                        type: 'path',
                        path: evenPath
                    });
                } 
                me.gridEven.setAttributes(Ext.apply({
                    path: evenPath,
                    hidden: false
                }, even || {}), true);
            }
        }
        else {
            if (path.length) {
                if (!me.gridLines) {
                    me.gridLines = me.chart.surface.add({
                        type: 'path',
                        path: path,
                        "stroke-width": me.lineWidth || 1,
                        stroke: me.gridColor || '#ccc'
                    });
                }
                me.gridLines.setAttributes({
                    hidden: false,
                    path: path
                }, true);
            }
            else if (me.gridLines) {
                me.gridLines.hide(true);
            }
        }
    },

    //@private

    getOrCreateLabel: function(i, text) {
        var me = this,
            labelGroup = me.labelGroup,
            textLabel = labelGroup.getAt(i),
            surface = me.chart.surface;
        if (textLabel) {
            if (text != textLabel.attr.text) {
                textLabel.setAttributes(Ext.apply({
                    text: text
                }, me.label), true);
                textLabel._bbox = textLabel.getBBox();
            }
        }
        else {
            textLabel = surface.add(Ext.apply({
                group: labelGroup,
                type: 'text',
                x: 0,
                y: 0,
                text: text
            }, me.label));
            surface.renderItem(textLabel);
            textLabel._bbox = textLabel.getBBox();
        }
        
        if (me.label.rotation) {
            textLabel.setAttributes({
                rotation: {
                    degrees: 0    
                }    
            }, true);
            textLabel._ubbox = textLabel.getBBox();
            textLabel.setAttributes(me.label, true);
        } else {
            textLabel._ubbox = textLabel._bbox;
        }
        return textLabel;
    },
    
    rect2pointArray: function(sprite) {
        var surface = this.chart.surface,
            rect = surface.getBBox(sprite, true),
            p1 = [rect.x, rect.y],
            p1p = p1.slice(),
            p2 = [rect.x + rect.width, rect.y],
            p2p = p2.slice(),
            p3 = [rect.x + rect.width, rect.y + rect.height],
            p3p = p3.slice(),
            p4 = [rect.x, rect.y + rect.height],
            p4p = p4.slice(),
            matrix = sprite.matrix;
        
        p1[0] = matrix.x.apply(matrix, p1p);
        p1[1] = matrix.y.apply(matrix, p1p);
        
        p2[0] = matrix.x.apply(matrix, p2p);
        p2[1] = matrix.y.apply(matrix, p2p);
        
        p3[0] = matrix.x.apply(matrix, p3p);
        p3[1] = matrix.y.apply(matrix, p3p);
        
        p4[0] = matrix.x.apply(matrix, p4p);
        p4[1] = matrix.y.apply(matrix, p4p);
        return [p1, p2, p3, p4];
    },
    
    intersect: function(l1, l2) {
        var r1 = this.rect2pointArray(l1),
            r2 = this.rect2pointArray(l2);
        return !!Ext.draw.Draw.intersect(r1, r2).length;
    },
    
    drawHorizontalLabels: function() {
       var  me = this,
            labelConf = me.label,
            floor = Math.floor,
            max = Math.max,
            axes = me.chart.axes,
            position = me.position,
            inflections = me.inflections,
            ln = inflections.length,
            labels = me.labels,
            labelGroup = me.labelGroup,
            maxHeight = 0,
            ratio,
            gutterY = me.chart.maxGutter[1],
            ubbox, bbox, point, prevX, prevLabel,
            projectedWidth = 0,
            textLabel, attr, textRight, text,
            label, last, x, y, i, firstLabel;

        last = ln - 1;
        
        point = inflections[0];
        firstLabel = me.getOrCreateLabel(0, me.label.renderer(labels[0]));
        ratio = Math.abs(Math.sin(labelConf.rotate && (labelConf.rotate.degrees * Math.PI / 180) || 0)) >> 0;
        
        for (i = 0; i < ln; i++) {
            point = inflections[i];
            text = me.label.renderer(labels[i]);
            textLabel = me.getOrCreateLabel(i, text);
            bbox = textLabel._bbox;
            maxHeight = max(maxHeight, bbox.height + me.dashSize + me.label.padding);
            x = floor(point[0] - (ratio? bbox.height : bbox.width) / 2);
            if (me.chart.maxGutter[0] == 0) {
                if (i == 0 && axes.findIndex('position', 'left') == -1) {
                    x = point[0];
                }
                else if (i == last && axes.findIndex('position', 'right') == -1) {
                    x = point[0] - bbox.width;
                }
            }
            if (position == 'top') {
                y = point[1] - (me.dashSize * 2) - me.label.padding - (bbox.height / 2);
            }
            else {
                y = point[1] + (me.dashSize * 2) + me.label.padding + (bbox.height / 2);
            }
            
            textLabel.setAttributes({
                hidden: false,
                x: x,
                y: y
            }, true);

            
            if (i != 0 && (me.intersect(textLabel, prevLabel)
                || me.intersect(textLabel, firstLabel))) {
                textLabel.hide(true);
                continue;
            }
            
            prevLabel = textLabel;
        }

        return maxHeight;
    },
    
    drawVerticalLabels: function() {
        var me = this,
            inflections = me.inflections,
            position = me.position,
            ln = inflections.length,
            labels = me.labels,
            maxWidth = 0,
            max = Math.max,
            floor = Math.floor,
            ceil = Math.ceil,
            axes = me.chart.axes,
            gutterY = me.chart.maxGutter[1],
            ubbox, bbox, point, prevLabel,
            projectedWidth = 0,
            textLabel, attr, textRight, text,
            label, last, x, y, i;

        last = ln;
        for (i = 0; i < last; i++) {
            point = inflections[i];
            text = me.label.renderer(labels[i]);
            textLabel = me.getOrCreateLabel(i, text);
            bbox = textLabel._bbox;
            
            maxWidth = max(maxWidth, bbox.width + me.dashSize + me.label.padding);
            y = point[1];
            if (gutterY < bbox.height / 2) {
                if (i == last - 1 && axes.findIndex('position', 'top') == -1) {
                    y = me.y - me.length + ceil(bbox.height / 2);
                }
                else if (i == 0 && axes.findIndex('position', 'bottom') == -1) {
                    y = me.y - floor(bbox.height / 2);
                }
            }
            if (position == 'left') {
                x = point[0] - bbox.width - me.dashSize - me.label.padding - 2;
            }
            else {
                x = point[0] + me.dashSize + me.label.padding + 2;
            }    
            textLabel.setAttributes(Ext.apply({
                hidden: false,
                x: x,
                y: y
            }, me.label), true);
            
            if (i != 0 && me.intersect(textLabel, prevLabel)) {
                textLabel.hide(true);
                continue;
            }
            prevLabel = textLabel;
        }
        
        return maxWidth;
    },

    
    drawLabel: function() {
        var me = this,
            position = me.position,
            labelGroup = me.labelGroup,
            inflections = me.inflections,
            maxWidth = 0,
            maxHeight = 0,
            ln, i;

        if (position == 'left' || position == 'right') {
            maxWidth = me.drawVerticalLabels();    
        } else {
            maxHeight = me.drawHorizontalLabels();
        }

        
        ln = labelGroup.getCount();
        i = inflections.length;
        for (; i < ln; i++) {
            labelGroup.getAt(i).hide(true);
        }

        me.bbox = {};
        Ext.apply(me.bbox, me.axisBBox);
        me.bbox.height = maxHeight;
        me.bbox.width = maxWidth;
        if (Ext.isString(me.title)) {
            me.drawTitle(maxWidth, maxHeight);
        }
    },

    
    elipsis: function(sprite, text, desiredWidth, minWidth, center) {
        var bbox,
            x;

        if (desiredWidth < minWidth) {
            sprite.hide(true);
            return false;
        }
        while (text.length > 4) {
            text = text.substr(0, text.length - 4) + "...";
            sprite.setAttributes({
                text: text
            }, true);
            bbox = sprite.getBBox();
            if (bbox.width < desiredWidth) {
                if (typeof center == 'number') {
                    sprite.setAttributes({
                        x: Math.floor(center - (bbox.width / 2))
                    }, true);
                }
                break;
            }
        }
        return true;
    },

    
    setTitle: function(title) {
        this.title = title;
        this.drawLabel();
    },

    
    drawTitle: function(maxWidth, maxHeight) {
        var me = this,
            position = me.position,
            surface = me.chart.surface,
            displaySprite = me.displaySprite,
            title = me.title,
            rotate = (position == 'left' || position == 'right'),
            x = me.x,
            y = me.y,
            base, bbox, pad;

        if (displaySprite) {
            displaySprite.setAttributes({text: title}, true);
        } else {
            base = {
                type: 'text',
                x: 0,
                y: 0,
                text: title
            };
            displaySprite = me.displaySprite = surface.add(Ext.apply(base, me.axisTitleStyle, me.labelTitle));
            surface.renderItem(displaySprite);
        }
        bbox = displaySprite.getBBox();
        pad = me.dashSize + me.label.padding;

        if (rotate) {
            y -= ((me.length / 2) - (bbox.height / 2));
            if (position == 'left') {
                x -= (maxWidth + pad + (bbox.width / 2));
            }
            else {
                x += (maxWidth + pad + bbox.width - (bbox.width / 2));
            }
            me.bbox.width += bbox.width + 10;
        }
        else {
            x += (me.length / 2) - (bbox.width * 0.5);
            if (position == 'top') {
                y -= (maxHeight + pad + (bbox.height * 0.3));
            }
            else {
                y += (maxHeight + pad + (bbox.height * 0.8));
            }
            me.bbox.height += bbox.height + 10;
        }
        displaySprite.setAttributes({
            translate: {
                x: x,
                y: y
            }
        }, true);
    }
});


Ext.define('Ext.chart.axis.Category', {

    

    extend: 'Ext.chart.axis.Axis',

    alternateClassName: 'Ext.chart.CategoryAxis',

    alias: 'axis.category',

    

    
    categoryNames: null,

    
    calculateCategoryCount: false,

    
    setLabels: function() {
        var store = this.chart.store,
            fields = this.fields,
            ln = fields.length,
            i;

        this.labels = [];
        store.each(function(record) {
            for (i = 0; i < ln; i++) {
                this.labels.push(record.get(fields[i]));
            }
        }, this);
    },

    
    applyData: function() {
        this.callParent();
        this.setLabels();
        var count = this.chart.store.getCount();
        return {
            from: 0,
            to: count,
            power: 1,
            step: 1,
            steps: count - 1
        };
    }
});


Ext.define('Ext.chart.axis.Gauge', {

    

    extend: 'Ext.chart.axis.Abstract',

    
    
    

    

    

    

    position: 'gauge',

    alias: 'axis.gauge',

    drawAxis: function(init) {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + bbox.height,
            margin = this.margin || 10,
            rho = Math.min(bbox.width, 2 * bbox.height) /2 + margin,
            sprites = [], sprite,
            steps = this.steps,
            i, pi = Math.PI,
            cos = Math.cos,
            sin = Math.sin;

        if (this.sprites && !chart.resizing) {
            this.drawLabel();
            return;
        }

        if (this.margin >= 0) {
            if (!this.sprites) {
                
                for (i = 0; i <= steps; i++) {
                    sprite = surface.add({
                        type: 'path',
                        path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi),
                                    centerY + (rho - margin) * sin(i / steps * pi - pi),
                                    'L', centerX + rho * cos(i / steps * pi - pi),
                                    centerY + rho * sin(i / steps * pi - pi), 'Z'],
                        stroke: '#ccc'
                    });
                    sprite.setAttributes({
                        hidden: false
                    }, true);
                    sprites.push(sprite);
                }
            } else {
                sprites = this.sprites;
                
                for (i = 0; i <= steps; i++) {
                    sprites[i].setAttributes({
                        path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi),
                                    centerY + (rho - margin) * sin(i / steps * pi - pi),
                               'L', centerX + rho * cos(i / steps * pi - pi),
                                    centerY + rho * sin(i / steps * pi - pi), 'Z'],
                        stroke: '#ccc'
                    }, true);
                }
            }
        }
        this.sprites = sprites;
        this.drawLabel();
        if (this.title) {
            this.drawTitle();
        }
    },
    
    drawTitle: function() {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            labelSprite = me.titleSprite,
            labelBBox;
        
        if (!labelSprite) {
            me.titleSprite = labelSprite = surface.add({
                type: 'text',
                zIndex: 2
            });    
        }
        labelSprite.setAttributes(Ext.apply({
            text: me.title
        }, me.label || {}), true);
        labelBBox = labelSprite.getBBox();
        labelSprite.setAttributes({
            x: bbox.x + (bbox.width / 2) - (labelBBox.width / 2),
            y: bbox.y + bbox.height - (labelBBox.height / 2) - 4
        }, true);
    },

    
    setTitle: function(title) {
        this.title = title;
        this.drawTitle();
    },

    drawLabel: function() {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + bbox.height,
            margin = this.margin || 10,
            rho = Math.min(bbox.width, 2 * bbox.height) /2 + 2 * margin,
            round = Math.round,
            labelArray = [], label,
            maxValue = this.maximum || 0,
            steps = this.steps, i = 0,
            adjY,
            pi = Math.PI,
            cos = Math.cos,
            sin = Math.sin,
            labelConf = this.label,
            renderer = labelConf.renderer || function(v) { return v; };

        if (!this.labelArray) {
            
            for (i = 0; i <= steps; i++) {
                
                adjY = (i === 0 || i === steps) ? 7 : 0;
                label = surface.add({
                    type: 'text',
                    text: renderer(round(i / steps * maxValue)),
                    x: centerX + rho * cos(i / steps * pi - pi),
                    y: centerY + rho * sin(i / steps * pi - pi) - adjY,
                    'text-anchor': 'middle',
                    'stroke-width': 0.2,
                    zIndex: 10,
                    stroke: '#333'
                });
                label.setAttributes({
                    hidden: false
                }, true);
                labelArray.push(label);
            }
        }
        else {
            labelArray = this.labelArray;
            
            for (i = 0; i <= steps; i++) {
                
                adjY = (i === 0 || i === steps) ? 7 : 0;
                labelArray[i].setAttributes({
                    text: renderer(round(i / steps * maxValue)),
                    x: centerX + rho * cos(i / steps * pi - pi),
                    y: centerY + rho * sin(i / steps * pi - pi) - adjY
                }, true);
            }
        }
        this.labelArray = labelArray;
    }
});

Ext.define('Ext.chart.axis.Numeric', {

    

    extend: 'Ext.chart.axis.Axis',

    alternateClassName: 'Ext.chart.NumericAxis',

    

    type: 'numeric',

    alias: 'axis.numeric',

    constructor: function(config) {
        var me = this, label, f;
        me.callParent([config]);
        label = me.label;
        if (me.roundToDecimal === false) {
            return;
        }
        if (label.renderer) {
            f = label.renderer;
            label.renderer = function(v) {
                return me.roundToDecimal( f(v), me.decimals );
            };
        } else {
            label.renderer = function(v) {
                return me.roundToDecimal(v, me.decimals);
            };
        }
    },
    
    roundToDecimal: function(v, dec) {
        var val = Math.pow(10, dec || 0);
        return ((v * val) >> 0) / val;
    },
    
    
    minimum: NaN,

    
    maximum: NaN,

    
    decimals: 2,

    
    scale: "linear",

    
    position: 'left',

    
    adjustMaximumByMajorUnit: false,

    
    adjustMinimumByMajorUnit: false,

    
    applyData: function() {
        this.callParent();
        return this.calcEnds();
    }
});


Ext.define('Ext.chart.axis.Radial', {

    

    extend: 'Ext.chart.axis.Abstract',

    

    position: 'radial',

    alias: 'axis.radial',

    drawAxis: function(init) {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            store = chart.store,
            l = store.getCount(),
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + (bbox.height / 2),
            rho = Math.min(bbox.width, bbox.height) /2,
            sprites = [], sprite,
            steps = this.steps,
            i, j, pi2 = Math.PI * 2,
            cos = Math.cos, sin = Math.sin;

        if (this.sprites && !chart.resizing) {
            this.drawLabel();
            return;
        }

        if (!this.sprites) {
            
            for (i = 1; i <= steps; i++) {
                sprite = surface.add({
                    type: 'circle',
                    x: centerX,
                    y: centerY,
                    radius: Math.max(rho * i / steps, 0),
                    stroke: '#ccc'
                });
                sprite.setAttributes({
                    hidden: false
                }, true);
                sprites.push(sprite);
            }
            
            store.each(function(rec, i) {
                sprite = surface.add({
                    type: 'path',
                    path: ['M', centerX, centerY, 'L', centerX + rho * cos(i / l * pi2), centerY + rho * sin(i / l * pi2), 'Z'],
                    stroke: '#ccc'
                });
                sprite.setAttributes({
                    hidden: false
                }, true);
                sprites.push(sprite);
            });
        } else {
            sprites = this.sprites;
            
            for (i = 0; i < steps; i++) {
                sprites[i].setAttributes({
                    x: centerX,
                    y: centerY,
                    radius: Math.max(rho * (i + 1) / steps, 0),
                    stroke: '#ccc'
                }, true);
            }
            
            store.each(function(rec, j) {
                sprites[i + j].setAttributes({
                    path: ['M', centerX, centerY, 'L', centerX + rho * cos(j / l * pi2), centerY + rho * sin(j / l * pi2), 'Z'],
                    stroke: '#ccc'
                }, true);
            });
        }
        this.sprites = sprites;

        this.drawLabel();
    },

    drawLabel: function() {
        var chart = this.chart,
            surface = chart.surface,
            bbox = chart.chartBBox,
            store = chart.store,
            centerX = bbox.x + (bbox.width / 2),
            centerY = bbox.y + (bbox.height / 2),
            rho = Math.min(bbox.width, bbox.height) /2,
            max = Math.max, round = Math.round,
            labelArray = [], label,
            fields = [], nfields,
            categories = [], xField,
            aggregate = !this.maximum,
            maxValue = this.maximum || 0,
            steps = this.steps, i = 0, j, dx, dy,
            pi2 = Math.PI * 2,
            cos = Math.cos, sin = Math.sin,
            display = this.label.display,
            draw = display !== 'none',
            margin = 10;

        if (!draw) {
            return;
        }

        
        chart.series.each(function(series) {
            fields.push(series.yField);
            xField = series.xField;
        });
        
        
        store.each(function(record, i) {
            if (aggregate) {
                for (i = 0, nfields = fields.length; i < nfields; i++) {
                    maxValue = max(+record.get(fields[i]), maxValue);
                }
            }
            categories.push(record.get(xField));
        });
        if (!this.labelArray) {
            if (display != 'categories') {
                
                for (i = 1; i <= steps; i++) {
                    label = surface.add({
                        type: 'text',
                        text: round(i / steps * maxValue),
                        x: centerX,
                        y: centerY - rho * i / steps,
                        'text-anchor': 'middle',
                        'stroke-width': 0.1,
                        stroke: '#333'
                    });
                    label.setAttributes({
                        hidden: false
                    }, true);
                    labelArray.push(label);
                }
            }
            if (display != 'scale') {
                
                for (j = 0, steps = categories.length; j < steps; j++) {
                    dx = cos(j / steps * pi2) * (rho + margin);
                    dy = sin(j / steps * pi2) * (rho + margin);
                    label = surface.add({
                        type: 'text',
                        text: categories[j],
                        x: centerX + dx,
                        y: centerY + dy,
                        'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start')
                    });
                    label.setAttributes({
                        hidden: false
                    }, true);
                    labelArray.push(label);
                }
            }
        }
        else {
            labelArray = this.labelArray;
            if (display != 'categories') {
                
                for (i = 0; i < steps; i++) {
                    labelArray[i].setAttributes({
                        text: round((i + 1) / steps * maxValue),
                        x: centerX,
                        y: centerY - rho * (i + 1) / steps,
                        'text-anchor': 'middle',
                        'stroke-width': 0.1,
                        stroke: '#333'
                    }, true);
                }
            }
            if (display != 'scale') {
                
                for (j = 0, steps = categories.length; j < steps; j++) {
                    dx = cos(j / steps * pi2) * (rho + margin);
                    dy = sin(j / steps * pi2) * (rho + margin);
                    if (labelArray[i + j]) {
                        labelArray[i + j].setAttributes({
                            type: 'text',
                            text: categories[j],
                            x: centerX + dx,
                            y: centerY + dy,
                            'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start')
                        }, true);
                    }
                }
            }
        }
        this.labelArray = labelArray;
    }
});

Ext.define('Ext.data.AbstractStore', {
    requires: ['Ext.util.MixedCollection', 'Ext.data.Operation', 'Ext.util.Filter'],
    
    mixins: {
        observable: 'Ext.util.Observable',
        sortable: 'Ext.util.Sortable'
    },
    
    statics: {
        create: function(store){
            if (!store.isStore) {
                if (!store.type) {
                    store.type = 'store';
                }
                store = Ext.createByAlias('store.' + store.type, store);
            }
            return store;
        }    
    },
    
    remoteSort  : false,
    remoteFilter: false,

    

    
    autoLoad: false,

    
    autoSync: false,

    
    batchUpdateMode: 'operation',

    
    filterOnLoad: true,

    
    sortOnLoad: true,

    
    implicitModel: false,

    
    defaultProxyType: 'memory',

    
    isDestroyed: false,

    isStore: true,

    
    
    

    sortRoot: 'data',
    
    
    constructor: function(config) {
        var me = this,
            filters;
        
        me.addEvents(
            
            'add',

            
            'remove',
            
            
            'update',

            
            'datachanged',

            
            'beforeload',

            
            'load',

            
            'beforesync',
            
            'clear'
        );
        
        Ext.apply(me, config);
        

        
        me.removed = [];

        me.mixins.observable.constructor.apply(me, arguments);
        me.model = Ext.ModelManager.getModel(me.model);

        
        Ext.applyIf(me, {
            modelDefaults: {}
        });

        
        if (!me.model && me.fields) {
            me.model = Ext.define('Ext.data.Store.ImplicitModel-' + (me.storeId || Ext.id()), {
                extend: 'Ext.data.Model',
                fields: me.fields,
                proxy: me.proxy || me.defaultProxyType
            });

            delete me.fields;

            me.implicitModel = true;
        }

        
        me.setProxy(me.proxy || me.model.getProxy());

        if (me.id && !me.storeId) {
            me.storeId = me.id;
            delete me.id;
        }

        if (me.storeId) {
            Ext.data.StoreManager.register(me);
        }
        
        me.mixins.sortable.initSortable.call(me);        
        
        
        filters = me.decodeFilters(me.filters);
        me.filters = Ext.create('Ext.util.MixedCollection');
        me.filters.addAll(filters);
    },

    
    setProxy: function(proxy) {
        var me = this;
        
        if (proxy instanceof Ext.data.proxy.Proxy) {
            proxy.setModel(me.model);
        } else {
            if (Ext.isString(proxy)) {
                proxy = {
                    type: proxy    
                };
            }
            Ext.applyIf(proxy, {
                model: me.model
            });
            
            proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);
        }
        
        me.proxy = proxy;
        
        return me.proxy;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    create: function(data, options) {
        var me = this,
            instance = Ext.ModelManager.create(Ext.applyIf(data, me.modelDefaults), me.model.modelName),
            operation;
        
        options = options || {};

        Ext.applyIf(options, {
            action : 'create',
            records: [instance]
        });

        operation = Ext.create('Ext.data.Operation', options);

        me.proxy.create(operation, me.onProxyWrite, me);
        
        return instance;
    },

    read: function() {
        return this.load.apply(this, arguments);
    },

    onProxyRead: Ext.emptyFn,

    update: function(options) {
        var me = this,
            operation;
        options = options || {};

        Ext.applyIf(options, {
            action : 'update',
            records: me.getUpdatedRecords()
        });

        operation = Ext.create('Ext.data.Operation', options);

        return me.proxy.update(operation, me.onProxyWrite, me);
    },

    
    onProxyWrite: function(operation) {
        var me = this,
            success = operation.wasSuccessful(),
            records = operation.getRecords();

        switch (operation.action) {
            case 'create':
                me.onCreateRecords(records, operation, success);
                break;
            case 'update':
                me.onUpdateRecords(records, operation, success);
                break;
            case 'destroy':
                me.onDestroyRecords(records, operation, success);
                break;
        }

        if (success) {
            me.fireEvent('write', me, operation);
            me.fireEvent('datachanged', me);
        }
        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, success]);
    },


    
    destroy: function(options) {
        var me = this,
            operation;
            
        options = options || {};

        Ext.applyIf(options, {
            action : 'destroy',
            records: me.getRemovedRecords()
        });

        operation = Ext.create('Ext.data.Operation', options);

        return me.proxy.destroy(operation, me.onProxyWrite, me);
    },

    
    onBatchOperationComplete: function(batch, operation) {
        return this.onProxyWrite(operation);
    },

    
    onBatchComplete: function(batch, operation) {
        var me = this,
            operations = batch.operations,
            length = operations.length,
            i;

        me.suspendEvents();

        for (i = 0; i < length; i++) {
            me.onProxyWrite(operations[i]);
        }

        me.resumeEvents();

        me.fireEvent('datachanged', me);
    },

    onBatchException: function(batch, operation) {
        
        
        
        
        
    },

    
    filterNew: function(item) {
        
        return item.phantom === true && item.isValid();
    },

    
    getNewRecords: function() {
        return [];
    },

    
    getUpdatedRecords: function() {
        return [];
    },

    
    filterUpdated: function(item) {
        
        return item.dirty === true && item.phantom !== true && item.isValid();
    },

    
    getRemovedRecords: function() {
        return this.removed;
    },

    filter: function(filters, value) {

    },

    
    decodeFilters: function(filters) {
        if (!Ext.isArray(filters)) {
            if (filters === undefined) {
                filters = [];
            } else {
                filters = [filters];
            }
        }

        var length = filters.length,
            Filter = Ext.util.Filter,
            config, i;

        for (i = 0; i < length; i++) {
            config = filters[i];

            if (!(config instanceof Filter)) {
                Ext.apply(config, {
                    root: 'data'
                });

                
                if (config.fn) {
                    config.filterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        filterFn: config
                    };
                }

                filters[i] = new Filter(config);
            }
        }

        return filters;
    },

    clearFilter: function(supressEvent) {

    },

    isFiltered: function() {

    },

    filterBy: function(fn, scope) {

    },
    
    
    sync: function() {
        var me        = this,
            options   = {},
            toCreate  = me.getNewRecords(),
            toUpdate  = me.getUpdatedRecords(),
            toDestroy = me.getRemovedRecords(),
            needsSync = false;

        if (toCreate.length > 0) {
            options.create = toCreate;
            needsSync = true;
        }

        if (toUpdate.length > 0) {
            options.update = toUpdate;
            needsSync = true;
        }

        if (toDestroy.length > 0) {
            options.destroy = toDestroy;
            needsSync = true;
        }

        if (needsSync && me.fireEvent('beforesync', options) !== false) {
            me.proxy.batch(options, me.getBatchListeners());
        }
    },


    
    getBatchListeners: function() {
        var me = this,
            listeners = {
                scope: me,
                exception: me.onBatchException
            };

        if (me.batchUpdateMode == 'operation') {
            listeners.operationcomplete = me.onBatchOperationComplete;
        } else {
            listeners.complete = me.onBatchComplete;
        }

        return listeners;
    },

    
    save: function() {
        return this.sync.apply(this, arguments);
    },

    
    load: function(options) {
        var me = this,
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'read',
            filters: me.filters.items,
            sorters: me.getSorters()
        });
        
        operation = Ext.create('Ext.data.Operation', options);

        if (me.fireEvent('beforeload', me, operation) !== false) {
            me.loading = true;
            me.proxy.read(operation, me.onProxyLoad, me);
        }
        
        return me;
    },

    
    afterEdit : function(record) {
        var me = this;
        
        if (me.autoSync) {
            me.sync();
        }
        
        me.fireEvent('update', me, record, Ext.data.Model.EDIT);
    },

    
    afterReject : function(record) {
        this.fireEvent('update', this, record, Ext.data.Model.REJECT);
    },

    
    afterCommit : function(record) {
        this.fireEvent('update', this, record, Ext.data.Model.COMMIT);
    },

    clearData: Ext.emptyFn,

    destroyStore: function() {
        var me = this;
        
        if (!me.isDestroyed) {
            if (me.storeId) {
                Ext.data.StoreManager.unregister(me);
            }
            me.clearData();
            me.data = null;
            me.tree = null;
            
            me.reader = me.writer = null;
            me.clearListeners();
            me.isDestroyed = true;

            if (me.implicitModel) {
                Ext.destroy(me.model);
            }
        }
    },
    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.data.sortBy(sorterFn);
            me.fireEvent('datachanged', me);
        }
    },

    getCount: Ext.emptyFn,

    getById: Ext.emptyFn,
    
    
    removeAll: Ext.emptyFn,
    
    

    
    isLoading: function() {
        return this.loading;
     }
});


 
Ext.define('Ext.util.Grouper', {

    

    extend: 'Ext.util.Sorter',

    

    
    getGroupString: function(instance) {
        return instance.get(this.property);
    }
});

Ext.define('Ext.data.Store', {
    extend: 'Ext.data.AbstractStore',

    alias: 'store.store',

    requires: ['Ext.ModelManager', 'Ext.data.Model', 'Ext.util.Grouper'],
    uses: ['Ext.data.proxy.Memory'],

    
    remoteSort: false,

    
    remoteFilter: false,
    
    
    remoteGroup : false,

    

    

    

    
    groupField: undefined,

    
    groupDir: "ASC",

    
    pageSize: 25,

    
    currentPage: 1,

    
    clearOnPageLoad: true,

    
    loading: false,

    
    sortOnFilter: true,
    
    
    buffered: false,
    
    
    purgePageCount: 5,

    isStore: true,

    
    constructor: function(config) {
        config = config || {};

        var me = this,
            groupers = config.groupers || me.groupers,
            groupField = config.groupField || me.groupField,
            proxy,
            data;
            
        if (config.buffered || me.buffered) {
            me.prefetchData = Ext.create('Ext.util.MixedCollection', false, function(record) {
                return record.index;
            });
            me.pendingRequests = [];
            me.pagesRequested = [];
            
            me.sortOnLoad = false;
            me.filterOnLoad = false;
        }
            
        me.addEvents(
            
            'beforeprefetch',
            
            'groupchange',
            
            'prefetch'
        );
        data = config.data || me.data;

        
        me.data = Ext.create('Ext.util.MixedCollection', false, function(record) {
            return record.internalId;
        });

        if (data) {
            me.inlineData = data;
            delete config.data;
        }
        
        if (!groupers && groupField) {
            groupers = [{
                property : groupField,
                direction: config.groupDir || me.groupDir
            }];
        }
        delete config.groupers;
        
        
        me.groupers = Ext.create('Ext.util.MixedCollection');
        me.groupers.addAll(me.decodeGroupers(groupers));

        this.callParent([config]);
        
        
        if (me.groupers.items.length) {
            me.sort(me.groupers.items, 'prepend', false);
        }

        proxy = me.proxy;
        data = me.inlineData;

        if (data) {
            if (proxy instanceof Ext.data.proxy.Memory) {
                proxy.data = data;
                me.read();
            } else {
                me.add.apply(me, data);
            }

            me.sort();
            delete me.inlineData;
        } else if (me.autoLoad) {
            Ext.defer(me.load, 10, me, [typeof me.autoLoad === 'object' ? me.autoLoad: undefined]);
            
            
        }
    },
    
    onBeforeSort: function() {
        this.sort(this.groupers.items, 'prepend', false);
    },
    
    
    decodeGroupers: function(groupers) {
        if (!Ext.isArray(groupers)) {
            if (groupers === undefined) {
                groupers = [];
            } else {
                groupers = [groupers];
            }
        }

        var length  = groupers.length,
            Grouper = Ext.util.Grouper,
            config, i;

        for (i = 0; i < length; i++) {
            config = groupers[i];

            if (!(config instanceof Grouper)) {
                if (Ext.isString(config)) {
                    config = {
                        property: config
                    };
                }
                
                Ext.applyIf(config, {
                    root     : 'data',
                    direction: "ASC"
                });

                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                groupers[i] = new Grouper(config);
            }
        }

        return groupers;
    },
    
    
    group: function(groupers, direction) {
        var me = this,
            grouper,
            newGroupers;
            
        if (Ext.isArray(groupers)) {
            newGroupers = groupers;
        } else if (Ext.isObject(groupers)) {
            newGroupers = [groupers];
        } else if (Ext.isString(groupers)) {
            grouper = me.groupers.get(groupers);

            if (!grouper) {
                grouper = {
                    property : groupers,
                    direction: direction
                };
                newGroupers = [grouper];
            } else if (direction === undefined) {
                grouper.toggle();
            } else {
                grouper.setDirection(direction);
            }
        }
        
        if (newGroupers && newGroupers.length) {
            newGroupers = me.decodeGroupers(newGroupers);
            me.groupers.clear();
            me.groupers.addAll(newGroupers);
        }
        
        if (me.remoteGroup) {
            me.load({
                scope: me,
                callback: me.fireGroupChange
            });
        } else {
            me.sort();
            me.fireEvent('groupchange', me, me.groupers);
        }
    },
    
    
    clearGrouping: function(){
        var me = this;
        
        me.groupers.each(function(grouper){
            me.sorters.remove(grouper);
        });
        me.groupers.clear();
        if (me.remoteGroup) {
            me.load({
                scope: me,
                callback: me.fireGroupChange
            });
        } else {
            me.sort();
            me.fireEvent('groupchange', me, me.groupers);
        }
    },
    
    
    isGrouped: function() {
        return this.groupers.getCount() > 0;    
    },
    
    
    fireGroupChange: function(){
        this.fireEvent('groupchange', this, this.groupers);    
    },

    
    getGroups: function(requestGroupString) {
        var records = this.data.items,
            length = records.length,
            groups = [],
            pointers = {},
            record,
            groupStr,
            group,
            i;

        for (i = 0; i < length; i++) {
            record = records[i];
            groupStr = this.getGroupString(record);
            group = pointers[groupStr];

            if (group === undefined) {
                group = {
                    name: groupStr,
                    children: []
                };

                groups.push(group);
                pointers[groupStr] = group;
            }

            group.children.push(record);
        }

        return requestGroupString ? pointers[requestGroupString] : groups;
    },

    
    getGroupsForGrouper: function(records, grouper) {
        var length = records.length,
            groups = [],
            oldValue,
            newValue,
            record,
            group,
            i;

        for (i = 0; i < length; i++) {
            record = records[i];
            newValue = grouper.getGroupString(record);

            if (newValue !== oldValue) {
                group = {
                    name: newValue,
                    grouper: grouper,
                    records: []
                };
                groups.push(group);
            }

            group.records.push(record);

            oldValue = newValue;
        }

        return groups;
    },

    
    getGroupsForGrouperIndex: function(records, grouperIndex) {
        var me = this,
            groupers = me.groupers,
            grouper = groupers.getAt(grouperIndex),
            groups = me.getGroupsForGrouper(records, grouper),
            length = groups.length,
            i;

        if (grouperIndex + 1 < groupers.length) {
            for (i = 0; i < length; i++) {
                groups[i].children = me.getGroupsForGrouperIndex(groups[i].records, grouperIndex + 1);
            }
        }

        for (i = 0; i < length; i++) {
            groups[i].depth = grouperIndex;
        }

        return groups;
    },

    
    getGroupData: function(sort) {
        var me = this;
        if (sort !== false) {
            me.sort();
        }

        return me.getGroupsForGrouperIndex(me.data.items, 0);
    },

    
    getGroupString: function(instance) {
        var group = this.groupers.first();
        if (group) {
            return instance.get(group.property);
        }
        return '';
    },
    
    insert: function(index, records) {
        var me = this,
            sync = false,
            i,
            record,
            len;

        records = [].concat(records);
        for (i = 0, len = records.length; i < len; i++) {
            record = me.createModel(records[i]);
            record.set(me.modelDefaults);
            
            records[i] = record;
            
            me.data.insert(index + i, record);
            record.join(me);

            sync = sync || record.phantom === true;
        }

        if (me.snapshot) {
            me.snapshot.addAll(records);
        }

        me.fireEvent('add', me, records, index);
        me.fireEvent('datachanged', me);
        if (me.autoSync && sync) {
            me.sync();
        }
    },

    
    add: function(records) {
        
        if (!Ext.isArray(records)) {
            records = Array.prototype.slice.apply(arguments);
        }

        var me = this,
            i = 0,
            length = records.length,
            record;

        for (; i < length; i++) {
            record = me.createModel(records[i]);
            
            records[i] = record;
        }

        me.insert(me.data.length, records);

        return records;
    },

    
    createModel: function(record) {
        if (!record.isModel) {
            record = Ext.ModelManager.create(record, this.model);
        }

        return record;
    },

    
    each: function(fn, scope) {
        this.data.each(fn, scope);
    },

    
    remove: function(records,  isMove) {
        if (!Ext.isArray(records)) {
            records = [records];
        }

        
        isMove = isMove === true;
        var me = this,
            sync = false,
            i = 0,
            length = records.length,
            isPhantom,
            index,
            record;

        for (; i < length; i++) {
            record = records[i];
            index = me.data.indexOf(record);
            
            if (me.snapshot) {
                me.snapshot.remove(record);
            }
            
            if (index > -1) {
                isPhantom = record.phantom === true;
                if (!isMove && !isPhantom) {
                    
                    me.removed.push(record);
                }

                record.unjoin(me);
                me.data.remove(record);
                sync = sync || !isPhantom;

                me.fireEvent('remove', me, record, index);
            }
        }

        me.fireEvent('datachanged', me);
        if (!isMove && me.autoSync && sync) {
            me.sync();
        }
    },

    
    removeAt: function(index) {
        var record = this.getAt(index);

        if (record) {
            this.remove(record);
        }
    },

    
    load: function(options) {
        var me = this;
            
        options = options || {};

        if (Ext.isFunction(options)) {
            options = {
                callback: options
            };
        }

        Ext.applyIf(options, {
            groupers: me.groupers.items,
            page: me.currentPage,
            start: (me.currentPage - 1) * me.pageSize,
            limit: me.pageSize,
            addRecords: false
        });      

        return me.callParent([options]);
    },

    
    onProxyLoad: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful();

        if (resultSet) {
            me.totalCount = resultSet.total;
        }

        if (successful) {
            me.loadRecords(records, operation);
        }

        me.loading = false;
        me.fireEvent('load', me, records, successful);

        
        
        me.fireEvent('read', me, records, operation.wasSuccessful());

        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },
    
    
    onCreateRecords: function(records, operation, success) {
        if (success) {
            var i = 0,
                data = this.data,
                snapshot = this.snapshot,
                length = records.length,
                originalRecords = operation.records,
                record,
                original,
                index;

            
            for (; i < length; ++i) {
                record = records[i];
                original = originalRecords[i];
                if (original) {
                    index = data.indexOf(original);
                    if (index > -1) {
                        data.removeAt(index);
                        data.insert(index, record);
                    }
                    if (snapshot) {
                        index = snapshot.indexOf(original);
                        if (index > -1) {
                            snapshot.removeAt(index);
                            snapshot.insert(index, record);
                        }
                    }
                    record.phantom = false;
                    record.join(this);
                }
            }
        }
    },

    
    onUpdateRecords: function(records, operation, success){
        if (success) {
            var i = 0,
                length = records.length,
                data = this.data,
                snapshot = this.snapshot,
                record;

            for (; i < length; ++i) {
                record = records[i];
                data.replace(record);
                if (snapshot) {
                    snapshot.replace(record);
                }
                record.join(this);
            }
        }
    },

    
    onDestroyRecords: function(records, operation, success){
        if (success) {
            var me = this,
                i = 0,
                length = records.length,
                data = me.data,
                snapshot = me.snapshot,
                record;

            for (; i < length; ++i) {
                record = records[i];
                record.unjoin(me);
                data.remove(record);
                if (snapshot) {
                    snapshot.remove(record);
                }
            }
            me.removed = [];
        }
    },

    
    getNewRecords: function() {
        return this.data.filterBy(this.filterNew).items;
    },

    
    getUpdatedRecords: function() {
        return this.data.filterBy(this.filterUpdated).items;
    },

    
    filter: function(filters, value) {
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value: value
            };
        }

        var me = this,
            decoded = me.decodeFilters(filters),
            i = 0,
            doLocalSort = me.sortOnFilter && !me.remoteSort,
            length = decoded.length;

        for (; i < length; i++) {
            me.filters.replace(decoded[i]);
        }

        if (me.remoteFilter) {
            
            me.load();
        } else {
            
            if (me.filters.getCount()) {
                me.snapshot = me.snapshot || me.data.clone();
                me.data = me.data.filter(me.filters.items);

                if (doLocalSort) {
                    me.sort();
                }
                
                if (!doLocalSort || me.sorters.length < 1) {
                    me.fireEvent('datachanged', me);
                }
            }
        }
    },

    
    clearFilter: function(suppressEvent) {
        var me = this;

        me.filters.clear();

        if (me.remoteFilter) {
            me.load();
        } else if (me.isFiltered()) {
            me.data = me.snapshot.clone();
            delete me.snapshot;

            if (suppressEvent !== true) {
                me.fireEvent('datachanged', me);
            }
        }
    },

    
    isFiltered: function() {
        var snapshot = this.snapshot;
        return !! snapshot && snapshot !== this.data;
    },

    
    filterBy: function(fn, scope) {
        var me = this;

        me.snapshot = me.snapshot || me.data.clone();
        me.data = me.queryBy(fn, scope || me);
        me.fireEvent('datachanged', me);
    },

    
    queryBy: function(fn, scope) {
        var me = this,
        data = me.snapshot || me.data;
        return data.filterBy(fn, scope || me);
    },

    
    loadData: function(data, append) {
        var model = this.model,
            length = data.length,
            i,
            record;

        
        for (i = 0; i < length; i++) {
            record = data[i];

            if (! (record instanceof Ext.data.Model)) {
                data[i] = Ext.ModelManager.create(record, model);
            }
        }

        this.loadRecords(data, {addRecords: append});
    },

    
    loadRecords: function(records, options) {
        var me     = this,
            i      = 0,
            length = records.length;

        options = options || {};


        if (!options.addRecords) {
            delete me.snapshot;
            me.data.clear();
        }

        me.data.addAll(records);

        
        for (; i < length; i++) {
            if (options.start !== undefined) {
                records[i].index = options.start + i;

            }
            records[i].join(me);
        }

        
        me.suspendEvents();

        if (me.filterOnLoad && !me.remoteFilter) {
            me.filter();
        }

        if (me.sortOnLoad && !me.remoteSort) {
            me.sort();
        }

        me.resumeEvents();
        me.fireEvent('datachanged', me, records);
    },

    
    
    loadPage: function(page) {
        var me = this;

        me.currentPage = page;

        me.read({
            page: page,
            start: (page - 1) * me.pageSize,
            limit: me.pageSize,
            addRecords: !me.clearOnPageLoad
        });
    },

    
    nextPage: function() {
        this.loadPage(this.currentPage + 1);
    },

    
    previousPage: function() {
        this.loadPage(this.currentPage - 1);
    },

    
    clearData: function() {
        this.data.each(function(record) {
            record.unjoin();
        });

        this.data.clear();
    },
    
    
    
    prefetch: function(options) {
        var me = this,
            operation,
            requestId = me.getRequestId();

        options = options || {};

        Ext.applyIf(options, {
            action : 'read',
            filters: me.filters.items,
            sorters: me.sorters.items,
            requestId: requestId
        });
        me.pendingRequests.push(requestId);

        operation = Ext.create('Ext.data.Operation', options);

        
        
        
        
        if (me.fireEvent('beforeprefetch', me, operation) !== false) {
            me.loading = true;
            me.proxy.read(operation, me.onProxyPrefetch, me);
        }
        
        return me;
    },
    
    
    prefetchPage: function(page, options) {
        var me = this,
            pageSize = me.pageSize,
            start = (page - 1) * me.pageSize,
            end = start + pageSize;
        
        
        if (Ext.Array.indexOf(me.pagesRequested, page) === -1 && !me.rangeSatisfied(start, end)) {
            options = options || {};
            me.pagesRequested.push(page);
            Ext.applyIf(options, {
                page : page,
                start: start,
                limit: pageSize,
                callback: me.onWaitForGuarantee,
                scope: me
            });
            
            me.prefetch(options);
        }
        
    },
    
    
    getRequestId: function() {
        this.requestSeed = this.requestSeed || 1;
        return this.requestSeed++;
    },
    
    
    onProxyPrefetch: function(operation) {
        var me         = this,
            resultSet  = operation.getResultSet(),
            records    = operation.getRecords(),
            
            successful = operation.wasSuccessful();
        
        if (resultSet) {
            me.totalCount = resultSet.total;
            me.fireEvent('totalcountchange', me.totalCount);
        }
        
        if (successful) {
            me.cacheRecords(records, operation);
        }
        Ext.Array.remove(me.pendingRequests, operation.requestId);
        if (operation.page) {
            Ext.Array.remove(me.pagesRequested, operation.page);
        }
        
        me.loading = false;
        me.fireEvent('prefetch', me, records, successful, operation);
        
        
        if (operation.blocking) {
            me.fireEvent('load', me, records, successful);
        }

        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },
    
    
    cacheRecords: function(records, operation) {
        var me     = this,
            i      = 0,
            length = records.length,
            start  = operation ? operation.start : 0;
        
        if (!Ext.isDefined(me.totalCount)) {
            me.totalCount = records.length;
            me.fireEvent('totalcountchange', me.totalCount);
        }
        
        for (; i < length; i++) {
            
            records[i].index = start + i;
        }
        
        me.prefetchData.addAll(records);
        if (me.purgePageCount) {
            me.purgeRecords();
        }
        
    },
    
    
    
    purgeRecords: function() {
        var me = this,
            prefetchCount = me.prefetchData.getCount(),
            purgeCount = me.purgePageCount * me.pageSize,
            numRecordsToPurge = prefetchCount - purgeCount - 1,
            i = 0;

        for (; i <= numRecordsToPurge; i++) {
            me.prefetchData.removeAt(0);
        }
    },
    
    
    rangeSatisfied: function(start, end) {
        var me = this,
            i = start,
            satisfied = true;

        for (; i < end; i++) {
            if (!me.prefetchData.getByKey(i)) {
                satisfied = false;
                if (end - i > me.pageSize) {
                    Ext.Error.raise("A single page prefetch could never satisfy this request.");
                }
                break;
            }
        }
        return satisfied;
    },
    
    
    getPageFromRecordIndex: function(index) {
        return Math.floor(index / this.pageSize) + 1;
    },
    
    
    onGuaranteedRange: function() {
        var me = this,
            totalCount = me.getTotalCount(),
            start = me.requestStart,
            end = ((totalCount - 1) < me.requestEnd) ? totalCount - 1 : me.requestEnd,
            range = [],
            record,
            i = start;
            
        if (start > end) {
            Ext.Error.raise("Start (" + start + ") was greater than end (" + end + ")");
        }
        
        if (start !== me.guaranteedStart && end !== me.guaranteedEnd) {
            me.guaranteedStart = start;
            me.guaranteedEnd = end;
            
            for (; i <= end; i++) {
                record = me.prefetchData.getByKey(i);
                if (!record) {
                    Ext.Error.raise("Record was not found and store said it was guaranteed");
                }
                range.push(record);
            }
            me.fireEvent('guaranteedrange', range, start, end);
            if (me.cb) {
                me.cb.call(me.scope || me, range);
            }
        }
        
        me.unmask();
    },
    
    
    mask: function() {
        this.masked = true;
        this.fireEvent('beforeload');
    },
    
    
    unmask: function() {
        if (this.masked) {
            this.fireEvent('load');
        }
    },
    
    
    hasPendingRequests: function() {
        return this.pendingRequests.length;
    },
    
    
    
    onWaitForGuarantee: function() {
        if (!this.hasPendingRequests()) {
            this.onGuaranteedRange();
        }
    },
    
    
    guaranteeRange: function(start, end, cb, scope) {
        if (start && end) {
            if (end - start > this.pageSize) {
                Ext.Error.raise({
                    start: start,
                    end: end,
                    pageSize: this.pageSize,
                    msg: "Requested a bigger range than the specified pageSize"
                });
            }
        }
        
        end = (end > this.totalCount) ? this.totalCount - 1 : end;
        
        var me = this,
            i = start,
            prefetchData = me.prefetchData,
            range = [],
            startLoaded = !!prefetchData.getByKey(start),
            endLoaded = !!prefetchData.getByKey(end),
            startPage = me.getPageFromRecordIndex(start),
            endPage = me.getPageFromRecordIndex(end);
            
        me.cb = cb;
        me.scope = scope;

        me.requestStart = start;
        me.requestEnd = end;
        
        if (!startLoaded || !endLoaded) {
            
            if (startPage === endPage) {
                me.mask();
                me.prefetchPage(startPage, {
                    
                    callback: me.onWaitForGuarantee,
                    scope: me
                });
            
            } else {
                me.mask();
                me.prefetchPage(startPage, {
                    
                    callback: me.onWaitForGuarantee,
                    scope: me
                });
                me.prefetchPage(endPage, {
                    
                    callback: me.onWaitForGuarantee,
                    scope: me
                });
            }
        
        } else {
            me.onGuaranteedRange();
        }
    },
    
    
    
    sort: function() {
        var me = this,
            prefetchData = me.prefetchData,
            sorters,
            start,
            end,
            range;
            
        if (me.buffered) {
            if (me.remoteSort) {
                prefetchData.clear();
                me.callParent(arguments);
            } else {
                sorters = me.getSorters();
                start = me.guaranteedStart;
                end = me.guaranteedEnd;
                
                if (sorters.length) {
                    prefetchData.sort(sorters);
                    range = prefetchData.getRange();
                    prefetchData.clear();
                    me.cacheRecords(range);
                    delete me.guaranteedStart;
                    delete me.guaranteedEnd;
                    me.guaranteeRange(start, end);
                }
                me.callParent(arguments);
            }
        } else {
            me.callParent(arguments);
        }
    },

    
    
    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.data.sortBy(sorterFn);
            if (!me.buffered) {
                var range = me.getRange(),
                    ln = range.length,
                    i  = 0;
                for (; i < ln; i++) {
                    range[i].index = i;
                }
            }
            me.fireEvent('datachanged', me);
        }
    },
    
    
    find: function(property, value, start, anyMatch, caseSensitive, exactMatch) {
        var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
        return fn ? this.data.findIndexBy(fn, null, start) : -1;
    },

    
    findRecord: function() {
        var me = this,
            index = me.find.apply(me, arguments);
        return index !== -1 ? me.getAt(index) : null;
    },

    
    createFilterFn: function(property, value, anyMatch, caseSensitive, exactMatch) {
        if (Ext.isEmpty(value)) {
            return false;
        }
        value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
        return function(r) {
            return value.test(r.data[property]);
        };
    },

    
    findExact: function(property, value, start) {
        return this.data.findIndexBy(function(rec) {
            return rec.get(property) === value;
        },
        this, start);
    },

    
    findBy: function(fn, scope, start) {
        return this.data.findIndexBy(fn, scope, start);
    },

    
    collect: function(dataIndex, allowNull, bypassFilter) {
        var me = this,
            data = (bypassFilter === true && me.snapshot) ? me.snapshot: me.data;

        return data.collect(dataIndex, 'data', allowNull);
    },

    
    getCount: function() {
        return this.data.length || 0;
    },

    
    getTotalCount: function() {
        return this.totalCount;
    },

    
    getAt: function(index) {
        return this.data.getAt(index);
    },

    
    getRange: function(start, end) {
        return this.data.getRange(start, end);
    },

    
    getById: function(id) {
        return (this.snapshot || this.data).findBy(function(record) {
            return record.getId() === id;
        });
    },

    
    indexOf: function(record) {
        return this.data.indexOf(record);
    },


    
    indexOfTotal: function(record) {
        return record.index || this.indexOf(record);
    },

    
    indexOfId: function(id) {
        return this.data.indexOfKey(id);
    },
        
    
    removeAll: function(silent) {
        var me = this;

        me.clearData();
        if (me.snapshot) {
            me.snapshot.clear();
        }
        if (silent !== true) {
            me.fireEvent('clear', me);
        }
    },

    

    
    first: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                return records.length ? records[0] : undefined;
            }, me, true);
        } else {
            return me.data.first();
        }
    },

    
    last: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                var len = records.length;
                return len ? records[len - 1] : undefined;
            }, me, true);
        } else {
            return me.data.last();
        }
    },

    
    sum: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getSum, me, true, [field]);
        } else {
            return me.getSum(me.data.items, field);
        }
    },

    
    getSum: function(records, field) {
        var total = 0,
            i = 0,
            len = records.length;

        for (; i < len; ++i) {
            total += records[i].get(field);
        }

        return total;
    },

    
    count: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                return records.length;
            }, me, true);
        } else {
            return me.getCount();
        }
    },

    
    min: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getMin, me, true, [field]);
        } else {
            return me.getMin(me.data.items, field);
        }
    },

    
    getMin: function(records, field){
        var i = 1,
            len = records.length,
            value, min;

        if (len > 0) {
            min = records[0].get(field);
        }

        for (; i < len; ++i) {
            value = records[i].get(field);
            if (value < min) {
                min = value;
            }
        }
        return min;
    },

    
    max: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getMax, me, true, [field]);
        } else {
            return me.getMax(me.data.items, field);
        }
    },

    
    getMax: function(records, field) {
        var i = 1,
            len = records.length,
            value,
            max;

        if (len > 0) {
            max = records[0].get(field);
        }

        for (; i < len; ++i) {
            value = records[i].get(field);
            if (value > max) {
                max = value;
            }
        }
        return max;
    },

    
    average: function(field, grouped) {
        var me = this;
        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getAverage, me, true, [field]);
        } else {
            return me.getAverage(me.data.items, field);
        }
    },

    
    getAverage: function(records, field) {
        var i = 0,
            len = records.length,
            sum = 0;

        if (records.length > 0) {
            for (; i < len; ++i) {
                sum += records[i].get(field);
            }
            return sum / len;
        }
        return 0;
    },

    
    aggregate: function(fn, scope, grouped, args) {
        args = args || [];
        if (grouped && this.isGrouped()) {
            var groups = this.getGroups(),
                i = 0,
                len = groups.length,
                out = {},
                group;

            for (; i < len; ++i) {
                group = groups[i];
                out[group.name] = fn.apply(scope || this, [group.children].concat(args));
            }
            return out;
        } else {
            return fn.apply(scope || this, [this.data.items].concat(args));
        }
    }
});


Ext.define('Ext.data.JsonStore',  {
    extend: 'Ext.data.Store',
    alias: 'store.json',

    
    constructor: function(config) {
        config = config || {};

        Ext.applyIf(config, {
            proxy: {
                type  : 'ajax',
                reader: 'json',
                writer: 'json'
            }
        });

        this.callParent([config]);
    }
});


Ext.define('Ext.chart.axis.Time', {

    

    extend: 'Ext.chart.axis.Category',

    alternateClassName: 'Ext.chart.TimeAxis',

    alias: 'axis.time',

    requires: ['Ext.data.Store', 'Ext.data.JsonStore'],

    

     
    calculateByLabelSize: true,
    
     
    dateFormat: false,
    
     
    groupBy: 'year,month,day',
    
    
    aggregateOp: 'sum',
    
    
    fromDate: false,
    
    
    toDate: false,
    
    
    step: [Ext.Date.DAY, 1],
    
    
    constrain: false,
    
    
    dateMethods: {
        'year': function(date) {
            return date.getFullYear();
        },
        'month': function(date) {
            return date.getMonth() + 1;
        },
        'day': function(date) {
            return date.getDate();
        },
        'hour': function(date) {
            return date.getHours();
        },
        'minute': function(date) {
            return date.getMinutes();
        },
        'second': function(date) {
            return date.getSeconds();
        },
        'millisecond': function(date) {
            return date.getMilliseconds();
        }
    },
    
    
    aggregateFn: (function() {
        var etype = (function() {
            var rgxp = /^\[object\s(.*)\]$/,
                toString = Object.prototype.toString;
            return function(e) {
                return toString.call(e).match(rgxp)[1];
            };
        })();
        return {
            'sum': function(list) {
                var i = 0, l = list.length, acum = 0;
                if (!list.length || etype(list[0]) != 'Number') {
                    return list[0];
                }
                for (; i < l; i++) {
                    acum += list[i];
                }
                return acum;
            },
            'max': function(list) {
                if (!list.length || etype(list[0]) != 'Number') {
                    return list[0];
                }
                return Math.max.apply(Math, list);
            },
            'min': function(list) {
                if (!list.length || etype(list[0]) != 'Number') {
                    return list[0];
                }
                return Math.min.apply(Math, list);
            },
            'avg': function(list) {
                var i = 0, l = list.length, acum = 0;
                if (!list.length || etype(list[0]) != 'Number') {
                    return list[0];
                }
                for (; i < l; i++) {
                    acum += list[i];
                }
                return acum / l;
            }
        };
    })(),
    
    
    constrainDates: function() {
        var fromDate = Ext.Date.clone(this.fromDate),
            toDate = Ext.Date.clone(this.toDate),
            step = this.step,
            field = this.fields,
            store = this.chart.store,
            record, recObj, fieldNames = [],
            newStore = Ext.create('Ext.data.Store', {
                model: store.model
            });
        
        var getRecordByDate = (function() {
            var index = 0, l = store.getCount();
            return function(date) {
                var rec, recDate;
                for (; index < l; index++) {
                    rec = store.getAt(index);
                    recDate = rec.get(field);
                    if (+recDate > +date) {
                        return false;
                    } else if (+recDate == +date) {
                        return rec;
                    }
                }
                return false;
            };
        })();
        
        if (!this.constrain) {
            this.chart.filteredStore = this.chart.store;
            return;
        }

        while(+fromDate <= +toDate) {
            record = getRecordByDate(fromDate);
            recObj = {};
            if (record) {
                newStore.add(record.data);
            } else {
                newStore.model.prototype.fields.each(function(f) {
                    recObj[f.name] = false;
                });
                recObj.date = fromDate;
                newStore.add(recObj);
            }
            fromDate = Ext.Date.add(fromDate, step[0], step[1]);
        }
         
        this.chart.filteredStore = newStore;
    },
    
    
    aggregate: function() {
        var aggStore = {}, 
            aggKeys = [], key, value,
            op = this.aggregateOp,
            field = this.fields, i,
            fields = this.groupBy.split(','),
            curField,
            recFields = [],
            recFieldsLen = 0,
            obj,
            dates = [],
            json = [],
            l = fields.length,
            dateMethods = this.dateMethods,
            aggregateFn = this.aggregateFn,
            store = this.chart.filteredStore || this.chart.store;
        
        store.each(function(rec) {
            
            if (!recFields.length) {
                rec.fields.each(function(f) {
                    recFields.push(f.name);
                });
                recFieldsLen = recFields.length;
            }
            
            value = rec.get(field);
            
            for (i = 0; i < l; i++) {
                if (i == 0) {
                    key = String(dateMethods[fields[i]](value));
                } else {
                    key += '||' + dateMethods[fields[i]](value);
                }
            }
            
            if (key in aggStore) {
                obj = aggStore[key];
            } else {
                obj = aggStore[key] = {};
                aggKeys.push(key);
                dates.push(value);
            }
            
            for (i = 0; i < recFieldsLen; i++) {
                curField = recFields[i];
                if (!obj[curField]) {
                    obj[curField] = [];
                }
                if (rec.get(curField) !== undefined) {
                    obj[curField].push(rec.get(curField));
                }
            }
        });
        
        for (key in aggStore) {
            obj = aggStore[key];
            for (i = 0; i < recFieldsLen; i++) {
                curField = recFields[i];
                obj[curField] = aggregateFn[op](obj[curField]);
            }
            json.push(obj);
        }
        this.chart.substore = Ext.create('Ext.data.JsonStore', {
            fields: recFields,
            data: json
        });
        
        this.dates = dates;
    },
    
    
     setLabels: function() {
        var store = this.chart.substore,
            fields = this.fields,
            format = this.dateFormat,
            labels, i, dates = this.dates,
            formatFn = Ext.Date.format;
        this.labels = labels = [];
        store.each(function(record, i) {
            if (!format) {
                labels.push(record.get(fields));
            } else {
                labels.push(formatFn(dates[i], format));
            }
         }, this);
     },

    processView: function() {
         
         if (this.constrain) {
             this.constrainDates();
             this.aggregate();
             this.chart.substore = this.chart.filteredStore;
         } else {
             this.aggregate();
         }
    },

     
     applyData: function() {
        this.setLabels();
        var count = this.chart.substore.getCount();
         return {
             from: 0,
             to: count,
             steps: count - 1,
             step: 1
         };
     }
 });



Ext.define('Ext.chart.series.Series', {

    

    mixins: {
        observable: 'Ext.util.Observable',
        labels: 'Ext.chart.Label',
        highlights: 'Ext.chart.Highlight',
        tips: 'Ext.chart.Tip',
        callouts: 'Ext.chart.Callout'
    },

    

    

    

    
    type: null,

    
    title: null,

    
    showInLegend: true,

    
    renderer: function(sprite, record, attributes, index, store) {
        return attributes;
    },

    
    shadowAttributes: null,
    
    //@private triggerdrawlistener flag

    triggerAfterDraw: false,

    
    
    constructor: function(config) {
        var me = this;
        if (config) {
            Ext.apply(me, config);
        }
        
        me.shadowGroups = [];
        
        me.mixins.labels.constructor.call(me, config);
        me.mixins.highlights.constructor.call(me, config);
        me.mixins.tips.constructor.call(me, config);
        me.mixins.callouts.constructor.call(me, config);

        me.addEvents({
            scope: me,
            itemmouseover: true,
            itemmouseout: true,
            itemmousedown: true,
            itemmouseup: true,
            mouseleave: true,
            afterdraw: true,

            
            titlechange: true
        });

        me.mixins.observable.constructor.call(me, config);

        me.on({
            scope: me,
            itemmouseover: me.onItemMouseOver,
            itemmouseout: me.onItemMouseOut,
            mouseleave: me.onMouseLeave
        });
    },

    
    setBBox: function(noGutter) {
        var me = this,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            gutterX = noGutter ? 0 : chart.maxGutter[0],
            gutterY = noGutter ? 0 : chart.maxGutter[1],
            clipBox, bbox;

        clipBox = {
            x: chartBBox.x,
            y: chartBBox.y,
            width: chartBBox.width,
            height: chartBBox.height
        };
        me.clipBox = clipBox;

        bbox = {
            x: (clipBox.x + gutterX) - (chart.zoom.x * chart.zoom.width),
            y: (clipBox.y + gutterY) - (chart.zoom.y * chart.zoom.height),
            width: (clipBox.width - (gutterX * 2)) * chart.zoom.width,
            height: (clipBox.height - (gutterY * 2)) * chart.zoom.height
        };
        me.bbox = bbox;
    },

    
    onAnimate: function(sprite, attr) {
        var me = this;
        sprite.stopAnimation();
        if (me.triggerAfterDraw) {
            return sprite.animate(Ext.applyIf(attr, me.chart.animate));
        } else {
            me.triggerAfterDraw = true;
            return sprite.animate(Ext.apply(Ext.applyIf(attr, me.chart.animate), {
                listeners: {
                    'afteranimate': function() {
                        me.triggerAfterDraw = false;
                        me.fireEvent('afterrender');
                    }    
                }    
            }));
        }
    },
    
    
    getGutters: function() {
        return [0, 0];
    },

    
    onItemMouseOver: function(item) { 
        var me = this;
        if (item.series === me) {
            if (me.highlight) {
                me.highlightItem(item);
            }
            if (me.tooltip) {
                me.showTip(item);
            }
        }
    },

    
    onItemMouseOut: function(item) {
        var me = this;
        if (item.series === me) {
            me.unHighlightItem();
            if (me.tooltip) {
                me.hideTip(item);
            }
        }
    },

    
    onMouseLeave: function() {
        var me = this;
        me.unHighlightItem();
        if (me.tooltip) {
            me.hideTip();
        }
    },

    
    getItemForPoint: function(x, y) {
        
        if (!this.items || !this.items.length || this.seriesIsHidden) {
            return null;
        }
        var me = this,
            items = me.items,
            bbox = me.bbox,
            item, i, ln;
        
        if (!Ext.draw.Draw.withinBox(x, y, bbox)) {
            return null;
        }
        for (i = 0, ln = items.length; i < ln; i++) {
            if (items[i] && this.isItemInPoint(x, y, items[i], i)) {
                return items[i];
            }
        }
        
        return null;
    },
    
    isItemInPoint: function(x, y, item, i) {
        return false;
    },

    
    hideAll: function() {
        var me = this,
            items = me.items,
            item, len, i, sprite;

        me.seriesIsHidden = true;
        me._prevShowMarkers = me.showMarkers;

        me.showMarkers = false;
        
        me.hideLabels(0);
        
        for (i = 0, len = items.length; i < len; i++) {
            item = items[i];
            sprite = item.sprite;
            if (sprite) {
                sprite.setAttributes({
                    hidden: true
                }, true);
            }
        }
    },

    
    showAll: function() {
        var me = this,
            prevAnimate = me.chart.animate;
        me.chart.animate = false;
        me.seriesIsHidden = false;
        me.showMarkers = me._prevShowMarkers;
        me.drawSeries();
        me.chart.animate = prevAnimate;
    },
    
    
    getLegendColor: function(index) {
        var me = this, fill, stroke;
        if (me.seriesStyle) {
            fill = me.seriesStyle.fill;
            stroke = me.seriesStyle.stroke;
            if (fill && fill != 'none') {
                return fill;
            }
            return stroke;
        }
        return '#000';
    },
    
    
    visibleInLegend: function(index){
        var excludes = this.__excludes;
        if (excludes) {
            return !excludes[index];
        }
        return !this.seriesIsHidden;
    },

    
    setTitle: function(index, title) {
        var me = this,
            oldTitle = me.title;

        if (Ext.isString(index)) {
            title = index;
            index = 0;
        }

        if (Ext.isArray(oldTitle)) {
            oldTitle[index] = title;
        } else {
            me.title = title;
        }

        me.fireEvent('titlechange', title, index);
    }
});


Ext.define('Ext.chart.series.Cartesian', {

    

    extend: 'Ext.chart.series.Series',

    alternateClassName: ['Ext.chart.CartesianSeries', 'Ext.chart.CartesianChart'],

    

    
    xField: null,

    
    yField: null,

    
    axis: 'left'
});


Ext.define('Ext.chart.series.Area', {

    

    extend: 'Ext.chart.series.Cartesian',
    
    alias: 'series.area',

    requires: ['Ext.chart.axis.Axis', 'Ext.draw.Color', 'Ext.fx.Anim'],

    

    type: 'area',

    
    stacked: true,

    
    style: {},

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            i, l;
        Ext.apply(me, config, {
            __excludes: [],
            highlightCfg: {
                lineWidth: 3,
                stroke: '#55c',
                opacity: 0.8,
                color: '#f00'
            }
        });
        if (me.highlight) {
            me.highlightSprite = surface.add({
                type: 'path',
                path: ['M', 0, 0],
                zIndex: 1000,
                opacity: 0.3,
                lineWidth: 5,
                hidden: true,
                stroke: '#444'
            });
        }
        me.group = surface.getGroup(me.seriesId);
    },

    
    shrink: function(xValues, yValues, size) {
        var len = xValues.length,
            ratio = Math.floor(len / size),
            i, j,
            xSum = 0,
            yCompLen = this.areas.length,
            ySum = [],
            xRes = [],
            yRes = [];
        
        for (j = 0; j < yCompLen; ++j) {
            ySum[j] = 0;
        }
        for (i = 0; i < len; ++i) {
            xSum += xValues[i];
            for (j = 0; j < yCompLen; ++j) {
                ySum[j] += yValues[i][j];
            }
            if (i % ratio == 0) {
                
                xRes.push(xSum/ratio);
                for (j = 0; j < yCompLen; ++j) {
                    ySum[j] /= ratio;
                }
                yRes.push(ySum);
                
                xSum = 0;
                for (j = 0, ySum = []; j < yCompLen; ++j) {
                    ySum[j] = 0;
                }
            }
        }
        return {
            x: xRes,
            y: yRes
        };
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            areas = [].concat(me.yField),
            areasLen = areas.length,
            xValues = [],
            yValues = [],
            infinity = Infinity,
            minX = infinity,
            minY = infinity,
            maxX = -infinity,
            maxY = -infinity,
            math = Math,
            mmin = math.min,
            mmax = math.max,
            bbox, xScale, yScale, xValue, yValue, areaIndex, acumY, ln, sumValues, clipBox, areaElem;

        me.setBBox();
        bbox = me.bbox;

        
        if (me.axis) {
            axis = chart.axes.get(me.axis);
            if (axis) {
                out = axis.calcEnds();
                minY = out.from || axis.prevMin;
                maxY = mmax(out.to || axis.prevMax, 0);
            }
        }

        if (me.yField && !Ext.isNumber(minY)) {
            axis = Ext.create('Ext.chart.axis.Axis', {
                chart: chart,
                fields: [].concat(me.yField)
            });
            out = axis.calcEnds();
            minY = out.from || axis.prevMin;
            maxY = mmax(out.to || axis.prevMax, 0);
        }

        if (!Ext.isNumber(minY)) {
            minY = 0;
        }
        if (!Ext.isNumber(maxY)) {
            maxY = 0;
        }

        store.each(function(record, i) {
            xValue = record.get(me.xField);
            yValue = [];
            if (typeof xValue != 'number') {
                xValue = i;
            }
            xValues.push(xValue);
            acumY = 0;
            for (areaIndex = 0; areaIndex < areasLen; areaIndex++) {
                areaElem = record.get(areas[areaIndex]);
                if (typeof areaElem == 'number') {
                    minY = mmin(minY, areaElem);
                    yValue.push(areaElem);
                    acumY += areaElem;
                }
            }
            minX = mmin(minX, xValue);
            maxX = mmax(maxX, xValue);
            maxY = mmax(maxY, acumY);
            yValues.push(yValue);
        }, me);

        xScale = bbox.width / (maxX - minX);
        yScale = bbox.height / (maxY - minY);

        ln = xValues.length;
        if ((ln > bbox.width) && me.areas) {
            sumValues = me.shrink(xValues, yValues, bbox.width);
            xValues = sumValues.x;
            yValues = sumValues.y;
        }

        return {
            bbox: bbox,
            minX: minX,
            minY: minY,
            xValues: xValues,
            yValues: yValues,
            xScale: xScale,
            yScale: yScale,
            areasLen: areasLen
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            first = true,
            bounds = me.getBounds(),
            bbox = bounds.bbox,
            items = me.items = [],
            componentPaths = [],
            componentPath,
            paths = [],
            i, ln, x, y, xValue, yValue, acumY, areaIndex, prevAreaIndex, areaElem, path;

        ln = bounds.xValues.length;
        
        for (i = 0; i < ln; i++) {
            xValue = bounds.xValues[i];
            yValue = bounds.yValues[i];
            x = bbox.x + (xValue - bounds.minX) * bounds.xScale;
            acumY = 0;
            for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) {
                
                if (me.__excludes[areaIndex]) {
                    continue;
                }
                if (!componentPaths[areaIndex]) {
                    componentPaths[areaIndex] = [];
                }
                areaElem = yValue[areaIndex];
                acumY += areaElem;
                y = bbox.y + bbox.height - (acumY - bounds.minY) * bounds.yScale;
                if (!paths[areaIndex]) {
                    paths[areaIndex] = ['M', x, y];
                    componentPaths[areaIndex].push(['L', x, y]);
                } else {
                    paths[areaIndex].push('L', x, y);
                    componentPaths[areaIndex].push(['L', x, y]);
                }
                if (!items[areaIndex]) {
                    items[areaIndex] = {
                        pointsUp: [],
                        pointsDown: [],
                        series: me
                    };
                }
                items[areaIndex].pointsUp.push([x, y]);
            }
        }
        
        
        for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) {
            
            if (me.__excludes[areaIndex]) {
                continue;
            }
            path = paths[areaIndex];
            
            if (areaIndex == 0 || first) {
                first = false;
                path.push('L', x, bbox.y + bbox.height,
                          'L', bbox.x, bbox.y + bbox.height,
                          'Z');
            }
            
            else {
                componentPath = componentPaths[prevAreaIndex];
                componentPath.reverse();
                path.push('L', x, componentPath[0][2]);
                for (i = 0; i < ln; i++) {
                    path.push(componentPath[i][0],
                              componentPath[i][1],
                              componentPath[i][2]);
                    items[areaIndex].pointsDown[ln -i -1] = [componentPath[i][1], componentPath[i][2]];
                }
                path.push('L', bbox.x, path[2], 'Z');
            }
            prevAreaIndex = areaIndex;
        }
        return {
            paths: paths,
            areasLen: bounds.areasLen
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            surface = chart.surface,
            animate = chart.animate,
            group = me.group,
            endLineStyle = Ext.apply(me.seriesStyle, me.style),
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            areaIndex, areaElem, paths, path, rendererAttributes;

        me.unHighlightItem();
        me.cleanHighlights();

        if (!store || !store.getCount()) {
            return;
        }
        
        paths = me.getPaths();

        if (!me.areas) {
            me.areas = [];
        }

        for (areaIndex = 0; areaIndex < paths.areasLen; areaIndex++) {
            
            if (me.__excludes[areaIndex]) {
                continue;
            }
            if (!me.areas[areaIndex]) {
                me.items[areaIndex].sprite = me.areas[areaIndex] = surface.add(Ext.apply({}, {
                    type: 'path',
                    group: group,
                    
                    path: paths.paths[areaIndex],
                    stroke: endLineStyle.stroke || colorArrayStyle[areaIndex % colorArrayLength],
                    fill: colorArrayStyle[areaIndex % colorArrayLength]
                }, endLineStyle || {}));
            }
            areaElem = me.areas[areaIndex];
            path = paths.paths[areaIndex];
            if (animate) {
                
                rendererAttributes = me.renderer(areaElem, false, { 
                    path: path,
                    
                    fill: colorArrayStyle[areaIndex % colorArrayLength],
                    stroke: endLineStyle.stroke || colorArrayStyle[areaIndex % colorArrayLength]
                }, areaIndex, store);
                
                me.animation = me.onAnimate(areaElem, {
                    to: rendererAttributes
                });
            } else {
                rendererAttributes = me.renderer(areaElem, false, { 
                    path: path,
                    
                    hidden: false,
                    fill: colorArrayStyle[areaIndex % colorArrayLength],
                    stroke: endLineStyle.stroke || colorArrayStyle[areaIndex % colorArrayLength]
                }, areaIndex, store);
                me.areas[areaIndex].setAttributes(rendererAttributes, true);
            }
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            bbox = me.bbox,
            endLabelStyle = Ext.apply(config, me.seriesLabelStyle);

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': item.point[0],
            'y': bbox.y + bbox.height / 2
        }, endLabelStyle || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = item.point[0],
            y = item.point[1],
            bb, width, height;
        
        label.setAttributes({
            text: format(storeItem.get(field[index])),
            hidden: true
        }, true);
        
        bb = label.getBBox();
        width = bb.width / 2;
        height = bb.height / 2;
        
        x = x - width < bbox.x? bbox.x + width : x;
        x = (x + width > bbox.x + bbox.width) ? (x - (x + width - bbox.x - bbox.width)) : x;
        y = y - height < bbox.y? bbox.y + height : y;
        y = (y + height > bbox.y + bbox.height) ? (y - (y + height - bbox.y - bbox.height)) : y;

        if (me.chart.animate && !me.chart.resizing) {
            label.show(true);
            me.onAnimate(label, {
                to: {
                    x: x,
                    y: y
                }
            });
        } else {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (resizing) {
                me.animation.on('afteranimate', function() {
                    label.show(true);
                });
            } else {
                label.show(true);
            }
        }
    },

    
    onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            prev = (i == 0) ? false : items[i -1].point,
            next = (i == items.length -1) ? false : items[i +1].point,
            cur = item.point,
            dir, norm, normal, a, aprev, anext,
            bbox = callout.label.getBBox(),
            offsetFromViz = 30,
            offsetToSide = 10,
            offsetBox = 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.clipRect,
            x, y;

        
        if (!prev) {
            prev = cur;
        }
        if (!next) {
            next = cur;
        }
        a = (next[1] - prev[1]) / (next[0] - prev[0]);
        aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
        anext = (next[1] - cur[1]) / (next[0] - cur[0]);
        
        norm = Math.sqrt(1 + a * a);
        dir = [1 / norm, a / norm];
        normal = [-dir[1], dir[0]];
        
        
        if (aprev > 0 && anext < 0 && normal[1] < 0 || aprev < 0 && anext > 0 && normal[1] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0 || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;
        
        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;
        
        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;
        
        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;
        
        
        callout.lines.setAttributes({
            path: ["M", cur[0], cur[1], "L", x, y, "Z"]
        }, true);
        
        callout.box.setAttributes({
            x: boxx,
            y: boxy,
            width: boxw,
            height: boxh
        }, true);
        
        callout.label.setAttributes({
            x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
            y: y
        }, true);
        for (p in callout) {
            callout[p].show(true);
        }
    },
    
    isItemInPoint: function(x, y, item, i) {
        var me = this,
            pointsUp = item.pointsUp,
            pointsDown = item.pointsDown,
            abs = Math.abs,
            dist = Infinity, p, pln, point;
        
        for (p = 0, pln = pointsUp.length; p < pln; p++) {
            point = [pointsUp[p][0], pointsUp[p][1]];
            if (dist > abs(x - point[0])) {
                dist = abs(x - point[0]);
            } else {
                point = pointsUp[p -1];
                if (y >= point[1] && (!pointsDown.length || y <= (pointsDown[p -1][1]))) {
                    item.storeIndex = p -1;
                    item.storeField = me.yField[i];
                    item.storeItem = me.chart.store.getAt(p -1);
                    item._points = pointsDown.length? [point, pointsDown[p -1]] : [point];
                    return true;
                } else {
                    break;
                }
            }
        }
        return false;
    },

    
    highlightSeries: function() {
        var area, to, fillColor;
        if (this._index !== undefined) {
            area = this.areas[this._index];
            if (area.__highlightAnim) {
                area.__highlightAnim.paused = true;
            }
            area.__highlighted = true;
            area.__prevOpacity = area.__prevOpacity || area.attr.opacity || 1;
            area.__prevFill = area.__prevFill || area.attr.fill;
            area.__prevLineWidth = area.__prevLineWidth || area.attr.lineWidth;
            fillColor = Ext.draw.Color.fromString(area.__prevFill);
            to = {
                lineWidth: (area.__prevLineWidth || 0) + 2
            };
            if (fillColor) {
                to.fill = fillColor.getLighter(0.2).toString();
            }
            else {
                to.opacity = Math.max(area.__prevOpacity - 0.3, 0);
            }
            if (this.chart.animate) {
                area.__highlightAnim = Ext.create('Ext.fx.Anim', Ext.apply({
                    target: area,
                    to: to
                }, this.chart.animate));
            }
            else {
                area.setAttributes(to, true);
            }
        }
    },

    
    unHighlightSeries: function() {
        var area;
        if (this._index !== undefined) {
            area = this.areas[this._index];
            if (area.__highlightAnim) {
                area.__highlightAnim.paused = true;
            }
            if (area.__highlighted) {
                area.__highlighted = false;
                area.__highlightAnim = Ext.create('Ext.fx.Anim', {
                    target: area,
                    to: {
                        fill: area.__prevFill,
                        opacity: area.__prevOpacity,
                        lineWidth: area.__prevLineWidth
                    }
                });
            }
        }
    },

    
    highlightItem: function(item) {
        var me = this,
            points, path;
        if (!item) {
            this.highlightSeries();
            return;
        }
        points = item._points;
        path = points.length == 2? ['M', points[0][0], points[0][1], 'L', points[1][0], points[1][1]]
                : ['M', points[0][0], points[0][1], 'L', points[0][0], me.bbox.y + me.bbox.height];
        me.highlightSprite.setAttributes({
            path: path,
            hidden: false
        }, true);
    },

    
    unHighlightItem: function(item) {
        if (!item) {
            this.unHighlightSeries();
        }

        if (this.highlightSprite) {
            this.highlightSprite.hide(true);
        }
    },

    
    hideAll: function() {
        if (!isNaN(this._index)) {
            this.__excludes[this._index] = true;
            this.areas[this._index].hide(true);
            this.drawSeries();
        }
    },

    
    showAll: function() {
        if (!isNaN(this._index)) {
            this.__excludes[this._index] = false;
            this.areas[this._index].show(true);
            this.drawSeries();
        }
    },

    
    getLegendColor: function(index) {
        var me = this;
        return me.colorArrayStyle[index % me.colorArrayStyle.length];
    }
});


Ext.define('Ext.chart.series.Bar', {

    

    extend: 'Ext.chart.series.Cartesian',

    alternateClassName: ['Ext.chart.BarSeries', 'Ext.chart.BarChart', 'Ext.chart.StackedBarChart'],

    requires: ['Ext.chart.axis.Axis', 'Ext.fx.Anim'],

    

    type: 'bar',

    alias: 'series.bar',
    
    column: false,
    
    
    style: {},
    
    
    gutter: 38.2,

    
    groupGutter: 38.2,

    
    xPadding: 0,

    
    yPadding: 10,

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            shadow = me.chart.shadow,
            i, l;
        Ext.apply(me, config, {
            highlightCfg: {
                lineWidth: 3,
                stroke: '#55c',
                opacity: 0.8,
                color: '#f00'
            },
            
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 1.2
                }
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 0.9
                }
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 0.6
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId + '-bars');
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },

    
    getBarGirth: function() {
        var me = this,
            store = me.chart.store,
            column = me.column,
            ln = store.getCount(),
            gutter = me.gutter / 100;
        
        return (me.chart.chartBBox[column ? 'width' : 'height'] - me[column ? 'xPadding' : 'yPadding'] * 2) / (ln * (gutter + 1) - gutter);
    },

    
    getGutters: function() {
        var me = this,
            column = me.column,
            gutter = Math.ceil(me[column ? 'xPadding' : 'yPadding'] + me.getBarGirth() / 2);
        return me.column ? [gutter, 0] : [0, gutter];
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            bars = [].concat(me.yField),
            barsLen = bars.length,
            groupBarsLen = barsLen,
            groupGutter = me.groupGutter / 100,
            column = me.column,
            xPadding = me.xPadding,
            yPadding = me.yPadding,
            stacked = me.stacked,
            barWidth = me.getBarGirth(),
            math = Math,
            mmax = math.max,
            mabs = math.abs,
            groupBarWidth, bbox, minY, maxY, axis, out,
            scale, zero, total, rec, j, plus, minus;

        me.setBBox(true);
        bbox = me.bbox;

        
        if (me.__excludes) {
            for (j = 0, total = me.__excludes.length; j < total; j++) {
                if (me.__excludes[j]) {
                    groupBarsLen--;
                }
            }
        }

        if (me.axis) {
            axis = chart.axes.get(me.axis);
            if (axis) {
                out = axis.calcEnds();
                minY = out.from || axis.prevMin;
                maxY = mmax(out.to || axis.prevMax, 0);
            }
        }

        if (me.yField && !Ext.isNumber(minY)) {
            axis = Ext.create('Ext.chart.axis.Axis', {
                chart: chart,
                fields: [].concat(me.yField)
            });
            out = axis.calcEnds();
            minY = out.from || axis.prevMin;
            maxY = mmax(out.to || axis.prevMax, 0);
        }

        if (!Ext.isNumber(minY)) {
            minY = 0;
        }
        if (!Ext.isNumber(maxY)) {
            maxY = 0;
        }
        scale = (column ? bbox.height - yPadding * 2 : bbox.width - xPadding * 2) / (maxY - minY);
        groupBarWidth = barWidth / ((stacked ? 1 : groupBarsLen) * (groupGutter + 1) - groupGutter);
        zero = (column) ? bbox.y + bbox.height - yPadding : bbox.x + xPadding;

        if (stacked) {
            total = [[], []];
            store.each(function(record, i) {
                total[0][i] = total[0][i] || 0;
                total[1][i] = total[1][i] || 0;
                for (j = 0; j < barsLen; j++) {
                    if (me.__excludes && me.__excludes[j]) {
                        continue;
                    }
                    rec = record.get(bars[j]);
                    total[+(rec > 0)][i] += mabs(rec);
                }
            });
            total[+(maxY > 0)].push(mabs(maxY));
            total[+(minY > 0)].push(mabs(minY));
            minus = mmax.apply(math, total[0]);
            plus = mmax.apply(math, total[1]);
            scale = (column ? bbox.height - yPadding * 2 : bbox.width - xPadding * 2) / (plus + minus);
            zero = zero + minus * scale * (column ? -1 : 1);
        }
        else if (minY / maxY < 0) {
            zero = zero - minY * scale * (column ? -1 : 1);
        }
        return {
            bars: bars,
            bbox: bbox,
            barsLen: barsLen,
            groupBarsLen: groupBarsLen,
            barWidth: barWidth,
            groupBarWidth: groupBarWidth,
            scale: scale,
            zero: zero,
            xPadding: xPadding,
            yPadding: yPadding,
            signed: minY / maxY < 0,
            minY: minY,
            maxY: maxY
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            bounds = me.bounds = me.getBounds(),
            items = me.items = [],
            gutter = me.gutter / 100,
            groupGutter = me.groupGutter / 100,
            animate = chart.animate,
            column = me.column,
            group = me.group,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            shadowGroupsLn = shadowGroups.length,
            bbox = bounds.bbox,
            xPadding = me.xPadding,
            yPadding = me.yPadding,
            stacked = me.stacked,
            barsLen = bounds.barsLen,
            colors = me.colorArrayStyle,
            colorLength = colors && colors.length || 0,
            math = Math,
            mmax = math.max,
            mmin = math.min,
            mabs = math.abs,
            j, yValue, height, totalDim, totalNegDim, bottom, top, hasShadow, barAttr, attrs, counter,
            shadowIndex, shadow, sprite, offset, floorY;

        store.each(function(record, i, total) {
            bottom = bounds.zero;
            top = bounds.zero;
            totalDim = 0;
            totalNegDim = 0;
            hasShadow = false; 
            for (j = 0, counter = 0; j < barsLen; j++) {
                
                if (me.__excludes && me.__excludes[j]) {
                    continue;
                }
                yValue = record.get(bounds.bars[j]);
                height = Math.round((yValue - ((bounds.minY < 0) ? 0 : bounds.minY)) * bounds.scale);
                barAttr = {
                    fill: colors[(barsLen > 1 ? j : 0) % colorLength]
                };
                if (column) {
                    Ext.apply(barAttr, {
                        height: height,
                        width: mmax(bounds.groupBarWidth, 0),
                        x: (bbox.x + xPadding + i * bounds.barWidth * (1 + gutter) + counter * bounds.groupBarWidth * (1 + groupGutter) * !stacked),
                        y: bottom - height
                    });
                }
                else {
                    
                    offset = (total - 1) - i;
                    Ext.apply(barAttr, {
                        height: mmax(bounds.groupBarWidth, 0),
                        width: height + (bottom == bounds.zero),
                        x: bottom + (bottom != bounds.zero),
                        y: (bbox.y + yPadding + offset * bounds.barWidth * (1 + gutter) + counter * bounds.groupBarWidth * (1 + groupGutter) * !stacked + 1)
                    });
                }
                if (height < 0) {
                    if (column) {
                        barAttr.y = top;
                        barAttr.height = mabs(height);
                    } else {
                        barAttr.x = top + height;
                        barAttr.width = mabs(height);
                    }
                }
                if (stacked) {
                    if (height < 0) {
                        top += height * (column ? -1 : 1);
                    } else {
                        bottom += height * (column ? -1 : 1);
                    }
                    totalDim += mabs(height);
                    if (height < 0) {
                        totalNegDim += mabs(height);
                    }
                }
                barAttr.x = Math.floor(barAttr.x) + 1;
                floorY = Math.floor(barAttr.y);
                if (!Ext.isIE9 && barAttr.y > floorY) {
                    floorY--;
                }
                barAttr.y = floorY;
                barAttr.width = Math.floor(barAttr.width);
                barAttr.height = Math.floor(barAttr.height);
                items.push({
                    series: me,
                    storeItem: record,
                    value: [record.get(me.xField), yValue],
                    attr: barAttr,
                    point: column ? [barAttr.x + barAttr.width / 2, yValue >= 0 ? barAttr.y : barAttr.y + barAttr.height] :
                                    [yValue >= 0 ? barAttr.x + barAttr.width : barAttr.x, barAttr.y + barAttr.height / 2]
                });
                
                if (animate && chart.resizing) {
                    attrs = column ? {
                        x: barAttr.x,
                        y: bounds.zero,
                        width: barAttr.width,
                        height: 0
                    } : {
                        x: bounds.zero,
                        y: barAttr.y,
                        width: 0,
                        height: barAttr.height
                    };
                    if (enableShadows && (stacked && !hasShadow || !stacked)) {
                        hasShadow = true;
                        
                        for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                            shadow = shadowGroups[shadowIndex].getAt(stacked ? i : (i * barsLen + j));
                            if (shadow) {
                                shadow.setAttributes(attrs, true);
                            }
                        }
                    }
                    
                    sprite = group.getAt(i * barsLen + j);
                    if (sprite) {
                        sprite.setAttributes(attrs, true);
                    }
                }
                counter++;
            }
            if (stacked && items.length) {
                items[i * counter].totalDim = totalDim;
                items[i * counter].totalNegDim = totalNegDim;
            }
        }, me);
    },

    
    renderShadows: function(i, barAttr, baseAttrs, bounds) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            animate = chart.animate,
            stacked = me.stacked,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            shadowGroupsLn = shadowGroups.length,
            store = chart.substore || chart.store,
            column = me.column,
            items = me.items,
            shadows = [],
            zero = bounds.zero,
            shadowIndex, shadowBarAttr, shadow, totalDim, totalNegDim, j, rendererAttributes;

        if ((stacked && (i % bounds.groupBarsLen === 0)) || !stacked) {
            j = i / bounds.groupBarsLen;
            
            for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                shadowBarAttr = Ext.apply({}, shadowAttributes[shadowIndex]);
                shadow = shadowGroups[shadowIndex].getAt(stacked ? j : i);
                Ext.copyTo(shadowBarAttr, barAttr, 'x,y,width,height');
                if (!shadow) {
                    shadow = surface.add(Ext.apply({
                        type: 'rect',
                        group: shadowGroups[shadowIndex]
                    }, Ext.apply({}, baseAttrs, shadowBarAttr)));
                }
                if (stacked) {
                    totalDim = items[i].totalDim;
                    totalNegDim = items[i].totalNegDim;
                    if (column) {
                        shadowBarAttr.y = zero - totalNegDim;
                        shadowBarAttr.height = totalDim;
                    }
                    else {
                        shadowBarAttr.x = zero - totalNegDim;
                        shadowBarAttr.width = totalDim;
                    }
                }
                if (animate) {
                    if (!stacked) {
                        rendererAttributes = me.renderer(shadow, store.getAt(j), shadowBarAttr, i, store);
                        me.onAnimate(shadow, { to: rendererAttributes });
                    }
                    else {
                        rendererAttributes = me.renderer(shadow, store.getAt(j), Ext.apply(shadowBarAttr, { hidden: true }), i, store);
                        shadow.setAttributes(rendererAttributes, true);
                    }
                }
                else {
                    rendererAttributes = me.renderer(shadow, store.getAt(j), Ext.apply(shadowBarAttr, { hidden: false }), i, store);
                    shadow.setAttributes(rendererAttributes, true);
                }
                shadows.push(shadow);
            }
        }
        return shadows;
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            surface = chart.surface,
            animate = chart.animate,
            stacked = me.stacked,
            column = me.column,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowGroupsLn = shadowGroups.length,
            group = me.group,
            seriesStyle = me.seriesStyle,
            items, ln, i, j, baseAttrs, sprite, rendererAttributes, shadowIndex, shadowGroup,
            bounds, endSeriesStyle, barAttr, attrs, anim;
        
        if (!store || !store.getCount()) {
            return;
        }
        
        
        delete seriesStyle.fill;
        endSeriesStyle = Ext.apply(seriesStyle, this.style);
        me.unHighlightItem();
        me.cleanHighlights();

        me.getPaths();
        bounds = me.bounds;
        items = me.items;

        baseAttrs = column ? {
            y: bounds.zero,
            height: 0
        } : {
            x: bounds.zero,
            width: 0
        };
        ln = items.length;
        
        for (i = 0; i < ln; i++) {
            sprite = group.getAt(i);
            barAttr = items[i].attr;

            if (enableShadows) {
                items[i].shadows = me.renderShadows(i, barAttr, baseAttrs, bounds);
            }

            
            if (!sprite) {
                attrs = Ext.apply({}, baseAttrs, barAttr);
                attrs = Ext.apply(attrs, endSeriesStyle || {});
                sprite = surface.add(Ext.apply({}, {
                    type: 'rect',
                    group: group
                }, attrs));
            }
            if (animate) {
                rendererAttributes = me.renderer(sprite, store.getAt(i), barAttr, i, store);
                sprite._to = rendererAttributes;
                anim = me.onAnimate(sprite, { to: Ext.apply(rendererAttributes, endSeriesStyle) });
                if (enableShadows && stacked && (i % bounds.barsLen === 0)) {
                    j = i / bounds.barsLen;
                    for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                        anim.on('afteranimate', function() {
                            this.show(true);
                        }, shadowGroups[shadowIndex].getAt(j));
                    }
                }
            }
            else {
                rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(barAttr, { hidden: false }), i, store);
                sprite.setAttributes(Ext.apply(rendererAttributes, endSeriesStyle), true);
            }
            items[i].sprite = sprite;
        }

        
        ln = group.getCount();
        for (j = i; j < ln; j++) {
            group.getAt(j).hide(true);
        }
        
        if (enableShadows) {
            for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
                shadowGroup = shadowGroups[shadowIndex];
                ln = shadowGroup.getCount();
                for (j = i; j < ln; j++) {
                    shadowGroup.getAt(j).hide(true);
                }
            }
        }
        me.renderLabels();
    },
    
    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            surface = me.chart.surface,
            group = me.labelsGroup,
            config = me.label,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle || {}),
            sprite;
        return surface.add(Ext.apply({
            type: 'text',
            group: group
        }, endLabelStyle || {}));
    },
    
    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, j, index) {
        
        
        var me = this,
            opt = me.bounds,
            groupBarWidth = opt.groupBarWidth,
            column = me.column,
            chart = me.chart,
            chartBBox = chart.chartBBox,
            resizing = chart.resizing,
            xValue = item.value[0],
            yValue = item.value[1],
            attr = item.attr,
            config = me.label,
            rotate = config.orientation == 'vertical',
            field = [].concat(config.field),
            format = config.renderer,
            text = format(storeItem.get(field[index])),
            size = me.getLabelSize(text),
            width = size.width,
            height = size.height,
            zero = opt.zero,
            outside = 'outside',
            insideStart = 'insideStart',
            insideEnd = 'insideEnd',
            offsetX = 10,
            offsetY = 6,
            signed = opt.signed,
            x, y, finalAttr;

        label.setAttributes({
            text: text
        });

        if (column) {
            if (display == outside) {
                if (height + offsetY + attr.height > (yValue >= 0 ? zero - chartBBox.y : chartBBox.y + chartBBox.height - zero)) {
                    display = insideEnd;
                }
            } else {
                if (height + offsetY > attr.height) {
                    display = outside;
                }
            }
            x = attr.x + groupBarWidth / 2;
            y = display == insideStart ?
                    (zero + ((height / 2 + 3) * (yValue >= 0 ? -1 : 1))) :
                    (yValue >= 0 ? (attr.y + ((height / 2 + 3) * (display == outside ? -1 : 1))) :
                                   (attr.y + attr.height + ((height / 2 + 3) * (display === outside ? 1 : -1))));
        }
        else {
            if (display == outside) {
                if (width + offsetX + attr.width > (yValue >= 0 ? chartBBox.x + chartBBox.width - zero : zero - chartBBox.x)) {
                    display = insideEnd;
                }
            }
            else {
                if (width + offsetX > attr.width) {
                    display = outside;
                }
            }
            x = display == insideStart ?
                (zero + ((width / 2 + 5) * (yValue >= 0 ? 1 : -1))) :
                (yValue >= 0 ? (attr.x + attr.width + ((width / 2 + 5) * (display === outside ? 1 : -1))) :
                (attr.x + ((width / 2 + 5) * (display === outside ? -1 : 1))));
            y = attr.y + groupBarWidth / 2;
        }
        
        finalAttr = {
            x: x,
            y: y
        };
        
        if (rotate) {
            finalAttr.rotate = {
                x: x,
                y: y,
                degrees: 270
            };
        }
        
        if (animate && resizing) {
            if (column) {
                x = attr.x + attr.width / 2;
                y = zero;
            } else {
                x = zero;
                y = attr.y + attr.height / 2;
            }
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (rotate) {
                label.setAttributes({
                    rotate: {
                        x: x,
                        y: y,
                        degrees: 270
                    }
                }, true);
            }
        }
        
        if (animate) {
            me.onAnimate(label, { to: finalAttr });
        }
        else {
            label.setAttributes(Ext.apply(finalAttr, {
                hidden: false
            }), true);
        }
    },

    
    getLabelSize: function(value) {
        var tester = this.testerLabel,
            config = this.label,
            endLabelStyle = Ext.apply({}, config, this.seriesLabelStyle || {}),
            rotated = config.orientation === 'vertical',
            bbox, w, h,
            undef;
        if (!tester) {
            tester = this.testerLabel = this.chart.surface.add(Ext.apply({
                type: 'text',
                opacity: 0
            }, endLabelStyle));
        }
        tester.setAttributes({
            text: value
        }, true);

        
        bbox = tester.getBBox();
        w = bbox.width;
        h = bbox.height;
        return {
            width: rotated ? h : w,
            height: rotated ? w : h
        };
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },
    
    isItemInPoint: function(x, y, item) {
        var bbox = item.sprite.getBBox();
        return bbox.x <= x && bbox.y <= y
            && (bbox.x + bbox.width) >= x
            && (bbox.y + bbox.height) >= y;
    },
    
    
    hideAll: function() {
        var axes = this.chart.axes;
        if (!isNaN(this._index)) {
            if (!this.__excludes) {
                this.__excludes = [];
            }
            this.__excludes[this._index] = true;
            this.drawSeries();
            axes.each(function(axis) {
                axis.drawAxis();
            });
        }
    },

    
    showAll: function() {
        var axes = this.chart.axes;
        if (!isNaN(this._index)) {
            if (!this.__excludes) {
                this.__excludes = [];
            }
            this.__excludes[this._index] = false;
            this.drawSeries();
            axes.each(function(axis) {
                axis.drawAxis();
            });
        }
    },
    
    
    getLegendColor: function(index) {
        var me = this;
        return me.colorArrayStyle[index % me.colorArrayStyle.length];
    }
});

Ext.define('Ext.chart.series.Column', {

    

    alternateClassName: ['Ext.chart.ColumnSeries', 'Ext.chart.ColumnChart', 'Ext.chart.StackedColumnChart'],

    extend: 'Ext.chart.series.Bar',

    

    type: 'column',
    alias: 'series.column',

    column: true,

    
    xPadding: 10,

    
    yPadding: 0
});

Ext.define('Ext.chart.series.Gauge', {

    

    extend: 'Ext.chart.series.Series',

    

    type: "gauge",
    alias: 'series.gauge',

    rad: Math.PI / 180,

    
    highlightDuration: 150,

    
    angleField: false,

    
    needle: false,
    
    
    donut: false,

    
    showInLegend: false,

    
    style: {},
    
    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            store = chart.store,
            shadow = chart.shadow, i, l, cfg;
        Ext.apply(me, config, {
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 1,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 2
                }
            },
            {
                "stroke-width": 4,
                "stroke-opacity": 1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 1.5
                }
            },
            {
                "stroke-width": 2,
                "stroke-opacity": 1,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
        surface.customAttributes.segment = function(opt) {
            return me.getSegment(opt);
        };
    },
    
    //@private updates some onbefore render parameters.

    initialize: function() {
        var me = this,
            store = me.chart.substore || me.chart.store;
        
        me.yField = [];
        if (me.label.field) {
            store.each(function(rec) {
                me.yField.push(rec.get(me.label.field));
            });
        }
    },

    
    getSegment: function(opt) {
        var me = this,
            rad = me.rad,
            cos = Math.cos,
            sin = Math.sin,
            abs = Math.abs,
            x = me.centerX,
            y = me.centerY,
            x1 = 0, x2 = 0, x3 = 0, x4 = 0,
            y1 = 0, y2 = 0, y3 = 0, y4 = 0,
            delta = 1e-2,
            r = opt.endRho - opt.startRho,
            startAngle = opt.startAngle,
            endAngle = opt.endAngle,
            midAngle = (startAngle + endAngle) / 2 * rad,
            margin = opt.margin || 0,
            flag = abs(endAngle - startAngle) > 180,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            singleSlice = false;

        x += margin * cos(midAngle);
        y += margin * sin(midAngle);

        x1 = x + opt.startRho * cos(a1);
        y1 = y + opt.startRho * sin(a1);

        x2 = x + opt.endRho * cos(a1);
        y2 = y + opt.endRho * sin(a1);

        x3 = x + opt.startRho * cos(a2);
        y3 = y + opt.startRho * sin(a2);

        x4 = x + opt.endRho * cos(a2);
        y4 = y + opt.endRho * sin(a2);

        if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
            singleSlice = true;
        }
        
        if (singleSlice) {
            return {
                path: [
                ["M", x1, y1],
                ["L", x2, y2],
                ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
                ["Z"]]
            };
        } else {
            return {
                path: [
                ["M", x1, y1],
                ["L", x2, y2],
                ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
                ["L", x3, y3],
                ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
                ["Z"]]
            };
        }
    },

    
    calcMiddle: function(item) {
        var me = this,
            rad = me.rad,
            slice = item.slice,
            x = me.centerX,
            y = me.centerY,
            startAngle = slice.startAngle,
            endAngle = slice.endAngle,
            radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
            donut = +me.donut,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            midAngle = -(a1 + (a2 - a1) / 2),
            xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
            ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);

        item.middle = {
            x: xm,
            y: ym
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            group = me.group,
            animate = me.chart.animate,
            axis = me.chart.axes.get(0),
            minimum = axis && axis.minimum || me.minimum || 0,
            maximum = axis && axis.maximum || me.maximum || 0,
            field = me.angleField || me.field || me.xField,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            rad = me.rad,
            donut = +me.donut,
            values = {},
            items = [],
            seriesStyle = me.seriesStyle,
            seriesLabelStyle = me.seriesLabelStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            gutterX = chart.maxGutter[0],
            gutterY = chart.maxGutter[1],
            cos = Math.cos,
            sin = Math.sin,
            rendererAttributes, centerX, centerY, slice, slices, sprite, value,
            item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
            p, spriteOptions, bbox, splitAngle, sliceA, sliceB;
        
        Ext.apply(seriesStyle, me.style || {});

        me.setBBox();
        bbox = me.bbox;

        
        if (me.colorSet) {
            colorArrayStyle = me.colorSet;
            colorArrayLength = colorArrayStyle.length;
        }
        
        
        if (!store || !store.getCount()) {
            return;
        }
        
        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + chartBBox.height;
        me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
        me.slices = slices = [];
        me.items = items = [];
        
        if (!me.value) {
            record = store.getAt(0);
            me.value = record.get(field);
        }
        
        value = me.value;
        if (me.needle) {
            sliceA = {
                series: me,
                value: value,
                startAngle: -180,
                endAngle: 0,
                rho: me.radius
            };
            splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
            slices.push(sliceA);
        } else {
            splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
            sliceA = {
                series: me,
                value: value,
                startAngle: -180,
                endAngle: splitAngle,
                rho: me.radius
            };
            sliceB = {
                series: me,
                value: me.maximum - value,
                startAngle: splitAngle,
                endAngle: 0,
                rho: me.radius
            };
            slices.push(sliceA, sliceB);
        }
        
        
        for (i = 0, ln = slices.length; i < ln; i++) {
            slice = slices[i];
            sprite = group.getAt(i);
            
            rendererAttributes = Ext.apply({
                segment: {
                    startAngle: slice.startAngle,
                    endAngle: slice.endAngle,
                    margin: 0,
                    rho: slice.rho,
                    startRho: slice.rho * +donut / 100,
                    endRho: slice.rho
                } 
            }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));

            item = Ext.apply({},
            rendererAttributes.segment, {
                slice: slice,
                series: me,
                storeItem: record,
                index: i
            });
            items[i] = item;
            
            if (!sprite) {
                spriteOptions = Ext.apply({
                    type: "path",
                    group: group
                }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
                sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
            }
            slice.sprite = slice.sprite || [];
            item.sprite = sprite;
            slice.sprite.push(sprite);
            if (animate) {
                rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store);
                sprite._to = rendererAttributes;
                me.onAnimate(sprite, {
                    to: rendererAttributes
                });
            } else {
                rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, {
                    hidden: false
                }), i, store);
                sprite.setAttributes(rendererAttributes, true);
            }
        }
        
        if (me.needle) {
            splitAngle = splitAngle * Math.PI / 180;
            
            if (!me.needleSprite) {
                me.needleSprite = me.chart.surface.add({
                    type: 'path',
                    path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                           'L', centerX + me.radius * cos(splitAngle),
                                centerY + -Math.abs(me.radius * sin(splitAngle))],
                    'stroke-width': 4,
                    'stroke': '#222'
                });
            } else {
                if (animate) {
                    me.onAnimate(me.needleSprite, {
                        to: {
                        path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                    centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                               'L', centerX + me.radius * cos(splitAngle),
                                    centerY + -Math.abs(me.radius * sin(splitAngle))]
                        }
                    });
                } else {
                    me.needleSprite.setAttributes({
                        type: 'path',
                        path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
                                    centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
                               'L', centerX + me.radius * cos(splitAngle),
                                    centerY + -Math.abs(me.radius * sin(splitAngle))]
                    });
                }
            }
            me.needleSprite.setAttributes({
                hidden: false    
            }, true);
        }
        
        delete me.value;
    },
    
    
    setValue: function (value) {
        this.value = value;
        this.drawSeries();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {},

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},

    
    onPlaceCallout: function() {},

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item, i) {
        return false;
    },
    
    
    showAll: function() {
        if (!isNaN(this._index)) {
            this.__excludes[this._index] = false;
            this.drawSeries();
        }
    },
    
    
    getLegendColor: function(index) {
        var me = this;
        return me.colorArrayStyle[index % me.colorArrayStyle.length];
    }
});



Ext.define('Ext.chart.series.Line', {

    

    extend: 'Ext.chart.series.Cartesian',

    alternateClassName: ['Ext.chart.LineSeries', 'Ext.chart.LineChart'],

    requires: ['Ext.chart.axis.Axis', 'Ext.chart.Shape', 'Ext.draw.Draw', 'Ext.fx.Anim'],

    

    type: 'line',
    
    alias: 'series.line',
    
    

    
    selectionTolerance: 20,
    
    
    showMarkers: true,

    
    markerConfig: {},

    
    style: {},
    
    
    smooth: false,

    
    fill: false,

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface,
            shadow = me.chart.shadow,
            i, l;
        Ext.apply(me, config, {
            highlightCfg: {
                'stroke-width': 3
            },
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(0, 0, 0)',
                translate: {
                    x: 1,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (me.showMarkers) {
            me.markerGroup = surface.getGroup(me.seriesId + '-markers');
        }
        if (shadow) {
            for (i = 0, l = this.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },
    
    
    shrink: function(xValues, yValues, size) {
        
        var len = xValues.length,
            ratio = Math.floor(len / size),
            i = 1,
            xSum = 0,
            ySum = 0,
            xRes = [xValues[0]],
            yRes = [yValues[0]];
        
        for (; i < len; ++i) {
            xSum += xValues[i] || 0;
            ySum += yValues[i] || 0;
            if (i % ratio == 0) {
                xRes.push(xSum/ratio);
                yRes.push(ySum/ratio);
                xSum = 0;
                ySum = 0;
            }
        }
        return {
            x: xRes,
            y: yRes
        };
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            bbox = {},
            group = me.group,
            gutterX = chart.maxGutter[0],
            gutterY = chart.maxGutter[1],
            showMarkers = me.showMarkers,
            markerGroup = me.markerGroup,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = this.shadowAttributes,
            lnsh = shadowGroups.length,
            dummyPath = ["M"],
            path = ["M"],
            markerIndex = chart.markerIndex,
            axes = [].concat(me.axis),
            shadowGroup,
            shadowBarAttr,
            xValues = [],
            yValues = [],
            numericAxis = true,
            axisCount = 0,
            onbreak = false,
            markerStyle = me.markerStyle,
            seriesStyle = me.seriesStyle,
            seriesLabelStyle = me.seriesLabelStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            posHash = {
                'left': 'right',
                'right': 'left',
                'top': 'bottom',
                'bottom': 'top'
            },
            seriesIdx = me.seriesIdx, shadows, shadow, shindex, fromPath, fill, fillPath, rendererAttributes,
            x, y, prevX, prevY, firstY, markerCount, i, j, ln, axis, ends, marker, markerAux, item, xValue,
            yValue, coords, xScale, yScale, minX, maxX, minY, maxY, line, animation, endMarkerStyle,
            endLineStyle, type, props, firstMarker, count;
        
        
        if (!store || !store.getCount()) {
            return;
        }
        
        
        endMarkerStyle = Ext.apply(markerStyle, me.markerConfig);
        type = endMarkerStyle.type;
        delete endMarkerStyle.type;
        endLineStyle = Ext.apply(seriesStyle, me.style);
        
        
        if (!endLineStyle['stroke-width']) {
            endLineStyle['stroke-width'] = 0.5;
        }
        
        
        if (markerIndex && markerGroup && markerGroup.getCount()) {
            for (i = 0; i < markerIndex; i++) {
                marker = markerGroup.getAt(i);
                markerGroup.remove(marker);
                markerGroup.add(marker);
                markerAux = markerGroup.getAt(markerGroup.getCount() - 2);
                marker.setAttributes({
                    x: 0,
                    y: 0,
                    translate: {
                        x: markerAux.attr.translation.x,
                        y: markerAux.attr.translation.y
                    }
                }, true);
            }
        }
        
        me.unHighlightItem();
        me.cleanHighlights();

        me.setBBox();
        bbox = me.bbox;

        me.clipRect = [bbox.x, bbox.y, bbox.width, bbox.height];

        chart.axes.each(function(axis) {
            
            
            
            
            if (axis.position == me.axis || axis.position != posHash[me.axis]) {
                axisCount++;
                if (axis.type != 'Numeric') {
                    numericAxis = false;
                    return;
                }
                numericAxis = (numericAxis && axis.type == 'Numeric');
                if (axis) {
                    ends = axis.calcEnds();
                    if (axis.position == 'top' || axis.position == 'bottom') {
                        minX = ends.from;
                        maxX = ends.to;
                    }
                    else {
                        minY = ends.from;
                        maxY = ends.to;
                    }
                }
            }
        });
        
        
        
        
        if (numericAxis && axisCount == 1) {
            numericAxis = false;
        }
        
        
        
        
        if (me.xField && !Ext.isNumber(minX)) {
            if (me.axis == 'bottom' || me.axis == 'top') {
                axis = Ext.create('Ext.chart.axis.Axis', {
                    chart: chart,
                    fields: [].concat(me.xField)
                }).calcEnds();
                minX = axis.from;
                maxX = axis.to;
            } else if (numericAxis) {
                axis = Ext.create('Ext.chart.axis.Axis', {
                    chart: chart,
                    fields: [].concat(me.xField),
                    forceMinMax: true
                }).calcEnds();
                minX = axis.from;
                maxX = axis.to;
            }
        }
        
        if (me.yField && !Ext.isNumber(minY)) {
            if (me.axis == 'right' || me.axis == 'left') {
                axis = Ext.create('Ext.chart.axis.Axis', {
                    chart: chart,
                    fields: [].concat(me.yField)
                }).calcEnds();
                minY = axis.from;
                maxY = axis.to;
            } else if (numericAxis) {
                axis = Ext.create('Ext.chart.axis.Axis', {
                    chart: chart,
                    fields: [].concat(me.yField),
                    forceMinMax: true
                }).calcEnds();
                minY = axis.from;
                maxY = axis.to;
            }
        }
        
        if (isNaN(minX)) {
            minX = 0;
            xScale = bbox.width / (store.getCount() - 1);
        }
        else {
            xScale = bbox.width / (maxX - minX);
        }

        if (isNaN(minY)) {
            minY = 0;
            yScale = bbox.height / (store.getCount() - 1);
        } 
        else {
            yScale = bbox.height / (maxY - minY);
        }
        
        store.each(function(record, i) {
            xValue = record.get(me.xField);
            yValue = record.get(me.yField);
            
            if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)) {
                if (Ext.isDefined(Ext.global.console)) {
                    Ext.global.console.warn("[Ext.chart.series.Line]  Skipping a store element with an undefined value at ", record, xValue, yValue);
                }
                return;
            }
            
            if (typeof xValue == 'string' || typeof xValue == 'object'
                
                || (me.axis != 'top' && me.axis != 'bottom' && !numericAxis)) {
                xValue = i;
            }
            if (typeof yValue == 'string' || typeof yValue == 'object'
                
                || (me.axis != 'left' && me.axis != 'right' && !numericAxis)) {
                yValue = i;
            }
            xValues.push(xValue);
            yValues.push(yValue);
        }, me);

        ln = xValues.length;
        if (ln > bbox.width) {
            coords = me.shrink(xValues, yValues, bbox.width);
            xValues = coords.x;
            yValues = coords.y;
        }

        me.items = [];

        count = 0;
        ln = xValues.length;
        for (i = 0; i < ln; i++) {
            xValue = xValues[i];
            yValue = yValues[i];
            if (yValue === false) {
                if (path.length == 1) {
                    path = [];
                }
                onbreak = true;
                me.items.push(false);
                continue;
            } else {
                x = (bbox.x + (xValue - minX) * xScale).toFixed(2);
                y = ((bbox.y + bbox.height) - (yValue - minY) * yScale).toFixed(2);
                if (onbreak) {
                    onbreak = false;
                    path.push('M');
                } 
                path = path.concat([x, y]);
            }
            if ((typeof firstY == 'undefined') && (typeof y != 'undefined')) {
                firstY = y;
            }
            
            if (!me.line || chart.resizing) {
                dummyPath = dummyPath.concat([x, bbox.y + bbox.height / 2]);
            }

            
            if (chart.animate && chart.resizing && me.line) {
                me.line.setAttributes({
                    path: dummyPath
                }, true);
                if (me.fillPath) {
                    me.fillPath.setAttributes({
                        path: dummyPath,
                        opacity: 0.2
                    }, true);
                }
                if (me.line.shadows) {
                    shadows = me.line.shadows;
                    for (j = 0, lnsh = shadows.length; j < lnsh; j++) {
                        shadow = shadows[j];
                        shadow.setAttributes({
                            path: dummyPath
                        }, true);
                    }
                }
            }
            if (showMarkers) {
                marker = markerGroup.getAt(count++);
                if (!marker) {
                    marker = Ext.chart.Shape[type](surface, Ext.apply({
                        group: [group, markerGroup],
                        x: 0, y: 0,
                        translate: {
                            x: prevX || x, 
                            y: prevY || (bbox.y + bbox.height / 2)
                        },
                        value: '"' + xValue + ', ' + yValue + '"'
                    }, endMarkerStyle));
                    marker._to = {
                        translate: {
                            x: x,
                            y: y
                        }
                    };
                } else {
                    marker.setAttributes({
                        value: '"' + xValue + ', ' + yValue + '"',
                        x: 0, y: 0,
                        hidden: false
                    }, true);
                    marker._to = {
                        translate: {
                            x: x, y: y
                        }
                    };
                }
            }
            me.items.push({
                series: me,
                value: [xValue, yValue],
                point: [x, y],
                sprite: marker,
                storeItem: store.getAt(i)
            });
            prevX = x;
            prevY = y;
        }
        
        if (path.length <= 1) {
            
            return;    
        }
        
        if (me.smooth) {
            path = Ext.draw.Draw.smooth(path, 6);
        }
        
        
        if (chart.markerIndex && me.previousPath) {
            fromPath = me.previousPath;
            fromPath.splice(1, 2);
        } else {
            fromPath = path;
        }

        
        if (!me.line) {
            me.line = surface.add(Ext.apply({
                type: 'path',
                group: group,
                path: dummyPath,
                stroke: endLineStyle.stroke || endLineStyle.fill
            }, endLineStyle || {}));
            
            me.line.setAttributes({
                fill: 'none'
            });
            if (!endLineStyle.stroke && colorArrayLength) {
                me.line.setAttributes({
                    stroke: colorArrayStyle[seriesIdx % colorArrayLength]
                }, true);
            }
            if (enableShadows) {
                
                shadows = me.line.shadows = [];                
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowBarAttr = shadowAttributes[shindex];
                    shadowBarAttr = Ext.apply({}, shadowBarAttr, { path: dummyPath });
                    shadow = chart.surface.add(Ext.apply({}, {
                        type: 'path',
                        group: shadowGroups[shindex]
                    }, shadowBarAttr));
                    shadows.push(shadow);
                }
            }
        }
        if (me.fill) {
            fillPath = path.concat([
                ["L", x, bbox.y + bbox.height],
                ["L", bbox.x, bbox.y + bbox.height],
                ["L", bbox.x, firstY]
            ]);
            if (!me.fillPath) {
                me.fillPath = surface.add({
                    group: group,
                    type: 'path',
                    opacity: endLineStyle.opacity || 0.3,
                    fill: colorArrayStyle[seriesIdx % colorArrayLength] || endLineStyle.fill,
                    path: dummyPath
                });
            }
        }
        markerCount = showMarkers && markerGroup.getCount();
        if (chart.animate) {
            fill = me.fill;
            line = me.line;
            
            rendererAttributes = me.renderer(line, false, { path: path }, i, store);
            Ext.apply(rendererAttributes, endLineStyle || {}, {
                stroke: endLineStyle.stroke || endLineStyle.fill
            });
            
            delete rendererAttributes.fill;
            if (chart.markerIndex && me.previousPath) {
                me.animation = animation = me.onAnimate(line, {
                    to: rendererAttributes,
                    from: {
                        path: fromPath
                    }
                });
            } else {
                me.animation = animation = me.onAnimate(line, {
                    to: rendererAttributes
                });
            }
            
            if (enableShadows) {
                shadows = line.shadows;
                for(j = 0; j < lnsh; j++) {
                    if (chart.markerIndex && me.previousPath) {
                        me.onAnimate(shadows[j], {
                            to: { path: path },
                            from: { path: fromPath }
                        });
                    } else {
                        me.onAnimate(shadows[j], {
                            to: { path: path }
                        });
                    }
                }
            }
            
            if (fill) {
                me.onAnimate(me.fillPath, {
                    to: Ext.apply({}, {
                        path: fillPath,
                        fill: colorArrayStyle[seriesIdx % colorArrayLength] || endLineStyle.fill
                    }, endLineStyle || {})
                });
            }
            
            if (showMarkers) {
                count = 0;
                for(i = 0; i < ln; i++) {
                    if (me.items[i]) {
                        item = markerGroup.getAt(count++);
                        if (item) {
                            rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
                            me.onAnimate(item, {
                                to: Ext.apply(rendererAttributes, endMarkerStyle || {})
                            });
                        }
                    } 
                }
                for(; count < markerCount; count++) {
                    item = markerGroup.getAt(count);
                    item.hide(true);
                }
            }
        } else {
            rendererAttributes = me.renderer(me.line, false, { path: path, hidden: false }, i, store);
            Ext.apply(rendererAttributes, endLineStyle || {}, {
                stroke: endLineStyle.stroke || endLineStyle.fill
            });
            
            delete rendererAttributes.fill;
            me.line.setAttributes(rendererAttributes, true);
            
            if (enableShadows) {
                shadows = me.line.shadows;
                for(j = 0; j < lnsh; j++) {
                    shadows[j].setAttributes({
                        path: path
                    }, true);
                }
            }
            if (me.fill) {
                me.fillPath.setAttributes({
                    path: fillPath
                }, true);
            }
            if (showMarkers) {
                count = 0;
                for(i = 0; i < ln; i++) {
                    if (me.items[i]) {
                        item = markerGroup.getAt(count++);
                        if (item) {
                            rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
                            item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
                        }
                    } 
                }
                for(; count < markerCount; count++) {
                    item = markerGroup.getAt(count);
                    item.hide(true);
                }
            }
        }

        if (chart.markerIndex) {
            path.splice(1, 0, path[1], path[2]);
            me.previousPath = path;
        }
        me.renderLabels();
        me.renderCallouts();
    },
    
    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            bbox = me.bbox,
            endLabelStyle = Ext.apply(config, me.seriesLabelStyle);

        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': item.point[0],
            'y': bbox.y + bbox.height / 2
        }, endLabelStyle || {}));
    },
    
    
    onPlaceLabel: function(label, storeItem, item, i, display, animate) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = item.point[0],
            y = item.point[1],
            radius = item.sprite.attr.radius,
            bb, width, height;
        
        label.setAttributes({
            text: format(storeItem.get(field)),
            hidden: true
        }, true);
        
        if (display == 'rotate') {
            label.setAttributes({
                'text-anchor': 'start',
                'rotation': {
                    x: x,
                    y: y,
                    degrees: -45
                }
            }, true);
            
            bb = label.getBBox();
            width = bb.width;
            height = bb.height;
            x = x < bbox.x? bbox.x : x;
            x = (x + width > bbox.x + bbox.width)? (x - (x + width - bbox.x - bbox.width)) : x;
            y = (y - height < bbox.y)? bbox.y + height : y;
        
        } else if (display == 'under' || display == 'over') {
            
            bb = item.sprite.getBBox();
            bb.width = bb.width || (radius * 2);
            bb.height = bb.height || (radius * 2);
            y = y + (display == 'over'? -bb.height : bb.height);
            
            bb = label.getBBox();
            width = bb.width/2;
            height = bb.height/2;
            x = x - width < bbox.x? bbox.x + width : x;
            x = (x + width > bbox.x + bbox.width) ? (x - (x + width - bbox.x - bbox.width)) : x;
            y = y - height < bbox.y? bbox.y + height : y;
            y = (y + height > bbox.y + bbox.height) ? (y - (y + height - bbox.y - bbox.height)) : y;
        }
        
        if (me.chart.animate && !me.chart.resizing) {
            label.show(true);
            me.onAnimate(label, {
                to: {
                    x: x,
                    y: y
                }
            });
        } else {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            if (resizing) {
                me.animation.on('afteranimate', function() {
                    label.show(true);
                });
            } else {
                label.show(true);
            }
        }
    },

    //@private Overriding highlights.js highlightItem method.

    highlightItem: function() {
        var me = this;
        me.callParent(arguments);
        if (this.line && !this.highlighted) {
            if (!('__strokeWidth' in this.line)) {
                this.line.__strokeWidth = this.line.attr['stroke-width'] || 0;
            }
            if (this.line.__anim) {
                this.line.__anim.paused = true;
            }
            this.line.__anim = Ext.create('Ext.fx.Anim', {
                target: this.line,
                to: {
                    'stroke-width': this.line.__strokeWidth + 3
                }
            });
            this.highlighted = true;
        }
    },

    //@private Overriding highlights.js unHighlightItem method.

    unHighlightItem: function() {
        var me = this;
        me.callParent(arguments);
        if (this.line && this.highlighted) {
            this.line.__anim = Ext.create('Ext.fx.Anim', {
                target: this.line,
                to: {
                    'stroke-width': this.line.__strokeWidth
                }
            });
            this.highlighted = false;
        }
    },

    //@private called when a callout needs to be placed.

    onPlaceCallout : function(callout, storeItem, item, i, display, animate, index) {
        if (!display) {
            return;
        }
        
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            prev = i == 0? false : items[i -1].point,
            next = (i == items.length -1)? false : items[i +1].point,
            cur = [+item.point[0], +item.point[1]],
            dir, norm, normal, a, aprev, anext,
            offsetFromViz = config.offsetFromViz || 30,
            offsetToSide = config.offsetToSide || 10,
            offsetBox = config.offsetBox || 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.clipRect,
            bbox = {
                width: config.styles.width || 10,
                height: config.styles.height || 10
            },
            x, y;

        
        if (!prev) {
            prev = cur;
        }
        if (!next) {
            next = cur;
        }
        a = (next[1] - prev[1]) / (next[0] - prev[0]);
        aprev = (cur[1] - prev[1]) / (cur[0] - prev[0]);
        anext = (next[1] - cur[1]) / (next[0] - cur[0]);
        
        norm = Math.sqrt(1 + a * a);
        dir = [1 / norm, a / norm];
        normal = [-dir[1], dir[0]];
        
        
        if (aprev > 0 && anext < 0 && normal[1] < 0
            || aprev < 0 && anext > 0 && normal[1] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        } else if (Math.abs(aprev) < Math.abs(anext) && normal[0] < 0
                   || Math.abs(aprev) > Math.abs(anext) && normal[0] > 0) {
            normal[0] *= -1;
            normal[1] *= -1;
        }
        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;

        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;
        
        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }

        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;
        
        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;
        
        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", cur[0], cur[1], "L", x, y, "Z"]
                }
            });
            
            if (callout.panel) {
                callout.panel.setPosition(boxx, boxy, true);
            }
        }
        else {
            
            callout.lines.setAttributes({
                path: ["M", cur[0], cur[1], "L", x, y, "Z"]
            }, true);
            
            if (callout.panel) {
                callout.panel.setPosition(boxx, boxy);
            }
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },
    
    isItemInPoint: function(x, y, item, i) {
        var me = this,
            items = me.items,
            tolerance = me.selectionTolerance,
            result = null,
            prevItem,
            nextItem,
            prevPoint,
            nextPoint,
            ln,
            x1,
            y1,
            x2,
            y2,
            xIntersect,
            yIntersect,
            dist1, dist2, dist, midx, midy,
            sqrt = Math.sqrt, abs = Math.abs;
        
        nextItem = items[i];
        prevItem = i && items[i - 1];
        
        if (i >= ln) {
            prevItem = items[ln - 1];
        }
        prevPoint = prevItem && prevItem.point;
        nextPoint = nextItem && nextItem.point;
        x1 = prevItem ? prevPoint[0] : nextPoint[0] - tolerance;
        y1 = prevItem ? prevPoint[1] : nextPoint[1];
        x2 = nextItem ? nextPoint[0] : prevPoint[0] + tolerance;
        y2 = nextItem ? nextPoint[1] : prevPoint[1];
        dist1 = sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
        dist2 = sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
        dist = Math.min(dist1, dist2);
        
        if (dist <= tolerance) {
            return dist == dist1? prevItem : nextItem;
        }
        return false;
    },
    
    
    toggleAll: function(show) {
        var me = this,
            i, ln, shadow, shadows;
        if (!show) {
            Ext.chart.series.Line.superclass.hideAll.call(me);
        }
        else {
            Ext.chart.series.Line.superclass.showAll.call(me);
        }
        if (me.line) {
            me.line.setAttributes({
                hidden: !show
            }, true);
            
            if (me.line.shadows) {
                for (i = 0, shadows = me.line.shadows, ln = shadows.length; i < ln; i++) {
                    shadow = shadows[i];
                    shadow.setAttributes({
                        hidden: !show
                    }, true);
                }
            }
        }
        if (me.fillPath) {
            me.fillPath.setAttributes({
                hidden: !show
            }, true);
        }
    },
    
    
    hideAll: function() {
        this.toggleAll(false);
    },
    
    
    showAll: function() {
        this.toggleAll(true);
    }
});

Ext.define('Ext.chart.series.Pie', {

    

    alternateClassName: ['Ext.chart.PieSeries', 'Ext.chart.PieChart'],

    extend: 'Ext.chart.series.Series',

    

    type: "pie",
    
    alias: 'series.pie',

    rad: Math.PI / 180,

    
    highlightDuration: 150,

    
    angleField: false,

    
    lengthField: false,

    
    donut: false,

    
    showInLegend: false,

    
    
    
    style: {},
    
    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            store = chart.store,
            shadow = chart.shadow, i, l, cfg;
        Ext.applyIf(me, {
            highlightCfg: {
                segment: {
                    margin: 20
                }
            }
        });
        Ext.apply(me, config, {            
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 1,
                stroke: 'rgb(200, 200, 200)',
                translate: {
                    x: 1.2,
                    y: 2
                }
            },
            {
                "stroke-width": 4,
                "stroke-opacity": 1,
                stroke: 'rgb(150, 150, 150)',
                translate: {
                    x: 0.9,
                    y: 1.5
                }
            },
            {
                "stroke-width": 2,
                "stroke-opacity": 1,
                stroke: 'rgb(100, 100, 100)',
                translate: {
                    x: 0.6,
                    y: 1
                }
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
        surface.customAttributes.segment = function(opt) {
            return me.getSegment(opt);
        };
    },
    
    //@private updates some onbefore render parameters.

    initialize: function() {
        var me = this,
            store = me.chart.substore || me.chart.store;
        
        me.yField = [];
        if (me.label.field) {
            store.each(function(rec) {
                me.yField.push(rec.get(me.label.field));
            });
        }
    },

    
    getSegment: function(opt) {
        var me = this,
            rad = me.rad,
            cos = Math.cos,
            sin = Math.sin,
            abs = Math.abs,
            x = me.centerX,
            y = me.centerY,
            x1 = 0, x2 = 0, x3 = 0, x4 = 0,
            y1 = 0, y2 = 0, y3 = 0, y4 = 0,
            delta = 1e-2,
            r = opt.endRho - opt.startRho,
            startAngle = opt.startAngle,
            endAngle = opt.endAngle,
            midAngle = (startAngle + endAngle) / 2 * rad,
            margin = opt.margin || 0,
            flag = abs(endAngle - startAngle) > 180,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            singleSlice = false;

        x += margin * cos(midAngle);
        y += margin * sin(midAngle);

        x1 = x + opt.startRho * cos(a1);
        y1 = y + opt.startRho * sin(a1);

        x2 = x + opt.endRho * cos(a1);
        y2 = y + opt.endRho * sin(a1);

        x3 = x + opt.startRho * cos(a2);
        y3 = y + opt.startRho * sin(a2);

        x4 = x + opt.endRho * cos(a2);
        y4 = y + opt.endRho * sin(a2);

        if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
            singleSlice = true;
        }
        
        if (singleSlice) {
            return {
                path: [
                ["M", x1, y1],
                ["L", x2, y2],
                ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
                ["Z"]]
            };
        } else {
            return {
                path: [
                ["M", x1, y1],
                ["L", x2, y2],
                ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
                ["L", x3, y3],
                ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
                ["Z"]]
            };
        }
    },

    
    calcMiddle: function(item) {
        var me = this,
            rad = me.rad,
            slice = item.slice,
            x = me.centerX,
            y = me.centerY,
            startAngle = slice.startAngle,
            endAngle = slice.endAngle,
            donut = +me.donut,
            a1 = Math.min(startAngle, endAngle) * rad,
            a2 = Math.max(startAngle, endAngle) * rad,
            midAngle = -(a1 + (a2 - a1) / 2),
            xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
            ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);

        item.middle = {
            x: xm,
            y: ym
        };
    },

    
    drawSeries: function() {
        var me = this,
            store = me.chart.substore || me.chart.store,
            group = me.group,
            animate = me.chart.animate,
            field = me.angleField || me.field || me.xField,
            lenField = [].concat(me.lengthField),
            totalLenField = 0,
            colors = me.colorSet,
            chart = me.chart,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            rad = me.rad,
            layers = lenField.length,
            rhoAcum = 0,
            donut = +me.donut,
            layerTotals = [],
            values = {},
            fieldLength,
            items = [],
            passed = false,
            totalField = 0,
            maxLenField = 0,
            cut = 9,
            defcut = true,
            angle = 0,
            seriesStyle = me.seriesStyle,
            seriesLabelStyle = me.seriesLabelStyle,
            colorArrayStyle = me.colorArrayStyle,
            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
            gutterX = chart.maxGutter[0],
            gutterY = chart.maxGutter[1],
            rendererAttributes,
            shadowGroup,
            shadowAttr,
            shadows,
            shadow,
            shindex,
            centerX,
            centerY,
            deltaRho,
            first = 0,
            slice,
            slices,
            sprite,
            value,
            item,
            lenValue,
            ln,
            record,
            i,
            j,
            startAngle,
            endAngle,
            middleAngle,
            sliceLength,
            path,
            p,
            spriteOptions, bbox;
        
        Ext.apply(seriesStyle, me.style || {});

        me.setBBox();
        bbox = me.bbox;

        
        if (me.colorSet) {
            colorArrayStyle = me.colorSet;
            colorArrayLength = colorArrayStyle.length;
        }
        
        
        if (!store || !store.getCount()) {
            return;
        }
        
        me.unHighlightItem();
        me.cleanHighlights();

        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
        me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
        me.slices = slices = [];
        me.items = items = [];

        store.each(function(record, i) {
            if (this.__excludes && this.__excludes[i]) {
                
                return;
            }
            totalField += +record.get(field);
            if (lenField[0]) {
                for (j = 0, totalLenField = 0; j < layers; j++) {
                    totalLenField += +record.get(lenField[j]);
                }
                layerTotals[i] = totalLenField;
                maxLenField = Math.max(maxLenField, totalLenField);
            }
        }, this);

        store.each(function(record, i) {
            if (this.__excludes && this.__excludes[i]) {
                
                return;
            } 
            value = record.get(field);
            middleAngle = angle - 360 * value / totalField / 2;
            
            if (isNaN(middleAngle)) {
                middleAngle = 360;
                value = 1;
                totalField = 1;
            }
            
            if (!i || first == 0) {
                angle = 360 - middleAngle;
                me.firstAngle = angle;
                middleAngle = angle - 360 * value / totalField / 2;
            }
            endAngle = angle - 360 * value / totalField;
            slice = {
                series: me,
                value: value,
                startAngle: angle,
                endAngle: endAngle,
                storeItem: record
            };
            if (lenField[0]) {
                lenValue = layerTotals[i];
                slice.rho = me.radius * (lenValue / maxLenField);
            } else {
                slice.rho = me.radius;
            }
            slices[i] = slice;
            if((slice.startAngle % 360) == (slice.endAngle % 360)) {
                slice.startAngle -= 0.0001;
            }
            angle = endAngle;
            first++;
        }, me);
        
        
        if (enableShadows) {
            for (i = 0, ln = slices.length; i < ln; i++) {
                if (this.__excludes && this.__excludes[i]) {
                    
                    continue;
                }
                slice = slices[i];
                slice.shadowAttrs = [];
                for (j = 0, rhoAcum = 0, shadows = []; j < layers; j++) {
                    sprite = group.getAt(i * layers + j);
                    deltaRho = lenField[j] ? store.getAt(i).get(lenField[j]) / layerTotals[i] * slice.rho: slice.rho;
                    
                    rendererAttributes = {
                        segment: {
                            startAngle: slice.startAngle,
                            endAngle: slice.endAngle,
                            margin: 0,
                            rho: slice.rho,
                            startRho: rhoAcum + (deltaRho * donut / 100),
                            endRho: rhoAcum + deltaRho
                        }
                    };
                    
                    for (shindex = 0, shadows = []; shindex < lnsh; shindex++) {
                        shadowAttr = shadowAttributes[shindex];
                        shadow = shadowGroups[shindex].getAt(i);
                        if (!shadow) {
                            shadow = chart.surface.add(Ext.apply({}, {
                                type: 'path',
                                group: shadowGroups[shindex],
                                strokeLinejoin: "round"
                            }, rendererAttributes, shadowAttr));
                        }
                        if (animate) {
                            shadowAttr = me.renderer(shadow, store.getAt(i), Ext.apply({}, rendererAttributes, shadowAttr), i, store);
                            me.onAnimate(shadow, {
                                to: shadowAttr
                            });
                        } else {
                            shadowAttr = me.renderer(shadow, store.getAt(i), Ext.apply(shadowAttr, {
                                hidden: false
                            }), i, store);
                            shadow.setAttributes(shadowAttr, true);
                        }
                        shadows.push(shadow);
                    }
                    slice.shadowAttrs[j] = shadows;
                }
            }
        }
        
        for (i = 0, ln = slices.length; i < ln; i++) {
            if (this.__excludes && this.__excludes[i]) {
                
                continue;
            }
            slice = slices[i];
            for (j = 0, rhoAcum = 0; j < layers; j++) {
                sprite = group.getAt(i * layers + j);
                deltaRho = lenField[j] ? store.getAt(i).get(lenField[j]) / layerTotals[i] * slice.rho: slice.rho;
                
                rendererAttributes = Ext.apply({
                    segment: {
                        startAngle: slice.startAngle,
                        endAngle: slice.endAngle,
                        margin: 0,
                        rho: slice.rho,
                        startRho: rhoAcum + (deltaRho * donut / 100),
                        endRho: rhoAcum + deltaRho
                    } 
                }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
                item = Ext.apply({},
                rendererAttributes.segment, {
                    slice: slice,
                    series: me,
                    storeItem: slice.storeItem,
                    index: i
                });
                me.calcMiddle(item);
                if (enableShadows) {
                    item.shadows = slice.shadowAttrs[j];
                }
                items[i] = item;
                
                if (!sprite) {
                    spriteOptions = Ext.apply({
                        type: "path",
                        group: group,
                        middle: item.middle
                    }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[(layers > 1? j : i) % colorArrayLength] } || {}));
                    sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
                }
                slice.sprite = slice.sprite || [];
                item.sprite = sprite;
                slice.sprite.push(sprite);
                slice.point = [item.middle.x, item.middle.y];
                if (animate) {
                    rendererAttributes = me.renderer(sprite, store.getAt(i), rendererAttributes, i, store);
                    sprite._to = rendererAttributes;
                    sprite._animating = true;
                    me.onAnimate(sprite, {
                        to: rendererAttributes,
                        listeners: {
                            afteranimate: {
                                fn: function() {
                                    this._animating = false;
                                },
                                scope: sprite
                            }
                        }
                    });
                } else {
                    rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply(rendererAttributes, {
                        hidden: false
                    }), i, store);
                    sprite.setAttributes(rendererAttributes, true);
                }
                rhoAcum += deltaRho;
            }
        }
        
        
        ln = group.getCount();
        for (i = 0; i < ln; i++) {
            if (!slices[(i / layers) >> 0] && group.getAt(i)) {
                group.getAt(i).hide(true);
            }
        }
        if (enableShadows) {
            lnsh = shadowGroups.length;
            for (shindex = 0; shindex < ln; shindex++) {
                if (!slices[(shindex / layers) >> 0]) {
                    for (j = 0; j < lnsh; j++) {
                        if (shadowGroups[j].getAt(shindex)) {
                            shadowGroups[j].getAt(shindex).hide(true);
                        }
                    }
                }
            }
        }
        me.renderLabels();
        me.renderCallouts();
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config || {});
        
        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': middle.x,
            'y': middle.y
        }, endLabelStyle));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = [].concat(config.field),
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            opt = {
                x: middle.x,
                y: middle.y
            },
            x = middle.x - centerX,
            y = middle.y - centerY,
            from = {},
            rho = 1,
            theta = Math.atan2(y, x || 1),
            dg = theta * 180 / Math.PI,
            prevDg;
        
        function fixAngle(a) {
            if (a < 0) a += 360;
            return a % 360;
        }

        label.setAttributes({
            text: format(storeItem.get(field[index]))
        }, true);

        switch (display) {
        case 'outside':
            rho = Math.sqrt(x * x + y * y) * 2;
            
            opt.x = rho * Math.cos(theta) + centerX;
            opt.y = rho * Math.sin(theta) + centerY;
            break;

        case 'rotate':
            dg = fixAngle(dg);
            dg = (dg > 90 && dg < 270) ? dg + 180: dg;

            prevDg = label.attr.rotation.degrees;
            if (prevDg != null && Math.abs(prevDg - dg) > 180) {
                if (dg > prevDg) {
                    dg -= 360;
                } else {
                    dg += 360;
                }
                dg = dg % 360;
            } else {
                dg = fixAngle(dg);
            }
            
            opt.rotate = {
                degrees: dg,
                x: opt.x,
                y: opt.y
            };
            break;

        default:
            break;
        }
        
        opt.translate = {
            x: 0, y: 0    
        };
        if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
            me.onAnimate(label, {
                to: opt
            });
        } else {
            label.setAttributes(opt, true);
        }
        label._from = from;
    },

    
    onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.callouts,
            centerX = me.centerX,
            centerY = me.centerY,
            middle = item.middle,
            opt = {
                x: middle.x,
                y: middle.y
            },
            x = middle.x - centerX,
            y = middle.y - centerY,
            rho = 1,
            rhoCenter,
            theta = Math.atan2(y, x || 1),
            bbox = callout.label.getBBox(),
            offsetFromViz = 20,
            offsetToSide = 10,
            offsetBox = 10,
            p;

        
        rho = item.endRho + offsetFromViz;
        rhoCenter = (item.endRho + item.startRho) / 2 + (item.endRho - item.startRho) / 3;
        
        opt.x = rho * Math.cos(theta) + centerX;
        opt.y = rho * Math.sin(theta) + centerY;

        x = rhoCenter * Math.cos(theta);
        y = rhoCenter * Math.sin(theta);

        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"]
                }
            });
            
            me.onAnimate(callout.box, {
                to: {
                    x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)),
                    y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)),
                    width: bbox.width + 2 * offsetBox,
                    height: bbox.height + 2 * offsetBox
                }
            });
            
            me.onAnimate(callout.label, {
                to: {
                    x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)),
                    y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4)
                }
            });
        } else {
            
            callout.lines.setAttributes({
                path: ["M", x + centerX, y + centerY, "L", opt.x, opt.y, "Z", "M", opt.x, opt.y, "l", x > 0 ? offsetToSide: -offsetToSide, 0, "z"]
            },
            true);
            
            callout.box.setAttributes({
                x: opt.x + (x > 0 ? offsetToSide: -(offsetToSide + bbox.width + 2 * offsetBox)),
                y: opt.y + (y > 0 ? ( - bbox.height - offsetBox / 2) : ( - bbox.height - offsetBox / 2)),
                width: bbox.width + 2 * offsetBox,
                height: bbox.height + 2 * offsetBox
            },
            true);
            
            callout.label.setAttributes({
                x: opt.x + (x > 0 ? (offsetToSide + offsetBox) : -(offsetToSide + bbox.width + offsetBox)),
                y: opt.y + (y > 0 ? -bbox.height / 4: -bbox.height / 4)
            },
            true);
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item, i) {
        var me = this,
            cx = me.centerX,
            cy = me.centerY,
            abs = Math.abs,
            dx = abs(x - cx),
            dy = abs(y - cy),
            startAngle = item.startAngle,
            endAngle = item.endAngle,
            rho = Math.sqrt(dx * dx + dy * dy),
            angle = Math.atan2(y - cy, x - cx) / me.rad + 360;
        
        
        if (angle > me.firstAngle) {
            angle -= 360;
        }
        return (angle <= startAngle && angle > endAngle
                && rho >= item.startRho && rho <= item.endRho);
    },
    
    
    hideAll: function() {
        var i, l, shadow, shadows, sh, lsh, sprite;
        if (!isNaN(this._index)) {
            this.__excludes = this.__excludes || [];
            this.__excludes[this._index] = true;
            sprite = this.slices[this._index].sprite;
            for (sh = 0, lsh = sprite.length; sh < lsh; sh++) {
                sprite[sh].setAttributes({
                    hidden: true
                }, true);
            }
            if (this.slices[this._index].shadowAttrs) {
                for (i = 0, shadows = this.slices[this._index].shadowAttrs, l = shadows.length; i < l; i++) {
                    shadow = shadows[i];
                    for (sh = 0, lsh = shadow.length; sh < lsh; sh++) {
                        shadow[sh].setAttributes({
                            hidden: true
                        }, true);
                    }
                }
            }
            this.drawSeries();
        }
    },
    
    
    showAll: function() {
        if (!isNaN(this._index)) {
            this.__excludes[this._index] = false;
            this.drawSeries();
        }
    },

    
    highlightItem: function(item) {
        var me = this,
            rad = me.rad;
        item = item || this.items[this._index];
        
        
        
        
        
        this.unHighlightItem();
        
        if (!item || item.sprite && item.sprite._animating) {
            return;
        }
        me.callParent([item]);
        if (!me.highlight) {
            return;
        }
        if ('segment' in me.highlightCfg) {
            var highlightSegment = me.highlightCfg.segment,
                animate = me.chart.animate,
                attrs, i, shadows, shadow, ln, to, itemHighlightSegment, prop;
            
            if (me.labelsGroup) {
                var group = me.labelsGroup,
                    display = me.label.display,
                    label = group.getAt(item.index),
                    middle = (item.startAngle + item.endAngle) / 2 * rad,
                    r = highlightSegment.margin || 0,
                    x = r * Math.cos(middle),
                    y = r * Math.sin(middle);

                
                
                
                
                
                if (Math.abs(x) < 1e-10) {
                    x = 0;
                }
                if (Math.abs(y) < 1e-10) {
                    y = 0;
                }
                
                if (animate) {
                    label.stopAnimation();
                    label.animate({
                        to: {
                            translate: {
                                x: x,
                                y: y
                            }
                        },
                        duration: me.highlightDuration
                    });
                }
                else {
                    label.setAttributes({
                        translate: {
                            x: x,
                            y: y
                        }
                    }, true);
                }
            }
            
            if (me.chart.shadow && item.shadows) {
                i = 0;
                shadows = item.shadows;
                ln = shadows.length;
                for (; i < ln; i++) {
                    shadow = shadows[i];
                    to = {};
                    itemHighlightSegment = item.sprite._from.segment;
                    for (prop in itemHighlightSegment) {
                        if (! (prop in highlightSegment)) {
                            to[prop] = itemHighlightSegment[prop];
                        }
                    }
                    attrs = {
                        segment: Ext.applyIf(to, me.highlightCfg.segment)
                    };
                    if (animate) {
                        shadow.stopAnimation();
                        shadow.animate({
                            to: attrs,
                            duration: me.highlightDuration
                        });
                    }
                    else {
                        shadow.setAttributes(attrs, true);
                    }
                }
            }
        }
    },

    
    unHighlightItem: function() {
        var me = this;
        if (!me.highlight) {
            return;
        }

        if (('segment' in me.highlightCfg) && me.items) {
            var items = me.items,
                animate = me.chart.animate,
                shadowsEnabled = !!me.chart.shadow,
                group = me.labelsGroup,
                len = items.length,
                i = 0,
                j = 0,
                display = me.label.display,
                shadowLen, p, to, ihs, hs, sprite, shadows, shadow, item, label, attrs;

            for (; i < len; i++) {
                item = items[i];
                if (!item) {
                    continue;
                }
                sprite = item.sprite;
                if (sprite && sprite._highlighted) {
                    
                    if (group) {
                        label = group.getAt(item.index);
                        attrs = Ext.apply({
                            translate: {
                                x: 0,
                                y: 0
                            }
                        },
                        display == 'rotate' ? {
                            rotate: {
                                x: label.attr.x,
                                y: label.attr.y,
                                degrees: label.attr.rotation.degrees
                            }
                        }: {});
                        if (animate) {
                            label.stopAnimation();
                            label.animate({
                                to: attrs,
                                duration: me.highlightDuration
                            });
                        }
                        else {
                            label.setAttributes(attrs, true);
                        }
                    }
                    if (shadowsEnabled) {
                        shadows = item.shadows;
                        shadowLen = shadows.length;
                        for (; j < shadowLen; j++) {
                            to = {};
                            ihs = item.sprite._to.segment;
                            hs = item.sprite._from.segment;
                            Ext.apply(to, hs);
                            for (p in ihs) {
                                if (! (p in hs)) {
                                    to[p] = ihs[p];
                                }
                            }
                            shadow = shadows[j];
                            if (animate) {
                                shadow.stopAnimation();
                                shadow.animate({
                                    to: {
                                        segment: to
                                    },
                                    duration: me.highlightDuration
                                });
                            }
                            else {
                                shadow.setAttributes({ segment: to }, true);
                            }
                        }
                    }
                }
            }
        }
        me.callParent(arguments);
    },
    
    
    getLegendColor: function(index) {
        var me = this;
        return me.colorArrayStyle[index % me.colorArrayStyle.length];
    }
});



Ext.define('Ext.chart.series.Radar', {

    

    extend: 'Ext.chart.series.Series',

    requires: ['Ext.chart.Shape', 'Ext.fx.Anim'],

    

    type: "radar",
    alias: 'series.radar',

    
    rad: Math.PI / 180,

    showInLegend: false,

    
    style: {},
    
    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            surface = me.chart.surface, i, l;
        me.group = surface.getGroup(me.seriesId);
        if (me.showMarkers) {
            me.markerGroup = surface.getGroup(me.seriesId + '-markers');
        }
    },

    
    drawSeries: function() {
        var me = this,
            store = me.chart.substore || me.chart.store,
            group = me.group,
            sprite,
            chart = me.chart,
            animate = chart.animate,
            field = me.field || me.yField,
            surface = chart.surface,
            chartBBox = chart.chartBBox,
            rendererAttributes,
            centerX, centerY,
            items,
            radius,
            maxValue = 0,
            fields = [],
            max = Math.max,
            cos = Math.cos,
            sin = Math.sin,
            pi2 = Math.PI * 2,
            l = store.getCount(),
            startPath, path, x, y, rho,
            i, nfields,
            seriesStyle = me.seriesStyle,
            seriesLabelStyle = me.seriesLabelStyle,
            first = chart.resizing || !me.radar,
            axis = chart.axes && chart.axes.get(0),
            aggregate = !(axis && axis.maximum);
        
        me.setBBox();

        maxValue = aggregate? 0 : (axis.maximum || 0);
        
        Ext.apply(seriesStyle, me.style || {});
        
        
        if (!store || !store.getCount()) {
            return;
        }
        
        me.unHighlightItem();
        me.cleanHighlights();

        centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
        centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
        me.radius = radius = Math.min(chartBBox.width, chartBBox.height) /2;
        me.items = items = [];

        if (aggregate) {
            
            chart.series.each(function(series) {
                fields.push(series.yField);
            });
            
            store.each(function(record, i) {
                for (i = 0, nfields = fields.length; i < nfields; i++) {
                    maxValue = max(+record.get(fields[i]), maxValue);
                }
            });
        }
        
        maxValue = maxValue || 1;
        
        startPath = []; path = [];
        store.each(function(record, i) {
            rho = radius * record.get(field) / maxValue;
            x = rho * cos(i / l * pi2);
            y = rho * sin(i / l * pi2);
            if (i == 0) {
                path.push('M', x + centerX, y + centerY);
                startPath.push('M', 0.01 * x + centerX, 0.01 * y + centerY);
            } else {
                path.push('L', x + centerX, y + centerY);
                startPath.push('L', 0.01 * x + centerX, 0.01 * y + centerY);
            }
            items.push({
                sprite: false, 
                point: [centerX + x, centerY + y],
                series: me
            });
        });
        path.push('Z');
        
        if (!me.radar) {
            me.radar = surface.add(Ext.apply({
                type: 'path',
                group: group,
                path: startPath
            }, seriesStyle || {}));
        }
        
        if (chart.resizing) {
            me.radar.setAttributes({
                path: startPath
            }, true);
        }
        
        if (chart.animate) {
            me.onAnimate(me.radar, {
                to: Ext.apply({
                    path: path
                }, seriesStyle || {})
            });
        } else {
            me.radar.setAttributes(Ext.apply({
                path: path
            }, seriesStyle || {}), true);
        }
        
        if (me.showMarkers) {
            me.drawMarkers();
        }
        me.renderLabels();
        me.renderCallouts();
    },
    
    
    drawMarkers: function() {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            markerStyle = Ext.apply({}, me.markerStyle || {}),
            endMarkerStyle = Ext.apply(markerStyle, me.markerConfig),
            items = me.items, 
            type = endMarkerStyle.type,
            markerGroup = me.markerGroup,
            centerX = me.centerX,
            centerY = me.centerY,
            item, i, l, marker;
        
        delete endMarkerStyle.type;
        
        for (i = 0, l = items.length; i < l; i++) {
            item = items[i];
            marker = markerGroup.getAt(i);
            if (!marker) {
                marker = Ext.chart.Shape[type](surface, Ext.apply({
                    group: markerGroup,
                    x: 0,
                    y: 0,
                    translate: {
                        x: centerX,
                        y: centerY
                    }
                }, endMarkerStyle));
            }
            else {
                marker.show();
            }
            if (chart.resizing) {
                marker.setAttributes({
                    x: 0,
                    y: 0,
                    translate: {
                        x: centerX,
                        y: centerY
                    }
                }, true);
            }
            marker._to = {
                translate: {
                    x: item.point[0],
                    y: item.point[1]
                }
            };
            
            if (chart.animate) {
                me.onAnimate(marker, {
                    to: marker._to
                });
            }
            else {
                marker.setAttributes(Ext.apply(marker._to, endMarkerStyle || {}), true);
            }
        }
    },
    
    isItemInPoint: function(x, y, item) {
        var point,
            tolerance = 10,
            abs = Math.abs;
        point = item.point;
        return (abs(point[0] - x) <= tolerance &&
                abs(point[1] - y) <= tolerance);
    },

    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            centerX = me.centerX,
            centerY = me.centerY,
            point = item.point,
            endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config);
        
        return me.chart.surface.add(Ext.apply({
            'type': 'text',
            'text-anchor': 'middle',
            'group': group,
            'x': centerX,
            'y': centerY
        }, config || {}));
    },

    
    onPlaceLabel: function(label, storeItem, item, i, display, animate) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            centerX = me.centerX,
            centerY = me.centerY,
            opt = {
                x: item.point[0],
                y: item.point[1]
            },
            x = opt.x - centerX,
            y = opt.y - centerY;

        label.setAttributes({
            text: format(storeItem.get(field)),
            hidden: true
        },
        true);
        
        if (resizing) {
            label.setAttributes({
                x: centerX,
                y: centerY
            }, true);
        }
        
        if (animate) {
            label.show(true);
            me.onAnimate(label, {
                to: opt
            });
        } else {
            label.setAttributes(opt, true);
            label.show(true);
        }
    },

    
    toggleAll: function(show) {
        var me = this,
            i, ln, shadow, shadows;
        if (!show) {
            Ext.chart.series.Radar.superclass.hideAll.call(me);
        }
        else {
            Ext.chart.series.Radar.superclass.showAll.call(me);
        }
        if (me.radar) {
            me.radar.setAttributes({
                hidden: !show
            }, true);
            
            if (me.radar.shadows) {
                for (i = 0, shadows = me.radar.shadows, ln = shadows.length; i < ln; i++) {
                    shadow = shadows[i];
                    shadow.setAttributes({
                        hidden: !show
                    }, true);
                }
            }
        }
    },
    
    
    hideAll: function() {
        this.toggleAll(false);
        this.hideMarkers(0);
    },
    
    
    showAll: function() {
        this.toggleAll(true);
    },
    
    
    hideMarkers: function(index) {
        var me = this,
            count = me.markerGroup && me.markerGroup.getCount() || 0,
            i = index || 0;
        for (; i < count; i++) {
            me.markerGroup.getAt(i).hide(true);
        }
    }
});



Ext.define('Ext.chart.series.Scatter', {

    

    extend: 'Ext.chart.series.Cartesian',

    requires: ['Ext.chart.axis.Axis', 'Ext.chart.Shape', 'Ext.fx.Anim'],

    

    type: 'scatter',
    alias: 'series.scatter',

    
    
    

    constructor: function(config) {
        this.callParent(arguments);
        var me = this,
            shadow = me.chart.shadow,
            surface = me.chart.surface, i, l;
        Ext.apply(me, config, {
            style: {},
            markerConfig: {},
            shadowAttributes: [{
                "stroke-width": 6,
                "stroke-opacity": 0.05,
                stroke: 'rgb(0, 0, 0)'
            }, {
                "stroke-width": 4,
                "stroke-opacity": 0.1,
                stroke: 'rgb(0, 0, 0)'
            }, {
                "stroke-width": 2,
                "stroke-opacity": 0.15,
                stroke: 'rgb(0, 0, 0)'
            }]
        });
        me.group = surface.getGroup(me.seriesId);
        if (shadow) {
            for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
                me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
            }
        }
    },

    
    getBounds: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            axes = [].concat(me.axis),
            bbox, xScale, yScale, ln, minX, minY, maxX, maxY, i, axis, ends;

        me.setBBox();
        bbox = me.bbox;

        for (i = 0, ln = axes.length; i < ln; i++) { 
            axis = chart.axes.get(axes[i]);
            if (axis) {
                ends = axis.calcEnds();
                if (axis.position == 'top' || axis.position == 'bottom') {
                    minX = ends.from;
                    maxX = ends.to;
                }
                else {
                    minY = ends.from;
                    maxY = ends.to;
                }
            }
        }
        
        if (me.xField && !Ext.isNumber(minX)) {
            axis = Ext.create('Ext.chart.axis.Axis', {
                chart: chart,
                fields: [].concat(me.xField)
            }).calcEnds();
            minX = axis.from;
            maxX = axis.to;
        }
        if (me.yField && !Ext.isNumber(minY)) {
            axis = Ext.create('Ext.chart.axis.Axis', {
                chart: chart,
                fields: [].concat(me.yField)
            }).calcEnds();
            minY = axis.from;
            maxY = axis.to;
        }

        if (isNaN(minX)) {
            minX = 0;
            maxX = store.getCount() - 1;
            xScale = bbox.width / (store.getCount() - 1);
        }
        else {
            xScale = bbox.width / (maxX - minX);
        }

        if (isNaN(minY)) {
            minY = 0;
            maxY = store.getCount() - 1;
            yScale = bbox.height / (store.getCount() - 1);
        } 
        else {
            yScale = bbox.height / (maxY - minY);
        }

        return {
            bbox: bbox,
            minX: minX,
            minY: minY,
            xScale: xScale,
            yScale: yScale
        };
    },

    
    getPaths: function() {
        var me = this,
            chart = me.chart,
            enableShadows = chart.shadow,
            store = chart.substore || chart.store,
            group = me.group,
            bounds = me.bounds = me.getBounds(),
            bbox = me.bbox,
            xScale = bounds.xScale,
            yScale = bounds.yScale,
            minX = bounds.minX,
            minY = bounds.minY,
            boxX = bbox.x,
            boxY = bbox.y,
            boxHeight = bbox.height,
            items = me.items = [],
            attrs = [],
            x, y, xValue, yValue, sprite;

        store.each(function(record, i) {
            xValue = record.get(me.xField);
            yValue = record.get(me.yField);
            
            if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)) {
                if (Ext.isDefined(Ext.global.console)) {
                    Ext.global.console.warn("[Ext.chart.series.Scatter]  Skipping a store element with an undefined value at ", record, xValue, yValue);
                }
                return;
            }
            
            if (typeof xValue == 'string' || typeof xValue == 'object') {
                xValue = i;
            }
            if (typeof yValue == 'string' || typeof yValue == 'object') {
                yValue = i;
            }
            x = boxX + (xValue - minX) * xScale;
            y = boxY + boxHeight - (yValue - minY) * yScale;
            attrs.push({
                x: x,
                y: y
            });

            me.items.push({
                series: me,
                value: [xValue, yValue],
                point: [x, y],
                storeItem: record
            });

            
            if (chart.animate && chart.resizing) {
                sprite = group.getAt(i);
                if (sprite) {
                    me.resetPoint(sprite);
                    if (enableShadows) {
                        me.resetShadow(sprite);
                    }
                }
            }
        });
        return attrs;
    },

    
    resetPoint: function(sprite) {
        var bbox = this.bbox;
        sprite.setAttributes({
            translate: {
                x: (bbox.x + bbox.width) / 2,
                y: (bbox.y + bbox.height) / 2
            }
        }, true);
    },

    
    resetShadow: function(sprite) {
        var me = this,
            shadows = sprite.shadows,
            shadowAttributes = me.shadowAttributes,
            ln = me.shadowGroups.length,
            bbox = me.bbox,
            i, attr;
        for (i = 0; i < ln; i++) {
            attr = Ext.apply({}, shadowAttributes[i]);
            if (attr.translate) {
                attr.translate.x += (bbox.x + bbox.width) / 2;
                attr.translate.y += (bbox.y + bbox.height) / 2;
            }
            else {
                attr.translate = {
                    x: (bbox.x + bbox.width) / 2,
                    y: (bbox.y + bbox.height) / 2
                };
            }
            shadows[i].setAttributes(attr, true);
        }
    },

    
    createPoint: function(attr, type) {
        var me = this,
            chart = me.chart,
            group = me.group,
            bbox = me.bbox;

        return Ext.chart.Shape[type](chart.surface, Ext.apply({}, {
            x: 0,
            y: 0,
            group: group,
            translate: {
                x: (bbox.x + bbox.width) / 2,
                y: (bbox.y + bbox.height) / 2
            }
        }, attr));
    },

    
    createShadow: function(sprite, endMarkerStyle, type) {
        var me = this,
            chart = me.chart,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            bbox = me.bbox,
            i, shadow, shadows, attr;

        sprite.shadows = shadows = [];

        for (i = 0; i < lnsh; i++) {
            attr = Ext.apply({}, shadowAttributes[i]);
            if (attr.translate) {
                attr.translate.x += (bbox.x + bbox.width) / 2;
                attr.translate.y += (bbox.y + bbox.height) / 2;
            }
            else {
                Ext.apply(attr, {
                    translate: {
                        x: (bbox.x + bbox.width) / 2,
                        y: (bbox.y + bbox.height) / 2
                    }
                });
            }
            Ext.apply(attr, endMarkerStyle);
            shadow = Ext.chart.Shape[type](chart.surface, Ext.apply({}, {
                x: 0,
                y: 0,
                group: shadowGroups[i]
            }, attr));
            shadows.push(shadow);
        }
    },

    
    drawSeries: function() {
        var me = this,
            chart = me.chart,
            store = chart.substore || chart.store,
            group = me.group,
            enableShadows = chart.shadow,
            shadowGroups = me.shadowGroups,
            shadowAttributes = me.shadowAttributes,
            lnsh = shadowGroups.length,
            sprite, attrs, attr, ln, i, endMarkerStyle, shindex, type, shadows,
            rendererAttributes, shadowAttribute;

        endMarkerStyle = Ext.apply(me.markerStyle, me.markerConfig);
        type = endMarkerStyle.type;
        delete endMarkerStyle.type;

        
        if (!store || !store.getCount()) {
            return;
        }

        me.unHighlightItem();
        me.cleanHighlights();

        attrs = me.getPaths();
        ln = attrs.length;
        for (i = 0; i < ln; i++) {
            attr = attrs[i];
            sprite = group.getAt(i);
            Ext.apply(attr, endMarkerStyle);

            
            if (!sprite) {
                sprite = me.createPoint(attr, type);
                if (enableShadows) {
                    me.createShadow(sprite, endMarkerStyle, type);
                }
            }

            shadows = sprite.shadows;
            if (chart.animate) {
                rendererAttributes = me.renderer(sprite, store.getAt(i), { translate: attr }, i, store);
                sprite._to = rendererAttributes;
                me.onAnimate(sprite, {
                    to: rendererAttributes
                });
                
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowAttribute = Ext.apply({}, shadowAttributes[shindex]);
                    rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({}, { 
                        translate: {
                            x: attr.x + (shadowAttribute.translate? shadowAttribute.translate.x : 0),
                            y: attr.y + (shadowAttribute.translate? shadowAttribute.translate.y : 0)
                        } 
                    }, shadowAttribute), i, store);
                    me.onAnimate(shadows[shindex], { to: rendererAttributes });
                }
            }
            else {
                rendererAttributes = me.renderer(sprite, store.getAt(i), Ext.apply({ translate: attr }, { hidden: false }), i, store);
                sprite.setAttributes(rendererAttributes, true);
                
                for (shindex = 0; shindex < lnsh; shindex++) {
                    shadowAttribute = shadowAttributes[shindex];
                    rendererAttributes = me.renderer(shadows[shindex], store.getAt(i), Ext.apply({ 
                        x: attr.x,
                        y: attr.y
                    }, shadowAttribute), i, store);
                    shadows[shindex].setAttributes(rendererAttributes, true);
                }
            }
            me.items[i].sprite = sprite;
        }

        
        ln = group.getCount();
        for (i = attrs.length; i < ln; i++) {
            group.getAt(i).hide(true);
        }
        me.renderLabels();
        me.renderCallouts();
    },
    
    
    onCreateLabel: function(storeItem, item, i, display) {
        var me = this,
            group = me.labelsGroup,
            config = me.label,
            endLabelStyle = Ext.apply({}, config, me.seriesLabelStyle),
            bbox = me.bbox;
        
        return me.chart.surface.add(Ext.apply({
            type: 'text',
            group: group,
            x: item.point[0],
            y: bbox.y + bbox.height / 2
        }, endLabelStyle));
    },
    
    
    onPlaceLabel: function(label, storeItem, item, i, display, animate) {
        var me = this,
            chart = me.chart,
            resizing = chart.resizing,
            config = me.label,
            format = config.renderer,
            field = config.field,
            bbox = me.bbox,
            x = item.point[0],
            y = item.point[1],
            radius = item.sprite.attr.radius,
            bb, width, height, anim;
        
        label.setAttributes({
            text: format(storeItem.get(field)),
            hidden: true
        }, true);
        
        if (display == 'rotate') {
            label.setAttributes({
                'text-anchor': 'start',
                'rotation': {
                    x: x,
                    y: y,
                    degrees: -45
                }
            }, true);
            
            bb = label.getBBox();
            width = bb.width;
            height = bb.height;
            x = x < bbox.x? bbox.x : x;
            x = (x + width > bbox.x + bbox.width)? (x - (x + width - bbox.x - bbox.width)) : x;
            y = (y - height < bbox.y)? bbox.y + height : y;
        
        } else if (display == 'under' || display == 'over') {
            
            bb = item.sprite.getBBox();
            bb.width = bb.width || (radius * 2);
            bb.height = bb.height || (radius * 2);
            y = y + (display == 'over'? -bb.height : bb.height);
            
            bb = label.getBBox();
            width = bb.width/2;
            height = bb.height/2;
            x = x - width < bbox.x ? bbox.x + width : x;
            x = (x + width > bbox.x + bbox.width) ? (x - (x + width - bbox.x - bbox.width)) : x;
            y = y - height < bbox.y? bbox.y + height : y;
            y = (y + height > bbox.y + bbox.height) ? (y - (y + height - bbox.y - bbox.height)) : y;
        }

        if (!chart.animate) {
            label.setAttributes({
                x: x,
                y: y
            }, true);
            label.show(true);
        }
        else {
            if (resizing) {
                anim = item.sprite.getActiveAnimation();
                if (anim) {
                    anim.on('afteranimate', function() {
                        label.setAttributes({
                            x: x,
                            y: y
                        }, true);
                        label.show(true);
                    });   
                }
                else {
                    label.show(true);
                }
            }
            else {
                me.onAnimate(label, {
                    to: {
                        x: x,
                        y: y
                    }
                });
            }
        }
    },
    
    
    onPlaceCallout: function(callout, storeItem, item, i, display, animate, index) {
        var me = this,
            chart = me.chart,
            surface = chart.surface,
            resizing = chart.resizing,
            config = me.callouts,
            items = me.items,
            cur = item.point,
            normal,
            bbox = callout.label.getBBox(),
            offsetFromViz = 30,
            offsetToSide = 10,
            offsetBox = 3,
            boxx, boxy, boxw, boxh,
            p, clipRect = me.bbox,
            x, y;
    
        
        normal = [Math.cos(Math.PI /4), -Math.sin(Math.PI /4)];
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;
        
        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;
        
        
        
        if (boxx < clipRect[0] || (boxx + boxw) > (clipRect[0] + clipRect[2])) {
            normal[0] *= -1;
        }
        if (boxy < clipRect[1] || (boxy + boxh) > (clipRect[1] + clipRect[3])) {
            normal[1] *= -1;
        }
    
        
        x = cur[0] + normal[0] * offsetFromViz;
        y = cur[1] + normal[1] * offsetFromViz;
        
        
        boxx = x + (normal[0] > 0? 0 : -(bbox.width + 2 * offsetBox));
        boxy = y - bbox.height /2 - offsetBox;
        boxw = bbox.width + 2 * offsetBox;
        boxh = bbox.height + 2 * offsetBox;
        
        if (chart.animate) {
            
            me.onAnimate(callout.lines, {
                to: {
                    path: ["M", cur[0], cur[1], "L", x, y, "Z"]
                }
            }, true);
            
            me.onAnimate(callout.box, {
                to: {
                    x: boxx,
                    y: boxy,
                    width: boxw,
                    height: boxh
                }
            }, true);
            
            me.onAnimate(callout.label, {
                to: {
                    x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
                    y: y
                }
            }, true);
        } else {
            
            callout.lines.setAttributes({
                path: ["M", cur[0], cur[1], "L", x, y, "Z"]
            }, true);
            
            callout.box.setAttributes({
                x: boxx,
                y: boxy,
                width: boxw,
                height: boxh
            }, true);
            
            callout.label.setAttributes({
                x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
                y: y
            }, true);
        }
        for (p in callout) {
            callout[p].show(true);
        }
    },

    
    onAnimate: function(sprite, attr) {
        sprite.show();
        return this.callParent(arguments);
    },

    isItemInPoint: function(x, y, item) {
        var point,
            tolerance = 10,
            abs = Math.abs;

        function dist(point) {
            var dx = abs(point[0] - x),
                dy = abs(point[1] - y);
            return Math.sqrt(dx * dx + dy * dy);
        }
        point = item.point;
        return (point[0] - tolerance <= x && point[0] + tolerance >= x &&
            point[1] - tolerance <= y && point[1] + tolerance >= y);
    }
});



Ext.define('Ext.chart.theme.Base', {

    

    requires: ['Ext.chart.theme.Theme'],

    

    constructor: function(config) {
        Ext.chart.theme.call(this, config, {
            background: false,
            axis: {
                stroke: '#444',
                'stroke-width': 1
            },
            axisLabelTop: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: function(v) { return v; }
            },
            axisLabelRight: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: function(v) { return v; }
            },
            axisLabelBottom: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: function(v) { return v; }
            },
            axisLabelLeft: {
                fill: '#444',
                font: '12px Arial, Helvetica, sans-serif',
                spacing: 2,
                padding: 5,
                renderer: function(v) { return v; }
            },
            axisTitleTop: {
                font: 'bold 18px Arial',
                fill: '#444'
            },
            axisTitleRight: {
                font: 'bold 18px Arial',
                fill: '#444',
                rotate: {
                    x:0, y:0,
                    degrees: 270
                }
            },
            axisTitleBottom: {
                font: 'bold 18px Arial',
                fill: '#444'
            },
            axisTitleLeft: {
                font: 'bold 18px Arial',
                fill: '#444',
                rotate: {
                    x:0, y:0,
                    degrees: 270
                }
            },
            series: {
                'stroke-width': 0
            },
            seriesLabel: {
                font: '12px Arial',
                fill: '#333'
            },
            marker: {
                stroke: '#555',
                fill: '#000',
                radius: 3,
                size: 3
            },
            colors: [ "#94ae0a", "#115fa6","#a61120", "#ff8809", "#ffd13e", "#a61187", "#24ad9a", "#7c7474", "#a66111"],
            seriesThemes: [{
                fill: "#115fa6"
            }, {
                fill: "#94ae0a"
            }, {
                fill: "#a61120"
            }, {
                fill: "#ff8809"
            }, {
                fill: "#ffd13e"
            }, {
                fill: "#a61187"
            }, {
                fill: "#24ad9a"
            }, {
                fill: "#7c7474"
            }, {
                fill: "#a66111"
            }],
            markerThemes: [{
                fill: "#115fa6",
                type: 'circle' 
            }, {
                fill: "#94ae0a",
                type: 'cross'
            }, {
                fill: "#a61120",
                type: 'plus'
            }]
        });
    }
}, function() {
    var palette = ['#b1da5a', '#4ce0e7', '#e84b67', '#da5abd', '#4d7fe6', '#fec935'],
        names = ['Green', 'Sky', 'Red', 'Purple', 'Blue', 'Yellow'],
        i = 0, j = 0, l = palette.length, themes = Ext.chart.theme,
        categories = [['#f0a50a', '#c20024', '#2044ba', '#810065', '#7eae29'],
                      ['#6d9824', '#87146e', '#2a9196', '#d39006', '#1e40ac'],
                      ['#fbbc29', '#ce2e4e', '#7e0062', '#158b90', '#57880e'],
                      ['#ef5773', '#fcbd2a', '#4f770d', '#1d3eaa', '#9b001f'],
                      ['#7eae29', '#fdbe2a', '#910019', '#27b4bc', '#d74dbc'],
                      ['#44dce1', '#0b2592', '#996e05', '#7fb325', '#b821a1']],
        cats = categories.length;
    
    
    for (; i < l; i++) {
        themes[names[i]] = (function(color) {
            return Ext.extend(themes.Base, {
                constructor: function(config) {
                    themes.Base.prototype.constructor.call(this, Ext.apply({
                        baseColor: color
                    }, config));
                }
            });
        })(palette[i]);
    }
    
    
    for (i = 0; i < cats; i++) {
        themes['Category' + (i + 1)] = (function(category) {
            return Ext.extend(themes.Base, {
                constructor: function(config) {
                    themes.Base.prototype.constructor.call(this, Ext.apply({
                        colors: category
                    }, config));
                }
            });
        })(categories[i]);
    }
});


Ext.define('Ext.data.ArrayStore', {
    extend: 'Ext.data.Store',
    alias: 'store.array',
    uses: ['Ext.data.reader.Array'],

    
    constructor: function(config) {
        config = config || {};

        Ext.applyIf(config, {
            proxy: {
                type: 'memory',
                reader: 'array'
            }
        });

        this.callParent([config]);
    },

    loadData: function(data, append) {
        if (this.expandData === true) {
            var r = [],
                i = 0,
                ln = data.length;

            for (; i < ln; i++) {
                r[r.length] = [data[i]];
            }

            data = r;
        }

        this.callParent([data, append]);
    }
}, function() {
    
    Ext.data.SimpleStore = Ext.data.ArrayStore;
    
});


Ext.define('Ext.data.Batch', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    
    autoStart: false,
    
    
    current: -1,
    
    
    total: 0,
    
    
    isRunning: false,
    
    
    isComplete: false,
    
    
    hasException: false,
    
    
    pauseOnException: true,
    
    constructor: function(config) {   
        var me = this;
                     
        me.addEvents(
          
          'complete',
          
          
          'exception',
          
          
          'operationcomplete'
        );
        
        me.mixins.observable.constructor.call(me, config);
        
        
        me.operations = [];
    },
    
    
    add: function(operation) {
        this.total++;
        
        operation.setBatch(this);
        
        this.operations.push(operation);
    },
    
    
    start: function() {
        this.hasException = false;
        this.isRunning = true;
        
        this.runNextOperation();
    },
    
    
    runNextOperation: function() {
        this.runOperation(this.current + 1);
    },
    
    
    pause: function() {
        this.isRunning = false;
    },
    
    
    runOperation: function(index) {
        var me = this,
            operations = me.operations,
            operation  = operations[index],
            onProxyReturn;
        
        if (operation === undefined) {
            me.isRunning  = false;
            me.isComplete = true;
            me.fireEvent('complete', me, operations[operations.length - 1]);
        } else {
            me.current = index;
            
            onProxyReturn = function(operation) {
                var hasException = operation.hasException();
                
                if (hasException) {
                    me.hasException = true;
                    me.fireEvent('exception', me, operation);
                } else {
                    me.fireEvent('operationcomplete', me, operation);
                }

                if (hasException && me.pauseOnException) {
                    me.pause();
                } else {
                    operation.setCompleted();
                    me.runNextOperation();
                }
            };
            
            operation.setStarted();
            
            me.proxy[operation.action](operation, onProxyReturn, me);
        }
    }
});

Ext.define('Ext.data.BelongsToAssociation', {
    extend: 'Ext.data.Association',

    alias: 'association.belongsto',

    

    

    
    
    

    constructor: function(config) {
        this.callParent(arguments);

        var me             = this,
            ownerProto     = me.ownerModel.prototype,
            associatedName = me.associatedName,
            getterName     = me.getterName || 'get' + associatedName,
            setterName     = me.setterName || 'set' + associatedName;

        Ext.applyIf(me, {
            name        : associatedName,
            foreignKey  : associatedName.toLowerCase() + "_id",
            instanceName: associatedName + 'BelongsToInstance',
            associationKey: associatedName.toLowerCase()
        });

        ownerProto[getterName] = me.createGetter();
        ownerProto[setterName] = me.createSetter();
    },

    
    createSetter: function() {
        var me              = this,
            ownerModel      = me.ownerModel,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey;

        
        return function(value, options, scope) {
            this.set(foreignKey, value);

            if (typeof options == 'function') {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    
    createGetter: function() {
        var me              = this,
            ownerModel      = me.ownerModel,
            associatedName  = me.associatedName,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey,
            instanceName    = me.instanceName;

        
        return function(options, scope) {
            options = options || {};

            var foreignKeyId = this.get(foreignKey),
                instance, callbackFn;

            if (this[instanceName] === undefined) {
                instance = Ext.ModelManager.create({}, associatedName);
                instance.set(primaryKey, foreignKeyId);

                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || this
                    };
                }

                associatedModel.load(foreignKeyId, options);
            } else {
                instance = this[instanceName];

                
                
                
                if (typeof options == 'function') {
                    options.call(scope || this, instance);
                }

                if (options.success) {
                    options.success.call(scope || this, instance);
                }

                if (options.callback) {
                    options.callback.call(scope || this, instance);
                }

                return instance;
            }
        };
    },

    
    read: function(record, reader, associationData){
        record[this.instanceName] = reader.read([associationData]).records[0];
    }
});


Ext.define('Ext.data.BufferStore', {
    extend: 'Ext.data.Store',
    alias: 'store.buffer',
    sortOnLoad: false,
    filterOnLoad: false,
    
    constructor: function() {
        Ext.Error.raise('The BufferStore class has been deprecated. Instead, specify the buffered config option on Ext.data.Store');
    }
});


Ext.define('Ext.direct.Manager', {
    
    
    singleton: true,
   
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    requires: ['Ext.util.MixedCollection'],
    
    statics: {
        exceptions: {
            TRANSPORT: 'xhr',
            PARSE: 'parse',
            LOGIN: 'login',
            SERVER: 'exception'
        }
    },
    
    
   
    constructor: function(){
        var me = this;
       
        me.addEvents(
            
            'event',
            
            'exception'
        );
        me.transactions = Ext.create('Ext.util.MixedCollection');
        me.providers = Ext.create('Ext.util.MixedCollection');
        
        me.mixins.observable.constructor.call(me);
    },
    
    
    addProvider : function(provider){
        var me = this,
            args = arguments,
            i = 0,
            len;
            
        if (args.length > 1) {
            for (len = args.length; i < len; ++i) {
                me.addProvider(args[i]);
            }
            return;
        }

        
        if (!provider.isProvider) {
            provider = Ext.create('direct.' + provider.type + 'provider', provider);
        }
        me.providers.add(provider);
        provider.on('data', me.onProviderData, me);


        if (!provider.isConnected()) {
            provider.connect();
        }

        return provider;
    },
    
    
    getProvider : function(id){
        return id.isProvider ? id : this.providers.get(id);
    },
    
    
    removeProvider : function(provider){
        var me = this,
            providers = me.providers,
            provider = provider.isProvider ? provider : providers.get(provider);
            
        if (provider) {
            provider.un('data', me.onProviderData, me);
            providers.remove(provider);
            return provider;
        }
        return null;
    },
    
    
    addTransaction: function(transaction){
        this.transactions.add(transaction);
        return transaction;
    },

    
    removeTransaction: function(transaction){
        transaction = this.getTransaction(transaction);
        this.transactions.remove(transaction);
        return transaction;
    },

    
    getTransaction: function(transaction){
        return transaction.isTransaction ? transaction : this.transactions.get(transaction);
    },
    
    onProviderData : function(provider, event){
        var me = this,
            i = 0,
            len;
            
        if (Ext.isArray(event)) {
            for (len = event.length; i < len; ++i) {
                me.onProviderData(provider, event[i]);
            }
            return;
        }
        if (event.name && event.name != 'event' && event.name != 'exception') {
            me.fireEvent(event.name, event);
        } else if (event.type == 'exception') {
            me.fireEvent('exception', event);
        }
        me.fireEvent('event', event, provider);
    }
}, function(){
    
    Ext.Direct = Ext.direct.Manager;
});


Ext.define('Ext.data.proxy.Direct', {
    
    
    extend: 'Ext.data.proxy.Server',
    alternateClassName: 'Ext.data.DirectProxy',
    
    alias: 'proxy.direct',
    
    requires: ['Ext.direct.Manager'],
    
    
   
   
    paramOrder: undefined,

    
    paramsAsHash: true,

    
    directFn : undefined,
    
    
    
    
    
    
    paramOrderRe: /[\s,|]/,
    
    constructor: function(config){
        var me = this;
        
        Ext.apply(me, config);
        if (Ext.isString(me.paramOrder)) {
            me.paramOrder = me.paramOrder.split(me.paramOrderRe);
        }
        me.callParent(arguments);
    },
    
    doRequest: function(operation, callback, scope) {
        var me = this,
            writer = me.getWriter(),
            request = me.buildRequest(operation, callback, scope),
            fn = me.api[request.action]  || me.directFn,
            args = [],
            params = request.params,
            paramOrder = me.paramOrder,
            method,
            i = 0,
            len;
            
        if (!fn) {
            Ext.Error.raise('No direct function specified for this proxy');
        }
            
        if (operation.allowWrite()) {
            request = writer.write(request);
        }
        
        if (operation.action == 'read') {
            
            method = fn.directCfg.method;
            
            if (method.ordered) {
                if (method.len > 0) {
                    if (paramOrder) {
                        for (len = paramOrder.length; i < len; ++i) {
                            args.push(params[paramOrder[i]]);
                        }
                    } else if (me.paramsAsHash) {
                        args.push(params);
                    }
                }
            } else {
                args.push(params);
            }
        } else {
            args.push(request.jsonData);
        }
        
        Ext.apply(request, {
            args: args,
            directFn: fn
        });
        args.push(me.createRequestCallback(request, operation, callback, scope), me);
        fn.apply(window, args);
    },
    
    
    applyEncoding: function(value){
        return value;
    },
    
    createRequestCallback: function(request, operation, callback, scope){
        var me = this;
        
        return function(data, event){
            me.processResponse(event.status, operation, request, event, callback, scope);
        };
    },
    
    
    extractResponseData: function(response){
        return Ext.isDefined(response.result) ? response.result : response.data;
    },
    
    
    setException: function(operation, response) {
        operation.setException(response.message);
    },
    
    
    buildUrl: function(){
        return '';
    }
});



Ext.define('Ext.data.DirectStore', {
    
    
    extend: 'Ext.data.Store',
    
    alias: 'store.direct',
    
    requires: ['Ext.data.proxy.Direct'],
   
    
   
   constructor : function(config){
        config = Ext.apply({}, config);
        if (!config.proxy) {
            var proxy = {
                type: 'direct',
                reader: {
                    type: 'json'
                }
            };
            Ext.copyTo(proxy, config, 'paramOrder,paramsAsHash,directFn,api,simpleSortMode');
            Ext.copyTo(proxy.reader, config, 'totalProperty,root,idProperty');
            config.proxy = proxy;
        }
        this.callParent([config]);
    }    
});



Ext.define('Ext.util.Inflector', {

    

    singleton: true,

    

    
    plurals: [
        [(/(quiz)$/i),                "$1zes"  ],
        [(/^(ox)$/i),                 "$1en"   ],
        [(/([m|l])ouse$/i),           "$1ice"  ],
        [(/(matr|vert|ind)ix|ex$/i),  "$1ices" ],
        [(/(x|ch|ss|sh)$/i),          "$1es"   ],
        [(/([^aeiouy]|qu)y$/i),       "$1ies"  ],
        [(/(hive)$/i),                "$1s"    ],
        [(/(?:([^f])fe|([lr])f)$/i),  "$1$2ves"],
        [(/sis$/i),                   "ses"    ],
        [(/([ti])um$/i),              "$1a"    ],
        [(/(buffal|tomat|potat)o$/i), "$1oes"  ],
        [(/(bu)s$/i),                 "$1ses"  ],
        [(/(alias|status|sex)$/i),    "$1es"   ],
        [(/(octop|vir)us$/i),         "$1i"    ],
        [(/(ax|test)is$/i),           "$1es"   ],
        [(/^person$/),                "people" ],
        [(/^man$/),                   "men"    ],
        [(/^(child)$/),               "$1ren"  ],
        [(/s$/i),                     "s"      ],
        [(/$/),                       "s"      ]
    ],
    
    
    singulars: [
      [(/(quiz)zes$/i),                                                    "$1"     ],
      [(/(matr)ices$/i),                                                   "$1ix"   ],
      [(/(vert|ind)ices$/i),                                               "$1ex"   ],
      [(/^(ox)en/i),                                                       "$1"     ],
      [(/(alias|status)es$/i),                                             "$1"     ],
      [(/(octop|vir)i$/i),                                                 "$1us"   ],
      [(/(cris|ax|test)es$/i),                                             "$1is"   ],
      [(/(shoe)s$/i),                                                      "$1"     ],
      [(/(o)es$/i),                                                        "$1"     ],
      [(/(bus)es$/i),                                                      "$1"     ],
      [(/([m|l])ice$/i),                                                   "$1ouse" ],
      [(/(x|ch|ss|sh)es$/i),                                               "$1"     ],
      [(/(m)ovies$/i),                                                     "$1ovie" ],
      [(/(s)eries$/i),                                                     "$1eries"],
      [(/([^aeiouy]|qu)ies$/i),                                            "$1y"    ],
      [(/([lr])ves$/i),                                                    "$1f"    ],
      [(/(tive)s$/i),                                                      "$1"     ],
      [(/(hive)s$/i),                                                      "$1"     ],
      [(/([^f])ves$/i),                                                    "$1fe"   ],
      [(/(^analy)ses$/i),                                                  "$1sis"  ],
      [(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i), "$1$2sis"],
      [(/([ti])a$/i),                                                      "$1um"   ],
      [(/(n)ews$/i),                                                       "$1ews"  ],
      [(/people$/i),                                                       "person" ],
      [(/s$/i),                                                            ""       ]
    ],
    
    
     uncountable: [
        "sheep",
        "fish",
        "series",
        "species",
        "money",
        "rice",
        "information",
        "equipment",
        "grass",
        "mud",
        "offspring",
        "deer",
        "means"
    ],
    
    
    singular: function(matcher, replacer) {
        this.singulars.unshift([matcher, replacer]);
    },
    
    
    plural: function(matcher, replacer) {
        this.plurals.unshift([matcher, replacer]);
    },
    
    
    clearSingulars: function() {
        this.singulars = [];
    },
    
    
    clearPlurals: function() {
        this.plurals = [];
    },
    
    
    isTransnumeral: function(word) {
        return Ext.Array.indexOf(this.uncountable, word) != -1;
    },

    
    pluralize: function(word) {
        if (this.isTransnumeral(word)) {
            return word;
        }

        var plurals = this.plurals,
            length  = plurals.length,
            tuple, regex, i;
        
        for (i = 0; i < length; i++) {
            tuple = plurals[i];
            regex = tuple[0];
            
            if (regex == word || (regex.test && regex.test(word))) {
                return word.replace(regex, tuple[1]);
            }
        }
        
        return word;
    },
    
    
    singularize: function(word) {
        if (this.isTransnumeral(word)) {
            return word;
        }

        var singulars = this.singulars,
            length    = singulars.length,
            tuple, regex, i;
        
        for (i = 0; i < length; i++) {
            tuple = singulars[i];
            regex = tuple[0];
            
            if (regex == word || (regex.test && regex.test(word))) {
                return word.replace(regex, tuple[1]);
            }
        }
        
        return word;
    },
    
    
    classify: function(word) {
        return Ext.String.capitalize(this.singularize(word));
    },
    
    
    ordinalize: function(number) {
        var parsed = parseInt(number, 10),
            mod10  = parsed % 10,
            mod100 = parsed % 100;
        
        
        if (11 <= mod100 && mod100 <= 13) {
            return number + "th";
        } else {
            switch(mod10) {
                case 1 : return number + "st";
                case 2 : return number + "nd";
                case 3 : return number + "rd";
                default: return number + "th";
            }
        }
    }
}, function() {
    
    var irregulars = {
            alumnus: 'alumni',
            cactus : 'cacti',
            focus  : 'foci',
            nucleus: 'nuclei',
            radius: 'radii',
            stimulus: 'stimuli',
            ellipsis: 'ellipses',
            paralysis: 'paralyses',
            oasis: 'oases',
            appendix: 'appendices',
            index: 'indexes',
            beau: 'beaux',
            bureau: 'bureaux',
            tableau: 'tableaux',
            woman: 'women',
            child: 'children',
            man: 'men',
            corpus:	'corpora',
            criterion: 'criteria',
            curriculum:	'curricula',
            genus: 'genera',
            memorandum:	'memoranda',
            phenomenon:	'phenomena',
            foot: 'feet',
            goose: 'geese',
            tooth: 'teeth',
            antenna: 'antennae',
            formula: 'formulae',
            nebula: 'nebulae',
            vertebra: 'vertebrae',
            vita: 'vitae'
        },
        singular;
    
    for (singular in irregulars) {
        this.plural(singular, irregulars[singular]);
        this.singular(irregulars[singular], singular);
    }
});

Ext.define('Ext.data.HasManyAssociation', {
    extend: 'Ext.data.Association',
    requires: ['Ext.util.Inflector'],

    alias: 'association.hasmany',

    
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this,
            ownerProto,
            name;
            
        me.callParent(arguments);
        
        me.name = me.name || Ext.util.Inflector.pluralize(me.associatedName.toLowerCase());
        
        ownerProto = me.ownerModel.prototype;
        name = me.name;
        
        Ext.applyIf(me, {
            storeName : name + "Store",
            foreignKey: me.ownerName.toLowerCase() + "_id"
        });
        
        ownerProto[name] = me.createStore();
    },
    
    
    createStore: function() {
        var that            = this,
            associatedModel = that.associatedModel,
            storeName       = that.storeName,
            foreignKey      = that.foreignKey,
            primaryKey      = that.primaryKey,
            filterProperty  = that.filterProperty,
            autoLoad        = that.autoLoad,
            storeConfig     = that.storeConfig || {};
        
        return function() {
            var me = this,
                config, filter,
                modelDefaults = {};
                
            if (me[storeName] === undefined) {
                if (filterProperty) {
                    filter = {
                        property  : filterProperty,
                        value     : me.get(filterProperty),
                        exactMatch: true
                    };
                } else {
                    filter = {
                        property  : foreignKey,
                        value     : me.get(primaryKey),
                        exactMatch: true
                    };
                }
                
                modelDefaults[foreignKey] = me.get(primaryKey);
                
                config = Ext.apply({}, storeConfig, {
                    model        : associatedModel,
                    filters      : [filter],
                    remoteFilter : false,
                    modelDefaults: modelDefaults
                });
                
                me[storeName] = Ext.create('Ext.data.Store', config);
                if (autoLoad) {
                    me[storeName].load();
                }
            }
            
            return me[storeName];
        };
    },
    
    
    read: function(record, reader, associationData){
        var store = record[this.name](),
            inverse;
    
        store.add(reader.read(associationData).records);
    
        
        
        inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
            return assoc.type === 'belongsTo' && assoc.associatedName === record.$className;
        });
    
        
        if (inverse) {
            store.data.each(function(associatedRecord){
                associatedRecord[inverse.instanceName] = record;
            });
        }
    }
});

Ext.define('Ext.data.JsonP', {
    
    
    
    singleton: true,
    
    statics: {
        requestCount: 0,
        requests: {}
    },
    
    
    
    
    timeout: 30000,
    
    
    disableCaching: true,
   
    
    disableCachingParam: '_dc',
   
    
    callbackKey: 'callback',
   
    
    request: function(options){
        options = Ext.apply({}, options);
       
        if (!options.url) {
            Ext.Error.raise('A url must be specified for a JSONP request.');
        }
        
        var me = this, 
            disableCaching = Ext.isDefined(options.disableCaching) ? options.disableCaching : me.disableCaching, 
            cacheParam = options.disableCachingParam || me.disableCachingParam, 
            id = ++me.statics().requestCount, 
            callbackName = options.callbackName || 'callback' + id, 
            callbackKey = options.callbackKey || me.callbackKey, 
            timeout = Ext.isDefined(options.timeout) ? options.timeout : me.timeout, 
            params = Ext.apply({}, options.params), 
            url = options.url,
            request, 
            script;
            
        params[callbackKey] = 'Ext.data.JsonP.' + callbackName;
        if (disableCaching) {
            params[cacheParam] = new Date().getTime();
        }
        
        script = me.createScript(url, params);
        
        me.statics().requests[id] = request = {
            url: url,
            params: params,
            script: script,
            id: id,
            scope: options.scope,
            success: options.success,
            failure: options.failure,
            callback: options.callback,
            callbackName: callbackName
        };
        
        if (timeout > 0) {
            request.timeout = setTimeout(Ext.bind(me.handleTimeout, me, [request]), timeout);
        }
        
        me.setupErrorHandling(request);
        me[callbackName] = Ext.bind(me.handleResponse, me, [request], true);
        Ext.getHead().appendChild(script);
        return request;
    },
    
    
    abort: function(request){
        var requests = this.statics().requests,
            key;
            
        if (request) {
            if (!request.id) {
                request = requests[request];
            }
            this.abort(request);
        } else {
            for (key in requests) {
                if (requests.hasOwnProperty(key)) {
                    this.abort(requests[key]);
                }
            }
        }
    },
    
    
    setupErrorHandling: function(request){
        request.script.onerror = Ext.bind(this.handleError, this, [request]);
    },
    
    
    handleAbort: function(request){
        request.errorType = 'abort';
        this.handleResponse(null, request);
    },
    
    
    handleError: function(request){
        request.errorType = 'error';
        this.handleResponse(null, request);
    },
 
    
    cleanupErrorHandling: function(request){
        request.script.onerror = null;
    },
 
    
    handleTimeout: function(request){
        request.errorType = 'timeout';
        this.handleResponse(null, request);
    },
 
    
    handleResponse: function(result, request){
 
        var success = true;
 
        if (request.timeout) {
            clearTimeout(request.timeout);
        }
        delete this[request.callbackName];
        delete this.statics()[request.id];
        this.cleanupErrorHandling(request);
        Ext.fly(request.script).remove();
 
        if (request.errorType) {
            success = false;
            Ext.callback(request.failure, request.scope, [request.errorType]);
        } else {
            Ext.callback(request.success, request.scope, [result]);
        }
        Ext.callback(request.callback, request.scope, [success, result, request.errorType]);
    },
    
    
    createScript: function(url, params) {
        var script = document.createElement('script');
        script.setAttribute("src", Ext.urlAppend(url, Ext.Object.toQueryString(params)));
        script.setAttribute("async", true);
        script.setAttribute("type", "text/javascript");
        return script;
    }
});


Ext.define('Ext.data.JsonPStore', {
    extend: 'Ext.data.Store',
    alias : 'store.jsonp',

    
    constructor: function(config) {
        this.callParent(Ext.apply(config, {
            reader: Ext.create('Ext.data.reader.Json', config),
            proxy : Ext.create('Ext.data.proxy.JsonP', config)
        }));
    }
});


Ext.define('Ext.data.NodeInterface', {
    requires: ['Ext.data.Field'],
    
    statics: {
        
        decorate: function(record) {
            if (!record.isNode) {
                
                
                var mgr = Ext.ModelManager,
                    modelName = record.modelName,
                    modelClass = mgr.getModel(modelName),
                    idName = modelClass.prototype.idProperty,
                    instances = Ext.Array.filter(mgr.all.getArray(), function(item) {
                        return item.modelName == modelName;
                    }),
                    iln = instances.length,
                    newFields = [],
                    i, instance, jln, j, newField;

                
                modelClass.override(this.getPrototypeBody());
                newFields = this.applyFields(modelClass, [
                    {name: idName,      type: 'string',  defaultValue: null},
                    {name: 'parentId',  type: 'string',  defaultValue: null},
                    {name: 'index',     type: 'int',     defaultValue: null},
                    {name: 'depth',     type: 'int',     defaultValue: 0}, 
                    {name: 'expanded',  type: 'bool',    defaultValue: false, persist: false},
                    {name: 'checked',   type: 'auto',    defaultValue: null},
                    {name: 'leaf',      type: 'bool',    defaultValue: false, persist: false},
                    {name: 'cls',       type: 'string',  defaultValue: null, persist: false},
                    {name: 'iconCls',   type: 'string',  defaultValue: null, persist: false},
                    {name: 'root',      type: 'boolean', defaultValue: false, persist: false},
                    {name: 'isLast',    type: 'boolean', defaultValue: false, persist: false},
                    {name: 'isFirst',   type: 'boolean', defaultValue: false, persist: false},
                    {name: 'allowDrop', type: 'boolean', defaultValue: true, persist: false},
                    {name: 'allowDrag', type: 'boolean', defaultValue: true, persist: false},
                    {name: 'loaded',    type: 'boolean', defaultValue: false, persist: false},
                    {name: 'loading',   type: 'boolean', defaultValue: false, persist: false},
                    {name: 'href',      type: 'string',  defaultValue: null, persist: false},
                    {name: 'hrefTarget',type: 'string',  defaultValue: null, persist: false},
                    {name: 'qtip',      type: 'string',  defaultValue: null, persist: false},
                    {name: 'qtitle',    type: 'string',  defaultValue: null, persist: false}
                ]);

                jln = newFields.length;
                
                for (i = 0; i < iln; i++) {
                    instance = instances[i];
                    for (j = 0; j < jln; j++) {
                        newField = newFields[j];
                        if (instance.get(newField.name) === undefined) {
                            instance.data[newField.name] = newField.defaultValue;
                        }
                    }
                }
            }
            
            Ext.applyIf(record, {
                firstChild: null,
                lastChild: null,
                parentNode: null,
                previousSibling: null,
                nextSibling: null,
                childNodes: []
            });
            
            record.commit(true);
            
            record.enableBubble([
                
                "append",

                
                "remove",

                
                "move",

                
                "insert",

                
                "beforeappend",

                
                "beforeremove",

                
                "beforemove",

                 
                "beforeinsert",
                
                
                "expand",
                
                
                "collapse",
                
                
                "beforeexpand",
                
                
                "beforecollapse",
                
                
                "sort"
            ]);
            
            return record;
        },
        
        applyFields: function(modelClass, addFields) {
            var modelPrototype = modelClass.prototype,
                fields = modelPrototype.fields,
                keys = fields.keys,
                ln = addFields.length,
                addField, i, name,
                newFields = [];
                
            for (i = 0; i < ln; i++) {
                addField = addFields[i];
                if (!Ext.Array.contains(keys, addField.name)) {
                    addField = Ext.create('data.field', addField);
                    
                    newFields.push(addField);
                    fields.add(addField);
                }
            }
            
            return newFields;
        },
        
        getPrototypeBody: function() {
            return {
                isNode: true,

                
                createNode: function(node) {
                    if (Ext.isObject(node) && !node.isModel) {
                        node = Ext.ModelManager.create(node, this.modelName);
                    }
                    
                    return Ext.data.NodeInterface.decorate(node);
                },
                
                
                isLeaf : function() {
                    return this.get('leaf') === true;
                },

                
                setFirstChild : function(node) {
                    this.firstChild = node;
                },

                
                setLastChild : function(node) {
                    this.lastChild = node;
                },

                
                updateInfo: function(silent) {
                    var me = this,
                        isRoot = me.isRoot(),
                        parentNode = me.parentNode,
                        isFirst = (!parentNode ? true : parentNode.firstChild == me),
                        isLast = (!parentNode ? true : parentNode.lastChild == me),
                        depth = 0,
                        parent = me,
                        children = me.childNodes,
                        len = children.length,
                        i = 0;

                    while (parent.parentNode) {
                        ++depth;
                        parent = parent.parentNode;
                    }                                            
                    
                    me.beginEdit();
                    me.set({
                        isFirst: isFirst,
                        isLast: isLast,
                        depth: depth,
                        index: parentNode ? parentNode.indexOf(me) : 0,
                        parentId: parentNode ? parentNode.getId() : null
                    });
                    me.endEdit(silent);
                    if (silent) {
                        me.commit();
                    }
                    
                    for (i = 0; i < len; i++) {
                        children[i].updateInfo(silent);
                    }
                },

                
                isLast : function() {
                   return this.get('isLast');
                },

                
                isFirst : function() {
                   return this.get('isFirst');
                },

                
                hasChildNodes : function() {
                    return !this.isLeaf() && this.childNodes.length > 0;
                },

                
                isExpandable : function() {
                    return this.get('expandable') || this.hasChildNodes();
                },

                
                appendChild : function(node, suppressEvents, suppressNodeUpdate) {
                    var me = this,
                        i, ln,
                        index,
                        oldParent,
                        ps;

                    
                    if (Ext.isArray(node)) {
                        for (i = 0, ln = node.length; i < ln; i++) {
                            me.appendChild(node[i]);
                        }
                    } else {
                        
                        node = me.createNode(node);
                        
                        if (suppressEvents !== true && me.fireEvent("beforeappend", me, node) === false) {
                            return false;                         
                        }

                        index = me.childNodes.length;
                        oldParent = node.parentNode;

                        
                        if (oldParent) {
                            if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index) === false) {
                                return false;
                            }
                            oldParent.removeChild(node, null, false, true);
                        }

                        index = me.childNodes.length;
                        if (index === 0) {
                            me.setFirstChild(node);
                        }

                        me.childNodes.push(node);
                        node.parentNode = me;
                        node.nextSibling = null;

                        me.setLastChild(node);
                                                
                        ps = me.childNodes[index - 1];
                        if (ps) {
                            node.previousSibling = ps;
                            ps.nextSibling = node;
                            ps.updateInfo(suppressNodeUpdate);
                        } else {
                            node.previousSibling = null;
                        }

                        node.updateInfo(suppressNodeUpdate);
                        
                        
                        if (!me.isLoaded()) {
                            me.set('loaded', true);                            
                        }
                        
                        else if (me.childNodes.length === 1) {
                            me.set('loaded', me.isLoaded());
                        }
                        
                        if (suppressEvents !== true) {
                            me.fireEvent("append", me, node, index);

                            if (oldParent) {
                                node.fireEvent("move", node, oldParent, me, index);
                            }                            
                        }

                        return node;
                    }
                },
                
                
                getBubbleTarget: function() {
                    return this.parentNode;
                },

                
                removeChild : function(node, destroy, suppressEvents, suppressNodeUpdate) {
                    var me = this,
                        index = me.indexOf(node);
                    
                    if (index == -1 || (suppressEvents !== true && me.fireEvent("beforeremove", me, node) === false)) {
                        return false;
                    }

                    
                    me.childNodes.splice(index, 1);

                    
                    if (me.firstChild == node) {
                        me.setFirstChild(node.nextSibling);
                    }
                    if (me.lastChild == node) {
                        me.setLastChild(node.previousSibling);
                    }
                    
                    
                    if (node.previousSibling) {
                        node.previousSibling.nextSibling = node.nextSibling;
                        node.previousSibling.updateInfo(suppressNodeUpdate);
                    }
                    if (node.nextSibling) {
                        node.nextSibling.previousSibling = node.previousSibling;
                        node.nextSibling.updateInfo(suppressNodeUpdate);
                    }

                    if (suppressEvents !== true) {
                        me.fireEvent("remove", me, node);
                    }
                    
                    
                    
                    if (!me.childNodes.length) {
                        me.set('loaded', me.isLoaded());
                    }
                    
                    if (destroy) {
                        node.destroy(true);
                    } else {
                        node.clear();
                    }

                    return node;
                },

                
                copy: function(newId, deep) {
                    var me = this,
                        result = me.callOverridden(arguments),
                        len = me.childNodes ? me.childNodes.length : 0,
                        i;

                    
                    if (deep) {
                        for (i = 0; i < len; i++) {
                            result.appendChild(me.childNodes[i].copy(true));
                        }
                    }
                    return result;
                },

                
                clear : function(destroy) {
                    var me = this;
                    
                    
                    me.parentNode = me.previousSibling = me.nextSibling = null;
                    if (destroy) {
                        me.firstChild = me.lastChild = null;
                    }
                },

                
                destroy : function(silent) {
                    
                    var me = this,
                        options = me.destroyOptions;
                    
                    if (silent === true) {
                        me.clear(true);
                        Ext.each(me.childNodes, function(n) {
                            n.destroy(true);
                        });
                        me.childNodes = null;
                        delete me.destroyOptions;
                        me.callOverridden([options]);
                    } else {
                        me.destroyOptions = silent;
                        
                        me.remove(true);
                    }
                },

                
                insertBefore : function(node, refNode, suppressEvents) {
                    var me = this,
                        index     = me.indexOf(refNode),
                        oldParent = node.parentNode,
                        refIndex  = index,
                        ps;
                    
                    if (!refNode) { 
                        return me.appendChild(node);
                    }
                    
                    
                    if (node == refNode) {
                        return false;
                    }

                    
                    node = me.createNode(node);
                    
                    if (suppressEvents !== true && me.fireEvent("beforeinsert", me, node, refNode) === false) {
                        return false;
                    }
                    
                    
                    if (oldParent == me && me.indexOf(node) < index) {
                        refIndex--;
                    }

                    
                    if (oldParent) {
                        if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index, refNode) === false) {
                            return false;
                        }
                        oldParent.removeChild(node);
                    }

                    if (refIndex === 0) {
                        me.setFirstChild(node);
                    }

                    me.childNodes.splice(refIndex, 0, node);
                    node.parentNode = me;
                    
                    node.nextSibling = refNode;
                    refNode.previousSibling = node;
                    
                    ps = me.childNodes[refIndex - 1];
                    if (ps) {
                        node.previousSibling = ps;
                        ps.nextSibling = node;
                        ps.updateInfo();
                    } else {
                        node.previousSibling = null;
                    }
                    
                    node.updateInfo();
                    
                    if (!me.isLoaded()) {
                        me.set('loaded', true);                            
                    }    
                    
                    else if (me.childNodes.length === 1) {
                        me.set('loaded', me.isLoaded());
                    }

                    if (suppressEvents !== true) {
                        me.fireEvent("insert", me, node, refNode);

                        if (oldParent) {
                            node.fireEvent("move", node, oldParent, me, refIndex, refNode);
                        }                        
                    }

                    return node;
                },
                
                    
                insertChild: function(index, node) {
                    var sibling = this.childNodes[index];
                    if (sibling) {
                        return this.insertBefore(node, sibling);
                    }
                    else {
                        return this.appendChild(node);
                    }
                },

                
                remove : function(destroy, suppressEvents) {
                    var parentNode = this.parentNode;

                    if (parentNode) {
                        parentNode.removeChild(this, destroy, suppressEvents, true);
                    }
                    return this;
                },

                
                removeAll : function(destroy, suppressEvents) {
                    var cn = this.childNodes,
                        n;

                    while ((n = cn[0])) {
                        this.removeChild(n, destroy, suppressEvents);
                    }
                    return this;
                },

                
                getChildAt : function(index) {
                    return this.childNodes[index];
                },

                
                replaceChild : function(newChild, oldChild, suppressEvents) {
                    var s = oldChild ? oldChild.nextSibling : null;
                    
                    this.removeChild(oldChild, suppressEvents);
                    this.insertBefore(newChild, s, suppressEvents);
                    return oldChild;
                },

                
                indexOf : function(child) {
                    return Ext.Array.indexOf(this.childNodes, child);
                },

                
                getDepth : function() {
                    return this.get('depth');
                },

                
                bubble : function(fn, scope, args) {
                    var p = this;
                    while (p) {
                        if (fn.apply(scope || p, args || [p]) === false) {
                            break;
                        }
                        p = p.parentNode;
                    }
                },

                cascade: function() {
                    if (Ext.isDefined(Ext.global.console)) {
                        Ext.global.console.warn('Ext.data.Node: cascade has been deprecated. Please use cascadeBy instead.');
                    }
                    return this.cascadeBy.apply(this, arguments);
                },

                
                cascadeBy : function(fn, scope, args) {
                    if (fn.apply(scope || this, args || [this]) !== false) {
                        var childNodes = this.childNodes,
                            length     = childNodes.length,
                            i;

                        for (i = 0; i < length; i++) {
                            childNodes[i].cascadeBy(fn, scope, args);
                        }
                    }
                },

                
                eachChild : function(fn, scope, args) {
                    var childNodes = this.childNodes,
                        length     = childNodes.length,
                        i;

                    for (i = 0; i < length; i++) {
                        if (fn.apply(scope || this, args || [childNodes[i]]) === false) {
                            break;
                        }
                    }
                },

                
                findChild : function(attribute, value, deep) {
                    return this.findChildBy(function() {
                        return this.get(attribute) == value;
                    }, null, deep);
                },

                
                findChildBy : function(fn, scope, deep) {
                    var cs = this.childNodes,
                        len = cs.length,
                        i = 0, n, res;

                    for (; i < len; i++) {
                        n = cs[i];
                        if (fn.call(scope || n, n) === true) {
                            return n;
                        }
                        else if (deep) {
                            res = n.findChildBy(fn, scope, deep);
                            if (res !== null) {
                                return res;
                            }
                        }
                    }

                    return null;
                },

                
                contains : function(node) {
                    return node.isAncestor(this);
                },

                
                isAncestor : function(node) {
                    var p = this.parentNode;
                    while (p) {
                        if (p == node) {
                            return true;
                        }
                        p = p.parentNode;
                    }
                    return false;
                },

                
                sort : function(sortFn, recursive, suppressEvent) {
                    var cs  = this.childNodes,
                        ln = cs.length,
                        i, n;
                    
                    if (ln > 0) {
                        Ext.Array.sort(cs, sortFn);
                        for (i = 0; i < ln; i++) {
                            n = cs[i];
                            n.previousSibling = cs[i-1];
                            n.nextSibling = cs[i+1];
                        
                            if (i === 0) {
                                this.setFirstChild(n);
                                n.updateInfo();
                            }
                            if (i == ln - 1) {
                                this.setLastChild(n);
                                n.updateInfo();
                            }
                            if (recursive && !n.isLeaf()) {
                                n.sort(sortFn, true, true);
                            }
                        }
                        
                        if (suppressEvent !== true) {
                            this.fireEvent('sort', this, cs);
                        }
                    }
                },
                        
                        
                isExpanded: function() {
                    return this.get('expanded');
                },
                
                 
                isLoaded: function() {
                    return this.get('loaded');
                },

                 
                isLoading: function() {
                    return this.get('loading');
                },
                                
                 
                isRoot: function() {
                    return !this.parentNode;
                },
                
                 
                isVisible: function() {
                    var parent = this.parentNode;
                    while (parent) {
                        if (!parent.isExpanded()) {
                            return false;
                        }
                        parent = parent.parentNode;
                    }
                    return true;
                },
                
                
                expand: function(recursive, callback, scope) {
                    var me = this;

                    
                    

                    
                    if (!me.isLeaf()) {
                        
                        if (!me.isLoading() && !me.isExpanded()) {
                            
                            
                            
                            
                            me.fireEvent('beforeexpand', me, function(records) {
                                me.set('expanded', true); 
                                me.fireEvent('expand', me, me.childNodes, false);
                                
                                
                                if (recursive) {
                                    me.expandChildren(true, callback, scope);
                                }
                                else {
                                    Ext.callback(callback, scope || me, [me.childNodes]);                                
                                }
                            }, me);                            
                        }
                        
                        else if (recursive) {
                            me.expandChildren(true, callback, scope);
                        }
                        else {
                            Ext.callback(callback, scope || me, [me.childNodes]);
                        }

                        
                        
                        
                    }
                    
                    else {
                        Ext.callback(callback, scope || me); 
                    }
                },
                
                
                expandChildren: function(recursive, callback, scope) {
                    var me = this,
                        i = 0,
                        nodes = me.childNodes,
                        ln = nodes.length,
                        node,
                        expanding = 0;

                    for (; i < ln; ++i) {
                        node = nodes[i];
                        if (!node.isLeaf() && !node.isExpanded()) {
                            expanding++;
                            nodes[i].expand(recursive, function () {
                                expanding--;
                                if (callback && !expanding) {
                                    Ext.callback(callback, scope || me, me.childNodes); 
                                }
                            });                            
                        }
                    }
                    
                    if (!expanding && callback) {
                        Ext.callback(callback, scope || me, me.childNodes);
                    }
                },

                
                collapse: function(recursive, callback, scope) {
                    var me = this;

                    
                    if (!me.isLeaf()) {
                        
                        if (!me.collapsing && me.isExpanded()) {
                            me.fireEvent('beforecollapse', me, function(records) {
                                me.set('expanded', false); 
                                me.fireEvent('collapse', me, me.childNodes, false);
                                
                                
                                if (recursive) {
                                    me.collapseChildren(true, callback, scope);
                                }
                                else {
                                    Ext.callback(callback, scope || me, [me.childNodes]);                                
                                }
                            }, me);                            
                        }
                        
                        else if (recursive) {
                            me.collapseChildren(true, callback, scope);
                        }
                    }
                    
                    else {
                        Ext.callback(callback, scope || me, me.childNodes); 
                    }
                },
                
                
                collapseChildren: function(recursive, callback, scope) {
                    var me = this,
                        i = 0,
                        nodes = me.childNodes,
                        ln = nodes.length,
                        node,
                        collapsing = 0;

                    for (; i < ln; ++i) {
                        node = nodes[i];
                        if (!node.isLeaf() && node.isExpanded()) {
                            collapsing++;
                            nodes[i].collapse(recursive, function () {
                                collapsing--;
                                if (callback && !collapsing) {
                                    Ext.callback(callback, scope || me, me.childNodes); 
                                }
                            });                            
                        }
                    }
                    
                    if (!collapsing && callback) {
                        Ext.callback(callback, scope || me, me.childNodes);
                    }
                }
            };
        }
    }
});

Ext.define('Ext.data.NodeStore', {
    extend: 'Ext.data.Store',
    alias: 'store.node',
    requires: ['Ext.data.NodeInterface'],
    
    
    node: null,
    
    
    recursive: false,
    
        
    rootVisible: false,
    
    constructor: function(config) {
        var me = this,
            node;
            
        config = config || {};
        Ext.apply(me, config);
        
        if (Ext.isDefined(me.proxy)) {
            Ext.Error.raise("A NodeStore cannot be bound to a proxy. Instead bind it to a record " +
                            "decorated with the NodeInterface by setting the node config.");
        }

        config.proxy = {type: 'proxy'};
        me.callParent([config]);

        me.addEvents('expand', 'collapse', 'beforeexpand', 'beforecollapse');
        
        node = me.node;
        if (node) {
            me.node = null;
            me.setNode(node);
        }
    },
    
    setNode: function(node) {
        var me = this;
        
        if (me.node && me.node != node) {
            
            me.mun(me.node, {
                expand: me.onNodeExpand,
                collapse: me.onNodeCollapse,
                append: me.onNodeAppend,
                insert: me.onNodeInsert,
                remove: me.onNodeRemove,
                sort: me.onNodeSort,
                scope: me
            });
            me.node = null;
        }
        
        if (node) {
            Ext.data.NodeInterface.decorate(node);
            me.removeAll();
            if (me.rootVisible) {
                me.add(node);
            }
            me.mon(node, {
                expand: me.onNodeExpand,
                collapse: me.onNodeCollapse,
                append: me.onNodeAppend,
                insert: me.onNodeInsert,
                remove: me.onNodeRemove,
                sort: me.onNodeSort,
                scope: me
            });
            me.node = node;
            if (node.isExpanded() && node.isLoaded()) {
                me.onNodeExpand(node, node.childNodes, true);
            }
        }
    },
    
    onNodeSort: function(node, childNodes) {
        var me = this;
        
        if ((me.indexOf(node) !== -1 || (node === me.node && !me.rootVisible) && node.isExpanded())) {
            me.onNodeCollapse(node, childNodes, true);
            me.onNodeExpand(node, childNodes, true);
        }
    },
    
    onNodeExpand: function(parent, records, suppressEvent) {
        var me = this,
            insertIndex = me.indexOf(parent) + 1,
            ln = records ? records.length : 0,
            i, record;
            
        if (!me.recursive && parent !== me.node) {
            return;
        }
        
        if (!me.isVisible(parent)) {
            return;
        }

        if (!suppressEvent && me.fireEvent('beforeexpand', parent, records, insertIndex) === false) {
            return;
        }
        
        if (ln) {
            me.insert(insertIndex, records);
            for (i = 0; i < ln; i++) {
                record = records[i];
                if (record.isExpanded()) {
                    if (record.isLoaded()) {
                        
                        me.onNodeExpand(record, record.childNodes, true);
                    }
                    else {
                        record.set('expanded', false);
                        record.expand();
                    }
                }
            }
        }

        if (!suppressEvent) {
            me.fireEvent('expand', parent, records);
        }
    },

    onNodeCollapse: function(parent, records, suppressEvent) {
        var me = this,
            ln = records.length,
            collapseIndex = me.indexOf(parent) + 1,
            i, record;
            
        if (!me.recursive && parent !== me.node) {
            return;
        }
        
        if (!suppressEvent && me.fireEvent('beforecollapse', parent, records, collapseIndex) === false) {
            return;
        }

        for (i = 0; i < ln; i++) {
            record = records[i];
            me.remove(record);
            if (record.isExpanded()) {
                me.onNodeCollapse(record, record.childNodes, true);
            }
        }
        
        if (!suppressEvent) {
            me.fireEvent('collapse', parent, records, collapseIndex);
        }
    },
    
    onNodeAppend: function(parent, node, index) {
        var me = this,
            refNode, sibling;

        if (me.isVisible(node)) {
            if (index === 0) {
                refNode = parent;
            } else {
                sibling = node.previousSibling;
                while (sibling.isExpanded() && sibling.lastChild) {
                    sibling = sibling.lastChild;
                }
                refNode = sibling;
            }
            me.insert(me.indexOf(refNode) + 1, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes, true);
                }
                else {
                    node.set('expanded', false);
                    node.expand();
                }
            }
        } 
    },
    
    onNodeInsert: function(parent, node, refNode) {
        var me = this,
            index = this.indexOf(refNode);
            
        if (index != -1 && me.isVisible(node)) {
            me.insert(index, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes, true);
                }
                else {
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
    },
    
    onNodeRemove: function(parent, node, index) {
        var me = this;
        if (me.indexOf(node) != -1) {
            if (!node.isLeaf() && node.isExpanded()) {
                me.onNodeCollapse(node, node.childNodes, true);
            }            
            me.remove(node);
        }
    },
    
    isVisible: function(node) {
        var parent = node.parentNode;
        while (parent) {
            if (parent === this.node && !this.rootVisible && parent.isExpanded()) {
                return true;
            }
            
            if (this.indexOf(parent) === -1 || !parent.isExpanded()) {
                return false;
            }
            
            parent = parent.parentNode;
        }
        return true;
    }
});

Ext.define('Ext.data.Request', {
    
    action: undefined,
    
    
    params: undefined,
    
    
    method: 'GET',
    
    
    url: undefined,

    constructor: function(config) {
        Ext.apply(this, config);
    }
});

Ext.define('Ext.data.Tree', {
    alias: 'data.tree',
    
    mixins: {
        observable: "Ext.util.Observable"
    },

    
    root: null,
        
    constructor: function(root) {
        var me = this;
        
        me.nodeHash = {};

        me.mixins.observable.constructor.call(me);
                        
        if (root) {
            me.setRootNode(root);
        }
    },

    
    getRootNode : function() {
        return this.root;
    },

    
    setRootNode : function(node) {
        var me = this;
        
        me.root = node;
        Ext.data.NodeInterface.decorate(node);
        
        if (me.fireEvent('beforeappend', null, node) !== false) {
            node.set('root', true);
            node.updateInfo();
            
            me.relayEvents(node, [
                
                "append",

                
                "remove",

                
                "move",

                
                "insert",

                
                "beforeappend",

                
                "beforeremove",

                
                "beforemove",

                
                "beforeinsert",

                 
                 "expand",

                 
                 "collapse",

                 
                 "beforeexpand",

                 
                 "beforecollapse" ,

                 
                 "rootchange"
            ]);
            
            node.on({
                scope: me,
                insert: me.onNodeInsert,
                append: me.onNodeAppend,
                remove: me.onNodeRemove
            });

            me.registerNode(node);        
            me.fireEvent('append', null, node);
            me.fireEvent('rootchange', node);
        }
            
        return node;
    },
    
    
    flatten: function(){
        var nodes = [],
            hash = this.nodeHash,
            key;
            
        for (key in hash) {
            if (hash.hasOwnProperty(key)) {
                nodes.push(hash[key]);
            }
        }
        return nodes;
    },
    
    
    onNodeInsert: function(parent, node) {
        this.registerNode(node);
    },
    
    
    onNodeAppend: function(parent, node) {
        this.registerNode(node);
    },
    
    
    onNodeRemove: function(parent, node) {
        this.unregisterNode(node);
    },

    
    getNodeById : function(id) {
        return this.nodeHash[id];
    },

    
    registerNode : function(node) {
        this.nodeHash[node.getId() || node.internalId] = node;
    },

    
    unregisterNode : function(node) {
        delete this.nodeHash[node.getId() || node.internalId];
    },
    
    
    sort: function(sorterFn, recursive) {
        this.getRootNode().sort(sorterFn, recursive);
    },
    
     
    filter: function(filters, recursive) {
        this.getRootNode().filter(filters, recursive);
    }
});

Ext.define('Ext.data.TreeStore', {
    extend: 'Ext.data.AbstractStore',
    alias: 'store.tree',
    requires: ['Ext.data.Tree', 'Ext.data.NodeInterface', 'Ext.data.NodeStore'],

    
    clearOnLoad : true,

    
    nodeParam: 'node',

    
    defaultRootId: 'root',
    
    
    defaultRootProperty: 'children',

    
    folderSort: false,
    
    constructor: function(config) {
        var me = this, 
            root,
            fields;
            
        
        config = Ext.apply({}, config);
        
        
        fields = config.fields || me.fields;
        if (!fields) {
            config.fields = [{name: 'text', type: 'string'}];
        }

        me.callParent([config]);
        
        
        me.tree = Ext.create('Ext.data.Tree');
        
        me.tree.on({
            scope: me,
            remove: me.onNodeRemove,
            beforeexpand: me.onBeforeNodeExpand,
            beforecollapse: me.onBeforeNodeCollapse,
            append: me.onNodeAdded,
            insert: me.onNodeAdded
        });

        me.onBeforeSort();
                
        root = me.root;
        if (root) {
            delete me.root;
            me.setRootNode(root);            
        }

        me.relayEvents(me.tree, [
            
            "append",
            
            
            "remove",
            
            
            "move",
            
            
            "insert",
            
            
            "beforeappend",
            
            
            "beforeremove",
            
            
            "beforemove",
            
            
            "beforeinsert",
             
             
             "expand",
             
             
             "collapse",
             
             
             "beforeexpand",
             
             
             "beforecollapse",

                          
             "sort",
             
             
             "rootchange"
        ]);
        
        me.addEvents(
            
            'rootchange'
        );
        
        if (Ext.isDefined(me.nodeParameter)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.data.TreeStore: nodeParameter has been deprecated. Please use nodeParam instead.');
            }
            me.nodeParam = me.nodeParameter;
            delete me.nodeParameter;
        }
    },
    
    
    setProxy: function(proxy) {
        var reader,
            needsRoot;
        
        if (proxy instanceof Ext.data.proxy.Proxy) {
            
            needsRoot = Ext.isEmpty(proxy.getReader().root);
        } else if (Ext.isString(proxy)) {
            
            needsRoot = true;
        } else {
            
            reader = proxy.reader;
            needsRoot = !(reader && !Ext.isEmpty(reader.root));
        }
        proxy = this.callParent(arguments);
        if (needsRoot) {
            reader = proxy.getReader();
            reader.root = this.defaultRootProperty;
            
            reader.buildExtractors(true);
        }
    },
    
    
    onBeforeSort: function() {
        if (this.folderSort) {
            this.sort({
                property: 'leaf',
                direction: 'ASC'
            }, 'prepend', false);    
        }
    },
    
    
    onBeforeNodeExpand: function(node, callback, scope) {
        if (node.isLoaded()) {
            Ext.callback(callback, scope || node, [node.childNodes]);
        }
        else if (node.isLoading()) {
            this.on('load', function() {
                Ext.callback(callback, scope || node, [node.childNodes]);
            }, this, {single: true});
        }
        else {
            this.read({
                node: node,
                callback: function() {
                    Ext.callback(callback, scope || node, [node.childNodes]);
                }
            });            
        }
    },
    
    
    getNewRecords: function() {
        return Ext.Array.filter(this.tree.flatten(), this.filterNew);
    },

    
    getUpdatedRecords: function() {
        return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
    },
    
    
    onBeforeNodeCollapse: function(node, callback, scope) {
        callback.call(scope || node, node.childNodes);
    },
    
    onNodeRemove: function(parent, node) {
        var removed = this.removed;
        
        if (!node.isReplace && Ext.Array.indexOf(removed, node) == -1) {
            removed.push(node);
        }
    },
    
    onNodeAdded: function(parent, node) {
        var proxy = this.getProxy(),
            reader = proxy.getReader(),
            data = node.raw || node.data,
            dataRoot, children;
            
        Ext.Array.remove(this.removed, node); 
        
        if (!node.isLeaf() && !node.isLoaded()) {
            dataRoot = reader.getRoot(data);
            if (dataRoot) {
                this.fillNode(node, reader.extractData(dataRoot));
                delete data[reader.root];
            }
        }
    },
        
    
    setRootNode: function(root) {
        var me = this;

        root = root || {};        
        if (!root.isNode) {
            
            Ext.applyIf(root, {
                id: me.defaultRootId,
                text: 'Root',
                allowDrag: false
            });
            root = Ext.ModelManager.create(root, me.model);
        }
        Ext.data.NodeInterface.decorate(root);

        
        
        me.getProxy().getReader().buildExtractors(true);
        
        
        me.tree.setRootNode(root);
        
        
        if (!root.isLoaded() && root.isExpanded()) {
            me.load({
                node: root
            });
        }
        
        return root;
    },
        
    
    getRootNode: function() {
        return this.tree.getRootNode();
    },

    
    getNodeById: function(id) {
        return this.tree.getNodeById(id);
    },

    
    load: function(options) {
        options = options || {};
        options.params = options.params || {};
        
        var me = this,
            node = options.node || me.tree.getRootNode(),
            root;
            
        
        
        if (!node) {
            node = me.setRootNode({
                expanded: true
            });
        }
        
        if (me.clearOnLoad) {
            node.removeAll();
        }
        
        Ext.applyIf(options, {
            node: node
        });
        options.params[me.nodeParam] = node ? node.getId() : 'root';
        
        if (node) {
            node.set('loading', true);
        }
        
        return me.callParent([options]);
    },
        

    
    fillNode: function(node, records) {
        var me = this,
            ln = records ? records.length : 0,
            i = 0, sortCollection;

        if (ln && me.sortOnLoad && !me.remoteSort && me.sorters && me.sorters.items) {
            sortCollection = Ext.create('Ext.util.MixedCollection');
            sortCollection.addAll(records);
            sortCollection.sort(me.sorters.items);
            records = sortCollection.items;
        }
        
        node.set('loaded', true);
        for (; i < ln; i++) {
            node.appendChild(records[i], undefined, true);
        }
        
        return records;
    },

    
    onProxyLoad: function(operation) {
        var me = this,
            successful = operation.wasSuccessful(),
            records = operation.getRecords(),
            node = operation.node;

        node.set('loading', false);
        if (successful) {
            records = me.fillNode(node, records);
        }
        
        me.fireEvent('read', me, operation.node, records, successful);
        me.fireEvent('load', me, operation.node, records, successful);
        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },
    
    
    onCreateRecords: function(records, operation, success) {
        if (success) {
            var i = 0,
                length = records.length,
                originalRecords = operation.records,
                parentNode,
                record,
                original,
                index;

            
            for (; i < length; ++i) {
                record = records[i];
                original = originalRecords[i];
                if (original) {
                    parentNode = original.parentNode;
                    if (parentNode) {
                        
                        original.isReplace = true;
                        parentNode.replaceChild(record, original);
                        delete original.isReplace;
                    }
                    record.phantom = false;
                }
            }
        }
    },

    
    onUpdateRecords: function(records, operation, success){
        if (success) {
            var me = this,
                i = 0,
                length = records.length,
                data = me.data,
                original,
                parentNode,
                record;

            for (; i < length; ++i) {
                record = records[i];
                original = me.tree.getNodeById(record.getId());
                parentNode = original.parentNode;
                if (parentNode) {
                    
                    original.isReplace = true;
                    parentNode.replaceChild(record, original);
                    original.isReplace = false;
                }
            }
        }
    },

    
    onDestroyRecords: function(records, operation, success){
        if (success) {
            this.removed = [];
        }
    },

    
    removeAll: function() {
        this.getRootNode().destroy(true);
        this.fireEvent('clear', this);
    },

    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.tree.sort(sorterFn, true);
            me.fireEvent('datachanged', me);
        }   
        me.fireEvent('sort', me);
    }
});

Ext.define('Ext.data.XmlStore', {
    extend: 'Ext.data.Store',
    alternateClassName: 'Ext.data.XmlStore',
    alias: 'store.xml',

    
    constructor: function(config){
        config = config || {};
        config = config || {};

        Ext.applyIf(config, {
            proxy: {
                type: 'ajax',
                reader: 'xml',
                writer: 'xml'
            }
        });

        this.callParent([config]);
    }
});


Ext.define('Ext.data.proxy.Client', {
    extend: 'Ext.data.proxy.Proxy',
    alternateClassName: 'Ext.data.ClientProxy',
    
    
    clear: function() {
        Ext.Error.raise("The Ext.data.proxy.Client subclass that you are using has not defined a 'clear' function. See src/data/ClientProxy.js for details.");
    }
});

Ext.define('Ext.data.proxy.JsonP', {
    extend: 'Ext.data.proxy.Server',
    alternateClassName: 'Ext.data.ScriptTagProxy',
    alias: ['proxy.jsonp', 'proxy.scripttag'],
    requires: ['Ext.data.JsonP'],

    defaultWriterType: 'base',

    
    callbackKey : 'callback',

    
    recordParam: 'records',

    
    autoAppendParams: true,

    constructor: function(){
        this.addEvents(
            
            'exception'
        );
        this.callParent(arguments);
    },

    
    doRequest: function(operation, callback, scope) {
        
        var me      = this,
            writer  = me.getWriter(),
            request = me.buildRequest(operation),
            params = request.params;

        if (operation.allowWrite()) {
            request = writer.write(request);
        }

        
        Ext.apply(request, {
            callbackKey: me.callbackKey,
            timeout: me.timeout,
            scope: me,
            disableCaching: false, 
            callback: me.createRequestCallback(request, operation, callback, scope)
        });
        
        
        if (me.autoAppendParams) {
            request.params = {};
        }
        
        request.jsonp = Ext.data.JsonP.request(request);
        
        request.params = params;
        operation.setStarted();
        me.lastRequest = request;

        return request;
    },

    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;

        return function(success, response, errorType) {
            delete me.lastRequest;
            me.processResponse(success, operation, request, response, callback, scope);
        };
    },
    
    
    setException: function(operation, response) {
        operation.setException(operation.request.jsonp.errorType);
    },


    
    buildUrl: function(request) {
        var me      = this,
            url     = me.callParent(arguments),
            params  = Ext.apply({}, request.params),
            filters = params.filters,
            records,
            filter, i;

        delete params.filters;
 
        if (me.autoAppendParams) {
            url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
        }

        if (filters && filters.length) {
            for (i = 0; i < filters.length; i++) {
                filter = filters[i];

                if (filter.value) {
                    url = Ext.urlAppend(url, filter.property + "=" + filter.value);
                }
            }
        }

        
        records = request.records;

        if (Ext.isArray(records) && records.length > 0) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.recordParam, me.encodeRecords(records)));
        }

        return url;
    },

    
    destroy: function() {
        this.abort();
        this.callParent();
    },

    
    abort: function() {
        var lastRequest = this.lastRequest;
        if (lastRequest) {
            Ext.data.JsonP.abort(lastRequest.jsonp);
        }
    },

    
    encodeRecords: function(records) {
        var encoded = "",
            i = 0,
            len = records.length;

        for (; i < len; i++) {
            encoded += Ext.Object.toQueryString(records[i].data);
        }

        return encoded;
    }
});


Ext.define('Ext.data.proxy.WebStorage', {
    extend: 'Ext.data.proxy.Client',
    alternateClassName: 'Ext.data.WebStorageProxy',
    
    
    id: undefined,

    
    constructor: function(config) {
        this.callParent(arguments);
        
        
        this.cache = {};

        if (this.getStorageObject() === undefined) {
            Ext.Error.raise("Local Storage is not supported in this browser, please use another type of data proxy");
        }

        
        this.id = this.id || (this.store ? this.store.storeId : undefined);

        if (this.id === undefined) {
            Ext.Error.raise("No unique id was provided to the local storage proxy. See Ext.data.proxy.LocalStorage documentation for details");
        }

        this.initialize();
    },

    
    create: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),
            id, record, i;
        
        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];

            if (record.phantom) {
                record.phantom = false;
                id = this.getNextId();
            } else {
                id = record.getId();
            }

            this.setRecord(record, id);
            ids.push(id);
        }

        this.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    read: function(operation, callback, scope) {
        

        var records = [],
            ids     = this.getIds(),
            length  = ids.length,
            i, recordData, record;
        
        
        if (operation.id) {
            record = this.getRecord(operation.id);
            
            if (record) {
                records.push(record);
                operation.setSuccessful();
            }
        } else {
            for (i = 0; i < length; i++) {
                records.push(this.getRecord(ids[i]));
            }
            operation.setSuccessful();
        }
        
        operation.setCompleted();

        operation.resultSet = Ext.create('Ext.data.ResultSet', {
            records: records,
            total  : records.length,
            loaded : true
        });

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    update: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),
            record, id, i;

        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];
            this.setRecord(record);
            
            
            
            id = record.getId();
            if (id !== undefined && Ext.Array.indexOf(ids, id) == -1) {
                ids.push(id);
            }
        }
        this.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    destroy: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),

            
            newIds  = [].concat(ids),
            i;

        for (i = 0; i < length; i++) {
            Ext.Array.remove(newIds, records[i].getId());
            this.removeRecord(records[i], false);
        }

        this.setIds(newIds);
        
        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    getRecord: function(id) {
        if (this.cache[id] === undefined) {
            var rawData = Ext.decode(this.getStorageObject().getItem(this.getRecordKey(id))),
                data    = {},
                Model   = this.model,
                fields  = Model.prototype.fields.items,
                length  = fields.length,
                i, field, name, record;

            for (i = 0; i < length; i++) {
                field = fields[i];
                name  = field.name;

                if (typeof field.decode == 'function') {
                    data[name] = field.decode(rawData[name]);
                } else {
                    data[name] = rawData[name];
                }
            }

            record = new Model(data, id);
            record.phantom = false;

            this.cache[id] = record;
        }
        
        return this.cache[id];
    },

    
    setRecord: function(record, id) {
        if (id) {
            record.setId(id);
        } else {
            id = record.getId();
        }

        var me = this,
            rawData = record.data,
            data    = {},
            model   = me.model,
            fields  = model.prototype.fields.items,
            length  = fields.length,
            i = 0,
            field, name, obj, key;

        for (; i < length; i++) {
            field = fields[i];
            name  = field.name;

            if (typeof field.encode == 'function') {
                data[name] = field.encode(rawData[name], record);
            } else {
                data[name] = rawData[name];
            }
        }

        obj = me.getStorageObject();
        key = me.getRecordKey(id);
        
        
        me.cache[id] = record;
        
        
        obj.removeItem(key);
        obj.setItem(key, Ext.encode(data));
    },

    
    removeRecord: function(id, updateIds) {
        var me = this,
            ids;
            
        if (id.isModel) {
            id = id.getId();
        }

        if (updateIds !== false) {
            ids = me.getIds();
            Ext.Array.remove(ids, id);
            me.setIds(ids);
        }

        me.getStorageObject().removeItem(me.getRecordKey(id));
    },

    
    getRecordKey: function(id) {
        if (id.isModel) {
            id = id.getId();
        }

        return Ext.String.format("{0}-{1}", this.id, id);
    },

    
    getRecordCounterKey: function() {
        return Ext.String.format("{0}-counter", this.id);
    },

    
    getIds: function() {
        var ids    = (this.getStorageObject().getItem(this.id) || "").split(","),
            length = ids.length,
            i;

        if (length == 1 && ids[0] === "") {
            ids = [];
        } else {
            for (i = 0; i < length; i++) {
                ids[i] = parseInt(ids[i], 10);
            }
        }

        return ids;
    },

    
    setIds: function(ids) {
        var obj = this.getStorageObject(),
            str = ids.join(",");
        
        obj.removeItem(this.id);
        
        if (!Ext.isEmpty(str)) {
            obj.setItem(this.id, str);
        }
    },

    
    getNextId: function() {
        var obj  = this.getStorageObject(),
            key  = this.getRecordCounterKey(),
            last = obj.getItem(key),
            ids, id;
        
        if (last === null) {
            ids = this.getIds();
            last = ids[ids.length - 1] || 0;
        }
        
        id = parseInt(last, 10) + 1;
        obj.setItem(key, id);
        
        return id;
    },

    
    initialize: function() {
        var storageObject = this.getStorageObject();
        storageObject.setItem(this.id, storageObject.getItem(this.id) || "");
    },

    
    clear: function() {
        var obj = this.getStorageObject(),
            ids = this.getIds(),
            len = ids.length,
            i;

        
        for (i = 0; i < len; i++) {
            this.removeRecord(ids[i]);
        }

        
        obj.removeItem(this.getRecordCounterKey());
        obj.removeItem(this.id);
    },

    
    getStorageObject: function() {
        Ext.Error.raise("The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass");
    }
});

Ext.define('Ext.data.proxy.LocalStorage', {
    extend: 'Ext.data.proxy.WebStorage',
    alias: 'proxy.localstorage',
    alternateClassName: 'Ext.data.LocalStorageProxy',
    
    
    getStorageObject: function() {
        return window.localStorage;
    }
});

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

    

    constructor: function(config) {
        this.callParent([config]);

        
        this.setReader(this.reader);
    },

    
    read: function(operation, callback, scope) {
        var me     = this,
            reader = me.getReader(),
            result = reader.read(me.data);

        Ext.apply(operation, {
            resultSet: result
        });

        operation.setCompleted();
        operation.setSuccessful();
        Ext.callback(callback, scope || me, [operation]);
    },

    clear: Ext.emptyFn
});


Ext.define('Ext.data.proxy.Rest', {
    extend: 'Ext.data.proxy.Ajax',
    alternateClassName: 'Ext.data.RestProxy',
    alias : 'proxy.rest',
    
    
    appendId: true,
    
    
    
    
    batchActions: false,
    
    
    buildUrl: function(request) {
        var me        = this,
            operation = request.operation,
            records   = operation.records || [],
            record    = records[0],
            format    = me.format,
            url       = me.getUrl(request),
            id        = record ? record.getId() : operation.id;
        
        if (me.appendId && id) {
            if (!url.match(/\/$/)) {
                url += '/';
            }
            
            url += id;
        }
        
        if (format) {
            if (!url.match(/\.$/)) {
                url += '.';
            }
            
            url += format;
        }
        
        request.url = url;
        
        return me.callParent(arguments);
    }
}, function() {
    Ext.apply(this.prototype, {
        
        actionMethods: {
            create : 'POST',
            read   : 'GET',
            update : 'PUT',
            destroy: 'DELETE'
        }
    });
});


Ext.define('Ext.data.proxy.SessionStorage', {
    extend: 'Ext.data.proxy.WebStorage',
    alias: 'proxy.sessionstorage',
    alternateClassName: 'Ext.data.SessionStorageProxy',
    
    
    getStorageObject: function() {
        return window.sessionStorage;
    }
});


Ext.define('Ext.data.reader.Array', {
    extend: 'Ext.data.reader.Json',
    alternateClassName: 'Ext.data.ArrayReader',
    alias : 'reader.array',

    
    buildExtractors: function() {
        this.callParent(arguments);
        
        var fields = this.model.prototype.fields.items,
            length = fields.length,
            extractorFunctions = [],
            i;
        
        for (i = 0; i < length; i++) {
            extractorFunctions.push(function(index) {
                return function(data) {
                    return data[index];
                };
            }(fields[i].mapping || i));
        }
        
        this.extractorFunctions = extractorFunctions;
    }
});


Ext.define('Ext.data.reader.Xml', {
    extend: 'Ext.data.reader.Reader',
    alternateClassName: 'Ext.data.XmlReader',
    alias : 'reader.xml',
    
    

    
    createAccessor: function(expr) {
        var me = this;
        
        if (Ext.isEmpty(expr)) {
            return Ext.emptyFn;
        }
        
        if (Ext.isFunction(expr)) {
            return expr;
        }
        
        return function(root) {
            var node = Ext.DomQuery.selectNode(expr, root),
                val = me.getNodeValue(node);
                
            return Ext.isEmpty(val) ? null : val;
        };
    },
    
    getNodeValue: function(node) {
        var val;
        if (node && node.firstChild) {
            val = node.firstChild.nodeValue;
        }
        return val || null;
    },

    
    getResponseData: function(response) {
        var xml = response.responseXML;

        if (!xml) {
            Ext.Error.raise({
                response: response,
                msg: 'XML data not found in the response'
            });
        }

        return xml;
    },

    
    getData: function(data) {
        return data.documentElement || data;
    },

    
    getRoot: function(data) {
        var nodeName = data.nodeName,
            root     = this.root;
        
        if (!root || (nodeName && nodeName == root)) {
            return data;
        } else if (Ext.DomQuery.isXml(data)) {
            
            
            
            return Ext.DomQuery.selectNode(root, data);
        }
    },

    
    extractData: function(root) {
        var recordName = this.record;
        
        if (!recordName) {
            Ext.Error.raise('Record is a required parameter');
        }
        
        if (recordName != root.nodeName) {
            root = Ext.DomQuery.select(recordName, root);
        } else {
            root = [root];
        }
        return this.callParent([root]);
    },
    
    
    getAssociatedDataRoot: function(data, associationName) {
        return Ext.DomQuery.select(associationName, data)[0];
    },

    
    readRecords: function(doc) {
        
        if (Ext.isArray(doc)) {
            doc = doc[0];
        }
        
        
        this.xmlData = doc;
        return this.callParent([doc]);
    }
});


Ext.define('Ext.data.writer.Xml', {
    
    
    
    extend: 'Ext.data.writer.Writer',
    alternateClassName: 'Ext.data.XmlWriter',
    
    alias: 'writer.xml',
    
    
    
    
    documentRoot: 'xmlData',
    
    
    defaultDocumentRoot: 'xmlData',

    
    header: '',

    
    record: 'record',

    
    writeRecords: function(request, data) {
        var me = this,
            xml = [],
            i = 0,
            len = data.length,
            root = me.documentRoot,
            record = me.record,
            needsRoot = data.length !== 1,
            item,
            key;
            
        
        xml.push(me.header || '');
        
        if (!root && needsRoot) {
            root = me.defaultDocumentRoot;
        }
        
        if (root) {
            xml.push('<', root, '>');
        }
            
        for (; i < len; ++i) {
            item = data[i];
            xml.push('<', record, '>');
            for (key in item) {
                if (item.hasOwnProperty(key)) {
                    xml.push('<', key, '>', item[key], '</', key, '>');
                }
            }
            xml.push('</', record, '>');
        }
        
        if (root) {
            xml.push('</', root, '>');
        }
            
        request.xmlData = xml.join('');
        return request;
    }
});


Ext.define('Ext.direct.Event', {
    
    
   
    alias: 'direct.event',
    
    requires: ['Ext.direct.Manager'],
    
    
   
    status: true,
    
    constructor: function(config) {
        Ext.apply(this, config);
    },
    
    
    getData: function(){
        return this.data;
    }
});


Ext.define('Ext.direct.RemotingEvent', {
    
    
   
    extend: 'Ext.direct.Event',
    
    alias: 'direct.rpc',
    
    
    
    
    getTransaction: function(){
        return this.transaction || Ext.direct.Manager.getTransaction(this.tid);
    }
});


Ext.define('Ext.direct.ExceptionEvent', {
    
    
   
    extend: 'Ext.direct.RemotingEvent',
    
    alias: 'direct.exception',
    
    
   
   status: false
});


Ext.define('Ext.direct.Provider', {
    
    
   
   alias: 'direct.provider',
   
    mixins: {
        observable: 'Ext.util.Observable'   
    },
   
    
   
   
    
    constructor : function(config){
        var me = this;
        
        Ext.apply(me, config);
        me.addEvents(
                        
            'connect',
                        
            'disconnect',
                        
            'data',
                                    
            'exception'
        );
        me.mixins.observable.constructor.call(me, config);
    },
    
    
    isConnected: function(){
        return false;
    },

    
    connect: Ext.emptyFn,
    
    
    disconnect: Ext.emptyFn
});



Ext.define('Ext.direct.JsonProvider', {
    
    
    
    extend: 'Ext.direct.Provider',
    
    alias: 'direct.jsonprovider',
    
    uses: ['Ext.direct.ExceptionEvent'],
    
    
   
   
   parseResponse: function(response){
        if (!Ext.isEmpty(response.responseText)) {
            if (Ext.isObject(response.responseText)) {
                return response.responseText;
            }
            return Ext.decode(response.responseText);
        }
        return null;
    },

    
    createEvents: function(response){
        var data = null,
            events = [],
            event,
            i = 0,
            len;
            
        try{
            data = this.parseResponse(response);
        } catch(e) {
            event = Ext.create('Ext.direct.ExceptionEvent', {
                data: e,
                xhr: response,
                code: Ext.direct.Manager.self.exceptions.PARSE,
                message: 'Error parsing json response: \n\n ' + data
            });
            return [event];
        }
        
        if (Ext.isArray(data)) {
            for (len = data.length; i < len; ++i) {
                events.push(this.createEvent(data[i]));
            }
        } else {
            events.push(this.createEvent(data));
        }
        return events;
    },
    
    
    createEvent: function(response){
        return Ext.create('direct.' + response.type, response);
    }
});

Ext.define('Ext.direct.PollingProvider', {
    
    
    
    extend: 'Ext.direct.JsonProvider',
    
    alias: 'direct.pollingprovider',
    
    uses: ['Ext.direct.ExceptionEvent'],
    
    requires: ['Ext.Ajax', 'Ext.util.DelayedTask'],
    
    
    
    
    interval: 3000,

    
    
    

    
    constructor : function(config){
        this.callParent(arguments);
        this.addEvents(
            
            'beforepoll',            
            
            'poll'
        );
    },

    
    isConnected: function(){
        return !!this.pollTask;
    },

    
    connect: function(){
        var me = this, url = me.url;
        
        if (url && !me.pollTask) {
            me.pollTask = Ext.TaskManager.start({
                run: function(){
                    if (me.fireEvent('beforepoll', me) !== false) {
                        if (Ext.isFunction(url)) {
                            url(me.baseParams);
                        } else {
                            Ext.Ajax.request({
                                url: url,
                                callback: me.onData,
                                scope: me,
                                params: me.baseParams
                            });
                        }
                    }
                },
                interval: me.interval,
                scope: me
            });
            me.fireEvent('connect', me);
        } else if (!url) {
            Ext.Error.raise('Error initializing PollingProvider, no url configured.');
        }
    },

    
    disconnect: function(){
        var me = this;
        
        if (me.pollTask) {
            Ext.TaskManager.stop(me.pollTask);
            delete me.pollTask;
            me.fireEvent('disconnect', me);
        }
    },

    
    onData: function(opt, success, response){
        var me = this, 
            i = 0, 
            len,
            events;
        
        if (success) {
            events = me.createEvents(response);
            for (len = events.length; i < len; ++i) {
                me.fireEvent('data', me, events[i]);
            }
        } else {
            me.fireEvent('data', me, Ext.create('Ext.direct.ExceptionEvent', {
                data: null,
                code: Ext.direct.Manager.self.exceptions.TRANSPORT,
                message: 'Unable to connect to the server.',
                xhr: response
            }));
        }
    }
});

Ext.define('Ext.direct.RemotingMethod', {
    
    constructor: function(config){
        var me = this,
            params = Ext.isDefined(config.params) ? config.params : config.len,
            name;
            
        me.name = config.name;
        me.formHandler = config.formHandler;
        if (Ext.isNumber(params)) {
            
            me.len = params;
            me.ordered = true;
        } else {
            
            me.params = [];
            Ext.each(params, function(param){
                name = Ext.isObject(param) ? param.name : param;
                me.params.push(name);
            });
        }
    },
    
    
    getCallData: function(args){
        var me = this,
            data = null,
            len  = me.len,
            params = me.params,
            callback,
            scope,
            name;
            
        if (me.ordered) {
            callback = args[len];
            scope = args[len + 1];
            if (len !== 0) {
                data = args.slice(0, len);
            }
        } else {
            data = Ext.apply({}, args[0]);
            callback = args[1];
            scope = args[2];
            
            
            for (name in data) {
                if (data.hasOwnProperty(name)) {
                    if (!Ext.Array.contains(params, name)) {
                        delete data[name];
                    }
                }
            }
        }
        
        return {
            data: data,
            callback: callback,
            scope: scope    
        };
    }
});


Ext.define('Ext.direct.Transaction', {
    
    
   
    alias: 'direct.transaction',
    alternateClassName: 'Ext.Direct.Transaction',
   
    statics: {
        TRANSACTION_ID: 0
    },
   
    
   
    constructor: function(config){
        var me = this;
        
        Ext.apply(me, config);
        me.id = ++me.self.TRANSACTION_ID;
        me.retryCount = 0;
    },
   
    send: function(){
         this.provider.queueTransaction(this);
    },

    retry: function(){
        this.retryCount++;
        this.send();
    },

    getProvider: function(){
        return this.provider;
    }
});


Ext.define('Ext.direct.RemotingProvider', {
    
    
   
    alias: 'direct.remotingprovider',
    
    extend: 'Ext.direct.JsonProvider', 
    
    requires: [
        'Ext.util.MixedCollection', 
        'Ext.util.DelayedTask', 
        'Ext.direct.Transaction',
        'Ext.direct.RemotingMethod'
    ],
   
    
   
   
    
    
    
    
    
    
    
    
    enableBuffer: 10,
    
    
    maxRetries: 1,
    
    
    timeout: undefined,
    
    constructor : function(config){
        var me = this;
        me.callParent(arguments);
        me.addEvents(
                        
            'beforecall',            
                        
            'call'
        );
        me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || window;
        me.transactions = Ext.create('Ext.util.MixedCollection');
        me.callBuffer = [];
    },
    
    
    initAPI : function(){
        var actions = this.actions,
            namespace = this.namespace,
            action,
            cls,
            methods,
            i,
            len,
            method;
            
        for (action in actions) {
            cls = namespace[action];
            if (!cls) {
                cls = namespace[action] = {};
            }
            methods = actions[action];
            
            for (i = 0, len = methods.length; i < len; ++i) {
                method = Ext.create('Ext.direct.RemotingMethod', methods[i]);
                cls[method.name] = this.createHandler(action, method);
            }
        }
    },
    
    
    createHandler : function(action, method){
        var me = this,
            handler;
        
        if (!method.formHandler) {
            handler = function(){
                me.configureRequest(action, method, Array.prototype.slice.call(arguments, 0));
            };
        } else {
            handler = function(form, callback, scope){
                me.configureFormRequest(action, method, form, callback, scope);
            };
        }
        handler.directCfg = {
            action: action,
            method: method
        };
        return handler;
    },
    
    
    isConnected: function(){
        return !!this.connected;
    },

    
    connect: function(){
        var me = this;
        
        if (me.url) {
            me.initAPI();
            me.connected = true;
            me.fireEvent('connect', me);
        } else if(!me.url) {
            Ext.Error.raise('Error initializing RemotingProvider, no url configured.');
        }
    },

    
    disconnect: function(){
        var me = this;
        
        if (me.connected) {
            me.connected = false;
            me.fireEvent('disconnect', me);
        }
    },
    
    
    runCallback: function(transaction, event){
        var funcName = event.status ? 'success' : 'failure',
            callback,
            result;
        
        if (transaction && transaction.callback) {
            callback = transaction.callback;
            result = Ext.isDefined(event.result) ? event.result : event.data;
        
            if (Ext.isFunction(callback)) {
                callback(result, event);
            } else {
                Ext.callback(callback[funcName], callback.scope, [result, event]);
                Ext.callback(callback.callback, callback.scope, [result, event]);
            }
        }
    },
    
    
    onData: function(options, success, response){
        var me = this,
            i = 0,
            len,
            events,
            event,
            transaction,
            transactions;
            
        if (success) {
            events = me.createEvents(response);
            for (len = events.length; i < len; ++i) {
                event = events[i];
                transaction = me.getTransaction(event);
                me.fireEvent('data', me, event);
                if (transaction) {
                    me.runCallback(transaction, event, true);
                    Ext.direct.Manager.removeTransaction(transaction);
                }
            }
        } else {
            transactions = [].concat(options.transaction);
            for (len = transactions.length; i < len; ++i) {
                transaction = me.getTransaction(transactions[i]);
                if (transaction && transaction.retryCount < me.maxRetries) {
                    transaction.retry();
                } else {
                    event = Ext.create('Ext.direct.ExceptionEvent', {
                        data: null,
                        transaction: transaction,
                        code: Ext.direct.Manager.self.exceptions.TRANSPORT,
                        message: 'Unable to connect to the server.',
                        xhr: response
                    });
                    me.fireEvent('data', me, event);
                    if (transaction) {
                        me.runCallback(transaction, event, false);
                        Ext.direct.Manager.removeTransaction(transaction);
                    }
                }
            }
        }
    },
    
    
    getTransaction: function(options){
        return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
    },
    
    
    configureRequest: function(action, method, args){
        var me = this,
            callData = method.getCallData(args),
            data = callData.data, 
            callback = callData.callback, 
            scope = callData.scope,
            transaction;

        transaction = Ext.create('Ext.direct.Transaction', {
            provider: me,
            args: args,
            action: action,
            method: method.name,
            data: data,
            callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback
        });

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            me.queueTransaction(transaction);
            me.fireEvent('call', me, transaction, method);
        }
    },
    
    
    getCallData: function(transaction){
        return {
            action: transaction.action,
            method: transaction.method,
            data: transaction.data,
            type: 'rpc',
            tid: transaction.id
        };
    },
    
    
    sendRequest : function(data){
        var me = this,
            request = {
                url: me.url,
                callback: me.onData,
                scope: me,
                transaction: data,
                timeout: me.timeout
            }, callData,
            enableUrlEncode = me.enableUrlEncode,
            i = 0,
            len,
            params;
            

        if (Ext.isArray(data)) {
            callData = [];
            for (len = data.length; i < len; ++i) {
                callData.push(me.getCallData(data[i]));
            }
        } else {
            callData = me.getCallData(data);
        }

        if (enableUrlEncode) {
            params = {};
            params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);
            request.params = params;
        } else {
            request.jsonData = callData;
        }
        Ext.Ajax.request(request);
    },
    
    
    queueTransaction: function(transaction){
        var me = this,
            enableBuffer = me.enableBuffer;
        
        if (transaction.form) {
            me.sendFormRequest(transaction);
            return;
        }
        
        me.callBuffer.push(transaction);
        if (enableBuffer) {
            if (!me.callTask) {
                me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me);
            }
            me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
        } else {
            me.combineAndSend();
        }
    },
    
    
    combineAndSend : function(){
        var buffer = this.callBuffer,
            len = buffer.length;
            
        if (len > 0) {
            this.sendRequest(len == 1 ? buffer[0] : buffer);
            this.callBuffer = [];
        }
    },
    
    
    configureFormRequest : function(action, method, form, callback, scope){
        var me = this,
            transaction = Ext.create('Ext.direct.Transaction', {
                provider: me,
                action: action,
                method: method.name,
                args: [form, callback, scope],
                callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
                isForm: true
            }),
            isUpload,
            params;

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data';
            
            params = {
                extTID: transaction.id,
                extAction: action,
                extMethod: method.name,
                extType: 'rpc',
                extUpload: String(isUpload)
            };
            
            
            
            Ext.apply(transaction, {
                form: Ext.getDom(form),
                isUpload: isUpload,
                params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
            });
            me.fireEvent('call', me, transaction, method);
            me.sendFormRequest(transaction);
        }
    },
    
    
    sendFormRequest: function(transaction){
        Ext.Ajax.request({
            url: this.url,
            params: transaction.params,
            callback: this.onData,
            scope: this,
            form: transaction.form,
            isUpload: transaction.isUpload,
            transaction: transaction
        });
    }
    
});


Ext.define('Ext.draw.Matrix', {

    

    requires: ['Ext.draw.Draw'],

    

    constructor: function(a, b, c, d, e, f) {
        if (a != null) {
            this.matrix = [[a, c, e], [b, d, f], [0, 0, 1]];
        }
        else {
            this.matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
        }
    },

    add: function(a, b, c, d, e, f) {
        var me = this,
            out = [[], [], []],
            matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
            x,
            y,
            z,
            res;

        for (x = 0; x < 3; x++) {
            for (y = 0; y < 3; y++) {
                res = 0;
                for (z = 0; z < 3; z++) {
                    res += me.matrix[x][z] * matrix[z][y];
                }
                out[x][y] = res;
            }
        }
        me.matrix = out;
    },

    prepend: function(a, b, c, d, e, f) {
        var me = this,
            out = [[], [], []],
            matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
            x,
            y,
            z,
            res;

        for (x = 0; x < 3; x++) {
            for (y = 0; y < 3; y++) {
                res = 0;
                for (z = 0; z < 3; z++) {
                    res += matrix[x][z] * me.matrix[z][y];
                }
                out[x][y] = res;
            }
        }
        me.matrix = out;
    },

    invert: function() {
        var matrix = this.matrix,
            a = matrix[0][0],
            b = matrix[1][0],
            c = matrix[0][1],
            d = matrix[1][1],
            e = matrix[0][2],
            f = matrix[1][2],
            x = a * d - b * c;
        return new Ext.draw.Matrix(d / x, -b / x, -c / x, a / x, (c * f - d * e) / x, (b * e - a * f) / x);
    },

    clone: function() {
        var matrix = this.matrix,
            a = matrix[0][0],
            b = matrix[1][0],
            c = matrix[0][1],
            d = matrix[1][1],
            e = matrix[0][2],
            f = matrix[1][2];
        return new Ext.draw.Matrix(a, b, c, d, e, f);
    },

    translate: function(x, y) {
        this.prepend(1, 0, 0, 1, x, y);
    },

    scale: function(x, y, cx, cy) {
        var me = this;
        if (y == null) {
            y = x;
        }
        me.add(1, 0, 0, 1, cx, cy);
        me.add(x, 0, 0, y, 0, 0);
        me.add(1, 0, 0, 1, -cx, -cy);
    },

    rotate: function(a, x, y) {
        a = Ext.draw.Draw.rad(a);
        var me = this,
            cos = +Math.cos(a).toFixed(9),
            sin = +Math.sin(a).toFixed(9);
        me.add(cos, sin, -sin, cos, x, y);
        me.add(1, 0, 0, 1, -x, -y);
    },

    x: function(x, y) {
        var matrix = this.matrix;
        return x * matrix[0][0] + y * matrix[0][1] + matrix[0][2];
    },

    y: function(x, y) {
        var matrix = this.matrix;
        return x * matrix[1][0] + y * matrix[1][1] + matrix[1][2];
    },

    get: function(i, j) {
        return + this.matrix[i][j].toFixed(4);
    },

    toString: function() {
        var me = this;
        return [me.get(0, 0), me.get(0, 1), me.get(1, 0), me.get(1, 1), 0, 0].join();
    },

    toSvg: function() {
        var me = this;
        return "matrix(" + [me.get(0, 0), me.get(1, 0), me.get(0, 1), me.get(1, 1), me.get(0, 2), me.get(1, 2)].join() + ")";
    },

    toFilter: function() {
        var me = this;
        return "progid:DXImageTransform.Microsoft.Matrix(M11=" + me.get(0, 0) +
            ", M12=" + me.get(0, 1) + ", M21=" + me.get(1, 0) + ", M22=" + me.get(1, 1) +
            ", Dx=" + me.get(0, 2) + ", Dy=" + me.get(1, 2) + ")";
    },

    offset: function() {
        var matrix = this.matrix;
        return [matrix[0][2].toFixed(4), matrix[1][2].toFixed(4)];
    },

    
    split: function () {
        function norm(a) {
            return a[0] * a[0] + a[1] * a[1];
        }
        function normalize(a) {
            var mag = Math.sqrt(norm(a));
            a[0] /= mag;
            a[1] /= mag;
        }
        var matrix = this.matrix,
            out = {
                translateX: matrix[0][2],
                translateY: matrix[1][2]
            },
            row;

        
        row = [[matrix[0][0], matrix[0][1]], [matrix[1][1], matrix[1][1]]];
        out.scaleX = Math.sqrt(norm(row[0]));
        normalize(row[0]);

        out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
        row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];

        out.scaleY = Math.sqrt(norm(row[1]));
        normalize(row[1]);
        out.shear /= out.scaleY;

        
        out.rotate = Math.asin(-row[0][1]);

        out.isSimple = !+out.shear.toFixed(9) && (out.scaleX.toFixed(9) == out.scaleY.toFixed(9) || !out.rotate);

        return out;
    }
});

Ext.define('Ext.draw.SpriteDD', {
    extend: 'Ext.dd.DragSource',

    constructor : function(sprite, cfg){
        var me = this,
            el = sprite.el;
        me.sprite = sprite;
        me.el = el;
        me.dragData = {el: el, sprite: sprite};
        me.callParent([el, cfg]);
        me.sprite.setStyle('cursor', 'move');
    },

    showFrame: Ext.emptyFn,
    createFrame : Ext.emptyFn,

    getDragEl : function(e){
        return this.el;
    },
    
    getRegion: function() {
        var me = this,
            el = me.el,
            pos, x1, x2, y1, y2, t, r, b, l, bbox, sprite;
        
        sprite = me.sprite;
        bbox = sprite.getBBox();
        
        try {
            pos = Ext.core.Element.getXY(el);
        } catch (e) { }

        if (!pos) {
            return null;
        }

        x1 = pos[0];
        x2 = x1 + bbox.width;
        y1 = pos[1];
        y2 = y1 + bbox.height;
        
        return Ext.create('Ext.util.Region', y1, x2, y2, x1);
    },

    
     
    startDrag: function(x, y) {
        var me = this,
            attr = me.sprite.attr,
            trans = attr.translation;
        if (me.sprite.vml) {
            me.prevX = x + attr.x;
            me.prevY = y + attr.y;
        } else {
            me.prevX = x - trans.x;
            me.prevY = y - trans.y;
        }
    },

    onDrag: function(e) {
        var xy = e.getXY(),
            me = this,
            sprite = me.sprite,
            attr = sprite.attr;
        me.translateX = xy[0] - me.prevX;
        me.translateY = xy[1] - me.prevY;
        sprite.setAttributes({
            translate: {
                x: me.translateX,
                y: me.translateY
            }
        }, true);
        if (sprite.vml) {
            me.prevX = xy[0] + attr.x || 0;
            me.prevY = xy[1] + attr.y || 0;
        }
    }
});

Ext.define('Ext.draw.Sprite', {
    

    mixins: {
        observable: 'Ext.util.Observable',
        animate: 'Ext.util.Animate'
    },

    requires: ['Ext.draw.SpriteDD'],

    

    dirty: false,
    dirtyHidden: false,
    dirtyTransform: false,
    dirtyPath: true,
    dirtyFont: true,
    zIndexDirty: true,
    isSprite: true,
    zIndex: 0,
    fontProperties: [
        'font',
        'font-size',
        'font-weight',
        'font-style',
        'font-family',
        'text-anchor',
        'text'
    ],
    pathProperties: [
        'x',
        'y',
        'd',
        'path',
        'height',
        'width',
        'radius',
        'r',
        'rx',
        'ry',
        'cx',
        'cy'
    ],
    constructor: function(config) {
        var me = this;
        config = config || {};
        me.id = Ext.id(null, 'ext-sprite-');
        me.transformations = [];
        Ext.copyTo(this, config, 'surface,group,type,draggable');
        
        me.bbox = {};
        me.attr = {
            zIndex: 0,
            translation: {
                x: null,
                y: null
            },
            rotation: {
                degrees: null,
                x: null,
                y: null
            },
            scaling: {
                x: null,
                y: null,
                cx: null,
                cy: null
            }
        };
        
        delete config.surface;
        delete config.group;
        delete config.type;
        delete config.draggable;
        me.setAttributes(config);
        me.addEvents(
            'beforedestroy',
            'destroy',
            'render',
            'mousedown',
            'mouseup',
            'mouseover',
            'mouseout',
            'mousemove',
            'click'
        );
        me.mixins.observable.constructor.apply(this, arguments);
    },

    
    initDraggable: function() {
        var me = this;
        me.draggable = true;
        
        if (!me.el) {
            me.surface.createSpriteElement(me);
        }
        me.dd = Ext.create('Ext.draw.SpriteDD', me, Ext.isBoolean(me.draggable) ? null : me.draggable);
        me.on('beforedestroy', me.dd.destroy, me.dd);
    },

    
    setAttributes: function(attrs, redraw) {
        var me = this,
            fontProps = me.fontProperties,
            fontPropsLength = fontProps.length,
            pathProps = me.pathProperties,
            pathPropsLength = pathProps.length,
            hasSurface = !!me.surface,
            custom = hasSurface && me.surface.customAttributes || {},
            spriteAttrs = me.attr,
            attr, i, translate, translation, rotate, rotation, scale, scaling;

        attrs = Ext.apply({}, attrs);
        for (attr in custom) {
            if (attrs.hasOwnProperty(attr) && typeof custom[attr] == "function") {
                Ext.apply(attrs, custom[attr].apply(me, [].concat(attrs[attr])));
            }
        }

        
        if (!!attrs.hidden !== !!spriteAttrs.hidden) {
            me.dirtyHidden = true;
        }

        
        for (i = 0; i < pathPropsLength; i++) {
            attr = pathProps[i];
            if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) {
                me.dirtyPath = true;
                break;
            }
        }

        
        if ('zIndex' in attrs) {
            me.zIndexDirty = true;
        }

        
        for (i = 0; i < fontPropsLength; i++) {
            attr = fontProps[i];
            if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) {
                me.dirtyFont = true;
                break;
            }
        }

        translate = attrs.translate;
        translation = spriteAttrs.translation;
        if (translate) {
            if ((translate.x && translate.x !== translation.x) ||
                (translate.y && translate.y !== translation.y)) {
                Ext.apply(translation, translate);
                me.dirtyTransform = true;
            }
            delete attrs.translate;
        }

        rotate = attrs.rotate;
        rotation = spriteAttrs.rotation;
        if (rotate) {
            if ((rotate.x && rotate.x !== rotation.x) || 
                (rotate.y && rotate.y !== rotation.y) ||
                (rotate.degrees && rotate.degrees !== rotation.degrees)) {
                Ext.apply(rotation, rotate);
                me.dirtyTransform = true;
            }
            delete attrs.rotate;
        }

        scale = attrs.scale;
        scaling = spriteAttrs.scaling;
        if (scale) {
            if ((scale.x && scale.x !== scaling.x) || 
                (scale.y && scale.y !== scaling.y) ||
                (scale.cx && scale.cx !== scaling.cx) ||
                (scale.cy && scale.cy !== scaling.cy)) {
                Ext.apply(scaling, scale);
                me.dirtyTransform = true;
            }
            delete attrs.scale;
        }

        Ext.apply(spriteAttrs, attrs);
        me.dirty = true;

        if (redraw === true && hasSurface) {
            me.redraw();
        }
        return this;
    },

    
    getBBox: function() {
        return this.surface.getBBox(this);
    },
    
    setText: function(text) {
        return this.surface.setText(this, text);
    },

    
    hide: function(redraw) {
        this.setAttributes({
            hidden: true
        }, redraw);
        return this;
    },

    
    show: function(redraw) {
        this.setAttributes({
            hidden: false
        }, redraw);
        return this;
    },

    
    remove: function() {
        if (this.surface) {
            this.surface.remove(this);
            return true;
        }
        return false;
    },

    onRemove: function() {
        this.surface.onRemove(this);
    },

    
    destroy: function() {
        var me = this;
        if (me.fireEvent('beforedestroy', me) !== false) {
            me.remove();
            me.surface.onDestroy(me);
            me.clearListeners();
            me.fireEvent('destroy');
        }
    },

    
    redraw: function() {
        this.surface.renderItem(this);
        return this;
    },

    
    setStyle: function() {
        this.el.setStyle.apply(this.el, arguments);
        return this;
    },

    
    addCls: function(obj) {
        this.surface.addCls(this, obj);
        return this;
    },

    
    removeCls: function(obj) {
        this.surface.removeCls(this, obj);
        return this;
    }
});


Ext.define('Ext.draw.engine.Svg', {

    

    extend: 'Ext.draw.Surface',

    requires: ['Ext.draw.Draw', 'Ext.draw.Sprite', 'Ext.draw.Matrix', 'Ext.core.Element'],

    

    engine: 'Svg',

    trimRe: /^\s+|\s+$/g,
    spacesRe: /\s+/,
    xlink: "http:/" + "/www.w3.org/1999/xlink",

    translateAttrs: {
        radius: "r",
        radiusX: "rx",
        radiusY: "ry",
        path: "d",
        lineWidth: "stroke-width",
        fillOpacity: "fill-opacity",
        strokeOpacity: "stroke-opacity",
        strokeLinejoin: "stroke-linejoin"
    },

    minDefaults: {
        circle: {
            cx: 0,
            cy: 0,
            r: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        ellipse: {
            cx: 0,
            cy: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        rect: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        text: {
            x: 0,
            y: 0,
            "text-anchor": "start",
            "font-family": null,
            "font-size": null,
            "font-weight": null,
            "font-style": null,
            fill: "#000",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        path: {
            d: "M0,0",
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        image: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            preserveAspectRatio: "none",
            opacity: null
        }
    },

    createSvgElement: function(type, attrs) {
        var el = this.domRef.createElementNS("http:/" + "/www.w3.org/2000/svg", type),
            key;
        if (attrs) {
            for (key in attrs) {
                el.setAttribute(key, String(attrs[key]));
            }
        }
        return el;
    },

    createSpriteElement: function(sprite) {
        
        var el = this.createSvgElement(sprite.type);
        el.id = sprite.id;
        if (el.style) {
            el.style.webkitTapHighlightColor = "rgba(0,0,0,0)";
        }
        sprite.el = Ext.get(el);
        this.applyZIndex(sprite); 
        sprite.matrix = Ext.create('Ext.draw.Matrix');
        sprite.bbox = {
            plain: 0,
            transform: 0
        };
        sprite.fireEvent("render", sprite);
        return el;
    },

    getBBox: function (sprite, isWithoutTransform) {
        var realPath = this["getPath" + sprite.type](sprite);
        if (isWithoutTransform) {
            sprite.bbox.plain = sprite.bbox.plain || Ext.draw.Draw.pathDimensions(realPath);
            return sprite.bbox.plain;
        }
        sprite.bbox.transform = sprite.bbox.transform || Ext.draw.Draw.pathDimensions(Ext.draw.Draw.mapPath(realPath, sprite.matrix));
        return sprite.bbox.transform;
    },
    
    getBBoxText: function (sprite) {
        var bbox = {},
            bb, height, width, i, ln, el;

        if (sprite && sprite.el) {
            el = sprite.el.dom;
            try {
                bbox = el.getBBox();
                return bbox;
            } catch(e) {
                
            }
            bbox = {x: bbox.x, y: Infinity, width: 0, height: 0};
            ln = el.getNumberOfChars();
            for (i = 0; i < ln; i++) {
                bb = el.getExtentOfChar(i);
                bbox.y = Math.min(bb.y, bbox.y);
                height = bb.y + bb.height - bbox.y;
                bbox.height = Math.max(bbox.height, height);
                width = bb.x + bb.width - bbox.x;
                bbox.width = Math.max(bbox.width, width);
            }
            return bbox;
        }
    },

    hide: function() {
        Ext.get(this.el).hide();
    },

    show: function() {
        Ext.get(this.el).show();
    },

    hidePrim: function(sprite) {
        this.addCls(sprite, Ext.baseCSSPrefix + 'hide-visibility');
    },

    showPrim: function(sprite) {
        this.removeCls(sprite, Ext.baseCSSPrefix + 'hide-visibility');
    },

    getDefs: function() {
        return this._defs || (this._defs = this.createSvgElement("defs"));
    },

    transform: function(sprite) {
        var me = this,
            matrix = Ext.create('Ext.draw.Matrix'),
            transforms = sprite.transformations,
            transformsLength = transforms.length,
            i = 0,
            transform, type;
            
        for (; i < transformsLength; i++) {
            transform = transforms[i];
            type = transform.type;
            if (type == "translate") {
                matrix.translate(transform.x, transform.y);
            }
            else if (type == "rotate") {
                matrix.rotate(transform.degrees, transform.x, transform.y);
            }
            else if (type == "scale") {
                matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY);
            }
        }
        sprite.matrix = matrix;
        sprite.el.set({transform: matrix.toSvg()});
    },

    setSize: function(w, h) {
        var me = this,
            el = me.el;
        
        w = +w || me.width;
        h = +h || me.height;
        me.width = w;
        me.height = h;

        el.setSize(w, h);
        el.set({
            width: w,
            height: h
        });
        me.callParent([w, h]);
    },

    
    getRegion: function() {
        
        
        var svgXY = this.el.getXY(),
            rectXY = this.bgRect.getXY(),
            max = Math.max,
            x = max(svgXY[0], rectXY[0]),
            y = max(svgXY[1], rectXY[1]);
        return {
            left: x,
            top: y,
            right: x + this.width,
            bottom: y + this.height
        };
    },

    onRemove: function(sprite) {
        if (sprite.el) {
            sprite.el.remove();
            delete sprite.el;
        }
        this.callParent(arguments);
    },
    
    setViewBox: function(x, y, width, height) {
        if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) {
            this.callParent(arguments);
            this.el.dom.setAttribute("viewBox", [x, y, width, height].join(" "));
        }
    },

    render: function (container) {
        var me = this;
        if (!me.el) {
            var width = me.width || 10,
                height = me.height || 10,
                el = me.createSvgElement('svg', {
                    xmlns: "http:/" + "/www.w3.org/2000/svg",
                    version: 1.1,
                    width: width,
                    height: height
                }),
                defs = me.getDefs(),

                
                
                
                
                bgRect = me.createSvgElement("rect", {
                    width: "100%",
                    height: "100%",
                    fill: "#000",
                    stroke: "none",
                    opacity: 0
                }),
                webkitRect;
            
                if (Ext.isSafari3) {
                    
                    webkitRect = me.createSvgElement("rect", {
                        x: -10,
                        y: -10,
                        width: "110%",
                        height: "110%",
                        fill: "none",
                        stroke: "#000"
                    });
                }
            el.appendChild(defs);
            if (Ext.isSafari3) {
                el.appendChild(webkitRect);
            }
            el.appendChild(bgRect);
            container.appendChild(el);
            me.el = Ext.get(el);
            me.bgRect = Ext.get(bgRect);
            if (Ext.isSafari3) {
                me.webkitRect = Ext.get(webkitRect);
                me.webkitRect.hide();
            }
            me.el.on({
                scope: me,
                mouseup: me.onMouseUp,
                mousedown: me.onMouseDown,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousemove: me.onMouseMove,
                mouseenter: me.onMouseEnter,
                mouseleave: me.onMouseLeave,
                click: me.onClick
            });
        }
        me.renderAll();
    },

    
    onMouseEnter: function(e) {
        if (this.el.parent().getRegion().contains(e.getPoint())) {
            this.fireEvent('mouseenter', e);
        }
    },

    
    onMouseLeave: function(e) {
        if (!this.el.parent().getRegion().contains(e.getPoint())) {
            this.fireEvent('mouseleave', e);
        }
    },
    
    processEvent: function(name, e) {
        var target = e.getTarget(),
            surface = this.surface,
            sprite;

        this.fireEvent(name, e);
        
        if (target.nodeName == "tspan" && target.parentNode) {
            target = target.parentNode;
        }
        sprite = this.items.get(target.id);
        if (sprite) {
            sprite.fireEvent(name, sprite, e);
        }
    },

    
    tuneText: function (sprite, attrs) {
        var el = sprite.el.dom,
            tspans = [],
            height, tspan, text, i, ln, texts, factor;

        if (attrs.hasOwnProperty("text")) {
           tspans = this.setText(sprite, attrs.text);
        }
        
        if (tspans.length) {
            height = this.getBBoxText(sprite).height;
            for (i = 0, ln = tspans.length; i < ln; i++) {
                
                
                factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4;
                tspans[i].setAttribute("dy", i ? height * 1.2 : height / factor);
            }
            sprite.dirty = true;
        }
    },

    setText: function(sprite, textString) {
         var me = this,
             el = sprite.el.dom,
             x = el.getAttribute("x"),
             tspans = [],
             height, tspan, text, i, ln, texts;
        
        while (el.firstChild) {
            el.removeChild(el.firstChild);
        }
        
        texts = String(textString).split("\n");
        for (i = 0, ln = texts.length; i < ln; i++) {
            text = texts[i];
            if (text) {
                tspan = me.createSvgElement("tspan");
                tspan.appendChild(document.createTextNode(Ext.htmlDecode(text)));
                tspan.setAttribute("x", x);
                el.appendChild(tspan);
                tspans[i] = tspan;
            }
        }
        return tspans;
    },

    renderAll: function() {
        this.items.each(this.renderItem, this);
    },

    renderItem: function (sprite) {
        if (!this.el) {
            return;
        }
        if (!sprite.el) {
            this.createSpriteElement(sprite);
        }
        if (sprite.zIndexDirty) {
            this.applyZIndex(sprite);
        }
        if (sprite.dirty) {
            this.applyAttrs(sprite);
            this.applyTransformations(sprite);
        }
    },

    redraw: function(sprite) {
        sprite.dirty = sprite.zIndexDirty = true;
        this.renderItem(sprite);
    },

    applyAttrs: function (sprite) {
        var me = this,
            el = sprite.el,
            group = sprite.group,
            sattr = sprite.attr,
            groups, i, ln, attrs, font, key, style, name, rect;

        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                me.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        attrs = me.scrubAttrs(sprite) || {};

        
            sprite.bbox.plain = 0;
            sprite.bbox.transform = 0;
            if (sprite.type == "circle" || sprite.type == "ellipse") {
                attrs.cx = attrs.cx || attrs.x;
                attrs.cy = attrs.cy || attrs.y;
            }
            else if (sprite.type == "rect") {
                attrs.rx = attrs.ry = attrs.r;
            }
            else if (sprite.type == "path" && attrs.d) {
                attrs.d = Ext.draw.Draw.pathToString(Ext.draw.Draw.pathToAbsolute(attrs.d));
                
            }
            sprite.dirtyPath = false;
        
        
        
        

        if (attrs['clip-rect']) {
            me.setClip(sprite, attrs);
            delete attrs['clip-rect'];
        }
        if (sprite.type == 'text' && attrs.font && sprite.dirtyFont) {
            el.set({ style: "font: " + attrs.font});
            sprite.dirtyFont = false;
        }
        if (sprite.type == "image") {
            el.dom.setAttributeNS(me.xlink, "href", attrs.src);
        }
        Ext.applyIf(attrs, me.minDefaults[sprite.type]);

        if (sprite.dirtyHidden) {
            (sattr.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);
            sprite.dirtyHidden = false;
        }
        for (key in attrs) {
            if (attrs.hasOwnProperty(key) && attrs[key] != null) {
                el.dom.setAttribute(key, attrs[key]);
            }
        }
        if (sprite.type == 'text') {
            me.tuneText(sprite, attrs);
        }

        
        style = sattr.style;
        if (style) {
            el.setStyle(style);
        }

        sprite.dirty = false;

        if (Ext.isSafari3) {
            
            me.webkitRect.show();
            setTimeout(function () {
                me.webkitRect.hide();
            });
        }
    },

    setClip: function(sprite, params) {
        var me = this,
            rect = params["clip-rect"],
            clipEl, clipPath;
        if (rect) {
            if (sprite.clip) {
                sprite.clip.parentNode.parentNode.removeChild(sprite.clip.parentNode);
            }
            clipEl = me.createSvgElement('clipPath');
            clipPath = me.createSvgElement('rect');
            clipEl.id = Ext.id(null, 'ext-clip-');
            clipPath.setAttribute("x", rect.x);
            clipPath.setAttribute("y", rect.y);
            clipPath.setAttribute("width", rect.width);
            clipPath.setAttribute("height", rect.height);
            clipEl.appendChild(clipPath);
            me.getDefs().appendChild(clipEl);
            sprite.el.dom.setAttribute("clip-path", "url(#" + clipEl.id + ")");
            sprite.clip = clipPath;
        }
        
        
        
        
        
        
    },

    
    applyZIndex: function(sprite) {
        var idx = this.normalizeSpriteCollection(sprite),
            el = sprite.el,
            prevEl;
        if (this.el.dom.childNodes[idx + 2] !== el.dom) { 
            if (idx > 0) {
                
                do {
                    prevEl = this.items.getAt(--idx).el;
                } while (!prevEl && idx > 0);
            }
            el.insertAfter(prevEl || this.bgRect);
        }
        sprite.zIndexDirty = false;
    },

    createItem: function (config) {
        var sprite = Ext.create('Ext.draw.Sprite', config);
        sprite.surface = this;
        return sprite;
    },

    addGradient: function(gradient) {
        gradient = Ext.draw.Draw.parseGradient(gradient);
        var ln = gradient.stops.length,
            vector = gradient.vector,
            gradientEl,
            stop,
            stopEl,
            i;
        if (gradient.type == "linear") {
            gradientEl = this.createSvgElement("linearGradient");
            gradientEl.setAttribute("x1", vector[0]);
            gradientEl.setAttribute("y1", vector[1]);
            gradientEl.setAttribute("x2", vector[2]);
            gradientEl.setAttribute("y2", vector[3]);
        }
        else {
            gradientEl = this.createSvgElement("radialGradient");
            gradientEl.setAttribute("cx", gradient.centerX);
            gradientEl.setAttribute("cy", gradient.centerY);
            gradientEl.setAttribute("r", gradient.radius);
            if (Ext.isNumber(gradient.focalX) && Ext.isNumber(gradient.focalY)) {
                gradientEl.setAttribute("fx", gradient.focalX);
                gradientEl.setAttribute("fy", gradient.focalY);
            }
        }    
        gradientEl.id = gradient.id;
        this.getDefs().appendChild(gradientEl);

        for (i = 0; i < ln; i++) {
            stop = gradient.stops[i];
            stopEl = this.createSvgElement("stop");
            stopEl.setAttribute("offset", stop.offset + "%");
            stopEl.setAttribute("stop-color", stop.color);
            stopEl.setAttribute("stop-opacity",stop.opacity);
            gradientEl.appendChild(stopEl);
        }
    },

    
    hasCls: function(sprite, className) {
        return className && (' ' + (sprite.el.dom.getAttribute('class') || '') + ' ').indexOf(' ' + className + ' ') != -1;
    },

    addCls: function(sprite, className) {
        var el = sprite.el,
            i,
            len,
            v,
            cls = [],
            curCls =  el.getAttribute('class') || '';
        
        if (!Ext.isArray(className)) {
            if (typeof className == 'string' && !this.hasCls(sprite, className)) {
                el.set({ 'class': curCls + ' ' + className });
            }
        }
        else {
            for (i = 0, len = className.length; i < len; i++) {
                v = className[i];
                if (typeof v == 'string' && (' ' + curCls + ' ').indexOf(' ' + v + ' ') == -1) {
                    cls.push(v);
                }
            }
            if (cls.length) {
                el.set({ 'class': ' ' + cls.join(' ') });
            }
        }
    },

    removeCls: function(sprite, className) {
        var me = this,
            el = sprite.el,
            curCls =  el.getAttribute('class') || '',
            i, idx, len, cls, elClasses;
        if (!Ext.isArray(className)){
            className = [className];
        }
        if (curCls) {
            elClasses = curCls.replace(me.trimRe, ' ').split(me.spacesRe);
            for (i = 0, len = className.length; i < len; i++) {
                cls = className[i];
                if (typeof cls == 'string') {
                    cls = cls.replace(me.trimRe, '');
                    idx = Ext.Array.indexOf(elClasses, cls);
                    if (idx != -1) {
                        elClasses.splice(idx, 1);
                    }
                }
            }
            el.set({ 'class': elClasses.join(' ') });
        }
    },

    destroy: function() {
        var me = this;
        
        me.callParent();
        if (me.el) {
            me.el.remove();
        }
        delete me.el;
    }
});


Ext.define('Ext.draw.engine.Vml', {

    

    extend: 'Ext.draw.Surface',

    requires: ['Ext.draw.Draw', 'Ext.draw.Color', 'Ext.draw.Sprite', 'Ext.draw.Matrix', 'Ext.core.Element'],

    

    engine: 'Vml',

    map: {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
    bitesRe: /([clmz]),?([^clmz]*)/gi,
    valRe: /-?[^,\s-]+/g,
    fillUrlRe: /^url\(\s*['"]?([^\)]+?)['"]?\s*\)$/i,
    pathlike: /^(path|rect)$/,
    NonVmlPathRe: /[ahqstv]/ig, // Non-VML Pathing ops
    partialPathRe: /[clmz]/g,
    fontFamilyRe: /^['"]+|['"]+$/g,
    baseVmlCls: Ext.baseCSSPrefix + 'vml-base',
    vmlGroupCls: Ext.baseCSSPrefix + 'vml-group',
    spriteCls: Ext.baseCSSPrefix + 'vml-sprite',
    measureSpanCls: Ext.baseCSSPrefix + 'vml-measure-span',
    zoom: 21600,
    coordsize: 1000,
    coordorigin: '0 0',

    // @private
    // Convert an SVG standard path into a VML path
    path2vml: function (path) {
        var me = this,
            nonVML =  me.NonVmlPathRe,
            map = me.map,
            val = me.valRe,
            zoom = me.zoom,
            bites = me.bitesRe,
            command = Ext.Function.bind(Ext.draw.Draw.pathToAbsolute, Ext.draw.Draw),
            res, pa, p, r, i, ii, j, jj;
        if (String(path).match(nonVML)) {
            command = Ext.Function.bind(Ext.draw.Draw.path2curve, Ext.draw.Draw);
        } else if (!String(path).match(me.partialPathRe)) {
            res = String(path).replace(bites, function (all, command, args) {
                var vals = [],
                    isMove = command.toLowerCase() == "m",
                    res = map[command];
                args.replace(val, function (value) {
                    if (isMove && vals[length] == 2) {
                        res += vals + map[command == "m" ? "l" : "L"];
                        vals = [];
                    }
                    vals.push(Math.round(value * zoom));
                });
                return res + vals;
            });
            return res;
        }
        pa = command(path);
        res = [];
        for (i = 0, ii = pa.length; i < ii; i++) {
            p = pa[i];
            r = pa[i][0].toLowerCase();
            if (r == "z") {
                r = "x";
            }
            for (j = 1, jj = p.length; j < jj; j++) {
                r += Math.round(p[j] * me.zoom) + (j != jj - 1 ? "," : "");
            }
            res.push(r);
        }
        return res.join(" ");
    },

    // @private - set of attributes which need to be translated from the sprite API to the native browser API
    translateAttrs: {
        radius: "r",
        radiusX: "rx",
        radiusY: "ry",
        lineWidth: "stroke-width",
        fillOpacity: "fill-opacity",
        strokeOpacity: "stroke-opacity",
        strokeLinejoin: "stroke-linejoin"
    },

    // @private - Minimun set of defaults for different types of sprites.
    minDefaults: {
        circle: {
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        ellipse: {
            cx: 0,
            cy: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        rect: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            rx: 0,
            ry: 0,
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        text: {
            x: 0,
            y: 0,
            "text-anchor": "start",
            font: '10px "Arial"',
            fill: "#000",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        path: {
            d: "M0,0",
            fill: "none",
            stroke: null,
            "stroke-width": null,
            opacity: null,
            "fill-opacity": null,
            "stroke-opacity": null
        },
        image: {
            x: 0,
            y: 0,
            width: 0,
            height: 0,
            preserveAspectRatio: "none",
            opacity: null
        }
    },

    // private
    onMouseEnter: function(e) {
        this.fireEvent("mouseenter", e);
    },

    // private
    onMouseLeave: function(e) {
        this.fireEvent("mouseleave", e);
    },

    // @private - Normalize a delegated single event from the main container to each sprite and sprite group
    processEvent: function(name, e) {
        var target = e.getTarget(),
            surface = this.surface,
            sprite;
        this.fireEvent(name, e);
        sprite = this.items.get(target.id);
        if (sprite) {
            sprite.fireEvent(name, sprite, e);
        }
    },

    // Create the VML element/elements and append them to the DOM
    createSpriteElement: function(sprite) {
        var me = this,
            attr = sprite.attr,
            type = sprite.type,
            zoom = me.zoom,
            vml = sprite.vml || (sprite.vml = {}),
            round = Math.round,
            el = (type === 'image') ? me.createNode('image') : me.createNode('shape'),
            path, skew, textPath;

        el.coordsize = zoom + ' ' + zoom;
        el.coordorigin = attr.coordorigin || "0 0";
        Ext.get(el).addCls(me.spriteCls);
        if (type == "text") {
            vml.path = path = me.createNode("path");
            path.textpathok = true;
            vml.textpath = textPath = me.createNode("textpath");
            textPath.on = true;
            el.appendChild(textPath);
            el.appendChild(path);
        }
        el.id = sprite.id;
        sprite.el = Ext.get(el);
        me.el.appendChild(el);
        if (type !== 'image') {
            skew = me.createNode("skew");
            skew.on = true;
            el.appendChild(skew);
            sprite.skew = skew;
        }
        sprite.matrix = Ext.create('Ext.draw.Matrix');
        sprite.bbox = {
            plain: null,
            transform: null
        };
        sprite.fireEvent("render", sprite);
        return sprite.el;
    },

    // @private - Get bounding box for the sprite.  The Sprite itself has the public method.
    getBBox: function (sprite, isWithoutTransform) {
        var realPath = this["getPath" + sprite.type](sprite);
        if (isWithoutTransform) {
            sprite.bbox.plain = sprite.bbox.plain || Ext.draw.Draw.pathDimensions(realPath);
            return sprite.bbox.plain;
        }
        sprite.bbox.transform = sprite.bbox.transform || Ext.draw.Draw.pathDimensions(Ext.draw.Draw.mapPath(realPath, sprite.matrix));
        return sprite.bbox.transform;
    },

    getBBoxText: function (sprite) {
        var vml = sprite.vml;
        return {
            x: vml.X + (vml.bbx || 0) - vml.W / 2,
            y: vml.Y - vml.H / 2,
            width: vml.W,
            height: vml.H
        };
    },

    applyAttrs: function (sprite) {
        var me = this,
            vml = sprite.vml,
            group = sprite.group,
            spriteAttr = sprite.attr,
            el = sprite.el,
            dom = el.dom,
            style, name, groups, i, ln, scrubbedAttrs, font, key, bbox;

        if (group) {
            groups = [].concat(group);
            ln = groups.length;
            for (i = 0; i < ln; i++) {
                group = groups[i];
                me.getGroup(group).add(sprite);
            }
            delete sprite.group;
        }
        scrubbedAttrs = me.scrubAttrs(sprite) || {};

        if (sprite.zIndexDirty) {
            me.setZIndex(sprite);
        }

        // Apply minimum default attributes
        Ext.applyIf(scrubbedAttrs, me.minDefaults[sprite.type]);

        if (sprite.type == 'image') {
            Ext.apply(sprite.attr, {
                x: scrubbedAttrs.x,
                y: scrubbedAttrs.y,
                width: scrubbedAttrs.width,
                height: scrubbedAttrs.height
            });
            bbox = sprite.getBBox();
            el.setStyle({
                width: bbox.width + 'px',
                height: bbox.height + 'px'
            });
            dom.src = scrubbedAttrs.src;
        }

        if (dom.href) {
            dom.href = scrubbedAttrs.href;
        }
        if (dom.title) {
            dom.title = scrubbedAttrs.title;
        }
        if (dom.target) {
            dom.target = scrubbedAttrs.target;
        }
        if (dom.cursor) {
            dom.cursor = scrubbedAttrs.cursor;
        }

        // Change visibility
        if (sprite.dirtyHidden) {
            (scrubbedAttrs.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);
            sprite.dirtyHidden = false;
        }

        // Update path
        if (sprite.dirtyPath) {
            if (sprite.type == "circle" || sprite.type == "ellipse") {
                var cx = scrubbedAttrs.x,
                    cy = scrubbedAttrs.y,
                    rx = scrubbedAttrs.rx || scrubbedAttrs.r || 0,
                    ry = scrubbedAttrs.ry || scrubbedAttrs.r || 0;
                dom.path = Ext.String.format("ar{0},{1},{2},{3},{4},{1},{4},{1}",
                            Math.round((cx - rx) * me.zoom),
                            Math.round((cy - ry) * me.zoom),
                            Math.round((cx + rx) * me.zoom),
                            Math.round((cy + ry) * me.zoom),
                            Math.round(cx * me.zoom));
                sprite.dirtyPath = false;
            }
            else if (sprite.type !== "text" && sprite.type !== 'image') {
                sprite.attr.path = scrubbedAttrs.path = me.setPaths(sprite, scrubbedAttrs) || scrubbedAttrs.path;
                dom.path = me.path2vml(scrubbedAttrs.path);
                sprite.dirtyPath = false;
            }
        }

        // Apply clipping
        if ("clip-rect" in scrubbedAttrs) {
            me.setClip(sprite, scrubbedAttrs);
        }

        // Handle text (special handling required)
        if (sprite.type == "text") {
            me.setTextAttributes(sprite, scrubbedAttrs);
        }

        // Handle fill and opacity
        if (scrubbedAttrs.opacity  || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {
            me.setFill(sprite, scrubbedAttrs);
        }

        // Handle stroke (all fills require a stroke element)
        if (scrubbedAttrs.stroke || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {
            me.setStroke(sprite, scrubbedAttrs);
        }
        
        //set styles
        style = spriteAttr.style;
        if (style) {
            el.setStyle(style);
        }

        sprite.dirty = false;
    },

    setZIndex: function(sprite) {
        if (sprite.el) {
            if (sprite.attr.zIndex != undefined) {
                sprite.el.setStyle('zIndex', sprite.attr.zIndex);
            }
            sprite.zIndexDirty = false;
        }
    },

    // Normalize all virtualized types into paths.
    setPaths: function(sprite, params) {
        var spriteAttr = sprite.attr;
        // Clear bbox cache
        sprite.bbox.plain = null;
        sprite.bbox.transform = null;
        if (sprite.type == 'circle') {
            spriteAttr.rx = spriteAttr.ry = params.r;
            return Ext.draw.Draw.ellipsePath(sprite);
        }
        else if (sprite.type == 'ellipse') {
            spriteAttr.rx = params.rx;
            spriteAttr.ry = params.ry;
            return Ext.draw.Draw.ellipsePath(sprite);
        }
        else if (sprite.type == 'rect') {
            spriteAttr.rx = spriteAttr.ry = params.r;
            return Ext.draw.Draw.rectPath(sprite);
        }
        else if (sprite.type == 'path' && spriteAttr.path) {
            return Ext.draw.Draw.pathToAbsolute(spriteAttr.path);
        }
        return false;
    },

    setFill: function(sprite, params) {
        var me = this,
            el = sprite.el.dom,
            fillEl = el.fill,
            newfill = false,
            opacity, gradient, fillUrl, rotation, angle;

        if (!fillEl) {
            // NOT an expando (but it sure looks like one)...
            fillEl = el.fill = me.createNode("fill");
            newfill = true;
        }
        if (Ext.isArray(params.fill)) {
            params.fill = params.fill[0];
        }
        if (params.fill == "none") {
            fillEl.on = false;
        }
        else {
            if (typeof params.opacity == "number") {
                fillEl.opacity = params.opacity;
            }
            if (typeof params["fill-opacity"] == "number") {
                fillEl.opacity = params["fill-opacity"];
            }
            fillEl.on = true;
            if (typeof params.fill == "string") {
                fillUrl = params.fill.match(me.fillUrlRe);
                if (fillUrl) {
                    fillUrl = fillUrl[1];
                    // If the URL matches one of the registered gradients, render that gradient
                    if (fillUrl.charAt(0) == "#") {
                        gradient = me.gradientsColl.getByKey(fillUrl.substring(1));
                    }
                    if (gradient) {
                        // VML angle is offset and inverted from standard, and must be adjusted to match rotation transform
                        rotation = params.rotation;
                        angle = -(gradient.angle + 270 + (rotation ? rotation.degrees : 0)) % 360;
                        // IE will flip the angle at 0 degrees...
                        if (angle === 0) {
                            angle = 180;
                        }
                        fillEl.angle = angle;
                        fillEl.type = "gradient";
                        fillEl.method = "sigma";
                        fillEl.colors.value = gradient.colors;
                    }
                    // Otherwise treat it as an image
                    else {
                        fillEl.src = fillUrl;
                        fillEl.type = "tile";
                    }
                }
                else {
                    fillEl.color = Ext.draw.Color.toHex(params.fill);
                    fillEl.src = "";
                    fillEl.type = "solid";
                }
            }
        }
        if (newfill) {
            el.appendChild(fillEl);
        }
    },

    setStroke: function(sprite, params) {
        var me = this,
            el = sprite.el.dom,
            strokeEl = sprite.strokeEl,
            newStroke = false,
            width, opacity;

        if (!strokeEl) {
            strokeEl = sprite.strokeEl = me.createNode("stroke");
            newStroke = true;
        }
        if (Ext.isArray(params.stroke)) {
            params.stroke = params.stroke[0];
        }
        if (!params.stroke || params.stroke == "none" || params.stroke == 0 || params["stroke-width"] == 0) {
            strokeEl.on = false;
        }
        else {
            strokeEl.on = true;
            if (params.stroke && !params.stroke.match(me.fillUrlRe)) {
                // VML does NOT support a gradient stroke :(
                strokeEl.color = Ext.draw.Color.toHex(params.stroke);
            }
            strokeEl.joinstyle = params["stroke-linejoin"];
            strokeEl.endcap = params["stroke-linecap"] || "round";
            strokeEl.miterlimit = params["stroke-miterlimit"] || 8;
            width = parseFloat(params["stroke-width"] || 1) * 0.75;
            opacity = params["stroke-opacity"] || 1;
            // VML Does not support stroke widths under 1, so we're going to fiddle with stroke-opacity instead.
            if (Ext.isNumber(width) && width < 1) {
                strokeEl.weight = 1;
                strokeEl.opacity = opacity * width;
            }
            else {
                strokeEl.weight = width;
                strokeEl.opacity = opacity;
            }
        }
        if (newStroke) {
            el.appendChild(strokeEl);
        }
    },

    setClip: function(sprite, params) {
        var me = this,
            el = sprite.el,
            clipEl = sprite.clipEl,
            rect = String(params["clip-rect"]).split(me.separatorRe);
        if (!clipEl) {
            clipEl = sprite.clipEl = me.el.insertFirst(Ext.getDoc().dom.createElement("div"));
            clipEl.addCls(Ext.baseCSSPrefix + 'vml-sprite');
        }
        if (rect.length == 4) {
            rect[2] = +rect[2] + (+rect[0]);
            rect[3] = +rect[3] + (+rect[1]);
            clipEl.setStyle("clip", Ext.String.format("rect({1}px {2}px {3}px {0}px)", rect[0], rect[1], rect[2], rect[3]));
            clipEl.setSize(me.el.width, me.el.height);
        }
        else {
            clipEl.setStyle("clip", "");
        }
    },

    setTextAttributes: function(sprite, params) {
        var me = this,
            vml = sprite.vml,
            textStyle = vml.textpath.style,
            spanCacheStyle = me.span.style,
            zoom = me.zoom,
            round = Math.round,
            fontObj = {
                fontSize: "font-size",
                fontWeight: "font-weight",
                fontStyle: "font-style"
            },
            fontProp,
            paramProp;
        if (sprite.dirtyFont) {
            if (params.font) {
                textStyle.font = spanCacheStyle.font = params.font;
            }
            if (params["font-family"]) {
                textStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(me.fontFamilyRe, "") + '"';
                spanCacheStyle.fontFamily = params["font-family"];
            }

            for (fontProp in fontObj) {
                paramProp = params[fontObj[fontProp]];
                if (paramProp) {
                    textStyle[fontProp] = spanCacheStyle[fontProp] = paramProp;
                }
            }

            me.setText(sprite, params.text);
            
            if (vml.textpath.string) {
                me.span.innerHTML = String(vml.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>");
            }
            vml.W = me.span.offsetWidth;
            vml.H = me.span.offsetHeight + 2; 

            
            if (params["text-anchor"] == "middle") {
                textStyle["v-text-align"] = "center";
            }
            else if (params["text-anchor"] == "end") {
                textStyle["v-text-align"] = "right";
                vml.bbx = -Math.round(vml.W / 2);
            }
            else {
                textStyle["v-text-align"] = "left";
                vml.bbx = Math.round(vml.W / 2);
            }
        }
        vml.X = params.x;
        vml.Y = params.y;
        vml.path.v = Ext.String.format("m{0},{1}l{2},{1}", Math.round(vml.X * zoom), Math.round(vml.Y * zoom), Math.round(vml.X * zoom) + 1);
        
        sprite.bbox.plain = null;
        sprite.bbox.transform = null;
        sprite.dirtyFont = false;
    },
    
    setText: function(sprite, text) {
        sprite.vml.textpath.string = Ext.htmlDecode(text);
    },

    hide: function() {
        this.el.hide();
    },

    show: function() {
        this.el.show();
    },

    hidePrim: function(sprite) {
        sprite.el.addCls(Ext.baseCSSPrefix + 'hide-visibility');
    },

    showPrim: function(sprite) {
        sprite.el.removeCls(Ext.baseCSSPrefix + 'hide-visibility');
    },

    setSize: function(width, height) {
        var me = this,
            viewBox = me.viewBox,
            scaleX, scaleY, items, i, len;
        width = width || me.width;
        height = height || me.height;
        me.width = width;
        me.height = height;

        if (!me.el) {
            return;
        }

        
        if (width != undefined) {
            me.el.setWidth(width);
        }
        if (height != undefined) {
            me.el.setHeight(height);
        }

        
        if (viewBox && (width || height)) {
            var viewBoxX = viewBox.x,
                viewBoxY = viewBox.y,
                viewBoxWidth = viewBox.width,
                viewBoxHeight = viewBox.height,
                relativeHeight = height / viewBoxHeight,
                relativeWidth = width / viewBoxWidth,
                size;
            if (viewBoxWidth * relativeHeight < width) {
                viewBoxX -= (width - viewBoxWidth * relativeHeight) / 2 / relativeHeight;
            }
            if (viewBoxHeight * relativeWidth < height) {
                viewBoxY -= (height - viewBoxHeight * relativeWidth) / 2 / relativeWidth;
            }
            size = 1 / Math.max(viewBoxWidth / width, viewBoxHeight / height);
            
            me.viewBoxShift = {
                dx: -viewBoxX,
                dy: -viewBoxY,
                scale: size
            };
            items = me.items.items;
            for (i = 0, len = items.length; i < len; i++) {
                me.transform(items[i]);
            }
        }
        this.callParent(arguments);
    },

    setViewBox: function(x, y, width, height) {
        this.callParent(arguments);
        this.viewBox = {
            x: x,
            y: y,
            width: width,
            height: height
        };
    },

    onAdd: function(item) {
        this.callParent(arguments);
        if (this.el) {
            this.renderItem(item);
        }
    },

    onRemove: function(sprite) {
        if (sprite.el) {
            sprite.el.remove();
            delete sprite.el;
        }
        this.callParent(arguments);
    },

    render: function (container) {
        var me = this,
            doc = Ext.getDoc().dom;
        
        if (!me.createNode) {
            try {
                if (!doc.namespaces.rvml) {
                    doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
                }
                me.createNode = function (tagName) {
                    return doc.createElement("<rvml:" + tagName + ' class="rvml">');
                };
            } catch (e) {
                me.createNode = function (tagName) {
                    return doc.createElement("<" + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
                };
            }
        }

        if (!me.el) {
            var el = doc.createElement("div");
            me.el = Ext.get(el);
            me.el.addCls(me.baseVmlCls);

            
            me.span = doc.createElement("span");
            Ext.get(me.span).addCls(me.measureSpanCls);
            el.appendChild(me.span);
            me.el.setSize(me.width || 10, me.height || 10);
            container.appendChild(el);
            me.el.on({
                scope: me,
                mouseup: me.onMouseUp,
                mousedown: me.onMouseDown,
                mouseover: me.onMouseOver,
                mouseout: me.onMouseOut,
                mousemove: me.onMouseMove,
                mouseenter: me.onMouseEnter,
                mouseleave: me.onMouseLeave,
                click: me.onClick
            });
        }
        me.renderAll();
    },

    renderAll: function() {
        this.items.each(this.renderItem, this);
    },

    redraw: function(sprite) {
        sprite.dirty = true;
        this.renderItem(sprite);
    },

    renderItem: function (sprite) {
        
        if (!this.el) {
            return;
        }

        
        if (!sprite.el) {
            this.createSpriteElement(sprite);
        }

        if (sprite.dirty) {
            this.applyAttrs(sprite);
            if (sprite.dirtyTransform) {
                this.applyTransformations(sprite);
            }
        }
    },

    rotationCompensation: function (deg, dx, dy) {
        var matrix = Ext.create('Ext.draw.Matrix');
        matrix.rotate(-deg, 0.5, 0.5);
        return {
            x: matrix.x(dx, dy),
            y: matrix.y(dx, dy)
        };
    },

    transform: function(sprite) {
        var me = this,
            matrix = Ext.create('Ext.draw.Matrix'),
            transforms = sprite.transformations,
            transformsLength = transforms.length,
            i = 0,
            deltaDegrees = 0,
            deltaScaleX = 1,
            deltaScaleY = 1,
            flip = "",
            el = sprite.el,
            dom = el.dom,
            domStyle = dom.style,
            zoom = me.zoom,
            skew = sprite.skew,
            deltaX, deltaY, transform, type, compensate, y, fill, newAngle,zoomScaleX, zoomScaleY, newOrigin;

        for (; i < transformsLength; i++) {
            transform = transforms[i];
            type = transform.type;
            if (type == "translate") {
                matrix.translate(transform.x, transform.y);
            }
            else if (type == "rotate") {
                matrix.rotate(transform.degrees, transform.x, transform.y);
                deltaDegrees += transform.degrees;
            }
            else if (type == "scale") {
                matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY);
                deltaScaleX *= transform.x;
                deltaScaleY *= transform.y;
            }
        }

        if (me.viewBoxShift) {
            matrix.scale(me.viewBoxShift.scale, me.viewBoxShift.scale, -1, -1);
            matrix.add(1, 0, 0, 1, me.viewBoxShift.dx, me.viewBoxShift.dy);
        }

        sprite.matrix = matrix;


        

        if (sprite.type != "image" && skew) {
            
            skew.matrix = matrix.toString();
            skew.offset = matrix.offset();
        }
        else {
            deltaX = matrix.matrix[0][2];
            deltaY = matrix.matrix[1][2];
            
            zoomScaleX = zoom / deltaScaleX;
            zoomScaleY = zoom / deltaScaleY;

            dom.coordsize = Math.abs(zoomScaleX) + " " + Math.abs(zoomScaleY);

            
            newAngle = deltaDegrees * (deltaScaleX * ((deltaScaleY < 0) ? -1 : 1));
            if (newAngle != domStyle.rotation && !(newAngle === 0 && !domStyle.rotation)) {
                domStyle.rotation = newAngle;
            }
            if (deltaDegrees) {
                
                compensate = me.rotationCompensation(deltaDegrees, deltaX, deltaY);
                deltaX = compensate.x;
                deltaY = compensate.y;
            }

            
            if (deltaScaleX < 0) {
                flip += "x";
            }
            if (deltaScaleY < 0) {
                flip += " y";
                y = -1;
            }
            if (flip != "" && !dom.style.flip) {
                domStyle.flip = flip;
            }

            
            newOrigin = (deltaX * -zoomScaleX) + " " + (deltaY * -zoomScaleY);
            if (newOrigin != dom.coordorigin) {
                dom.coordorigin = (deltaX * -zoomScaleX) + " " + (deltaY * -zoomScaleY);
            }
        }
    },

    createItem: function (config) {
        return Ext.create('Ext.draw.Sprite', config);
    },

    getRegion: function() {
        return this.el.getRegion();
    },

    addCls: function(sprite, className) {
        if (sprite && sprite.el) {
            sprite.el.addCls(className);
        }
    },

    removeCls: function(sprite, className) {
        if (sprite && sprite.el) {
            sprite.el.removeCls(className);
        }
    },

    
    addGradient: function(gradient) {
        var gradients = this.gradientsColl || (this.gradientsColl = Ext.create('Ext.util.MixedCollection')),
            colors = [],
            stops = Ext.create('Ext.util.MixedCollection');

        
        stops.addAll(gradient.stops);
        stops.sortByKey("ASC", function(a, b) {
            a = parseInt(a, 10);
            b = parseInt(b, 10);
            return a > b ? 1 : (a < b ? -1 : 0);
        });
        stops.eachKey(function(k, v) {
            colors.push(k + "% " + v.color);
        });

        gradients.add(gradient.id, {
            colors: colors.join(","),
            angle: gradient.angle
        });
    },

    destroy: function() {
        var me = this;
        
        me.callParent(arguments);
        if (me.el) {
            me.el.remove();
        }
        delete me.el;
    }
});


Ext.define('Ext.fx.target.ElementCSS', {

    

    extend: 'Ext.fx.target.Element',

    

    setAttr: function(targetData, isFirstFrame) {
        var cssArr = {
                attrs: [],
                duration: [],
                easing: []
            },
            ln = targetData.length,
            attributes,
            attrs,
            attr,
            easing,
            duration,
            o,
            i,
            j,
            ln2;
        for (i = 0; i < ln; i++) {
            attrs = targetData[i];
            duration = attrs.duration;
            easing = attrs.easing;
            attrs = attrs.attrs;
            for (attr in attrs) {
                if (Ext.Array.indexOf(cssArr.attrs, attr) == -1) {
                    cssArr.attrs.push(attr.replace(/[A-Z]/g, function(v) {
                        return '-' + v.toLowerCase();
                    }));
                    cssArr.duration.push(duration + 'ms');
                    cssArr.easing.push(easing);
                }
            }
        }
        attributes = cssArr.attrs.join(',');
        duration = cssArr.duration.join(',');
        easing = cssArr.easing.join(', ');
        for (i = 0; i < ln; i++) {
            attrs = targetData[i].attrs;
            for (attr in attrs) {
                ln2 = attrs[attr].length;
                for (j = 0; j < ln2; j++) {
                    o = attrs[attr][j];
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', isFirstFrame ? '' : attributes);
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', isFirstFrame ? '' : duration);
                    o[0].setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', isFirstFrame ? '' : easing);
                    o[0].setStyle(attr, o[1]);

                    
                    if (isFirstFrame) {
                        o = o[0].dom.offsetWidth;
                    }
                    else {
                        
                        o[0].on(Ext.supports.CSS3TransitionEnd, function() {
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionProperty', null);
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionDuration', null);
                            this.setStyle(Ext.supports.CSS3Prefix + 'TransitionTimingFunction', null);
                        }, o[0], { single: true });
                    }
                }
            }
        }
    }
});

Ext.define('Ext.fx.target.CompositeElementCSS', {

    

    extend: 'Ext.fx.target.CompositeElement',

    requires: ['Ext.fx.target.ElementCSS'],

    
    setAttr: function() {
        return Ext.fx.target.ElementCSS.prototype.setAttr.apply(this, arguments);
    }
});

Ext.define('Ext.layout.container.AbstractFit', {

    

    extend: 'Ext.layout.container.Container',

    

    itemCls: Ext.baseCSSPrefix + 'fit-item',
    targetCls: Ext.baseCSSPrefix + 'layout-fit',
    type: 'fit'
});

Ext.define('Ext.layout.container.Fit', {

    

    extend: 'Ext.layout.container.AbstractFit',
    alias: 'layout.fit',
    alternateClassName: 'Ext.layout.FitLayout',

    
   
    
    onLayout : function() {
        var me = this;
        me.callParent();

        if (me.owner.items.length) {
            me.setItemBox(me.owner.items.get(0), me.getLayoutTargetSize());
        }
    },

    getTargetBox : function() {
        return this.getLayoutTargetSize();
    },

    setItemBox : function(item, box) {
        var me = this;
        if (item && box.height > 0) {
            if (me.isManaged('width') === true) {
               box.width = undefined;
            }
            if (me.isManaged('height') === true) {
               box.height = undefined;
            }
            me.setItemSize(item, box.width, box.height);
        }
    }
});


Ext.define('Ext.layout.container.AbstractCard', {

    

    extend: 'Ext.layout.container.Fit',

    

    type: 'card',

    sizeAllCards: false,

    hideInactive: true,

    
    deferredRender : false,

    beforeLayout: function() {
        var me = this;
        me.activeItem = me.getActiveItem();
        if (me.activeItem && me.deferredRender) {
            me.renderItems([me.activeItem], me.getRenderTarget());
            return true;
        }
        else {
            return this.callParent(arguments);
        }
    },

    onLayout: function() {
        var me = this,
            activeItem = me.activeItem,
            items = me.getVisibleItems(),
            ln = items.length,
            targetBox = me.getTargetBox(),
            i, item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            me.setItemBox(item, targetBox);
        }

        if (!me.firstActivated && activeItem) {
            if (activeItem.fireEvent('beforeactivate', activeItem) !== false) {
                activeItem.fireEvent('activate', activeItem);
            }
            me.firstActivated = true;
        }
    },

    isValidParent : function(item, target, position) {
        
        
        var itemEl = item.el ? item.el.dom : Ext.getDom(item);
        return (itemEl && itemEl.parentNode === (target.dom || target)) || false;
    },

    
    getActiveItem: function() {
        var me = this;
        if (!me.activeItem && me.owner) {
            me.activeItem = me.parseActiveItem(me.owner.activeItem);
        }

        if (me.activeItem && me.owner.items.indexOf(me.activeItem) != -1) {
            return me.activeItem;
        }

        return null;
    },

    
    parseActiveItem: function(item) {
        if (item && item.isComponent) {
            return item;
        }
        else if (typeof item == 'number' || item === undefined) {
            return this.getLayoutItems()[item || 0];
        }
        else {
            return this.owner.getComponent(item);
        }
    },

    
    configureItem: function(item, position) {
        this.callParent([item, position]);
        if (this.hideInactive && this.activeItem !== item) {
            item.hide();
        }
        else {
            item.show();
        }
    },

    onRemove: function(component) {
        if (component === this.activeItem) {
            this.activeItem = null;
            if (this.owner.items.getCount() === 0) {
                this.firstActivated = false;
            }
        }
    },

    
    getAnimation: function(newCard, owner) {
        var newAnim = (newCard || {}).cardSwitchAnimation;
        if (newAnim === false) {
            return false;
        }
        return newAnim || owner.cardSwitchAnimation;
    },

    
    getNext: function(wrap) {
        
        
        
        var items = this.getLayoutItems(),
            index = Ext.Array.indexOf(items, this.activeItem);
        return items[index + 1] || (wrap ? items[0] : false);
    },

    
    next: function(anim, wrap) {
        
        
        
        return this.setActiveItem(this.getNext(wrap), anim);
    },

    
    getPrev: function(wrap) {
        
        
        
        var items = this.getLayoutItems(),
            index = Ext.Array.indexOf(items, this.activeItem);
        return items[index - 1] || (wrap ? items[items.length - 1] : false);
    },

    
    prev: function(anim, wrap) {
        
        
        
        return this.setActiveItem(this.getPrev(wrap), anim);
    }
});


Ext.define('Ext.selection.Model', {
    extend: 'Ext.util.Observable',
    alternateClassName: 'Ext.AbstractStoreSelectionModel',
    requires: ['Ext.data.StoreManager'],
    

    
    
    
    allowDeselect: false,

    
    selected: null,
    
    
    
    pruneRemoved: true,

    constructor: function(cfg) {
        var me = this;
        
        cfg = cfg || {};
        Ext.apply(me, cfg);
        
        me.addEvents(
            
             'selectionchange'
        );

        me.modes = {
            SINGLE: true,
            SIMPLE: true,
            MULTI: true
        };

        
        me.setSelectionMode(cfg.mode || me.mode);

        
        me.selected = Ext.create('Ext.util.MixedCollection');
        
        me.callParent(arguments);
    },

    
    bind : function(store, initial){
        var me = this;
        
        if(!initial && me.store){
            if(store !== me.store && me.store.autoDestroy){
                me.store.destroy();
            }else{
                me.store.un("add", me.onStoreAdd, me);
                me.store.un("clear", me.onStoreClear, me);
                me.store.un("remove", me.onStoreRemove, me);
                me.store.un("update", me.onStoreUpdate, me);
            }
        }
        if(store){
            store = Ext.data.StoreManager.lookup(store);
            store.on({
                add: me.onStoreAdd,
                clear: me.onStoreClear,
                remove: me.onStoreRemove,
                update: me.onStoreUpdate,
                scope: me
            });
        }
        me.store = store;
        if(store && !initial) {
            me.refresh();
        }
    },

    
    selectAll: function(suppressEvent) {
        var me = this,
            selections = me.store.getRange(),
            i = 0,
            len = selections.length,
            start = me.getSelection().length;
            
        me.bulkChange = true;
        for (; i < len; i++) {
            me.doSelect(selections[i], true, suppressEvent);
        }
        delete me.bulkChange;
        
        me.maybeFireSelectionChange(me.getSelection().length !== start);
    },

    
    deselectAll: function(suppressEvent) {
        var me = this,
            selections = me.getSelection(),
            i = 0,
            len = selections.length,
            start = me.getSelection().length;
            
        me.bulkChange = true;
        for (; i < len; i++) {
            me.doDeselect(selections[i], suppressEvent);
        }
        delete me.bulkChange;
        
        me.maybeFireSelectionChange(me.getSelection().length !== start);
    },

    
    
    
    selectWithEvent: function(record, e) {
        var me = this;
        
        switch (me.selectionMode) {
            case 'MULTI':
                if (e.ctrlKey && me.isSelected(record)) {
                    me.doDeselect(record, false);
                } else if (e.shiftKey && me.lastFocused) {
                    me.selectRange(me.lastFocused, record, e.ctrlKey);
                } else if (e.ctrlKey) {
                    me.doSelect(record, true, false);
                } else if (me.isSelected(record) && !e.shiftKey && !e.ctrlKey && me.selected.getCount() > 1) {
                    me.doSelect(record, false, false);
                } else {
                    me.doSelect(record, false);
                }
                break;
            case 'SIMPLE':
                if (me.isSelected(record)) {
                    me.doDeselect(record);
                } else {
                    me.doSelect(record, true);
                }
                break;
            case 'SINGLE':
                
                if (me.allowDeselect && me.isSelected(record)) {
                    me.doDeselect(record);
                
                } else {
                    me.doSelect(record, false);
                }
                break;
        }
    },

    
    selectRange : function(startRow, endRow, keepExisting, dir){
        var me = this,
            store = me.store,
            selectedCount = 0,
            i,
            tmp,
            dontDeselect,
            records = [];
        
        if (me.isLocked()){
            return;
        }
        
        if (!keepExisting) {
            me.clearSelections();
        }
        
        if (!Ext.isNumber(startRow)) {
            startRow = store.indexOf(startRow);
        } 
        if (!Ext.isNumber(endRow)) {
            endRow = store.indexOf(endRow);
        }
        
        
        if (startRow > endRow){
            tmp = endRow;
            endRow = startRow;
            startRow = tmp;
        }

        for (i = startRow; i <= endRow; i++) {
            if (me.isSelected(store.getAt(i))) {
                selectedCount++;
            }
        }

        if (!dir) {
            dontDeselect = -1;
        } else {
            dontDeselect = (dir == 'up') ? startRow : endRow;
        }
        
        for (i = startRow; i <= endRow; i++){
            if (selectedCount == (endRow - startRow + 1)) {
                if (i != dontDeselect) {
                    me.doDeselect(i, true);
                }
            } else {
                records.push(store.getAt(i));
            }
        }
        me.doMultiSelect(records, true);
    },
    
    
    select: function(records, keepExisting, suppressEvent) {
        this.doSelect(records, keepExisting, suppressEvent);
    },

    
    deselect: function(records, suppressEvent) {
        this.doDeselect(records, suppressEvent);
    },
    
    doSelect: function(records, keepExisting, suppressEvent) {
        var me = this,
            record;
            
        if (me.locked) {
            return;
        }
        if (typeof records === "number") {
            records = [me.store.getAt(records)];
        }
        if (me.selectionMode == "SINGLE" && records) {
            record = records.length ? records[0] : records;
            me.doSingleSelect(record, suppressEvent);
        } else {
            me.doMultiSelect(records, keepExisting, suppressEvent);
        }
    },

    doMultiSelect: function(records, keepExisting, suppressEvent) {
        var me = this,
            selected = me.selected,
            change = false,
            i = 0,
            len, record;
            
        if (me.locked) {
            return;
        }
        

        records = !Ext.isArray(records) ? [records] : records;
        len = records.length;
        if (!keepExisting && selected.getCount() > 0) {
            change = true;
            me.doDeselect(me.getSelection(), suppressEvent);
        }

        for (; i < len; i++) {
            record = records[i];
            if (keepExisting && me.isSelected(record)) {
                continue;
            }
            change = true;
            me.lastSelected = record;
            selected.add(record);

            me.onSelectChange(record, true, suppressEvent);
        }
        me.setLastFocused(record, suppressEvent);
        
        me.maybeFireSelectionChange(change && !suppressEvent);
    },

    
    doDeselect: function(records, suppressEvent) {
        var me = this,
            selected = me.selected,
            change = false,
            i = 0,
            len, record;
            
        if (me.locked) {
            return;
        }

        if (typeof records === "number") {
            records = [me.store.getAt(records)];
        }

        records = !Ext.isArray(records) ? [records] : records;
        len = records.length;
        for (; i < len; i++) {
            record = records[i];
            if (selected.remove(record)) {
                if (me.lastSelected == record) {
                    me.lastSelected = selected.last();
                }
                me.onSelectChange(record, false, suppressEvent);
                change = true;
            }
        }
        
        me.maybeFireSelectionChange(change && !suppressEvent);
    },

    doSingleSelect: function(record, suppressEvent) {
        var me = this,
            selected = me.selected;
            
        if (me.locked) {
            return;
        }
        
        
        if (me.isSelected(record)) {
            return;
        }
        if (selected.getCount() > 0) {
            me.doDeselect(me.lastSelected, suppressEvent);
        }
        selected.add(record);
        me.lastSelected = record;
        me.onSelectChange(record, true, suppressEvent);
        if (!suppressEvent) {
            me.setLastFocused(record);
        }
        me.maybeFireSelectionChange(!suppressEvent);
    },

    
    setLastFocused: function(record, supressFocus) {
        var me = this,
            recordBeforeLast = me.lastFocused;
        me.lastFocused = record;
        me.onLastFocusChanged(recordBeforeLast, record, supressFocus);
    },
    
    
    isFocused: function(record) {
        return record === this.getLastFocused();
    },


    
    
    maybeFireSelectionChange: function(fireEvent) {
        var me = this;
        if (fireEvent && !me.bulkChange) {
            me.fireEvent('selectionchange', me, me.getSelection());
        }
    },

    
    getLastSelected: function() {
        return this.lastSelected;
    },
    
    getLastFocused: function() {
        return this.lastFocused;
    },

    
    getSelection: function() {
        return this.selected.getRange();
    },

    
    getSelectionMode: function() {
        return this.selectionMode;
    },

    
    setSelectionMode: function(selMode) {
        selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
        
        
        this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
    },

    
    isLocked: function() {
        return this.locked;
    },

    
    setLocked: function(locked) {
        this.locked = !!locked;
    },

    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.store.getAt(record) : record;
        return this.selected.indexOf(record) !== -1;
    },
    
    
    hasSelection: function() {
        return this.selected.getCount() > 0;
    },

    refresh: function() {
        var me = this,
            toBeSelected = [],
            oldSelections = me.getSelection(),
            len = oldSelections.length,
            selection,
            change,
            i = 0,
            lastFocused = this.getLastFocused();

        
        
        
        for (; i < len; i++) {
            selection = oldSelections[i];
            if (!this.pruneRemoved || me.store.indexOf(selection) !== -1) {
                toBeSelected.push(selection);
            }
        }

        
        
        if (me.selected.getCount() != toBeSelected.length) {
            change = true;
        }

        me.clearSelections();
        
        if (me.store.indexOf(lastFocused) !== -1) {
            
            this.setLastFocused(lastFocused, true);
        }

        if (toBeSelected.length) {
            
            me.doSelect(toBeSelected, false, true);
        }
        
        me.maybeFireSelectionChange(change);
    },

    
    clearSelections: function() {
        
        var me = this;
        me.selected.clear();
        me.lastSelected = null;
        me.setLastFocused(null);
    },

    
    onStoreAdd: function() {

    },

    
    
    onStoreClear: function() {
        var me = this,
            selected = this.selected;
            
        if (selected.getCount > 0) {
            selected.clear();
            me.lastSelected = null;
            me.setLastFocused(null);
            me.maybeFireSelectionChange(true);
        }
    },

    
    
    
    onStoreRemove: function(store, record) {
        var me = this,
            selected = me.selected;
            
        if (me.locked || !me.pruneRemoved) {
            return;
        }

        if (selected.remove(record)) {
            if (me.lastSelected == record) {
                me.lastSelected = null;
            }
            if (me.getLastFocused() == record) {
                me.setLastFocused(null);
            }
            me.maybeFireSelectionChange(true);
        }
    },

    getCount: function() {
        return this.selected.getCount();
    },

    
    destroy: function() {

    },

    
    onStoreUpdate: function() {

    },

    
    onSelectChange: function(record, isSelected, suppressEvent) {

    },

    
    onLastFocusChanged: function(oldFocused, newFocused) {

    },

    
    onEditorKey: function(field, e) {

    },

    
    bindComponent: function(cmp) {

    }
});

Ext.define('Ext.selection.DataViewModel', {
    extend: 'Ext.selection.Model',
    
    requires: ['Ext.util.KeyNav'],

    deselectOnContainerClick: true,
    
    
    enableKeyNav: true,
    
    constructor: function(cfg){
        this.addEvents(
            
            'deselect',
            
            
            'select'
        );
        this.callParent(arguments);
    },
    
    bindComponent: function(view) {
        var me = this,
            eventListeners = {
                refresh: me.refresh,
                scope: me
            };

        me.view = view;
        me.bind(view.getStore());

        view.on(view.triggerEvent, me.onItemClick, me);
        view.on(view.triggerCtEvent, me.onContainerClick, me);

        view.on(eventListeners);

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },

    onItemClick: function(view, record, item, index, e) {
        this.selectWithEvent(record, e);
    },

    onContainerClick: function() {
        if (this.deselectOnContainerClick) {
            this.deselectAll();
        }
    },
    
    initKeyNav: function(view) {
        var me = this;
        
        if (!view.rendered) {
            view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
            return;
        }
        
        view.el.set({
            tabIndex: -1
        });
        me.keyNav = Ext.create('Ext.util.KeyNav', view.el, {
            down: Ext.pass(me.onNavKey, [1], me),
            right: Ext.pass(me.onNavKey, [1], me),
            left: Ext.pass(me.onNavKey, [-1], me),
            up: Ext.pass(me.onNavKey, [-1], me),
            scope: me
        });
    },
    
    onNavKey: function(step) {
        step = step || 1;
        var me = this,
            view = me.view,
            selected = me.getSelection()[0],
            numRecords = me.view.store.getCount(),
            idx;
                
        if (selected) {
            idx = view.indexOf(view.getNode(selected)) + step;
        } else {
            idx = 0;
        }
        
        if (idx < 0) {
            idx = numRecords - 1;
        } else if (idx >= numRecords) {
            idx = 0;
        }
        
        me.select(idx);
    },

    
    onSelectChange: function(record, isSelected, suppressEvent) {
        var me = this,
            view = me.view,
            allowSelect = true;
        
        if (isSelected) {
            if (!suppressEvent) {
                allowSelect = me.fireEvent('beforeselect', me, record) !== false;
            }
            if (allowSelect) {
                view.onItemSelect(record);
                if (!suppressEvent) {
                    me.fireEvent('select', me, record);
                }
            }
        } else {
            view.onItemDeselect(record);
            if (!suppressEvent) {
                me.fireEvent('deselect', me, record);
            }
        }
    }
});


Ext.define('Ext.state.CookieProvider', {
    extend: 'Ext.state.Provider',

    constructor : function(config){
        var me = this;
        me.path = "/";
        me.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); 
        me.domain = null;
        me.secure = false;
        me.callParent(arguments);
        me.state = me.readCookies();
    },
    
    
    set : function(name, value){
        var me = this;
        
        if(typeof value == "undefined" || value === null){
            me.clear(name);
            return;
        }
        me.setCookie(name, value);
        me.callParent(arguments);
    },

    
    clear : function(name){
        this.clearCookie(name);
        this.callParent(arguments);
    },

    
    readCookies : function(){
        var cookies = {},
            c = document.cookie + ";",
            re = /\s?(.*?)=(.*?);/g,
            prefix = this.prefix,
            len = prefix.length,
            matches,
            name,
            value;
            
        while((matches = re.exec(c)) != null){
            name = matches[1];
            value = matches[2];
            if (name && name.substring(0, len) == prefix){
                cookies[name.substr(len)] = this.decodeValue(value);
            }
        }
        return cookies;
    },

    
    setCookie : function(name, value){
        var me = this;
        
        document.cookie = me.prefix + name + "=" + me.encodeValue(value) +
           ((me.expires == null) ? "" : ("; expires=" + me.expires.toGMTString())) +
           ((me.path == null) ? "" : ("; path=" + me.path)) +
           ((me.domain == null) ? "" : ("; domain=" + me.domain)) +
           ((me.secure == true) ? "; secure" : "");
    },

    
    clearCookie : function(name){
        var me = this;
        
        document.cookie = me.prefix + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
           ((me.path == null) ? "" : ("; path=" + me.path)) +
           ((me.domain == null) ? "" : ("; domain=" + me.domain)) +
           ((me.secure == true) ? "; secure" : "");
    }
});

Ext.define('Ext.state.LocalStorageProvider', {
    
    
    extend: 'Ext.state.Provider',
    
    alias: 'state.localstorage',
    
    
   
    constructor: function(){
        var me = this;
        me.callParent(arguments);
        me.store = me.getStorageObject();
        me.state = me.readLocalStorage();
    },
    
    readLocalStorage: function(){
        var store = this.store,
            i = 0,
            len = store.length,
            prefix = this.prefix,
            prefixLen = prefix.length,
            data = {},
            key;
            
        for (; i < len; ++i) {
            key = store.key(i);
            if (key.substring(0, prefixLen) == prefix) {
                data[key.substr(prefixLen)] = this.decodeValue(store.getItem(key));
            }            
        }
        return data;
    },
    
    set : function(name, value){
        var me = this;
        
        me.clear(name);
        if (typeof value == "undefined" || value === null) {
            return;
        }
        me.store.setItem(me.prefix + name, me.encodeValue(value));
        me.callParent(arguments);
    },

    
    clear : function(name){
        this.store.removeItem(this.prefix + name);
        this.callParent(arguments);
    },
    
    getStorageObject: function(){
        try {
            var supports = 'localStorage' in window && window['localStorage'] !== null;
            if (supports) {
                return window.localStorage;
            }
        } catch (e) {
            return false;
        }
        Ext.Error.raise('LocalStorage is not supported by the current browser');
    }    
});



Ext.define('Ext.util.Point', {

    
    extend: 'Ext.util.Region',

    statics: {

        
        fromEvent: function(e) {
            e = (e.changedTouches && e.changedTouches.length > 0) ? e.changedTouches[0] : e;
            return new this(e.pageX, e.pageY);
        }
    },

    

    constructor: function(x, y) {
        this.callParent([y, x, y, x]);
    },

    
    toString: function() {
        return "Point[" + this.x + "," + this.y + "]";
    },

    
    equals: function(p) {
        return (this.x == p.x && this.y == p.y);
    },

    
    isWithin: function(p, threshold) {
        if (!Ext.isObject(threshold)) {
            threshold = {
                x: threshold,
                y: threshold
            };
        }

        return (this.x <= p.x + threshold.x && this.x >= p.x - threshold.x &&
                this.y <= p.y + threshold.y && this.y >= p.y - threshold.y);
    },

    
    roundedEquals: function(p) {
        return (Math.round(this.x) == Math.round(p.x) && Math.round(this.y) == Math.round(p.y));
    }
}, function() {
    
    this.prototype.translate = Ext.util.Region.prototype.translateBy;
});


Ext.define('Ext.view.AbstractView', {
    extend: 'Ext.Component',
    alternateClassName: 'Ext.view.AbstractView',
    requires: [
        'Ext.LoadMask',
        'Ext.data.StoreManager',
        'Ext.CompositeElementLite',
        'Ext.DomQuery',
        'Ext.selection.DataViewModel'
    ],
    
    inheritableStatics: {
        getRecord: function(node) {
            return this.getBoundView(node).getRecord(node);
        },
        
        getBoundView: function(node) {
            return Ext.getCmp(node.boundView);
        }
    },
    
    
    

    
    
    
    itemCls: Ext.baseCSSPrefix + 'dataview-item',
    
    

    

    
    loadingText: 'Loading...',
    
    
    
    
    loadingUseMsg: true,
    

    

    
    selectedItemCls: Ext.baseCSSPrefix + 'item-selected',

    
    emptyText: "",

    
    deferEmptyText: true,

    
    trackOver: false,

    
    blockRefresh: false,

    


    
    last: false,
    
    triggerEvent: 'itemclick',
    triggerCtEvent: 'containerclick',
    
    addCmpEvents: function() {
        
    },

    
    initComponent : function(){
        var me = this,
            isDef = Ext.isDefined,
            itemTpl = me.itemTpl,
            memberFn = {};
            
        if (itemTpl) {
            if (Ext.isArray(itemTpl)) {
                
                itemTpl = itemTpl.join('');
            } else if (Ext.isObject(itemTpl)) {
                
                memberFn = Ext.apply(memberFn, itemTpl.initialConfig);
                itemTpl = itemTpl.html;
            }
            
            if (!me.itemSelector) {
                me.itemSelector = '.' + me.itemCls;
            }
            
            itemTpl = Ext.String.format('<tpl for="."><div class="{0}">{1}</div></tpl>', me.itemCls, itemTpl);
            me.tpl = Ext.create('Ext.XTemplate', itemTpl, memberFn);
        }

        if (!isDef(me.tpl) || !isDef(me.itemSelector)) {
            Ext.Error.raise({
                sourceClass: 'Ext.view.View',
                tpl: me.tpl,
                itemSelector: me.itemSelector,
                msg: "DataView requires both tpl and itemSelector configurations to be defined."
            });
        }

        me.callParent();
        if(Ext.isString(me.tpl) || Ext.isArray(me.tpl)){
            me.tpl = Ext.create('Ext.XTemplate', me.tpl);
        }

        
        
        if (isDef(me.overCls) || isDef(me.overClass)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.view.View: Using the deprecated overCls or overClass configuration. Use overItemCls instead.');
            }
            me.overItemCls = me.overCls || me.overClass;
            delete me.overCls;
            delete me.overClass;
        }

        if (me.overItemCls) {
            me.trackOver = true;
        }
        
        if (isDef(me.selectedCls) || isDef(me.selectedClass)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.view.View: Using the deprecated selectedCls or selectedClass configuration. Use selectedItemCls instead.');
            }
            me.selectedItemCls = me.selectedCls || me.selectedClass;
            delete me.selectedCls;
            delete me.selectedClass;
        }
        
        me.addEvents(
            
            'beforerefresh',
            
            'refresh',
            
            'itemupdate',
            
            'itemadd',
            
            'itemremove'
        );

        me.addCmpEvents();

        if (me.store) {
            me.store = Ext.data.StoreManager.lookup(me.store);
        }
        me.all = new Ext.CompositeElementLite();
        me.getSelectionModel().bindComponent(me);
    },

    onRender: function() {
        var me = this,
            loadingText = me.loadingText,
            loadingHeight = me.loadingHeight,
            undef;

        me.callParent(arguments);
        if (loadingText) {
            
            
            
            
            
            me.loadMask = Ext.create('Ext.LoadMask', me.floating ? me : me.ownerCt || me, {
                msg: loadingText,
                msgCls: me.loadingCls,
                useMsg: me.loadingUseMsg,
                listeners: {
                    beforeshow: function() {
                        me.getTargetEl().update('');
                        me.getSelectionModel().deselectAll();
                        me.all.clear();
                        if (loadingHeight) {
                            me.setCalculatedSize(undef, loadingHeight);
                        }
                    },
                    hide: function() {
                        if (loadingHeight) {
                            me.setHeight(me.height);
                        }
                    }
                }
            });
        }
    },

    getSelectionModel: function(){
        var me = this,
            mode = 'SINGLE';

        if (!me.selModel) {
            me.selModel = {};
        }

        if (me.simpleSelect) {
            mode = 'SIMPLE';
        } else if (me.multiSelect) {
            mode = 'MULTI';
        }

        Ext.applyIf(me.selModel, {
            allowDeselect: me.allowDeselect,
            mode: mode
        });

        if (!me.selModel.events) {
            me.selModel = Ext.create('Ext.selection.DataViewModel', me.selModel);
        }

        if (!me.selModel.hasRelaySetup) {
            me.relayEvents(me.selModel, ['selectionchange', 'beforeselect', 'select', 'deselect']);
            me.selModel.hasRelaySetup = true;
        }

        
        
        if (me.disableSelection) {
            me.selModel.locked = true;
        }

        return me.selModel;
    },

    
    refresh: function() {
        var me = this,
            el,
            records;
            
        if (!me.rendered) {
            return;
        }
        
        me.fireEvent('beforerefresh', me);
        el = me.getTargetEl();
        records = me.store.getRange();

        el.update('');
        if (records.length < 1) {
            if (!me.deferEmptyText || me.hasSkippedEmptyText) {
                el.update(me.emptyText);
            }
            me.all.clear();
        } else {
            me.tpl.overwrite(el, me.collectData(records, 0));
            me.all.fill(Ext.query(me.getItemSelector(), el.dom));
            me.updateIndexes(0);
        }
        
        me.selModel.refresh();
        me.hasSkippedEmptyText = true;
        me.fireEvent('refresh', me);
    },

    
    prepareData: function(data, index, record) {
        if (record) {    
            Ext.apply(data, record.getAssociatedData());            
        }
        return data;
    },
    
    
    collectData : function(records, startIndex){
        var r = [],
            i = 0,
            len = records.length;

        for(; i < len; i++){
            r[r.length] = this.prepareData(records[i].data, startIndex + i, records[i]);
        }

        return r;
    },

    
    bufferRender : function(records, index){
        var div = document.createElement('div');
        this.tpl.overwrite(div, this.collectData(records, index));
        return Ext.query(this.getItemSelector(), div);
    },

    
    onUpdate : function(ds, record){
        var me = this,
            index = me.store.indexOf(record),
            original,
            node;

        if (index > -1){
            original = me.all.elements[index];
            node = me.bufferRender([record], index)[0];

            me.all.replaceElement(index, node, true);
            me.updateIndexes(index, index);

            
            
            me.selModel.refresh();
            me.fireEvent('itemupdate', record, index, node);
        }

    },

    
    onAdd : function(ds, records, index) {
        var me = this,
            nodes;
            
        if (me.all.getCount() === 0) {
            me.refresh();
            return;
        }
        
        nodes = me.bufferRender(records, index);
        me.doAdd(nodes, records, index);

        me.selModel.refresh();
        me.updateIndexes(index);
        me.fireEvent('itemadd', records, index, nodes);
    },

    doAdd: function(nodes, records, index) {
        var n, a = this.all.elements;
        if (index < this.all.getCount()) {
            n = this.all.item(index).insertSibling(nodes, 'before', true);
            a.splice.apply(a, [index, 0].concat(nodes));
        } 
        else {
            n = this.all.last().insertSibling(nodes, 'after', true);
            a.push.apply(a, nodes);
        }    
    },
    
    
    onRemove : function(ds, record, index) {
        var me = this;
        
        me.doRemove(record, index);
        me.updateIndexes(index);
        if (me.store.getCount() === 0){
            me.refresh();
        }
        me.fireEvent('itemremove', record, index);
    },
    
    doRemove: function(record, index) {
        this.all.removeElement(index, true);
    },

    
    refreshNode : function(index){
        this.onUpdate(this.store, this.store.getAt(index));
    },

    
    updateIndexes : function(startIndex, endIndex) {
        var ns = this.all.elements,
            records = this.store.getRange();
        startIndex = startIndex || 0;
        endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
        for(var i = startIndex; i <= endIndex; i++){
            ns[i].viewIndex = i;
            ns[i].viewRecordId = records[i].internalId;
            if (!ns[i].boundView) {
                ns[i].boundView = this.id;
            }
        }
    },

    
    getStore : function(){
        return this.store;
    },

    
    bindStore : function(store, initial) {
        var me = this;
        
        if (!initial && me.store) {
            if (store !== me.store && me.store.autoDestroy) {
                me.store.destroy();
            } 
            else {
                me.mun(me.store, {
                    scope: me,
                    datachanged: me.onDataChanged,
                    add: me.onAdd,
                    remove: me.onRemove,
                    update: me.onUpdate,
                    clear: me.refresh
                });
            }
            if (!store) {
                if (me.loadMask) {
                    me.loadMask.bindStore(null);
                }
                me.store = null;
            }
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            me.mon(store, {
                scope: me,
                datachanged: me.onDataChanged,
                add: me.onAdd,
                remove: me.onRemove,
                update: me.onUpdate,
                clear: me.refresh
            });
            if (me.loadMask) {
                me.loadMask.bindStore(store);
            }
        }
        
        me.store = store;
        
        me.getSelectionModel().bind(store);
        
        if (store) {
            me.refresh(true);
        }
    },

    
    onDataChanged: function() {
        if (this.blockRefresh !== true) {
            this.refresh.apply(this, arguments);
        }
    },

    
    findItemByChild: function(node){
        return Ext.fly(node).findParent(this.getItemSelector(), this.getTargetEl());
    },
    
    
    findTargetByEvent: function(e) {
        return e.getTarget(this.getItemSelector(), this.getTargetEl());
    },


    
    getSelectedNodes: function(){
        var nodes   = [],
            records = this.selModel.getSelection(),
            ln = records.length,
            i  = 0;

        for (; i < ln; i++) {
            nodes.push(this.getNode(records[i]));
        }

        return nodes;
    },

    
    getRecords: function(nodes) {
        var records = [],
            i = 0,
            len = nodes.length,
            data = this.store.data;

        for (; i < len; i++) {
            records[records.length] = data.getByKey(nodes[i].viewRecordId);
        }

        return records;
    },

    
    getRecord: function(node){
        return this.store.data.getByKey(Ext.getDom(node).viewRecordId);
    },
    

    
    isSelected : function(node) {
        
        var r = this.getRecord(node);
        return this.selModel.isSelected(r);
    },
    
    
    select: function(records, keepExisting, suppressEvent) {
        this.selModel.select(records, keepExisting, suppressEvent);
    },

    
    deselect: function(records, suppressEvent) {
        this.selModel.deselect(records, suppressEvent);
    },

    
    getNode : function(nodeInfo) {
        if (Ext.isString(nodeInfo)) {
            return document.getElementById(nodeInfo);
        } else if (Ext.isNumber(nodeInfo)) {
            return this.all.elements[nodeInfo];
        } else if (nodeInfo instanceof Ext.data.Model) {
            return this.getNodeByRecord(nodeInfo);
        }
        return nodeInfo;
    },
    
    
    getNodeByRecord: function(record) {
        var ns = this.all.elements,
            ln = ns.length,
            i = 0;
        
        for (; i < ln; i++) {
            if (ns[i].viewRecordId === record.internalId) {
                return ns[i];
            }
        }
        
        return null;
    },
    
    
    getNodes: function(start, end) {
        var ns = this.all.elements,
            nodes = [],
            i;

        start = start || 0;
        end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
        if (start <= end) {
            for (i = start; i <= end && ns[i]; i++) {
                nodes.push(ns[i]);
            }
        } else {
            for (i = start; i >= end && ns[i]; i--) {
                nodes.push(ns[i]);
            }
        }
        return nodes;
    },

    
    indexOf: function(node) {
        node = this.getNode(node);
        if (Ext.isNumber(node.viewIndex)) {
            return node.viewIndex;
        }
        return this.all.indexOf(node);
    },

    onDestroy : function() {
        var me = this;
        
        me.all.clear();
        me.callParent();
        me.bindStore(null);
        me.selModel.destroy();
    },

    
    onItemSelect: function(record) {
        var node = this.getNode(record);
        Ext.fly(node).addCls(this.selectedItemCls);
    },

    
    onItemDeselect: function(record) {
        var node = this.getNode(record);
        Ext.fly(node).removeCls(this.selectedItemCls);
    },
    
    getItemSelector: function() {
        return this.itemSelector;
    }
}, function() {
    
    
    
    
    Ext.deprecate('extjs', '4.0', function() {
        Ext.view.AbstractView.override({
            
            
            
            
            
            getSelectionCount : function(){
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: getSelectionCount will be removed, please interact with the Ext.selection.DataViewModel");
                }
                return this.selModel.getSelection().length;
            },
        
            
            getSelectedRecords : function(){
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: getSelectedRecords will be removed, please interact with the Ext.selection.DataViewModel");
                }
                return this.selModel.getSelection();
            },
    
            select: function(records, keepExisting, supressEvents) {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: select will be removed, please access select through a DataView's SelectionModel, ie: view.getSelectionModel().select()");
                }
                var sm = this.getSelectionModel();
                return sm.select.apply(sm, arguments);
            },
            
            clearSelections: function() {
                if (Ext.global.console) {
                    Ext.global.console.warn("DataView: clearSelections will be removed, please access deselectAll through DataView's SelectionModel, ie: view.getSelectionModel().deselectAll()");
                }
                var sm = this.getSelectionModel();
                return sm.deselectAll();
            }
        });    
    });
});


Ext.define('Ext.Action', {

    

    

    
    
    
    
    
    
    

    constructor : function(config){
        this.initialConfig = config;
        this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
        this.items = [];
    },

    
    isAction : true,

    
    setText : function(text){
        this.initialConfig.text = text;
        this.callEach('setText', [text]);
    },

    
    getText : function(){
        return this.initialConfig.text;
    },

    
    setIconCls : function(cls){
        this.initialConfig.iconCls = cls;
        this.callEach('setIconCls', [cls]);
    },

    
    getIconCls : function(){
        return this.initialConfig.iconCls;
    },

    
    setDisabled : function(v){
        this.initialConfig.disabled = v;
        this.callEach('setDisabled', [v]);
    },

    
    enable : function(){
        this.setDisabled(false);
    },

    
    disable : function(){
        this.setDisabled(true);
    },

    
    isDisabled : function(){
        return this.initialConfig.disabled;
    },

    
    setHidden : function(v){
        this.initialConfig.hidden = v;
        this.callEach('setVisible', [!v]);
    },

    
    show : function(){
        this.setHidden(false);
    },

    
    hide : function(){
        this.setHidden(true);
    },

    
    isHidden : function(){
        return this.initialConfig.hidden;
    },

    
    setHandler : function(fn, scope){
        this.initialConfig.handler = fn;
        this.initialConfig.scope = scope;
        this.callEach('setHandler', [fn, scope]);
    },

    
    each : function(fn, scope){
        Ext.each(this.items, fn, scope);
    },

    
    callEach : function(fnName, args){
        var items = this.items,
            i = 0,
            len = items.length;
            
        for(; i < len; i++){
            items[i][fnName].apply(items[i], args);
        }
    },

    
    addComponent : function(comp){
        this.items.push(comp);
        comp.on('destroy', this.removeComponent, this);
    },

    
    removeComponent : function(comp){
        Ext.Array.remove(this.items, comp);
    },

    
    execute : function(){
        this.initialConfig.handler.apply(this.initialConfig.scope || Ext.global, arguments);
    }
});


Ext.define('Ext.layout.component.Editor', {

    

    alias: ['layout.editor'],

    extend: 'Ext.layout.component.Component',

    

    onLayout: function(width, height) {
        var me = this,
            owner = me.owner,
            autoSize = owner.autoSize;
            
        if (autoSize === true) {
            autoSize = {
                width: 'field',
                height: 'field'    
            };
        }
        
        if (autoSize) {
            width = me.getDimension(owner, autoSize.width, 'Width', width);
            height = me.getDimension(owner, autoSize.height, 'Height', height);
        }
        me.setTargetSize(width, height);
        owner.field.setSize(width, height);
    },
    
    getDimension: function(owner, type, dimension, actual){
        var method = 'get' + dimension;
        switch (type) {
            case 'boundEl':
                return owner.boundEl[method]();
            case 'field':
                return owner.field[method]();
            default:
                return actual;
        }
    }
});

Ext.define('Ext.Editor', {

    

    extend: 'Ext.Component',

    alias: 'widget.editor',

    requires: ['Ext.layout.component.Editor'],

    

   componentLayout: 'editor',

    

    
    allowBlur: true,

    

    
    revertInvalid: true,

    

    

    
    value : '',

    
    alignment: 'c-c?',

    
    offsets: [0, 0],

    
    shadow : 'frame',

    
    constrain : false,

    
    swallowKeys : true,

    
    completeOnEnter : true,

    
    cancelOnEsc : true,

    
    updateEl : false,

    

    
    hidden: true,
    baseCls: Ext.baseCSSPrefix + 'editor',

    initComponent : function() {
        var me = this,
            field = me.field = Ext.ComponentManager.create(me.field, 'textfield');

        Ext.apply(field, {
            inEditor: true,
            msgTarget: field.msgTarget == 'title' ? 'title' :  'qtip'
        });
        me.mon(field, {
            scope: me,
            blur: {
                fn: me.onBlur,
                
                delay: 1
            },
            specialkey: me.onSpecialKey
        });

        if (field.grow) {
            me.mon(field, 'autosize', me.onAutoSize,  me, {delay: 1});
        }
        me.floating = {
            constrain: me.constrain
        };

        me.callParent(arguments);

        me.addEvents(
            
            'beforestartedit',
            
            'startedit',
            
            'beforecomplete',
            
            'complete',
            
            'canceledit',
            
            'specialkey'
        );
    },

    
    onAutoSize: function(){
        this.doComponentLayout();
    },

    
    onRender : function(ct, position) {
        var me = this,
            field = me.field;

        me.callParent(arguments);

        field.render(me.el);
        
        
        field.inputEl.dom.name = '';
        if (me.swallowKeys) {
            field.inputEl.swallowEvent([
                'keypress', 
                'keydown'   
            ]);
        }
    },

    
    onSpecialKey : function(field, event) {
        var me = this,
            key = event.getKey(),
            complete = me.completeOnEnter && key == event.ENTER,
            cancel = me.cancelOnEsc && key == event.ESC;

        if (complete || cancel) {
            event.stopEvent();
            
            
            Ext.defer(function() {
                if (complete) {
                    me.completeEdit();
                } else {
                    me.cancelEdit();
                }
                if (field.triggerBlur) {
                    field.triggerBlur();
                }
            }, 10);
        }

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

    
    startEdit : function(el, value) {
        var me = this,
            field = me.field;

        me.completeEdit();
        me.boundEl = Ext.get(el);
        value = Ext.isDefined(value) ? value : me.boundEl.dom.innerHTML;

        if (!me.rendered) {
            me.render(me.parentEl || document.body);
        }

        if (me.fireEvent('beforestartedit', me, me.boundEl, value) !== false) {
            me.startValue = value;
            me.show();
            field.reset();
            field.setValue(value);
            me.realign(true);
            field.focus(false, 10);
            if (field.autoSize) {
                field.autoSize();
            }
            me.editing = true;
        }
    },

    
    realign : function(autoSize) {
        var me = this;
        if (autoSize === true) {
            me.doComponentLayout();
        }
        me.alignTo(me.boundEl, me.alignment, me.offsets);
    },

    
    completeEdit : function(remainVisible) {
        var me = this,
            field = me.field,
            value;

        if (!me.editing) {
            return;
        }

        
        if (field.assertValue) {
            field.assertValue();
        }

        value = me.getValue();
        if (!field.isValid()) {
            if (me.revertInvalid !== false) {
                me.cancelEdit(remainVisible);
            }
            return;
        }

        if (String(value) === String(me.startValue) && me.ignoreNoChange) {
            me.hideEdit(remainVisible);
            return;
        }

        if (me.fireEvent('beforecomplete', me, value, me.startValue) !== false) {
            
            value = me.getValue();
            if (me.updateEl && me.boundEl) {
                me.boundEl.update(value);
            }
            me.hideEdit(remainVisible);
            me.fireEvent('complete', me, value, me.startValue);
        }
    },

    
    onShow : function() {
        var me = this;

        me.callParent(arguments);
        if (me.hideEl !== false) {
            me.boundEl.hide();
        }
        me.fireEvent("startedit", me.boundEl, me.startValue);
    },

    
    cancelEdit : function(remainVisible) {
        var me = this,
            startValue = me.startValue,
            value;

        if (me.editing) {
            value = me.getValue();
            me.setValue(startValue);
            me.hideEdit(remainVisible);
            me.fireEvent('canceledit', me, value, startValue);
        }
    },

    
    hideEdit: function(remainVisible) {
        if (remainVisible !== true) {
            this.editing = false;
            this.hide();
        }
    },

    
    onBlur : function() {
        var me = this;

        
        if(me.allowBlur === true && me.editing && me.selectSameEditor !== true) {
            me.completeEdit();
        }
    },

    
    onHide : function() {
        var me = this,
            field = me.field;

        if (me.editing) {
            me.completeEdit();
            return;
        }
        field.blur();
        if (field.collapse) {
            field.collapse();
        }

        
        if (me.hideEl !== false) {
            me.boundEl.show();
        }
        me.callParent(arguments);
    },

    
    setValue : function(value) {
        this.field.setValue(value);
    },

    
    getValue : function() {
        return this.field.getValue();
    },

    beforeDestroy : function() {
        var me = this;

        Ext.destroy(me.field);
        delete me.field;
        delete me.parentEl;
        delete me.boundEl;

        me.callParent(arguments);
    }
});

Ext.define('Ext.Img', {
    extend: 'Ext.Component',
    alias: ['widget.image', 'widget.imagecomponent'],
    
    src: '',

    getElConfig: function() {
        return {
            tag: 'img',
            src: this.src
        };
    },
    
    
    initRenderTpl: Ext.emptyFn,
    
    
    setSrc: function(src) {
        var me = this,
            img = me.el;
        me.src = src;
        if (img) {
            img.dom.src = src;
        }
    }
});


Ext.define('Ext.Layer', {
    uses: ['Ext.Shadow'],

    
    statics: {
        shims: []
    },

    extend: 'Ext.core.Element',

    constructor: function(config, existingEl) {
        config = config || {};
        var me = this,
            dh = Ext.core.DomHelper,
            cp = config.parentEl,
            pel = cp ? Ext.getDom(cp) : document.body,
        hm = config.hideMode;

        if (existingEl) {
            me.dom = Ext.getDom(existingEl);
        }
        if (!me.dom) {
            me.dom = dh.append(pel, config.dh || {
                tag: 'div',
                cls: Ext.baseCSSPrefix + 'layer'
            });
        } else {
            me.addCls(Ext.baseCSSPrefix + 'layer');
            if (!me.dom.parentNode) {
                pel.appendChild(me.dom);
            }
        }

        if (config.cls) {
            me.addCls(config.cls);
        }
        me.constrain = config.constrain !== false;

        
        
        
        if (hm) {
            me.setVisibilityMode(Ext.core.Element[hm.toUpperCase()]);
            if (me.visibilityMode == Ext.core.Element.ASCLASS) {
                me.visibilityCls = config.visibilityCls;
            }
        } else if (config.useDisplay) {
            me.setVisibilityMode(Ext.core.Element.DISPLAY);
        } else {
            me.setVisibilityMode(Ext.core.Element.VISIBILITY);
        }

        if (config.id) {
            me.id = me.dom.id = config.id;
        } else {
            me.id = Ext.id(me.dom);
        }
        me.position('absolute');
        if (config.shadow) {
            me.shadowOffset = config.shadowOffset || 4;
            me.shadow = Ext.create('Ext.Shadow', {
                offset: me.shadowOffset,
                mode: config.shadow
            });
            me.disableShadow();
        } else {
            me.shadowOffset = 0;
        }
        me.useShim = config.shim !== false && Ext.useShims;
        if (config.hidden === true) {
            me.hide();
        } else {
            this.show();
        }
    },

    getZIndex: function() {
        return parseInt((this.getShim() || this).getStyle('z-index'), 10);
    },

    getShim: function() {
        var me = this,
            shim, pn;

        if (!me.useShim) {
            return null;
        }
        if (!me.shim) {
            shim = me.self.shims.shift();
            if (!shim) {
                shim = me.createShim();
                shim.enableDisplayMode('block');
                shim.hide();
            }
            pn = me.dom.parentNode;
            if (shim.dom.parentNode != pn) {
                pn.insertBefore(shim.dom, me.dom);
            }
            me.shim = shim;
        }
        return me.shim;
    },

    hideShim: function() {
        if (this.shim) {
            this.shim.setDisplayed(false);
            this.self.shims.push(this.shim);
            delete this.shim;
        }
    },

    disableShadow: function() {
        if (this.shadow) {
            this.shadowDisabled = true;
            this.shadow.hide();
            this.lastShadowOffset = this.shadowOffset;
            this.shadowOffset = 0;
        }
    },

    enableShadow: function(show) {
        if (this.shadow) {
            this.shadowDisabled = false;
            this.shadowOffset = this.lastShadowOffset;
            delete this.lastShadowOffset;
            if (show) {
                this.sync(true);
            }
        }
    },

    
    sync: function(doShow) {
        var me = this,
            shadow = me.shadow,
            shadowPos, shimStyle, shadowSize;

        if (!this.updating && this.isVisible() && (shadow || this.useShim)) {
            var shim = this.getShim(),
                l = this.getLeft(true),
                t = this.getTop(true),
                w = this.getWidth(),
                h = this.getHeight(),
                shimIndex;

            if (shadow && !this.shadowDisabled) {
                if (doShow && !shadow.isVisible()) {
                    shadow.show(this);
                } else {
                    shadow.realign(l, t, w, h);
                }
                if (shim) {
                    
                    shimIndex = shim.getStyle('z-index');
                    if (shimIndex > me.zindex) {
                        me.shim.setStyle('z-index', me.zindex - 2);
                    }
                    shim.show();
                    
                    if (shadow.isVisible()) {
                        shadowPos = shadow.el.getXY();
                        shimStyle = shim.dom.style;
                        shadowSize = shadow.el.getSize();
                        shimStyle.left = (shadowPos[0]) + 'px';
                        shimStyle.top = (shadowPos[1]) + 'px';
                        shimStyle.width = (shadowSize.width) + 'px';
                        shimStyle.height = (shadowSize.height) + 'px';
                    } else {
                        shim.setSize(w, h);
                        shim.setLeftTop(l, t);
                    }
                }
            } else if (shim) {
                
                shimIndex = shim.getStyle('z-index');
                if (shimIndex > me.zindex) {
                    me.shim.setStyle('z-index', me.zindex - 2);
                }
                shim.show();
                shim.setSize(w, h);
                shim.setLeftTop(l, t);
            }
        }
        return this;
    },

    remove: function() {
        this.hideUnders();
        this.callParent();
    },

    
    beginUpdate: function() {
        this.updating = true;
    },

    
    endUpdate: function() {
        this.updating = false;
        this.sync(true);
    },

    
    hideUnders: function() {
        if (this.shadow) {
            this.shadow.hide();
        }
        this.hideShim();
    },

    
    constrainXY: function() {
        if (this.constrain) {
            var vw = Ext.core.Element.getViewWidth(),
                vh = Ext.core.Element.getViewHeight(),
                s = Ext.getDoc().getScroll(),
                xy = this.getXY(),
                x = xy[0],
                y = xy[1],
                so = this.shadowOffset,
                w = this.dom.offsetWidth + so,
                h = this.dom.offsetHeight + so,
                moved = false; 
            
            if ((x + w) > vw + s.left) {
                x = vw - w - so;
                moved = true;
            }
            if ((y + h) > vh + s.top) {
                y = vh - h - so;
                moved = true;
            }
            
            if (x < s.left) {
                x = s.left;
                moved = true;
            }
            if (y < s.top) {
                y = s.top;
                moved = true;
            }
            if (moved) {
                Ext.Layer.superclass.setXY.call(this, [x, y]);
                this.sync();
            }
        }
        return this;
    },

    getConstrainOffset: function() {
        return this.shadowOffset;
    },

    
    setVisible: function(visible, animate, duration, callback, easing) {
        var me = this,
            cb;

        
        cb = function() {
            if (visible) {
                me.sync(true);
            }
            if (callback) {
                callback();
            }
        };

        
        if (!visible) {
            this.hideUnders(true);
        }
        this.callParent([visible, animate, duration, callback, easing]);
        if (!animate) {
            cb();
        }
        return this;
    },

    
    beforeFx: function() {
        this.beforeAction();
        return this.callParent(arguments);
    },

    
    afterFx: function() {
        this.callParent(arguments);
        this.sync(this.isVisible());
    },

    
    beforeAction: function() {
        if (!this.updating && this.shadow) {
            this.shadow.hide();
        }
    },

    
    setLeft: function(left) {
        this.callParent(arguments);
        return this.sync();
    },

    setTop: function(top) {
        this.callParent(arguments);
        return this.sync();
    },

    setLeftTop: function(left, top) {
        this.callParent(arguments);
        return this.sync();
    },

    setXY: function(xy, animate, duration, callback, easing) {

        
        callback = this.createCB(callback);

        this.fixDisplay();
        this.beforeAction();
        this.callParent([xy, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return this;
    },

    
    createCB: function(callback) {
        var me = this,
            showShadow = me.shadow && me.shadow.isVisible();

        return function() {
            me.constrainXY();
            me.sync(showShadow);
            if (callback) {
                callback();
            }
        };
    },

    
    setX: function(x, animate, duration, callback, easing) {
        this.setXY([x, this.getY()], animate, duration, callback, easing);
        return this;
    },

    
    setY: function(y, animate, duration, callback, easing) {
        this.setXY([this.getX(), y], animate, duration, callback, easing);
        return this;
    },

    
    setSize: function(w, h, animate, duration, callback, easing) {
        
        callback = this.createCB(callback);

        this.beforeAction();
        this.callParent([w, h, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return this;
    },

    
    setWidth: function(w, animate, duration, callback, easing) {
        
        callback = this.createCB(callback);

        this.beforeAction();
        this.callParent([w, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return this;
    },

    
    setHeight: function(h, animate, duration, callback, easing) {
        
        callback = this.createCB(callback);

        this.beforeAction();
        this.callParent([h, animate, duration, callback, easing]);
        if (!animate) {
            callback();
        }
        return this;
    },

    
    setBounds: function(x, y, width, height, animate, duration, callback, easing) {
        
        callback = this.createCB(callback);

        this.beforeAction();
        if (!animate) {
            Ext.Layer.superclass.setXY.call(this, [x, y]);
            Ext.Layer.superclass.setSize.call(this, width, height);
            callback();
        } else {
            this.callParent([x, y, width, height, animate, duration, callback, easing]);
        }
        return this;
    },

    
    setZIndex: function(zindex) {
        this.zindex = zindex;
        if (this.getShim()) {
            this.shim.setStyle('z-index', zindex++);
        }
        if (this.shadow) {
            this.shadow.setZIndex(zindex++);
        }
        this.setStyle('z-index', zindex);
        return this;
    }
});



Ext.define('Ext.layout.component.ProgressBar', {

    

    alias: ['layout.progressbar'],

    extend: 'Ext.layout.component.Component',

    

    type: 'progressbar',

    onLayout: function(width, height) {
        var me = this,
            owner = me.owner,
            textEl = owner.textEl;
        
        me.setElementSize(owner.el, width, height);
        textEl.setWidth(owner.el.getWidth(true));
        
        me.callParent([width, height]);
        
        owner.updateProgress(owner.value);
    }
});

Ext.define('Ext.ProgressBar', {
    extend: 'Ext.Component',
    alias: 'widget.progressbar',

    requires: [
        'Ext.Template',
        'Ext.CompositeElement',
        'Ext.TaskManager',
        'Ext.layout.component.ProgressBar'
    ],

    uses: ['Ext.fx.Anim'],
   
    baseCls: Ext.baseCSSPrefix + 'progress',

    config: {
        
        animate: false,

        
        text: ''
    },

    
    waitTimer: null,

    renderTpl: [
        '<div class="{baseCls}-text {baseCls}-text-back">',
            '<div>&#160;</div>',
        '</div>',
        '<div class="{baseCls}-bar">',
            '<div class="{baseCls}-text">',
                '<div>&#160;</div>',
            '</div>',
        '</div>'
    ],

    componentLayout: 'progressbar',

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

        this.renderSelectors = Ext.apply(this.renderSelectors || {}, {
            textTopEl: '.' + this.baseCls + '-text',
            textBackEl: '.' + this.baseCls + '-text-back',
            bar: '.' + this.baseCls + '-bar'
        });

        this.addEvents(
            
            "update"
        );
    },

    afterRender : function() {
        var me = this;

        me.textEl = me.textEl ? Ext.get(me.textEl) : me.el.select('.' + me.baseCls + '-text');

        this.callParent(arguments);

        if (me.value) {
            me.updateProgress(me.value, me.text);
        }
        else {
            me.updateText(me.text);
        }
    },

    
    updateProgress: function(value, text, animate) {
        var newWidth;
        this.value = value || 0;
        if (text) {
            this.updateText(text);
        }
        if (this.rendered && !this.isDestroyed) {
            newWidth = Math.floor(this.value * this.el.getWidth(true));
            if (Ext.isForcedBorderBox) {
                newWidth += this.bar.getBorderWidth("lr");
            }
            if (animate === true || (animate !== false && this.animate)) {
                this.bar.stopAnimation();
                this.bar.animate(Ext.apply({
                    to: {
                        width: newWidth + 'px'
                    }
                }, this.animate));
            } else {
                this.bar.setWidth(newWidth);
            }
        }
        this.fireEvent('update', this, this.value, text);
        return this;
    },

    
    updateText: function(text) {
        this.text = text;
        if (this.rendered) {
            this.textEl.update(this.text);
        }
        return this;
    },

    applyText : function(text) {
        this.updateText(text);
    },

    
    wait: function(o) {
        if (!this.waitTimer) {
            var scope = this;
            o = o || {};
            this.updateText(o.text);
            this.waitTimer = Ext.TaskManager.start({
                run: function(i){
                    var inc = o.increment || 10;
                    i -= 1;
                    this.updateProgress(((((i+inc)%inc)+1)*(100/inc))*0.01, null, o.animate);
                },
                interval: o.interval || 1000,
                duration: o.duration,
                onStop: function(){
                    if (o.fn) {
                        o.fn.apply(o.scope || this);
                    }
                    this.reset();
                },
                scope: scope
            });
        }
        return this;
    },

    
    isWaiting: function(){
        return this.waitTimer !== null;
    },

    
    reset: function(hide){
        this.updateProgress(0);
        this.clearTimer();
        if (hide === true) {
            this.hide();
        }
        return this;
    },

    
    clearTimer: function(){
        if (this.waitTimer) {
            this.waitTimer.onStop = null; 
            Ext.TaskManager.stop(this.waitTimer);
            this.waitTimer = null;
        }
    },

    onDestroy: function(){
        this.clearTimer();
        if (this.rendered) {
            if (this.textEl.isComposite) {
                this.textEl.clear();
            }
            Ext.destroyMembers(this, 'textEl', 'progressBar', 'textTopEl');
        }
        this.callParent();
    }
});


Ext.define('Ext.ShadowPool', {
    singleton: true,
    requires: ['Ext.core.DomHelper'],

    markup: function() {
        if (Ext.supports.CSS3BoxShadow) {
            return '<div class="' + Ext.baseCSSPrefix + 'css-shadow" role="presentation"></div>';
        } else if (Ext.isIE) {
            return '<div class="' + Ext.baseCSSPrefix + 'ie-shadow" role="presentation"></div>';
        } else {
            return '<div class="' + Ext.baseCSSPrefix + 'frame-shadow" role="presentation">' +
                '<div class="xst" role="presentation">' +
                    '<div class="xstl" role="presentation"></div>' +
                    '<div class="xstc" role="presentation"></div>' +
                    '<div class="xstr" role="presentation"></div>' +
                '</div>' +
                '<div class="xsc" role="presentation">' +
                    '<div class="xsml" role="presentation"></div>' +
                    '<div class="xsmc" role="presentation"></div>' +
                    '<div class="xsmr" role="presentation"></div>' +
                '</div>' +
                '<div class="xsb" role="presentation">' +
                    '<div class="xsbl" role="presentation"></div>' +
                    '<div class="xsbc" role="presentation"></div>' +
                    '<div class="xsbr" role="presentation"></div>' +
                '</div>' +
            '</div>';
        }
    }(),

    shadows: [],

    pull: function() {
        var sh = this.shadows.shift();
        if (!sh) {
            sh = Ext.get(Ext.core.DomHelper.insertHtml("beforeBegin", document.body.firstChild, this.markup));
            sh.autoBoxAdjust = false;
        }
        return sh;
    },

    push: function(sh) {
        this.shadows.push(sh);
    },
    
    reset: function() {
        Ext.Array.each(this.shadows, function(shadow) {
            shadow.remove();
        });
        this.shadows = [];
    }
});

Ext.define('Ext.Shadow', {
    requires: ['Ext.ShadowPool'],

    constructor: function(config) {
        Ext.apply(this, config);
        if (typeof this.mode != "string") {
            this.mode = this.defaultMode;
        }
        var offset = this.offset,
            adjusts = {
                h: 0
            },
            rad = Math.floor(this.offset / 2);

        switch (this.mode.toLowerCase()) {
            
            case "drop":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts.w = adjusts.h = -offset;
                    adjusts.l = adjusts.t = offset;
                } else {
                    adjusts.w = 0;
                    adjusts.l = adjusts.t = offset;
                    adjusts.t -= 1;
                    if (Ext.isIE) {
                        adjusts.l -= offset + rad;
                        adjusts.t -= offset + rad;
                        adjusts.w -= rad;
                        adjusts.h -= rad;
                        adjusts.t += 1;
                    }
                }
                break;
            case "sides":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts.h -= offset;
                    adjusts.t = offset;
                    adjusts.l = adjusts.w = 0;
                } else {
                    adjusts.w = (offset * 2);
                    adjusts.l = -offset;
                    adjusts.t = offset - 1;
                    if (Ext.isIE) {
                        adjusts.l -= (offset - rad);
                        adjusts.t -= offset + rad;
                        adjusts.l += 1;
                        adjusts.w -= (offset - rad) * 2;
                        adjusts.w -= rad + 1;
                        adjusts.h -= 1;
                    }
                }
                break;
            case "frame":
                if (Ext.supports.CSS3BoxShadow) {
                    adjusts.l = adjusts.w = adjusts.t = 0;
                } else {
                    adjusts.w = adjusts.h = (offset * 2);
                    adjusts.l = adjusts.t = -offset;
                    adjusts.t += 1;
                    adjusts.h -= 2;
                    if (Ext.isIE) {
                        adjusts.l -= (offset - rad);
                        adjusts.t -= (offset - rad);
                        adjusts.l += 1;
                        adjusts.w -= (offset + rad + 1);
                        adjusts.h -= (offset + rad);
                        adjusts.h += 1;
                    }
                    break;
                }
        }
        this.adjusts = adjusts;
    },

    
    
    offset: 4,

    
    defaultMode: "drop",

    
    show: function(target) {
        target = Ext.get(target);
        if (!this.el) {
            this.el = Ext.ShadowPool.pull();
            if (this.el.dom.nextSibling != target.dom) {
                this.el.insertBefore(target);
            }
        }
        this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10) - 1);
        if (Ext.isIE && !Ext.supports.CSS3BoxShadow) {
            this.el.dom.style.filter = "progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius=" + (this.offset) + ")";
        }
        this.realign(
            target.getLeft(true),
            target.getTop(true),
            target.getWidth(),
            target.getHeight()
        );
        this.el.dom.style.display = "block";
    },

    
    isVisible: function() {
        return this.el ? true: false;
    },

    
    realign: function(l, t, targetWidth, targetHeight) {
        if (!this.el) {
            return;
        }
        var adjusts = this.adjusts,
            d = this.el.dom,
            targetStyle = d.style,
            shadowWidth,
            shadowHeight,
            cn,
            sww, 
            sws, 
            shs;

        targetStyle.left = (l + adjusts.l) + "px";
        targetStyle.top = (t + adjusts.t) + "px";
        shadowWidth = Math.max(targetWidth + adjusts.w, 0);
        shadowHeight = Math.max(targetHeight + adjusts.h, 0);
        sws = shadowWidth + "px";
        shs = shadowHeight + "px";
        if (targetStyle.width != sws || targetStyle.height != shs) {
            targetStyle.width = sws;
            targetStyle.height = shs;
            if (Ext.supports.CSS3BoxShadow) {
                targetStyle.boxShadow = '0 0 ' + this.offset + 'px 0 #888';
            } else {

                
                if (!Ext.isIE) {
                    cn = d.childNodes;
                    sww = Math.max(0, (shadowWidth - 12)) + "px";
                    cn[0].childNodes[1].style.width = sww;
                    cn[1].childNodes[1].style.width = sww;
                    cn[2].childNodes[1].style.width = sww;
                    cn[1].style.height = Math.max(0, (shadowHeight - 12)) + "px";
                }
            }
        }
    },

    
    hide: function() {
        if (this.el) {
            this.el.dom.style.display = "none";
            Ext.ShadowPool.push(this.el);
            delete this.el;
        }
    },

    
    setZIndex: function(z) {
        this.zIndex = z;
        if (this.el) {
            this.el.setStyle("z-index", z);
        }
    }
});


Ext.define('Ext.button.Split', {

    

    alias: 'widget.splitbutton',

    extend: 'Ext.button.Button',
    alternateClassName: 'Ext.SplitButton',

    
    arrowCls      : 'split',
    split         : true,

    
    initComponent : function(){
        this.callParent();
        
        this.addEvents("arrowclick");
    },

     
    setArrowHandler : function(handler, scope){
        this.arrowHandler = handler;
        this.scope = scope;
    },

    
    onClick : function(e, t) {
        var me = this;
        
        e.preventDefault();
        if (!me.disabled) {
            if (me.overMenuTrigger) {
                if (me.menu && !me.menu.isVisible() && !me.ignoreNextClick) {
                    me.showMenu();
                }
                me.fireEvent("arrowclick", me, e);
                if (me.arrowHandler) {
                    me.arrowHandler.call(me.scope || me, me, e);
                }
            } else {
                if (me.enableToggle) {
                    me.toggle();
                }
                me.fireEvent("click", me, e);
                if (me.handler) {
                    me.handler.call(me.scope || me, me, e);
                }
                me.onBlur();
            }
        }
    }
});


Ext.define('Ext.button.Cycle', {

    

    alias: 'widget.cycle',

    extend: 'Ext.button.Split',
    alternateClassName: 'Ext.CycleButton',

    

    
    
    
    
    
    

    
    getButtonText: function(item) {
        var me = this,
            text = '';

        if (item && me.showText === true) {
            if (me.prependText) {
                text += me.prependText;
            }
            text += item.text;
            return text;
        }
        return me.text;
    },

    
    setActiveItem: function(item, suppressEvent) {
        var me = this;

        if (!Ext.isObject(item)) {
            item = me.menu.getComponent(item);
        }
        if (item) {
            if (!me.rendered) {
                me.text = me.getButtonText(item);
                me.iconCls = item.iconCls;
            } else {
                me.setText(me.getButtonText(item));
                me.setIconCls(item.iconCls);
            }
            me.activeItem = item;
            if (!item.checked) {
                item.setChecked(true, false);
            }
            if (me.forceIcon) {
                me.setIconCls(me.forceIcon);
            }
            if (!suppressEvent) {
                me.fireEvent('change', me, item);
            }
        }
    },

    
    getActiveItem: function() {
        return this.activeItem;
    },

    
    initComponent: function() {
        var me = this,
            checked = 0,
            items;

        me.addEvents(
            
            "change"
        );

        if (me.changeHandler) {
            me.on('change', me.changeHandler, me.scope || me);
            delete me.changeHandler;
        }

        
        
        items = (me.menu.items||[]).concat(me.items||[]);
        me.menu = Ext.applyIf({
            cls: Ext.baseCSSPrefix + 'cycle-menu',
            items: []
        }, me.menu);

        
        Ext.each(items, function(item, i) {
            item = Ext.applyIf({
                group: me.id,
                itemIndex: i,
                checkHandler: me.checkHandler,
                scope: me,
                checked: item.checked || false
            }, item);
            me.menu.items.push(item);
            if (item.checked) {
                checked = i;
            }
        });
        me.itemCount = me.menu.items.length;
        me.callParent(arguments);
        me.on('click', me.toggleSelected, me);
        me.setActiveItem(checked, me);

        
        if (me.width && me.showText) {
            me.addCls(Ext.baseCSSPrefix + 'cycle-fixed-width');
        }
    },

    
    checkHandler: function(item, pressed) {
        if (pressed) {
            this.setActiveItem(item);
        }
    },

    
    toggleSelected: function() {
        var me = this,
            m = me.menu,
            checkItem;

        checkItem = me.activeItem.next(':not([disabled])') || m.items.getAt(0);
        checkItem.setChecked(true);
    }
});

Ext.define('Ext.container.ButtonGroup', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.buttongroup',
    alternateClassName: 'Ext.ButtonGroup',

    

    
    baseCls: Ext.baseCSSPrefix + 'btn-group',

    
    layout: {
        type: 'table'
    },

    defaultType: 'button',

    
    frame: true,
    
    frameHeader: false,
    
    internalDefaults: {removeMode: 'container', hideParent: true},

    initComponent : function(){
        
        var me = this,
            cols = me.columns;

        me.noTitleCls = me.baseCls + '-notitle';
        if (cols) {
            me.layout = Ext.apply({}, {columns: cols}, me.layout);
        }

        if (!me.title) {
            me.addCls(me.noTitleCls);
        }
        me.callParent(arguments);
    },

    afterLayout: function() {
        var me = this;
        
        me.callParent(arguments);

        
        
        if (me.layout.table && (Ext.isIEQuirks || Ext.isIE6) && !me.width) {
            var t = me.getTargetEl();
            t.setWidth(me.layout.table.offsetWidth + t.getPadding('lr'));
        }
    },

    afterRender: function() {
        var me = this;
        
        
        if (me.header) {
            me.header.insert(0, {
                xtype: 'component',
                ui   : me.ui,
                html : '&nbsp;',
                flex : 1
            });
        }
        
        me.callParent(arguments);
    },
    
    
    onBeforeAdd: function(component) {
        if (component.is('button')) {
            component.ui = component.ui + '-toolbar';
        }
        this.callParent(arguments);
    },

    
    applyDefaults: function(c) {
        if (!Ext.isString(c)) {
            c = this.callParent(arguments);
            var d = this.internalDefaults;
            if (c.events) {
                Ext.applyIf(c.initialConfig, d);
                Ext.apply(c, d);
            } else {
                Ext.applyIf(c, d);
            }
        }
        return c;
    }

    
    
    
    
    
});


Ext.define('Ext.container.Viewport', {
    extend: 'Ext.container.Container',
    alias: 'widget.viewport',
    requires: ['Ext.EventManager'],
    alternateClassName: 'Ext.Viewport',

    
    
    
    
    
    
    
    
    
    
    
    

    isViewport: true,

    ariaRole: 'application',
    initComponent : function() {
        var me = this,
            html = Ext.fly(document.body.parentNode),
            el;
        me.callParent(arguments);
        html.addCls(Ext.baseCSSPrefix + 'viewport');
        if (me.autoScroll) {
            html.setStyle('overflow', 'auto');
        }
        me.el = el = Ext.getBody();
        el.setHeight = Ext.emptyFn;
        el.setWidth = Ext.emptyFn;
        el.setSize = Ext.emptyFn;
        el.dom.scroll = 'no';
        me.allowDomMove = false;
        
        
        Ext.EventManager.onWindowResize(me.fireResize, me);
        me.renderTo = me.el;
    },

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





Ext.define('Ext.dd.DDTarget', {
    extend: 'Ext.dd.DragDrop',
    constructor: function(id, sGroup, config) {
        if (id) {
            this.initTarget(id, sGroup, config);
        }
    },

    
    getDragEl: Ext.emptyFn,
    
    isValidHandleChild: Ext.emptyFn,
    
    startDrag: Ext.emptyFn,
    
    endDrag: Ext.emptyFn,
    
    onDrag: Ext.emptyFn,
    
    onDragDrop: Ext.emptyFn,
    
    onDragEnter: Ext.emptyFn,
    
    onDragOut: Ext.emptyFn,
    
    onDragOver: Ext.emptyFn,
    
    onInvalidDrop: Ext.emptyFn,
    
    onMouseDown: Ext.emptyFn,
    
    onMouseUp: Ext.emptyFn,
    
    setXConstraint: Ext.emptyFn,
    
    setYConstraint: Ext.emptyFn,
    
    resetConstraints: Ext.emptyFn,
    
    clearConstraints: Ext.emptyFn,
    
    clearTicks: Ext.emptyFn,
    
    setInitPosition: Ext.emptyFn,
    
    setDragElId: Ext.emptyFn,
    
    setHandleElId: Ext.emptyFn,
    
    setOuterHandleElId: Ext.emptyFn,
    
    addInvalidHandleClass: Ext.emptyFn,
    
    addInvalidHandleId: Ext.emptyFn,
    
    addInvalidHandleType: Ext.emptyFn,
    
    removeInvalidHandleClass: Ext.emptyFn,
    
    removeInvalidHandleId: Ext.emptyFn,
    
    removeInvalidHandleType: Ext.emptyFn,

    toString: function() {
        return ("DDTarget " + this.id);
    }
});

Ext.define('Ext.dd.DragTracker', {

    uses: ['Ext.util.Region'],

    mixins: {
        observable: 'Ext.util.Observable'
    },

    
    active: false,

    

    
    trackOver: false,

    

    

    
    tolerance: 5,

    
    autoStart: false,

    

    

    

    constructor : function(config){
        Ext.apply(this, config);
        this.addEvents(
            
            'mouseover',

            
            'mouseout',

            
            'mousedown',

            
            'mouseup',

            
            'mousemove',

            
            'beforedragstart',

            
            'dragstart',

            
            'dragend',

            
            'drag'
        );

        this.dragRegion = Ext.create('Ext.util.Region', 0,0,0,0);

        if (this.el) {
            this.initEl(this.el);
        }

        
        this.mixins.observable.constructor.call(this);
        if (this.disabled) {
            this.disable();
        }

    },

    
    initEl: function(el) {
        this.el = Ext.get(el);

        
        this.handle = Ext.get(this.delegate);

        
        this.delegate = this.handle ? undefined : this.delegate;

        if (!this.handle) {
            this.handle = this.el;
        }

        
        
        this.mon(this.handle, {
            mousedown: this.onMouseDown,
            delegate: this.delegate,
            scope: this
        });

        
        
        
        if (this.trackOver || this.overCls) {
            this.mon(this.handle, {
                mouseover: this.onMouseOver,
                mouseout: this.onMouseOut,
                delegate: this.delegate,
                scope: this
            });
        }
    },

    disable: function() {
        this.disabled = true;
    },

    enable: function() {
        this.disabled = false;
    },

    destroy : function() {
        this.clearListeners();
        delete this.el;
    },

    
    
    onMouseOver: function(e, target) {
        var me = this;
        if (!me.disabled) {
            if (Ext.EventManager.contains(e) || me.delegate) {
                me.mouseIsOut = false;
                if (me.overCls) {
                    me.el.addCls(me.overCls);
                }
                me.fireEvent('mouseover', me, e, me.delegate ? e.getTarget(me.delegate, target) : me.handle);
            }
        }
    },

    
    
    onMouseOut: function(e) {
        if (this.mouseIsDown) {
            this.mouseIsOut = true;
        } else {
            if (this.overCls) {
                this.el.removeCls(this.overCls);
            }
            this.fireEvent('mouseout', this, e);
        }
    },

    onMouseDown: function(e, target){
        
        if (this.disabled ||e.dragTracked) {
            return;
        }

        
        this.dragTarget = this.delegate ? target : this.handle.dom;
        this.startXY = this.lastXY = e.getXY();
        this.startRegion = Ext.fly(this.dragTarget).getRegion();

        if (this.fireEvent('mousedown', this, e) === false ||
            this.fireEvent('beforedragstart', this, e) === false ||
            this.onBeforeStart(e) === false) {
            return;
        }

        
        
        this.mouseIsDown = true;

        
        e.dragTracked = true;

        if (this.preventDefault !== false) {
            e.preventDefault();
        }
        Ext.getDoc().on({
            scope: this,
            mouseup: this.onMouseUp,
            mousemove: this.onMouseMove,
            selectstart: this.stopSelect
        });
        if (this.autoStart) {
            this.timer =  Ext.defer(this.triggerStart, this.autoStart === true ? 1000 : this.autoStart, this, [e]);
        }
    },

    onMouseMove: function(e, target){
        
        
        if (this.active && Ext.isIE && !e.browserEvent.button) {
            e.preventDefault();
            this.onMouseUp(e);
            return;
        }

        e.preventDefault();
        var xy = e.getXY(),
            s = this.startXY;

        this.lastXY = xy;
        if (!this.active) {
            if (Math.max(Math.abs(s[0]-xy[0]), Math.abs(s[1]-xy[1])) > this.tolerance) {
                this.triggerStart(e);
            } else {
                return;
            }
        }

        
        if (this.fireEvent('mousemove', this, e) === false) {
            this.onMouseUp(e);
        } else {
            this.onDrag(e);
            this.fireEvent('drag', this, e);
        }
    },

    onMouseUp: function(e) {
        
        
        this.mouseIsDown = false;

        
        delete e.dragTracked;

        
        if (this.mouseIsOut) {
            this.mouseIsOut = false;
            this.onMouseOut(e);
        }
        e.preventDefault();
        this.fireEvent('mouseup', this, e);
        this.endDrag(e);
    },

    
    endDrag: function(e) {
        var doc = Ext.getDoc(),
        wasActive = this.active;

        doc.un('mousemove', this.onMouseMove, this);
        doc.un('mouseup', this.onMouseUp, this);
        doc.un('selectstart', this.stopSelect, this);
        this.clearStart();
        this.active = false;
        if (wasActive) {
            this.onEnd(e);
            this.fireEvent('dragend', this, e);
        }
        
        delete this._constrainRegion;
    },

    triggerStart: function(e) {
        this.clearStart();
        this.active = true;
        this.onStart(e);
        this.fireEvent('dragstart', this, e);
    },

    clearStart : function() {
        if (this.timer) {
            clearTimeout(this.timer);
            delete this.timer;
        }
    },

    stopSelect : function(e) {
        e.stopEvent();
        return false;
    },

    
    onBeforeStart : function(e) {

    },

    
    onStart : function(xy) {

    },

    
    onDrag : function(e) {

    },

    
    onEnd : function(e) {

    },

    
    getDragTarget : function(){
        return this.dragTarget;
    },

    
    getDragCt : function(){
        return this.el;
    },

    
    getConstrainRegion: function() {
        if (this.constrainTo) {
            if (this.constrainTo instanceof Ext.util.Region) {
                return this.constrainTo;
            }
            if (!this._constrainRegion) {
                this._constrainRegion = Ext.fly(this.constrainTo).getViewRegion();
            }
        } else {
            if (!this._constrainRegion) {
                this._constrainRegion = this.getDragCt().getViewRegion();
            }
        }
        return this._constrainRegion;
    },

    getXY : function(constrain){
        return constrain ? this.constrainModes[constrain](this, this.lastXY) : this.lastXY;
    },

    
    getOffset : function(constrain){
        var xy = this.getXY(constrain),
            s = this.startXY;

        return [xy[0]-s[0], xy[1]-s[1]];
    },

    constrainModes: {
        
        point: function(me, xy) {
            var dr = me.dragRegion,
                constrainTo = me.getConstrainRegion();

            
            if (!constrainTo) {
                return xy;
            }

            dr.x = dr.left = dr[0] = dr.right = xy[0];
            dr.y = dr.top = dr[1] = dr.bottom = xy[1];
            dr.constrainTo(constrainTo);

            return [dr.left, dr.top];
        },

        
        dragTarget: function(me, xy) {
            var s = me.startXY,
                dr = me.startRegion.copy(),
                constrainTo = me.getConstrainRegion(),
                adjust;

            
            if (!constrainTo) {
                return xy;
            }

            
            
            
            dr.translateBy(xy[0]-s[0], xy[1]-s[1]);

            
            if (dr.right > constrainTo.right) {
                xy[0] += adjust = (constrainTo.right - dr.right);    
                dr.left += adjust;
            }
            if (dr.left < constrainTo.left) {
                xy[0] += (constrainTo.left - dr.left);      
            }

            
            if (dr.bottom > constrainTo.bottom) {
                xy[1] += adjust = (constrainTo.bottom - dr.bottom);  
                dr.top += adjust;
            }
            if (dr.top < constrainTo.top) {
                xy[1] += (constrainTo.top - dr.top);        
            }
            return xy;
        }
    }
});

Ext.define('Ext.dd.DragZone', {

    extend: 'Ext.dd.DragSource',

    constructor : function(el, config){
        this.callParent([el, config]);
        if (this.containerScroll) {
            Ext.dd.ScrollManager.register(this.el);
        }
    },

    

    

    
    getDragData : function(e){
        return Ext.dd.Registry.getHandleFromEvent(e);
    },

    
    onInitDrag : function(x, y){
        this.proxy.update(this.dragData.ddel.cloneNode(true));
        this.onStartDrag(x, y);
        return true;
    },

    
    afterRepair : function(){
        var me = this;
        if (Ext.enableFx) {
            Ext.fly(me.dragData.ddel).highlight(me.repairHighlightColor);
        }
        me.dragging = false;
    },

    
    getRepairXY : function(e){
        return Ext.core.Element.fly(this.dragData.ddel).getXY();
    },

    destroy : function(){
        this.callParent();
        if (this.containerScroll) {
            Ext.dd.ScrollManager.unregister(this.el);
        }
    }
});


Ext.define('Ext.dd.ScrollManager', {
    singleton: true,
    requires: [
        'Ext.dd.DragDropManager'
    ],

    constructor: function() {
        var ddm = Ext.dd.DragDropManager;
        ddm.fireEvents = Ext.Function.createSequence(ddm.fireEvents, this.onFire, this);
        ddm.stopDrag = Ext.Function.createSequence(ddm.stopDrag, this.onStop, this);
        this.doScroll = Ext.Function.bind(this.doScroll, this);
        this.ddmInstance = ddm;
        this.els = {};
        this.dragEl = null;
        this.proc = {};
    },

    onStop: function(e){
        var sm = Ext.dd.ScrollManager;
        sm.dragEl = null;
        sm.clearProc();
    },

    triggerRefresh: function() {
        if (this.ddmInstance.dragCurrent) {
            this.ddmInstance.refreshCache(this.ddmInstance.dragCurrent.groups);
        }
    },

    doScroll: function() {
        if (this.ddmInstance.dragCurrent) {
            var proc   = this.proc,
                procEl = proc.el,
                ddScrollConfig = proc.el.ddScrollConfig,
                inc = ddScrollConfig ? ddScrollConfig.increment : this.increment;

            if (!this.animate) {
                if (procEl.scroll(proc.dir, inc)) {
                    this.triggerRefresh();
                }
            } else {
                procEl.scroll(proc.dir, inc, true, this.animDuration, this.triggerRefresh);
            }
        }
    },

    clearProc: function() {
        var proc = this.proc;
        if (proc.id) {
            clearInterval(proc.id);
        }
        proc.id = 0;
        proc.el = null;
        proc.dir = "";
    },

    startProc: function(el, dir) {
        this.clearProc();
        this.proc.el = el;
        this.proc.dir = dir;
        var group = el.ddScrollConfig ? el.ddScrollConfig.ddGroup : undefined,
            freq  = (el.ddScrollConfig && el.ddScrollConfig.frequency)
                  ? el.ddScrollConfig.frequency
                  : this.frequency;

        if (group === undefined || this.ddmInstance.dragCurrent.ddGroup == group) {
            this.proc.id = setInterval(this.doScroll, freq);
        }
    },

    onFire: function(e, isDrop) {
        if (isDrop || !this.ddmInstance.dragCurrent) {
            return;
        }
        if (!this.dragEl || this.dragEl != this.ddmInstance.dragCurrent) {
            this.dragEl = this.ddmInstance.dragCurrent;
            
            this.refreshCache();
        }

        var xy = e.getXY(),
            pt = e.getPoint(),
            proc = this.proc,
            els = this.els;

        for (var id in els) {
            var el = els[id], r = el._region;
            var c = el.ddScrollConfig ? el.ddScrollConfig : this;
            if (r && r.contains(pt) && el.isScrollable()) {
                if (r.bottom - pt.y <= c.vthresh) {
                    if(proc.el != el){
                        this.startProc(el, "down");
                    }
                    return;
                }else if (r.right - pt.x <= c.hthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "left");
                    }
                    return;
                } else if(pt.y - r.top <= c.vthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "up");
                    }
                    return;
                } else if(pt.x - r.left <= c.hthresh) {
                    if (proc.el != el) {
                        this.startProc(el, "right");
                    }
                    return;
                }
            }
        }
        this.clearProc();
    },

    
    register : function(el){
        if (Ext.isArray(el)) {
            for(var i = 0, len = el.length; i < len; i++) {
                    this.register(el[i]);
            }
        } else {
            el = Ext.get(el);
            this.els[el.id] = el;
        }
    },

    
    unregister : function(el){
        if(Ext.isArray(el)){
            for (var i = 0, len = el.length; i < len; i++) {
                this.unregister(el[i]);
            }
        }else{
            el = Ext.get(el);
            delete this.els[el.id];
        }
    },

    
    vthresh : 25,
    
    hthresh : 25,

    
    increment : 100,

    
    frequency : 500,

    
    animate: true,

    
    animDuration: 0.4,

    
    ddGroup: undefined,

    
    refreshCache : function(){
        var els = this.els,
            id;
        for (id in els) {
            if(typeof els[id] == 'object'){ 
                els[id]._region = els[id].getRegion();
            }
        }
    }
});


Ext.define('Ext.dd.DropTarget', {
    extend: 'Ext.dd.DDTarget',
    requires: ['Ext.dd.ScrollManager'],

    constructor : function(el, config){
        this.el = Ext.get(el);

        Ext.apply(this, config);

        if(this.containerScroll){
            Ext.dd.ScrollManager.register(this.el);
        }

        this.callParent([this.el.dom, this.ddGroup || this.group,
              {isTarget: true}]);
    },

    
    
    
    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
    
    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',

    
    isTarget : true,

    
    isNotifyTarget : true,

    
    notifyEnter : function(dd, e, data){
        if(this.overClass){
            this.el.addCls(this.overClass);
        }
        return this.dropAllowed;
    },

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

    
    notifyOut : function(dd, e, data){
        if(this.overClass){
            this.el.removeCls(this.overClass);
        }
    },

    
    notifyDrop : function(dd, e, data){
        return false;
    },

    destroy : function(){
        this.callParent();
        if(this.containerScroll){
            Ext.dd.ScrollManager.unregister(this.el);
        }
    }
});


Ext.define('Ext.dd.Registry', {
    singleton: true,
    constructor: function() {
        this.elements = {}; 
        this.handles = {}; 
        this.autoIdSeed = 0;
    },
    
    getId: function(el, autogen){
        if(typeof el == "string"){
            return el;
        }
        var id = el.id;
        if(!id && autogen !== false){
            id = "extdd-" + (++this.autoIdSeed);
            el.id = id;
        }
        return id;
    },
    
    
    register : function(el, data){
        data = data || {};
        if (typeof el == "string") {
            el = document.getElementById(el);
        }
        data.ddel = el;
        this.elements[this.getId(el)] = data;
        if (data.isHandle !== false) {
            this.handles[data.ddel.id] = data;
        }
        if (data.handles) {
            var hs = data.handles;
            for (var i = 0, len = hs.length; i < len; i++) {
                this.handles[this.getId(hs[i])] = data;
            }
        }
    },

    
    unregister : function(el){
        var id = this.getId(el, false);
        var data = this.elements[id];
        if(data){
            delete this.elements[id];
            if(data.handles){
                var hs = data.handles;
                for (var i = 0, len = hs.length; i < len; i++) {
                    delete this.handles[this.getId(hs[i], false)];
                }
            }
        }
    },

    
    getHandle : function(id){
        if(typeof id != "string"){ 
            id = id.id;
        }
        return this.handles[id];
    },

    
    getHandleFromEvent : function(e){
        var t = e.getTarget();
        return t ? this.handles[t.id] : null;
    },

    
    getTarget : function(id){
        if(typeof id != "string"){ 
            id = id.id;
        }
        return this.elements[id];
    },

    
    getTargetFromEvent : function(e){
        var t = e.getTarget();
        return t ? this.elements[t.id] || this.handles[t.id] : null;
    }
});

Ext.define('Ext.dd.DropZone', {
    extend: 'Ext.dd.DropTarget',
    requires: ['Ext.dd.Registry'],

    
    getTargetFromEvent : function(e){
        return Ext.dd.Registry.getTargetFromEvent(e);
    },

    
    onNodeEnter : function(n, dd, e, data){
        
    },

    
    onNodeOver : function(n, dd, e, data){
        return this.dropAllowed;
    },

    
    onNodeOut : function(n, dd, e, data){
        
    },

    
    onNodeDrop : function(n, dd, e, data){
        return false;
    },

    
    onContainerOver : function(dd, e, data){
        return this.dropNotAllowed;
    },

    
    onContainerDrop : function(dd, e, data){
        return false;
    },

    
    notifyEnter : function(dd, e, data){
        return this.dropNotAllowed;
    },

    
    notifyOver : function(dd, e, data){
        var n = this.getTargetFromEvent(e);
        if(!n) { 
            if(this.lastOverNode){
                this.onNodeOut(this.lastOverNode, dd, e, data);
                this.lastOverNode = null;
            }
            return this.onContainerOver(dd, e, data);
        }
        if(this.lastOverNode != n){
            if(this.lastOverNode){
                this.onNodeOut(this.lastOverNode, dd, e, data);
            }
            this.onNodeEnter(n, dd, e, data);
            this.lastOverNode = n;
        }
        return this.onNodeOver(n, dd, e, data);
    },

    
    notifyOut : function(dd, e, data){
        if(this.lastOverNode){
            this.onNodeOut(this.lastOverNode, dd, e, data);
            this.lastOverNode = null;
        }
    },

    
    notifyDrop : function(dd, e, data){
        if(this.lastOverNode){
            this.onNodeOut(this.lastOverNode, dd, e, data);
            this.lastOverNode = null;
        }
        var n = this.getTargetFromEvent(e);
        return n ?
            this.onNodeDrop(n, dd, e, data) :
            this.onContainerDrop(dd, e, data);
    },

    
    triggerCacheRefresh : function() {
        Ext.dd.DDM.refreshCache(this.groups);
    }
});

Ext.define('Ext.flash.Component', {
    extend: 'Ext.Component',
    alternateClassName: 'Ext.FlashComponent',
    alias: 'widget.flash',

    
    flashVersion : '9.0.115',

    
    backgroundColor: '#ffffff',

    
    wmode: 'opaque',

    

    

    

    

    
    swfWidth: '100%',

    
    swfHeight: '100%',

    
    expressInstall: false,

    

    
    renderTpl: ['<div id="{swfId}"></div>'],

    initComponent: function() {
        if (!('swfobject' in window)) {
            Ext.Error.raise('The SWFObject library is not loaded. Ext.flash.Component requires SWFObject version 2.2 or later: http://code.google.com/p/swfobject/');
        }
        if (!this.url) {
            Ext.Error.raise('The "url" config is required for Ext.flash.Component');
        }

        this.callParent();
        this.addEvents(
            
            'success',

            
            'failure'
        );
    },

    onRender: function() {
        var me = this,
            params, vars, undef,
            swfId = me.getSwfId();

        me.renderData.swfId = swfId;

        me.callParent(arguments);

        params = Ext.apply({
            allowScriptAccess: 'always',
            bgcolor: me.backgroundColor,
            wmode: me.wmode
        }, me.flashParams);

        vars = Ext.apply({
            allowedDomain: document.location.hostname
        }, me.flashVars);

        new swfobject.embedSWF(
            me.url,
            swfId,
            me.swfWidth,
            me.swfHeight,
            me.flashVersion,
            me.expressInstall ? me.statics.EXPRESS_INSTALL_URL : undef,
            vars,
            params,
            me.flashAttributes,
            Ext.bind(me.swfCallback, me)
        );
    },

    
    swfCallback: function(e) {
        var me = this;
        if (e.success) {
            me.swf = Ext.get(e.ref);
            me.onSuccess();
            me.fireEvent('success', me);
        } else {
            me.onFailure();
            me.fireEvent('failure', me);
        }
    },

    
    getSwfId: function() {
        return this.swfId || (this.swfId = "extswf" + this.getAutoId());
    },

    onSuccess: function() {
        
        
        this.swf.setStyle('visibility', 'inherit');
    },

    onFailure: Ext.emptyFn,

    beforeDestroy: function() {
        var me = this,
            swf = me.swf;
        if (swf) {
            swfobject.removeSWF(me.getSwfId());
            Ext.destroy(swf);
            delete me.swf;
        }
        me.callParent();
    },

    statics: {
        
        EXPRESS_INSTALL_URL: 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf'
    }
});


Ext.define('Ext.form.action.Action', {
    alternateClassName: 'Ext.form.Action',

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    

    



    constructor: function(config) {
        if (config) {
            Ext.apply(this, config);
        }

        
        var params = config.params;
        if (Ext.isString(params)) {
            this.params = Ext.Object.fromQueryString(params);
        }
    },

    
    run: Ext.emptyFn,

    

    

    
    onFailure : function(response){
        this.response = response;
        this.failureType = Ext.form.action.Action.CONNECT_FAILURE;
        this.form.afterAction(this, false);
    },

    
    processResponse : function(response){
        this.response = response;
        if (!response.responseText && !response.responseXML) {
            return true;
        }
        return (this.result = this.handleResponse(response));
    },

    
    getUrl: function() {
        return this.url || this.form.url;
    },

    
    getMethod: function() {
        return (this.method || this.form.method || 'POST').toUpperCase();
    },

    
    getParams: function() {
        return Ext.apply({}, this.params, this.form.baseParams);
    },

    
    createCallback: function() {
        var me = this,
            undef,
            form = me.form;
        return {
            success: me.onSuccess,
            failure: me.onFailure,
            scope: me,
            timeout: (this.timeout * 1000) || (form.timeout * 1000),
            upload: form.fileUpload ? me.onSuccess : undef
        };
    },

    statics: {
        
        CLIENT_INVALID: 'client',

        
        SERVER_INVALID: 'server',

        
        CONNECT_FAILURE: 'connect',

        
        LOAD_FAILURE: 'load'


    }
});


Ext.define('Ext.form.action.Submit', {
    extend:'Ext.form.action.Action',
    alternateClassName: 'Ext.form.Action.Submit',
    alias: 'formaction.submit',

    type: 'submit',

    

    
    run : function(){
        var form = this.form;
        if (this.clientValidation === false || form.isValid()) {
            this.doSubmit();
        } else {
            
            this.failureType = Ext.form.action.Action.CLIENT_INVALID;
            form.afterAction(this, false);
        }
    },

    
    doSubmit: function() {
        var formEl,
            ajaxOptions = Ext.apply(this.createCallback(), {
                url: this.getUrl(),
                method: this.getMethod(),
                headers: this.headers
            });

        
        
        if (this.form.hasUpload()) {
            formEl = ajaxOptions.form = this.buildForm();
            ajaxOptions.isUpload = true;
        } else {
            ajaxOptions.params = this.getParams();
        }

        Ext.Ajax.request(ajaxOptions);

        if (formEl) {
            Ext.removeNode(formEl);
        }
    },

    
    getParams: function() {
        var nope = false,
            configParams = this.callParent(),
            fieldParams = this.form.getValues(nope, nope, this.submitEmptyText !== nope);
        return Ext.apply({}, fieldParams, configParams);
    },

    
    buildForm: function() {
        var fieldsSpec = [],
            formSpec,
            formEl,
            basicForm = this.form,
            params = this.getParams(),
            uploadFields = [];

        basicForm.getFields().each(function(field) {
            if (field.isFileUpload()) {
                uploadFields.push(field);
            }
        });

        function addField(name, val) {
            fieldsSpec.push({
                tag: 'input',
                type: 'hidden',
                name: name,
                value: Ext.String.htmlEncode(val)
            });
        }

        
        Ext.iterate(params, function(key, val) {
            if (Ext.isArray(val)) {
                Ext.each(val, function(v) {
                    addField(key, v);
                });
            } else {
                addField(key, val);
            }
        });

        formSpec = {
            tag: 'form',
            action: this.getUrl(),
            method: this.getMethod(),
            target: this.target || '_self',
            style: 'display:none',
            cn: fieldsSpec
        };

        
        if (uploadFields.length) {
            formSpec.encoding = formSpec.enctype = 'multipart/form-data';
        }

        
        formEl = Ext.core.DomHelper.append(Ext.getBody(), formSpec);

        
        
        
        Ext.Array.each(uploadFields, function(field) {
            if (field.rendered) { 
                formEl.appendChild(field.extractFileInput());
            }
        });

        return formEl;
    },



    
    onSuccess: function(response) {
        var form = this.form,
            success = true,
            result = this.processResponse(response);
        if (result !== true && !result.success) {
            if (result.errors) {
                form.markInvalid(result.errors);
            }
            this.failureType = Ext.form.action.Action.SERVER_INVALID;
            success = false;
        }
        form.afterAction(this, success);
    },

    
    handleResponse: function(response) {
        var form = this.form,
            errorReader = form.errorReader,
            rs, errors, i, len, records;
        if (errorReader) {
            rs = errorReader.read(response);
            records = rs.records;
            errors = [];
            if (records) {
                for(i = 0, len = records.length; i < len; i++) {
                    errors[i] = records[i].data;
                }
            }
            if (errors.length < 1) {
                errors = null;
            }
            return {
                success : rs.success,
                errors : errors
            };
        }
        return Ext.decode(response.responseText);
    }
});


Ext.define('Ext.util.ComponentDragger', {

    

    

    

    extend: 'Ext.dd.DragTracker',

    autoStart: 500,

    constructor: function(comp, config) {
        this.comp = comp;
        this.initialConstrainTo = config.constrainTo;
        this.callParent([ config ]);
    },

    onStart: function(e) {
        var me = this,
            comp = me.comp;

        
        this.startPosition = comp.getPosition();

        
        
        if (comp.ghost && !comp.liveDrag) {
             me.proxy = comp.ghost();
             me.dragTarget = me.proxy.header.el;
        }

        
        if (me.constrain || me.constrainDelegate) {
            me.constrainTo = me.calculateConstrainRegion();
        }
    },

    calculateConstrainRegion: function() {
        var me = this,
            comp = me.comp,
            c = me.initialConstrainTo,
            delegateRegion,
            elRegion,
            shadowSize = comp.el.shadow ? comp.el.shadow.offset : 0;

        
        if (!(c instanceof Ext.util.Region)) {
            c =  Ext.fly(c).getViewRegion();
        }

        
        if (shadowSize) {
            c.adjust(0, -shadowSize, -shadowSize, shadowSize);
        }

        
        
        
        if (!me.constrainDelegate) {
            delegateRegion = Ext.fly(me.dragTarget).getRegion();
            elRegion = me.proxy ? me.proxy.el.getRegion() : comp.el.getRegion();

            c.adjust(
                delegateRegion.top - elRegion.top,
                delegateRegion.right - elRegion.right,
                delegateRegion.bottom - elRegion.bottom,
                delegateRegion.left - elRegion.left
            );
        }
        return c;
    },

    
    onDrag: function(e) {
        var me = this,
            comp = (me.proxy && !me.comp.liveDrag) ? me.proxy : me.comp,
            offset = me.getOffset(me.constrain || me.constrainDelegate ? 'dragTarget' : null);

        comp.setPosition(me.startPosition[0] + offset[0], me.startPosition[1] + offset[1]);
    },

    onEnd: function(e) {
        if (this.proxy && !this.comp.liveDrag) {
            this.comp.unghost();
        }
    }
});

Ext.define("Ext.form.Labelable", {
    requires: ['Ext.XTemplate'],

    
    labelableRenderTpl: [
        '<tpl if="!hideLabel && !(!fieldLabel && hideEmptyLabel)">',
            '<label<tpl if="inputId"> for="{inputId}"</tpl> class="{labelCls}"<tpl if="labelStyle"> style="{labelStyle}"</tpl>>',
                '<tpl if="fieldLabel">{fieldLabel}{labelSeparator}</tpl>',
            '</label>',
        '</tpl>',
        '<div class="{baseBodyCls} {fieldBodyCls}"<tpl if="inputId"> id="{baseBodyCls}-{inputId}"</tpl> role="presentation">{subTplMarkup}</div>',
        '<div class="{errorMsgCls}" style="display:none"></div>',
        '<div class="{clearCls}" role="presentation"><!-- --></div>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    
    activeErrorsTpl: [
        '<tpl if="errors && errors.length">',
            '<ul><tpl for="errors"><li<tpl if="xindex == xcount"> class="last"</tpl>>{.}</li></tpl></ul>',
        '</tpl>'
    ],

    
    isFieldLabelable: true,

    
    formItemCls: Ext.baseCSSPrefix + 'form-item',

    
    labelCls: Ext.baseCSSPrefix + 'form-item-label',

    
    errorMsgCls: Ext.baseCSSPrefix + 'form-error-msg',

    
    baseBodyCls: Ext.baseCSSPrefix + 'form-item-body',

    
    fieldBodyCls: '',

    
    clearCls: Ext.baseCSSPrefix + 'clear',

    
    invalidCls : Ext.baseCSSPrefix + 'form-invalid',

    
    fieldLabel: undefined,

    
    labelAlign : 'left',

    
    labelWidth: 100,

    
    labelPad : 5,

    
    labelSeparator : ':',

    

    
    hideLabel: false,

    
    hideEmptyLabel: true,

    
    preventMark: false,

    
    autoFitErrors: true,

    
    msgTarget: 'qtip',

    


    
    initLabelable: function() {
        this.addCls(this.formItemCls);

        this.addEvents(
            
            'errorchange'
        );
    },

    
    getFieldLabel: function() {
        return this.fieldLabel || '';
    },

    
    getLabelableRenderData: function() {
        var me = this,
            labelAlign = me.labelAlign,
            labelPad = me.labelPad,
            labelStyle;

        
        
        if (labelAlign === 'top') {
            labelStyle = 'margin-bottom:' + labelPad + 'px;';
        } else {
            labelStyle = 'margin-right:' + labelPad + 'px;';
            
            if (Ext.isBorderBox) {
                labelStyle += 'width:' + me.labelWidth + 'px;';
            }
        }

        return Ext.copyTo(
            {
                inputId: me.getInputId(),
                fieldLabel: me.getFieldLabel(),
                labelStyle: labelStyle + (me.labelStyle || ''),
                subTplMarkup: me.getSubTplMarkup()
            },
            me,
            'hideLabel,hideEmptyLabel,labelCls,fieldBodyCls,baseBodyCls,errorMsgCls,clearCls,labelSeparator',
            true
        );
    },

    
    getLabelableSelectors: function() {
        return {
            
            labelEl: 'label.' + this.labelCls,

            
            bodyEl: '.' + this.baseBodyCls,

            
            errorEl: '.' + this.errorMsgCls
        };
    },

    
    getSubTplMarkup: function() {
        return '';
    },

    
    getInputId: function() {
        return '';
    },

    
    getActiveError : function() {
        return this.activeError || '';
    },

    
    hasActiveError: function() {
        return !!this.getActiveError();
    },

    
    setActiveError: function(msg) {
        this.activeError = msg;
        this.activeErrors = [msg];
        this.renderActiveError();
    },

    
    getActiveErrors: function() {
        return this.activeErrors || [];
    },

    
    setActiveErrors: function(errors) {
        this.activeErrors = errors;
        this.activeError = this.getTpl('activeErrorsTpl').apply({errors: errors});
        this.renderActiveError();
    },

    
    unsetActiveError: function() {
        delete this.activeError;
        delete this.activeErrors;
        this.renderActiveError();
    },

    
    renderActiveError: function() {
        var me = this,
            activeError = me.getActiveError(),
            hasError = !!activeError;

        if (activeError !== me.lastActiveError) {
            me.fireEvent('errorchange', me, activeError);
            me.lastActiveError = activeError;
        }

        if (me.rendered && !me.isDestroyed && !me.preventMark) {
            
            me.el[hasError ? 'addCls' : 'removeCls'](me.invalidCls);

            
            me.getActionEl().dom.setAttribute('aria-invalid', hasError);

            
            me.errorEl.dom.innerHTML = activeError;
        }
    },

    
    setFieldDefaults: function(defaults) {
        var me = this;
        Ext.iterate(defaults, function(key, val) {
            if (!me.hasOwnProperty(key)) {
                me[key] = val;
            }
        });
    },

    
    getBodyNaturalWidth: function() {
        return this.bodyEl.getWidth();
    }

});


Ext.define('Ext.form.field.Field', {

    
    isFormField : true,

    
    
    

    
    disabled : false,

    
    submitValue: true,

    
    validateOnChange: true,

    
    suspendCheckChange: 0,

    
    initField: function() {
        this.addEvents(
            
            'change',
            
            'validitychange',
            
            'dirtychange'
        );

        this.initValue();
    },

    
    initValue: function() {
        var me = this;

        
        me.originalValue = me.lastValue = me.value;

        
        me.suspendCheckChange++;
        me.setValue(me.value);
        me.suspendCheckChange--;
    },

    
    getName: function() {
        return this.name;
    },

    
    getValue: function() {
        return this.value;
    },
    
    
    setValue: function(value) {
        var me = this;
        me.value = value;
        me.checkChange();
        return me;
    },

    
    isEqual: function(value1, value2) {
        return String(value1) === String(value2);
    },

    
    getSubmitData: function() {
        var me = this,
            data = null;
        if (!me.disabled && me.submitValue && !me.isFileUpload()) {
            data = {};
            data[me.getName()] = '' + me.getValue();
        }
        return data;
    },

    
    getModelData: function() {
        var me = this,
            data = null;
        if (!me.disabled && !me.isFileUpload()) {
            data = {};
            data[me.getName()] = me.getValue();
        }
        return data;
    },

    
    reset : function(){
        var me = this;
        
        me.setValue(me.originalValue);
        me.clearInvalid();
        
        delete me.wasValid;
    },

    
    resetOriginalValue: function() {
        this.originalValue = this.getValue();
        this.checkDirty();
    },

    
    checkChange: function() {
        if (!this.suspendCheckChange) {
            var me = this,
                newVal = me.getValue(),
                oldVal = me.lastValue;
            if (!me.isEqual(newVal, oldVal) && !me.isDestroyed) {
                me.lastValue = newVal;
                me.fireEvent('change', me, newVal, oldVal);
                me.onChange(newVal, oldVal);
            }
        }
    },

    
    onChange: function(newVal, oldVal) {
        if (this.validateOnChange) {
            this.validate();
        }
        this.checkDirty();
    },

    
    isDirty : function() {
        var me = this;
        return !me.disabled && !me.isEqual(me.getValue(), me.originalValue);
    },

    
    checkDirty: function() {
        var me = this,
            isDirty = me.isDirty();
        if (isDirty !== me.wasDirty) {
            me.fireEvent('dirtychange', me, isDirty);
            me.onDirtyChange(isDirty);
            me.wasDirty = isDirty;
        }
    },

    
    onDirtyChange: Ext.emptyFn,

    
    getErrors: function(value) {
        return [];
    },

    
    isValid : function() {
        var me = this;
        return me.disabled || Ext.isEmpty(me.getErrors());
    },

    
    validate : function() {
        var me = this,
            isValid = me.isValid();
        if (isValid !== me.wasValid) {
            me.wasValid = isValid;
            me.fireEvent('validitychange', me, isValid);
        }
        return isValid;
    },

    
    batchChanges: function(fn) {
        this.suspendCheckChange++;
        fn();
        this.suspendCheckChange--;
        this.checkChange();
    },

    
    isFileUpload: function() {
        return false;
    },

    
    extractFileInput: function() {
        return null;
    },

    
    markInvalid: Ext.emptyFn,

    
    clearInvalid: Ext.emptyFn

});


Ext.define('Ext.layout.component.field.Field', {

    

    alias: ['layout.field'],

    extend: 'Ext.layout.component.Component',

    uses: ['Ext.tip.QuickTip', 'Ext.util.TextMetrics'],

    

    type: 'field',

    beforeLayout: function(width, height) {
        var me = this;
        return me.callParent(arguments) || (!me.owner.preventMark && me.activeError !== me.owner.getActiveError());
    },

    onLayout: function(width, height) {
        var me = this,
            owner = me.owner,
            labelStrategy = me.getLabelStrategy(),
            errorStrategy = me.getErrorStrategy(),
            isDefined = Ext.isDefined,
            isNumber = Ext.isNumber,
            lastSize, autoWidth, autoHeight, info, undef;

        lastSize = me.lastComponentSize || {};
        if (!isDefined(width)) {
            width = lastSize.width;
            if (width < 0) { 
                width = undef;
            }
        }
        if (!isDefined(height)) {
            height = lastSize.height;
            if (height < 0) { 
                height = undef;
            }
        }
        autoWidth = !isNumber(width);
        autoHeight = !isNumber(height);

        info = {
            autoWidth: autoWidth,
            autoHeight: autoHeight,
            width: autoWidth ? owner.getBodyNaturalWidth() : width, 
            height: height,
            setOuterWidth: false, 

            
            insets: {
                top: 0,
                right: 0,
                bottom: 0,
                left: 0
            }
        };

        
        
        

        
        labelStrategy.prepare(owner, info);
        errorStrategy.prepare(owner, info);

        
        labelStrategy.adjustHorizInsets(owner, info);
        errorStrategy.adjustHorizInsets(owner, info);

        
        labelStrategy.layoutHoriz(owner, info);
        errorStrategy.layoutHoriz(owner, info);

        
        labelStrategy.adjustVertInsets(owner, info);
        errorStrategy.adjustVertInsets(owner, info);

        
        labelStrategy.layoutVert(owner, info);
        errorStrategy.layoutVert(owner, info);

        
        if (autoWidth && autoHeight) {
            
            me.setElementSize(owner.el, (info.setOuterWidth ? info.width : undef), info.height);
        } else {
            me.setTargetSize((!autoWidth || info.setOuterWidth ? info.width : undef), info.height);
        }
        me.sizeBody(info);

        me.activeError = owner.getActiveError();
    },


    
    sizeBody: function(info) {
        var me = this,
            owner = me.owner,
            insets = info.insets,
            totalWidth = info.width,
            totalHeight = info.height,
            width = Ext.isNumber(totalWidth) ? totalWidth - insets.left - insets.right : totalWidth,
            height = Ext.isNumber(totalHeight) ? totalHeight - insets.top - insets.bottom : totalHeight;

        
        me.setElementSize(owner.bodyEl, width, height);

        
        me.sizeBodyContents(width, height);
    },

    
    sizeBodyContents: Ext.emptyFn,


    
    getLabelStrategy: function() {
        var me = this,
            strategies = me.labelStrategies,
            labelAlign = me.owner.labelAlign;
        return strategies[labelAlign] || strategies.base;
    },

    
    getErrorStrategy: function() {
        var me = this,
            owner = me.owner,
            strategies = me.errorStrategies,
            msgTarget = owner.msgTarget;
        return !owner.preventMark && Ext.isString(msgTarget) ?
                (strategies[msgTarget] || strategies.elementId) :
                strategies.none;
    },



    
    labelStrategies: (function() {
        var applyIf = Ext.applyIf,
            emptyFn = Ext.emptyFn,
            base = {
                prepare: function(owner, info) {
                    var cls = owner.labelCls + '-' + owner.labelAlign,
                        labelEl = owner.labelEl;
                    if (labelEl && !labelEl.hasCls(cls)) {
                        labelEl.addCls(cls);
                    }
                },
                adjustHorizInsets: emptyFn,
                adjustVertInsets: emptyFn,
                layoutHoriz: emptyFn,
                layoutVert: emptyFn
            },
            left = applyIf({
                prepare: function(owner, info) {
                    base.prepare(owner, info);
                    
                    if (info.autoWidth) {
                        info.width += (!owner.labelEl ? 0 : owner.labelWidth + owner.labelPad);
                    }
                    
                    info.setOuterWidth = true;
                },
                adjustHorizInsets: function(owner, info) {
                    if (owner.labelEl) {
                        info.insets.left += owner.labelWidth + owner.labelPad;
                    }
                },
                layoutHoriz: function(owner, info) {
                    
                    
                    
                    
                    
                    var labelEl = owner.labelEl;
                    if (labelEl && !owner.isLabelSized && !Ext.isBorderBox) {
                        labelEl.setWidth(owner.labelWidth);
                        owner.isLabelSized = true;
                    }
                }
            }, base);


        return {
            base: base,

            
            top: applyIf({
                adjustVertInsets: function(owner, info) {
                    var labelEl = owner.labelEl;
                    if (labelEl) {
                        info.insets.top += Ext.util.TextMetrics.measure(labelEl, owner.fieldLabel, info.width).height +
                                           labelEl.getFrameWidth('tb') + owner.labelPad;
                    }
                }
            }, base),

            
            left: left,

            
            right: left
        };
    })(),



    
    errorStrategies: (function() {
        function setDisplayed(el, displayed) {
            var wasDisplayed = el.getStyle('display') !== 'none';
            if (displayed !== wasDisplayed) {
                el.setDisplayed(displayed);
            }
        }

        function setStyle(el, name, value) {
            if (el.getStyle(name) !== value) {
                el.setStyle(name, value);
            }
        }

        var applyIf = Ext.applyIf,
            emptyFn = Ext.emptyFn,
            base = {
                prepare: function(owner) {
                    setDisplayed(owner.errorEl, false);
                },
                adjustHorizInsets: emptyFn,
                adjustVertInsets: emptyFn,
                layoutHoriz: emptyFn,
                layoutVert: emptyFn
            };

        return {
            none: base,

            
            side: applyIf({
                prepare: function(owner) {
                    var errorEl = owner.errorEl;
                    errorEl.addCls(Ext.baseCSSPrefix + 'form-invalid-icon');
                    Ext.layout.component.field.Field.initTip();
                    errorEl.dom.setAttribute('data-errorqtip', owner.getActiveError() || '');
                    setDisplayed(errorEl, owner.hasActiveError());
                },
                adjustHorizInsets: function(owner, info) {
                    if (owner.autoFitErrors && owner.hasActiveError()) {
                        info.insets.right += owner.errorEl.getWidth();
                    }
                },
                layoutHoriz: function(owner, info) {
                    if (owner.hasActiveError()) {
                        setStyle(owner.errorEl, 'left', info.width - info.insets.right + 'px');
                    }
                },
                layoutVert: function(owner, info) {
                    if (owner.hasActiveError()) {
                        setStyle(owner.errorEl, 'top', info.insets.top + 'px');
                    }
                }
            }, base),

            
            under: applyIf({
                prepare: function(owner) {
                    var errorEl = owner.errorEl,
                        cls = Ext.baseCSSPrefix + 'form-invalid-under';
                    if (!errorEl.hasCls(cls)) {
                        errorEl.addCls(cls);
                    }
                    setDisplayed(errorEl, owner.hasActiveError());
                },
                adjustVertInsets: function(owner, info) {
                    if (owner.autoFitErrors) {
                        info.insets.bottom += owner.errorEl.getHeight();
                    }
                },
                layoutHoriz: function(owner, info) {
                    var errorEl = owner.errorEl,
                        insets = info.insets;

                    setStyle(errorEl, 'width', info.width - insets.right - insets.left + 'px');
                    setStyle(errorEl, 'marginLeft', insets.left + 'px');
                }
            }, base),

            
            qtip: applyIf({
                prepare: function(owner) {
                    setDisplayed(owner.errorEl, false);
                    Ext.layout.component.field.Field.initTip();
                    owner.getActionEl().dom.setAttribute('data-errorqtip', owner.getActiveError() || '');
                }
            }, base),

            
            title: applyIf({
                prepare: function(owner) {
                    setDisplayed(owner.errorEl, false);
                    owner.el.dom.title = owner.getActiveError() || '';
                }
            }, base),

            
            elementId: applyIf({
                prepare: function(owner) {
                    setDisplayed(owner.errorEl, false);
                    var targetEl = Ext.fly(owner.msgTarget);
                    if (targetEl) {
                        targetEl.dom.innerHTML = owner.getActiveError() || '';
                        targetEl.setDisplayed(owner.hasActiveError());
                    }
                }
            }, base)
        };
    })(),

    statics: {
        
        initTip: function() {
            var tip = this.tip;
            if (!tip) {
                tip = this.tip = Ext.create('Ext.tip.QuickTip', {
                    baseCls: Ext.baseCSSPrefix + 'form-invalid-tip',
                    renderTo: Ext.getBody()
                });
                tip.tagConfig = Ext.apply({}, {attribute: 'errorqtip'}, tip.tagConfig);
            }
        },

        
        destroyTip: function() {
            var tip = this.tip;
            if (tip) {
                tip.destroy();
                delete this.tip;
            }
        }
    }

});


Ext.define('Ext.form.field.VTypes', (function(){
    
    var alpha = /^[a-zA-Z_]+$/,
        alphanum = /^[a-zA-Z0-9_]+$/,
        email = /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/,
        url = /(((^https?)|(^ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;

    
    return {
        singleton: true,
        alternateClassName: 'Ext.form.VTypes',

        
        'email' : function(v){
            return email.test(v);
        },
        
        'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
        
        'emailMask' : /[a-z0-9_\.\-@\+]/i,

        
        'url' : function(v){
            return url.test(v);
        },
        
        'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',

        
        'alpha' : function(v){
            return alpha.test(v);
        },
        
        'alphaText' : 'This field should only contain letters and _',
        
        'alphaMask' : /[a-z_]/i,

        
        'alphanum' : function(v){
            return alphanum.test(v);
        },
        
        'alphanumText' : 'This field should only contain letters, numbers and _',
        
        'alphanumMask' : /[a-z0-9_]/i
    };
})());


Ext.define('Ext.layout.component.field.Text', {
    extend: 'Ext.layout.component.field.Field',
    alias: 'layout.textfield',
    requires: ['Ext.util.TextMetrics'],

    type: 'textfield',


    
    beforeLayout: function(width, height) {
        var me = this,
            owner = me.owner,
            lastValue = this.lastValue,
            value = owner.getRawValue();
        this.lastValue = value;
        return me.callParent(arguments) || (owner.grow && value !== lastValue);
    },


    
    sizeBodyContents: function(width, height) {
        var size = this.adjustForGrow(width, height);
        this.setElementSize(this.owner.inputEl, size[0], size[1]);
    },


    
    adjustForGrow: function(width, height) {
        var me = this,
            owner = me.owner,
            inputEl, value, calcWidth,
            result = [width, height];

        if (owner.grow) {
            inputEl = owner.inputEl;

            
            value = (inputEl.dom.value || (owner.hasFocus ? '' : owner.emptyText) || '') + owner.growAppend;
            calcWidth = inputEl.getTextWidth(value) + inputEl.getBorderWidth("lr") + inputEl.getPadding("lr");

            
            result[0] = Ext.Number.constrain(calcWidth, owner.growMin,
                    Math.max(owner.growMin, Math.min(owner.growMax, Ext.isNumber(width) ? width : Infinity)));
        }

        return result;
    }

});


Ext.define('Ext.layout.component.field.TextArea', {
    extend: 'Ext.layout.component.field.Text',
    alias: 'layout.textareafield',

    type: 'textareafield',


    
    adjustForGrow: function(width, height) {
        var me = this,
            owner = me.owner,
            inputEl, value, max,
            curWidth, curHeight, calcHeight,
            result = [width, height];

        if (owner.grow) {
            inputEl = owner.inputEl;
            curWidth = inputEl.getWidth(true); 
            curHeight = inputEl.getHeight();

            
            value = inputEl.dom.value || '&#160;';
            value += owner.growAppend;

            
            value = value.replace(/\n/g, '<br>');

            
            calcHeight = Ext.util.TextMetrics.measure(inputEl, value, curWidth).height +
                         inputEl.getBorderWidth("tb") + inputEl.getPadding("tb");

            
            max = owner.growMax;
            if (Ext.isNumber(height)) {
                max = Math.min(max, height);
            }
            result[1] = Ext.Number.constrain(calcHeight, owner.growMin, max);
        }

        return result;
    }

});


Ext.define('Ext.layout.container.Anchor', {

    

    alias: 'layout.anchor',
    extend: 'Ext.layout.container.Container',
    alternateClassName: 'Ext.layout.AnchorLayout',

    

    

    type: 'anchor',

    
    defaultAnchor: '100%',

    parseAnchorRE: /^(r|right|b|bottom)$/i,

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

        var me = this,
            size = me.getLayoutTargetSize(),
            owner = me.owner,
            target = me.getTarget(),
            ownerWidth = size.width,
            ownerHeight = size.height,
            overflow = target.getStyle('overflow'),
            components = me.getVisibleItems(owner),
            len = components.length,
            boxes = [],
            box, newTargetSize, anchorWidth, anchorHeight, component, anchorSpec, calcWidth, calcHeight,
            anchorsArray, anchor, i, el, cleaner;

        if (ownerWidth < 20 && ownerHeight < 20) {
            return;
        }

        
        
        
        if (!me.clearEl) {
            me.clearEl = target.createChild({
                cls: Ext.baseCSSPrefix + 'clear',
                role: 'presentation'
            });
        }

        
        if (owner.anchorSize) {
            if (typeof owner.anchorSize == 'number') {
                anchorWidth = owner.anchorSize;
            }
            else {
                anchorWidth = owner.anchorSize.width;
                anchorHeight = owner.anchorSize.height;
            }
        }
        else {
            anchorWidth = owner.initialConfig.width;
            anchorHeight = owner.initialConfig.height;
        }

        
        if (!Ext.supports.RightMargin) {
            cleaner = Ext.core.Element.getRightMarginFixCleaner(target);
            target.addCls(Ext.baseCSSPrefix + 'inline-children');
        }

        for (i = 0; i < len; i++) {
            component = components[i];
            el = component.el;
            anchor = component.anchor;

            if (!component.anchor && component.items && !Ext.isNumber(component.width) && !(Ext.isIE6 && Ext.isStrict)) {
                component.anchor = anchor = me.defaultAnchor;
            }

            if (anchor) {
                anchorSpec = component.anchorSpec;
                
                if (!anchorSpec) {
                    anchorsArray = anchor.split(' ');
                    component.anchorSpec = anchorSpec = {
                        right: me.parseAnchor(anchorsArray[0], component.initialConfig.width, anchorWidth),
                        bottom: me.parseAnchor(anchorsArray[1], component.initialConfig.height, anchorHeight)
                    };
                }
                calcWidth = anchorSpec.right ? me.adjustWidthAnchor(anchorSpec.right(ownerWidth) - el.getMargin('lr'), component) : undefined;
                calcHeight = anchorSpec.bottom ? me.adjustHeightAnchor(anchorSpec.bottom(ownerHeight) - el.getMargin('tb'), component) : undefined;

                boxes.push({
                    component: component,
                    anchor: true,
                    width: calcWidth || undefined,
                    height: calcHeight || undefined
                });
            } else {
                boxes.push({
                    component: component,
                    anchor: false
                });
            }
        }

        
        if (!Ext.supports.RightMargin) {
            target.removeCls(Ext.baseCSSPrefix + 'inline-children');
            cleaner();
        }

        for (i = 0; i < len; i++) {
            box = boxes[i];
            me.setItemSize(box.component, box.width, box.height);
        }

        if (overflow && overflow != 'hidden' && !me.adjustmentPass) {
            newTargetSize = me.getLayoutTargetSize();
            if (newTargetSize.width != size.width || newTargetSize.height != size.height) {
                me.adjustmentPass = true;
                me.onLayout();
            }
        }

        delete me.adjustmentPass;
    },

    
    parseAnchor: function(a, start, cstart) {
        if (a && a != 'none') {
            var ratio;
            
            if (this.parseAnchorRE.test(a)) {
                var diff = cstart - start;
                return function(v) {
                    return v - diff;
                };
            }    
            
            else if (a.indexOf('%') != -1) {
                ratio = parseFloat(a.replace('%', '')) * 0.01;
                return function(v) {
                    return Math.floor(v * ratio);
                };
            }    
            
            else {
                a = parseInt(a, 10);
                if (!isNaN(a)) {
                    return function(v) {
                        return v + a;
                    };
                }
            }
        }
        return null;
    },

    
    adjustWidthAnchor: function(value, comp) {
        return value;
    },

    
    adjustHeightAnchor: function(value, comp) {
        return value;
    }

});

Ext.define('Ext.form.action.Load', {
    extend:'Ext.form.action.Action',
    requires: ['Ext.data.Connection'],
    alternateClassName: 'Ext.form.Action.Load',
    alias: 'formaction.load',

    type: 'load',

    
    run: function() {
        Ext.Ajax.request(Ext.apply(
            this.createCallback(),
            {
                method: this.getMethod(),
                url: this.getUrl(),
                headers: this.headers,
                params: this.getParams()
            }
        ));
    },

    
    onSuccess: function(response){
        var result = this.processResponse(response),
            form = this.form;
        if (result === true || !result.success || !result.data) {
            this.failureType = Ext.form.action.Action.LOAD_FAILURE;
            form.afterAction(this, false);
            return;
        }
        form.clearInvalid();
        form.setValues(result.data);
        form.afterAction(this, true);
    },

    
    handleResponse: function(response) {
        var reader = this.form.reader,
            rs, data;
        if (reader) {
            rs = reader.read(response);
            data = rs.records && rs.records[0] ? rs.records[0].data : null;
            return {
                success : rs.success,
                data : data
            };
        }
        return Ext.decode(response.responseText);
    }
});



Ext.define('Ext.window.Window', {
    extend: 'Ext.panel.Panel',

    alternateClassName: 'Ext.Window',

    requires: ['Ext.util.ComponentDragger', 'Ext.util.Region', 'Ext.EventManager'],

    alias: 'widget.window',

    
    
    
    
    
    
    
    

    
    baseCls: Ext.baseCSSPrefix + 'window',

    
    resizable: true,

    
    draggable: true,

    
    constrain: false,

    
    constrainHeader: false,

    
    plain: false,

    
    minimizable: false,

    
    maximizable: false,

    
    minHeight: 100,

    
    minWidth: 200,

    
    expandOnShow: true,

    
    collapsible: false,

    
    closable: true,

    
    hidden: true,

    
    autoRender: true,

    
    hideMode: 'visibility',

    
    floating: true,

    ariaRole: 'alertdialog',
    
    itemCls: 'x-window-item',

    overlapHeader: true,
    
    ignoreHeaderBorderManagement: true,

    
    initComponent: function() {
        var me = this;
        me.callParent();
        me.addEvents(
            
            
            
            'resize',
            
            'maximize',
            
            'minimize',
            
            'restore'
        );

        if (me.plain) {
            me.addClsWithUI('plain');
        }

        if (me.modal) {
            me.ariaRole = 'dialog';
        }
    },

    
    

    initStateEvents: function(){
        var events = this.stateEvents;
        
        Ext.each(['maximize', 'restore', 'resize', 'dragend'], function(event){
            if (Ext.Array.indexOf(events, event)) {
                events.push(event);
            }
        });
        this.callParent();
    },

    getState: function() {
        var me = this,
            state = me.callParent() || {},
            maximized = !!me.maximized;

        state.maximized = maximized;
        Ext.apply(state, {
            size: maximized ? me.restoreSize : me.getSize(),
            pos: maximized ? me.restorePos : me.getPosition()
        });
        return state;
    },

    applyState: function(state){
        var me = this;

        if (state) {
            me.maximized = state.maximized;
            if (me.maximized) {
                me.hasSavedRestore = true;
                me.restoreSize = state.size;
                me.restorePos = state.pos;
            } else {
                Ext.apply(me, {
                    width: state.size.width,
                    height: state.size.height,
                    x: state.pos[0],
                    y: state.pos[1]
                });
            }
        }
    },

    
    onMouseDown: function () {
        if (this.floating) {
            this.toFront();
        }
    },

    
    onRender: function(ct, position) {
        var me = this;
        me.callParent(arguments);
        me.focusEl = me.el;

        
        if (me.maximizable) {
            me.header.on({
                dblclick: {
                    fn: me.toggleMaximize,
                    element: 'el',
                    scope: me
                }
            });
        }
    },

    
    afterRender: function() {
        var me = this,
            hidden = me.hidden,
            keyMap;

        me.hidden = false;
        
        me.callParent();
        me.hidden = hidden;

        
        me.proxy = me.getProxy();

        
        me.mon(me.el, 'mousedown', me.onMouseDown, me);

        
        if (me.maximized) {
            me.maximized = false;
            me.maximize();
        }

        if (me.closable) {
            keyMap = me.getKeyMap();
            keyMap.on(27, me.onEsc, me);

            
                keyMap.disable();
            
        }

        if (!hidden) {
            me.syncMonitorWindowResize();
            me.doConstrain();
        }
    },

    
    initDraggable: function() {
        var me = this,
            ddConfig;

        if (!me.header) {
            me.updateHeader(true);
        }
        
        
        if (me.header) {
            ddConfig = Ext.applyIf({
                el: me.el,
                delegate: '#' + me.header.id
            }, me.draggable);

            
            if (me.constrain || me.constrainHeader) {
                ddConfig.constrain = me.constrain;
                ddConfig.constrainDelegate = me.constrainHeader;
                ddConfig.constrainTo = me.constrainTo || me.container;
            }

            
            me.dd = Ext.create('Ext.util.ComponentDragger', this, ddConfig);
            me.relayEvents(me.dd, ['dragstart', 'drag', 'dragend']);
        }
    },

    
    onEsc: function(k, e) {
        e.stopEvent();
        this[this.closeAction]();
    },

    
    beforeDestroy: function() {
        var me = this;
        if (me.rendered) {
            delete this.animateTarget;
            me.hide();
            Ext.destroy(
                me.keyMap
            );
        }
        me.callParent();
    },

    
    addTools: function() {
        var me = this;

        
        me.callParent();

        if (me.minimizable) {
            me.addTool({
                type: 'minimize',
                handler: Ext.Function.bind(me.minimize, me, [])
            });
        }
        if (me.maximizable) {
            me.addTool({
                type: 'maximize',
                handler: Ext.Function.bind(me.maximize, me, [])
            });
            me.addTool({
                type: 'restore',
                handler: Ext.Function.bind(me.restore, me, []),
                hidden: true
            });
        }
    },

    
    getFocusEl: function() {
        var me = this,
            f = me.focusEl,
            defaultComp = me.defaultButton || me.defaultFocus,
            t = typeof db,
            el,
            ct;

        if (Ext.isDefined(defaultComp)) {
            if (Ext.isNumber(defaultComp)) {
                f = me.query('button')[defaultComp];
            } else if (Ext.isString(defaultComp)) {
                f = me.down('#' + defaultComp);
            } else {
                f = defaultComp;
            }
        }
        return f || me.focusEl;
    },

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

        if (this.expandOnShow) {
            this.expand(false);
        }
    },

    
    afterShow: function(animateTarget) {
        var me = this;

        
        
        me.callParent(arguments);

        if (me.maximized) {
            me.fitContainer();
        }

        me.syncMonitorWindowResize();
        me.doConstrain();

        if (me.keyMap) {
            me.keyMap.enable();
        }
    },

    
    doClose: function() {
        var me = this;

        
        if (me.hidden) {
            me.fireEvent('close', me);
            me[me.closeAction]();
        } else {
            
            me.hide(me.animTarget, me.doClose, me);
        }
    },

    
    afterHide: function() {
        var me = this;

        
        me.syncMonitorWindowResize();

        
        if (me.keyMap) {
            me.keyMap.disable();
        }

        
        me.callParent(arguments);
    },

    
    onWindowResize: function() {
        if (this.maximized) {
            this.fitContainer();
        }
        this.doConstrain();
    },

    
    minimize: function() {
        this.fireEvent('minimize', this);
        return this;
    },

    afterCollapse: function() {
        var me = this;

        if (me.maximizable) {
            me.tools.maximize.hide();
            me.tools.restore.hide();
        }
        if (me.resizer) {
            me.resizer.disable();
        }
        me.callParent(arguments);
    },

    afterExpand: function() {
        var me = this;

        if (me.maximized) {
            me.tools.restore.show();
        } else if (me.maximizable) {
            me.tools.maximize.show();
        }
        if (me.resizer) {
            me.resizer.enable();
        }
        me.callParent(arguments);
    },

    
    maximize: function() {
        var me = this;

        if (!me.maximized) {
            me.expand(false);
            if (!me.hasSavedRestore) {
                me.restoreSize = me.getSize();
                me.restorePos = me.getPosition(true);
            }
            if (me.maximizable) {
                me.tools.maximize.hide();
                me.tools.restore.show();
            }
            me.maximized = true;
            me.el.disableShadow();

            if (me.dd) {
                me.dd.disable();
            }
            if (me.collapseTool) {
                me.collapseTool.hide();
            }
            me.el.addCls(Ext.baseCSSPrefix + 'window-maximized');
            me.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct');

            me.syncMonitorWindowResize();
            me.setPosition(0, 0);
            me.fitContainer();
            me.fireEvent('maximize', me);
        }
        return me;
    },

    
    restore: function() {
        var me = this,
            tools = me.tools;

        if (me.maximized) {
            delete me.hasSavedRestore;
            me.removeCls(Ext.baseCSSPrefix + 'window-maximized');

            
            if (tools.restore) {
                tools.restore.hide();
            }
            if (tools.maximize) {
                tools.maximize.show();
            }
            if (me.collapseTool) {
                me.collapseTool.show();
            }

            
            me.setPosition(me.restorePos);
            me.setSize(me.restoreSize);

            
            delete me.restorePos;
            delete me.restoreSize;

            me.maximized = false;

            me.el.enableShadow(true);

            
            if (me.dd) {
                me.dd.enable();
            }

            me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct');

            me.syncMonitorWindowResize();
            me.doConstrain();
            me.fireEvent('restore', me);
        }
        return me;
    },

    
    syncMonitorWindowResize: function () {
        var me = this,
            currentlyMonitoring = me._monitoringResize,
            
            yes = me.monitorResize || me.constrain || me.constrainHeader || me.maximized,
            
            veto = me.hidden || me.destroying || me.isDestroyed;

        if (yes && !veto) {
            
            if (!currentlyMonitoring) {
                
                Ext.EventManager.onWindowResize(me.onWindowResize, me);
                me._monitoringResize = true;
            }
        } else if (currentlyMonitoring) {
            
            Ext.EventManager.removeResizeListener(me.onWindowResize, me);
            me._monitoringResize = false;
        }
    },

    
    toggleMaximize: function() {
        return this[this.maximized ? 'restore': 'maximize']();
    }

    
});

Ext.define('Ext.form.field.Base', {
    extend: 'Ext.Component',
    mixins: {
        labelable: 'Ext.form.Labelable',
        field: 'Ext.form.field.Field'
    },
    alias: 'widget.field',
    alternateClassName: ['Ext.form.Field', 'Ext.form.BaseField'],
    requires: ['Ext.util.DelayedTask', 'Ext.XTemplate', 'Ext.layout.component.field.Field'],

    fieldSubTpl: [
        '<input id="{id}" type="{type}" ',
        '<tpl if="name">name="{name}" </tpl>',
        '<tpl if="size">size="{size}" </tpl>',
        '<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>',
        'class="{fieldCls} {typeCls}" autocomplete="off" />',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    

    
    inputType: 'text',

    

    
    invalidText : 'The value in this field is invalid',

    
    fieldCls : Ext.baseCSSPrefix + 'form-field',

    

    
    focusCls : Ext.baseCSSPrefix + 'form-focus',

    
    dirtyCls : Ext.baseCSSPrefix + 'form-dirty',

    
    checkChangeEvents: Ext.isIE && (!document.documentMode || document.documentMode < 9) ?
                        ['change', 'propertychange'] :
                        ['change', 'input', 'textInput', 'keyup', 'dragdrop'],

    
    checkChangeBuffer: 50,

    componentLayout: 'field',

    
    readOnly : false,

    
    readOnlyCls: Ext.baseCSSPrefix + 'form-readonly',

    

    
    validateOnBlur: true,

    
    hasFocus : false,
    
    baseCls: Ext.baseCSSPrefix + 'field',
    
    maskOnDisable: false,

    
    initComponent : function() {
        var me = this;

        me.callParent();

        me.subTplData = me.subTplData || {};

        me.addEvents(
            
            'focus',
            
            'blur',
            
            'specialkey'
        );

        
        me.initLabelable();
        me.initField();

        
        if (!me.name) {
            me.name = me.getInputId();
        }
    },

    
    getInputId: function() {
        return this.inputId || (this.inputId = Ext.id());
    },

    
    getSubTplData: function() {
        var me = this,
            type = me.inputType,
            inputId = me.getInputId();

        return Ext.applyIf(me.subTplData, {
            id: inputId,
            name: me.name || inputId,
            type: type,
            size: me.size || 20,
            cls: me.cls,
            fieldCls: me.fieldCls,
            tabIdx: me.tabIndex,
            typeCls: Ext.baseCSSPrefix + 'form-' + (type === 'password' ? 'text' : type)
        });
    },

    
    getSubTplMarkup: function() {
        return this.getTpl('fieldSubTpl').apply(this.getSubTplData());
    },

    initRenderTpl: function() {
        var me = this;
        if (!me.hasOwnProperty('renderTpl')) {
            me.renderTpl = me.getTpl('labelableRenderTpl');
        }
        return me.callParent();
    },

    initRenderData: function() {
        return Ext.applyIf(this.callParent(), this.getLabelableRenderData());
    },

    
    setFieldStyle: function(style) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            inputEl.applyStyles(style);
        }
        me.fieldStyle = style;
    },

    
    onRender : function() {
        var me = this,
            fieldStyle = me.fieldStyle,
            renderSelectors = me.renderSelectors;

        Ext.applyIf(renderSelectors, me.getLabelableSelectors());

        Ext.applyIf(renderSelectors, {
            
            inputEl: '.' + me.fieldCls
        });

        me.callParent(arguments);

        
        me.setRawValue(me.rawValue);

        if (me.readOnly) {
            me.setReadOnly(true);
        }
        if (me.disabled) {
            me.disable();
        }
        if (fieldStyle) {
            me.setFieldStyle(fieldStyle);
        }

        me.renderActiveError();
    },

    initAria: function() {
        var me = this;
        me.callParent();

        
        me.getActionEl().dom.setAttribute('aria-describedby', Ext.id(me.errorEl));
    },

    getFocusEl: function() {
        return this.inputEl;
    },

    isFileUpload: function() {
        return this.inputType === 'file';
    },

    extractFileInput: function() {
        var me = this,
            fileInput = me.isFileUpload() ? me.inputEl.dom : null,
            clone;
        if (fileInput) {
            clone = fileInput.cloneNode(true);
            fileInput.parentNode.replaceChild(clone, fileInput);
            me.inputEl = Ext.get(clone);
        }
        return fileInput;
    },

    
    getSubmitData: function() {
        var me = this,
            data = null,
            val;
        if (!me.disabled && me.submitValue && !me.isFileUpload()) {
            val = me.getSubmitValue();
            if (val !== null) {
                data = {};
                data[me.getName()] = val;
            }
        }
        return data;
    },

    
    getSubmitValue: function() {
        return this.processRawValue(this.getRawValue());
    },

    
    getRawValue: function() {
        var me = this,
            v = (me.inputEl ? me.inputEl.getValue() : Ext.value(me.rawValue, ''));
        me.rawValue = v;
        return v;
    },

    
    setRawValue: function(value) {
        var me = this;
        value = Ext.value(value, '');
        me.rawValue = value;

        
        if (me.inputEl) {
            me.inputEl.dom.value = value;
        }
        return value;
    },

    
    valueToRaw: function(value) {
        return '' + Ext.value(value, '');
    },

    
    rawToValue: function(rawValue) {
        return rawValue;
    },

    
    processRawValue: function(value) {
        return value;
    },

    
    getValue: function() {
        var me = this,
            val = me.rawToValue(me.processRawValue(me.getRawValue()));
        me.value = val;
        return val;
    },

    
    setValue: function(value) {
        var me = this;
        me.setRawValue(me.valueToRaw(value));
        return me.mixins.field.setValue.call(me, value);
    },


    
    onDisable: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent();
        if (inputEl) {
            inputEl.dom.disabled = true;
        }
    },

    
    onEnable: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent();
        if (inputEl) {
            inputEl.dom.disabled = false;
        }
    },

    
    setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            inputEl.dom.readOnly = readOnly;
            inputEl.dom.setAttribute('aria-readonly', readOnly);
        }
        me[readOnly ? 'addCls' : 'removeCls'](me.readOnlyCls);
        me.readOnly = readOnly;
    },

    
    fireKey: function(e){
        if(e.isSpecialKey()){
            this.fireEvent('specialkey', this, Ext.create('Ext.EventObjectImpl', e));
        }
    },

    
    initEvents : function(){
        var me = this,
            inputEl = me.inputEl,
            onChangeTask,
            onChangeEvent;
        if (inputEl) {
            me.mon(inputEl, Ext.EventManager.getKeyEvent(), me.fireKey,  me);
            me.mon(inputEl, 'focus', me.onFocus, me);

            
            
            me.mon(inputEl, 'blur', me.onBlur, me, me.inEditor ? {buffer:10} : null);

            
            onChangeTask = Ext.create('Ext.util.DelayedTask', me.checkChange, me);
            me.onChangeEvent = onChangeEvent = function() {
                onChangeTask.delay(me.checkChangeBuffer);
            };
            Ext.each(me.checkChangeEvents, function(eventName) {
                if (eventName === 'propertychange') {
                    me.usesPropertychange = true;
                }
                me.mon(inputEl, eventName, onChangeEvent);
            }, me);
        }
        me.callParent();
    },

    doComponentLayout: function() {
        var me = this,
            inputEl = me.inputEl,
            usesPropertychange = me.usesPropertychange,
            ename = 'propertychange',
            onChangeEvent = me.onChangeEvent;

        
        
        
        if (usesPropertychange) {
            me.mun(inputEl, ename, onChangeEvent);
        }
        me.callParent(arguments);
        if (usesPropertychange) {
            me.mon(inputEl, ename, onChangeEvent);
        }
    },

    
    preFocus: Ext.emptyFn,

    
    onFocus: function() {
        var me = this,
            focusCls = me.focusCls,
            inputEl = me.inputEl;
        me.preFocus();
        if (focusCls && inputEl) {
            inputEl.addCls(focusCls);
        }
        if (!me.hasFocus) {
            me.hasFocus = true;
            me.fireEvent('focus', me);
        }
    },

    
    beforeBlur : Ext.emptyFn,

    
    onBlur : function(){
        var me = this,
            focusCls = me.focusCls,
            inputEl = me.inputEl;
        me.beforeBlur();
        if (focusCls && inputEl) {
            inputEl.removeCls(focusCls);
        }
        if (me.validateOnBlur) {
            me.validate();
        }
        me.hasFocus = false;
        me.fireEvent('blur', me);
        me.postBlur();
    },

    
    postBlur : Ext.emptyFn,


    
    onDirtyChange: function(isDirty) {
        this[isDirty ? 'addCls' : 'removeCls'](this.dirtyCls);
    },


    
    isValid : function() {
        var me = this;
        return me.disabled || me.validateValue(me.processRawValue(me.getRawValue()));
    },


    
    validateValue: function(value) {
        var me = this,
            errors = me.getErrors(value),
            isValid = Ext.isEmpty(errors);
        if (!me.preventMark) {
            if (isValid) {
                me.clearInvalid();
            } else {
                me.markInvalid(errors);
            }
        }

        return isValid;
    },

    
    markInvalid : function(errors) {
        
        var me = this,
            oldMsg = me.getActiveError();
        me.setActiveErrors(Ext.Array.from(errors));
        if (oldMsg !== me.getActiveError()) {
            me.doComponentLayout();
        }
    },

    
    clearInvalid : function() {
        
        var me = this,
            hadError = me.hasActiveError();
        me.unsetActiveError();
        if (hadError) {
            me.doComponentLayout();
        }
    },

    
    renderActiveError: function() {
        var me = this,
            hasError = me.hasActiveError();
        if (me.inputEl) {
            
            me.inputEl[hasError ? 'addCls' : 'removeCls'](me.invalidCls + '-field');
        }
        me.mixins.labelable.renderActiveError.call(me);
    },


    getActionEl: function() {
        return this.inputEl || this.el;
    }

});


Ext.define('Ext.form.field.Text', {
    extend:'Ext.form.field.Base',
    alias: 'widget.textfield',
    requires: ['Ext.form.field.VTypes', 'Ext.layout.component.field.Text'],
    alternateClassName: ['Ext.form.TextField', 'Ext.form.Text'],

    
    
    

    
    size: 20,

    

    
    growMin : 30,
    
    
    growMax : 800,

    
    growAppend: 'W',
    
    

    

    

    
    allowBlank : true,
    
    
    minLength : 0,
    
    
    maxLength : Number.MAX_VALUE,
    
    

    
    minLengthText : 'The minimum length for this field is {0}',
    
    
    maxLengthText : 'The maximum length for this field is {0}',
    
    
    
    
    blankText : 'This field is required',
    
    

    

    
    regexText : '',
    
    

    
    emptyCls : Ext.baseCSSPrefix + 'form-empty-field',

    ariaRole: 'textbox',

    

    componentLayout: 'textfield',

    initComponent : function(){
        this.callParent();
        this.addEvents(
            
            'autosize',

            
            'keydown',
            
            'keyup',
            
            'keypress'
        );
    },

    
    initEvents : function(){
        var me = this,
            el = me.inputEl;
        
        me.callParent();
        if(me.selectOnFocus || me.emptyText){
            me.mon(el, 'mousedown', me.onMouseDown, me);
        }
        if(me.maskRe || (me.vtype && me.disableKeyFilter !== true && (me.maskRe = Ext.form.field.VTypes[me.vtype+'Mask']))){
            me.mon(el, 'keypress', me.filterKeys, me);
        }

        if (me.enableKeyEvents) {
            me.mon(el, {
                scope: me,
                keyup: me.onKeyUp,
                keydown: me.onKeyDown,
                keypress: me.onKeyPress
            });
        }
    },

    
    isEqual: function(value1, value2) {
        return String(Ext.value(value1, '')) === String(Ext.value(value2, ''));
    },

    
    onChange: function() {
        this.callParent();
        this.autoSize();
    },
    
    afterRender: function(){
        var me = this;
        if (me.enforceMaxLength) {
            me.inputEl.dom.maxLength = me.maxLength;
        }
        me.applyEmptyText();
        me.autoSize();
        me.callParent();
    },

    onMouseDown: function(e){
        var me = this;
        if(!me.hasFocus){
            me.mon(me.inputEl, 'mouseup', Ext.emptyFn, me, { single: true, preventDefault: true });
        }
    },

    
    processRawValue: function(value) {
        var me = this,
            stripRe = me.stripCharsRe,
            newValue;
            
        if (stripRe) {
            newValue = value.replace(stripRe, '');
            if (newValue !== value) {
                me.setRawValue(newValue);
                value = newValue;
            }
        }
        return value;
    },

    
    onDisable: function(){
        this.callParent();
        if (Ext.isIE) {
            this.inputEl.dom.unselectable = 'on';
        }
    },

    
    onEnable: function(){
        this.callParent();
        if (Ext.isIE) {
            this.inputEl.dom.unselectable = '';
        }
    },

    onKeyDown: function(e) {
        this.fireEvent('keydown', this, e);
    },

    onKeyUp: function(e) {
        this.fireEvent('keyup', this, e);
    },

    onKeyPress: function(e) {
        this.fireEvent('keypress', this, e);
    },

    
    reset : function(){
        this.callParent();
        this.applyEmptyText();
    },

    applyEmptyText : function(){
        var me = this,
            emptyText = me.emptyText,
            isEmpty;

        if (me.rendered && emptyText) {
            isEmpty = me.getRawValue().length < 1 && !me.hasFocus;
            
            if (Ext.supports.Placeholder) {
                me.inputEl.dom.placeholder = emptyText;
            } else if (isEmpty) {
                me.setRawValue(emptyText);
            }
            
            
            
            if (isEmpty) {
                me.inputEl.addCls(me.emptyCls);
            }

            me.autoSize();
        }
    },

    
    preFocus : function(){
        var me = this,
            inputEl = me.inputEl,
            emptyText = me.emptyText,
            isEmpty;

        if (emptyText && !Ext.supports.Placeholder && inputEl.dom.value === emptyText) {
            me.setRawValue('');
            isEmpty = true;
            inputEl.removeCls(me.emptyCls);
        } else if (Ext.supports.Placeholder) {
            me.inputEl.removeCls(me.emptyCls);
        }
        if (me.selectOnFocus || isEmpty) {
            inputEl.dom.select();
        }
    },

    onFocus: function() {
        var me = this;
        me.callParent(arguments);
        if (me.emptyText) {
            me.autoSize();
        }
    },

    
    postBlur : function(){
        this.applyEmptyText();
    },

    
    filterKeys : function(e){
        if(e.ctrlKey){
            return;
        }
        var key = e.getKey(),
            charCode = String.fromCharCode(e.getCharCode());
            
        if(Ext.isGecko && (e.isNavKeyPress() || key === e.BACKSPACE || (key === e.DELETE && e.button === -1))){
            return;
        }
        
        if(!Ext.isGecko && e.isSpecialKey() && !charCode){
            return;
        }
        if(!this.maskRe.test(charCode)){
            e.stopEvent();
        }
    },

    
    getRawValue: function() {
        var me = this,
            v = me.callParent();
        if (v === me.emptyText) {
            v = '';
        }
        return v;
    },

    
    setValue: function(value) {
        var me = this,
            inputEl = me.inputEl;
        
        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyCls);
        }
        
        me.callParent(arguments);

        me.applyEmptyText();
        return me;
    },

    
    getErrors: function(value) {
        var me = this,
            errors = me.callParent(arguments),
            validator = me.validator,
            emptyText = me.emptyText,
            allowBlank = me.allowBlank,
            vtype = me.vtype,
            vtypes = Ext.form.field.VTypes,
            regex = me.regex,
            format = Ext.String.format,
            msg;

        value = value || me.processRawValue(me.getRawValue());

        if (Ext.isFunction(validator)) {
            msg = validator.call(me, value);
            if (msg !== true) {
                errors.push(msg);
            }
        }

        if (value.length < 1 || value === emptyText) {
            if (!allowBlank) {
                errors.push(me.blankText);
            }
            
            return errors;
        }

        if (value.length < me.minLength) {
            errors.push(format(me.minLengthText, me.minLength));
        }

        if (value.length > me.maxLength) {
            errors.push(format(me.maxLengthText, me.maxLength));
        }

        if (vtype) {
            if(!vtypes[vtype](value, me)){
                errors.push(me.vtypeText || vtypes[vtype +'Text']);
            }
        }

        if (regex && !regex.test(value)) {
            errors.push(me.regexText || me.invalidText);
        }

        return errors;
    },

    
    selectText : function(start, end){
        var me = this,
            v = me.getRawValue(),
            doFocus = true,
            el = me.inputEl.dom,
            undef,
            range;
            
        if (v.length > 0) {
            start = start === undef ? 0 : start;
            end = end === undef ? v.length : end;
            if (el.setSelectionRange) {
                el.setSelectionRange(start, end);
            }
            else if(el.createTextRange) {
                range = el.createTextRange();
                range.moveStart('character', start);
                range.moveEnd('character', end - v.length);
                range.select();
            }
            doFocus = Ext.isGecko || Ext.isOpera;
        }
        if (doFocus) {
            me.focus();
        }
    },

    
    autoSize: function() {
        var me = this,
            width;
        if (me.grow && me.rendered) {
            me.doComponentLayout();
            width = me.inputEl.getWidth();
            if (width !== me.lastInputWidth) {
                me.fireEvent('autosize', width);
                me.lastInputWidth = width;
            }
        }
    },

    initAria: function() {
        this.callParent();
        this.getActionEl().dom.setAttribute('aria-required', this.allowBlank === false);
    },

    
    getBodyNaturalWidth: function() {
        return Math.round(this.size * 6.5) + 20;
    }

});


Ext.define('Ext.form.field.TextArea', {
    extend:'Ext.form.field.Text',
    alias: ['widget.textareafield', 'widget.textarea'],
    alternateClassName: 'Ext.form.TextArea',
    requires: ['Ext.XTemplate', 'Ext.layout.component.field.TextArea'],

    fieldSubTpl: [
        '<textarea id="{id}" ',
            '<tpl if="name">name="{name}" </tpl>',
            '<tpl if="rows">rows="{rows}" </tpl>',
            '<tpl if="cols">cols="{cols}" </tpl>',
            '<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>',
            'class="{fieldCls} {typeCls}" ',
            'autocomplete="off">',
        '</textarea>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    
    growMin: 60,

    
    growMax: 1000,

    
    growAppend: '\n-',

    
    cols: 20,

    
    rows: 4,

    
    enterIsSpecial: false,

    
    preventScrollbars: false,

    
    componentLayout: 'textareafield',

    
    onRender: function(ct, position) {
        var me = this;
        Ext.applyIf(me.subTplData, {
            cols: me.cols,
            rows: me.rows
        });

        me.callParent(arguments);
    },

    
    afterRender: function(){
        var me = this;

        me.callParent(arguments);

        if (me.grow) {
            if (me.preventScrollbars) {
                me.inputEl.setStyle('overflow', 'hidden');
            }
            me.inputEl.setHeight(me.growMin);
        }
    },

    
    fireKey: function(e) {
        if (e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() !== e.ENTER || e.hasModifier()))) {
            this.fireEvent('specialkey', this, e);
        }
    },

    
    autoSize: function() {
        var me = this,
            height;

        if (me.grow && me.rendered) {
            me.doComponentLayout();
            height = me.inputEl.getHeight();
            if (height !== me.lastInputHeight) {
                me.fireEvent('autosize', height);
                me.lastInputHeight = height;
            }
        }
    },

    
    initAria: function() {
        this.callParent(arguments);
        this.getActionEl().dom.setAttribute('aria-multiline', true);
    },

    
    getBodyNaturalWidth: function() {
        return Math.round(this.cols * 6.5) + 20;
    }

});



Ext.define('Ext.window.MessageBox', {
    extend: 'Ext.window.Window',

    requires: [
        'Ext.toolbar.Toolbar',
        'Ext.form.field.Text',
        'Ext.form.field.TextArea',
        'Ext.button.Button',
        'Ext.layout.container.Anchor',
        'Ext.layout.container.HBox',
        'Ext.ProgressBar'
    ],

    alternateClassName: 'Ext.MessageBox',

    alias: 'widget.messagebox',

    
    OK : 1,
    
    YES : 2,
    
    NO : 4,
    
    CANCEL : 8,
    
    OKCANCEL : 9,
    
    YESNO : 6,
    
    YESNOCANCEL : 14,
    
    INFO : 'ext-mb-info',
    
    WARNING : 'ext-mb-warning',
    
    QUESTION : 'ext-mb-question',
    
    ERROR : 'ext-mb-error',

    
    hideMode: 'offsets',
    closeAction: 'hide',
    resizable: false,
    title: '&#160;',

    width: 600,
    height: 500,
    minWidth: 250,
    maxWidth: 600,
    minHeight: 110,
    maxHeight: 500,
    constrain: true,

    cls: Ext.baseCSSPrefix + 'message-box',

    layout: {
        type: 'anchor'
    },

    
    defaultTextHeight : 75,
    
    minProgressWidth : 250,
    
    minPromptWidth: 250,
    
    buttonText: {
        ok: 'OK',
        yes: 'Yes',
        no: 'No',
        cancel: 'Cancel'
    },

    buttonIds: [
        'ok', 'yes', 'no', 'cancel'
    ],

    titleText: {
        confirm: 'Confirm',
        prompt: 'Prompt',
        wait: 'Loading...',
        alert: 'Attention'
    },

    iconHeight: 35,

    makeButton: function(btnIdx) {
        var btnId = this.buttonIds[btnIdx];
        return Ext.create('Ext.button.Button', {
            handler: this.btnCallback,
            itemId: btnId,
            scope: this,
            text: this.buttonText[btnId],
            minWidth: 75
        });
    },

    btnCallback: function(btn) {
        var me = this,
            value,
            field;

        if (me.cfg.prompt || me.cfg.multiline) {
            if (me.cfg.multiline) {
                field = me.textArea;
            } else {
                field = me.textField;
            }
            value = field.getValue();
            field.reset();
        }

        
        btn.blur();
        me.hide();
        me.userCallback(btn.itemId, value, me.cfg);
    },

    hide: function() {
        var me = this;
        me.dd.endDrag();
        me.progressBar.reset();
        me.removeCls(me.cfg.cls);
        me.callParent();
    },

    initComponent: function() {
        var me = this,
            i, button;

        me.title = '&#160;';

        me.topContainer = Ext.create('Ext.container.Container', {
            anchor: '100%',
            style: {
                padding: '10px',
                overflow: 'hidden'
            },
            items: [
                me.iconComponent = Ext.create('Ext.Component', {
                    cls: 'ext-mb-icon',
                    width: 50,
                    height: me.iconHeight,
                    style: {
                        'float': 'left'
                    }
                }),
                me.promptContainer = Ext.create('Ext.container.Container', {
                    layout: {
                        type: 'anchor'
                    },
                    items: [
                        me.msg = Ext.create('Ext.Component', {
                            autoEl: { tag: 'span' },
                            cls: 'ext-mb-text'
                        }),
                        me.textField = Ext.create('Ext.form.field.Text', {
                            anchor: '100%',
                            enableKeyEvents: true,
                            listeners: {
                                keydown: me.onPromptKey,
                                scope: me
                            }
                        }),
                        me.textArea = Ext.create('Ext.form.field.TextArea', {
                            anchor: '100%',
                            height: 75
                        })
                    ]
                })
            ]
        });
        me.progressBar = Ext.create('Ext.ProgressBar', {
            anchor: '-10',
            style: 'margin-left:10px'
        });

        me.items = [me.topContainer, me.progressBar];

        
        me.msgButtons = [];
        for (i = 0; i < 4; i++) {
            button = me.makeButton(i);
            me.msgButtons[button.itemId] = button;
            me.msgButtons.push(button);
        }
        me.bottomTb = Ext.create('Ext.toolbar.Toolbar', {
            ui: 'footer',
            dock: 'bottom',
            layout: {
                pack: 'center'
            },
            items: [
                me.msgButtons[0],
                me.msgButtons[1],
                me.msgButtons[2],
                me.msgButtons[3]
            ]
        });
        me.dockedItems = [me.bottomTb];

        me.callParent();
    },

    onPromptKey: function(textField, e) {
        var me = this,
            blur;

        if (e.keyCode === Ext.EventObject.RETURN || e.keyCode === 10) {
            if (me.msgButtons.ok.isVisible()) {
                blur = true;
                me.msgButtons.ok.handler.call(me, me.msgButtons.ok);
            } else if (me.msgButtons.yes.isVisible()) {
                me.msgButtons.yes.handler.call(me, me.msgButtons.yes);
                blur = true;
            }

            if (blur) {
                me.textField.blur();
            }
        }
    },

    reconfigure: function(cfg) {
        var me = this,
            buttons = cfg.buttons || 0,
            hideToolbar = true,
            initialWidth = me.maxWidth,
            i;

        cfg = cfg || {};
        me.cfg = cfg;
        if (cfg.width) {
            initialWidth = cfg.width;
        }

        
        delete me.defaultFocus;

        
        me.animateTarget = cfg.animateTarget || undefined;

        
        me.modal = cfg.modal !== false;

        
        if (cfg.title) {
            me.setTitle(cfg.title||'&#160;');
        }

        if (!me.rendered) {
            me.width = initialWidth;
            me.render(Ext.getBody());
        } else {
            me.hidden = false;
            me.setSize(initialWidth, me.maxHeight);
        }
        me.setPosition(-10000, -10000);

        
        me.closable = cfg.closable && !cfg.wait;
        if (cfg.closable === false) {
            me.tools.close.hide();
        } else {
            me.tools.close.show();
        }

        
        if (!cfg.title && !me.closable) {
            me.header.hide();
        } else {
            me.header.show();
        }

        
        me.liveDrag = !cfg.proxyDrag;

        
        me.userCallback = Ext.Function.bind(cfg.callback ||cfg.fn || Ext.emptyFn, cfg.scope || Ext.global);

        
        me.setIcon(cfg.icon);

        
        if (cfg.msg) {
            me.msg.update(cfg.msg);
            me.msg.show();
        } else {
            me.msg.hide();
        }

        
        if (cfg.prompt || cfg.multiline) {
            me.multiline = cfg.multiline;
            if (cfg.multiline) {
                me.textArea.setValue(cfg.value);
                me.textArea.setHeight(cfg.defaultTextHeight || me.defaultTextHeight);
                me.textArea.show();
                me.textField.hide();
                me.defaultFocus = me.textArea;
            } else {
                me.textField.setValue(cfg.value);
                me.textArea.hide();
                me.textField.show();
                me.defaultFocus = me.textField;
            }
        } else {
            me.textArea.hide();
            me.textField.hide();
        }

        
        if (cfg.progress || cfg.wait) {
            me.progressBar.show();
            me.updateProgress(0, cfg.progressText);
            if(cfg.wait === true){
                me.progressBar.wait(cfg.waitConfig);
            }
        } else {
            me.progressBar.hide();
        }

        
        for (i = 0; i < 4; i++) {
            if (buttons & Math.pow(2, i)) {

                
                if (!me.defaultFocus) {
                    me.defaultFocus = me.msgButtons[i];
                }
                me.msgButtons[i].show();
                hideToolbar = false;
            } else {
                me.msgButtons[i].hide();
            }
        }

        
        if (hideToolbar) {
            me.bottomTb.hide();
        } else {
            me.bottomTb.show();
        }
        me.hidden = true;
    },

    
    show: function(cfg) {
        var me = this;

        me.reconfigure(cfg);
        me.addCls(cfg.cls);
        if (cfg.animateTarget) {
            me.doAutoSize(false);
            me.callParent();
        } else {
            me.callParent();
            me.doAutoSize(true);
        }
        return me;
    },

    afterShow: function(){
        if (this.animateTarget) {
            this.center();
        }
        this.callParent(arguments);
    },

    doAutoSize: function(center) {
        var me = this,
            icon = me.iconComponent,
            iconHeight = me.iconHeight;

        if (!Ext.isDefined(me.frameWidth)) {
            me.frameWidth = me.el.getWidth() - me.body.getWidth();
        }

        
        icon.setHeight(iconHeight);

        
        me.minWidth = me.cfg.minWidth || Ext.getClass(this).prototype.minWidth;

        
        
        me.topContainer.doLayout();
        if (Ext.isIE6 || Ext.isIEQuirks) {
            
            
            
            me.textField.setCalculatedSize(9);
            me.textArea.setCalculatedSize(9);
        }
        var width = me.cfg.width || me.msg.getWidth() + icon.getWidth() + 25, 
            height = (me.header.rendered ? me.header.getHeight() : 0) +
            Math.max(me.promptContainer.getHeight(), icon.getHeight()) +
            me.progressBar.getHeight() +
            (me.bottomTb.rendered ? me.bottomTb.getHeight() : 0) + 20 ;

        
        icon.setHeight(Math.max(iconHeight, me.msg.getHeight()));
        me.setSize(width + me.frameWidth, height + me.frameWidth);
        if (center) {
            me.center();
        }
        return me;
    },

    updateText: function(text) {
        this.msg.update(text);
        return this.doAutoSize(true);
    },

    
    setIcon : function(icon) {
        var me = this;
        me.iconComponent.removeCls(me.iconCls);
        if (icon) {
            me.iconComponent.show();
            me.iconComponent.addCls(Ext.baseCSSPrefix + 'dlg-icon');
            me.iconComponent.addCls(me.iconCls = icon);
        } else {
            me.iconComponent.removeCls(Ext.baseCSSPrefix + 'dlg-icon');
            me.iconComponent.hide();
        }
        return me;
    },

    
    updateProgress : function(value, progressText, msg){
        this.progressBar.updateProgress(value, progressText);
        if (msg){
            this.updateText(msg);
        }
        return this;
    },

    onEsc: function() {
        if (this.closable !== false) {
            this.callParent(arguments);
        }
    },

    
    confirm: function(cfg, msg, fn, scope) {
        if (Ext.isString(cfg)) {
            cfg = {
                title: cfg,
                icon: 'ext-mb-question',
                msg: msg,
                buttons: this.YESNO,
                callback: fn,
                scope: scope
            };
        }
        return this.show(cfg);
    },

    
    prompt : function(cfg, msg, fn, scope, multiline, value){
        if (Ext.isString(cfg)) {
            cfg = {
                prompt: true,
                title: cfg,
                minWidth: this.minPromptWidth,
                msg: msg,
                buttons: this.OKCANCEL,
                callback: fn,
                scope: scope,
                multiline: multiline,
                value: value
            };
        }
        return this.show(cfg);
    },

    
    wait : function(cfg, title, config){
        if (Ext.isString(cfg)) {
            cfg = {
                title : title,
                msg : cfg,
                closable: false,
                wait: true,
                modal: true,
                minWidth: this.minProgressWidth,
                waitConfig: config
            };
        }
        return this.show(cfg);
    },

    
    alert: function(cfg, msg, fn, scope) {
        if (Ext.isString(cfg)) {
            cfg = {
                title : cfg,
                msg : msg,
                buttons: this.OK,
                fn: fn,
                scope : scope,
                minWidth: this.minWidth
            };
        }
        return this.show(cfg);
    },

    
    progress : function(cfg, msg, progressText){
        if (Ext.isString(cfg)) {
            cfg = {
                title: cfg,
                msg: msg,
                progressText: progressText
            };
        }
        return this.show(cfg);
    }
}, function() {
    Ext.MessageBox = Ext.Msg = new this();
});




Ext.define('Ext.form.Basic', {
    extend: 'Ext.util.Observable',
    alternateClassName: 'Ext.form.BasicForm',
    requires: ['Ext.util.MixedCollection', 'Ext.form.action.Load', 'Ext.form.action.Submit',
               'Ext.window.MessageBox', 'Ext.data.Errors', 'Ext.util.DelayedTask'],

    constructor: function(owner, config) {
        var me = this,
            onItemAddOrRemove = me.onItemAddOrRemove;

        
        me.owner = owner;

        
        me.mon(owner, {
            add: onItemAddOrRemove,
            remove: onItemAddOrRemove,
            scope: me
        });

        Ext.apply(me, config);

        
        if (Ext.isString(me.paramOrder)) {
            me.paramOrder = me.paramOrder.split(/[\s,|]/);
        }

        me.checkValidityTask = Ext.create('Ext.util.DelayedTask', me.checkValidity, me);

        me.addEvents(
            
            'beforeaction',
            
            'actionfailed',
            
            'actioncomplete',
            
            'validitychange',
            
            'dirtychange'
        );
        me.callParent();
    },

    
    initialize: function(){
        this.initialized = true;
        this.onValidityChange(!this.hasInvalidField());
    },

    
    
    

    

    

    
    timeout: 30,

    

    

    
    paramsAsHash: false,

    
    waitTitle: 'Please Wait...',

    
    trackResetOnLoad: false,

    

    


    
    wasDirty: false,


    
    destroy: function() {
        this.clearListeners();
        this.checkValidityTask.cancel();
    },

    
    onItemAddOrRemove: function(parent, child) {
        var me = this,
            isAdding = !!child.ownerCt,
            isContainer = child.isContainer;

        function handleField(field) {
            
            me[isAdding ? 'mon' : 'mun'](field, {
                validitychange: me.checkValidity,
                dirtychange: me.checkDirty,
                scope: me,
                buffer: 100 
            });
            
            delete me._fields;
        }

        if (child.isFormField) {
            handleField(child);
        }
        else if (isContainer) {
            
            Ext.Array.forEach(child.query('[isFormField]'), handleField);
        }

        
        delete this._boundItems;

        
        
        if (me.initialized) {
            me.checkValidityTask.delay(10);
        }
    },

    
    getFields: function() {
        var fields = this._fields;
        if (!fields) {
            fields = this._fields = Ext.create('Ext.util.MixedCollection');
            fields.addAll(this.owner.query('[isFormField]'));
        }
        return fields;
    },

    getBoundItems: function() {
        var boundItems = this._boundItems;
        if (!boundItems) {
            boundItems = this._boundItems = Ext.create('Ext.util.MixedCollection');
            boundItems.addAll(this.owner.query('[formBind]'));
        }
        return boundItems;
    },

    
    hasInvalidField: function() {
        return !!this.getFields().findBy(function(field) {
            var preventMark = field.preventMark,
                isValid;
            field.preventMark = true;
            isValid = field.isValid();
            field.preventMark = preventMark;
            return !isValid;
        });
    },

    
    isValid: function() {
        var me = this,
            invalid;
        me.batchLayouts(function() {
            invalid = me.getFields().filterBy(function(field) {
                return !field.validate();
            });
        });
        return invalid.length < 1;
    },

    
    checkValidity: function() {
        var me = this,
            valid = !me.hasInvalidField();
        if (valid !== me.wasValid) {
            me.onValidityChange(valid);
            me.fireEvent('validitychange', me, valid);
            me.wasValid = valid;
        }
    },

    
    onValidityChange: function(valid) {
        var boundItems = this.getBoundItems();
        if (boundItems) {
            boundItems.each(function(cmp) {
                if (cmp.disabled === valid) {
                    cmp.setDisabled(!valid);
                }
            });
        }
    },

    
    isDirty: function() {
        return !!this.getFields().findBy(function(f) {
            return f.isDirty();
        });
    },

    
    checkDirty: function() {
        var dirty = this.isDirty();
        if (dirty !== this.wasDirty) {
            this.fireEvent('dirtychange', this, dirty);
            this.wasDirty = dirty;
        }
    },

    
    hasUpload: function() {
        return !!this.getFields().findBy(function(f) {
            return f.isFileUpload();
        });
    },

    
    doAction: function(action, options) {
        if (Ext.isString(action)) {
            action = Ext.ClassManager.instantiateByAlias('formaction.' + action, Ext.apply({}, options, {form: this}));
        }
        if (this.fireEvent('beforeaction', this, action) !== false) {
            this.beforeAction(action);
            Ext.defer(action.run, 100, action);
        }
        return this;
    },

    
    submit: function(options) {
        return this.doAction(this.standardSubmit ? 'standardsubmit' : this.api ? 'directsubmit' : 'submit', options);
    },

    
    load: function(options) {
        return this.doAction(this.api ? 'directload' : 'load', options);
    },

    
    updateRecord: function(record) {
        var fields = record.fields,
            values = this.getFieldValues(),
            name,
            obj = {};

        fields.each(function(f) {
            name = f.name;
            if (name in values) {
                obj[name] = values[name];
            }
        });

        record.beginEdit();
        record.set(obj);
        record.endEdit();

        return this;
    },

    
    loadRecord: function(record) {
        this._record = record;
        return this.setValues(record.data);
    },
    
    
    getRecord: function() {
        return this._record;
    },

    
    beforeAction: function(action) {
        var waitMsg = action.waitMsg,
            maskCls = Ext.baseCSSPrefix + 'mask-loading',
            waitMsgTarget;

        
        this.getFields().each(function(f) {
            if (f.isFormField && f.syncValue) {
                f.syncValue();
            }
        });

        if (waitMsg) {
            waitMsgTarget = this.waitMsgTarget;
            if (waitMsgTarget === true) {
                this.owner.el.mask(waitMsg, maskCls);
            } else if (waitMsgTarget) {
                waitMsgTarget = this.waitMsgTarget = Ext.get(waitMsgTarget);
                waitMsgTarget.mask(waitMsg, maskCls);
            } else {
                Ext.MessageBox.wait(waitMsg, action.waitTitle || this.waitTitle);
            }
        }
    },

    
    afterAction: function(action, success) {
        if (action.waitMsg) {
            var MessageBox = Ext.MessageBox,
                waitMsgTarget = this.waitMsgTarget;
            if (waitMsgTarget === true) {
                this.owner.el.unmask();
            } else if (waitMsgTarget) {
                waitMsgTarget.unmask();
            } else {
                MessageBox.updateProgress(1);
                MessageBox.hide();
            }
        }
        if (success) {
            if (action.reset) {
                this.reset();
            }
            Ext.callback(action.success, action.scope || action, [this, action]);
            this.fireEvent('actioncomplete', this, action);
        } else {
            Ext.callback(action.failure, action.scope || action, [this, action]);
            this.fireEvent('actionfailed', this, action);
        }
    },


    
    findField: function(id) {
        return this.getFields().findBy(function(f) {
            return f.id === id || f.getName() === id;
        });
    },


    
    markInvalid: function(errors) {
        var me = this;

        function mark(fieldId, msg) {
            var field = me.findField(fieldId);
            if (field) {
                field.markInvalid(msg);
            }
        }

        if (Ext.isArray(errors)) {
            Ext.each(errors, function(err) {
                mark(err.id, err.msg);
            });
        }
        else if (errors instanceof Ext.data.Errors) {
            errors.each(function(err) {
                mark(err.field, err.message);
            });
        }
        else {
            Ext.iterate(errors, mark);
        }
        return this;
    },

    
    setValues: function(values) {
        var me = this;

        function setVal(fieldId, val) {
            var field = me.findField(fieldId);
            if (field) {
                field.setValue(val);
                if (me.trackResetOnLoad) {
                    field.resetOriginalValue();
                }
            }
        }

        if (Ext.isArray(values)) {
            
            Ext.each(values, function(val) {
                setVal(val.id, val.value);
            });
        } else {
            
            Ext.iterate(values, setVal);
        }
        return this;
    },

    
    getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
        var values = {};

        this.getFields().each(function(field) {
            if (!dirtyOnly || field.isDirty()) {
                var data = field[useDataValues ? 'getModelData' : 'getSubmitData'](includeEmptyText);
                if (Ext.isObject(data)) {
                    Ext.iterate(data, function(name, val) {
                        if (includeEmptyText && val === '') {
                            val = field.emptyText || '';
                        }
                        if (name in values) {
                            var bucket = values[name],
                                isArray = Ext.isArray;
                            if (!isArray(bucket)) {
                                bucket = values[name] = [bucket];
                            }
                            if (isArray(val)) {
                                values[name] = bucket.concat(val);
                            } else {
                                bucket.push(val);
                            }
                        } else {
                            values[name] = val;
                        }
                    });
                }
            }
        });

        if (asString) {
            values = Ext.Object.toQueryString(values);
        }
        return values;
    },

    
    getFieldValues: function(dirtyOnly) {
        return this.getValues(false, dirtyOnly, false, true);
    },

    
    clearInvalid: function() {
        var me = this;
        me.batchLayouts(function() {
            me.getFields().each(function(f) {
                f.clearInvalid();
            });
        });
        return me;
    },

    
    reset: function() {
        var me = this;
        me.batchLayouts(function() {
            me.getFields().each(function(f) {
                f.reset();
            });
        });
        return me;
    },

    
    applyToFields: function(obj) {
        this.getFields().each(function(f) {
            Ext.apply(f, obj);
        });
        return this;
    },

    
    applyIfToFields: function(obj) {
        this.getFields().each(function(f) {
            Ext.applyIf(f, obj);
        });
        return this;
    },

    
    batchLayouts: function(fn) {
        var me = this,
            suspended = new Ext.util.HashMap();

        
        me.getFields().each(function(field) {
            var ownerCt = field.ownerCt;
            if (!suspended.contains(ownerCt)) {
                suspended.add(ownerCt);
                ownerCt.oldSuspendLayout = ownerCt.suspendLayout;
                ownerCt.suspendLayout = true;
            }
        });

        
        fn();

        
        suspended.each(function(id, ct) {
            ct.suspendLayout = ct.oldSuspendLayout;
            delete ct.oldSuspendLayout;
        });

        
        me.owner.doComponentLayout();
    }
});


Ext.define('Ext.form.FieldAncestor', {

    


    
    initFieldAncestor: function() {
        var me = this,
            onSubtreeChange = me.onFieldAncestorSubtreeChange;

        me.addEvents(
            
            'fieldvaliditychange',

            
            'fielderrorchange'
        );

        
        me.on('add', onSubtreeChange, me);
        me.on('remove', onSubtreeChange, me);

        me.initFieldDefaults();
    },

    
    initFieldDefaults: function() {
        if (!this.fieldDefaults) {
            this.fieldDefaults = {};
        }
    },

    
    onFieldAncestorSubtreeChange: function(parent, child) {
        var me = this,
            isAdding = !!child.ownerCt;

        function handleCmp(cmp) {
            var isLabelable = cmp.isFieldLabelable,
                isField = cmp.isFormField;
            if (isLabelable || isField) {
                if (isLabelable) {
                    me['onLabelable' + (isAdding ? 'Added' : 'Removed')](cmp);
                }
                if (isField) {
                    me['onField' + (isAdding ? 'Added' : 'Removed')](cmp);
                }
            }
            else if (cmp.isContainer) {
                Ext.Array.forEach(cmp.getRefItems(), handleCmp);
            }
        }
        handleCmp(child);
    },

    
    onLabelableAdded: function(labelable) {
        var me = this;

        
        me.mon(labelable, 'errorchange', me.handleFieldErrorChange, me, {buffer: 10});

        labelable.setFieldDefaults(me.fieldDefaults);
    },

    
    onFieldAdded: function(field) {
        var me = this;
        me.mon(field, 'validitychange', me.handleFieldValidityChange, me);
    },

    
    onLabelableRemoved: function(labelable) {
        var me = this;
        me.mun(labelable, 'errorchange', me.handleFieldErrorChange, me);
    },

    
    onFieldRemoved: function(field) {
        var me = this;
        me.mun(field, 'validitychange', me.handleFieldValidityChange, me);
    },

    
    handleFieldValidityChange: function(field, isValid) {
        var me = this;
        me.fireEvent('fieldvaliditychange', me, field, isValid);
        me.onFieldValidityChange();
    },

    
    handleFieldErrorChange: function(labelable, activeError) {
        var me = this;
        me.fireEvent('fielderrorchange', me, labelable, activeError);
        me.onFieldErrorChange();
    },

    
    onFieldValidityChange: Ext.emptyFn,

    
    onFieldErrorChange: Ext.emptyFn

});

Ext.define('Ext.layout.container.CheckboxGroup', {
    extend: 'Ext.layout.container.Container',
    alias: ['layout.checkboxgroup'],


    onLayout: function() {
        var numCols = this.getColCount(),
            shadowCt = this.getShadowCt(),
            owner = this.owner,
            items = owner.items,
            shadowItems = shadowCt.items,
            numItems = items.length,
            colIndex = 0,
            i, numRows;

        
        
        
        

        shadowItems.each(function(col) {
            col.items.clear();
        });

        
        
        while (shadowItems.length > numCols) {
            shadowCt.remove(shadowItems.last());
        }
        while (shadowItems.length < numCols) {
            shadowCt.add({
                xtype: 'container',
                cls: owner.groupCls,
                flex: 1
            });
        }

        if (owner.vertical) {
            numRows = Math.ceil(numItems / numCols);
            for (i = 0; i < numItems; i++) {
                if (i > 0 && i % numRows === 0) {
                    colIndex++;
                }
                shadowItems.getAt(colIndex).items.add(items.getAt(i));
            }
        } else {
            for (i = 0; i < numItems; i++) {
                colIndex = i % numCols;
                shadowItems.getAt(colIndex).items.add(items.getAt(i));
            }
        }

        if (!shadowCt.rendered) {
            shadowCt.render(this.getRenderTarget());
        } else {
            
            
            shadowItems.each(function(col) {
                var layout = col.getLayout();
                layout.renderItems(layout.getLayoutItems(), layout.getRenderTarget());
            });
        }

        shadowCt.doComponentLayout();
    },


    
    renderItems: Ext.emptyFn,


    
    getShadowCt: function() {
        var me = this,
            shadowCt = me.shadowCt,
            owner, items, item, columns, columnsIsArray, numCols, i;

        if (!shadowCt) {
            
            owner = me.owner;
            columns = owner.columns;
            columnsIsArray = Ext.isArray(columns);
            numCols = me.getColCount();
            items = [];
            for(i = 0; i < numCols; i++) {
                item = {
                    xtype: 'container',
                    cls: owner.groupCls
                };
                if (columnsIsArray) {
                    
                    
                    if (columns[i] < 1) {
                        item.flex = columns[i];
                    } else {
                        item.width = columns[i];
                    }
                }
                else {
                    
                    item.flex = 1;
                }
                items.push(item);
            }

            
            shadowCt = me.shadowCt = Ext.createWidget('container', {
                layout: 'hbox',
                items: items,
                ownerCt: owner
            });
        }
        
        return shadowCt;
    },


    
    getColCount: function() {
        var owner = this.owner,
            colsCfg = owner.columns;
        return Ext.isArray(colsCfg) ? colsCfg.length : (Ext.isNumber(colsCfg) ? colsCfg : owner.items.length);
    }

});


Ext.define('Ext.form.FieldContainer', {
    extend: 'Ext.container.Container',
    mixins: {
        labelable: 'Ext.form.Labelable',
        fieldAncestor: 'Ext.form.FieldAncestor'
    },
    alias: 'widget.fieldcontainer',

    componentLayout: 'field',

    
    combineLabels: false,

    
    labelConnector: ', ',

    
    combineErrors: false,
    
    maskOnDisable: false,

    initComponent: function() {
        var me = this,
            onSubCmpAddOrRemove = me.onSubCmpAddOrRemove;

        
        me.initLabelable();
        me.initFieldAncestor();

        me.callParent();
    },

    
    onLabelableAdded: function(labelable) {
        var me = this;
        me.mixins.fieldAncestor.onLabelableAdded.call(this, labelable);
        me.updateLabel();
    },

    
    onLabelableRemoved: function(labelable) {
        var me = this;
        me.mixins.fieldAncestor.onLabelableRemoved.call(this, labelable);
        me.updateLabel();
    },

    onRender: function() {
        var me = this,
            renderSelectors = me.renderSelectors,
            applyIf = Ext.applyIf;

        applyIf(renderSelectors, me.getLabelableSelectors());

        me.callParent(arguments);
    },

    initRenderTpl: function() {
        var me = this;
        if (!me.hasOwnProperty('renderTpl')) {
            me.renderTpl = me.getTpl('labelableRenderTpl');
        }
        return me.callParent();
    },

    initRenderData: function() {
        return Ext.applyIf(this.callParent(), this.getLabelableRenderData());
    },

    
    getFieldLabel: function() {
        var label = this.fieldLabel || '';
        if (!label && this.combineLabels) {
            label = Ext.Array.map(this.query('[isFieldLabelable]'), function(field) {
                return field.getFieldLabel();
            }).join(this.labelConnector);
        }
        return label;
    },

    
    updateLabel: function() {
        var me = this,
            label = me.labelEl;
        if (label) {
            label.update(me.getFieldLabel());
        }
    },


    
    onFieldErrorChange: function(field, activeError) {
        if (this.combineErrors) {
            var me = this,
                oldError = me.getActiveError(),
                invalidFields = Ext.Array.filter(me.query('[isFormField]'), function(field) {
                    return field.hasActiveError();
                }),
                newErrors = me.getCombinedErrors(invalidFields);

            if (newErrors) {
                me.setActiveErrors(newErrors);
            } else {
                me.unsetActiveError();
            }

            if (oldError !== me.getActiveError()) {
                me.doComponentLayout();
            }
        }
    },

    
    getCombinedErrors: function(invalidFields) {
        var forEach = Ext.Array.forEach,
            errors = [];
        forEach(invalidFields, function(field) {
            forEach(field.getActiveErrors(), function(error) {
                var label = field.getFieldLabel();
                errors.push((label ? label + ': ' : '') + error);
            });
        });
        return errors;
    },

    getTargetEl: function() {
        return this.bodyEl || this.callParent();
    }
});


Ext.define('Ext.form.CheckboxGroup', {
    extend:'Ext.form.FieldContainer',
    mixins: {
        field: 'Ext.form.field.Field'
    },
    alias: 'widget.checkboxgroup',
    requires: ['Ext.layout.container.CheckboxGroup', 'Ext.form.field.Base'],

    

    

    
    columns : 'auto',

    
    vertical : false,

    
    allowBlank : true,

    
    blankText : "You must select at least one item in this group",

    
    defaultType : 'checkboxfield',

    
    groupCls : Ext.baseCSSPrefix + 'form-check-group',

    
    fieldBodyCls: Ext.baseCSSPrefix + 'form-checkboxgroup-body',

    
    layout: 'checkboxgroup',

    initComponent: function() {
        var me = this;
        me.callParent();
        me.initField();
    },

    
    initValue: function() {
        var me = this,
            valueCfg = me.value;
        me.originalValue = me.lastValue = valueCfg || me.getValue();
        if (valueCfg) {
            me.setValue(valueCfg);
        }
    },

    
    onFieldAdded: function(field) {
        var me = this;
        if (field.isCheckbox) {
            me.mon(field, 'change', me.checkChange, me);
        }
        me.callParent(arguments);
    },

    onFieldRemoved: function(field) {
        var me = this;
        if (field.isCheckbox) {
            me.mun(field, 'change', me.checkChange, me);
        }
        me.callParent(arguments);
    },

    
    isEqual: function(value1, value2) {
        var toQueryString = Ext.Object.toQueryString;
        return toQueryString(value1) === toQueryString(value2);
    },

    
    getErrors: function() {
        var errors = [];
        if (!this.allowBlank && Ext.isEmpty(this.getChecked())) {
            errors.push(this.blankText);
        }
        return errors;
    },

    
    getBoxes: function() {
        return this.query('[isCheckbox]');
    },

    
    eachBox: function(fn, scope) {
        Ext.Array.forEach(this.getBoxes(), fn, scope || this);
    },

    
    getChecked: function() {
        return Ext.Array.filter(this.getBoxes(), function(cb) {
            return cb.getValue();
        });
    },

    
    isDirty: function(){
        return Ext.Array.some(this.getBoxes(), function(cb) {
            return cb.isDirty();
        });
    },

    
    setReadOnly: function(readOnly) {
        this.eachBox(function(cb) {
            cb.setReadOnly(readOnly);
        });
        this.readOnly = readOnly;
    },

    
    reset: function() {
        var me = this,
            hadError = me.hasActiveError(),
            preventMark = me.preventMark;
        me.preventMark = true;
        me.batchChanges(function() {
            me.eachBox(function(cb) {
                cb.reset();
            });
        });
        me.preventMark = preventMark;
        me.unsetActiveError();
        if (hadError) {
            me.doComponentLayout();
        }
    },

    
    resetOriginalValue: function() {
        
        
        Ext.defer(function() {
            this.callParent();
        }, 1, this);
    },


    
    setValue: function(value) {
        var me = this;
        me.batchChanges(function() {
            me.eachBox(function(cb) {
                var name = cb.getName(),
                    cbValue = false;
                if (value && name in value) {
                    if (Ext.isArray(value[name])) {
                        cbValue = Ext.Array.contains(value[name], cb.inputValue);
                    } else {
                        
                        cbValue = value[name];
                    }
                }
                cb.setValue(cbValue);
            });
        });
        return me;
    },


    
    getValue: function() {
        var values = {};
        this.eachBox(function(cb) {
            var name = cb.getName(),
                inputValue = cb.inputValue,
                bucket;
            if (cb.getValue()) {
                if (name in values) {
                    bucket = values[name];
                    if (!Ext.isArray(bucket)) {
                        bucket = values[name] = [bucket];
                    }
                    bucket.push(inputValue);
                } else {
                    values[name] = inputValue;
                }
            }
        });
        return values;
    },

    
    getSubmitData: function() {
        return null;
    },

    
    getModelData: function() {
        return null;
    },

    validate: function() {
        var me = this,
            errors = me.getErrors(),
            isValid = Ext.isEmpty(errors),
            wasValid = !me.hasActiveError();

        if (isValid) {
            me.unsetActiveError();
        } else {
            me.setActiveError(errors);
        }
        if (isValid !== wasValid) {
            me.fireEvent('validitychange', me, isValid);
            me.doComponentLayout();
        }

        return isValid;
    }

}, function() {

    this.borrow(Ext.form.field.Base, ['markInvalid', 'clearInvalid']);

});



Ext.define('Ext.form.CheckboxManager', {
    extend: 'Ext.util.MixedCollection',
    singleton: true,

    getByName: function(name) {
        return this.filterBy(function(item) {
            return item.name == name;
        });
    },

    getWithValue: function(name, value) {
        return this.filterBy(function(item) {
            return item.name == name && item.inputValue == value;
        });
    },

    getChecked: function(name) {
        return this.filterBy(function(item) {
            return item.name == name && item.checked;
        });
    }
});


Ext.define('Ext.form.FieldSet', {
    extend: 'Ext.container.Container',
    alias: 'widget.fieldset',
    uses: ['Ext.form.field.Checkbox', 'Ext.panel.Tool', 'Ext.layout.container.Anchor', 'Ext.layout.component.FieldSet'],

    

    

    

    

    
    collapsed: false,

    

    
    baseCls: Ext.baseCSSPrefix + 'fieldset',

    
    layout: 'anchor',

    componentLayout: 'fieldset',

    
    ariaRole: '',

    renderTpl: ['<div class="{baseCls}-body"></div>'],
    
    maskOnDisable: false,

    getElConfig: function(){
        return {tag: 'fieldset', id: this.id};
    },

    initComponent: function() {
        var me = this,
            baseCls = me.baseCls;

        me.callParent();

        
        me.initLegend();

        
        Ext.applyIf(me.renderSelectors, {
            body: '.' + baseCls + '-body'
        });

        if (me.collapsed) {
            me.addCls(baseCls + '-collapsed');
            me.collapse();
        }
    },

    
    onRender: function(container, position) {
        this.callParent(arguments);
        
        this.initLegend();
    },

    
    initLegend: function() {
        var me = this,
            legendItems,
            legend = me.legend;

        
        if (!legend && (me.title || me.checkboxToggle || me.collapsible)) {
            legendItems = [];

            
            if (me.checkboxToggle) {
                legendItems.push(me.createCheckboxCmp());
            }
            
            else if (me.collapsible) {
                legendItems.push(me.createToggleCmp());
            }

            
            legendItems.push(me.createTitleCmp());

            legend = me.legend = Ext.create('Ext.container.Container', {
                baseCls: me.baseCls + '-header',
                ariaRole: '',
                getElConfig: function(){
                    return {tag: 'legend', cls: this.baseCls};
                },
                items: legendItems
            });
        }

        
        if (legend && !legend.rendered && me.rendered) {
            me.legend.render(me.el, me.body); 
        }
    },

    
    createTitleCmp: function() {
        var me = this;
        me.titleCmp = Ext.create('Ext.Component', {
            html: me.title,
            cls: me.baseCls + '-header-text'
        });
        return me.titleCmp;
        
    },

    

    
    createCheckboxCmp: function() {
        var me = this,
            suffix = '-checkbox';
            
        me.checkboxCmp = Ext.create('Ext.form.field.Checkbox', {
            name: me.checkboxName || me.id + suffix,
            cls: me.baseCls + '-header' + suffix,
            checked: !me.collapsed,
            listeners: {
                change: me.onCheckChange,
                scope: me
            }
        });
        return me.checkboxCmp;
    },

    

    
    createToggleCmp: function() {
        var me = this;
        me.toggleCmp = Ext.create('Ext.panel.Tool', {
            type: 'toggle',
            handler: me.toggle,
            scope: me
        });
        return me.toggleCmp;
    },
    
    
    setTitle: function(title) {
        var me = this;
        me.title = title;
        me.initLegend();
        me.titleCmp.update(title);
        return me;
    },
    
    getTargetEl : function() {
        return this.body || this.frameBody || this.el;
    },
    
    getContentTarget: function() {
        return this.body;
    },
    
    
    getRefItems: function(deep) {
        var refItems = this.callParent(arguments),
            legend = this.legend;

        
        if (legend) {
            refItems.unshift(legend);
            if (deep) {
                refItems.unshift.apply(refItems, legend.getRefItems(true));
            }
        }
        return refItems;
    },

    
    expand : function(){
        return this.setExpanded(true);
    },
    
    
    collapse : function() {
        return this.setExpanded(false);
    },

    
    setExpanded: function(expanded) {
        var me = this,
            checkboxCmp = me.checkboxCmp;

        expanded = !!expanded;
        
        if (checkboxCmp) {
            checkboxCmp.setValue(expanded);
        }
        
        if (expanded) {
            me.removeCls(me.baseCls + '-collapsed');
        } else {
            me.addCls(me.baseCls + '-collapsed');
        }
        me.collapsed = !expanded;
        if (expanded) {
            
            me.getComponentLayout().childrenChanged = true;
        }
        me.doComponentLayout();
        return me;
    },

    
    toggle: function() {
        this.setExpanded(!!this.collapsed);
    },

    
    onCheckChange: function(cmp, checked) {
        this.setExpanded(checked);
    },

    beforeDestroy : function() {
        var legend = this.legend;
        if (legend) {
            legend.destroy();
        }
        this.callParent();
    }
});


Ext.define('Ext.form.Label', {
    extend:'Ext.Component',
    alias: 'widget.label',
    requires: ['Ext.util.Format'],

    
    
    
    
    maskOnDisable: false,
    getElConfig: function(){
        var me = this;
        return {
            tag: 'label', 
            id: me.id, 
            htmlFor: me.forId || '',
            html: me.text ? Ext.util.Format.htmlEncode(me.text) : (me.html || '') 
        };
    },

    
    setText : function(text, encode){
        var me = this;
        
        encode = encode !== false;
        if(encode) {
            me.text = text;
            delete me.html;
        } else {
            me.html = text;
            delete me.text;
        }
        
        if(me.rendered){
            me.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(text) : text;
        }
        return this;
    }
});



Ext.define('Ext.form.Panel', {
    extend:'Ext.panel.Panel',
    mixins: {
        fieldAncestor: 'Ext.form.FieldAncestor'
    },
    alias: 'widget.form',
    alternateClassName: ['Ext.FormPanel', 'Ext.form.FormPanel'],
    requires: ['Ext.form.Basic', 'Ext.util.TaskRunner'],

    

    

    
    layout: 'anchor',

    ariaRole: 'form',

    initComponent: function() {
        var me = this;
        
        if (me.frame) {
            me.border = false;
        }
        
        me.initFieldAncestor();
        me.callParent();

        me.relayEvents(me.form, [
            'beforeaction',
            'actionfailed',
            'actioncomplete',
            'validitychange',
            'dirtychange'
        ]);

        
        if (me.pollForChanges) {
            me.startPolling(me.pollInterval || 500);
        }
    },

    initItems: function() {
        
        var me = this;
        
        me.form = me.createForm();
        me.callParent();
        me.form.initialize();
    },

    
    createForm: function() {
        return Ext.create('Ext.form.Basic', this, Ext.applyIf({listeners: {}}, this.initialConfig));
    },

    
    getForm: function() {
        return this.form;
    },
    
    
    loadRecord: function(record) {
        return this.getForm().loadRecord(record);
    },
    
    
    getRecord: function() {
        return this.getForm().getRecord();
    },
    
    
    getValues: function() {
        return this.getForm().getValues();
    },

    beforeDestroy: function() {
        this.stopPolling();
        this.form.destroy();
        this.callParent();
    },

    
    load: function(options) {
        this.form.load(options);
    },

    
    submit: function(options) {
        this.form.submit(options);
    },

    
    disable: function(silent) {
        this.callParent(arguments);
        this.form.getFields().each(function(field) {
            field.disable();
        });
    },

    
    enable: function(silent) {
        this.callParent(arguments);
        this.form.getFields().each(function(field) {
            field.enable();
        });
    },

    
    startPolling: function(interval) {
        this.stopPolling();
        var task = Ext.create('Ext.util.TaskRunner', interval);
        task.start({
            interval: 0,
            run: this.checkChange,
            scope: this
        });
        this.pollTask = task;
    },

    
    stopPolling: function() {
        var task = this.pollTask;
        if (task) {
            task.stopAll();
            delete this.pollTask;
        }
    },

    
    checkChange: function() {
        this.form.getFields().each(function(field) {
            field.checkChange();
        });
    }
});


Ext.define('Ext.form.RadioGroup', {
    extend: 'Ext.form.CheckboxGroup',
    alias: 'widget.radiogroup',

    
    
    allowBlank : true,
    
    blankText : 'You must select one item in this group',
    
    
    defaultType : 'radiofield',
    
    
    groupCls : Ext.baseCSSPrefix + 'form-radio-group',

    getBoxes: function() {
        return this.query('[isRadio]');
    }

});


Ext.define('Ext.form.RadioManager', {
    extend: 'Ext.util.MixedCollection',
    singleton: true,

    getByName: function(name) {
        return this.filterBy(function(item) {
            return item.name == name;
        });
    },

    getWithValue: function(name, value) {
        return this.filterBy(function(item) {
            return item.name == name && item.inputValue == value;
        });
    },

    getChecked: function(name) {
        return this.findBy(function(item) {
            return item.name == name && item.checked;
        });
    }
});


Ext.define('Ext.form.action.DirectLoad', {
    extend:'Ext.form.action.Load',
    requires: ['Ext.direct.Manager'],
    alternateClassName: 'Ext.form.Action.DirectLoad',
    alias: 'formaction.directload',

    type: 'directload',

    run: function() {
        this.form.api.load.apply(window, this.getArgs());
    },

    
    getArgs: function() {
        var me = this,
            args = [],
            form = me.form,
            paramOrder = form.paramOrder,
            params = me.getParams(),
            i, len;

        
        if (paramOrder) {
            for (i = 0, len = paramOrder.length; i < len; i++) {
                args.push(params[paramOrder[i]]);
            }
        }
        
        else if (form.paramsAsHash) {
            args.push(params);
        }

        
        args.push(me.onSuccess, me);

        return args;
    },

    
    
    
    processResponse: function(result) {
        return (this.result = result);
    },

    onSuccess: function(result, trans) {
        if (trans.type == Ext.direct.Manager.self.exceptions.SERVER) {
            result = {};
        }
        this.callParent([result]);
    }
});




Ext.define('Ext.form.action.DirectSubmit', {
    extend:'Ext.form.action.Submit',
    requires: ['Ext.direct.Manager'],
    alternateClassName: 'Ext.form.Action.DirectSubmit',
    alias: 'formaction.directsubmit',

    type: 'directsubmit',

    doSubmit: function() {
        var me = this,
            callback = Ext.Function.bind(me.onSuccess, me),
            formEl = me.buildForm();
        me.form.api.submit(formEl, callback, me);
        Ext.removeNode(formEl);
    },

    
    
    
    processResponse: function(result) {
        return (this.result = result);
    },

    onSuccess: function(response, trans) {
        if (trans.type === Ext.direct.Manager.self.exceptions.SERVER) {
            response = {};
        }
        this.callParent([response]);
    }
});


Ext.define('Ext.form.action.StandardSubmit', {
    extend:'Ext.form.action.Submit',
    alias: 'formaction.standardsubmit',

    

    
    doSubmit: function() {
        var form = this.buildForm();
        form.submit();
        Ext.removeNode(form);
    }

});


Ext.define('Ext.form.field.Checkbox', {
    extend: 'Ext.form.field.Base',
    alias: ['widget.checkboxfield', 'widget.checkbox'],
    alternateClassName: 'Ext.form.Checkbox',
    requires: ['Ext.XTemplate', 'Ext.form.CheckboxManager'],

    fieldSubTpl: [
        '<tpl if="boxLabel && boxLabelAlign == \'before\'">',
            '<label class="{boxLabelCls} {boxLabelCls}-{boxLabelAlign}" for="{id}">{boxLabel}</label>',
        '</tpl>',
        
        
        
        '<input type="button" id="{id}" ',
            '<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>',
            'class="{fieldCls} {typeCls}" autocomplete="off" hidefocus="true" />',
        '<tpl if="boxLabel && boxLabelAlign == \'after\'">',
            '<label class="{boxLabelCls} {boxLabelCls}-{boxLabelAlign}" for="{id}">{boxLabel}</label>',
        '</tpl>',
        {
            disableFormats: true,
            compiled: true
        }
    ],

    isCheckbox: true,

    
    focusCls: Ext.baseCSSPrefix + 'form-cb-focus',

    

    
    fieldBodyCls: Ext.baseCSSPrefix + 'form-cb-wrap',

    
    checked: false,

    
    checkedCls: Ext.baseCSSPrefix + 'form-cb-checked',

    

    
    boxLabelCls: Ext.baseCSSPrefix + 'form-cb-label',

    
    boxLabelAlign: 'after',

    
    inputValue: 'on',

    

    

    

    
    checkChangeEvents: [],
    inputType: 'checkbox',
    ariaRole: 'checkbox',

    
    onRe: /^on$/i,

    initComponent: function(){
        this.callParent(arguments);
        this.getManager().add(this);
    },

    initValue: function() {
        var me = this,
            checked = !!me.checked;

        
        me.originalValue = me.lastValue = checked;

        
        me.setValue(checked);
    },

    
    onRender : function(ct, position) {
        var me = this;
        Ext.applyIf(me.renderSelectors, {
            
            boxLabelEl: 'label.' + me.boxLabelCls
        });
        Ext.applyIf(me.subTplData, {
            boxLabel: me.boxLabel,
            boxLabelCls: me.boxLabelCls,
            boxLabelAlign: me.boxLabelAlign
        });

        me.callParent(arguments);
    },

    initEvents: function() {
        var me = this;
        me.callParent();
        me.mon(me.inputEl, 'click', me.onBoxClick, me);
    },

    
    onBoxClick: function(e) {
        var me = this;
        if (!me.disabled && !me.readOnly) {
            this.setValue(!this.checked);
        }
    },

    
    getRawValue: function() {
        return this.checked;
    },

    
    getValue: function() {
        return this.checked;
    },

    
    getSubmitValue: function() {
        var unchecked = this.uncheckedValue,
            uncheckedVal = Ext.isDefined(unchecked) ? unchecked : null;
        return this.checked ? this.inputValue : uncheckedVal;
    },

    
    setRawValue: function(value) {
        var me = this,
            inputEl = me.inputEl,
            inputValue = me.inputValue,
            checked = (value === true || value === 'true' || value === '1' ||
                      ((Ext.isString(value) && inputValue) ? value == inputValue : me.onRe.test(value)));

        if (inputEl) {
            inputEl.dom.setAttribute('aria-checked', checked);
            me[checked ? 'addCls' : 'removeCls'](me.checkedCls);
        }

        me.checked = me.rawValue = checked;
        return checked;
    },

    
    setValue: function(checked) {
        var me = this;

        
        
        
        
        if (Ext.isArray(checked)) {
            me.getManager().getByName(me.name).each(function(cb) {
                cb.setValue(Ext.Array.contains(checked, cb.inputValue));
            });
        } else {
            me.callParent(arguments);
        }

        return me;
    },

    
    valueToRaw: function(value) {
        
        return value;
    },

    
    onChange: function(newVal, oldVal) {
        var me = this,
            handler = me.handler;
        if (handler) {
            handler.call(me.scope || me, me, newVal);
        }
        me.callParent(arguments);
    },

    
    getManager: function() {
        return Ext.form.CheckboxManager;
    },

    onEnable: function() {
        var me = this,
            inputEl = me.inputEl;
        me.callParent();
        if (inputEl) {
            
            inputEl.dom.disabled = me.readOnly;
        }
    },

    setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        if (inputEl) {
            
            inputEl.dom.disabled = readOnly || me.disabled;
        }
        me.readOnly = readOnly;
    },

    
    getBodyNaturalWidth: function() {
        var me = this,
            bodyEl = me.bodyEl,
            ws = 'white-space',
            width;
        bodyEl.setStyle(ws, 'nowrap');
        width = bodyEl.getWidth();
        bodyEl.setStyle(ws, '');
        return width;
    }

});



Ext.define('Ext.layout.component.field.Trigger', {

    

    alias: ['layout.triggerfield'],

    extend: 'Ext.layout.component.field.Field',

    

    type: 'triggerfield',

    sizeBodyContents: function(width, height) {
        var me = this,
            owner = me.owner,
            inputEl = owner.inputEl,
            triggerWrap = owner.triggerWrap,
            triggerWidth = owner.getTriggerWidth();

        
        
        if (owner.hideTrigger || owner.readOnly || triggerWidth > 0) {
            
            
            me.setElementSize(inputEl, Ext.isNumber(width) ? width - triggerWidth : width);
    
            
            triggerWrap.setWidth(triggerWidth);
        }
    }
});

Ext.define('Ext.view.View', {
    extend: 'Ext.view.AbstractView',
    alternateClassName: 'Ext.view.View',
    alias: 'widget.dataview',

    inheritableStatics: {
        EventMap: {
            mousedown: 'MouseDown',
            mouseup: 'MouseUp',
            click: 'Click',
            dblclick: 'DblClick',
            contextmenu: 'ContextMenu',
            mouseover: 'MouseOver',
            mouseout: 'MouseOut',
            mouseenter: 'MouseEnter',
            mouseleave: 'MouseLeave',
            keydown: 'KeyDown'
        }
    },

    addCmpEvents: function() {
        this.addEvents(
            
            'beforeitemmousedown',
            
            'beforeitemmouseup',
            
            'beforeitemmouseenter',
            
            'beforeitemmouseleave',
            
            'beforeitemclick',
            
            'beforeitemdblclick',
            
            'beforeitemcontextmenu',
            
            'beforeitemkeydown',
            
            'itemmousedown',
            
            'itemmouseup',
            
            'itemmouseenter',
            
            'itemmouseleave',
            
            'itemclick',
            
            'itemdblclick',
            
            'itemcontextmenu',
            
            'itemkeydown',
            
            'beforecontainermousedown',
            
            'beforecontainermouseup',
            
            'beforecontainermouseover',
            
            'beforecontainermouseout',
            
            'beforecontainerclick',
            
            'beforecontainerdblclick',
            
            'beforecontainercontextmenu',
            
            'beforecontainerkeydown',
            
            'containermouseup',
            
            'containermouseover',
            
            'containermouseout',
            
            'containerclick',
            
            'containerdblclick',
            
            'containercontextmenu',
            
            'containerkeydown',

            
            'selectionchange',
            
            'beforeselect'
        );
    },
    
    afterRender: function(){
        var me = this,
            listeners;

        me.callParent();

        listeners = {
            scope: me,
            click: me.handleEvent,
            mousedown: me.handleEvent,
            mouseup: me.handleEvent,
            dblclick: me.handleEvent,
            contextmenu: me.handleEvent,
            mouseover: me.handleEvent,
            mouseout: me.handleEvent,
            keydown: me.handleEvent
        };

        me.mon(me.getTargetEl(), listeners);

        if (me.store) {
            me.bindStore(me.store, true);
        }
    },

    handleEvent: function(e) {
        if (this.processUIEvent(e) !== false) {
            this.processSpecialEvent(e);
        }
    },

    
    processItemEvent: Ext.emptyFn,
    processContainerEvent: Ext.emptyFn,
    processSpecialEvent: Ext.emptyFn,

    
    stillOverItem: function (event, overItem) {
        var nowOver;

        
        
        
        
        
        
        if (overItem && typeof(overItem.offsetParent) === "object") {
            
            
            nowOver = (event.type == 'mouseout') ? event.getRelatedTarget() : event.getTarget();
            return Ext.fly(overItem).contains(nowOver);
        }

        return false;
    },

    processUIEvent: function(e) {
        var me = this,
            item = e.getTarget(me.getItemSelector(), me.getTargetEl()),
            map = this.statics().EventMap,
            index, record,
            type = e.type,
            overItem = me.mouseOverItem,
            newType;

        if (!item) {
            if (type == 'mouseover' && me.stillOverItem(e, overItem)) {
                item = overItem;
            }

            
            if (type == 'keydown') {
                record = me.getSelectionModel().getLastSelected();
                if (record) {
                    item = me.getNode(record);
                }
            }
        }

        if (item) {
            index = me.indexOf(item);
            if (!record) {
                record = me.getRecord(item);
            }

            if (me.processItemEvent(record, item, index, e) === false) {
                return false;
            }

            newType = me.isNewItemEvent(item, e);
            if (newType === false) {
                return false;
            }

            if (
                (me['onBeforeItem' + map[newType]](record, item, index, e) === false) ||
                (me.fireEvent('beforeitem' + newType, me, record, item, index, e) === false) ||
                (me['onItem' + map[newType]](record, item, index, e) === false)
            ) {
                return false;
            }

            me.fireEvent('item' + newType, me, record, item, index, e);
        }
        else {
            if (
                (me.processContainerEvent(e) === false) ||
                (me['onBeforeContainer' + map[type]](e) === false) ||
                (me.fireEvent('beforecontainer' + type, me, e) === false) ||
                (me['onContainer' + map[type]](e) === false)
            ) {
                return false;
            }

            me.fireEvent('container' + type, me, e);
        }

        return true;
    },

    isNewItemEvent: function (item, e) {
        var me = this,
            overItem = me.mouseOverItem,
            type = e.type;

        switch (type) {
            case 'mouseover':
                if (item === overItem) {
                    return false;
                }
                me.mouseOverItem = item;
                return 'mouseenter';

            case 'mouseout':
                
                if (me.stillOverItem(e, overItem)) {
                    return false;
                }
                me.mouseOverItem = null;
                return 'mouseleave';
        }
        return type;
    },

    
    onItemMouseEnter: function(record, item, index, e) {
        if (this.trackOver) {
            this.highlightItem(item);
        }
    },

    
    onItemMouseLeave : function(record, item, index, e) {
        if (this.trackOver) {
            this.clearHighlight();
        }
    },

    
    onItemMouseDown: Ext.emptyFn,
    onItemMouseUp: Ext.emptyFn,
    onItemClick: Ext.emptyFn,
    onItemDblClick: Ext.emptyFn,
    onItemContextMenu: Ext.emptyFn,
    onItemKeyDown: Ext.emptyFn,
    onBeforeItemMouseDown: Ext.emptyFn,
    onBeforeItemMouseUp: Ext.emptyFn,
    onBeforeItemMouseEnter: Ext.emptyFn,
    onBeforeItemMouseLeave: Ext.emptyFn,
    onBeforeItemClick: Ext.emptyFn,
    onBeforeItemDblClick: Ext.emptyFn,
    onBeforeItemContextMenu: Ext.emptyFn,
    onBeforeItemKeyDown: Ext.emptyFn,

    
    onContainerMouseDown: Ext.emptyFn,
    onContainerMouseUp: Ext.emptyFn,
    onContainerMouseOver: Ext.emptyFn,
    onContainerMouseOut: Ext.emptyFn,
    onContainerClick: Ext.emptyFn,
    onContainerDblClick: Ext.emptyFn,
    onContainerContextMenu: Ext.emptyFn,
    onContainerKeyDown: Ext.emptyFn,
    onBeforeContainerMouseDown: Ext.emptyFn,
    onBeforeContainerMouseUp: Ext.emptyFn,
    onBeforeContainerMouseOver: Ext.emptyFn,
    onBeforeContainerMouseOut: Ext.emptyFn,
    onBeforeContainerClick: Ext.emptyFn,
    onBeforeContainerDblClick: Ext.emptyFn,
    onBeforeContainerContextMenu: Ext.emptyFn,
    onBeforeContainerKeyDown: Ext.emptyFn,

    
    highlightItem: function(item) {
        var me = this;
        me.clearHighlight();
        me.highlightedItem = item;
        Ext.fly(item).addCls(me.overItemCls);
    },

    
    clearHighlight: function() {
        var me = this,
            highlighted = me.highlightedItem;

        if (highlighted) {
            Ext.fly(highlighted).removeCls(me.overItemCls);
            delete me.highlightedItem;
        }
    },

    refresh: function() {
        this.clearHighlight();
        this.callParent(arguments);
    }
});

Ext.define('Ext.layout.component.BoundList', {
    extend: 'Ext.layout.component.Component',
    alias: 'layout.boundlist',

    type: 'component',

    beforeLayout: function() {
        return this.callParent(arguments) || this.owner.refreshed > 0;
    },

    onLayout : function(width, height) {
        var me = this,
            owner = me.owner,
            floating = owner.floating,
            el = owner.el,
            xy = el.getXY(),
            isNumber = Ext.isNumber,
            minWidth, maxWidth, minHeight, maxHeight,
            naturalWidth, naturalHeight, constrainedWidth, constrainedHeight, undef;

        if (floating) {
            
            el.setXY([-9999,-9999]);
        }

        
        me.setTargetSize(width, height);

        
        if (!isNumber(width)) {
            minWidth = owner.minWidth;
            maxWidth = owner.maxWidth;
            if (isNumber(minWidth) || isNumber(maxWidth)) {
                naturalWidth = el.getWidth();
                if (naturalWidth < minWidth) {
                    constrainedWidth = minWidth;
                }
                else if (naturalWidth > maxWidth) {
                    constrainedWidth = maxWidth;
                }
                if (constrainedWidth) {
                    me.setTargetSize(constrainedWidth);
                }
            }
        }
        
        if (!isNumber(height)) {
            minHeight = owner.minHeight;
            maxHeight = owner.maxHeight;
            if (isNumber(minHeight) || isNumber(maxHeight)) {
                naturalHeight = el.getHeight();
                if (naturalHeight < minHeight) {
                    constrainedHeight = minHeight;
                }
                else if (naturalHeight > maxHeight) {
                    constrainedHeight = maxHeight;
                }
                if (constrainedHeight) {
                    me.setTargetSize(undef, constrainedHeight);
                }
            }
        }

        if (floating) {
            
            el.setXY(xy);
        }
    },

    afterLayout: function() {
        var me = this,
            toolbar = me.owner.pagingToolbar;
        me.callParent();
        if (toolbar) {
            toolbar.doComponentLayout();
        }
    },

    setTargetSize : function(width, height) {
        var me = this,
            owner = me.owner,
            listHeight = null,
            toolbar;

        
        if (Ext.isNumber(height)) {
            listHeight = height - owner.el.getFrameWidth('tb');
            toolbar = owner.pagingToolbar;
            if (toolbar) {
                listHeight -= toolbar.getHeight();
            }
        }
        me.setElementSize(owner.listEl, null, listHeight);

        me.callParent(arguments);
    }

});


Ext.define('Ext.toolbar.TextItem', {
    extend: 'Ext.toolbar.Item',
    requires: ['Ext.XTemplate'],
    alias: 'widget.tbtext',
    alternateClassName: 'Ext.Toolbar.TextItem',
    
    
    text: '',
    
    renderTpl: '{text}',
    
    baseCls: Ext.baseCSSPrefix + 'toolbar-text',
    
    onRender : function() {
        Ext.apply(this.renderData, {
            text: this.text
        });
        this.callParent(arguments);
    },

    
    setText : function(t) {
        if (this.rendered) {
            this.el.update(t);
            this.ownerCt.doLayout(); 
        } else {
            this.text = t;
        }
    }
});

Ext.define('Ext.form.field.Trigger', {
    extend:'Ext.form.field.Text',
    alias: ['widget.triggerfield', 'widget.trigger'],
    requires: ['Ext.core.DomHelper', 'Ext.util.ClickRepeater', 'Ext.layout.component.field.Trigger'],
    alternateClassName: ['Ext.form.TriggerField', 'Ext.form.TwinTriggerField', 'Ext.form.Trigger'],

    fieldSubTpl: [
        '<input id="{id}" type="{type}" ',
            '<tpl if="name">name="{name}" </tpl>',
            '<tpl if="size">size="{size}" </tpl>',
            '<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>',
            'class="{fieldCls} {typeCls}" autocomplete="off" />',
        '<div class="{triggerWrapCls}" role="presentation">',
            '{triggerEl}',
            '<div class="{clearCls}" role="presentation"></div>',
        '</div>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    

    
    triggerBaseCls: Ext.baseCSSPrefix + 'form-trigger',

    
    triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap',

    
    hideTrigger: false,

    
    editable: true,

    
    readOnly: false,

    

    
    repeatTriggerClick: false,


    
    autoSize: Ext.emptyFn,
    
    monitorTab: true,
    
    mimicing: false,
    
    triggerIndexRe: /trigger-index-(\d+)/,

    componentLayout: 'triggerfield',

    initComponent: function() {
        this.wrapFocusCls = this.triggerWrapCls + '-focus';
        this.callParent(arguments);
    },

    
    onRender: function(ct, position) {
        var me = this,
            triggerCls,
            triggerBaseCls = me.triggerBaseCls,
            triggerWrapCls = me.triggerWrapCls,
            triggerConfigs = [],
            i;

        
        
        
        
        if (!me.trigger1Cls) {
            me.trigger1Cls = me.triggerCls;
        }

        
        for (i = 0; (triggerCls = me['trigger' + (i + 1) + 'Cls']) || i < 1; i++) {
            triggerConfigs.push({
                cls: [Ext.baseCSSPrefix + 'trigger-index-' + i, triggerBaseCls, triggerCls].join(' '),
                role: 'button'
            });
        }
        triggerConfigs[i - 1].cls += ' ' + triggerBaseCls + '-last';

        Ext.applyIf(me.renderSelectors, {
            
            triggerWrap: '.' + triggerWrapCls
        });
        Ext.applyIf(me.subTplData, {
            triggerWrapCls: triggerWrapCls,
            triggerEl: Ext.core.DomHelper.markup(triggerConfigs),
            clearCls: me.clearCls
        });

        me.callParent(arguments);

        
        me.triggerEl = Ext.select('.' + triggerBaseCls, true, me.triggerWrap.dom);

        me.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
        me.initTrigger();
    },

    onEnable: function() {
        this.callParent();
        this.triggerWrap.unmask();
    },
    
    onDisable: function() {
        this.callParent();
        this.triggerWrap.mask();
    },
    
    afterRender: function() {
        this.callParent();
        this.updateEditState();
    },

    updateEditState: function() {
        var me = this,
            inputEl = me.inputEl,
            triggerWrap = me.triggerWrap,
            noeditCls = Ext.baseCSSPrefix + 'trigger-noedit',
            displayed,
            readOnly;

        if (me.rendered) {
            if (me.readOnly) {
                inputEl.addCls(noeditCls);
                readOnly = true;
                displayed = false;
            } else {
                if (me.editable) {
                    inputEl.removeCls(noeditCls);
                    readOnly = false;
                } else {
                    inputEl.addCls(noeditCls);
                    readOnly = true;
                }
                displayed = !me.hideTrigger;
            }

            triggerWrap.setDisplayed(displayed);
            inputEl.dom.readOnly = readOnly;
            me.doComponentLayout();
        }
    },

    
    getTriggerWidth: function() {
        var me = this,
            triggerWrap = me.triggerWrap,
            totalTriggerWidth = 0;
        if (triggerWrap && !me.hideTrigger && !me.readOnly) {
            me.triggerEl.each(function(trigger) {
                totalTriggerWidth += trigger.getWidth();
            });
            totalTriggerWidth += me.triggerWrap.getFrameWidth('lr');
        }
        return totalTriggerWidth;
    },

    setHideTrigger: function(hideTrigger) {
        if (hideTrigger != this.hideTrigger) {
            this.hideTrigger = hideTrigger;
            this.updateEditState();
        }
    },

    
    setEditable: function(editable) {
        if (editable != this.editable) {
            this.editable = editable;
            this.updateEditState();
        }
    },

    
    setReadOnly: function(readOnly) {
        if (readOnly != this.readOnly) {
            this.readOnly = readOnly;
            this.updateEditState();
        }
    },

    
    initTrigger: function() {
        var me = this,
            triggerWrap = me.triggerWrap,
            triggerEl = me.triggerEl;

        if (me.repeatTriggerClick) {
            me.triggerRepeater = Ext.create('Ext.util.ClickRepeater', triggerWrap, {
                preventDefault: true,
                handler: function(cr, e) {
                    me.onTriggerWrapClick(e);
                }
            });
        } else {
            me.mon(me.triggerWrap, 'click', me.onTriggerWrapClick, me);
        }

        triggerEl.addClsOnOver(me.triggerBaseCls + '-over');
        triggerEl.each(function(el, c, i) {
            el.addClsOnOver(me['trigger' + (i + 1) + 'Cls'] + '-over');
        });
        triggerEl.addClsOnClick(me.triggerBaseCls + '-click');
        triggerEl.each(function(el, c, i) {
            el.addClsOnClick(me['trigger' + (i + 1) + 'Cls'] + '-click');
        });
    },

    
    onDestroy: function() {
        var me = this;
        Ext.destroyMembers(me, 'triggerRepeater', 'triggerWrap', 'triggerEl');
        delete me.doc;
        me.callParent();
    },

    
    onFocus: function() {
        var me = this;
        this.callParent();
        if (!me.mimicing) {
            me.bodyEl.addCls(me.wrapFocusCls);
            me.mimicing = true;
            me.mon(me.doc, 'mousedown', me.mimicBlur, me, {
                delay: 10
            });
            if (me.monitorTab) {
                me.on('specialkey', me.checkTab, me);
            }
        }
    },

    
    checkTab: function(me, e) {
        if (!this.ignoreMonitorTab && e.getKey() == e.TAB) {
            this.triggerBlur();
        }
    },

    
    onBlur: Ext.emptyFn,

    
    mimicBlur: function(e) {
        if (!this.isDestroyed && !this.bodyEl.contains(e.target) && this.validateBlur(e)) {
            this.triggerBlur();
        }
    },

    
    triggerBlur: function() {
        var me = this;
        me.mimicing = false;
        me.mun(me.doc, 'mousedown', me.mimicBlur, me);
        if (me.monitorTab && me.inputEl) {
            me.un('specialkey', me.checkTab, me);
        }
        Ext.form.field.Trigger.superclass.onBlur.call(me);
        if (me.bodyEl) {
            me.bodyEl.removeCls(me.wrapFocusCls);
        }
    },

    beforeBlur: Ext.emptyFn,

    
    
    validateBlur: function(e) {
        return true;
    },

    
    
    
    onTriggerWrapClick: function(e) {
        var me = this,
            t = e && e.getTarget('.' + Ext.baseCSSPrefix + 'form-trigger', null),
            match = t && t.className.match(me.triggerIndexRe),
            idx,
            triggerClickMethod;

        if (match && !me.readOnly) {
            idx = parseInt(match[1], 10);
            triggerClickMethod = me['onTrigger' + (idx + 1) + 'Click'] || me.onTriggerClick;
            if (triggerClickMethod) {
                triggerClickMethod.call(me, e);
            }
        }
    },

    
    onTriggerClick: Ext.emptyFn

    
    
    
});


Ext.define('Ext.form.field.Picker', {
    extend: 'Ext.form.field.Trigger',
    alias: 'widget.pickerfield',
    alternateClassName: 'Ext.form.Picker',
    requires: ['Ext.util.KeyNav'],

    
    matchFieldWidth: true,

    
    pickerAlign: 'tl-bl?',

    

    
    openCls: Ext.baseCSSPrefix + 'pickerfield-open',

    

    
    editable: true,


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

        
        this.addEvents(
            
            'expand',
            
            'collapse',
            
            'select'
        );
    },


    initEvents: function() {
        var me = this;
        me.callParent();

        
        me.keyNav = Ext.create('Ext.util.KeyNav', me.inputEl, {
            down: function() {
                if (!me.isExpanded) {
                    
                    
                    me.onTriggerClick();
                }
            },
            esc: me.collapse,
            scope: me,
            forceKeyDown: true
        });

        
        if (!me.editable) {
            me.mon(me.inputEl, 'click', me.onTriggerClick, me);
        }

        
        if (Ext.isGecko) {
            me.inputEl.dom.setAttribute('autocomplete', 'off');
        }
    },


    
    expand: function() {
        var me = this,
            bodyEl, picker, collapseIf;

        if (me.rendered && !me.isExpanded && !me.isDestroyed) {
            bodyEl = me.bodyEl;
            picker = me.getPicker();
            collapseIf = me.collapseIf;

            
            picker.show();
            me.isExpanded = true;
            me.alignPicker();
            bodyEl.addCls(me.openCls);

            
            me.mon(Ext.getDoc(), {
                mousewheel: collapseIf,
                mousedown: collapseIf,
                scope: me
            });
            Ext.EventManager.onWindowResize(me.alignPicker, me);
            me.fireEvent('expand', me);
            me.onExpand();
        }
    },

    onExpand: Ext.emptyFn,

    
    alignPicker: function() {
        var me = this,
            picker, isAbove,
            aboveSfx = '-above';

        if (this.isExpanded) {
            picker = me.getPicker();
            if (me.matchFieldWidth) {
                
                picker.setSize(me.bodyEl.getWidth(), picker.store && picker.store.getCount() ? null : 0);
            }
            if (picker.isFloating()) {
                picker.alignTo(me.inputEl, me.pickerAlign, me.pickerOffset);

                
                
                isAbove = picker.el.getY() < me.inputEl.getY();
                me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
                picker.el[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
            }
        }
    },

    
    collapse: function() {
        if (this.isExpanded && !this.isDestroyed) {
            var me = this,
                openCls = me.openCls,
                picker = me.picker,
                doc = Ext.getDoc(),
                collapseIf = me.collapseIf,
                aboveSfx = '-above';

            
            picker.hide();
            me.isExpanded = false;

            
            me.bodyEl.removeCls([openCls, openCls + aboveSfx]);
            picker.el.removeCls(picker.baseCls + aboveSfx);

            
            doc.un('mousewheel', collapseIf, me);
            doc.un('mousedown', collapseIf, me);
            Ext.EventManager.removeResizeListener(me.alignPicker, me);
            me.fireEvent('collapse', me);
            me.onCollapse();
        }
    },

    onCollapse: Ext.emptyFn,


    
    collapseIf: function(e) {
        var me = this;
        if (!me.isDestroyed && !e.within(me.bodyEl, false, true) && !e.within(me.picker.el, false, true)) {
            me.collapse();
        }
    },

    
    getPicker: function() {
        var me = this;
        return me.picker || (me.picker = me.createPicker());
    },

    
    createPicker: Ext.emptyFn,

    
    onTriggerClick: function() {
        var me = this;
        if (!me.readOnly && !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                me.expand();
            }
            me.inputEl.focus();
        }
    },

    mimicBlur: function(e) {
        var me = this,
            picker = me.picker;
        
        if (!picker || !e.within(picker.el, false, true)) {
            me.callParent(arguments);
        }
    },

    onDestroy : function(){
        var me = this;
        Ext.EventManager.removeResizeListener(me.alignPicker, me);
        Ext.destroy(me.picker, me.keyNav);
        me.callParent();
    }

});



Ext.define('Ext.form.field.Spinner', {
    extend: 'Ext.form.field.Trigger',
    alias: 'widget.spinnerfield',
    alternateClassName: 'Ext.form.Spinner',
    requires: ['Ext.util.KeyNav'],

    trigger1Cls: Ext.baseCSSPrefix + 'form-spinner-up',
    trigger2Cls: Ext.baseCSSPrefix + 'form-spinner-down',

    
    spinUpEnabled: true,

    
    spinDownEnabled: true,

    
    keyNavEnabled: true,

    
    mouseWheelEnabled: true,

    
    repeatTriggerClick: true,

    
    onSpinUp: Ext.emptyFn,

    
    onSpinDown: Ext.emptyFn,

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

        this.addEvents(
            
            'spin',

            
            'spinup',

            
            'spindown'
        );
    },

    
    onRender: function() {
        var me = this,
            triggers;

        me.callParent(arguments);
        triggers = me.triggerEl;

        
        me.spinUpEl = triggers.item(0);
        
        me.spinDownEl = triggers.item(1);

        
        me.setSpinUpEnabled(me.spinUpEnabled);
        me.setSpinDownEnabled(me.spinDownEnabled);

        
        if (me.keyNavEnabled) {
            me.spinnerKeyNav = Ext.create('Ext.util.KeyNav', me.inputEl, {
                scope: me,
                up: me.spinUp,
                down: me.spinDown
            });
        }

        
        if (me.mouseWheelEnabled) {
            me.mon(me.bodyEl, 'mousewheel', me.onMouseWheel, me);
        }
    },

    
    getTriggerWidth: function() {
        return this.hideTrigger || this.readOnly ? 0 : this.spinUpEl.getWidth() + this.triggerWrap.getFrameWidth('lr');
    },

    
    onTrigger1Click: function() {
        this.spinUp();
    },

    
    onTrigger2Click: function() {
        this.spinDown();
    },

    
    spinUp: function() {
        var me = this;
        if (me.spinUpEnabled && !me.disabled) {
            me.fireEvent('spin', me, 'up');
            me.fireEvent('spinup', me);
            me.onSpinUp();
        }
    },

    
    spinDown: function() {
        var me = this;
        if (me.spinDownEnabled && !me.disabled) {
            me.fireEvent('spin', me, 'down');
            me.fireEvent('spindown', me);
            me.onSpinDown();
        }
    },

    
    setSpinUpEnabled: function(enabled) {
        var me = this,
            wasEnabled = me.spinUpEnabled;
        me.spinUpEnabled = enabled;
        if (wasEnabled !== enabled && me.rendered) {
            me.spinUpEl[enabled ? 'removeCls' : 'addCls'](me.trigger1Cls + '-disabled');
        }
    },

    
    setSpinDownEnabled: function(enabled) {
        var me = this,
            wasEnabled = me.spinDownEnabled;
        me.spinDownEnabled = enabled;
        if (wasEnabled !== enabled && me.rendered) {
            me.spinDownEl[enabled ? 'removeCls' : 'addCls'](me.trigger2Cls + '-disabled');
        }
    },

    
    onMouseWheel: function(e) {
        var me = this,
            delta;
        if (me.hasFocus) {
            delta = e.getWheelDelta();
            if (delta > 0) {
                me.spinUp();
            }
            else if (delta < 0) {
                me.spinDown();
            }
            e.stopEvent();
        }
    },

    onDestroy: function() {
        Ext.destroyMembers(this, 'spinnerKeyNav', 'spinUpEl', 'spinDownEl');
        this.callParent();
    }

});

Ext.define('Ext.form.field.Number', {
    extend:'Ext.form.field.Spinner',
    alias: 'widget.numberfield',
    alternateClassName: ['Ext.form.NumberField', 'Ext.form.Number'],

    
    

    
    allowDecimals : true,

    
    decimalSeparator : '.',

    
    decimalPrecision : 2,

    
    minValue: Number.NEGATIVE_INFINITY,

    
    maxValue: Number.MAX_VALUE,

    
    step: 1,

    
    minText : 'The minimum value for this field is {0}',

    
    maxText : 'The maximum value for this field is {0}',

    
    nanText : '{0} is not a valid number',

    
    negativeText : 'The value cannot be negative',

    
    baseChars : '0123456789',

    
    autoStripChars: false,

    initComponent: function() {
        var me = this,
            allowed;

        me.callParent();

        me.setMinValue(me.minValue);
        me.setMaxValue(me.maxValue);

        
        if (me.disableKeyFilter !== true) {
            allowed = me.baseChars + '';
            if (me.allowDecimals) {
                allowed += me.decimalSeparator;
            }
            if (me.minValue < 0) {
                allowed += '-';
            }
            allowed = Ext.String.escapeRegex(allowed);
            me.maskRe = new RegExp('[' + allowed + ']');
            if (me.autoStripChars) {
                me.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi');
            }
        }
    },

    
    getErrors: function(value) {
        var me = this,
            errors = me.callParent(arguments),
            format = Ext.String.format,
            num;

        value = Ext.isDefined(value) ? value : this.processRawValue(this.getRawValue());

        if (value.length < 1) { 
             return errors;
        }

        value = String(value).replace(me.decimalSeparator, '.');

        if(isNaN(value)){
            errors.push(format(me.nanText, value));
        }

        num = me.parseValue(value);

        if (me.minValue === 0 && num < 0) {
            errors.push(this.negativeText);
        }
        else if (num < me.minValue) {
            errors.push(format(me.minText, me.minValue));
        }

        if (num > me.maxValue) {
            errors.push(format(me.maxText, me.maxValue));
        }


        return errors;
    },

    rawToValue: function(rawValue) {
        return this.fixPrecision(this.parseValue(rawValue)) || rawValue || null;
    },

    valueToRaw: function(value) {
        var me = this,
            decimalSeparator = me.decimalSeparator;
        value = me.parseValue(value);
        value = me.fixPrecision(value);
        value = Ext.isNumber(value) ? value : parseFloat(String(value).replace(decimalSeparator, '.'));
        value = isNaN(value) ? '' : String(value).replace('.', decimalSeparator);
        return value;
    },

    onChange: function() {
        var me = this,
            value = me.getValue(),
            valueIsNull = value === null;

        me.callParent(arguments);

        
        me.setSpinUpEnabled(valueIsNull || value < me.maxValue);
        me.setSpinDownEnabled(valueIsNull || value > me.minValue);
    },

    
    setMinValue : function(value) {
        this.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY);
    },

    
    setMaxValue: function(value) {
        this.maxValue = Ext.Number.from(value, Number.MAX_VALUE);
    },

    
    parseValue : function(value) {
        value = parseFloat(String(value).replace(this.decimalSeparator, '.'));
        return isNaN(value) ? null : value;
    },

    
    fixPrecision : function(value) {
        var me = this,
            nan = isNaN(value),
            precision = me.decimalPrecision;

        if (nan || !value) {
            return nan ? '' : value;
        } else if (!me.allowDecimals || precision <= 0) {
            precision = 0;
        }

        return parseFloat(Ext.Number.toFixed(parseFloat(value), precision));
    },

    beforeBlur : function() {
        var me = this,
            v = me.parseValue(me.getRawValue());

        if (!Ext.isEmpty(v)) {
            me.setValue(v);
        }
    },

    onSpinUp: function() {
        var me = this;
        if (!me.readOnly) {
            me.setValue(Ext.Number.constrain(me.getValue() + me.step, me.minValue, me.maxValue));
        }
    },

    onSpinDown: function() {
        var me = this;
        if (!me.readOnly) {
            me.setValue(Ext.Number.constrain(me.getValue() - me.step, me.minValue, me.maxValue));
        }
    }
});


Ext.define('Ext.toolbar.Paging', {
    extend: 'Ext.toolbar.Toolbar',
    alias: 'widget.pagingtoolbar',
    alternateClassName: 'Ext.PagingToolbar',
    requires: ['Ext.toolbar.TextItem', 'Ext.form.field.Number'],
    
    
    displayInfo: false,
    
    prependButtons: false,
    
    displayMsg : 'Displaying {0} - {1} of {2}',
    
    emptyMsg : 'No data to display',
    
    beforePageText : 'Page',
    
    afterPageText : 'of {0}',
    
    firstText : 'First Page',
    
    prevText : 'Previous Page',
    
    nextText : 'Next Page',
    
    lastText : 'Last Page',
    
    refreshText : 'Refresh',
    
    inputItemWidth : 30,
    
    
    getPagingItems: function() {
        var me = this;
        
        return [{
            itemId: 'first',
            tooltip: me.firstText,
            overflowText: me.firstText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-first',
            disabled: true,
            handler: me.moveFirst,
            scope: me
        },{
            itemId: 'prev',
            tooltip: me.prevText,
            overflowText: me.prevText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-prev',
            disabled: true,
            handler: me.movePrevious,
            scope: me
        },
        '-',
        me.beforePageText,
        {
            xtype: 'numberfield',
            itemId: 'inputItem',
            name: 'inputItem',
            cls: Ext.baseCSSPrefix + 'tbar-page-number',
            allowDecimals: false,
            minValue: 1,
            hideTrigger: true,
            enableKeyEvents: true,
            selectOnFocus: true,
            submitValue: false,
            width: me.inputItemWidth,
            margins: '-1 2 3 2',
            listeners: {
                scope: me,
                keydown: me.onPagingKeyDown,
                blur: me.onPagingBlur
            }
        },{
            xtype: 'tbtext',
            itemId: 'afterTextItem',
            text: Ext.String.format(me.afterPageText, 1)
        },
        '-',
        {
            itemId: 'next',
            tooltip: me.nextText,
            overflowText: me.nextText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-next',
            disabled: true,
            handler: me.moveNext,
            scope: me
        },{
            itemId: 'last',
            tooltip: me.lastText,
            overflowText: me.lastText,
            iconCls: Ext.baseCSSPrefix + 'tbar-page-last',
            disabled: true,
            handler: me.moveLast,
            scope: me
        },
        '-',
        {
            itemId: 'refresh',
            tooltip: me.refreshText,
            overflowText: me.refreshText,
            iconCls: Ext.baseCSSPrefix + 'tbar-loading',
            handler: me.doRefresh,
            scope: me
        }];
    },

    initComponent : function(){
        var me = this,
            pagingItems = me.getPagingItems(),
            userItems   = me.items || me.buttons || [];
            
        if (me.prependButtons) {
            me.items = userItems.concat(pagingItems);
        } else {
            me.items = pagingItems.concat(userItems);
        }
        delete me.buttons;
        
        if (me.displayInfo) {
            me.items.push('->');
            me.items.push({xtype: 'tbtext', itemId: 'displayItem'});
        }
        
        me.callParent();
        
        me.addEvents(
            
            'change',
            
            'beforechange'
        );
        me.on('afterlayout', me.onLoad, me, {single: true});

        me.bindStore(me.store, true);
    },
    
    updateInfo : function(){
        var me = this,
            displayItem = me.child('#displayItem'),
            store = me.store,
            pageData = me.getPageData(),
            count, msg;

        if (displayItem) {
            count = store.getCount();
            if (count === 0) {
                msg = me.emptyMsg;
            } else {
                msg = Ext.String.format(
                    me.displayMsg,
                    pageData.fromRecord,
                    pageData.toRecord,
                    pageData.total
                );
            }
            displayItem.setText(msg);
            me.doComponentLayout();
        }
    },

    
    onLoad : function(){
        var me = this,
            pageData,
            currPage,
            pageCount,
            afterText;
            
        if (!me.rendered) {
            return;
        }

        pageData = me.getPageData();
        currPage = pageData.currentPage;
        pageCount = pageData.pageCount;
        afterText = Ext.String.format(me.afterPageText, isNaN(pageCount) ? 1 : pageCount);

        me.child('#afterTextItem').setText(afterText);
        me.child('#inputItem').setValue(currPage);
        me.child('#first').setDisabled(currPage === 1);
        me.child('#prev').setDisabled(currPage === 1);
        me.child('#next').setDisabled(currPage === pageCount);
        me.child('#last').setDisabled(currPage === pageCount);
        me.child('#refresh').enable();
        me.updateInfo();
        me.fireEvent('change', me, pageData);
    },

    
    getPageData : function(){
        var store = this.store,
            totalCount = store.getTotalCount();
            
        return {
            total : totalCount,
            currentPage : store.currentPage,
            pageCount: Math.ceil(totalCount / store.pageSize),
            
            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
            
        };
    },

    
    onLoadError : function(){
        if (!this.rendered) {
            return;
        }
        this.child('#refresh').enable();
    },

    
    readPageFromInput : function(pageData){
        var v = this.child('#inputItem').getValue(),
            pageNum = parseInt(v, 10);
            
        if (!v || isNaN(pageNum)) {
            this.child('#inputItem').setValue(pageData.currentPage);
            return false;
        }
        return pageNum;
    },

    onPagingFocus : function(){
        this.child('#inputItem').select();
    },

    
    onPagingBlur : function(e){
        var curPage = this.getPageData().currentPage;
        this.child('#inputItem').setValue(curPage);
    },

    
    onPagingKeyDown : function(field, e){
        var k = e.getKey(),
            pageData = this.getPageData(),
            increment = e.shiftKey ? 10 : 1,
            pageNum,
            me = this;

        if (k == e.RETURN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum !== false) {
                pageNum = Math.min(Math.max(1, pageNum), pageData.total);
                if(me.fireEvent('beforechange', me, pageNum) !== false){
                    me.store.loadPage(pageNum);
                }
            }
        } else if (k == e.HOME || k == e.END) {
            e.stopEvent();
            pageNum = k == e.HOME ? 1 : pageData.pageCount;
            field.setValue(pageNum);
        } else if (k == e.UP || k == e.PAGEUP || k == e.DOWN || k == e.PAGEDOWN) {
            e.stopEvent();
            pageNum = me.readPageFromInput(pageData);
            if (pageNum) {
                if (k == e.DOWN || k == e.PAGEDOWN) {
                    increment *= -1;
                }
                pageNum += increment;
                if (pageNum >= 1 && pageNum <= pageData.pages) {
                    field.setValue(pageNum);
                }
            }
        }
    },

    
    beforeLoad : function(){
        if(this.rendered && this.refresh){
            this.refresh.disable();
        }
    },

    
    doLoad : function(start){
        if(this.fireEvent('beforechange', this, o) !== false){
            this.store.load();
        }
    },

    
    moveFirst : function(){
        var me = this;
        if(me.fireEvent('beforechange', me, 1) !== false){
            me.store.loadPage(1);
        }
    },

    
    movePrevious : function(){
        var me = this,
            prev = me.store.currentPage - 1;
        
        if(me.fireEvent('beforechange', me, prev) !== false){
            me.store.previousPage();
        }
    },

    
    moveNext : function(){
        var me = this;        
        if(me.fireEvent('beforechange', me, me.store.currentPage + 1) !== false){
            me.store.nextPage();
        }
    },

    
    moveLast : function(){
        var me = this, 
            last = this.getPageData().pageCount;
        
        if(me.fireEvent('beforechange', me, last) !== false){
            me.store.loadPage(last);
        }
    },

    
    doRefresh : function(){
        var me = this,
            current = me.store.currentPage;
        
        if(me.fireEvent('beforechange', me, current) !== false){
            me.store.loadPage(current);
        }
    },

    
    bindStore : function(store, initial){
        var me = this;
        
        if (!initial && me.store) {
            if(store !== me.store && me.store.autoDestroy){
                me.store.destroy();
            }else{
                me.store.un('beforeload', me.beforeLoad, me);
                me.store.un('load', me.onLoad, me);
                me.store.un('exception', me.onLoadError, me);
            }
            if(!store){
                me.store = null;
            }
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            store.on({
                scope: me,
                beforeload: me.beforeLoad,
                load: me.onLoad,
                exception: me.onLoadError
            });
        }
        me.store = store;
    },

    
    unbind : function(store){
        this.bindStore(null);
    },

    
    bind : function(store){
        this.bindStore(store);
    },

    
    onDestroy : function(){
        this.bindStore(null);
        this.callParent();
    }
});


Ext.define('Ext.view.BoundList', {
    extend: 'Ext.view.View',
    alias: 'widget.boundlist',
    alternateClassName: 'Ext.BoundList',
    requires: ['Ext.layout.component.BoundList', 'Ext.toolbar.Paging'],

    
    pageSize: 0,

    

    
    autoScroll: true,
    baseCls: Ext.baseCSSPrefix + 'boundlist',
    listItemCls: '',
    shadow: false,
    trackOver: true,
    refreshed: 0,

    ariaRole: 'listbox',

    componentLayout: 'boundlist',

    renderTpl: ['<div class="list-ct"></div>'],

    initComponent: function() {
        var me = this,
            baseCls = me.baseCls,
            itemCls = baseCls + '-item';
        me.itemCls = itemCls;
        me.selectedItemCls = baseCls + '-selected';
        me.overItemCls = baseCls + '-item-over';
        me.itemSelector = "." + itemCls;

        if (me.floating) {
            me.addCls(baseCls + '-floating');
        }

        
        
        me.tpl = Ext.create('Ext.XTemplate', 
            '<ul><tpl for=".">',
                '<li role="option" class="' + itemCls + '">' + me.getInnerTpl(me.displayField) + '</li>',
            '</tpl></ul>'
        );

        if (me.pageSize) {
            me.pagingToolbar = me.createPagingToolbar();
        }

        me.callParent();

        Ext.applyIf(me.renderSelectors, {
            listEl: '.list-ct'
        });
    },

    createPagingToolbar: function() {
        return Ext.widget('pagingtoolbar', {
            pageSize: this.pageSize,
            store: this.store,
            border: false
        });
    },

    onRender: function() {
        var me = this,
            toolbar = me.pagingToolbar;
        me.callParent(arguments);
        if (toolbar) {
            toolbar.render(me.el);
        }
    },

    bindStore : function(store, initial) {
        var me = this,
            toolbar = me.pagingToolbar;
        me.callParent(arguments);
        if (toolbar) {
            toolbar.bindStore(store, initial);
        }
    },

    getTargetEl: function() {
        return this.listEl || this.el;
    },

    getInnerTpl: function(displayField) {
        return '{' + displayField + '}';
    },

    refresh: function() {
        var me = this;
        me.callParent();
        if (me.isVisible()) {
            me.refreshed++;
            me.doComponentLayout();
            me.refreshed--;
        }
    },
    
    initAria: function() {
        this.callParent();
        
        var selModel = this.getSelectionModel(),
            mode     = selModel.getSelectionMode(),
            actionEl = this.getActionEl();
        
        
        if (mode !== 'SINGLE') {
            actionEl.dom.setAttribute('aria-multiselectable', true);
        }
    },

    onDestroy: function() {
        Ext.destroyMembers(this, 'pagingToolbar', 'listEl');
        this.callParent();
    }
});


Ext.define('Ext.view.BoundListKeyNav', {
    extend: 'Ext.util.KeyNav',
    requires: 'Ext.view.BoundList',

    

    constructor: function(el, config) {
        var me = this;
        me.boundList = config.boundList;
        me.callParent([el, Ext.apply({}, config, me.defaultHandlers)]);
    },

    defaultHandlers: {
        up: function() {
            var me = this,
                boundList = me.boundList,
                allItems = boundList.all,
                oldItem = boundList.highlightedItem,
                oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1,
                newItemIdx = oldItemIdx > 0 ? oldItemIdx - 1 : allItems.getCount() - 1; 
            me.highlightAt(newItemIdx);
        },

        down: function() {
            var me = this,
                boundList = me.boundList,
                allItems = boundList.all,
                oldItem = boundList.highlightedItem,
                oldItemIdx = oldItem ? boundList.indexOf(oldItem) : -1,
                newItemIdx = oldItemIdx < allItems.getCount() - 1 ? oldItemIdx + 1 : 0; 
            me.highlightAt(newItemIdx);
        },

        pageup: function() {
            
        },

        pagedown: function() {
            
        },

        home: function() {
            this.highlightAt(0);
        },

        end: function() {
            var me = this;
            me.highlightAt(me.boundList.all.getCount() - 1);
        },

        enter: function(e) {
            this.selectHighlighted(e);
        }
    },

    
    highlightAt: function(index) {
        var boundList = this.boundList,
            item = boundList.all.item(index);
        if (item) {
            item = item.dom;
            boundList.highlightItem(item);
            boundList.getTargetEl().scrollChildIntoView(item, false);
        }
    },

    
    selectHighlighted: function(e) {
        var me = this,
            boundList = me.boundList,
            highlighted = boundList.highlightedItem,
            selModel = boundList.getSelectionModel();
        if (highlighted) {
            selModel.selectWithEvent(boundList.getRecord(highlighted), e);
        }
    }

});

Ext.define('Ext.form.field.ComboBox', {
    extend:'Ext.form.field.Picker',
    requires: ['Ext.util.DelayedTask', 'Ext.EventObject', 'Ext.view.BoundList', 'Ext.view.BoundListKeyNav', 'Ext.data.StoreManager'],
    alternateClassName: 'Ext.form.ComboBox',
    alias: ['widget.combobox', 'widget.combo'],

    
    triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',

    

    
    multiSelect: false,

    
    delimiter: ', ',

    
    displayField: 'text',

    

    
    triggerAction: 'all',

    
    allQuery: '',

    
    queryParam: 'query',

    
    queryMode: 'remote',

    queryCaching: true,

    
    pageSize: 0,

    

    

    
    autoSelect: true,

    
    typeAhead: false,

    
    typeAheadDelay: 250,

    
    selectOnTab: true,

    
    forceSelection: false,

    

    

    
    defaultListConfig: {
        emptyText: '',
        loadingText: 'Loading...',
        loadingHeight: 70,
        minWidth: 70,
        maxHeight: 300,
        shadow: 'sides'
    },

    

    

    
    ignoreSelection: 0,

    initComponent: function() {
        var me = this,
            isDefined = Ext.isDefined,
            store = me.store,
            transform = me.transform,
            transformSelect, isLocalMode;

        if (!store && !transform) {
            Ext.Error.raise('Either a valid store, or a HTML select to transform, must be configured on the combo.');
        }
        if (me.typeAhead && me.multiSelect) {
            Ext.Error.raise('typeAhead and multiSelect are mutually exclusive options -- please remove one of them.');
        }
        if (me.typeAhead && !me.editable) {
            Ext.Error.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.');
        }
        if (me.selectOnFocus && !me.editable) {
            Ext.Error.raise('If selectOnFocus is enabled the combo must be editable: true -- please change one of those settings.');
        }

        this.addEvents(
            

            
            'beforequery',

            
            'select'
        );

        
        if (!store && transform) {
            transformSelect = Ext.getDom(transform);
            if (transformSelect) {
                store = Ext.Array.map(Ext.Array.from(transformSelect.options), function(option) {
                    return [option.value, option.text];
                });
                if (!me.name) {
                    me.name = transformSelect.name;
                }
                if (!('value' in me)) {
                    me.value = transformSelect.value;
                }
            }
        }

        me.bindStore(store, true);
        store = me.store;
        if (store.autoCreated) {
            me.queryMode = 'local';
            me.valueField = me.displayField = 'field1';
            if (!store.expanded) {
                me.displayField = 'field2';
            }
        }


        if (!isDefined(me.valueField)) {
            me.valueField = me.displayField;
        }

        isLocalMode = me.queryMode === 'local';
        if (!isDefined(me.queryDelay)) {
            me.queryDelay = isLocalMode ? 10 : 500;
        }
        if (!isDefined(me.minChars)) {
            me.minChars = isLocalMode ? 0 : 4;
        }

        if (!me.displayTpl) {
            me.displayTpl = Ext.create('Ext.XTemplate',
                '<tpl for=".">' +
                    '{[typeof values === "string" ? values : values.' + me.displayField + ']}' +
                    '<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
                '</tpl>'
            );
        } else if (Ext.isString(me.displayTpl)) {
            me.displayTpl = Ext.create('Ext.XTemplate', me.displayTpl);
        }

        me.callParent();

        me.doQueryTask = Ext.create('Ext.util.DelayedTask', me.doRawQuery, me);

        
        if (me.store.getCount() > 0) {
            me.setValue(me.value);
        }

        
        if (transformSelect) {
            me.render(transformSelect.parentNode, transformSelect);
            Ext.removeNode(transformSelect);
            delete me.renderTo;
        }
    },

    beforeBlur: function() {
        var me = this;
        me.doQueryTask.cancel();
        if (me.forceSelection) {
            me.assertValue();
        } else {
            me.collapse();
        }
    },

    
    assertValue: function() {
        var me = this,
            value = me.getRawValue(),
            rec;

        if (me.multiSelect) {
            
            
            if (value !== me.getDisplayValue()) {
                me.setValue(me.lastSelection);
            }
        } else {
            
            
            rec = me.findRecordByDisplay(value);
            if (rec) {
                me.select(rec);
            } else {
                me.setValue(me.lastSelection);
            }
        }
        me.collapse();
    },

    onTypeAhead: function() {
        var me = this,
            displayField = me.displayField,
            record = me.store.findRecord(displayField, me.getRawValue()),
            boundList = me.getPicker(),
            newValue, len, selStart;

        if (record) {
            newValue = record.get(displayField);
            len = newValue.length;
            selStart = me.getRawValue().length;

            boundList.highlightItem(boundList.getNode(record));

            if (selStart !== 0 && selStart !== len) {
                me.setRawValue(newValue);
                me.selectText(selStart, newValue.length);
            }
        }
    },

    
    
    resetToDefault: function() {

    },

    bindStore: function(store, initial) {
        var me = this,
            oldStore = me.store;

        
        
        if (oldStore && !initial) {
            if (oldStore !== store && oldStore.autoDestroy) {
                oldStore.destroy();
            } else {
                oldStore.un({
                    scope: me,
                    load: me.onLoad,
                    exception: me.collapse
                });
            }
            if (!store) {
                me.store = null;
                if (me.picker) {
                    me.picker.bindStore(null);
                }
            }
        }
        if (store) {
            if (!initial) {
                me.resetToDefault();
            }

            me.store = Ext.data.StoreManager.lookup(store);
            me.store.on({
                scope: me,
                load: me.onLoad,
                exception: me.collapse
            });

            if (me.picker) {
                me.picker.bindStore(store);
            }
        }
    },

    onLoad: function() {
        var me = this,
            value = me.value;

        me.syncSelection();
        if (me.picker && !me.picker.getSelectionModel().hasSelection()) {
            me.doAutoSelect();
        }
    },

    
    doRawQuery: function() {
        this.doQuery(this.getRawValue());
    },

    
    doQuery: function(queryString, forceAll) {
        queryString = queryString || '';

        
        
        var me = this,
            qe = {
                query: queryString,
                forceAll: forceAll,
                combo: me,
                cancel: false
            },
            store = me.store,
            isLocalMode = me.queryMode === 'local';

        if (me.fireEvent('beforequery', qe) === false || qe.cancel) {
            return false;
        }

        
        queryString = qe.query;
        forceAll = qe.forceAll;

        
        if (forceAll || (queryString.length >= me.minChars)) {
            
            me.expand();

            
            if (!me.queryCaching || me.lastQuery !== queryString) {
                me.lastQuery = queryString;
                store.clearFilter(!forceAll);
                if (isLocalMode) {
                    if (!forceAll) {
                        store.filter(me.displayField, queryString);
                    }
                } else {
                    store.load({
                        params: me.getParams(queryString)
                    });
                }
            }

            
            if (me.getRawValue() !== me.getDisplayValue()) {
                me.ignoreSelection++;
                me.picker.getSelectionModel().deselectAll();
                me.ignoreSelection--;
            }

            if (isLocalMode) {
                me.doAutoSelect();
            }
            if (me.typeAhead) {
                me.doTypeAhead();
            }
        }
        return true;
    },

    
    getParams: function(queryString) {
        var p = {},
            pageSize = this.pageSize,
            param = this.queryParam;
            
        if (param) {
            p[param] = queryString;
        }
        
        if (pageSize) {
            p.start = 0;
            p.limit = pageSize;
        }
        return p;
    },

    
    doAutoSelect: function() {
        var me = this,
            picker = me.picker,
            lastSelected, itemNode;
        if (picker && me.autoSelect && me.store.getCount() > 0) {
            
            lastSelected = picker.getSelectionModel().lastSelected;
            itemNode = picker.getNode(lastSelected || 0);
            if (itemNode) {
                picker.highlightItem(itemNode);
                picker.listEl.scrollChildIntoView(itemNode, false);
            }
        }
    },

    doTypeAhead: function() {
        if (!this.typeAheadTask) {
            this.typeAheadTask = Ext.create('Ext.util.DelayedTask', this.onTypeAhead, this);
        }
        if (this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE) {
            this.typeAheadTask.delay(this.typeAheadDelay);
        }
    },

    onTriggerClick: function() {
        var me = this;
        if (!me.readOnly && !me.disabled) {
            if (me.isExpanded) {
                me.collapse();
            } else {
                me.onFocus({});
                if (me.triggerAction === 'all') {
                    me.doQuery(me.allQuery, true);
                } else {
                    me.doQuery(me.getRawValue());
                }
            }
            me.inputEl.focus();
        }
    },


    
    onKeyUp: function(e, t) {
        var me = this,
            key = e.getKey();

        if (!me.readOnly && !me.disabled && me.editable) {
            me.lastKey = key;
            
            

            
            if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
                me.doQueryTask.delay(me.queryDelay);
            }
        }
        
        if (me.enableKeyEvents) {
            me.callParent(arguments);
        }
    },

    initEvents: function() {
        var me = this;
        me.callParent();

        
        if (!me.enableKeyEvents) {
            me.mon(me.inputEl, 'keyup', me.onKeyUp, me);
        }
    },

    createPicker: function() {
        var me = this,
            picker,
            menuCls = Ext.baseCSSPrefix + 'menu',
            opts = Ext.apply({
                selModel: {
                    mode: me.multiSelect ? 'SIMPLE' : 'SINGLE'
                },
                floating: true,
                hidden: true,
                ownerCt: me.ownerCt,
                cls: me.el.up('.' + menuCls) ? menuCls : '',
                store: me.store,
                displayField: me.displayField,
                focusOnToFront: false,
                pageSize: me.pageSize
            }, me.listConfig, me.defaultListConfig);

        picker = me.picker = Ext.create('Ext.view.BoundList', opts);

        me.mon(picker, {
            itemclick: me.onItemClick,
            refresh: me.onListRefresh,
            scope: me
        });

        me.mon(picker.getSelectionModel(), {
            selectionChange: me.onListSelectionChange,
            scope: me
        });

        return picker;
    },

    onListRefresh: function() {
        this.alignPicker();
        this.syncSelection();
    },
    
    onItemClick: function(picker, record){
        
        var me = this,
            lastSelection = me.lastSelection,
            valueField = me.valueField,
            selected;
        
        if (!me.multiSelect && lastSelection) {
            selected = lastSelection[0];
            if (record.get(valueField) === selected.get(valueField)) {
                me.collapse();
            }
        }   
    },

    onListSelectionChange: function(list, selectedRecords) {
        var me = this;
        
        
        if (!me.ignoreSelection && me.isExpanded) {
            if (!me.multiSelect) {
                Ext.defer(me.collapse, 1, me);
            }
            me.setValue(selectedRecords, false);
            if (selectedRecords.length > 0) {
                me.fireEvent('select', me, selectedRecords);
            }
            me.inputEl.focus();
        }
    },

    
    onExpand: function() {
        var me = this,
            keyNav = me.listKeyNav,
            selectOnTab = me.selectOnTab,
            picker = me.getPicker();

        
        if (keyNav) {
            keyNav.enable();
        } else {
            keyNav = me.listKeyNav = Ext.create('Ext.view.BoundListKeyNav', this.inputEl, {
                boundList: picker,
                forceKeyDown: true,
                tab: function(e) {
                    if (selectOnTab) {
                        this.selectHighlighted(e);
                        me.triggerBlur();
                    }
                    
                    return true;
                }
            });
        }

        
        if (selectOnTab) {
            me.ignoreMonitorTab = true;
        }

        Ext.defer(keyNav.enable, 1, keyNav); 
        me.inputEl.focus();
    },

    
    onCollapse: function() {
        var me = this,
            keyNav = me.listKeyNav;
        if (keyNav) {
            keyNav.disable();
            me.ignoreMonitorTab = false;
        }
    },

    
    select: function(r) {
        this.setValue(r, true);
    },

    
    findRecord: function(field, value) {
        var ds = this.store,
            idx = ds.findExact(field, value);
        return idx !== -1 ? ds.getAt(idx) : false;
    },
    findRecordByValue: function(value) {
        return this.findRecord(this.valueField, value);
    },
    findRecordByDisplay: function(value) {
        return this.findRecord(this.displayField, value);
    },

    
    setValue: function(value, doSelect) {
        var me = this,
            valueNotFoundText = me.valueNotFoundText,
            inputEl = me.inputEl,
            i, len, record,
            models = [],
            displayTplData = [],
            processedValue = [];

        if (me.store.loading) {
            
            me.value = value;
            return me;
        }

        
        value = Ext.Array.from(value);

        
        for (i = 0, len = value.length; i < len; i++) {
            record = value[i];
            if (!record || !record.isModel) {
                record = me.findRecordByValue(record);
            }
            
            if (record) {
                models.push(record);
                displayTplData.push(record.data);
                processedValue.push(record.get(me.valueField));
            }
            
            
            else {
                
                if (Ext.isDefined(valueNotFoundText)) {
                    displayTplData.push(valueNotFoundText);
                }
                processedValue.push(value[i]);
            }
        }

        
        me.value = me.multiSelect ? processedValue : processedValue[0];
        if (!Ext.isDefined(me.value)) {
            me.value = null;
        }
        me.displayTplData = displayTplData; 
        me.lastSelection = me.valueModels = models;

        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
            inputEl.removeCls(me.emptyCls);
        }

        
        me.setRawValue(me.getDisplayValue());
        me.checkChange();

        if (doSelect !== false) {
            me.syncSelection();
        }
        me.applyEmptyText();

        return me;
    },

    
    getDisplayValue: function() {
        return this.displayTpl.apply(this.displayTplData);
    },

    getValue: function() {
        
        
        
        var me = this,
            picker = me.picker,
            rawValue = me.getRawValue(), 
            value = me.value; 

        if (me.getDisplayValue() !== rawValue) {
            value = rawValue;
            me.value = me.displayTplData = me.valueModels = null;
            if (picker) {
                me.ignoreSelection++;
                picker.getSelectionModel().deselectAll();
                me.ignoreSelection--;
            }
        }

        return value;
    },

    getSubmitValue: function() {
        return this.getValue();
    },

    isEqual: function(v1, v2) {
        var fromArray = Ext.Array.from,
            i, len;

        v1 = fromArray(v1);
        v2 = fromArray(v2);
        len = v1.length;

        if (len !== v2.length) {
            return false;
        }

        for(i = 0; i < len; i++) {
            if (v2[i] !== v1[i]) {
                return false;
            }
        }

        return true;
    },

    
    clearValue: function() {
        this.setValue([]);
    },

    
    syncSelection: function() {
        var me = this,
            ExtArray = Ext.Array,
            picker = me.picker,
            selection, selModel;
        if (picker) {
            
            selection = [];
            ExtArray.forEach(me.valueModels || [], function(value) {
                if (value && value.isModel && me.store.indexOf(value) >= 0) {
                    selection.push(value);
                }
            });

            
            me.ignoreSelection++;
            selModel = picker.getSelectionModel();
            selModel.deselectAll();
            if (selection.length) {
                selModel.select(selection);
            }
            me.ignoreSelection--;
        }
    }
});


Ext.define('Ext.picker.Month', {
    extend: 'Ext.Component',
    requires: ['Ext.XTemplate', 'Ext.util.ClickRepeater', 'Ext.Date', 'Ext.button.Button'],
    alias: 'widget.monthpicker',
    alternateClassName: 'Ext.MonthPicker',

    renderTpl: [
        '<div class="{baseCls}-body">',
          '<div class="{baseCls}-months">',
              '<tpl for="months">',
                  '<div class="{parent.baseCls}-item {parent.baseCls}-month"><a href="#" hidefocus="on">{.}</a></div>',
              '</tpl>',
          '</div>',
          '<div class="{baseCls}-years">',
              '<div class="{baseCls}-yearnav">',
                  '<button class="{baseCls}-yearnav-prev"></button>',
                  '<button class="{baseCls}-yearnav-next"></button>',
              '</div>',
              '<tpl for="years">',
                  '<div class="{parent.baseCls}-item {parent.baseCls}-year"><a href="#" hidefocus="on">{.}</a></div>',
              '</tpl>',
          '</div>',
        '</div>',
        '<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
        '<tpl if="showButtons">',
          '<div class="{baseCls}-buttons"></div>',
        '</tpl>'
    ],

    
    okText: 'OK',

    
    cancelText: 'Cancel',

    
    baseCls: Ext.baseCSSPrefix + 'monthpicker',

    
    showButtons: true,

    

    

    width: 175,

    height: 195,


    
    totalYears: 10,
    yearOffset: 5, 
    monthOffset: 6, 

    
    initComponent: function(){
        var me = this;

        me.selectedCls = me.baseCls + '-selected';
        me.addEvents(
            
            'cancelclick',

            
            'monthclick',

            
            'monthdblclick',

            
            'okclick',

            
            'select',

            
            'yearclick',

            
            'yeardblclick'
        );

        me.setValue(me.value);
        me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);
        this.callParent();
    },

    
    onRender: function(ct, position){
        var me = this,
            i = 0,
            months = [],
            shortName = Ext.Date.getShortMonthName,
            monthLen = me.monthOffset;

        for (; i < monthLen; ++i) {
            months.push(shortName(i), shortName(i + monthLen));
        }

        Ext.apply(me.renderData, {
            months: months,
            years: me.getYears(),
            showButtons: me.showButtons
        });

        Ext.apply(me.renderSelectors, {
            bodyEl: '.' + me.baseCls + '-body',
            prevEl: '.' + me.baseCls + '-yearnav-prev',
            nextEl: '.' + me.baseCls + '-yearnav-next',
            buttonsEl: '.' + me.baseCls + '-buttons'
        });
        this.callParent([ct, position]);
    },

    
    afterRender: function(){
        var me = this,
            body = me.bodyEl,
            buttonsEl = me.buttonsEl;

        me.callParent();

        me.mon(body, 'click', me.onBodyClick, me);
        me.mon(body, 'dblclick', me.onBodyClick, me);

        
        me.years = body.select('.' + me.baseCls + '-year a');
        me.months = body.select('.' + me.baseCls + '-month a');

        if (me.showButtons) {
            me.okBtn = Ext.create('Ext.button.Button', {
                text: me.okText,
                renderTo: buttonsEl,
                handler: me.onOkClick,
                scope: me
            });
            me.cancelBtn = Ext.create('Ext.button.Button', {
                text: me.cancelText,
                renderTo: buttonsEl,
                handler: me.onCancelClick,
                scope: me
            });
        }

        me.backRepeater = Ext.create('Ext.util.ClickRepeater', me.prevEl, {
            handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears])
        });

        me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
        me.nextRepeater = Ext.create('Ext.util.ClickRepeater', me.nextEl, {
            handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears])
        });
        me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
        me.updateBody();
    },

    
    setValue: function(value){
        var me = this,
            active = me.activeYear,
            offset = me.monthOffset,
            year,
            index;

        if (!value) {
            me.value = [null, null];
        } else if (Ext.isDate(value)) {
            me.value = [value.getMonth(), value.getFullYear()];
        } else {
            me.value = [value[0], value[1]];
        }

        if (me.rendered) {
            year = me.value[1];
            if (year !== null) {
                if ((year < active || year > active + me.yearOffset)) {
                    me.activeYear = year - me.yearOffset + 1;
                }
            }
            me.updateBody();
        }

        return me;
    },

    
    getValue: function(){
        return this.value;
    },

    
    hasSelection: function(){
        var value = this.value;
        return value[0] !== null && value[1] !== null;
    },

    
    getYears: function(){
        var me = this,
            offset = me.yearOffset,
            start = me.activeYear, 
            end = start + offset,
            i = start,
            years = [];

        for (; i < end; ++i) {
            years.push(i, i + offset);
        }

        return years;
    },

    
    updateBody: function(){
        var me = this,
            years = me.years,
            months = me.months,
            yearNumbers = me.getYears(),
            cls = me.selectedCls,
            value = me.getYear(null),
            month = me.value[0],
            monthOffset = me.monthOffset,
            year;

        if (me.rendered) {
            years.removeCls(cls);
            months.removeCls(cls);
            years.each(function(el, all, index){
                year = yearNumbers[index];
                el.dom.innerHTML = year;
                if (year == value) {
                    el.dom.className = cls;
                }
            });
            if (month !== null) {
                if (month < monthOffset) {
                    month = month * 2;
                } else {
                    month = (month - monthOffset) * 2 + 1;
                }
                months.item(month).addCls(cls);
            }
        }
    },

    
    getYear: function(defaultValue, offset) {
        var year = this.value[1];
        offset = offset || 0;
        return year === null ? defaultValue : year + offset;
    },

    
    onBodyClick: function(e, t) {
        var me = this,
            isDouble = e.type == 'dblclick';

        if (e.getTarget('.' + me.baseCls + '-month')) {
            e.stopEvent();
            me.onMonthClick(t, isDouble);
        } else if (e.getTarget('.' + me.baseCls + '-year')) {
            e.stopEvent();
            me.onYearClick(t, isDouble);
        }
    },

    
    adjustYear: function(offset){
        if (typeof offset != 'number') {
            offset = this.totalYears;
        }
        this.activeYear += offset;
        this.updateBody();
    },

    
    onOkClick: function(){
        this.fireEvent('okclick', this, this.value);
    },

    
    onCancelClick: function(){
        this.fireEvent('cancelclick', this);
    },

    
    onMonthClick: function(target, isDouble){
        var me = this;
        me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset);
        me.updateBody();
        me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);
    },

    
    onYearClick: function(target, isDouble){
        var me = this;
        me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
        me.updateBody();
        me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
        me.fireEvent('select', me, me.value);

    },

    
    resolveOffset: function(index, offset){
        if (index % 2 === 0) {
            return (index / 2);
        } else {
            return offset + Math.floor(index / 2);
        }
    },

    
    beforeDestroy: function(){
        var me = this;
        me.years = me.months = null;
        Ext.destroyMembers('backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
        this.callParent();
    }
});


Ext.define('Ext.picker.Date', {
    extend: 'Ext.Component',
    requires: [
        'Ext.XTemplate',
        'Ext.button.Button',
        'Ext.button.Split',
        'Ext.util.ClickRepeater',
        'Ext.util.KeyNav',
        'Ext.EventObject',
        'Ext.fx.Manager',
        'Ext.picker.Month'
    ],
    alias: 'widget.datepicker',
    alternateClassName: 'Ext.DatePicker',

    renderTpl: [
        '<div class="{cls}" id="{id}" role="grid" title="{ariaTitle} {value:this.longDay}">',
            '<div role="presentation" class="{baseCls}-header">',
                '<div class="{baseCls}-prev"><a href="#" role="button" title="{prevText}"></a></div>',
                '<div class="{baseCls}-month"></div>',
                '<div class="{baseCls}-next"><a href="#" role="button" title="{nextText}"></a></div>',
            '</div>',
            '<table class="{baseCls}-inner" cellspacing="0" role="presentation">',
                '<thead role="presentation"><tr role="presentation">',
                    '<tpl for="dayNames">',
                        '<th role="columnheader" title="{.}"><span>{.:this.firstInitial}</span></th>',
                    '</tpl>',
                '</tr></thead>',
                '<tbody role="presentation"><tr role="presentation">',
                    '<tpl for="days">',
                        '{#:this.isEndOfWeek}',
                        '<td role="gridcell" id="{[Ext.id()]}">',
                            '<a role="presentation" href="#" hidefocus="on" class="{parent.baseCls}-date" tabIndex="1">',
                                '<em role="presentation"><span role="presentation"></span></em>',
                            '</a>',
                        '</td>',
                    '</tpl>',
                '</tr></tbody>',
            '</table>',
            '<tpl if="showToday">',
                '<div role="presentation" class="{baseCls}-footer"></div>',
            '</tpl>',
        '</div>',
        {
            firstInitial: function(value) {
                return value.substr(0,1);
            },
            isEndOfWeek: function(value) {
                
                
                value--;
                var end = value % 7 === 0 && value !== 0;
                return end ? '</tr><tr role="row">' : '';
            },
            longDay: function(value){
                return Ext.Date.format(value, this.longDayFormat);
            }
        }
    ],

    ariaTitle: 'Date Picker',
    
    todayText : 'Today',
    
    
    
    todayTip : '{0} (Spacebar)',
    
    minText : 'This date is before the minimum date',
    
    maxText : 'This date is after the maximum date',
    
    
    disabledDaysText : 'Disabled',
    
    disabledDatesText : 'Disabled',
    
    
    
    nextText : 'Next Month (Control+Right)',
    
    prevText : 'Previous Month (Control+Left)',
    
    monthYearText : 'Choose a month (Control+Up/Down to move years)',
    
    startDay : 0,
    
    showToday : true,
    
    
    
    
    

    
    disableAnim: true,

    
    baseCls: Ext.baseCSSPrefix + 'datepicker',

    

    

    
    longDayFormat: 'F d, Y',

    

    
    focusOnShow: false,

    
    
    focusOnSelect: true,

    width: 178,

    
    
    initHour: 12, 

    numDays: 42,

    
    initComponent : function() {
        var me = this,
            clearTime = Ext.Date.clearTime;

        me.selectedCls = me.baseCls + '-selected';
        me.disabledCellCls = me.baseCls + '-disabled';
        me.prevCls = me.baseCls + '-prevday';
        me.activeCls = me.baseCls + '-active';
        me.nextCls = me.baseCls + '-prevday';
        me.todayCls = me.baseCls + '-today';
        me.dayNames = me.dayNames.slice(me.startDay).concat(me.dayNames.slice(0, me.startDay));
        this.callParent();

        me.value = me.value ?
                 clearTime(me.value, true) : clearTime(new Date());

        me.addEvents(
            
            'select'
        );

        me.initDisabledDays();
    },

    
    onRender : function(container, position){
        

        var me = this,
            days = new Array(me.numDays),
            today = Ext.Date.format(new Date(), me.format);

        Ext.applyIf(me, {
            renderData: {},
            renderSelectors: {}
        });

        Ext.apply(me.renderData, {
            dayNames: me.dayNames,
            ariaTitle: me.ariaTitle,
            value: me.value,
            showToday: me.showToday,
            prevText: me.prevText,
            nextText: me.nextText,
            days: days
        });
        me.getTpl('renderTpl').longDayFormat = me.longDayFormat;

        Ext.apply(me.renderSelectors, {
            eventEl: 'table.' + me.baseCls + '-inner',
            prevEl: '.' + me.baseCls + '-prev a',
            nextEl: '.' + me.baseCls + '-next a',
            middleBtnEl: '.' + me.baseCls + '-month',
            footerEl: '.' + me.baseCls + '-footer'
        });

        this.callParent(arguments);
        me.el.unselectable();

        me.cells = me.eventEl.select('tbody td');
        me.textNodes = me.eventEl.query('tbody td span');

        me.monthBtn = Ext.create('Ext.button.Split', {
            text: '',
            tooltip: me.monthYearText,
            renderTo: me.middleBtnEl
        });
        


        me.todayBtn = Ext.create('Ext.button.Button', {
            renderTo: me.footerEl,
            text: Ext.String.format(me.todayText, today),
            tooltip: Ext.String.format(me.todayTip, today),
            handler: me.selectToday,
            scope: me
        });
    },

    
    initEvents: function(){
        var me = this,
            eDate = Ext.Date,
            day = eDate.DAY;

        this.callParent();

        me.prevRepeater = Ext.create('Ext.util.ClickRepeater', me.prevEl, {
            handler: me.showPrevMonth,
            scope: me,
            preventDefault: true,
            stopDefault: true
        });

        me.nextRepeater = Ext.create('Ext.util.ClickRepeater', me.nextEl, {
            handler: me.showNextMonth,
            scope: me,
            preventDefault:true,
            stopDefault:true
        });

        me.keyNav = Ext.create('Ext.util.KeyNav', me.eventEl, Ext.apply({
            scope: me,
            'left' : function(e){
                if(e.ctrlKey){
                    me.showPrevMonth();
                }else{
                    me.update(eDate.add(me.activeDate, day, -1));
                }
            },

            'right' : function(e){
                if(e.ctrlKey){
                    me.showNextMonth();
                }else{
                    me.update(eDate.add(me.activeDate, day, 1));
                }
            },

            'up' : function(e){
                if(e.ctrlKey){
                    me.showNextYear();
                }else{
                    me.update(eDate.add(me.activeDate, day, -7));
                }
            },

            'down' : function(e){
                if(e.ctrlKey){
                    me.showPrevYear();
                }else{
                    me.update(eDate.add(me.activeDate, day, 7));
                }
            },
            'pageUp' : me.showNextMonth,
            'pageDown' : me.showPrevMonth,
            'enter' : function(e){
                e.stopPropagation();
                return true;
            }
        }, me.keyNavConfig));

        if(me.showToday){
            me.todayKeyListener = me.eventEl.addKeyListener(Ext.EventObject.SPACE, me.selectToday,  me);
        }
        me.mon(me.eventEl, 'mousewheel', me.handleMouseWheel, me);
        me.mon(me.eventEl, 'click', me.handleDateClick,  me, {delegate: 'a.' + me.baseCls + '-date'});
        me.mon(me.monthBtn, 'click', me.showMonthPicker, me);
        me.mon(me.monthBtn, 'arrowclick', me.showMonthPicker, me);
        me.update(me.value);
    },

    
    initDisabledDays : function(){
        var me = this,
            dd = me.disabledDates,
            re = '(?:',
            len;

        if(!me.disabledDatesRE && dd){
                len = dd.length - 1;

            Ext.each(dd, function(d, i){
                re += Ext.isDate(d) ? '^' + Ext.String.escapeRegex(Ext.Date.dateFormat(d, me.format)) + '$' : dd[i];
                if(i != len){
                    re += '|';
                }
            }, me);
            me.disabledDatesRE = new RegExp(re + ')');
        }
    },

    
    setDisabledDates : function(dd){
        var me = this;

        if(Ext.isArray(dd)){
            me.disabledDates = dd;
            me.disabledDatesRE = null;
        }else{
            me.disabledDatesRE = dd;
        }
        me.initDisabledDays();
        me.update(me.value, true);
        return me;
    },

    
    setDisabledDays : function(dd){
        this.disabledDays = dd;
        return this.update(this.value, true);
    },

    
    setMinDate : function(dt){
        this.minDate = dt;
        return this.update(this.value, true);
    },

    
    setMaxDate : function(dt){
        this.maxDate = dt;
        return this.update(this.value, true);
    },

    
    setValue : function(value){
        this.value = Ext.Date.clearTime(value, true);
        return this.update(this.value);
    },

    
    getValue : function(){
        return this.value;
    },

    
    focus : function(){
        this.update(this.activeDate);
    },

    
    onEnable: function(){
        this.callParent();
        this.setDisabledStatus(false);
        this.update(this.activeDate);

    },

    
    onDisable : function(){
        this.callParent();
        this.setDisabledStatus(true);
    },

    
    setDisabledStatus : function(disabled){
        var me = this;

        me.keyNav.setDisabled(disabled);
        me.prevRepeater.setDisabled(disabled);
        me.nextRepeater.setDisabled(disabled);
        if (me.showToday) {
            me.todayKeyListener.setDisabled(disabled);
            me.todayBtn.setDisabled(disabled);
        }
    },

    
    getActive: function(){
        return this.activeDate || me.value;
    },

    
    runAnimation: function(isHide){
        var options = {
                target: this.monthPicker,
                duration: 200
            };

        Ext.fx.Manager.run();
        if (isHide) {
            
        } else {
            
        }
        Ext.create('Ext.fx.Anim', options);
    },

    
    hideMonthPicker : function(){
        var me = this,
            picker = me.monthPicker;

        if (picker) {
            if (me.disableAnim) {
                picker.hide();
            } else {
                this.runAnimation(true);
            }
        }
        return me;
    },

    
    showMonthPicker : function(){

        var me = this,
            picker,
            size,
            top,
            left;


        if (me.rendered && !me.disabled) {
            size = me.getSize();
            picker = me.createMonthPicker();
            picker.show();
            picker.setSize(size);
            picker.setValue(me.getActive());

            if (me.disableAnim) {
                picker.setPosition(-1, -1);
            } else {
                me.runAnimation(false);
            }
        }
        return me;
    },

    
    createMonthPicker: function(){
        var me = this,
            picker = me.monthPicker;

        if (!picker) {
            me.monthPicker = picker = Ext.create('Ext.picker.Month', {
                renderTo: me.el,
                floating: true,
                shadow: false,
                listeners: {
                    scope: me,
                    cancelclick: me.onCancelClick,
                    okclick: me.onOkClick,
                    yeardblclick: me.onOkClick,
                    monthdblclick: me.onOkClick
                }
            });

            me.on('beforehide', me.hideMonthPicker, me);
        }
        return picker;
    },

    
    onOkClick: function(picker, value){
        var me = this,
            month = value[0],
            year = value[1],
            date = new Date(year, month, me.getActive().getDate());

        if (date.getMonth() !== month) {
            
            date = new Date(year, month, 1).getLastDateOfMonth();
        }
        me.update(date);
        me.hideMonthPicker();
    },

    
    onCancelClick: function(){
        this.hideMonthPicker();
    },

    
    showPrevMonth : function(e){
        return this.update(Ext.Date.add(this.activeDate, Ext.Date.MONTH, -1));
    },

    
    showNextMonth : function(e){
        return this.update(Ext.Date.add(this.activeDate, Ext.Date.MONTH, 1));
    },

    
    showPrevYear : function(){
        this.update(Ext.Date.add(this.activeDate, Ext.Date.YEAR, -1));
    },

    
    showNextYear : function(){
        this.update(Ext.Date.add(this.activeDate, Ext.Date.YEAR, 1));
    },

    
    handleMouseWheel : function(e){
        e.stopEvent();
        if(!this.disabled){
            var delta = e.getWheelDelta();
            if(delta > 0){
                this.showPrevMonth();
            } else if(delta < 0){
                this.showNextMonth();
            }
        }
    },

    
    handleDateClick : function(e, t){
        var me = this,
            handler = me.handler;

        e.stopEvent();
        if(!me.disabled && t.dateValue && !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)){
            me.cancelFocus = me.focusOnSelect === false;
            me.setValue(new Date(t.dateValue));
            delete me.cancelFocus;
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            
            
            
            
            me.onSelect();
        }
    },

    
    onSelect: function() {
        if (this.hideOnSelect) {
             this.hide();
         }
    },

    
    selectToday : function(){
        var me = this,
            btn = me.todayBtn,
            handler = me.handler;

        if(btn && !btn.disabled){
            me.setValue(Ext.Date.clearTime(new Date()));
            me.fireEvent('select', me, me.value);
            if (handler) {
                handler.call(me.scope || me, me, me.value);
            }
            me.onSelect();
        }
        return me;
    },

    
    selectedUpdate: function(date, active){
        var me = this,
            t = date.getTime(),
            cells = me.cells,
            cls = me.selectedCls;

        cells.removeCls(cls);
        cells.each(function(c){
            if (c.dom.firstChild.dateValue == t) {
                me.el.dom.setAttribute('aria-activedescendent', c.dom.id);
                c.addCls(cls);
                if(me.isVisible() && !me.cancelFocus){
                    Ext.fly(c.dom.firstChild).focus(50);
                }
                return false;
            }
        }, this);
    },

    
    fullUpdate: function(date, active){
        var me = this,
            cells = me.cells.elements,
            textNodes = me.textNodes,
            disabledCls = me.disabledCellCls,
            eDate = Ext.Date,
            i = 0,
            extraDays = 0,
            visible = me.isVisible(),
            sel = +eDate.clearTime(date, true),
            today = +eDate.clearTime(new Date()),
            min = me.minDate ? eDate.clearTime(me.minDate, true) : Number.NEGATIVE_INFINITY,
            max = me.maxDate ? eDate.clearTime(me.maxDate, true) : Number.POSITIVE_INFINITY,
            ddMatch = me.disabledDatesRE,
            ddText = me.disabledDatesText,
            ddays = me.disabledDays ? me.disabledDays.join('') : false,
            ddaysText = me.disabledDaysText,
            format = me.format,
            days = eDate.getDaysInMonth(date),
            firstOfMonth = eDate.getFirstDateOfMonth(date),
            startingPos = firstOfMonth.getDay() - me.startDay,
            previousMonth = eDate.add(date, eDate.MONTH, -1),
            longDayFormat = me.longDayFormat,
            prevStart,
            current,
            disableToday,
            tempDate,
            setCellClass,
            html,
            cls,
            formatValue,
            value;

        if (startingPos < 0) {
            startingPos += 7;
        }

        days += startingPos;
        prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;
        current = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), prevStart, me.initHour);

        if (me.showToday) {
            tempDate = eDate.clearTime(new Date());
            disableToday = (tempDate < min || tempDate > max ||
                (ddMatch && format && ddMatch.test(eDate.dateFormat(tempDate, format))) ||
                (ddays && ddays.indexOf(tempDate.getDay()) != -1));

            if (!me.disabled) {
                me.todayBtn.setDisabled(disableToday);
                me.todayKeyListener.setDisabled(disableToday);
            }
        }

        setCellClass = function(cell){
            value = +eDate.clearTime(current, true);
            cell.title = eDate.format(current, longDayFormat);
            
            cell.firstChild.dateValue = value;
            if(value == today){
                cell.className += ' ' + me.todayCls;
                cell.title = me.todayText;
            }
            if(value == sel){
                cell.className += ' ' + me.selectedCls;
                me.el.dom.setAttribute('aria-activedescendant', cell.id);
                if (visible && me.floating) {
                    Ext.fly(cell.firstChild).focus(50);
                }
            }
            
            if(value < min) {
                cell.className = disabledCls;
                cell.title = me.minText;
                return;
            }
            if(value > max) {
                cell.className = disabledCls;
                cell.title = me.maxText;
                return;
            }
            if(ddays){
                if(ddays.indexOf(current.getDay()) != -1){
                    cell.title = ddaysText;
                    cell.className = disabledCls;
                }
            }
            if(ddMatch && format){
                formatValue = eDate.dateFormat(current, format);
                if(ddMatch.test(formatValue)){
                    cell.title = ddText.replace('%0', formatValue);
                    cell.className = disabledCls;
                }
            }
        };

        for(; i < me.numDays; ++i) {
            if (i < startingPos) {
                html = (++prevStart);
                cls = me.prevCls;
            } else if (i >= days) {
                html = (++extraDays);
                cls = me.nextCls;
            } else {
                html = i - startingPos + 1;
                cls = me.activeCls;
            }
            textNodes[i].innerHTML = html;
            cells[i].className = cls;
            current.setDate(current.getDate() + 1);
            setCellClass(cells[i]);
        }

        me.monthBtn.setText(me.monthNames[date.getMonth()] + ' ' + date.getFullYear());
    },

    
    update : function(date, forceRefresh){
        var me = this,
            active = me.activeDate;

        if (me.rendered) {
            me.activeDate = date;
            if(!forceRefresh && active && me.el && active.getMonth() == date.getMonth() && active.getFullYear() == date.getFullYear()){
                me.selectedUpdate(date, active);
            } else {
                me.fullUpdate(date, active);
            }
        }
        return me;
    },

    
    beforeDestroy : function() {
        var me = this;

        if (me.rendered) {
            Ext.destroy(
                me.todayKeyListener,
                me.keyNav,
                me.monthPicker,
                me.monthBtn,
                me.nextRepeater,
                me.prevRepeater,
                me.todayBtn
            );
            delete me.textNodes;
            delete me.cells.elements;
        }
    },

    
    onShow: function() {
        this.callParent(arguments);
        if (this.focusOnShow) {
            this.focus();
        }
    }
},


function() {
    var proto = this.prototype;

    proto.monthNames = Ext.Date.monthNames;

    proto.dayNames = Ext.Date.dayNames;

    proto.format = Ext.Date.defaultFormat;
});


Ext.define('Ext.form.field.Date', {
    extend:'Ext.form.field.Picker',
    alias: 'widget.datefield',
    requires: ['Ext.picker.Date'],
    alternateClassName: ['Ext.form.DateField', 'Ext.form.Date'],

    
    format : "m/d/Y",
    
    altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d|n-j|n/j",
    
    disabledDaysText : "Disabled",
    
    disabledDatesText : "Disabled",
    
    minText : "The date in this field must be equal to or after {0}",
    
    maxText : "The date in this field must be equal to or before {0}",
    
    invalidText : "{0} is not a valid date - it must be in the format {1}",
    
    triggerCls : Ext.baseCSSPrefix + 'form-date-trigger',
    
    showToday : true,
    
    
    
    
    
    

    
    
    initTime: '12', 

    initTimeFormat: 'H',

    matchFieldWidth: false,
    
    startDay: 0,
    
    initComponent : function(){
        var me = this,
            isString = Ext.isString,
            min, max;

        min = me.minValue;
        max = me.maxValue;
        if(isString(min)){
            me.minValue = me.parseDate(min);
        }
        if(isString(max)){
            me.maxValue = me.parseDate(max);
        }
        me.disabledDatesRE = null;
        me.initDisabledDays();

        me.callParent();
    },

    initValue: function() {
        var me = this,
            value = me.value;

        
        if (Ext.isString(value)) {
            me.value = me.rawToValue(value);
        }

        me.callParent();
    },

    
    initDisabledDays : function(){
        if(this.disabledDates){
            var dd = this.disabledDates,
                len = dd.length - 1,
                re = "(?:";

            Ext.each(dd, function(d, i){
                re += Ext.isDate(d) ? '^' + Ext.String.escapeRegex(d.dateFormat(this.format)) + '$' : dd[i];
                if (i !== len) {
                    re += '|';
                }
            }, this);
            this.disabledDatesRE = new RegExp(re + ')');
        }
    },

    
    setDisabledDates : function(dd){
        var me = this,
            picker = me.picker;
            
        me.disabledDates = dd;
        me.initDisabledDays();
        if (picker) {
            picker.setDisabledDates(me.disabledDatesRE);
        }
    },

    
    setDisabledDays : function(dd){
        var picker = this.picker;
            
        this.disabledDays = dd;
        if (picker) {
            picker.setDisabledDays(dd);
        }
    },

    
    setMinValue : function(dt){
        var me = this,
            picker = me.picker,
            minValue = (Ext.isString(dt) ? me.parseDate(dt) : dt);
            
        me.minValue = minValue;
        if (picker) {
            picker.minText = Ext.String.format(me.minText, me.formatDate(me.minValue));
            picker.setMinDate(minValue);
        }
    },

    
    setMaxValue : function(dt){
        var me = this,
            picker = me.picker,
            maxValue = (Ext.isString(dt) ? me.parseDate(dt) : dt);
            
        me.maxValue = maxValue;
        if (picker) {
            picker.maxText = Ext.String.format(me.maxText, me.formatDate(me.maxValue));
            picker.setMaxDate(maxValue);
        }
    },

    
    getErrors: function(value) {
        var me = this,
            format = Ext.String.format,
            clearTime = Ext.Date.clearTime,
            errors = me.callParent(arguments),
            disabledDays = me.disabledDays,
            disabledDatesRE = me.disabledDatesRE,
            minValue = me.minValue,
            maxValue = me.maxValue,
            len = disabledDays ? disabledDays.length : 0,
            i = 0,
            svalue,
            fvalue,
            day,
            time;

        value = me.formatDate(value || me.processRawValue(me.getRawValue()));

        if (value === null || value.length < 1) { 
             return errors;
        }

        svalue = value;
        value = me.parseDate(value);
        if (!value) {
            errors.push(format(me.invalidText, svalue, me.format));
            return errors;
        }

        time = value.getTime();
        if (minValue && time < clearTime(minValue).getTime()) {
            errors.push(format(me.minText, me.formatDate(minValue)));
        }

        if (maxValue && time > clearTime(maxValue).getTime()) {
            errors.push(format(me.maxText, me.formatDate(maxValue)));
        }

        if (disabledDays) {
            day = value.getDay();

            for(; i < len; i++) {
                if (day === disabledDays[i]) {
                    errors.push(me.disabledDaysText);
                    break;
                }
            }
        }

        fvalue = me.formatDate(value);
        if (disabledDatesRE && disabledDatesRE.test(fvalue)) {
            errors.push(format(me.disabledDatesText, fvalue));
        }

        return errors;
    },

    rawToValue: function(rawValue) {
        return this.parseDate(rawValue) || rawValue || null;
    },

    valueToRaw: function(value) {
        return this.formatDate(this.parseDate(value));
    },

    

    
    safeParse : function(value, format) {
        var me = this,
            utilDate = Ext.Date,
            parsedDate,
            result = null;
            
        if (utilDate.formatContainsHourInfo(format)) {
            
            result = utilDate.parse(value, format);
        } else {
            
            parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat);
            if (parsedDate) {
                result = utilDate.clearTime(parsedDate);
            }
        }
        return result;
    },
    
    
    getSubmitValue: function() {
        var me = this,
            format = me.submitFormat || me.format,
            value = me.getValue();
            
        return value ? Ext.Date.format(value, format) : null;
    },

    
    parseDate : function(value) {
        if(!value || Ext.isDate(value)){
            return value;
        }

        var me = this,
            val = me.safeParse(value, me.format),
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;

        if (!val && altFormats) {
            altFormatsArray = altFormatsArray || altFormats.split('|');
            len = altFormatsArray.length;
            for (; i < len && !val; ++i) {
                val = me.safeParse(value, altFormatsArray[i]);
            }
        }
        return val;
    },

    
    formatDate : function(date){
        return Ext.isDate(date) ? Ext.Date.dateFormat(date, this.format) : date;
    },

    createPicker: function() {
        var me = this,
            format = Ext.String.format;

        return Ext.create('Ext.picker.Date', {
            ownerCt: me.ownerCt,
            renderTo: document.body,
            floating: true,
            hidden: true,
            focusOnShow: true,
            minDate: me.minValue,
            maxDate: me.maxValue,
            disabledDatesRE: me.disabledDatesRE,
            disabledDatesText: me.disabledDatesText,
            disabledDays: me.disabledDays,
            disabledDaysText: me.disabledDaysText,
            format: me.format,
            showToday: me.showToday,
            startDay: me.startDay,
            minText: format(me.minText, me.formatDate(me.minValue)),
            maxText: format(me.maxText, me.formatDate(me.maxValue)),
            listeners: {
                scope: me,
                select: me.onSelect
            },
            keyNavConfig: {
                esc: function() {
                    me.collapse();
                }
            }
        });
    },

    onSelect: function(m, d) {
        var me = this;
        
        me.setValue(d);
        me.fireEvent('select', me, d);
        me.collapse();
    },

    
    onExpand: function() {
        var me = this,
            value = me.getValue();
        me.picker.setValue(Ext.isDate(value) ? value : new Date());
    },

    
    onCollapse: function() {
        this.focus(false, 60);
    },

    
    beforeBlur : function(){
        var me = this,
            v = me.parseDate(me.getRawValue()),
            focusTask = me.focusTask;
        
        if (focusTask) {
            focusTask.cancel();
        }
        
        if (v) {
            me.setValue(v);
        }
    }

    
    
    
    
});


Ext.define('Ext.form.field.Display', {
    extend:'Ext.form.field.Base',
    alias: 'widget.displayfield',
    requires: ['Ext.util.Format', 'Ext.XTemplate'],
    alternateClassName: ['Ext.form.DisplayField', 'Ext.form.Display'],
    fieldSubTpl: [
        '<div id="{id}" class="{fieldCls}"></div>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    
    fieldCls: Ext.baseCSSPrefix + 'form-display-field',

    
    htmlEncode: false,

    validateOnChange: false,

    initEvents: Ext.emptyFn,

    submitValue: false,

    isValid: function() {
        return true;
    },

    validate: function() {
        return true;
    },

    getRawValue: function() {
        return this.rawValue;
    },

    setRawValue: function(value) {
        var me = this;
        value = Ext.value(value, '');
        me.rawValue = value;
        if (me.rendered) {
            me.inputEl.dom.innerHTML = me.htmlEncode ? Ext.util.Format.htmlEncode(value) : value;
        }
        return value;
    },

    
    getContentTarget: function() {
        return this.inputEl;
    }

    
    
    
    
    
    
});


Ext.define("Ext.form.field.File", {
    extend: 'Ext.form.field.Text',
    alias: ['widget.filefield', 'widget.fileuploadfield'],
    alternateClassName: ['Ext.form.FileUploadField', 'Ext.ux.form.FileUploadField', 'Ext.form.File'],
    uses: ['Ext.button.Button', 'Ext.layout.component.field.File'],

    
    buttonText: 'Browse...',

    
    buttonOnly: false,

    
    buttonMargin: 3,

    

    

    

    

    
    fieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap',


    
    readOnly: true,
    componentLayout: 'filefield',

    
    onRender: function() {
        var me = this,
            inputEl;

        me.callParent(arguments);

        me.createButton();
        me.createFileInput();
        
        
        
        if (me.disabled) {
            me.disableItems();
        }

        inputEl = me.inputEl;
        inputEl.dom.removeAttribute('name'); 
        if (me.buttonOnly) {
            inputEl.setDisplayed(false);
        }
    },

    
    createButton: function() {
        var me = this;
        me.button = Ext.widget('button', Ext.apply({
            renderTo: me.bodyEl,
            text: me.buttonText,
            cls: Ext.baseCSSPrefix + 'form-file-btn',
            preventDefault: false,
            style: me.buttonOnly ? '' : 'margin-left:' + me.buttonMargin + 'px'
        }, me.buttonConfig));
    },

    
    createFileInput : function() {
        var me = this;
        me.fileInputEl = me.button.el.createChild({
            name: me.getName(),
            cls: Ext.baseCSSPrefix + 'form-file-input',
            tag: 'input',
            type: 'file',
            size: 1
        }).on('change', me.onFileChange, me);
    },

    
    onFileChange: function() {
        this.lastValue = null; 
        Ext.form.field.File.superclass.setValue.call(this, this.fileInputEl.dom.value);
    },

    
    setValue: Ext.emptyFn,

    reset : function(){
        this.fileInputEl.remove();
        this.createFileInput();
        this.callParent();
    },

    onDisable: function(){
        this.callParent();
        this.disableItems();
    },
    
    disableItems: function(){
        var file = this.fileInputEl,
            button = this.button;
             
        if (file) {
            file.dom.disabled = true;
        }
        if (button) {
            button.disable();
        }    
    },

    onEnable: function(){
        var me = this;
        me.callParent();
        me.fileInputEl.dom.disabled = false;
        me.button.enable();
    },

    isFileUpload: function() {
        return true;
    },

    extractFileInput: function() {
        var fileInput = this.fileInputEl.dom;
        this.reset();
        return fileInput;
    },

    onDestroy: function(){
        Ext.destroyMembers(this, 'fileInputEl', 'button');
        this.callParent();
    }


});


Ext.define('Ext.form.field.Hidden', {
    extend:'Ext.form.field.Base',
    alias: ['widget.hiddenfield', 'widget.hidden'],
    alternateClassName: 'Ext.form.Hidden',

    
    inputType : 'hidden',
    hideLabel: true,
    
    initComponent: function(){
        this.formItemCls += '-hidden';
        this.callParent();    
    },

    
    initEvents: Ext.emptyFn,
    setSize : Ext.emptyFn,
    setWidth : Ext.emptyFn,
    setHeight : Ext.emptyFn,
    setPosition : Ext.emptyFn,
    setPagePosition : Ext.emptyFn,
    markInvalid : Ext.emptyFn,
    clearInvalid : Ext.emptyFn
});


Ext.define('Ext.picker.Color', {
    extend: 'Ext.Component',
    requires: 'Ext.XTemplate',
    alias: 'widget.colorpicker',
    alternateClassName: 'Ext.ColorPalette',
    
    
    componentCls : Ext.baseCSSPrefix + 'color-picker',
    
    
    selectedCls: Ext.baseCSSPrefix + 'color-picker-selected',
    
    
    value : null,
    
    
    clickEvent :'click',

    
    allowReselect : false,

    
    colors : [
        '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
        '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
        'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
        'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
        'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
    ],

    
    
    
    colorRe: /(?:^|\s)color-(.{6})(?:\s|$)/,
    
    constructor: function() {
        this.renderTpl = Ext.create('Ext.XTemplate', '<tpl for="colors"><a href="#" class="color-{.}" hidefocus="on"><em><span style="background:#{.}" unselectable="on">&#160;</span></em></a></tpl>');
        this.callParent(arguments);
    },
    
    
    initComponent : function(){
        var me = this;
        
        this.callParent(arguments);
        me.addEvents(
            
            'select'
        );

        if (me.handler) {
            me.on('select', me.handler, me.scope, true);
        }
    },


    
    onRender : function(container, position){
        var me = this,
            clickEvent = me.clickEvent;
            
        Ext.apply(me.renderData, {
            itemCls: me.itemCls,
            colors: me.colors    
        });
        this.callParent(arguments);

        me.mon(me.el, clickEvent, me.handleClick, me, {delegate: 'a'});
        
        if(clickEvent != 'click'){
            me.mon(me.el, 'click', Ext.emptyFn, me, {delegate: 'a', stopEvent: true});
        }
    },

    
    afterRender : function(){
        var me = this,
            value;
            
        this.callParent(arguments);
        if (me.value) {
            value = me.value;
            me.value = null;
            me.select(value, true);
        }
    },

    
    handleClick : function(event, target){
        var me = this,
            color;
            
        event.stopEvent();
        if (!me.disabled) {
            color = target.className.match(me.colorRe)[1];
            me.select(color.toUpperCase());
        }
    },

    
    select : function(color, suppressEvent){
        
        var me = this,
            selectedCls = me.selectedCls,
            value = me.value,
            el;
            
        color = color.replace('#', '');
        if (!me.rendered) {
            me.value = color;
            return;
        }
        
        
        if (color != value || me.allowReselect) {
            el = me.el;

            if (me.value) {
                el.down('a.color-' + value).removeCls(selectedCls);
            }
            el.down('a.color-' + color).addCls(selectedCls);
            me.value = color;
            if (suppressEvent !== true) {
                me.fireEvent('select', me, color);
            }
        }
    },
    
    
    getValue: function(){
        return this.value || null;
    }
});



Ext.define('Ext.layout.component.field.HtmlEditor', {
    extend: 'Ext.layout.component.field.Field',
    alias: ['layout.htmleditor'],

    type: 'htmleditor',

    sizeBodyContents: function(width, height) {
        var me = this,
            owner = me.owner,
            bodyEl = owner.bodyEl,
            toolbar = owner.getToolbar(),
            textarea = owner.textareaEl,
            iframe = owner.iframeEl,
            editorHeight;

        if (Ext.isNumber(width)) {
            width -= bodyEl.getFrameWidth('lr');
        }
        toolbar.setWidth(width);
        textarea.setWidth(width);
        iframe.setWidth(width);

        
        if (Ext.isNumber(height)) {
            editorHeight = height - toolbar.getHeight() - bodyEl.getFrameWidth('tb');
            textarea.setHeight(editorHeight);
            iframe.setHeight(editorHeight);
        }
    }
});

Ext.define('Ext.form.field.HtmlEditor', {
    extend:'Ext.Component',
    mixins: {
        labelable: 'Ext.form.Labelable',
        field: 'Ext.form.field.Field'
    },
    alias: 'widget.htmleditor',
    alternateClassName: 'Ext.form.HtmlEditor',
    requires: [
        'Ext.tip.QuickTipManager',
        'Ext.picker.Color',
        'Ext.toolbar.Item',
        'Ext.toolbar.Toolbar',
        'Ext.util.Format',
        'Ext.layout.component.field.HtmlEditor'
    ],

    fieldSubTpl: [
        '<div class="{toolbarWrapCls}"></div>',
        '<textarea id="{id}" name="{name}" tabIndex="-1" class="{textareaCls}" ',
            'style="{size}" autocomplete="off"></textarea>',
        '<iframe name="{iframeName}" frameBorder="0" style="overflow:auto;{size}" src="{iframeSrc}"></iframe>',
        {
            compiled: true,
            disableFormats: true
        }
    ],

    
    enableFormat : true,
    
    enableFontSize : true,
    
    enableColors : true,
    
    enableAlignments : true,
    
    enableLists : true,
    
    enableSourceEdit : true,
    
    enableLinks : true,
    
    enableFont : true,
    
    createLinkText : 'Please enter the URL for the link:',
    
    defaultLinkValue : 'http:/'+'/',
    
    fontFamilies : [
        'Arial',
        'Courier New',
        'Tahoma',
        'Times New Roman',
        'Verdana'
    ],
    defaultFont: 'tahoma',
    
    defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',

    fieldBodyCls: Ext.baseCSSPrefix + 'html-editor-wrap',

    componentLayout: 'htmleditor',

    
    initialized : false,
    activated : false,
    sourceEditMode : false,
    iframePad:3,
    hideMode:'offsets',

    maskOnDisable: true,
    
    
    initComponent : function(){
        var me = this;

        me.addEvents(
            
            'initialize',
            
            'activate',
             
            'beforesync',
             
            'beforepush',
             
            'sync',
             
            'push',
             
            'editmodechange'
        );

        me.callParent(arguments);

        
        me.initLabelable();
        me.initField();
    },

    
    createToolbar : function(editor){
        var me = this,
            items = [],
            tipsEnabled = Ext.tip.QuickTipManager && Ext.tip.QuickTipManager.isEnabled(),
            baseCSSPrefix = Ext.baseCSSPrefix,
            fontSelectItem, toolbar, undef;

        function btn(id, toggle, handler){
            return {
                itemId : id,
                cls : baseCSSPrefix + 'btn-icon',
                iconCls: baseCSSPrefix + 'edit-'+id,
                enableToggle:toggle !== false,
                scope: editor,
                handler:handler||editor.relayBtnCmd,
                clickEvent:'mousedown',
                tooltip: tipsEnabled ? editor.buttonTips[id] || undef : undef,
                overflowText: editor.buttonTips[id].title || undef,
                tabIndex:-1
            };
        }


        if (me.enableFont && !Ext.isSafari2) {
            fontSelectItem = Ext.widget('component', {
                renderTpl: [
                    '<select class="{cls}">',
                        '<tpl for="fonts">',
                            '<option value="{[values.toLowerCase()]}" style="font-family:{.}"<tpl if="values.toLowerCase()==parent.defaultFont"> selected</tpl>>{.}</option>',
                        '</tpl>',
                    '</select>'
                ],
                renderData: {
                    cls: baseCSSPrefix + 'font-select',
                    fonts: me.fontFamilies,
                    defaultFont: me.defaultFont
                },
                renderSelectors: {
                    selectEl: 'select'
                },
                onDisable: function() {
                    var selectEl = this.selectEl;
                    if (selectEl) {
                        selectEl.dom.disabled = true;
                    }
                    Ext.Component.superclass.onDisable.apply(this, arguments);
                },
                onEnable: function() {
                    var selectEl = this.selectEl;
                    if (selectEl) {
                        selectEl.dom.disabled = false;
                    }
                    Ext.Component.superclass.onEnable.apply(this, arguments);
                }
            });

            items.push(
                fontSelectItem,
                '-'
            );
        }

        if (me.enableFormat) {
            items.push(
                btn('bold'),
                btn('italic'),
                btn('underline')
            );
        }

        if (me.enableFontSize) {
            items.push(
                '-',
                btn('increasefontsize', false, me.adjustFont),
                btn('decreasefontsize', false, me.adjustFont)
            );
        }

        if (me.enableColors) {
            items.push(
                '-', {
                    itemId: 'forecolor',
                    cls: baseCSSPrefix + 'btn-icon',
                    iconCls: baseCSSPrefix + 'edit-forecolor',
                    overflowText: editor.buttonTips.forecolor.title,
                    tooltip: tipsEnabled ? editor.buttonTips.forecolor || undef : undef,
                    tabIndex:-1,
                    menu : Ext.widget('menu', {
                        plain: true,
                        items: [{
                            xtype: 'colorpicker',
                            allowReselect: true,
                            focus: Ext.emptyFn,
                            value: '000000',
                            plain: true,
                            clickEvent: 'mousedown',
                            handler: function(cp, color) {
                                me.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
                                me.deferFocus();
                                this.up('menu').hide();
                            }
                        }]
                    })
                }, {
                    itemId: 'backcolor',
                    cls: baseCSSPrefix + 'btn-icon',
                    iconCls: baseCSSPrefix + 'edit-backcolor',
                    overflowText: editor.buttonTips.backcolor.title,
                    tooltip: tipsEnabled ? editor.buttonTips.backcolor || undef : undef,
                    tabIndex:-1,
                    menu : Ext.widget('menu', {
                        plain: true,
                        items: [{
                            xtype: 'colorpicker',
                            focus: Ext.emptyFn,
                            value: 'FFFFFF',
                            plain: true,
                            allowReselect: true,
                            clickEvent: 'mousedown',
                            handler: function(cp, color) {
                                if (Ext.isGecko) {
                                    me.execCmd('useCSS', false);
                                    me.execCmd('hilitecolor', color);
                                    me.execCmd('useCSS', true);
                                    me.deferFocus();
                                } else {
                                    me.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);
                                    me.deferFocus();
                                }
                                this.up('menu').hide();
                            }
                        }]
                    })
                }
            );
        }

        if (me.enableAlignments) {
            items.push(
                '-',
                btn('justifyleft'),
                btn('justifycenter'),
                btn('justifyright')
            );
        }

        if (!Ext.isSafari2) {
            if (me.enableLinks) {
                items.push(
                    '-',
                    btn('createlink', false, me.createLink)
                );
            }

            if (me.enableLists) {
                items.push(
                    '-',
                    btn('insertorderedlist'),
                    btn('insertunorderedlist')
                );
            }
            if (me.enableSourceEdit) {
                items.push(
                    '-',
                    btn('sourceedit', true, function(btn){
                        me.toggleSourceEdit(!me.sourceEditMode);
                    })
                );
            }
        }

        
        toolbar = Ext.widget('toolbar', {
            renderTo: me.toolbarWrap,
            enableOverflow: true,
            items: items
        });

        if (fontSelectItem) {
            me.fontSelect = fontSelectItem.selectEl;

            me.mon(me.fontSelect, 'change', function(){
                me.relayCmd('fontname', me.fontSelect.dom.value);
                me.deferFocus();
            });
        }

        
        me.mon(toolbar.el, 'click', function(e){
            e.preventDefault();
        });

        me.toolbar = toolbar;
    },

    onDisable: function() {
        this.bodyEl.mask();
        this.callParent(arguments);
    },

    onEnable: function() {
        this.bodyEl.unmask();
        this.callParent(arguments);
    },

    
    setReadOnly: function(readOnly) {
        var me = this,
            textareaEl = me.textareaEl,
            iframeEl = me.iframeEl,
            body;

        me.readOnly = readOnly;

        if (textareaEl) {
            textareaEl.dom.readOnly = readOnly;
        }

        if (me.initialized) {
            body = me.getEditorBody();
            if (Ext.isIE) {
                
                iframeEl.setDisplayed(false);
                body.contentEditable = !readOnly;
                iframeEl.setDisplayed(true);
            } else {
                me.setDesignMode(!readOnly);
            }
            if (body) {
                body.style.cursor = readOnly ? 'default' : 'text';
            }
            me.disableItems(readOnly);
        }
    },

    
    getDocMarkup: function() {
        var me = this,
            h = me.iframeEl.getHeight() - me.iframePad * 2;
        return Ext.String.format('<html><head><style type="text/css">body{border:0;margin:0;padding:{0}px;height:{1}px;cursor:text}</style></head><body></body></html>', me.iframePad, h);
    },

    
    getEditorBody: function() {
        var doc = this.getDoc();
        return doc.body || doc.documentElement;
    },

    
    getDoc: function() {
        return (!Ext.isIE && this.iframeEl.dom.contentDocument) || this.getWin().document;
    },

    
    getWin: function() {
        return Ext.isIE ? this.iframeEl.dom.contentWindow : window.frames[this.iframeEl.dom.name];
    },

    
    onRender: function() {
        var me = this,
            renderSelectors = me.renderSelectors;

        Ext.applyIf(renderSelectors, me.getLabelableSelectors());

        Ext.applyIf(renderSelectors, {
            toolbarWrap: 'div.' + Ext.baseCSSPrefix + 'html-editor-tb',
            iframeEl: 'iframe',
            textareaEl: 'textarea'
        });

        me.callParent(arguments);

        me.textareaEl.dom.value = me.value || '';

        
        me.monitorTask = Ext.TaskManager.start({
            run: me.checkDesignMode,
            scope: me,
            interval:100
        });

        me.createToolbar(me);
        me.disableItems(true);
    },

    initRenderTpl: function() {
        var me = this;
        if (!me.hasOwnProperty('renderTpl')) {
            me.renderTpl = me.getTpl('labelableRenderTpl');
        }
        return me.callParent();
    },

    initRenderData: function() {
        return Ext.applyIf(this.callParent(), this.getLabelableRenderData());
    },

    getSubTplData: function() {
        var cssPrefix = Ext.baseCSSPrefix;
        return {
            toolbarWrapCls: cssPrefix + 'html-editor-tb',
            textareaCls: cssPrefix + 'hidden',
            iframeName: Ext.id(),
            iframeSrc: Ext.SSL_SECURE_URL,
            size: 'height:100px;'
        };
    },

    getSubTplMarkup: function() {
        return this.getTpl('fieldSubTpl').apply(this.getSubTplData());
    },

    getBodyNaturalWidth: function() {
        return 565;
    },

    initFrameDoc: function() {
        var me = this,
            doc, task;

        Ext.TaskManager.stop(me.monitorTask);

        doc = me.getDoc();
        me.win = me.getWin();

        doc.open();
        doc.write(me.getDocMarkup());
        doc.close();

        task = { 
            run: function() {
                var doc = me.getDoc();
                if (doc.body || doc.readyState === 'complete') {
                    Ext.TaskManager.stop(task);
                    me.setDesignMode(true);
                    Ext.defer(me.initEditor, 10, me);
                }
            },
            interval : 10,
            duration:10000,
            scope: me
        };
        Ext.TaskManager.start(task);
    },

    checkDesignMode: function() {
        var me = this,
            doc = me.getDoc();
        if (doc && (!doc.editorInitialized || me.getDesignMode() !== 'on')) {
            me.initFrameDoc();
        }
    },

    
    setDesignMode: function(mode) {
        var me = this,
            doc = me.getDoc();
        if (doc) {
            if (me.readOnly) {
                mode = false;
            }
            doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';
        }
    },

    
    getDesignMode: function() {
        var doc = this.getDoc();
        return !doc ? '' : String(doc.designMode).toLowerCase();
    },

    disableItems: function(disabled) {
        this.getToolbar().items.each(function(item){
            if(item.getItemId() !== 'sourceedit'){
                item.setDisabled(disabled);
            }
        });
    },

    
    toggleSourceEdit: function(sourceEditMode) {
        var me = this,
            iframe = me.iframeEl,
            textarea = me.textareaEl,
            hiddenCls = Ext.baseCSSPrefix + 'hidden',
            btn = me.getToolbar().getComponent('sourceedit');

        if (!Ext.isBoolean(sourceEditMode)) {
            sourceEditMode = !me.sourceEditMode;
        }
        me.sourceEditMode = sourceEditMode;

        if (btn.pressed !== sourceEditMode) {
            btn.toggle(sourceEditMode);
        }
        if (sourceEditMode) {
            me.disableItems(true);
            me.syncValue();
            iframe.addCls(hiddenCls);
            textarea.removeCls(hiddenCls);
            textarea.dom.removeAttribute('tabIndex');
            textarea.focus();
        }
        else {
            if (me.initialized) {
                me.disableItems(me.readOnly);
            }
            me.pushValue();
            iframe.removeCls(hiddenCls);
            textarea.addCls(hiddenCls);
            textarea.dom.setAttribute('tabIndex', -1);
            me.deferFocus();
        }
        me.fireEvent('editmodechange', me, sourceEditMode);
        me.doComponentLayout();
    },

    
    createLink : function() {
        var url = prompt(this.createLinkText, this.defaultLinkValue);
        if (url && url !== 'http:/'+'/') {
            this.relayCmd('createlink', url);
        }
    },

    clearInvalid: Ext.emptyFn,

    
    setValue: function(value) {
        var me = this,
            textarea = me.textareaEl;
        me.mixins.field.setValue.call(me, value);
        if (value === null || value === undefined) {
            value = '';
        }
        if (textarea) {
            textarea.dom.value = value;
        }
        me.pushValue();
        return this;
    },

    
    cleanHtml: function(html) {
        html = String(html);
        if (Ext.isWebKit) { 
            html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
        }

        
        if (html.charCodeAt(0) === this.defaultValue.replace(/\D/g, '')) {
            html = html.substring(1);
        }
        return html;
    },

    
    syncValue : function(){
        var me = this,
            body, html, bodyStyle, match;
        if (me.initialized) {
            body = me.getEditorBody();
            html = body.innerHTML;
            if (Ext.isWebKit) {
                bodyStyle = body.getAttribute('style'); 
                match = bodyStyle.match(/text-align:(.*?);/i);
                if (match && match[1]) {
                    html = '<div style="' + match[0] + '">' + html + '</div>';
                }
            }
            html = me.cleanHtml(html);
            if (me.fireEvent('beforesync', me, html) !== false) {
                me.textareaEl.dom.value = html;
                me.fireEvent('sync', me, html);
            }
        }
    },

    
    getValue : function() {
        var me = this,
            value;
        if (!me.sourceEditMode) {
            me.syncValue();
        }
        value = me.rendered ? me.textareaEl.dom.value : me.value;
        me.value = value;
        return value;
    },

    
    pushValue: function() {
        var me = this,
            v;
        if(me.initialized){
            v = me.textareaEl.dom.value || '';
            if (!me.activated && v.length < 1) {
                v = me.defaultValue;
            }
            if (me.fireEvent('beforepush', me, v) !== false) {
                me.getEditorBody().innerHTML = v;
                if (Ext.isGecko) {
                    
                    me.setDesignMode(false);  
                    me.setDesignMode(true);
                }
                me.fireEvent('push', me, v);
            }
        }
    },

    
    deferFocus : function(){
         this.focus(false, true);
    },

    getFocusEl: function() {
        var me = this,
            win = me.win;
        return win && !me.sourceEditMode ? win : me.textareaEl;
    },

    
    initEditor : function(){
        
        try {
            var me = this,
                dbody = me.getEditorBody(),
                ss = me.textareaEl.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),
                doc,
                fn;

            ss['background-attachment'] = 'fixed'; 
            dbody.bgProperties = 'fixed'; 

            Ext.core.DomHelper.applyStyles(dbody, ss);

            doc = me.getDoc();

            if (doc) {
                try {
                    Ext.EventManager.removeAll(doc);
                } catch(e) {}
            }

            
            fn = Ext.Function.bind(me.onEditorEvent, me);
            Ext.EventManager.on(doc, {
                mousedown: fn,
                dblclick: fn,
                click: fn,
                keyup: fn,
                buffer:100
            });

            
            
            
            
            
            fn = me.onRelayedEvent;
            Ext.EventManager.on(doc, {
                mousedown: fn, 
                mousemove: fn, 
                mouseup: fn,   
                click: fn,     
                dblclick: fn,  
                scope: me
            });

            if (Ext.isGecko) {
                Ext.EventManager.on(doc, 'keypress', me.applyCommand, me);
            }
            if (me.fixKeys) {
                Ext.EventManager.on(doc, 'keydown', me.fixKeys, me);
            }

            
            Ext.EventManager.on(window, 'unload', me.beforeDestroy, me);
            doc.editorInitialized = true;

            me.initialized = true;
            me.pushValue();
            me.setReadOnly(me.readOnly);
            me.fireEvent('initialize', me);
        } catch(ex) {
            
        }
    },

    
    beforeDestroy : function(){
        var me = this,
            monitorTask = me.monitorTask,
            doc, prop;

        if (monitorTask) {
            Ext.TaskManager.stop(monitorTask);
        }
        if (me.rendered) {
            try {
                doc = me.getDoc();
                if (doc) {
                    Ext.EventManager.removeAll(doc);
                    for (prop in doc) {
                        if (doc.hasOwnProperty(prop)) {
                            delete doc[prop];
                        }
                    }
                }
            } catch(e) {
                
            }
            Ext.destroyMembers('tb', 'toolbarWrap', 'iframeEl', 'textareaEl');
        }
        me.callParent();
    },

    
    onRelayedEvent: function (event) {
        

        var iframeEl = this.iframeEl,
            iframeXY = iframeEl.getXY(),
            eventXY = event.getXY();

        
        
        event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];

        event.injectEvent(iframeEl); 

        event.xy = eventXY; 
    },

    
    onFirstFocus : function(){
        var me = this,
            selection, range;
        me.activated = true;
        me.disableItems(me.readOnly);
        if (Ext.isGecko) { 
            me.win.focus();
            selection = me.win.getSelection();
            if (!selection.focusNode || selection.focusNode.nodeType !== 3) {
                range = selection.getRangeAt(0);
                range.selectNodeContents(me.getEditorBody());
                range.collapse(true);
                me.deferFocus();
            }
            try {
                me.execCmd('useCSS', true);
                me.execCmd('styleWithCSS', false);
            } catch(e) {
                
            }
        }
        me.fireEvent('activate', me);
    },

    
    adjustFont: function(btn) {
        var adjust = btn.getItemId() === 'increasefontsize' ? 1 : -1,
            size = this.getDoc().queryCommandValue('FontSize') || '2',
            isPxSize = Ext.isString(size) && size.indexOf('px') !== -1,
            isSafari;
        size = parseInt(size, 10);
        if (isPxSize) {
            
            
            if (size <= 10) {
                size = 1 + adjust;
            }
            else if (size <= 13) {
                size = 2 + adjust;
            }
            else if (size <= 16) {
                size = 3 + adjust;
            }
            else if (size <= 18) {
                size = 4 + adjust;
            }
            else if (size <= 24) {
                size = 5 + adjust;
            }
            else {
                size = 6 + adjust;
            }
            size = Ext.Number.constrain(size, 1, 6);
        } else {
            isSafari = Ext.isSafari;
            if (isSafari) { 
                adjust *= 2;
            }
            size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0);
        }
        this.execCmd('FontSize', size);
    },

    
    onEditorEvent: function(e) {
        this.updateToolbar();
    },

    
    updateToolbar: function() {
        var me = this,
            btns, doc, name, fontSelect;

        if (me.readOnly) {
            return;
        }

        if (!me.activated) {
            me.onFirstFocus();
            return;
        }

        btns = me.getToolbar().items.map;
        doc = me.getDoc();

        if (me.enableFont && !Ext.isSafari2) {
            name = (doc.queryCommandValue('FontName') || me.defaultFont).toLowerCase();
            fontSelect = me.fontSelect.dom;
            if (name !== fontSelect.value) {
                fontSelect.value = name;
            }
        }

        function updateButtons() {
            Ext.Array.forEach(Ext.Array.toArray(arguments), function(name) {
                btns[name].toggle(doc.queryCommandState(name));
            });
        }
        if(me.enableFormat){
            updateButtons('bold', 'italic', 'underline');
        }
        if(me.enableAlignments){
            updateButtons('justifyleft', 'justifycenter', 'justifyright');
        }
        if(!Ext.isSafari2 && me.enableLists){
            updateButtons('insertorderedlist', 'insertunorderedlist');
        }

        Ext.menu.Manager.hideAll();

        me.syncValue();
    },

    
    relayBtnCmd: function(btn) {
        this.relayCmd(btn.getItemId());
    },

    
    relayCmd: function(cmd, value) {
        Ext.defer(function() {
            var me = this;
            me.focus();
            me.execCmd(cmd, value);
            me.updateToolbar();
        }, 10, this);
    },

    
    execCmd : function(cmd, value){
        var me = this,
            doc = me.getDoc(),
            undef;
        doc.execCommand(cmd, false, value === undef ? null : value);
        me.syncValue();
    },

    
    applyCommand : function(e){
        if (e.ctrlKey) {
            var me = this,
                c = e.getCharCode(), cmd;
            if (c > 0) {
                c = String.fromCharCode(c);
                switch (c) {
                    case 'b':
                        cmd = 'bold';
                    break;
                    case 'i':
                        cmd = 'italic';
                    break;
                    case 'u':
                        cmd = 'underline';
                    break;
                }
                if (cmd) {
                    me.win.focus();
                    me.execCmd(cmd);
                    me.deferFocus();
                    e.preventDefault();
                }
            }
        }
    },

    
    insertAtCursor : function(text){
        var me = this,
            range;

        if (me.activated) {
            me.win.focus();
            if (Ext.isIE) {
                range = me.getDoc().selection.createRange();
                if (range) {
                    range.pasteHTML(text);
                    me.syncValue();
                    me.deferFocus();
                }
            }else{
                me.execCmd('InsertHTML', text);
                me.deferFocus();
            }
        }
    },

    
    fixKeys: function() { 
        if (Ext.isIE) {
            return function(e){
                var me = this,
                    k = e.getKey(),
                    doc = me.getDoc(),
                    range, target;
                if (k === e.TAB) {
                    e.stopEvent();
                    range = doc.selection.createRange();
                    if(range){
                        range.collapse(true);
                        range.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
                        me.deferFocus();
                    }
                }
                else if (k === e.ENTER) {
                    range = doc.selection.createRange();
                    if (range) {
                        target = range.parentElement();
                        if(!target || target.tagName.toLowerCase() !== 'li'){
                            e.stopEvent();
                            range.pasteHTML('<br />');
                            range.collapse(false);
                            range.select();
                        }
                    }
                }
            };
        }

        if (Ext.isOpera) {
            return function(e){
                var me = this;
                if (e.getKey() === e.TAB) {
                    e.stopEvent();
                    me.win.focus();
                    me.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');
                    me.deferFocus();
                }
            };
        }

        if (Ext.isWebKit) {
            return function(e){
                var me = this,
                    k = e.getKey();
                if (k === e.TAB) {
                    e.stopEvent();
                    me.execCmd('InsertText','\t');
                    me.deferFocus();
                }
                else if (k === e.ENTER) {
                    e.stopEvent();
                    me.execCmd('InsertHtml','<br /><br />');
                    me.deferFocus();
                }
            };
        }

        return null; 
    }(),

    
    getToolbar : function(){
        return this.toolbar;
    },

    
    buttonTips : {
        bold : {
            title: 'Bold (Ctrl+B)',
            text: 'Make the selected text bold.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        italic : {
            title: 'Italic (Ctrl+I)',
            text: 'Make the selected text italic.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        underline : {
            title: 'Underline (Ctrl+U)',
            text: 'Underline the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        increasefontsize : {
            title: 'Grow Text',
            text: 'Increase the font size.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        decreasefontsize : {
            title: 'Shrink Text',
            text: 'Decrease the font size.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        backcolor : {
            title: 'Text Highlight Color',
            text: 'Change the background color of the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        forecolor : {
            title: 'Font Color',
            text: 'Change the color of the selected text.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifyleft : {
            title: 'Align Text Left',
            text: 'Align text to the left.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifycenter : {
            title: 'Center Text',
            text: 'Center text in the editor.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        justifyright : {
            title: 'Align Text Right',
            text: 'Align text to the right.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        insertunorderedlist : {
            title: 'Bullet List',
            text: 'Start a bulleted list.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        insertorderedlist : {
            title: 'Numbered List',
            text: 'Start a numbered list.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        createlink : {
            title: 'Hyperlink',
            text: 'Make the selected text a hyperlink.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        },
        sourceedit : {
            title: 'Source Edit',
            text: 'Switch to source editing mode.',
            cls: Ext.baseCSSPrefix + 'html-editor-tip'
        }
    }

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
});


Ext.define('Ext.form.field.Radio', {
    extend:'Ext.form.field.Checkbox',
    alias: ['widget.radiofield', 'widget.radio'],
    alternateClassName: 'Ext.form.Radio',
    requires: ['Ext.form.RadioManager'],

    isRadio: true,

    

    
    inputType: 'radio',
    ariaRole: 'radio',

    
    getGroupValue: function() {
        var selected = this.getManager().getChecked(this.name);
        return selected ? selected.inputValue : null;
    },

    
    onBoxClick: function(e) {
        var me = this;
        if (!me.disabled && !me.readOnly) {
            this.setValue(true);
        }
    },

    
    setValue: function(v) {
        var me = this,
            active;

        if (Ext.isBoolean(v)) {
            me.callParent(arguments);
        } else {
            active = me.getManager().getWithValue(me.name, v).getAt(0);
            if (active) {
                active.setValue(true);
            }
        }
        return me;
    },

    
    getSubmitValue: function() {
        return this.checked ? this.inputValue : null;
    },

    getModelData: function() {
        return this.getSubmitData();
    },

    
    onChange: function(newVal, oldVal) {
        var me = this;
        me.callParent(arguments);

        if (newVal) {
            this.getManager().getByName(me.name).each(function(item){
                if (item !== me) {
                    item.setValue(false);
                }
            }, me);
        }
    },

    
    beforeDestroy: function(){
        this.callParent();
        this.getManager().removeAtKey(this.id);
    },

    
    getManager: function() {
        return Ext.form.RadioManager;
    }
});


Ext.define('Ext.picker.Time', {
    extend: 'Ext.view.BoundList',
    alias: 'widget.timepicker',
    requires: ['Ext.data.Store', 'Ext.Date'],

    

    

    
    increment: 15,

    
    format : "g:i A",

    
    displayField: 'disp',

    
    initDate: [2008,1,1],

    componentCls: Ext.baseCSSPrefix + 'timepicker',

    
    loadingText: '',

    initComponent: function() {
        var me = this,
            dateUtil = Ext.Date,
            clearTime = dateUtil.clearTime,
            initDate = me.initDate.join('/');

        
        me.absMin = clearTime(new Date(initDate));
        me.absMax = dateUtil.add(clearTime(new Date(initDate)), 'mi', (24 * 60) - 1);

        me.store = me.createStore();
        me.updateList();

        this.callParent();
    },

    
    setMinValue: function(value) {
        this.minValue = value;
        this.updateList();
    },

    
    setMaxValue: function(value) {
        this.maxValue = value;
        this.updateList();
    },

    
    normalizeDate: function(date) {
        var initDate = this.initDate;
        date.setFullYear(initDate[0], initDate[1] - 1, initDate[2]);
        return date;
    },

    
    updateList: function() {
        var me = this,
            min = me.normalizeDate(me.minValue || me.absMin),
            max = me.normalizeDate(me.maxValue || me.absMax);

        me.store.filterBy(function(record) {
            var date = record.get('date');
            return date >= min && date <= max;
        });
    },

    
    createStore: function() {
        var me = this,
            utilDate = Ext.Date,
            times = [],
            min = me.absMin,
            max = me.absMax;

        while(min <= max){
            times.push({
                disp: utilDate.dateFormat(min, me.format),
                date: min
            });
            min = utilDate.add(min, 'mi', me.increment);
        }

        return Ext.create('Ext.data.Store', {
            fields: ['disp', 'date'],
            data: times
        });
    }

});


Ext.define('Ext.form.field.Time', {
    extend:'Ext.form.field.Picker',
    alias: 'widget.timefield',
    requires: ['Ext.form.field.Date', 'Ext.picker.Time', 'Ext.view.BoundListKeyNav', 'Ext.Date'],
    alternateClassName: ['Ext.form.TimeField', 'Ext.form.Time'],

    
    triggerCls: Ext.baseCSSPrefix + 'form-time-trigger',

    

    

    
    minText : "The time in this field must be equal to or after {0}",

    
    maxText : "The time in this field must be equal to or before {0}",

    
    invalidText : "{0} is not a valid time",

    
    format : "g:i A",

    

    
    altFormats : "g:ia|g:iA|g:i a|g:i A|h:i|g:i|H:i|ga|ha|gA|h a|g a|g A|gi|hi|gia|hia|g|H|gi a|hi a|giA|hiA|gi A|hi A",

    
    increment: 15,

    
    pickerMaxHeight: 300,

    
    selectOnTab: true,

    
    initDate: '1/1/2008',
    initDateFormat: 'j/n/Y',


    initComponent: function() {
        var me = this,
            min = me.minValue,
            max = me.maxValue;
        if (min) {
            me.setMinValue(min);
        }
        if (max) {
            me.setMaxValue(max);
        }
        this.callParent();
    },

    initValue: function() {
        var me = this,
            value = me.value;

        
        if (Ext.isString(value)) {
            me.value = me.rawToValue(value);
        }

        me.callParent();
    },

    
    setMinValue: function(value) {
        var me = this,
            picker = me.picker;
        me.setLimit(value, true);
        if (picker) {
            picker.setMinValue(me.minValue);
        }
    },

    
    setMaxValue: function(value) {
        var me = this,
            picker = me.picker;
        me.setLimit(value, false);
        if (picker) {
            picker.setMaxValue(me.maxValue);
        }
    },

    
    setLimit: function(value, isMin) {
        var me = this,
            d, val;
        if (Ext.isString(value)) {
            d = me.parseDate(value);
        }
        else if (Ext.isDate(value)) {
            d = value;
        }
        if (d) {
            val = Ext.Date.clearTime(new Date(me.initDate));
            val.setHours(d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
            me[isMin ? 'minValue' : 'maxValue'] = val;
        }
    },

    rawToValue: function(rawValue) {
        return this.parseDate(rawValue) || rawValue || null;
    },

    valueToRaw: function(value) {
        return this.formatDate(this.parseDate(value));
    },

    
    getErrors: function(value) {
        var me = this,
            format = Ext.String.format,
            errors = me.callParent(arguments),
            minValue = me.minValue,
            maxValue = me.maxValue,
            date;

        value = me.formatDate(value || me.processRawValue(me.getRawValue()));

        if (value === null || value.length < 1) { 
             return errors;
        }

        date = me.parseDate(value);
        if (!date) {
            errors.push(format(me.invalidText, value, me.format));
            return errors;
        }

        if (minValue && date < minValue) {
            errors.push(format(me.minText, me.formatDate(minValue)));
        }

        if (maxValue && date > maxValue) {
            errors.push(format(me.maxText, me.formatDate(maxValue)));
        }

        return errors;
    },

    formatDate: function() {
        return Ext.form.field.Date.prototype.formatDate.apply(this, arguments);
    },

    
    parseDate: function(value) {
        if (!value || Ext.isDate(value)) {
            return value;
        }

        var me = this,
            val = me.safeParse(value, me.format),
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;

        if (!val && altFormats) {
            altFormatsArray = altFormatsArray || altFormats.split('|');
            len = altFormatsArray.length;
            for (; i < len && !val; ++i) {
                val = me.safeParse(value, altFormatsArray[i]);
            }
        }
        return val;
    },

    safeParse: function(value, format){
        var me = this,
            utilDate = Ext.Date,
            parsedDate,
            result = null;

        if (utilDate.formatContainsDateInfo(format)) {
            
            result = utilDate.parse(value, format);
        } else {
            
            parsedDate = utilDate.parse(me.initDate + ' ' + value, me.initDateFormat + ' ' + format);
            if (parsedDate) {
                result = parsedDate;
            }
        }
        return result;
    },

    
    getSubmitValue: function() {
        var me = this,
            format = me.submitFormat || me.format,
            value = me.getValue();

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

    
    createPicker: function() {
        var me = this,
            picker = Ext.create('Ext.picker.Time', {
                selModel: {
                    mode: 'SINGLE'
                },
                floating: true,
                hidden: true,
                minValue: me.minValue,
                maxValue: me.maxValue,
                increment: me.increment,
                format: me.format,
                ownerCt: this.ownerCt,
                renderTo: document.body,
                maxHeight: me.pickerMaxHeight,
                focusOnToFront: false
            });

        me.mon(picker.getSelectionModel(), {
            selectionchange: me.onListSelect,
            scope: me
        });

        return picker;
    },

    
    onExpand: function() {
        var me = this,
            keyNav = me.pickerKeyNav,
            selectOnTab = me.selectOnTab,
            picker = me.getPicker(),
            lastSelected = picker.getSelectionModel().lastSelected,
            itemNode;

        if (!keyNav) {
            keyNav = me.pickerKeyNav = Ext.create('Ext.view.BoundListKeyNav', this.inputEl, {
                boundList: picker,
                forceKeyDown: true,
                tab: function(e) {
                    if (selectOnTab) {
                        this.selectHighlighted(e);
                        me.triggerBlur();
                    }
                    
                    return true;
                }
            });
            
            if (selectOnTab) {
                me.ignoreMonitorTab = true;
            }
        }
        Ext.defer(keyNav.enable, 1, keyNav); 

        
        if (lastSelected) {
            itemNode = picker.getNode(lastSelected);
            if (itemNode) {
                picker.highlightItem(itemNode);
                picker.el.scrollChildIntoView(itemNode, false);
            }
        }
    },

    
    onCollapse: function() {
        var me = this,
            keyNav = me.pickerKeyNav;
        if (keyNav) {
            keyNav.disable();
            me.ignoreMonitorTab = false;
        }
    },

    
    onListSelect: function(list, recordArray) {
        var me = this,
            record = recordArray[0],
            val = record ? record.get('date') : null;
        me.setValue(val);
        me.fireEvent('select', me, val);
        me.picker.clearHighlight();
        me.collapse();
        me.inputEl.focus();
    }
});



Ext.define('Ext.grid.CellEditor', {
    extend: 'Ext.Editor',
    constructor: function(config) {
        if (config.field) {
            config.field.monitorTab = false;
        }
        config.autoSize = {
            width: 'boundEl'
        };
        this.callParent(arguments);
    },
    
    
    onShow: function() {
        var first = this.boundEl.first();
        if (first) {
            first.hide();
        }
        this.callParent(arguments);
    },
    
    
    onHide: function() {
        var first = this.boundEl.first();
        if (first) {
            first.show();
        }
        this.callParent(arguments);
    },
    
    
    afterRender: function() {
        this.callParent(arguments);
        var field = this.field;
        if (field.isXType('checkboxfield')) {
            field.mon(field.inputEl, 'mousedown', this.onCheckBoxMouseDown, this);
            field.mon(field.inputEl, 'click', this.onCheckBoxClick, this);
        }
    },
    
    
    onCheckBoxMouseDown: function() {
        this.completeEdit = Ext.emptyFn;
    },
    
    
    onCheckBoxClick: function() {
        delete this.completeEdit;
        this.field.focus(false, 10);
    },
    
    alignment: "tl-tl",
    hideEl : false,
    cls: Ext.baseCSSPrefix + "small-editor " + Ext.baseCSSPrefix + "grid-editor",
    shim: false,
    shadow: false
});

Ext.define('Ext.grid.ColumnLayout', {
    extend: 'Ext.layout.container.HBox',
    alias: 'layout.gridcolumn',
    type : 'column',

    
    clearInnerCtOnLayout: false,

    constructor: function() {
        var me = this;
        me.callParent(arguments);
        if (!Ext.isDefined(me.availableSpaceOffset)) {
            me.availableSpaceOffset = (Ext.getScrollBarWidth() - 2);
        }
    },

    beforeLayout: function() {
        var me = this,
            i = 0,
            items = me.getLayoutItems(),
            len = items.length,
            item, returnValue;

        returnValue = me.callParent(arguments);

        
        me.innerCt.setHeight(23);

        
        if (me.align == 'stretchmax') {
            for (; i < len; i++) {
                item = items[i];
                item.el.setStyle({
                    height: 'auto'
                });
                item.titleContainer.setStyle({
                    height: 'auto',
                    paddingTop: '0'
                });
                if (item.componentLayout && item.componentLayout.lastComponentSize) {
                    item.componentLayout.lastComponentSize.height = item.el.dom.offsetHeight;
                }
            }
        }
        return returnValue;
    },

    
    calculateChildBoxes: function(visibleItems, targetSize) {
        var me = this,
            calculations = me.callParent(arguments),
            boxes = calculations.boxes,
            metaData = calculations.meta,
            len = boxes.length, i = 0, box, item;

        if (targetSize.width && !me.isColumn) {
            
            if (me.owner.forceFit) {

                for (; i < len; i++) {
                    box = boxes[i];
                    item = box.component;

                    
                    item.minWidth = Ext.grid.plugin.HeaderResizer.prototype.minColWidth;

                    
                    
                    item.flex = box.width;
                }

                
                calculations = me.callParent(arguments);
            }
            else if (metaData.tooNarrow) {
                targetSize.width = metaData.desiredSize;
            }
        }

        return calculations;
    },

    afterLayout: function() {
        var me = this,
            i = 0,
            items = me.getLayoutItems(),
            len = items.length;

        me.callParent(arguments);

        
        if (me.align == 'stretchmax') {
            for (; i < len; i++) {
                items[i].setPadding();
            }
        }
    },

    
    
    updateInnerCtSize: function(tSize, calcs) {
        var me    = this,
            extra = 0;

        
        if (!me.isColumn && calcs.meta.tooNarrow) {
            if (
                Ext.isWebKit ||
                Ext.isGecko ||
                (Ext.isIEQuirks && (Ext.isIE6 || Ext.isIE7 || Ext.isIE8))
            ) {
                extra = 1;
            
            } else if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
                extra = 2;
            }
            
            
            extra++;
            tSize.width = calcs.meta.desiredSize + (me.reserveOffset ? me.availableSpaceOffset : 0) + extra;
        }
        return me.callParent(arguments);
    },

    doOwnerCtLayouts: function() {
        var ownerCt = this.owner.ownerCt;
        if (!ownerCt.componentLayout.layoutBusy) {
            ownerCt.doComponentLayout();
        }
    }
});

Ext.define('Ext.grid.LockingView', {
    
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    eventRelayRe: /^(beforeitem|beforecontainer|item|container|cell)/,
    
    constructor: function(config){
        var me = this,
            eventNames = [],
            eventRe = me.eventRelayRe,
            locked = config.locked.getView(),
            normal = config.normal.getView(),
            events,
            event;
        
        Ext.apply(me, {
            lockedView: locked,
            normalView: normal,
            lockedGrid: config.locked,
            normalGrid: config.normal,
            panel: config.panel
        });
        me.mixins.observable.constructor.call(me, config);
        
        
        events = locked.events;
        for (event in events) {
            if (events.hasOwnProperty(event) && eventRe.test(event)) {
                eventNames.push(event);
            }
        }
        me.relayEvents(locked, eventNames);
        me.relayEvents(normal, eventNames);
        
        normal.on({
            scope: me,
            itemmouseleave: me.onItemMouseLeave,
            itemmouseenter: me.onItemMouseEnter
        });
        
        locked.on({
            scope: me,
            itemmouseleave: me.onItemMouseLeave,
            itemmouseenter: me.onItemMouseEnter
        });
    },
    
    getGridColumns: function() {
        var cols = this.lockedGrid.headerCt.getGridColumns();
        return cols.concat(this.normalGrid.headerCt.getGridColumns());
    },
    
    onItemMouseEnter: function(view, record){
        var me = this,
            locked = me.lockedView,
            other = me.normalView,
            item;
            
        if (view.trackOver) {
            if (view !== locked) {
                other = locked;
            }
            item = other.getNode(record);
            other.highlightItem(item);
        }
    },
    
    onItemMouseLeave: function(view, record){
        var me = this,
            locked = me.lockedView,
            other = me.normalView;
            
        if (view.trackOver) {
            if (view !== locked) {
                other = locked;
            }
            other.clearHighlight();
        }
    },
    
    relayFn: function(name, args){
        args = args || [];
        
        var view = this.lockedView;
        view[name].apply(view, args || []);    
        view = this.normalView;
        view[name].apply(view, args || []);   
    },
    
    getSelectionModel: function(){
        return this.panel.getSelectionModel();    
    },
    
    getStore: function(){
        return this.panel.store;
    },
    
    getNode: function(nodeInfo){
        
        return this.normalView.getNode(nodeInfo);
    },
    
    getCell: function(record, column){
        var view = this.lockedView,
            row;
        
        
        if (view.getHeaderAtIndex(column) === -1) {
            view = this.normalView;
        }
        
        row = view.getNode(record);
        return Ext.fly(row).down(column.getCellSelector());
    },
    
    getRecord: function(node){
        var result = this.lockedView.getRecord(node);
        if (!node) {
            result = this.normalView.getRecord(node);
        }
        return result;
    },
    
    addElListener: function(eventName, fn, scope){
        this.relayFn('addElListener', arguments);
    },
    
    refreshNode: function(){
        this.relayFn('refreshNode', arguments);
    },
    
    refresh: function(){
        this.relayFn('refresh', arguments);
    },
    
    bindStore: function(){
        this.relayFn('bindStore', arguments);
    },
    
    addRowCls: function(){
        this.relayFn('addRowCls', arguments);
    },
    
    removeRowCls: function(){
        this.relayFn('removeRowCls', arguments);
    }
       
});

Ext.define('Ext.grid.Lockable', {
    
    requires: ['Ext.grid.LockingView'],
    
    
    syncRowHeight: true,
    
    
    
    

    
    
    
    spacerHidden: true,
    
    
    unlockText: 'Unlock',
    lockText: 'Lock',
    
    determineXTypeToCreate: function() {
        var me = this,
            typeToCreate;

        if (me.subGridXType) {
            typeToCreate = me.subGridXType;
        } else {
            var xtypes     = this.getXTypes().split('/'),
                xtypesLn   = xtypes.length,
                xtype      = xtypes[xtypesLn - 1],
                superxtype = xtypes[xtypesLn - 2];
                
            if (superxtype !== 'tablepanel') {
                typeToCreate = superxtype;
            } else {
                typeToCreate = xtype;
            }
        }
        
        return typeToCreate;
    },
    
    
    
    injectLockable: function() {
        
        this.lockable = true;
        
        
        this.hasView = true;

        var me = this,
            
            
            
            xtype = me.determineXTypeToCreate(),
            
            selModel = me.getSelectionModel(),
            lockedGrid = {
                xtype: xtype,
                
                enableAnimations: false,
                scroll: false,
                scrollerOwner: false,
                selModel: selModel,
                border: false,
                cls: Ext.baseCSSPrefix + 'grid-inner-locked'
            },
            normalGrid = {
                xtype: xtype,
                enableAnimations: false,
                scrollerOwner: false,
                selModel: selModel,
                border: false
            },
            i = 0,
            columns,
            lockedHeaderCt,
            normalHeaderCt;
        
        me.addCls(Ext.baseCSSPrefix + 'grid-locked');
        
        
        
        
        Ext.copyTo(normalGrid, me, me.normalCfgCopy);
        Ext.copyTo(lockedGrid, me, me.lockedCfgCopy);
        for (; i < me.normalCfgCopy.length; i++) {
            delete me[me.normalCfgCopy[i]];
        }
        for (i = 0; i < me.lockedCfgCopy.length; i++) {
            delete me[me.lockedCfgCopy[i]];
        }
        
        me.lockedHeights = [];
        me.normalHeights = [];
        
        columns = me.processColumns(me.columns);

        lockedGrid.width = columns.lockedWidth;
        lockedGrid.columns = columns.locked;
        normalGrid.columns = columns.normal;
        
        me.store = Ext.StoreManager.lookup(me.store);
        lockedGrid.store = me.store;
        normalGrid.store = me.store;
        
        
        normalGrid.flex = 1;
        lockedGrid.viewConfig = me.lockedViewConfig || {};
        lockedGrid.viewConfig.loadingUseMsg = false;
        normalGrid.viewConfig = me.normalViewConfig || {};
        
        Ext.applyIf(lockedGrid.viewConfig, me.viewConfig);
        Ext.applyIf(normalGrid.viewConfig, me.viewConfig);
        
        me.normalGrid = Ext.ComponentManager.create(normalGrid);
        me.lockedGrid = Ext.ComponentManager.create(lockedGrid);
        
        me.view = Ext.create('Ext.grid.LockingView', {
            locked: me.lockedGrid,
            normal: me.normalGrid,
            panel: me    
        });
        
        if (me.syncRowHeight) {
            me.lockedGrid.getView().on({
                refresh: me.onLockedGridAfterRefresh,
                itemupdate: me.onLockedGridAfterUpdate,
                scope: me
            });
            
            me.normalGrid.getView().on({
                refresh: me.onNormalGridAfterRefresh,
                itemupdate: me.onNormalGridAfterUpdate,
                scope: me
            });
        }
        
        lockedHeaderCt = me.lockedGrid.headerCt;
        normalHeaderCt = me.normalGrid.headerCt;
        
        lockedHeaderCt.lockedCt = true;
        lockedHeaderCt.lockableInjected = true;
        normalHeaderCt.lockableInjected = true;
        
        lockedHeaderCt.on({
            columnshow: me.onLockedHeaderShow,
            columnhide: me.onLockedHeaderHide,
            columnmove: me.onLockedHeaderMove,
            sortchange: me.onLockedHeaderSortChange,
            columnresize: me.onLockedHeaderResize,
            scope: me
        });
        
        normalHeaderCt.on({
            columnmove: me.onNormalHeaderMove,
            sortchange: me.onNormalHeaderSortChange,
            scope: me
        });
        
        me.normalGrid.on({
            scrollershow: me.onScrollerShow,
            scrollerhide: me.onScrollerHide,
            scope: me
        });
        
        me.lockedGrid.on('afterlayout', me.onLockedGridAfterLayout, me, {single: true});
        
        me.modifyHeaderCt();
        me.items = [me.lockedGrid, me.normalGrid];

        me.layout = {
            type: 'hbox',
            align: 'stretch'
        };
    },
    
    processColumns: function(columns){
        
        var i = 0,
            len = columns.length,
            lockedWidth = 0,
            lockedHeaders = [],
            normalHeaders = [],
            column;
            
        for (; i < len; ++i) {
            column = columns[i];
            
            
            column.processed = true;
            if (column.locked) {
                if (column.flex) {
                    Ext.Error.raise("Columns which are locked do NOT support a flex width. You must set a width on the " + columns[i].text + "column.");
                }
                lockedWidth += column.width;
                lockedHeaders.push(column);
            } else {
                normalHeaders.push(column);
            }
        }
        return {
            lockedWidth: lockedWidth,
            locked: lockedHeaders,
            normal: normalHeaders    
        };
    },
    
    
    onLockedGridAfterLayout: function() {
        var me         = this,
            lockedView = me.lockedGrid.getView();
        lockedView.on({
            refresh: me.createSpacer,
            beforerefresh: me.destroySpacer,
            scope: me
        });
    },
    
    
    onLockedHeaderMove: function() {
        if (this.syncRowHeight) {
            this.onNormalGridAfterRefresh();
        }
    },
    
    
    onNormalHeaderMove: function() {
        if (this.syncRowHeight) {
            this.onLockedGridAfterRefresh();
        }
    },
    
    
    
    createSpacer: function() {
        var me   = this,
            
            
            w    = Ext.getScrollBarWidth() + (Ext.isIE ? 2 : 0),
            view = me.lockedGrid.getView(),
            el   = view.el;

        me.spacerEl = Ext.core.DomHelper.append(el, {
            cls: me.spacerHidden ? (Ext.baseCSSPrefix + 'hidden') : '',
            style: 'height: ' + w + 'px;'
        }, true);
    },
    
    destroySpacer: function() {
        var me = this;
        if (me.spacerEl) {
            me.spacerEl.destroy();
            delete me.spacerEl;
        }
    },
    
    
    onLockedGridAfterRefresh: function() {
        var me     = this,
            view   = me.lockedGrid.getView(),
            el     = view.el,
            rowEls = el.query(view.getItemSelector()),
            ln     = rowEls.length,
            i = 0;
            
        
        me.lockedHeights = [];
        
        for (; i < ln; i++) {
            me.lockedHeights[i] = rowEls[i].clientHeight;
        }
        me.syncRowHeights();
    },
    
    
    onNormalGridAfterRefresh: function() {
        var me     = this,
            view   = me.normalGrid.getView(),
            el     = view.el,
            rowEls = el.query(view.getItemSelector()),
            ln     = rowEls.length,
            i = 0;
            
        
        me.normalHeights = [];
        
        for (; i < ln; i++) {
            me.normalHeights[i] = rowEls[i].clientHeight;
        }
        me.syncRowHeights();
    },
    
    
    onLockedGridAfterUpdate: function(record, index, node) {
        this.lockedHeights[index] = node.clientHeight;
        this.syncRowHeights();
    },
    
    
    onNormalGridAfterUpdate: function(record, index, node) {
        this.normalHeights[index] = node.clientHeight;
        this.syncRowHeights();
    },
    
    
    
    syncRowHeights: function() {
        var me = this,
            lockedHeights = me.lockedHeights,
            normalHeights = me.normalHeights,
            calcHeights   = [],
            ln = lockedHeights.length,
            i  = 0,
            lockedView, normalView,
            lockedRowEls, normalRowEls,
            vertScroller = me.getVerticalScroller(),
            scrollTop;

        
        
        if (lockedHeights.length && normalHeights.length) {
            lockedView = me.lockedGrid.getView();
            normalView = me.normalGrid.getView();
            lockedRowEls = lockedView.el.query(lockedView.getItemSelector());
            normalRowEls = normalView.el.query(normalView.getItemSelector());

            
            for (; i < ln; i++) {
                
                if (!isNaN(lockedHeights[i]) && !isNaN(normalHeights[i])) {
                    if (lockedHeights[i] > normalHeights[i]) {
                        Ext.fly(normalRowEls[i]).setHeight(lockedHeights[i]);
                    } else if (lockedHeights[i] < normalHeights[i]) {
                        Ext.fly(lockedRowEls[i]).setHeight(normalHeights[i]);
                    }
                }
            }

            
            me.normalGrid.invalidateScroller();
            
            
            
            if (vertScroller && vertScroller.setViewScrollTop) {
                vertScroller.setViewScrollTop(me.virtualScrollTop);
            } else {
                
                
                
                scrollTop = normalView.el.dom.scrollTop;
                normalView.el.dom.scrollTop = scrollTop;
                lockedView.el.dom.scrollTop = scrollTop;
            }
            
            
            me.lockedHeights = [];
            me.normalHeights = [];
        }
    },
    
    
    onScrollerShow: function(scroller, direction) {
        if (direction === 'horizontal') {
            this.spacerHidden = false;
            this.spacerEl.removeCls(Ext.baseCSSPrefix + 'hidden');
        }
    },
    
    
    onScrollerHide: function(scroller, direction) {
        if (direction === 'horizontal') {
            this.spacerHidden = true;
            this.spacerEl.addCls(Ext.baseCSSPrefix + 'hidden');
        }
    },

    
    
    modifyHeaderCt: function() {
        var me = this;
        me.lockedGrid.headerCt.getMenuItems = me.getMenuItems(true);
        me.normalGrid.headerCt.getMenuItems = me.getMenuItems(false);
    },
    
    onUnlockMenuClick: function() {
        this.unlock();
    },
    
    onLockMenuClick: function() {
        this.lock();
    },
    
    getMenuItems: function(locked) {
        var me            = this,
            unlockText    = me.unlockText,
            lockText      = me.lockText,
            
            unlockCls     = 'xg-hmenu-unlock',
            lockCls       = 'xg-hmenu-lock',
            unlockHandler = Ext.Function.bind(me.onUnlockMenuClick, me),
            lockHandler   = Ext.Function.bind(me.onLockMenuClick, me);
        
        
        return function() {
            var o = Ext.grid.header.Container.prototype.getMenuItems.call(this);
            o.push('-',{
                cls: unlockCls,
                text: unlockText,
                handler: unlockHandler,
                disabled: !locked
            });
            o.push({
                cls: lockCls,
                text: lockText,
                handler: lockHandler,
                disabled: locked
            });
            return o;
        };
    },
    
    
    
    lock: function(activeHd, toIdx) {
        var me         = this,
            normalGrid = me.normalGrid,
            lockedGrid = me.lockedGrid,
            normalHCt  = normalGrid.headerCt,
            lockedHCt  = lockedGrid.headerCt;
            
        activeHd = activeHd || normalHCt.getMenu().activeHeader;
        
        
        
        if (activeHd.flex) {
            activeHd.width = activeHd.getWidth();
            delete activeHd.flex;
        }
        
        normalHCt.remove(activeHd, false);
        lockedHCt.suspendLayout = true;
        if (Ext.isDefined(toIdx)) {
            lockedHCt.insert(toIdx, activeHd);
        } else {
            lockedHCt.add(activeHd);
        }
        lockedHCt.suspendLayout = false;
        me.syncLockedSection();
    },
    
    syncLockedSection: function() {
        var me = this;
        me.syncLockedWidth();
        me.lockedGrid.getView().refresh();
        me.normalGrid.getView().refresh();
    },
    
    
    
    syncLockedWidth: function() {
        var me = this,
            width = me.lockedGrid.headerCt.getFullWidth(true);
        me.lockedGrid.setWidth(width);
    },
    
    onLockedHeaderResize: function() {
        this.syncLockedWidth();
    },
    
    onLockedHeaderHide: function() {
        this.syncLockedWidth();
    },
    
    onLockedHeaderShow: function() {
        this.syncLockedWidth();
    },
    
    onLockedHeaderSortChange: function(headerCt, header, sortState) {
        if (sortState) {
            
            
            this.normalGrid.headerCt.clearOtherSortStates(null, true);
        }
    },
    
    onNormalHeaderSortChange: function(headerCt, header, sortState) {
        if (sortState) {
            
            
            this.lockedGrid.headerCt.clearOtherSortStates(null, true);
        }
    },
    
    
    
    unlock: function(activeHd, toIdx) {
        var me         = this,
            normalGrid = me.normalGrid,
            lockedGrid = me.lockedGrid,
            normalHCt  = normalGrid.headerCt,
            lockedHCt  = lockedGrid.headerCt;

        if (!Ext.isDefined(toIdx)) {
            toIdx = 0;
        }
        activeHd = activeHd || lockedHCt.getMenu().activeHeader;
        
        lockedHCt.remove(activeHd, false);
        me.syncLockedWidth();
        me.lockedGrid.getView().refresh();
        normalHCt.insert(toIdx, activeHd);
        me.normalGrid.getView().refresh();
    },
    
    
    reconfigureLockable: function(store, columns) {
        var me = this,
            lockedGrid = me.lockedGrid,
            normalGrid = me.normalGrid;
        
        if (columns) {
            lockedGrid.headerCt.removeAll();
            normalGrid.headerCt.removeAll();
            
            columns = me.processColumns(columns);
            lockedGrid.setWidth(columns.lockedWidth);
            lockedGrid.headerCt.add(columns.locked);
            normalGrid.headerCt.add(columns.normal);
        }
        
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            me.store = store;
            lockedGrid.bindStore(store);
            normalGrid.bindStore(store);
        } else {
            lockedGrid.getView().refresh();
            normalGrid.getView().refresh();
        }
    }
});


Ext.define('Ext.grid.Scroller', {
    extend: 'Ext.Component',
    alias: 'widget.gridscroller',
    weight: 110,
    cls: Ext.baseCSSPrefix + 'scroller',
    focusable: false,
    
    renderTpl: ['<div class="' + Ext.baseCSSPrefix + 'stretcher"></div>'],
    
    initComponent: function() {
        var me       = this,
            dock     = me.dock,
            cls      = Ext.baseCSSPrefix + 'scroller-vertical',
            sizeProp = 'width',
            
            
            
            
            
            scrollbarWidth = Ext.getScrollBarWidth() + (Ext.isIE ? 1 : -1);

        me.offsets = {bottom: 0};

        if (dock === 'top' || dock === 'bottom') {
            cls = Ext.baseCSSPrefix + 'scroller-horizontal';
            sizeProp = 'height';
        }
        me[sizeProp] = scrollbarWidth;
        
        me.cls += (' ' + cls);
        
        Ext.applyIf(me.renderSelectors, {
            stretchEl: '.' + Ext.baseCSSPrefix + 'stretcher'
        });
        me.callParent();
    },
    
    
    afterRender: function() {
        var me = this;
        me.callParent();
        me.ownerCt.on('afterlayout', me.onOwnerAfterLayout, me);
        me.mon(me.el, 'scroll', me.onElScroll, me);
        Ext.cache[me.el.id].skipGarbageCollection = true;
    },
    
    getSizeCalculation: function() {
        var owner  = this.getPanel(),
            dock   = this.dock,
            elDom  = this.el.dom,
            width  = 1,
            height = 1,
            view, tbl;
            
        if (dock === 'top' || dock === 'bottom') {
            
            
            var items  = owner.query('tableview'),
                center = items[1] || items[0];
            
            if (!center) {
                return false;
            }
            
            
            
            width = center.headerCt.getFullWidth();
            
            if (Ext.isIEQuirks) {
                width--;
            }
            
            width--;
        } else {            
            view = owner.down('tableview:not([lockableInjected])');
            if (!view) {
                return false;
            }
            tbl = view.el;
            if (!tbl) {
                return false;
            }
            
            
            
            height = tbl.dom.scrollHeight;
        }
        if (isNaN(width)) {
            width = 1;
        }
        if (isNaN(height)) {
            height = 1;
        }
        return {
            width: width,
            height: height
        };
    },
    
    invalidate: function(firstPass) {
        if (!this.stretchEl || !this.ownerCt) {
            return;
        }
        var size  = this.getSizeCalculation(),
            elDom = this.el.dom;
        if (size) {
            this.stretchEl.setSize(size);
        
            
            
            elDom.scrollTop = elDom.scrollTop;
        }
    },

    onOwnerAfterLayout: function(owner, layout) {
        this.invalidate();
    },

    
    setScrollTop: function(scrollTop) {
        if (this.el) {
            var elDom = this.el.dom;
            return elDom.scrollTop = Ext.Number.constrain(scrollTop, 0, elDom.scrollHeight - elDom.clientHeight);
        }
    },

    
    setScrollLeft: function(scrollLeft) {
        if (this.el) {
            var elDom = this.el.dom;
            return elDom.scrollLeft = Ext.Number.constrain(scrollLeft, 0, elDom.scrollWidth - elDom.clientWidth);
        }
    },

    
    scrollByDeltaY: function(delta) {
        if (this.el) {
            var elDom = this.el.dom;
            return this.setScrollTop(elDom.scrollTop + delta);
        }
    },

    
    scrollByDeltaX: function(delta) {
        if (this.el) {
            var elDom = this.el.dom;
            return this.setScrollLeft(elDom.scrollLeft + delta);
        }
    },
    
    
    
    scrollToTop : function(){
        this.setScrollTop(0);
    },
    
    
    onElScroll: function(event, target) {
        this.fireEvent('bodyscroll', event, target);
    },

    getPanel: function() {
        var me = this;
        if (!me.panel) {
            me.panel = this.up('[scrollerOwner]');
        }
        return me.panel;
    }
});



Ext.define('Ext.grid.PagingScroller', {
    extend: 'Ext.grid.Scroller',
    alias: 'widget.paginggridscroller',
    
    
    
    
    
    
    
    percentageFromEdge: 0.35,
    
    
    scrollToLoadBuffer: 200,
    
    activePrefetch: true,
    
    chunkSize: 50,
    snapIncrement: 25,
    
    syncScroll: true,
    
    initComponent: function() {
        var me = this,
            ds = me.store;

        ds.on('guaranteedrange', this.onGuaranteedRange, this);
        this.callParent(arguments);
    },
    
    
    onGuaranteedRange: function(range, start, end) {
        var me = this,
            ds = me.store,
            rs;
        
        if (range.length && me.visibleStart < range[0].index) {
            return;
        }
        
        ds.loadRecords(range);

        if (!me.firstLoad) {
            if (me.rendered) {
                me.invalidate();
            } else {
                me.on('afterrender', this.invalidate, this, {single: true});
            }
            me.firstLoad = true;
        } else {
            
            me.syncTo();
        }
    },
    
    syncTo: function() {
        var me            = this,
            pnl           = me.getPanel(),
            store         = pnl.store,
            scrollerElDom = this.el.dom,
            rowOffset     = me.visibleStart - store.guaranteedStart,
            scrollBy      = rowOffset * me.rowHeight,
            scrollHeight  = scrollerElDom.scrollHeight,
            clientHeight  = scrollerElDom.clientHeight,
            scrollTop     = scrollerElDom.scrollTop,
            useMaximum;
        
        
        
        if (Ext.isIE9 && Ext.isStrict) {
            clientHeight = scrollerElDom.offsetHeight + 2;
        }

        
        
        useMaximum = (scrollHeight - clientHeight - scrollTop <= 0);
        this.setViewScrollTop(scrollBy, useMaximum);
    },
    
    getPageData : function(){
        var panel = this.getPanel(),
            store = panel.store,
            totalCount = store.getTotalCount();
            
        return {
            total : totalCount,
            currentPage : store.currentPage,
            pageCount: Math.ceil(totalCount / store.pageSize),
            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
        };
    },
    
    onElScroll: function(e, t) {
        var me = this,
            panel = me.getPanel(),
            store = panel.store,
            pageSize = store.pageSize,
            guaranteedStart = store.guaranteedStart,
            guaranteedEnd = store.guaranteedEnd,
            totalCount = store.getTotalCount(),
            numFromEdge = Math.ceil(me.percentageFromEdge * store.pageSize),
            position = t.scrollTop,
            visibleStart = Math.floor(position / me.rowHeight),
            view = panel.down('tableview'),
            viewEl = view.el,
            visibleHeight = viewEl.getHeight(),
            visibleAhead = Math.ceil(visibleHeight / me.rowHeight),
            visibleEnd = visibleStart + visibleAhead,
            prevPage = Math.floor(visibleStart / store.pageSize),
            nextPage = Math.floor(visibleEnd / store.pageSize) + 2,
            lastPage = Math.ceil(totalCount / store.pageSize),
            
            requestStart = Math.floor(visibleStart / me.snapIncrement) * me.snapIncrement,
            requestEnd = requestStart + pageSize - 1,
            activePrefetch = me.activePrefetch;

        me.visibleStart = visibleStart;
        me.visibleEnd = visibleEnd;
        
        
        me.syncScroll = true;
        if (totalCount >= pageSize) {
            
            if (requestEnd > totalCount - 1) {
                this.cancelLoad();
                if (store.rangeSatisfied(totalCount - pageSize, totalCount - 1)) {
                    me.syncScroll = true;
                }
                store.guaranteeRange(totalCount - pageSize, totalCount - 1);
            
            } else if (visibleStart < guaranteedStart || visibleEnd > guaranteedEnd) {
                if (store.rangeSatisfied(requestStart, requestEnd)) {
                    this.cancelLoad();
                    store.guaranteeRange(requestStart, requestEnd);
                } else {
                    store.mask();
                    me.attemptLoad(requestStart, requestEnd);
                }
                
                me.syncScroll = false;
            } else if (activePrefetch && visibleStart < (guaranteedStart + numFromEdge) && prevPage > 0) {
                me.syncScroll = true;
                store.prefetchPage(prevPage);
            } else if (activePrefetch && visibleEnd > (guaranteedEnd - numFromEdge) && nextPage < lastPage) {
                me.syncScroll = true;
                store.prefetchPage(nextPage);
            }
        }
    
    
        if (me.syncScroll) {
            me.syncTo();
        }
    },
    
    getSizeCalculation: function() {
        
        
        var owner = this.ownerCt,
            view   = owner.getView(),
            store  = this.store,
            dock   = this.dock,
            elDom  = this.el.dom,
            width  = 1,
            height = 1;
        
        if (!this.rowHeight) {
            this.rowHeight = view.el.down(view.getItemSelector()).getHeight(false, true);
        }

        height = store.getTotalCount() * this.rowHeight;

        if (isNaN(width)) {
            width = 1;
        }
        if (isNaN(height)) {
            height = 1;
        }
        return {
            width: width,
            height: height
        };
    },
    
    attemptLoad: function(start, end) {
        var me = this;
        if (!me.loadTask) {
            me.loadTask = Ext.create('Ext.util.DelayedTask', me.doAttemptLoad, me, []);
        }
        me.loadTask.delay(me.scrollToLoadBuffer, me.doAttemptLoad, me, [start, end]);
    },
    
    cancelLoad: function() {
        if (this.loadTask) {
            this.loadTask.cancel();
        }
    },
    
    doAttemptLoad:  function(start, end) {
        var store = this.getPanel().store;
        store.guaranteeRange(start, end);
    },
    
    setViewScrollTop: function(scrollTop, useMax) {
        var owner = this.getPanel(),
            items = owner.query('tableview'),
            i = 0,
            len = items.length,
            center,
            centerEl,
            calcScrollTop,
            maxScrollTop,
            scrollerElDom = this.el.dom;
            
        owner.virtualScrollTop = scrollTop;
            
        center = items[1] || items[0];
        centerEl = center.el.dom;
        
        maxScrollTop = ((owner.store.pageSize * this.rowHeight) - centerEl.clientHeight);
        calcScrollTop = (scrollTop % ((owner.store.pageSize * this.rowHeight) + 1));
        if (useMax) {
            calcScrollTop = maxScrollTop;
        }
        if (calcScrollTop > maxScrollTop) {
            
            return;
            
        }
        for (; i < len; i++) {
            items[i].el.dom.scrollTop = calcScrollTop;
        }
    }
});



Ext.define('Ext.panel.Table', {
    extend: 'Ext.panel.Panel',

    alias: 'widget.tablepanel',

    uses: [
        'Ext.selection.RowModel',
        'Ext.grid.Scroller',
        'Ext.grid.header.Container',
        'Ext.grid.Lockable'
    ],

    cls: Ext.baseCSSPrefix + 'grid',
    extraBodyCls: Ext.baseCSSPrefix + 'grid-body',

    layout: 'fit',
    
    hasView: false,

    
    viewType: null,
    selType: 'rowmodel',

    
    scrollDelta: 40,

    
    scroll: true,

    

    

    

    
    sortableColumns: true,

    verticalScrollDock: 'right',
    verticalScrollerType: 'gridscroller',

    horizontalScrollerPresentCls: Ext.baseCSSPrefix + 'horizontal-scroller-present',
    verticalScrollerPresentCls: Ext.baseCSSPrefix + 'vertical-scroller-present',

    
    
    scrollerOwner: true,

    invalidateScrollerOnRefresh: true,
    
    enableColumnMove: true,
    enableColumnResize: true,


    initComponent: function() {
        if (!this.viewType) {
            Ext.Error.raise("You must specify a viewType config.");
        }
        if (!this.store) {
            Ext.Error.raise("You must specify a store config");
        }
        if (this.headers) {
            Ext.Error.raise("The headers config is not supported. Please specify columns instead.");
        }

        var me          = this,
            scroll      = me.scroll,
            vertical    = false,
            horizontal  = false,
            headerCtCfg = me.columns || me.colModel,
            i           = 0,
            view,
            border = me.border;

        
        me.determineScrollbars = Ext.Function.createBuffered(me.determineScrollbars, 30);
        me.invalidateScroller = Ext.Function.createBuffered(me.invalidateScroller, 30);
        me.injectView = Ext.Function.createBuffered(me.injectView, 30);

        if (me.hideHeaders) {
            border = false;
        }

        
        
        if (headerCtCfg instanceof Ext.grid.header.Container) {
            me.headerCt = headerCtCfg;
            me.headerCt.border = border;
            me.columns = me.headerCt.items.items;
        } else {
            if (Ext.isArray(headerCtCfg)) {
                headerCtCfg = {
                    items: headerCtCfg,
                    border: border
                };
            }
            Ext.apply(headerCtCfg, {
                forceFit: me.forceFit,
                sortable: me.sortableColumns,
                enableColumnMove: me.enableColumnMove,
                enableColumnResize: me.enableColumnResize,
                border:  border
            });
            me.columns = headerCtCfg.items;

             
             
             if (Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
                 me.self.mixin('lockable', Ext.grid.Lockable);
                 me.injectLockable();
             }
        }

        me.store = Ext.data.StoreManager.lookup(me.store);
        me.addEvents(
            
            'scrollerhide',
            
            'scrollershow'
        );

        me.bodyCls = me.bodyCls || '';
        me.bodyCls += (' ' + me.extraBodyCls);

        
        delete me.autoScroll;

        
        
        if (!me.hasView) {

            
            
            if (!me.headerCt) {
                me.headerCt = Ext.create('Ext.grid.header.Container', headerCtCfg);
            }

            
            me.columns = me.headerCt.items.items;

            if (me.hideHeaders) {
                me.headerCt.height = 0;
                me.headerCt.border = false;
                me.headerCt.addCls(Ext.baseCSSPrefix + 'grid-header-ct-hidden');
                me.addCls(Ext.baseCSSPrefix + 'grid-header-hidden');
                
                
                if (Ext.isIEQuirks) {
                    me.headerCt.style = {
                        display: 'none'
                    };
                }
            }

            
            if (scroll === true || scroll === 'both') {
                vertical = horizontal = true;
            } else if (scroll === 'horizontal') {
                horizontal = true;
            } else if (scroll === 'vertical') {
                vertical = true;
            
            } else {
                me.headerCt.availableSpaceOffset = 0;
            }

            if (vertical) {
                me.verticalScroller = me.verticalScroller || {};
                Ext.applyIf(me.verticalScroller, {
                    dock: me.verticalScrollDock,
                    xtype: me.verticalScrollerType,
                    store: me.store
                });
                me.verticalScroller = Ext.ComponentManager.create(me.verticalScroller);
                me.mon(me.verticalScroller, {
                    bodyscroll: me.onVerticalScroll,
                    scope: me
                });
            }

            if (horizontal) {
                me.horizontalScroller = Ext.ComponentManager.create({
                    xtype: 'gridscroller',
                    section: me,
                    dock: 'bottom',
                    store: me.store
                });
                me.mon(me.horizontalScroller, {
                    bodyscroll: me.onHorizontalScroll,
                    scope: me
                });
            }

            me.headerCt.on('columnresize', me.onHeaderResize, me);
            me.relayEvents(me.headerCt, ['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange']);
            me.features = me.features || [];
            me.dockedItems = me.dockedItems || [];
            me.dockedItems.unshift(me.headerCt);
            me.viewConfig = me.viewConfig || {};
            me.viewConfig.invalidateScrollerOnRefresh = me.invalidateScrollerOnRefresh;

            
            
            view = me.getView();

            if (view) {
                me.mon(view.store, {
                    load: me.onStoreLoad,
                    scope: me
                });
                me.mon(view, {
                    refresh: {
                        fn: this.onViewRefresh,
                        scope: me,
                        buffer: 50
                    },
                    itemupdate: me.onViewItemUpdate,
                    scope: me
                });
                this.relayEvents(view, [
                    
                    'beforeitemmousedown',
                    
                    'beforeitemmouseup',
                    
                    'beforeitemmouseenter',
                    
                    'beforeitemmouseleave',
                    
                    'beforeitemclick',
                    
                    'beforeitemdblclick',
                    
                    'beforeitemcontextmenu',
                    
                    'itemmousedown',
                    
                    'itemmouseup',
                    
                    'itemmouseenter',
                    
                    'itemmouseleave',
                    
                    'itemclick',
                    
                    'itemdblclick',
                    
                    'itemcontextmenu',
                    
                    'beforecontainermousedown',
                    
                    'beforecontainermouseup',
                    
                    'beforecontainermouseover',
                    
                    'beforecontainermouseout',
                    
                    'beforecontainerclick',
                    
                    'beforecontainerdblclick',
                    
                    'beforecontainercontextmenu',
                    
                    'containermouseup',
                    
                    'containermouseover',
                    
                    'containermouseout',
                    
                    'containerclick',
                    
                    'containerdblclick',
                    
                    'containercontextmenu',

                    
                    'selectionchange',
                    
                    'beforeselect'
                ]);
            }
        }
        me.callParent(arguments);
    },

    
    initStateEvents: function(){
        var events = this.stateEvents;
        
        Ext.each(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange'], function(event){
            if (Ext.Array.indexOf(events, event)) {
                events.push(event);
            }
        });
        this.callParent();
    },

    getState: function(){
        var state = {
            columns: []
        },
        sorter = this.store.sorters.first();

        this.headerCt.items.each(function(header){
            state.columns.push({
                id: header.headerId,
                width: header.flex ? undefined : header.width,
                hidden: header.hidden,
                sortable: header.sortable
            });
        });

        if (sorter) {
            state.sort = {
                property: sorter.property,
                direction: sorter.direction
            };
        }
        return state;
    },

    applyState: function(state) {
        var headers = state.columns,
            length = headers ? headers.length : 0,
            headerCt = this.headerCt,
            items = headerCt.items,
            sorter = state.sort,
            store = this.store,
            i = 0,
            index,
            headerState,
            header;

        for (; i < length; ++i) {
            headerState = headers[i];
            header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']');
            index = items.indexOf(header);
            if (i !== index) {
                headerCt.moveHeader(index, i);
            }
            header.sortable = headerState.sortable;
            if (Ext.isDefined(headerState.width)) {
                delete header.flex;
                if (header.rendered) {
                    header.setWidth(headerState.width);
                } else {
                    header.minWidth = header.width = headerState.width;
                }
            }
            header.hidden = headerState.hidden;
        }

        if (sorter) {
            if (store.remoteSort) {
                store.sorters.add(Ext.create('Ext.util.Sorter', {
                    property: sorter.property,
                    direction: sorter.direction
                }));
            }
            else {
                store.sort(sorter.property, sorter.direction);
            }
        }
    },

    
    getStore: function(){
        return this.store;
    },

    
    getView: function() {
        var me = this,
            sm;

        if (!me.view) {
            sm = me.getSelectionModel();
            me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
                xtype: me.viewType,
                store: me.store,
                headerCt: me.headerCt,
                selModel: sm,
                features: me.features,
                panel: me
            }));
            me.mon(me.view, {
                uievent: me.processEvent,
                scope: me
            });
            sm.view = me.view;
            me.headerCt.view = me.view;
            me.relayEvents(me.view, ['cellclick', 'celldblclick']);
        }
        return me.view;
    },

    
    setAutoScroll: Ext.emptyFn,

    
    
    
    elScroll: function(direction, distance, animate) {
        var me = this,
            scroller;

        if (direction === "up" || direction === "left") {
            distance = -distance;
        }

        if (direction === "down" || direction === "up") {
            scroller = me.getVerticalScroller();
            scroller.scrollByDeltaY(distance);
        } else {
            scroller = me.getHorizontalScroller();
            scroller.scrollByDeltaX(distance);
        }
    },
    
    afterLayout: function() {
        this.callParent(arguments);
        this.injectView();
    },
    

    
    injectView: function() {
        if (!this.hasView && !this.collapsed) {
            var me   = this,
                view = me.getView();

            me.hasView = true;
            me.add(view);

            
            view.el.scroll = Ext.Function.bind(me.elScroll, me);
            
            
            me.mon(view.el, {
                mousewheel: me.onMouseWheel,
                scope: me
            });
        }
    },

    afterExpand: function() {
        this.callParent(arguments);
        if (!this.hasView) {
            this.injectView();
        }
    },

    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
        var me = this,
            header;

        if (cellIndex !== -1) {
            header = me.headerCt.getGridColumns()[cellIndex];
            return header.processEvent.apply(header, arguments);
        }
    },

    
    determineScrollbars: function() {
        var me = this,
            viewElDom,
            centerScrollWidth,
            centerClientWidth,
            scrollHeight,
            clientHeight;

        if (!me.collapsed && me.view && me.view.el) {
            viewElDom = me.view.el.dom;
            
            centerScrollWidth = me.headerCt.getFullWidth();
            
            centerClientWidth = viewElDom.offsetWidth;
            if (me.verticalScroller && me.verticalScroller.el) {
                scrollHeight = me.verticalScroller.getSizeCalculation().height;
            } else {
                scrollHeight = viewElDom.scrollHeight;
            }

            clientHeight = viewElDom.clientHeight;

            me.suspendLayout = true;
            me.scrollbarChanged = false;
            if (!me.collapsed && scrollHeight > clientHeight) {
                me.showVerticalScroller();
            } else {
                me.hideVerticalScroller();
            }

            if (!me.collapsed && centerScrollWidth > (centerClientWidth + Ext.getScrollBarWidth() - 2)) {
                me.showHorizontalScroller();
            } else {
                me.hideHorizontalScroller();
            }
            me.suspendLayout = false;
            if (me.scrollbarChanged) {
                me.doComponentLayout();
            }
        }
    },

    onHeaderResize: function() {
        if (this.view && this.view.rendered) {
            this.determineScrollbars();
            this.invalidateScroller();
        }
    },

    
    hideHorizontalScroller: function() {
        var me = this;

        if (me.horizontalScroller && me.horizontalScroller.ownerCt === me) {
            me.scrollbarChanged = true;
            me.verticalScroller.offsets.bottom = 0;
            me.removeDocked(me.horizontalScroller, false);
            me.removeCls(me.horizontalScrollerPresentCls);
            me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
        }

    },

    
    showHorizontalScroller: function() {
        var me = this;

        if (me.verticalScroller) {
            me.verticalScroller.offsets.bottom = Ext.getScrollBarWidth() - 2;
        }
        if (me.horizontalScroller && me.horizontalScroller.ownerCt !== me) {
            me.scrollbarChanged = true;
            me.addDocked(me.horizontalScroller);
            me.addCls(me.horizontalScrollerPresentCls);
            me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
        }
    },

    
    hideVerticalScroller: function() {
        var me = this,
            headerCt = me.headerCt;

        
        if (headerCt && headerCt.layout.reserveOffset) {
            headerCt.layout.reserveOffset = false;
            headerCt.doLayout();
        }
        if (me.verticalScroller && me.verticalScroller.ownerCt === me) {
            me.scrollbarChanged = true;
            me.removeDocked(me.verticalScroller, false);
            me.removeCls(me.verticalScrollerPresentCls);
            me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
        }
    },

    
    showVerticalScroller: function() {
        var me = this,
            headerCt = me.headerCt;

        
        if (headerCt && !headerCt.layout.reserveOffset) {
            headerCt.layout.reserveOffset = true;
            headerCt.doLayout();
        }
        if (me.verticalScroller && me.verticalScroller.ownerCt !== me) {
            me.scrollbarChanged = true;
            me.addDocked(me.verticalScroller);
            me.addCls(me.verticalScrollerPresentCls);
            me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
        }
    },

    
    invalidateScroller: function() {
        var me = this,
            vScroll = me.verticalScroller,
            hScroll = me.horizontalScroller;

        if (vScroll) {
            vScroll.invalidate();
        }
        if (hScroll) {
            hScroll.invalidate();
        }
    },

    
    onHeaderMove: function(headerCt, header, fromIdx, toIdx) {
        this.view.refresh();
    },

    
    onHeaderHide: function(headerCt, header) {
        this.invalidateScroller();
    },

    onHeaderShow: function(headerCt, header) {
        this.invalidateScroller();
    },

    getVerticalScroller: function() {
        return this.getScrollerOwner().down('gridscroller[dock=' + this.verticalScrollDock + ']');
    },

    getHorizontalScroller: function() {
        return this.getScrollerOwner().down('gridscroller[dock=bottom]');
    },

    onMouseWheel: function(e) {
        var me = this,
            browserEvent = e.browserEvent,
            vertScroller = me.getVerticalScroller(),
            horizScroller = me.getHorizontalScroller(),
            scrollDelta = me.scrollDelta,
            deltaY, deltaX,
            vertScrollerEl, horizScrollerEl,
            vertScrollerElDom, horizScrollerElDom,
            horizontalCanScrollLeft, horizontalCanScrollRight,
            verticalCanScrollDown, verticalCanScrollUp;

        
        if (horizScroller) {
            horizScrollerEl = horizScroller.el;
            if (horizScrollerEl) {
                horizScrollerElDom = horizScrollerEl.dom;
                horizontalCanScrollRight = horizScrollerElDom.scrollLeft !== horizScrollerElDom.scrollWidth - horizScrollerElDom.clientWidth;
                horizontalCanScrollLeft  = horizScrollerElDom.scrollLeft !== 0;
            }
        }
        if (vertScroller) {
            vertScrollerEl = vertScroller.el;
            if (vertScrollerEl) {
                vertScrollerElDom = vertScrollerEl.dom;
                verticalCanScrollDown = vertScrollerElDom.scrollTop !== vertScrollerElDom.scrollHeight - vertScrollerElDom.clientHeight;
                verticalCanScrollUp   = vertScrollerElDom.scrollTop !== 0;
            }
        }

        
        if (browserEvent.wheelDeltaX || browserEvent.wheelDeltaY) {        
            deltaX = -browserEvent.wheelDeltaX / 120 * scrollDelta / 3;
            deltaY = -browserEvent.wheelDeltaY / 120 * scrollDelta / 3;
        } else {
            
            if (browserEvent.axis && browserEvent.axis === 1) {
                deltaX = -(scrollDelta * e.getWheelDelta()) / 3;
            } else {
                deltaY = -(scrollDelta * e.getWheelDelta() / 3);
            }
        }
        
        if (horizScroller) {
            if ((deltaX < 0 && horizontalCanScrollLeft) || (deltaX > 0 && horizontalCanScrollRight)) {
                e.stopEvent();
                horizScroller.scrollByDeltaX(deltaX);
            }
        }
        if (vertScroller) {
            if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {
                e.stopEvent();
                vertScroller.scrollByDeltaY(deltaY);    
            }
        }
    },

    
    onViewRefresh: function() {
        if (Ext.isIE) {
            this.syncCellHeight();
        }
        this.determineScrollbars();
        if (this.invalidateScrollerOnRefresh) {
            this.invalidateScroller();
        }
    },

    onViewItemUpdate: function(record, index, tr) {
        if (Ext.isIE) {
            this.syncCellHeight([tr]);
        }
    },

    
    
    
    syncCellHeight: function(trs) {
        var me    = this,
            i     = 0,
            tds,
            j, tdsLn,
            tr, td,
            trsLn,
            rowHeights = [],
            cellHeights,
            cellClsSelector = ('.' + Ext.baseCSSPrefix + 'grid-cell');

        trs   = trs || me.view.getNodes();
        
        trsLn = trs.length;
        
        for (; i < trsLn; i++) {
            tr = trs[i];
            tds = Ext.fly(tr).query(cellClsSelector);
            tdsLn = tds.length;
            cellHeights = [];
            for (j = 0; j < tdsLn; j++) {
                td = tds[j];
                cellHeights.push(td.clientHeight);
            }
            rowHeights.push(Ext.Array.max(cellHeights));
        }

        
        for (i = 0; i < trsLn; i++) {
            tr = trs[i];
            tdsLn = tr.childNodes.length;
            for (j = 0; j < tdsLn; j++) {
                td = Ext.fly(tr.childNodes[j]);
                if (rowHeights[i]) {
                    if (td.is(cellClsSelector)) {
                        td.setHeight(rowHeights[i]);
                    } else {
                        td.down(cellClsSelector).setHeight(rowHeights[i]);
                    }
                }
                
            }
        }
    },

    
    setScrollTop: function(top) {
        var me               = this,
            rootCmp          = me.getScrollerOwner(),
            verticalScroller = me.getVerticalScroller();

        rootCmp.virtualScrollTop = top;
        if (verticalScroller) {
            verticalScroller.setScrollTop(top);
        }

    },

    getScrollerOwner: function() {
        var rootCmp = this;
        if (!this.scrollerOwner) {
            rootCmp = this.up('[scrollerOwner]');
        }
        return rootCmp;
    },

    
    scrollByDeltaY: function(deltaY) {
        var rootCmp = this.getScrollerOwner(),
            scrollerRight;
        scrollerRight = rootCmp.down('gridscroller[dock=' + this.verticalScrollDock + ']');
        if (scrollerRight) {
            scrollerRight.scrollByDeltaY(deltaY);
        }
    },


    
    scrollByDeltaX: function(deltaX) {
        this.horizontalScroller.scrollByDeltaX(deltaX);
    },

    
    getLhsMarker: function() {
        var me = this;

        if (!me.lhsMarker) {
            me.lhsMarker = Ext.core.DomHelper.append(me.el, {
                cls: Ext.baseCSSPrefix + 'grid-resize-marker'
            }, true);
        }
        return me.lhsMarker;
    },

    
    getRhsMarker: function() {
        var me = this;

        if (!me.rhsMarker) {
            me.rhsMarker = Ext.core.DomHelper.append(me.el, {
                cls: Ext.baseCSSPrefix + 'grid-resize-marker'
            }, true);
        }
        return me.rhsMarker;
    },

    
    getSelectionModel: function(){
        if (!this.selModel) {
            this.selModel = {};
        }

        var mode = 'SINGLE',
            type;
        if (this.simpleSelect) {
            mode = 'SIMPLE';
        } else if (this.multiSelect) {
            mode = 'MULTI';
        }

        Ext.applyIf(this.selModel, {
            allowDeselect: this.allowDeselect,
            mode: mode
        });

        if (!this.selModel.events) {
            type = this.selModel.selType || this.selType;
            this.selModel = Ext.create('selection.' + type, this.selModel);
        }

        if (!this.selModel.hasRelaySetup) {
            this.relayEvents(this.selModel, ['selectionchange', 'select', 'deselect']);
            this.selModel.hasRelaySetup = true;
        }

        
        
        if (this.disableSelection) {
            this.selModel.locked = true;
        }
        return this.selModel;
    },

    onVerticalScroll: function(event, target) {
        var owner = this.getScrollerOwner(),
            items = owner.query('tableview'),
            i = 0,
            len = items.length;

        for (; i < len; i++) {
            items[i].el.dom.scrollTop = target.scrollTop;
        }
    },

    onHorizontalScroll: function(event, target) {
        var owner = this.getScrollerOwner(),
            items = owner.query('tableview'),
            i = 0,
            len = items.length,
            center,
            centerEl,
            centerScrollWidth,
            centerClientWidth,
            width;

        center = items[1] || items[0];
        centerEl = center.el.dom;
        centerScrollWidth = centerEl.scrollWidth;
        centerClientWidth = centerEl.offsetWidth;
        width = this.horizontalScroller.getWidth();

        centerEl.scrollLeft = target.scrollLeft;
        this.headerCt.el.dom.scrollLeft = target.scrollLeft;
    },

    
    onStoreLoad: Ext.emptyFn,

    getEditorParent: function() {
        return this.body;
    },

    bindStore: function(store) {
        var me = this;
        me.store = store;
        me.getView().bindStore(store);
    },

    reconfigure: function(store, columns) {
        var me = this;

        if (me.lockable) {
            me.reconfigureLockable(store, columns);
            return;
        }

        if (columns) {
            me.headerCt.removeAll();
            me.headerCt.add(columns);
        }
        if (store) {
            store = Ext.StoreManager.lookup(store);
            me.bindStore(store);
        } else {
            me.getView().refresh();
        }
    },
    
    afterComponentLayout: function() {
        var me = this;
        me.callParent(arguments);
        me.determineScrollbars();
        me.invalidateScroller();
    }
});

Ext.define('Ext.view.Table', {
    extend: 'Ext.view.View',
    alias: 'widget.tableview',
    uses: [
        'Ext.view.TableChunker',
        'Ext.util.DelayedTask',
        'Ext.util.MixedCollection'
    ],

    cls: Ext.baseCSSPrefix + 'grid-view',

    
    itemSelector: '.' + Ext.baseCSSPrefix + 'grid-row',
    
    cellSelector: '.' + Ext.baseCSSPrefix + 'grid-cell',

    selectedItemCls: Ext.baseCSSPrefix + 'grid-row-selected',
    selectedCellCls: Ext.baseCSSPrefix + 'grid-cell-selected',
    focusedItemCls: Ext.baseCSSPrefix + 'grid-row-focused',
    overItemCls: Ext.baseCSSPrefix + 'grid-row-over',
    altRowCls:   Ext.baseCSSPrefix + 'grid-row-alt',
    rowClsRe: /(?:^|\s*)grid-row-(first|last|alt)(?:\s+|$)/g,
    cellRe: new RegExp('x-grid-cell-([^\\s]+) ', ''),

    
    trackOver: true,

    
    getRowClass: null,

    initComponent: function() {
        var me = this;
        
        me.scrollState = {};
        me.selModel.view = me;
        me.headerCt.view = me;
        me.initFeatures();
        me.tpl = '<div></div>';
        me.callParent();
        me.mon(me.store, {
            load: me.onStoreLoad,
            scope: me
        });

        
        
        
        
        
        
        
        
        
    },

    
    onStoreLoad: function(){
        var me = this;
        
        if (me.invalidateScrollerOnRefresh) {
            if (Ext.isGecko) {
                if (!me.scrollToTopTask) {
                    me.scrollToTopTask = Ext.create('Ext.util.DelayedTask', me.scrollToTop, me);
                }
                me.scrollToTopTask.delay(1);
            } else {
                me    .scrollToTop();
            }
        }
    },

    
    scrollToTop: Ext.emptyFn,
    
    
    addElListener: function(eventName, fn, scope){
        this.mon(this, eventName, fn, scope, {
            element: 'el'
        });
    },
    
    
    getGridColumns: function() {
        return this.headerCt.getGridColumns();    
    },
    
    
    getHeaderAtIndex: function(index) {
        return this.headerCt.getHeaderAtIndex(index);
    },
    
    
    getCell: function(record, column) {
        var row = this.getNode(record);
        return Ext.fly(row).down(column.getCellSelector());
    },

    
    getFeature: function(id) {
        var features = this.featuresMC;
        if (features) {
            return features.get(id);
        }
    },

    
    initFeatures: function() {
        var me = this,
            i = 0,
            features,
            len;
            
        me.features = me.features || [];
        features = me.features;
        len = features.length;

        me.featuresMC = Ext.create('Ext.util.MixedCollection');
        for (; i < len; i++) {
            
            if (!features[i].isFeature) {
                features[i] = Ext.create('feature.' + features[i].ftype, features[i]);
            }
            
            features[i].view = me;
            me.featuresMC.add(features[i]);
        }
    },

    
    attachEventsForFeatures: function() {
        var features = this.features,
            ln       = features.length,
            i        = 0;

        for (; i < ln; i++) {
            if (features[i].isFeature) {
                features[i].attachEvents();
            }
        }
    },

    afterRender: function() {
        var me = this;
        
        me.callParent();
        me.mon(me.el, {
            scroll: me.fireBodyScroll,
            scope: me
        });
        me.el.unselectable();
        me.attachEventsForFeatures();
    },

    fireBodyScroll: function(e, t) {
        this.fireEvent('bodyscroll', e, t);
    },

    
    
    prepareData: function(data, idx, record) {
        var me       = this,
            orig     = me.headerCt.prepareData(data, idx, record, me, me.ownerCt),
            features = me.features,
            ln       = features.length,
            i        = 0,
            node, feature;

        for (; i < ln; i++) {
            feature = features[i];
            if (feature.isFeature) {
                Ext.apply(orig, feature.getAdditionalData(data, idx, record, orig, me));
            }
        }

        return orig;
    },

    
    collectData: function(records, startIndex) {
        var preppedRecords = this.callParent(arguments),
            headerCt  = this.headerCt,
            fullWidth = headerCt.getFullWidth(),
            features  = this.features,
            ln = features.length,
            o = {
                rows: preppedRecords,
                fullWidth: fullWidth
            },
            i  = 0,
            feature,
            j = 0,
            jln,
            rowParams;

        jln = preppedRecords.length;
        
        
        if (this.getRowClass) {
            for (; j < jln; j++) {
                rowParams = {};
                preppedRecords[j]['rowCls'] = this.getRowClass(records[j], j, rowParams, this.store);
                if (rowParams.alt) {
                    Ext.Error.raise("The getRowClass alt property is no longer supported.");
                }
                if (rowParams.tstyle) {
                    Ext.Error.raise("The getRowClass tstyle property is no longer supported.");
                }
                if (rowParams.cells) {
                    Ext.Error.raise("The getRowClass cells property is no longer supported.");
                }
                if (rowParams.body) {
                    Ext.Error.raise("The getRowClass body property is no longer supported. Use the getAdditionalData method of the rowbody feature.");
                }
                if (rowParams.bodyStyle) {
                    Ext.Error.raise("The getRowClass bodyStyle property is no longer supported.");
                }
                if (rowParams.cols) {
                    Ext.Error.raise("The getRowClass cols property is no longer supported.");
                }
            }
        }
        
        
        for (; i < ln; i++) {
            feature = features[i];
            if (feature.isFeature && feature.collectData && !feature.disabled) {
                o = feature.collectData(records, preppedRecords, startIndex, fullWidth, o);
                break;
            }
        }
        return o;
    },

    
    
    onHeaderResize: function(header, w, suppressFocus) {
        var me = this,
            el = me.el;
        if (el) {
            me.saveScrollState();
            
            
            
            el.select('.' + Ext.baseCSSPrefix + 'grid-col-resizer-'+header.id).setWidth(w);
            el.select('.' + Ext.baseCSSPrefix + 'grid-table-resizer').setWidth(me.headerCt.getFullWidth());
            me.restoreScrollState();
            me.setNewTemplate();
            if (!suppressFocus) {
                me.el.focus();
            }
        }
    },

    
    onHeaderShow: function(headerCt, header, suppressFocus) {
        
        if (header.oldWidth) {
            this.onHeaderResize(header, header.oldWidth, suppressFocus);
            delete header.oldWidth;
        
        
        
        } else if (header.width && !header.flex) {
            this.onHeaderResize(header, header.width, suppressFocus);
        }
        this.setNewTemplate();
    },

    
    onHeaderHide: function(headerCt, header, suppressFocus) {
        this.onHeaderResize(header, 0, suppressFocus);
    },

    
    setNewTemplate: function() {
        var me = this,
            columns = me.headerCt.getColumnsForTpl(true);
            
        me.tpl = me.getTableChunker().getTableTpl({
            columns: columns,
            features: me.features
        });
    },

    
    getTableChunker: function() {
        return this.chunker || Ext.view.TableChunker;
    },

    
    addRowCls: function(rowInfo, cls) {
        var row = this.getNode(rowInfo);
        if (row) {
            Ext.fly(row).addCls(cls);
        }
    },

    
    removeRowCls: function(rowInfo, cls) {
        var row = this.getNode(rowInfo);
        if (row) {
            Ext.fly(row).removeCls(cls);
        }
    },

    
    onRowSelect : function(rowIdx) {
        this.addRowCls(rowIdx, this.selectedItemCls);
    },

    
    onRowDeselect : function(rowIdx) {
        var me = this;
        
        me.removeRowCls(rowIdx, me.selectedItemCls);
        me.removeRowCls(rowIdx, me.focusedItemCls);
    },
    
    onCellSelect: function(position) {
        var cell = this.getCellByPosition(position);
        if (cell) {
            cell.addCls(this.selectedCellCls);
        }
    },
    
    onCellDeselect: function(position) {
        var cell = this.getCellByPosition(position);
        if (cell) {
            cell.removeCls(this.selectedCellCls);
        }
        
    },
    
    onCellFocus: function(position) {
        
        this.focusCell(position);
    },
    
    getCellByPosition: function(position) {
        var row    = position.row,
            column = position.column,
            store  = this.store,
            node   = this.getNode(row),
            header = this.headerCt.getHeaderAtIndex(column),
            cellSelector,
            cell = false;
            
        if (header && node) {
            cellSelector = header.getCellSelector();
            cell = Ext.fly(node).down(cellSelector);
        }
        return cell;
    },

    
    
    onRowFocus: function(rowIdx, highlight, supressFocus) {
        var me = this,
            row = me.getNode(rowIdx);

        if (highlight) {
            me.addRowCls(rowIdx, me.focusedItemCls);
            if (!supressFocus) {
                me.focusRow(rowIdx);
            }
            
        } else {
            me.removeRowCls(rowIdx, me.focusedItemCls);
        }
    },

    
    focusRow: function(rowIdx) {
        var me         = this,
            row        = me.getNode(rowIdx),
            el         = me.el,
            adjustment = 0,
            panel      = me.ownerCt,
            rowRegion,
            elRegion,
            record;
            
        if (row && el) {
            elRegion  = el.getRegion();
            rowRegion = Ext.fly(row).getRegion();
            
            if (rowRegion.top < elRegion.top) {
                adjustment = rowRegion.top - elRegion.top;
            
            } else if (rowRegion.bottom > elRegion.bottom) {
                adjustment = rowRegion.bottom - elRegion.bottom;
            }
            record = me.getRecord(row);
            rowIdx = me.store.indexOf(record);

            if (adjustment) {
                
                panel.scrollByDeltaY(adjustment);
            }
            me.fireEvent('rowfocus', record, row, rowIdx);
        }
    },

    focusCell: function(position) {
        var me          = this,
            cell        = me.getCellByPosition(position),
            el          = me.el,
            adjustmentY = 0,
            adjustmentX = 0,
            elRegion    = el.getRegion(),
            panel       = me.ownerCt,
            cellRegion,
            record;

        if (cell) {
            cellRegion = cell.getRegion();
            
            if (cellRegion.top < elRegion.top) {
                adjustmentY = cellRegion.top - elRegion.top;
            
            } else if (cellRegion.bottom > elRegion.bottom) {
                adjustmentY = cellRegion.bottom - elRegion.bottom;
            }

            
            if (cellRegion.left < elRegion.left) {
                adjustmentX = cellRegion.left - elRegion.left;
            
            } else if (cellRegion.right > elRegion.right) {
                adjustmentX = cellRegion.right - elRegion.right;
            }

            if (adjustmentY) {
                
                panel.scrollByDeltaY(adjustmentY);
            }
            if (adjustmentX) {
                panel.scrollByDeltaX(adjustmentX);
            }
            el.focus();
            me.fireEvent('cellfocus', record, cell, position);
        }
    },

    
    scrollByDelta: function(delta, dir) {
        dir = dir || 'scrollTop';
        var elDom = this.el.dom;
        elDom[dir] = (elDom[dir] += delta);
    },

    onUpdate: function(ds, index) {
        this.callParent(arguments);
    },

    
    saveScrollState: function() {
        var dom = this.el.dom,
            state = this.scrollState;

        state.left = dom.scrollLeft;
        state.top = dom.scrollTop;
    },

    
    restoreScrollState: function() {
        var dom = this.el.dom,
            state = this.scrollState,
            headerEl = this.headerCt.el.dom;

        headerEl.scrollLeft = dom.scrollLeft = state.left;
        dom.scrollTop = state.top;
    },

    
    refresh: function(firstPass) {
        var me = this,
            table;

        
        me.setNewTemplate();
        
        me.callParent(arguments);

        

        if (me.rendered && !firstPass) {
            
            
        }
    },

    processItemEvent: function(record, row, rowIndex, e) {
        var me = this,
            cell = e.getTarget(me.cellSelector, row),
            cellIndex = cell ? cell.cellIndex : -1,
            map = me.statics().EventMap,
            selModel = me.getSelectionModel(),
            type = e.type,
            result;

        if (type == 'keydown' && !cell && selModel.getCurrentPosition) {
            
            cell = me.getCellByPosition(selModel.getCurrentPosition());
            if (cell) {
                cell = cell.dom;
                cellIndex = cell.cellIndex;
            }
        }

        result = me.fireEvent('uievent', type, me, cell, rowIndex, cellIndex, e);

        if (result === false || me.callParent(arguments) === false) {
            return false;
        }

        
        if (type == 'mouseover' || type == 'mouseout') {
            return true;
        }

        return !(
            
            (me['onBeforeCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) ||
            (me.fireEvent('beforecell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false) ||
            (me['onCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) ||
            (me.fireEvent('cell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false)
        );
    },

    processSpecialEvent: function(e) {
        var me = this,
            map = me.statics().EventMap,
            features = me.features,
            ln = features.length,
            type = e.type,
            i, feature, prefix, featureTarget,
            beforeArgs, args,
            panel = me.ownerCt;

        me.callParent(arguments);

        if (type == 'mouseover' || type == 'mouseout') {
            return;
        }

        for (i = 0; i < ln; i++) {
            feature = features[i];
            if (feature.hasFeatureEvent) {
                featureTarget = e.getTarget(feature.eventSelector, me.getTargetEl());
                if (featureTarget) {
                    prefix = feature.eventPrefix;
                    
                    
                    beforeArgs = feature.getFireEventArgs('before' + prefix + type, me, featureTarget, e);
                    args = feature.getFireEventArgs(prefix + type, me, featureTarget, e);
                    
                    if (
                        
                        (me.fireEvent.apply(me, beforeArgs) === false) ||
                        
                        (panel.fireEvent.apply(panel, beforeArgs) === false) ||
                        
                        (me.fireEvent.apply(me, args) === false) ||
                        
                        (panel.fireEvent.apply(panel, args) === false)
                    ) {
                        return false;
                    }
                }
            }
        }
        return true;
    },

    onCellMouseDown: Ext.emptyFn,
    onCellMouseUp: Ext.emptyFn,
    onCellClick: Ext.emptyFn,
    onCellDblClick: Ext.emptyFn,
    onCellContextMenu: Ext.emptyFn,
    onCellKeyDown: Ext.emptyFn,
    onBeforeCellMouseDown: Ext.emptyFn,
    onBeforeCellMouseUp: Ext.emptyFn,
    onBeforeCellClick: Ext.emptyFn,
    onBeforeCellDblClick: Ext.emptyFn,
    onBeforeCellContextMenu: Ext.emptyFn,
    onBeforeCellKeyDown: Ext.emptyFn,

    
    expandToFit: function(header) {
        var maxWidth = this.getMaxContentWidth(header);
        delete header.flex;
        header.setWidth(maxWidth);
    },

    
    getMaxContentWidth: function(header) {
        var cellSelector = header.getCellInnerSelector(),
            cells        = this.el.query(cellSelector),
            i = 0,
            ln = cells.length,
            maxWidth = header.el.dom.scrollWidth,
            scrollWidth;

        for (; i < ln; i++) {
            scrollWidth = cells[i].scrollWidth;
            if (scrollWidth > maxWidth) {
                maxWidth = scrollWidth;
            }
        }
        return maxWidth;
    },

    getPositionByEvent: function(e) {
        var me       = this,
            cellNode = e.getTarget(me.cellSelector),
            rowNode  = e.getTarget(me.itemSelector),
            record   = me.getRecord(rowNode),
            header   = me.getHeaderByCell(cellNode);

        return me.getPosition(record, header);
    },

    getHeaderByCell: function(cell) {
        if (cell) {
            var m = cell.className.match(this.cellRe);
            if (m && m[1]) {
                return Ext.getCmp(m[1]);
            }
        }
        return false;
    },

    
    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
        var me       = this,
            row      = pos.row,
            column   = pos.column,
            rowCount = me.store.getCount(),
            firstCol = me.getFirstVisibleColumnIndex(),
            lastCol  = me.getLastVisibleColumnIndex(),
            newPos   = {row: row, column: column},
            activeHeader = me.headerCt.getHeaderAtIndex(column);

        
        if (!activeHeader || activeHeader.hidden) {
            return false;
        }

        e = e || {};
        direction = direction.toLowerCase();
        switch (direction) {
            case 'right':
                
                if (column === lastCol) {
                    
                    if (preventWrap || row === rowCount - 1) {
                        return false;
                    }
                    if (!e.ctrlKey) {
                        
                        newPos.row = row + 1;
                        newPos.column = firstCol;
                    }
                
                } else {
                    if (!e.ctrlKey) {
                        newPos.column = column + me.getRightGap(activeHeader);
                    } else {
                        newPos.column = lastCol;
                    }
                }
                break;

            case 'left':
                
                if (column === firstCol) {
                    
                    if (preventWrap || row === 0) {
                        return false;
                    }
                    if (!e.ctrlKey) {
                        
                        newPos.row = row - 1;
                        newPos.column = lastCol;
                    }
                
                } else {
                    if (!e.ctrlKey) {
                        newPos.column = column + me.getLeftGap(activeHeader);
                    } else {
                        newPos.column = firstCol;
                    }
                }
                break;

            case 'up':
                
                if (row === 0) {
                    return false;
                
                } else {
                    if (!e.ctrlKey) {
                        newPos.row = row - 1;
                    } else {
                        newPos.row = 0;
                    }
                }
                break;

            case 'down':
                
                if (row === rowCount - 1) {
                    return false;
                
                } else {
                    if (!e.ctrlKey) {
                        newPos.row = row + 1;
                    } else {
                        newPos.row = rowCount - 1;
                    }
                }
                break;
        }

        if (verifierFn && verifierFn.call(scope || window, newPos) !== true) {
            return false;
        } else {
            return newPos;
        }
    },
    getFirstVisibleColumnIndex: function() {
        var headerCt   = this.getHeaderCt(),
            allColumns = headerCt.getGridColumns(),
            visHeaders = Ext.ComponentQuery.query(':not([hidden])', allColumns),
            firstHeader = visHeaders[0];

        return headerCt.getHeaderIndex(firstHeader);
    },

    getLastVisibleColumnIndex: function() {
        var headerCt   = this.getHeaderCt(),
            allColumns = headerCt.getGridColumns(),
            visHeaders = Ext.ComponentQuery.query(':not([hidden])', allColumns),
            lastHeader = visHeaders[visHeaders.length - 1];

        return headerCt.getHeaderIndex(lastHeader);
    },

    getHeaderCt: function() {
        return this.headerCt;
    },

    getPosition: function(record, header) {
        var me = this,
            store = me.store,
            gridCols = me.headerCt.getGridColumns();

        return {
            row: store.indexOf(record),
            column: Ext.Array.indexOf(gridCols, header)
        };
    },

    
    getRightGap: function(activeHeader) {
        var headerCt        = this.getHeaderCt(),
            headers         = headerCt.getGridColumns(),
            activeHeaderIdx = Ext.Array.indexOf(headers, activeHeader),
            i               = activeHeaderIdx + 1,
            nextIdx;

        for (; i <= headers.length; i++) {
            if (!headers[i].hidden) {
                nextIdx = i;
                break;
            }
        }

        return nextIdx - activeHeaderIdx;
    },

    beforeDestroy: function() {
        if (this.rendered) {
            this.el.removeAllListeners();
        }
        this.callParent(arguments);
    },

    
    getLeftGap: function(activeHeader) {
        var headerCt        = this.getHeaderCt(),
            headers         = headerCt.getGridColumns(),
            activeHeaderIdx = Ext.Array.indexOf(headers, activeHeader),
            i               = activeHeaderIdx - 1,
            prevIdx;

        for (; i >= 0; i--) {
            if (!headers[i].hidden) {
                prevIdx = i;
                break;
            }
        }

        return prevIdx - activeHeaderIdx;
    }
});

Ext.define('Ext.grid.View', {
    extend: 'Ext.view.Table',
    alias: 'widget.gridview',

    
    stripeRows: true,
    
    invalidateScrollerOnRefresh: true,
    
    
    scrollToTop : function(){
        if (this.rendered) {
            var section = this.ownerCt,
                verticalScroller = section.verticalScroller;
                
            if (verticalScroller) {
                verticalScroller.scrollToTop();
            }
        }
    },

    
    onAdd: function(ds, records, index) {
        this.callParent(arguments);
        this.doStripeRows(index);
    },
    
    
    onRemove: function(ds, records, index) {
        this.callParent(arguments);
        this.doStripeRows(index);
    },
    
    onUpdate: function(ds, record, operation) {
        var index = ds.indexOf(record);
        this.callParent(arguments);
        this.doStripeRows(index, index);
    },
    
    
    doStripeRows: function(startRow, endRow) {
        
        if (this.stripeRows) {
            var rows   = this.getNodes(startRow, endRow),
                rowsLn = rows.length,
                i      = 0,
                row;
                
            for (; i < rowsLn; i++) {
                row = rows[i];
                
                row.className = row.className.replace(this.rowClsRe, ' ');
                startRow++;
                
                if (startRow % 2 === 0) {
                    row.className += (' ' + this.altRowCls);
                }
            }
        }
    },
    
    refresh: function(firstPass) {
        this.callParent(arguments);
        this.doStripeRows(0);
        
        var g = this.up('gridpanel');
        if (g && this.invalidateScrollerOnRefresh) {
            g.invalidateScroller();
        }
    }
});


Ext.define('Ext.grid.Panel', {
    extend: 'Ext.panel.Table',
    requires: ['Ext.grid.View'],
    alias: ['widget.gridpanel', 'widget.grid'],
    alternateClassName: ['Ext.list.ListView', 'Ext.ListView', 'Ext.grid.GridPanel'],
    viewType: 'gridview',
    
    lockable: false,
    
    
    
    normalCfgCopy: ['invalidateScrollerOnRefresh', 'verticalScroller', 'verticalScrollDock', 'verticalScrollerType', 'scroll'],
    lockedCfgCopy: ['invalidateScrollerOnRefresh'],
    
    
    
    initComponent: function() {
        var me = this;

        if (me.columnLines) {
            me.setColumnLines(me.columnLines);
        }
        
        me.callParent();
    },
    
    setColumnLines: function(show) {
        var me = this,
            method = (show) ? 'addClsWithUI' : 'removeClsWithUI';
        
        me[method]('with-col-lines')
    }
});









Ext.define('Ext.grid.RowEditor', {
    extend: 'Ext.form.Panel',
    requires: [
        'Ext.tip.ToolTip',
        'Ext.util.HashMap',
        'Ext.util.KeyNav'
    ],

    saveBtnText  : 'Update',
    cancelBtnText: 'Cancel',
    errorsText: 'Errors',
    dirtyText: 'You need to commit or cancel your changes',

    lastScrollLeft: 0,
    lastScrollTop: 0,

    border: false,
    
    
    
    hideMode: 'offsets',

    initComponent: function() {
        var me = this,
            form;

        me.cls = Ext.baseCSSPrefix + 'grid-row-editor';

        me.layout = {
            type: 'hbox',
            align: 'middle'
        };

        
        
        me.columns = Ext.create('Ext.util.HashMap');
        me.columns.getKey = function(columnHeader) {
            var f;
            if (columnHeader.getEditor) {
                f = columnHeader.getEditor();
                if (f) {
                    return f.id;
                }
            }
            return columnHeader.id;
        };
        me.mon(me.columns, {
            add: me.onFieldAdd,
            remove: me.onFieldRemove,
            replace: me.onFieldReplace,
            scope: me
        });

        me.callParent(arguments);

        if (me.fields) {
            me.setField(me.fields);
            delete me.fields;
        }

        form = me.getForm();
        form.trackResetOnLoad = true;
    },

    onFieldChange: function() {
        var me = this,
            form = me.getForm(),
            valid = form.isValid();
        if (me.errorSummary && me.isVisible()) {
            me[valid ? 'hideToolTip' : 'showToolTip']();
        }
        if (me.floatingButtons) {
            me.floatingButtons.child('#update').setDisabled(!valid);
        }
        me.isValid = valid;
    },

    afterRender: function() {
        var me = this,
            plugin = me.editingPlugin;

        me.callParent(arguments);
        me.mon(me.renderTo, 'scroll', me.onCtScroll, me, { buffer: 100 });

        
        me.mon(me.el, {
            click: Ext.emptyFn,
            stopPropagation: true
        });

        me.el.swallowEvent([
            'keypress',
            'keydown'
        ]);

        me.keyNav = Ext.create('Ext.util.KeyNav', me.el, {
            enter: plugin.completeEdit,
            esc: plugin.onEscKey,
            scope: plugin
        });

        me.mon(plugin.view, {
            beforerefresh: me.onBeforeViewRefresh,
            refresh: me.onViewRefresh,
            scope: me
        });
    },

    onBeforeViewRefresh: function(view) {
        var me = this,
            viewDom = view.el.dom;

        if (me.el.dom.parentNode === viewDom) {
            viewDom.removeChild(me.el.dom);
        }
    },

    onViewRefresh: function(view) {
        var me = this,
            viewDom = view.el.dom,
            context = me.context,
            idx;

        viewDom.appendChild(me.el.dom);

        
        if (context && (idx = context.store.indexOf(context.record)) >= 0) {
            context.row = view.getNode(idx);
            me.reposition();
            if (me.tooltip && me.tooltip.isVisible()) {
                me.tooltip.setTarget(context.row);
            }
        } else {
            me.editingPlugin.cancelEdit();
        }
    },

    onCtScroll: function(e, target) {
        var me = this,
            scrollTop  = target.scrollTop,
            scrollLeft = target.scrollLeft;

        if (scrollTop !== me.lastScrollTop) {
            me.lastScrollTop = scrollTop;
            if ((me.tooltip && me.tooltip.isVisible()) || me.hiddenTip) {
                me.repositionTip();
            }
        }
        if (scrollLeft !== me.lastScrollLeft) {
            me.lastScrollLeft = scrollLeft;
            me.reposition();
        }
    },

    onColumnAdd: function(column) {
        this.setField(column);
    },

    onColumnRemove: function(column) {
        this.columns.remove(column);
    },

    onColumnResize: function(column, width) {
        column.getEditor().setWidth(width - 2);
        if (this.isVisible()) {
            this.reposition();
        }
    },

    onColumnHide: function(column) {
        column.getEditor().hide();
        if (this.isVisible()) {
            this.reposition();
        }
    },

    onColumnShow: function(column) {
        var field = column.getEditor();
        field.setWidth(column.getWidth() - 2).show();
        if (this.isVisible()) {
            this.reposition();
        }
    },

    onColumnMove: function(column, fromIdx, toIdx) {
        var field = column.getEditor();
        if (this.items.indexOf(field) != toIdx) {
            this.move(fromIdx, toIdx);
        }
    },

    onFieldAdd: function(map, fieldId, column) {
        var me = this,
            colIdx = me.editingPlugin.grid.headerCt.getHeaderIndex(column),
            field = column.getEditor({ xtype: 'displayfield' });

        me.insert(colIdx, field);
    },

    onFieldRemove: function(map, fieldId, column) {
        var me = this,
            field = column.getEditor(),
            fieldEl = field.el;
        me.remove(field, false);
        if (fieldEl) {
            fieldEl.remove();
        }
    },

    onFieldReplace: function(map, fieldId, column, oldColumn) {
        var me = this;
        me.onFieldRemove(map, fieldId, oldColumn);
    },

    clearFields: function() {
        var me = this,
            map = me.columns;
        map.each(function(fieldId) {
            map.removeAtKey(fieldId);
        });
    },

    getFloatingButtons: function() {
        var me = this,
            cssPrefix = Ext.baseCSSPrefix,
            btnsCss = cssPrefix + 'grid-row-editor-buttons',
            plugin = me.editingPlugin,
            btns;

        if (!me.floatingButtons) {
            btns = me.floatingButtons = Ext.create('Ext.Container', {
                renderTpl: [
                    '<div class="{baseCls}-ml"></div>',
                    '<div class="{baseCls}-mr"></div>',
                    '<div class="{baseCls}-bl"></div>',
                    '<div class="{baseCls}-br"></div>',
                    '<div class="{baseCls}-bc"></div>'
                ],

                renderTo: me.el,
                baseCls: btnsCss,
                layout: {
                    type: 'hbox',
                    align: 'middle'
                },
                defaults: {
                    margins: '0 1 0 1'
                },
                items: [{
                    itemId: 'update',
                    flex: 1,
                    xtype: 'button',
                    handler: plugin.completeEdit,
                    scope: plugin,
                    text: me.saveBtnText,
                    disabled: !me.isValid
                }, {
                    flex: 1,
                    xtype: 'button',
                    handler: plugin.cancelEdit,
                    scope: plugin,
                    text: me.cancelBtnText
                }]
            });

            
            me.mon(btns.el, {
                
                
                mousedown: Ext.emptyFn,
                click: Ext.emptyFn,
                stopEvent: true
            });
        }
        return me.floatingButtons;
    },

    reposition: function(animateConfig) {
        var me = this,
            context = me.context,
            row = context && Ext.get(context.row),
            btns = me.getFloatingButtons(),
            btnEl = btns.el,
            grid = me.editingPlugin.grid,
            viewEl = grid.view.el,
            scroller = grid.verticalScroller,

            
            
            mainBodyWidth = grid.headerCt.getFullWidth(),
            scrollerWidth = grid.getWidth(),

            
            
            width = Math.min(mainBodyWidth, scrollerWidth),
            scrollLeft = grid.view.el.dom.scrollLeft,
            btnWidth = btns.getWidth(),
            left = (width - btnWidth) / 2 + scrollLeft,
            y, rowH, newHeight,

            invalidateScroller = function() {
                if (scroller) {
                    scroller.invalidate();
                    btnEl.scrollIntoView(viewEl, false);
                }
                if (animateConfig && animateConfig.callback) {
                    animateConfig.callback.call(animateConfig.scope || me);
                }
            };

        
        if (row && Ext.isElement(row.dom)) {
            
            
            row.scrollIntoView(viewEl, false);

            
            
            
            y = row.getXY()[1] - 5;
            rowH = row.getHeight();
            newHeight = rowH + 10;

            
            
            
            
            if (Ext.isIE) {
                newHeight += 2;
            }

            
            if (me.getHeight() != newHeight) {
                me.setHeight(newHeight);
                me.el.setLeft(0);
            }

            if (animateConfig) {
                var animObj = {
                    to: {
                        y: y
                    },
                    duration: animateConfig.duration || 125,
                    listeners: {
                        afteranimate: function() {
                            invalidateScroller();
                            y = row.getXY()[1] - 5;
                            me.el.setY(y);
                        }
                    }
                };
                me.animate(animObj);
            } else {
                me.el.setY(y);
                invalidateScroller();
            }
        }
        if (me.getWidth() != mainBodyWidth) {
            me.setWidth(mainBodyWidth);
        }
        btnEl.setLeft(left);
    },

    getEditor: function(fieldInfo) {
        var me = this;

        if (Ext.isNumber(fieldInfo)) {
            
            
            
            return me.query('>[isFormField]')[fieldInfo];
        } else if (fieldInfo instanceof Ext.grid.column.Column) {
            return fieldInfo.getEditor();
        }
    },

    removeField: function(field) {
        var me = this;

        
        field = me.getEditor(field);
        me.mun(field, 'validitychange', me.onValidityChange, me);

        
        
        me.columns.removeKey(field.id);
    },

    setField: function(column) {
        var me = this,
            field;

        if (Ext.isArray(column)) {
            Ext.Array.forEach(column, me.setField, me);
            return;
        }

        
        field = column.getEditor(null, {
            xtype: 'displayfield',
            
            
            getModelData: function() {
                return null;
            }
        });
        field.margins = '0 0 0 2';
        field.setWidth(column.getDesiredWidth() - 2);
        me.mon(field, 'change', me.onFieldChange, me);

        
        
        me.columns.add(field.id, column);
        
        if (me.isVisible() && me.context) {
            me.renderColumnData(field, me.context.record);
        }
    },

    loadRecord: function(record) {
        var me = this,
            form = me.getForm();
        form.loadRecord(record);
        if (form.isValid()) {
            me.hideToolTip();
        } else {
            me.showToolTip();
        }

        
        Ext.Array.forEach(me.query('>displayfield'), function(field) {
            me.renderColumnData(field, record);
        }, me);
    },

    renderColumnData: function(field, record) {
        var me = this,
            grid = me.editingPlugin.grid,
            headerCt = grid.headerCt,
            view = grid.view,
            store = view.store,
            column = me.columns.get(field.id),
            value = record.get(column.dataIndex);

        
        if (column.renderer) {
            var metaData = { tdCls: '', style: '' },
                rowIdx = store.indexOf(record),
                colIdx = headerCt.getHeaderIndex(column);

            value = column.renderer.call(
                column.scope || headerCt.ownerCt,
                value,
                metaData,
                record,
                rowIdx,
                colIdx,
                store,
                view
            );
        }

        field.setRawValue(value);
        field.resetOriginalValue();
    },

    beforeEdit: function() {
        var me = this;

        if (me.isVisible() && !me.autoCancel && me.isDirty()) {
            me.showToolTip();
            return false;
        }
    },

    
    startEdit: function(record, columnHeader) {
        var me = this,
            grid = me.editingPlugin.grid,
            view = grid.getView(),
            store = grid.store,
            context = me.context = Ext.apply(me.editingPlugin.context, {
                view: grid.getView(),
                store: store
            });

        
        context.grid.getSelectionModel().select(record);

        
        me.loadRecord(record);

        if (!me.isVisible()) {
            me.show();
            me.focusContextCell();
        } else {
            me.reposition({
                callback: this.focusContextCell
            });
        }
    },

    
    focusContextCell: function() {
        var field = this.getEditor(this.context.colIdx);
        if (field && field.focus) {
            field.focus();
        }
    },

    cancelEdit: function() {
        var me = this,
            form = me.getForm();

        me.hide();
        form.clearInvalid();
        form.reset();
    },

    completeEdit: function() {
        var me = this,
            form = me.getForm();

        if (!form.isValid()) {
            return;
        }

        form.updateRecord(me.context.record);
        me.hide();
        return true;
    },

    onShow: function() {
        var me = this;
        me.callParent(arguments);
        me.reposition();
    },

    onHide: function() {
        var me = this;
        me.callParent(arguments);
        me.hideToolTip();
        me.invalidateScroller();
        if (me.context) {
            me.context.view.focus();
            me.context = null;
        }
    },

    isDirty: function() {
        var me = this,
            form = me.getForm();
        return form.isDirty();
    },

    getToolTip: function() {
        var me = this,
            tip;

        if (!me.tooltip) {
            tip = me.tooltip = Ext.createWidget('tooltip', {
                cls: Ext.baseCSSPrefix + 'grid-row-editor-errors',
                title: me.errorsText,
                autoHide: false,
                closable: true,
                closeAction: 'disable',
                anchor: 'left'
            });
        }
        return me.tooltip;
    },

    hideToolTip: function() {
        var me = this,
            tip = me.getToolTip();
        if (tip.rendered) {
            tip.disable();
        }
        me.hiddenTip = false;
    },

    showToolTip: function() {
        var me = this,
            tip = me.getToolTip(),
            context = me.context,
            row = Ext.get(context.row),
            viewEl = context.grid.view.el;

        tip.setTarget(row);
        tip.showAt([-10000, -10000]);
        tip.body.update(me.getErrors());
        tip.mouseOffset = [viewEl.getWidth() - row.getWidth() + me.lastScrollLeft + 15, 0];
        me.repositionTip();
        tip.doLayout();
        tip.enable();
    },

    repositionTip: function() {
        var me = this,
            tip = me.getToolTip(),
            context = me.context,
            row = Ext.get(context.row),
            viewEl = context.grid.view.el,
            viewHeight = viewEl.getHeight(),
            viewTop = me.lastScrollTop,
            viewBottom = viewTop + viewHeight,
            rowHeight = row.getHeight(),
            rowTop = row.dom.offsetTop,
            rowBottom = rowTop + rowHeight;

        if (rowBottom > viewTop && rowTop < viewBottom) {
            tip.show();
            me.hiddenTip = false;
        } else {
            tip.hide();
            me.hiddenTip = true;
        }
    },

    getErrors: function() {
        var me = this,
            dirtyText = !me.autoCancel && me.isDirty() ? me.dirtyText + '<br />' : '',
            errors = [];

        Ext.Array.forEach(me.query('>[isFormField]'), function(field) {
            errors = errors.concat(
                Ext.Array.map(field.getErrors(), function(e) {
                    return '<li>' + e + '</li>';
                })
            );
        }, me);

        return dirtyText + '<ul>' + errors.join('') + '</ul>';
    },

    invalidateScroller: function() {
        var me = this,
            context = me.context,
            scroller = context.grid.verticalScroller;

        if (scroller) {
            scroller.invalidate();
        }
    }
});

Ext.define('Ext.grid.header.Container', {
    extend: 'Ext.container.Container',
    uses: [
        'Ext.grid.ColumnLayout',
        'Ext.grid.column.Column',
        'Ext.menu.Menu',
        'Ext.menu.CheckItem',
        'Ext.menu.Separator',
        'Ext.grid.plugin.HeaderResizer',
        'Ext.grid.plugin.HeaderReorderer'
    ],
    border: true,

    alias: 'widget.headercontainer',

    baseCls: Ext.baseCSSPrefix + 'grid-header-ct',
    dock: 'top',

    
    weight: 100,
    defaultType: 'gridcolumn',
    
    defaultWidth: 100,


    sortAscText: 'Sort Ascending',
    sortDescText: 'Sort Descending',
    sortClearText: 'Clear Sort',
    columnsText: 'Columns',

    lastHeaderCls: Ext.baseCSSPrefix + 'column-header-last',
    firstHeaderCls: Ext.baseCSSPrefix + 'column-header-first',
    headerOpenCls: Ext.baseCSSPrefix + 'column-header-open',

    
    triStateSort: false,

    ddLock: false,

    dragging: false,

    

    
    sortable: true,
    
    initComponent: function() {
        var me = this;
        
        me.headerCounter = 0;
        me.plugins = me.plugins || [];

        
        

        
        
        if (!me.isHeader) {
            me.resizer   = Ext.create('Ext.grid.plugin.HeaderResizer');
            me.reorderer = Ext.create('Ext.grid.plugin.HeaderReorderer');
            if (!me.enableColumnResize) {
                me.resizer.disable();
            } 
            if (!me.enableColumnMove) {
                me.reorderer.disable();
            }
            me.plugins.push(me.reorderer, me.resizer);
        }

        
        if (me.isHeader && !me.items) {
            me.layout = 'auto';
        }
        
        else {
            me.layout = {
                type: 'gridcolumn',
                availableSpaceOffset: me.availableSpaceOffset,
                align: 'stretchmax',
                resetStretch: true
            };
        }
        me.defaults = me.defaults || {};
        Ext.applyIf(me.defaults, {
            width: me.defaultWidth,
            triStateSort: me.triStateSort,
            sortable: me.sortable
        });
        me.callParent();
        me.addEvents(
            
            'columnresize',

            
            'headerclick',

            
            'headertriggerclick',

            
            'columnmove',
            
            'columnhide',
            
            'columnshow',
            
            'sortchange',
            
            'menucreate'
        );
    },

    onDestroy: function() {
        Ext.destroy(this.resizer, this.reorderer);
        this.callParent();
    },

    
    
    
    onAdd: function(c) {
        var me = this;
        if (!c.headerId) {
            c.headerId = 'h' + (++me.headerCounter);
        }
        me.callParent(arguments);
        me.purgeCache();
    },

    
    
    
    onRemove: function(c) {
        var me = this;
        me.callParent(arguments);
        me.purgeCache();
    },

    afterRender: function() {
        this.callParent();
        var store   = this.up('[store]').store,
            sorters = store.sorters,
            first   = sorters.first(),
            hd;

        if (first) {
            hd = this.down('gridcolumn[dataIndex=' + first.property  +']');
            if (hd) {
                hd.setSortState(first.direction, false, true);
            }
        }
    },

    afterLayout: function() {
        if (!this.isHeader) {
            var me = this,
                topHeaders = me.query('>gridcolumn:not([hidden])'),
                viewEl,
                firstHeaderEl,
                lastHeaderEl;

            me.callParent(arguments);

            if (topHeaders.length) {
                firstHeaderEl = topHeaders[0].el;
                if (firstHeaderEl !== me.pastFirstHeaderEl) {
                    if (me.pastFirstHeaderEl) {
                        me.pastFirstHeaderEl.removeCls(me.firstHeaderCls);
                    }
                    firstHeaderEl.addCls(me.firstHeaderCls);
                    me.pastFirstHeaderEl = firstHeaderEl;
                }
                
                lastHeaderEl = topHeaders[topHeaders.length - 1].el;
                if (lastHeaderEl !== me.pastLastHeaderEl) {
                    if (me.pastLastHeaderEl) {
                        me.pastLastHeaderEl.removeCls(me.lastHeaderCls);
                    }
                    lastHeaderEl.addCls(me.lastHeaderCls);
                    me.pastLastHeaderEl = lastHeaderEl
                }
            }
        }
        
    },

    onHeaderShow: function(header) {
        
        var me = this,
            gridSection = me.ownerCt,
            menu = me.getMenu(),
            topItems, topItemsVisible,
            colCheckItem,
            itemToEnable,
            len, i;

        if (menu) {

            colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
            if (colCheckItem) {
                colCheckItem.setChecked(true, true);
            }

            
            topItems = menu.query('#columnItem>menucheckitem[checked]');
            topItemsVisible = topItems.length;
            if ((me.getVisibleGridColumns().length > 1) && me.disabledMenuItems && me.disabledMenuItems.length) {
                if (topItemsVisible == 1) {
                    Ext.Array.remove(me.disabledMenuItems, topItems[0]);
                }
                for (i = 0, len = me.disabledMenuItems.length; i < len; i++) {
                    itemToEnable = me.disabledMenuItems[i];
                    if (!itemToEnable.isDestroyed) {
                        itemToEnable[itemToEnable.menu ? 'enableCheckChange' : 'enable']();
                    }
                }
                if (topItemsVisible == 1) {
                    me.disabledMenuItems = topItems;
                } else {
                    me.disabledMenuItems = [];
                }
            }
        }

        
        
        if (!header.isGroupHeader) {
            if (me.view) {
                me.view.onHeaderShow(me, header, true);
            }
            if (gridSection) {
                gridSection.onHeaderShow(me, header);
            }
        }
        me.fireEvent('columnshow', me, header);

        
        me.doLayout();
    },

    onHeaderHide: function(header, suppressLayout) {
        
        var me = this,
            gridSection = me.ownerCt,
            menu = me.getMenu(),
            colCheckItem;

        if (menu) {

            
            colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
            if (colCheckItem) {
                colCheckItem.setChecked(false, true);
            }
            me.setDisabledItems();
        }

        
        if (!header.isGroupHeader) {
            if (me.view) {
                me.view.onHeaderHide(me, header, true);
            }
            if (gridSection) {
                gridSection.onHeaderHide(me, header);
            }

            
            if (!suppressLayout) {
                me.doLayout();
            }
        }
        me.fireEvent('columnhide', me, header);
    },

    setDisabledItems: function(){
        var me = this,
            menu = me.getMenu(),
            i = 0,
            len,
            itemsToDisable,
            itemToDisable;

        
        itemsToDisable = menu.query('#columnItem>menucheckitem[checked]');
        if ((itemsToDisable.length === 1)) {
            if (!me.disabledMenuItems) {
                me.disabledMenuItems = [];
            }

            
            if ((me.getVisibleGridColumns().length === 1) && itemsToDisable[0].menu) {
                itemsToDisable = itemsToDisable.concat(itemsToDisable[0].menu.query('menucheckitem[checked]'));
            }

            len = itemsToDisable.length;
            
            for (i = 0; i < len; i++) {
                itemToDisable = itemsToDisable[i];
                if (!Ext.Array.contains(me.disabledMenuItems, itemToDisable)) {
                    itemToDisable[itemToDisable.menu ? 'disableCheckChange' : 'disable']();
                    me.disabledMenuItems.push(itemToDisable);
                }
            }
        }
    },

    
    tempLock: function() {
        this.ddLock = true;
        Ext.Function.defer(function() {
            this.ddLock = false;
        }, 200, this);
    },

    onHeaderResize: function(header, w, suppressFocus) {
        this.tempLock();
        if (this.view && this.view.rendered) {
            this.view.onHeaderResize(header, w, suppressFocus);
        }
        this.fireEvent('columnresize', this, header, w);
    },

    onHeaderClick: function(header, e, t) {
        this.fireEvent("headerclick", this, header, e, t);
    },

    onHeaderTriggerClick: function(header, e, t) {
        
        if (this.fireEvent("headertriggerclick", this, header, e, t) !== false) {
            this.showMenuBy(t, header);
        }
    },

    showMenuBy: function(t, header) {
        var menu = this.getMenu(),
            ascItem  = menu.down('#ascItem'),
            descItem = menu.down('#descItem'),
            sortableMth;

        menu.activeHeader = menu.ownerCt = header;
        menu.setFloatParent(header);
        
        header.titleContainer.addCls(this.headerOpenCls);

        
        sortableMth = header.sortable ? 'enable' : 'disable';
        if (ascItem) {
            ascItem[sortableMth]();
        }
        if (descItem) {
            descItem[sortableMth]();
        }
        menu.showBy(t);
    },

    
    onMenuDeactivate: function() {
        var menu = this.getMenu();
        
        menu.activeHeader.titleContainer.removeCls(this.headerOpenCls);
    },

    moveHeader: function(fromIdx, toIdx) {

        
        this.tempLock();
        this.onHeaderMoved(this.move(fromIdx, toIdx), fromIdx, toIdx);
    },

    purgeCache: function() {
        var me = this;
        
        delete me.gridDataColumns;

        
        if (me.menu) {
            me.menu.destroy();
            delete me.menu;
        }
    },

    onHeaderMoved: function(header, fromIdx, toIdx) {
        var me = this,
            gridSection = me.ownerCt;

        if (gridSection) {
            gridSection.onHeaderMove(me, header, fromIdx, toIdx);
        }
        me.fireEvent("columnmove", me, header, fromIdx, toIdx);
    },

    
    getMenu: function() {
        var me = this;

        if (!me.menu) {
            me.menu = Ext.create('Ext.menu.Menu', {
                items: me.getMenuItems(),
                listeners: {
                    deactivate: me.onMenuDeactivate,
                    scope: me
                }
            });
            me.setDisabledItems();
            me.fireEvent('menucreate', me, me.menu);
        }
        return me.menu;
    },

    
    getMenuItems: function() {
        var me = this,
            menuItems = [{
                itemId: 'columnItem',
                text: me.columnsText,
                cls: Ext.baseCSSPrefix + 'cols-icon',
                menu: me.getColumnMenu(me)
            }];

        if (me.sortable) {
            menuItems.unshift({
                itemId: 'ascItem',
                text: me.sortAscText,
                cls: 'xg-hmenu-sort-asc',
                handler: me.onSortAscClick,
                scope: me
            },{
                itemId: 'descItem',
                text: me.sortDescText,
                cls: 'xg-hmenu-sort-desc',
                handler: me.onSortDescClick,
                scope: me
            },'-');
        }
        return menuItems;
    },

    
    onSortAscClick: function() {
        var menu = this.getMenu(),
            activeHeader = menu.activeHeader;

        activeHeader.setSortState('ASC');
    },

    
    onSortDescClick: function() {
        var menu = this.getMenu(),
            activeHeader = menu.activeHeader;

        activeHeader.setSortState('DESC');
    },

    
    getColumnMenu: function(headerContainer) {
        var menuItems = [],
            i = 0,
            item,
            items = headerContainer.query('>gridcolumn[hideable]'),
            itemsLn = items.length,
            menuItem;

        for (; i < itemsLn; i++) {
            item = items[i];
            menuItem = Ext.create('Ext.menu.CheckItem', {
                text: item.text,
                checked: !item.hidden,
                hideOnClick: false,
                headerId: item.id,
                menu: item.isGroupHeader ? this.getColumnMenu(item) : undefined,
                checkHandler: this.onColumnCheckChange,
                scope: this
            });
            if (itemsLn === 1) {
                menuItem.disabled = true;
            }
            menuItems.push(menuItem);

            
            
            item.on({
                destroy: Ext.Function.bind(menuItem.destroy, menuItem)
            });
        }
        return menuItems;
    },

    onColumnCheckChange: function(checkItem, checked) {
        var header = Ext.getCmp(checkItem.headerId);
        header[checked ? 'show' : 'hide']();
    },

    
    getColumnsForTpl: function(flushCache) {
        var cols    = [],
            headers   = this.getGridColumns(flushCache),
            headersLn = headers.length,
            i = 0,
            header;

        for (; i < headersLn; i++) {
            header = headers[i];
            cols.push({
                dataIndex: header.dataIndex,
                align: header.align,
                width: header.hidden ? 0 : header.getDesiredWidth(),
                id: header.id,
                cls: header.tdCls,
                columnId: header.getItemId()
            });
        }
        return cols;
    },

    
    getColumnCount: function() {
        return this.getGridColumns().length;
    },

    
    getFullWidth: function(flushCache) {
        var fullWidth = 0,
            headers     = this.getVisibleGridColumns(flushCache),
            headersLn   = headers.length,
            i         = 0;

        for (; i < headersLn; i++) {
            if (!isNaN(headers[i].width)) {
                
                if (headers[i].getDesiredWidth) {
                    fullWidth += headers[i].getDesiredWidth();
                
                } else {
                    fullWidth += headers[i].getWidth();
                }
            }
        }
        return fullWidth;
    },

    
    clearOtherSortStates: function(activeHeader) {
        var headers   = this.getGridColumns(),
            headersLn = headers.length,
            i         = 0,
            oldSortState;

        for (; i < headersLn; i++) {
            if (headers[i] !== activeHeader) {
                oldSortState = headers[i].sortState;
                
                headers[i].setSortState(null, true);
                
                
                
            }
        }
    },

    
    getVisibleGridColumns: function(refreshCache) {
        return Ext.ComponentQuery.query(':not([hidden])', this.getGridColumns(refreshCache));
    },

    
    getGridColumns: function(refreshCache) {
        var me = this,
            result = refreshCache ? null : me.gridDataColumns;

        
        if (!result) {
            me.gridDataColumns = result = [];
            me.cascade(function(c) {
                if ((c !== me) && !c.isGroupHeader) {
                    result.push(c);
                }
            });
        }

        return result;
    },

    
    getHeaderIndex: function(header) {
        var columns = this.getGridColumns();
        return Ext.Array.indexOf(columns, header);
    },

    
    getHeaderAtIndex: function(index) {
        var columns = this.getGridColumns();
        return columns[index];
    },

    
    prepareData: function(data, rowIdx, record, view, panel) {
        var obj       = {},
            headers   = this.gridDataColumns || this.getGridColumns(),
            headersLn = headers.length,
            colIdx    = 0,
            header,
            headerId,
            renderer,
            value,
            metaData,
            store = panel.store;

        for (; colIdx < headersLn; colIdx++) {
            metaData = {
                tdCls: '',
                style: ''
            };
            header = headers[colIdx];
            headerId = header.id;
            renderer = header.renderer;
            value = data[header.dataIndex];

            
            
            if (typeof renderer === "string") {
                header.renderer = renderer = Ext.util.Format[renderer];
            }
            
            if (typeof renderer === "function") {
                value = renderer.call(
                    header.scope || this.ownerCt,
                    value,
                    
                    
                    metaData,
                    record,
                    rowIdx,
                    colIdx,
                    store,
                    view
                );
            }

            if (metaData.css) {
                
                obj.cssWarning = true;
                metaData.tdCls = metaData.css;
                delete metaData.css;
            }
            
            obj[headerId+'-modified'] = record.modified[header.dataIndex] ? Ext.baseCSSPrefix + 'grid-dirty-cell' : '';
            obj[headerId+'-tdCls'] = metaData.tdCls;
            obj[headerId+'-tdAttr'] = metaData.tdAttr;
            obj[headerId+'-style'] = metaData.style;
            if (value === undefined || value === null || value === '') {
                value = '&#160;';
            }
            obj[headerId] = value;
        }
        return obj;
    },

    expandToFit: function(header) {
        if (this.view) {
            this.view.expandToFit(header);
        }
    }
});


Ext.define('Ext.grid.column.Column', {
    extend: 'Ext.grid.header.Container',
    alias: 'widget.gridcolumn',
    requires: ['Ext.util.KeyNav'],
    alternateClassName: 'Ext.grid.Column',

    baseCls: Ext.baseCSSPrefix + 'column-header ' + Ext.baseCSSPrefix + 'unselectable',

    
    hoverCls: Ext.baseCSSPrefix + 'column-header-over',

    handleWidth: 5,

    sortState: null,

    possibleSortStates: ['ASC', 'DESC'],

    renderTpl:
        '<div class="' + Ext.baseCSSPrefix + 'column-header-inner">' +
            '<span class="' + Ext.baseCSSPrefix + 'column-header-text">' +
                '{text}' +
            '</span>' +
            '<tpl if="!values.menuDisabled"><div class="' + Ext.baseCSSPrefix + 'column-header-trigger"></div></tpl>' +
        '</div>',

    

    
    dataIndex: null,

    
    text: '&#160',

    
    sortable: true,
    
    
     
    
    hideable: true,

    
    menuDisabled: false,

    
    renderer: false,

    
    align: 'left',

    
    draggable: true,

    
    
    initDraggable: Ext.emptyFn,

    

    

    

    
    isHeader: true,

    initComponent: function() {
        var me = this,
            i,
            len;
        
        if (Ext.isDefined(me.header)) {
            me.text = me.header;
            delete me.header;
        }

        
        
        
        if (me.flex) {
            me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth;
        }
        
        
        else {
            me.minWidth = me.width;
        }

        if (!me.triStateSort) {
            me.possibleSortStates.length = 2;
        }

        
        if (Ext.isDefined(me.columns)) {
            me.isGroupHeader = true;

            if (me.dataIndex) {
                Ext.Error.raise('Ext.grid.column.Column: Group header may not accept a dataIndex');
            }
            if ((me.width && me.width !== Ext.grid.header.Container.prototype.defaultWidth) || me.flex) {
                Ext.Error.raise('Ext.grid.column.Column: Group header does not support setting explicit widths or flexs. The group header width is calculated by the sum of its children.');
            }

            
            me.items = me.columns;
            delete me.columns;
            delete me.flex;
            me.width = 0;

            
            for (i = 0, len = me.items.length; i < len; i++) {
                me.width += me.items[i].width || Ext.grid.header.Container.prototype.defaultWidth;
                if (me.items[i].flex) {
                    Ext.Error.raise('Ext.grid.column.Column: items of a grouped header do not support flexed values. Each item must explicitly define its width.');
                }
            }
            me.minWidth = me.width;

            me.cls = (me.cls||'') + ' ' + Ext.baseCSSPrefix + 'group-header';
            me.sortable = false;
            me.fixed = true;
            me.align = 'center';
        }

        Ext.applyIf(me.renderSelectors, {
            titleContainer: '.' + Ext.baseCSSPrefix + 'column-header-inner',
            triggerEl: '.' + Ext.baseCSSPrefix + 'column-header-trigger',
            textEl: '.' + Ext.baseCSSPrefix + 'column-header-text'
        });

        
        me.callParent(arguments);
    },

    onAdd: function(childHeader) {
        childHeader.isSubHeader = true;
        childHeader.addCls(Ext.baseCSSPrefix + 'group-sub-header');
    },

    onRemove: function(childHeader) {
        childHeader.isSubHeader = false;
        childHeader.removeCls(Ext.baseCSSPrefix + 'group-sub-header');
    },

    initRenderData: function() {
        var me = this;
        
        Ext.applyIf(me.renderData, {
            text: me.text,
            menuDisabled: me.menuDisabled
        });
        return me.callParent(arguments);
    },

    
    setText: function(text) {
        this.text = text;
        if (this.rendered) {
            this.textEl.update(text);
        } 
    },

    
    
    getOwnerHeaderCt: function() {
        return this.up(':not([isHeader])');
    },

    
    getIndex: function() {
        return this.isGroupColumn ? false : this.getOwnerHeaderCt().getHeaderIndex(this);
    },

    afterRender: function() {
        var me = this,
            el = me.el;

        me.callParent(arguments);

        el.addCls(Ext.baseCSSPrefix + 'column-header-align-' + me.align).addClsOnOver(me.overCls);

        me.mon(el, {
            click:     me.onElClick,
            dblclick:  me.onElDblClick,
            scope:     me
        });
        
        
        
        if (!Ext.isIE8 || !Ext.isStrict) {
            me.mon(me.getFocusEl(), {
                focus: me.onTitleMouseOver,
                blur: me.onTitleMouseOut,
                scope: me
            });
        }

        me.mon(me.titleContainer, {
            mouseenter:  me.onTitleMouseOver,
            mouseleave:  me.onTitleMouseOut,
            scope:      me
        });

        me.keyNav = Ext.create('Ext.util.KeyNav', el, {
            enter: me.onEnterKey,
            down: me.onDownKey,
            scope: me
        });
    },

    setSize: function(width, height) {
        var me = this,
            headerCt = me.ownerCt,
            ownerHeaderCt = me.getOwnerHeaderCt(),
            siblings,
            len, i,
            oldWidth = me.getWidth(),
            newWidth = 0;

        if (width !== oldWidth) {

            
            if (headerCt.isGroupHeader) {

                siblings = headerCt.items.items;
                len = siblings.length;

                
                if (siblings[len - 1].rendered) {

                    for (i = 0; i < len; i++) {
                        newWidth += (siblings[i] === me) ? width : siblings[i].getWidth();
                    }
                    headerCt.minWidth = newWidth;
                    headerCt.setWidth(newWidth);
                }
            }
            me.callParent(arguments);
        }
    },

    afterComponentLayout: function(width, height) {
        var me = this,
            ownerHeaderCt = this.getOwnerHeaderCt();

        me.callParent(arguments);

        
        
        
        if (width && !me.isGroupHeader && ownerHeaderCt) {
            ownerHeaderCt.onHeaderResize(me, width, true);
        }
    },

    
    
    setPadding: function() {
        var me = this,
            headerHeight,
            lineHeight = parseInt(me.textEl.getStyle('line-height'), 10);

        
        if (!me.isGroupHeader) {
            headerHeight = me.el.getViewSize().height;
            if (me.titleContainer.getHeight() < headerHeight) {
                me.titleContainer.dom.style.height = headerHeight + 'px';
            }
        }
        headerHeight = me.titleContainer.getViewSize().height;

        
        if (lineHeight) {
            me.titleContainer.setStyle({
                paddingTop: Math.max(((headerHeight - lineHeight) / 2), 0) + 'px'
            });
        }

        
        if (Ext.isIE && me.triggerEl) {
            me.triggerEl.setHeight(headerHeight);
        }
    },

    onDestroy: function() {
        var me = this;
        Ext.destroy(me.keyNav);
        delete me.keyNav;
        me.callParent(arguments);
    },

    onTitleMouseOver: function() {
        this.titleContainer.addCls(this.hoverCls);
    },

    onTitleMouseOut: function() {
        this.titleContainer.removeCls(this.hoverCls);
    },

    onDownKey: function(e) {
        if (this.triggerEl) {
            this.onElClick(e, this.triggerEl.dom || this.el.dom);
        }
    },

    onEnterKey: function(e) {
        this.onElClick(e, this.el.dom);
    },

    
    onElDblClick: function(e, t) {
        var me = this,
            ownerCt = me.ownerCt;
        if (ownerCt && Ext.Array.indexOf(ownerCt.items, me) !== 0 && me.isOnLeftEdge(e) ) {
            ownerCt.expandToFit(me.previousSibling('gridcolumn'));
        }
    },

    onElClick: function(e, t) {

        
        var me = this,
            ownerHeaderCt = me.getOwnerHeaderCt();

        if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
            
            
            if (me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl))) {
                ownerHeaderCt.onHeaderTriggerClick(me, e, t);
            
            } else if (e.getKey() || (!me.isOnLeftEdge(e) && !me.isOnRightEdge(e))) {
                me.toggleSortState();
                ownerHeaderCt.onHeaderClick(me, e, t);
            }
        }
    },

    
    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
        return this.fireEvent.apply(this, arguments);
    },

    toggleSortState: function() {
        var me = this,
            idx,
            nextIdx;
            
        if (me.sortable) {
            idx = Ext.Array.indexOf(me.possibleSortStates, me.sortState);

            nextIdx = (idx + 1) % me.possibleSortStates.length;
            me.setSortState(me.possibleSortStates[nextIdx]);
        }
    },

    doSort: function(state) {
        var ds = this.up('tablepanel').store;
        ds.sort({
            property: this.getSortParam(),
            direction: state
        });
    },

    
    getSortParam: function() {
        return this.dataIndex;
    },

    
    
    setSortState: function(state, skipClear, initial) {
        var me = this,
            colSortClsPrefix = Ext.baseCSSPrefix + 'column-header-sort-',
            ascCls = colSortClsPrefix + 'ASC',
            descCls = colSortClsPrefix + 'DESC',
            nullCls = colSortClsPrefix + 'null',
            ownerHeaderCt = me.getOwnerHeaderCt(),
            oldSortState = me.sortState;

        if (oldSortState !== state && me.getSortParam()) {
            me.addCls(colSortClsPrefix + state);
            
            if (state && !initial) {
                me.doSort(state);
            }
            switch (state) {
                case 'DESC':
                    me.removeCls([ascCls, nullCls]);
                    break;
                case 'ASC':
                    me.removeCls([descCls, nullCls]);
                    break;
                case null:
                    me.removeCls([ascCls, descCls]);
                    break;
            }
            if (ownerHeaderCt && !me.triStateSort && !skipClear) {
                ownerHeaderCt.clearOtherSortStates(me);
            }
            me.sortState = state;
            ownerHeaderCt.fireEvent('sortchange', ownerHeaderCt, me, state);
        }
    },

    hide: function() {
        var me = this,
            items,
            len, i,
            lb,
            newWidth = 0,
            ownerHeaderCt = me.getOwnerHeaderCt();

        
        me.oldWidth = me.getWidth();

        
        if (me.isGroupHeader) {
            items = me.items.items;
            me.callParent(arguments);
            ownerHeaderCt.onHeaderHide(me);
            for (i = 0, len = items.length; i < len; i++) {
                items[i].hidden = true;
                ownerHeaderCt.onHeaderHide(items[i], true);
            }
            return;
        }

        
        lb = me.ownerCt.componentLayout.layoutBusy;
        me.ownerCt.componentLayout.layoutBusy = true;
        me.callParent(arguments);
        me.ownerCt.componentLayout.layoutBusy = lb;

        
        ownerHeaderCt.onHeaderHide(me);

        if (me.ownerCt.isGroupHeader) {
            
            items = me.ownerCt.query('>:not([hidden])');
            if (!items.length) {
                me.ownerCt.hide();
            }
            
            else {
                for (i = 0, len = items.length; i < len; i++) {
                    newWidth += items[i].getWidth();
                }
                me.ownerCt.minWidth = newWidth;
                me.ownerCt.setWidth(newWidth);
            }
        }
    },

    show: function() {
        var me = this,
            ownerCt = me.getOwnerHeaderCt(),
            lb,
            items,
            len, i,
            newWidth = 0;

        
        lb = me.ownerCt.componentLayout.layoutBusy;
        me.ownerCt.componentLayout.layoutBusy = true;
        me.callParent(arguments);
        me.ownerCt.componentLayout.layoutBusy = lb;

        
        if (me.isSubHeader) {
            if (!me.ownerCt.isVisible()) {
                me.ownerCt.show();
            }
        }

        
        if (me.isGroupHeader && !me.query(':not([hidden])').length) {
            items = me.query('>*');
            for (i = 0, len = items.length; i < len; i++) {
                items[i].show();
            }
        }

        
        if (me.ownerCt.isGroupHeader) {
            items = me.ownerCt.query('>:not([hidden])');
            for (i = 0, len = items.length; i < len; i++) {
                newWidth += items[i].getWidth();
            }
            me.ownerCt.minWidth = newWidth;
            me.ownerCt.setWidth(newWidth);
        }

        
        if (ownerCt) {
            ownerCt.onHeaderShow(me);
        }
    },

    getDesiredWidth: function() {
        var me = this;
        if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) {
            
            
            
            
            
            
            return me.componentLayout.lastComponentSize.width;
        
        
        
        }
        else if (me.flex) {
            
            return me.width;
        }
        else {
            return me.width;
        }
    },

    getCellSelector: function() {
        return '.' + Ext.baseCSSPrefix + 'grid-cell-' + this.getItemId();
    },

    getCellInnerSelector: function() {
        return this.getCellSelector() + ' .' + Ext.baseCSSPrefix + 'grid-cell-inner';
    },

    isOnLeftEdge: function(e) {
        return (e.getXY()[0] - this.el.getLeft() <= this.handleWidth);
    },

    isOnRightEdge: function(e) {
        return (this.el.getRight() - e.getXY()[0] <= this.handleWidth);
    }
    
    
    
    
    
    
    
});

Ext.define('Ext.grid.RowNumberer', {
    extend: 'Ext.grid.column.Column',
    alias: 'widget.rownumberer',
    
    text: "&#160",

    
    width: 23,

    
    sortable: false,

    align: 'right',

    constructor : function(config){
        this.callParent(arguments);
        if (this.rowspan) {
            this.renderer = Ext.Function.bind(this.renderer, this);
        }
    },

    
    fixed: true,
    hideable: false,
    menuDisabled: true,
    dataIndex: '',
    cls: Ext.baseCSSPrefix + 'row-numberer',
    rowspan: undefined,

    
    renderer: function(value, metaData, record, rowIdx, colIdx, store) {
        if (this.rowspan){
            metaData.cellAttr = 'rowspan="'+this.rowspan+'"';
        }

        metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
        return store.indexOfTotal(record) + 1;
    }
});


Ext.define('Ext.view.DropZone', {
    extend: 'Ext.dd.DropZone',

    indicatorHtml: '<div class="x-grid-drop-indicator-left"></div><div class="x-grid-drop-indicator-right"></div>',
    indicatorCls: 'x-grid-drop-indicator',

    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);

        
        
        
        
        
        if (!me.ddGroup) {
            me.ddGroup = 'view-dd-zone-' + me.view.id;
        }

        
        
        
        me.callParent([me.view.el]);
    },



    fireViewEvent: function() {
        this.lock();
        var result = this.view.fireEvent.apply(this.view, arguments);
        this.unlock();
        return result;
    },

    getTargetFromEvent : function(e) {
        var node = e.getTarget(this.view.getItemSelector()),
            mouseY, nodeList, testNode, i, len, box;



        if (!node) {
            mouseY = e.getPageY();
            for (i = 0, nodeList = this.view.getNodes(), len = nodeList.length; i < len; i++) {
                testNode = nodeList[i];
                box = Ext.fly(testNode).getBox();
                if (mouseY <= box.bottom) {
                    return testNode;
                }
            }
        }
        return node;
    },

    getIndicator: function() {
        var me = this;

        if (!me.indicator) {
            me.indicator = Ext.createWidget('component', {
                html: me.indicatorHtml,
                cls: me.indicatorCls,
                ownerCt: me.view,
                floating: true,
                shadow: false
            });
        }
        return me.indicator;
    },

    getPosition: function(e, node) {
        var y      = e.getXY()[1],
            region = Ext.fly(node).getRegion(),
            pos;

        if ((region.bottom - y) >= (region.bottom - region.top) / 2) {
            pos = "before";
        } else {
            pos = "after";
        }
        return pos;
    },

    
    containsRecordAtOffset: function(records, record, offset) {
        if (!record) {
            return false;
        }
        var view = this.view,
            recordIndex = view.indexOf(record),
            nodeBefore = view.getNode(recordIndex + offset),
            recordBefore = nodeBefore ? view.getRecord(nodeBefore) : null;

        return recordBefore && Ext.Array.contains(records, recordBefore);
    },

    positionIndicator: function(node, data, e) {
        var me = this,
            view = me.view,
            pos = me.getPosition(e, node),
            overRecord = view.getRecord(node),
            draggingRecords = data.records,
            indicator, indicatorY;

        if (!Ext.Array.contains(draggingRecords, overRecord) && (
            pos == 'before' && !me.containsRecordAtOffset(draggingRecords, overRecord, -1) ||
            pos == 'after' && !me.containsRecordAtOffset(draggingRecords, overRecord, 1)
        )) {
            me.valid = true;

            if (me.overRecord != overRecord || me.currentPosition != pos) {

                indicatorY = Ext.fly(node).getY() - view.el.getY() - 1;
                if (pos == 'after') {
                    indicatorY += Ext.fly(node).getHeight();
                }
                me.getIndicator().setWidth(Ext.fly(view.el).getWidth()).showAt(0, indicatorY);

                
                me.overRecord = overRecord;
                me.currentPosition = pos;
            }
        } else {
            me.invalidateDrop();
        }
    },

    invalidateDrop: function() {
        if (this.valid) {
            this.valid = false;
            this.getIndicator().hide();
        }
    },

    
    onNodeOver: function(node, dragZone, e, data) {
        if (!Ext.Array.contains(data.records, this.view.getRecord(node))) {
            this.positionIndicator(node, data, e);
        }
        return this.valid ? this.dropAllowed : this.dropNotAllowed;
    },

    
    
    notifyOut: function(node, dragZone, e, data) {
        this.callParent(arguments);
        delete this.overRecord;
        delete this.currentPosition;
        if (this.indicator) {
            this.indicator.hide();
        }
    },

    
    onContainerOver : function(dd, e, data) {
        var v = this.view,
            c = v.store.getCount();

        
        if (c) {
            this.positionIndicator(v.getNode(c - 1), data, e);
        }

        
        else {
            delete this.overRecord;
            delete this.currentPosition;
            this.getIndicator().setWidth(Ext.fly(v.el).getWidth()).showAt(0, 0);
            this.valid = true;
        }
        return this.dropAllowed;
    },

    onContainerDrop : function(dd, e, data) {
        return this.onNodeDrop(dd, null, e, data);
    },

    onNodeDrop: function(node, dragZone, e, data) {
        var me = this,
            dropped = false,

            
            
            
            
            processDrop = function () {
                me.invalidateDrop();
                me.handleNodeDrop(data, me.overRecord, me.currentPosition);
                dropped = true;
                me.fireViewEvent('drop', node, data, me.overRecord, me.currentPosition);
            },
            performOperation;

        if (me.valid) {
            performOperation = me.fireViewEvent('beforedrop', node, data, me.overRecord, me.currentPosition, processDrop);
            if (performOperation === 0) {
                return;
            } else if (performOperation !== false) {
                
                if (!dropped) {
                    processDrop();
                }
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
});

Ext.define('Ext.grid.ViewDropZone', {
    extend: 'Ext.view.DropZone',

    indicatorHtml: '<div class="x-grid-drop-indicator-left"></div><div class="x-grid-drop-indicator-right"></div>',
    indicatorCls: 'x-grid-drop-indicator',

    handleNodeDrop : function(data, record, position) {
        var view = this.view,
            store = view.getStore(),
            index, records, i, len;

        
        if (data.copy) {
            records = data.records;
            data.records = [];
            for (i = 0, len = records.length; i < len; i++) {
                data.records.push(records[i].copy(records[i].getId()));
            }
        } else {
            
            data.view.store.remove(data.records, data.view === view);
        }

        index = store.indexOf(record);
        if (position == 'after') {
            index++;
        }
        store.insert(index, data.records);
        view.getSelectionModel().select(data.records);
    }
});

Ext.define('Ext.grid.column.Action', {
    extend: 'Ext.grid.column.Column',
    alias: ['widget.actioncolumn'],
    alternateClassName: 'Ext.grid.ActionColumn',

    
    
    
    
    
    
    
    
    header: '&#160;',

    actionIdRe: /x-action-col-(\d+)/,

    
    altText: '',
    
    sortable: false,

    constructor: function(config) {
        var me = this,
            cfg = Ext.apply({}, config),
            items = cfg.items || [me],
            l = items.length,
            i,
            item;

        
        delete cfg.items;
        me.callParent([cfg]);

        
        me.items = items;



        me.renderer = function(v, meta) {

            v = Ext.isFunction(cfg.renderer) ? cfg.renderer.apply(this, arguments)||'' : '';

            meta.tdCls += ' ' + Ext.baseCSSPrefix + 'action-col-cell';
            for (i = 0; i < l; i++) {
                item = items[i];
                v += '<img alt="' + me.altText + '" src="' + (item.icon || Ext.BLANK_IMAGE_URL) +
                    '" class="' + Ext.baseCSSPrefix + 'action-col-icon ' + Ext.baseCSSPrefix + 'action-col-' + String(i) + ' ' +  (item.iconCls || '') + 
                    ' ' + (Ext.isFunction(item.getClass) ? item.getClass.apply(item.scope||me.scope||me, arguments) : (me.iconCls || '')) + '"' +
                    ((item.tooltip) ? ' data-qtip="' + item.tooltip + '"' : '') + ' />';
            }
            return v;
        };
    },

    destroy: function() {
        delete this.items;
        delete this.renderer;
        return this.callParent(arguments);
    },

    
    processEvent : function(type, view, cell, recordIndex, cellIndex, e){
        var me = this,
            match = e.getTarget().className.match(me.actionIdRe),
            item, fn;
        if (match) {
            item = me.items[parseInt(match[1], 10)];
            if (item) {
                if (type == 'click') {
                    fn = item.handler || me.handler;
                    if (fn) {
                        fn.call(item.scope || me.scope || me, view, recordIndex, cellIndex, item, e);
                    }
                } else if (type == 'mousedown' && item.stopSelection !== false) {
                    return false;
                }
            }
        }
        return me.callParent(arguments);
    },

    cascade: function(fn, scope) {
        fn.call(scope||this, this);
    },

    
    getRefItems: function() {
        return [];
    }
});

Ext.define('Ext.grid.column.Boolean', {
    extend: 'Ext.grid.column.Column',
    alias: ['widget.booleancolumn'],
    alternateClassName: 'Ext.grid.BooleanColumn',

    
    trueText: 'true',

    
    falseText: 'false',

    
    undefinedText: '&#160;',

    constructor: function(cfg){
        this.callParent(arguments);
        var trueText      = this.trueText,
            falseText     = this.falseText,
            undefinedText = this.undefinedText;

        this.renderer = function(value){
            if(value === undefined){
                return undefinedText;
            }
            if(!value || value === 'false'){
                return falseText;
            }
            return trueText;
        };
    }
});

Ext.define('Ext.grid.column.Date', {
    extend: 'Ext.grid.column.Column',
    alias: ['widget.datecolumn'],
    requires: ['Ext.Date'],
    alternateClassName: 'Ext.grid.DateColumn',

    
    format : Ext.Date.defaultFormat,

    constructor: function(cfg){
        this.callParent(arguments);
        this.renderer = Ext.util.Format.dateRenderer(this.format);
    }
});

Ext.define('Ext.grid.column.Number', {
    extend: 'Ext.grid.column.Column',
    alias: ['widget.numbercolumn'],
    requires: ['Ext.util.Format'],
    alternateClassName: 'Ext.grid.NumberColumn',

    
    format : '0,000.00',
    constructor: function(cfg) {
        this.callParent(arguments);
        this.renderer = Ext.util.Format.numberRenderer(this.format);
    }
});

Ext.define('Ext.grid.column.Template', {
    extend: 'Ext.grid.column.Column',
    alias: ['widget.templatecolumn'],
    requires: ['Ext.XTemplate'],
    alternateClassName: 'Ext.grid.TemplateColumn',

    
    constructor: function(cfg){
        var me = this,
            tpl;
            
        me.callParent(arguments);
        tpl = me.tpl = (!Ext.isPrimitive(me.tpl) && me.tpl.compile) ? me.tpl : Ext.create('Ext.XTemplate', me.tpl);

        me.renderer = function(value, p, record) {
            var data = Ext.apply({}, record.data, record.getAssociatedData());
            return tpl.apply(data);
        };
    }
});


Ext.define('Ext.grid.feature.Feature', {
    extend: 'Ext.util.Observable',
    alias: 'feature.feature',
    
    isFeature: true,
    disabled: false,
    
    
    hasFeatureEvent: true,
    
    
    eventPrefix: null,
    
    
    eventSelector: null,
    
    
    view: null,
    
    
    grid: null,
    
    
    collectData: false,
        
    getFeatureTpl: function() {
        return '';
    },
    
    
    getFireEventArgs: function(eventName, view, featureTarget, e) {
        return [eventName, view, featureTarget, e];
    },
    
    
    attachEvents: function() {
        
    },
    
    getFragmentTpl: function() {
        return;
    },
    
    
    mutateMetaRowTpl: function(metaRowTplArray) {
        
    },
    
    
    getMetaRowTplFragments: function() {
        return {};
    },

    getTableFragments: function() {
        return {};
    },
    
    
    getAdditionalData: function(data, idx, record, orig) {
        return {};
    },
    
    
    enable: function() {
        this.disabled = false;
    },
    
    
    disable: function() {
        this.disabled = true;
    }
    
});

Ext.define('Ext.grid.feature.AbstractSummary', {
    
    
   
    extend: 'Ext.grid.feature.Feature',
    
    alias: 'feature.abstractsummary',
   
    
   
   
    showSummaryRow: true,
    
    
    nestedIdRe: /\{\{id\}([\w\-]*)\}/g,
    
    
    toggleSummaryRow: function(visible){
        this.showSummaryRow = !!visible;
    },
    
    
    getSummaryFragments: function(){
        var fragments = {};
        if (this.showSummaryRow) {
            Ext.apply(fragments, {
                printSummaryRow: Ext.bind(this.printSummaryRow, this)
            });
        }
        return fragments;
    },
    
    
    printSummaryRow: function(index){
        var inner = this.view.getTableChunker().metaRowTpl.join('');
        
        inner = inner.replace('x-grid-row', 'x-grid-row-summary');
        inner = inner.replace('{{id}}', '{gridSummaryValue}');
        inner = inner.replace(this.nestedIdRe, '{id$1}');  
        inner = inner.replace('{[this.embedRowCls()]}', '{rowCls}');
        inner = inner.replace('{[this.embedRowAttr()]}', '{rowAttr}');
        inner = Ext.create('Ext.XTemplate', inner, {
            firstOrLastCls: Ext.view.TableChunker.firstOrLastCls
        });
        
        return inner.applyTemplate({
            columns: this.getPrintData(index)
        });
    },
    
    
    getColumnValue: function(column, summaryData){
        var comp     = Ext.getCmp(column.id),
            value    = summaryData[column.dataIndex],
            renderer = comp.summaryRenderer;

        if (renderer) {
            value = renderer.call(
                comp.scope || this,
                value,
                summaryData,
                column.dataIndex
            );
        }
        return value;
    },
    
    
    getSummary: function(store, type, field, group){
        if (type) {
            if (Ext.isFunction(type)) {
                return store.aggregate(type, null, group);
            }
            
            switch (type) {
                case 'count':
                    return store.count(group);
                case 'min':
                    return store.min(field, group);
                case 'max':
                    return store.max(field, group);
                case 'sum':
                    return store.sum(field, group);
                case 'average':
                    return store.average(field, group);
                default:
                    return group ? {} : '';
                    
            }
        }
    }
    
});


Ext.define('Ext.grid.feature.Chunking', {
    extend: 'Ext.grid.feature.Feature',
    alias: 'feature.chunking',
    
    chunkSize: 20,
    rowHeight: Ext.isIE ? 27 : 26,
    visibleChunk: 0,
    hasFeatureEvent: false,
    attachEvents: function() {
        var grid = this.view.up('gridpanel'),
            scroller = grid.down('gridscroller[dock=right]');
        scroller.el.on('scroll', this.onBodyScroll, this, {buffer: 300});
        
    },
    
    onBodyScroll: function(e, t) {
        var view = this.view,
            top  = t.scrollTop,
            nextChunk = Math.floor(top / this.rowHeight / this.chunkSize);
        if (nextChunk !== this.visibleChunk) {
        
            this.visibleChunk = nextChunk;
            view.refresh();
            view.el.dom.scrollTop = top;
            
            view.el.dom.scrollTop = top;
        }
    },
    
    collectData: function(records, preppedRecords, startIndex, fullWidth, orig) {
        var o = {
            fullWidth: orig.fullWidth,
            chunks: []
        },
        
        
        recordCount = orig.rows.length,
        start = 0,
        i = 0,
        visibleChunk = this.visibleChunk,
        chunk,
        rows,
        chunkLength;

        for (; start < recordCount; start+=this.chunkSize, i++) {
            if (start+this.chunkSize > recordCount) {
                chunkLength = recordCount - start;
            } else {
                chunkLength = this.chunkSize;
            }
            
            if (i >= visibleChunk - 1 && i <= visibleChunk + 1) {
                rows = orig.rows.slice(start, start+this.chunkSize);
            } else {
                rows = [];
            }
            o.chunks.push({
                rows: rows,
                fullWidth: fullWidth,
                chunkHeight: chunkLength * this.rowHeight
            });
        }
        
        
        return o;
    },
    
    getTableFragments: function() {
        return {
            openTableWrap: function() {
                return '<tpl for="chunks"><div class="' + Ext.baseCSSPrefix + 'grid-chunk" style="height: {chunkHeight}px;">';
            },
            closeTableWrap: function() {
                return '</div></tpl>';
            }
        };
    }
});


Ext.define('Ext.grid.feature.Grouping', {
    extend: 'Ext.grid.feature.Feature',
    alias: 'feature.grouping',

    eventPrefix: 'group',
    eventSelector: '.' + Ext.baseCSSPrefix + 'grid-group-hd',

    constructor: function() {
        this.collapsedState = {};
        this.callParent(arguments);
    },
    
    

    

    

    

    

    
    groupHeaderTpl: 'Group: {name}',

    
    depthToIndent: 17,

    collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',
    hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',

    
    groupByText : 'Group By This Field',
    
    showGroupsText : 'Show in Groups',

    
    hideGroupedHeader : false,

    
    startCollapsed : false,

    
    enableGroupingMenu : true,

    
    enableNoGroups : true,
    
    enable: function() {
        var me    = this,
            view  = me.view,
            store = view.store,
            groupToggleMenuItem;
            
        if (me.lastGroupIndex) {
            store.group(me.lastGroupIndex);
        }
        me.callParent();
        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
        groupToggleMenuItem.setChecked(true, true);
        view.refresh();
    },

    disable: function() {
        var me    = this,
            view  = me.view,
            store = view.store,
            groupToggleMenuItem,
            lastGroup;
            
        lastGroup = store.groupers.first();
        if (lastGroup) {
            me.lastGroupIndex = lastGroup.property;
            store.groupers.clear();
        }
        
        me.callParent();
        groupToggleMenuItem = me.view.headerCt.getMenu().down('#groupToggleMenuItem');
        groupToggleMenuItem.setChecked(true, true);
        groupToggleMenuItem.setChecked(false, true);
        view.refresh();
    },

    getFeatureTpl: function(values, parent, x, xcount) {
        var me = this;
        
        return [
            '<tpl if="typeof rows !== \'undefined\'">',
                
                '<tr class="' + Ext.baseCSSPrefix + 'grid-group-hd ' + (me.startCollapsed ? me.hdCollapsedCls : '') + ' {hdCollapsedCls}"><td class="' + Ext.baseCSSPrefix + 'grid-cell" colspan="' + parent.columns.length + '" {[this.indentByDepth(values)]}><div class="' + Ext.baseCSSPrefix + 'grid-cell-inner"><div class="' + Ext.baseCSSPrefix + 'grid-group-title">{collapsed}' + me.groupHeaderTpl + '</div></div></td></tr>',
                
                '<tr id="{viewId}-gp-{name}" class="' + Ext.baseCSSPrefix + 'grid-group-body ' + (me.startCollapsed ? me.collapsedCls : '') + ' {collapsedCls}"><td colspan="' + parent.columns.length + '">{[this.recurse(values)]}</td></tr>',
            '</tpl>'
        ].join('');
    },

    getFragmentTpl: function() {
        return {
            indentByDepth: this.indentByDepth,
            depthToIndent: this.depthToIndent
        };
    },

    indentByDepth: function(values) {
        var depth = values.depth || 0;
        return 'style="padding-left:'+ depth * this.depthToIndent + 'px;"';
    },

    
    
    destroy: function() {
        var me = this;
        
        delete me.view;
        delete me.prunedHeader;
    },

    
    attachEvents: function() {
        var me = this,
            view = me.view,
            header, headerId, menu, menuItem;

        view.on({
            scope: me,
            groupclick: me.onGroupClick,
            rowfocus: me.onRowFocus
        });
        view.store.on('groupchange', me.onGroupChange, me);

        me.pruneGroupedHeader();

        if (me.enableGroupingMenu) {
            me.injectGroupingMenu();
        }

        if (me.hideGroupedHeader) {
            header = view.headerCt.down('gridcolumn[dataIndex=' + me.getGroupField() + ']');
            headerId = header.id;
            menu = view.headerCt.getMenu();
            menuItem = menu.down('menuitem[headerId='+ headerId +']');
            if (menuItem) {
                menuItem.setChecked(false);
            }
        }
    },
    
    injectGroupingMenu: function() {
        var me       = this,
            view     = me.view,
            headerCt = view.headerCt;
        headerCt.showMenuBy = me.showMenuBy;
        headerCt.getMenuItems = me.getMenuItems();
    },
    
    showMenuBy: function(t, header) {
        var menu = this.getMenu(),
            groupMenuItem  = menu.down('#groupMenuItem'),
            groupableMth = header.groupable === false ?  'disable' : 'enable';
            
        groupMenuItem[groupableMth]();
        Ext.grid.header.Container.prototype.showMenuBy.apply(this, arguments);
    },
    
    getMenuItems: function() {
        var me                 = this,
            groupByText        = me.groupByText,
            disabled           = me.disabled,
            showGroupsText     = me.showGroupsText,
            enableNoGroups     = me.enableNoGroups,
            groupMenuItemClick = Ext.Function.bind(me.onGroupMenuItemClick, me),
            groupToggleMenuItemClick = Ext.Function.bind(me.onGroupToggleMenuItemClick, me);
        
        
        return function() {
            var o = Ext.grid.header.Container.prototype.getMenuItems.call(this);
            o.push('-', {
                itemId: 'groupMenuItem',
                text: groupByText,
                handler: groupMenuItemClick
            });
            if (enableNoGroups) {
                o.push({
                    itemId: 'groupToggleMenuItem',
                    text: showGroupsText,
                    checked: !disabled,
                    checkHandler: groupToggleMenuItemClick
                });
            }
            return o;
        };
    },


    
    onGroupMenuItemClick: function(menuItem, e) {
        var menu = menuItem.parentMenu,
            hdr  = menu.activeHeader,
            view = this.view;

        delete this.lastGroupIndex;
        this.enable();
        view.store.group(hdr.dataIndex);
        this.pruneGroupedHeader();
        
    },

    
    onGroupToggleMenuItemClick: function(menuItem, checked) {
        this[checked ? 'enable' : 'disable']();
    },

    
    pruneGroupedHeader: function() {
        var me         = this,
            view       = me.view,
            store      = view.store,
            groupField = me.getGroupField(),
            headerCt   = view.headerCt,
            header     = headerCt.down('header[dataIndex=' + groupField + ']');

        if (header) {
            if (me.prunedHeader) {
                me.prunedHeader.show();
            }
            me.prunedHeader = header;
            header.hide();
        }
    },

    getGroupField: function(){
        var group = this.view.store.groupers.first();
        if (group) {
            return group.property;    
        }
        return ''; 
    },

    
    onRowFocus: function(rowIdx) {
        var node    = this.view.getNode(rowIdx),
            groupBd = Ext.fly(node).up('.' + this.collapsedCls);

        if (groupBd) {
            
            
            this.expand(groupBd);
        }
    },

    
    expand: function(groupBd) {
        var me = this,
            view = me.view,
            grid = view.up('gridpanel'),
            groupBdDom = Ext.getDom(groupBd);
            
        me.collapsedState[groupBdDom.id] = false;

        groupBd.removeCls(me.collapsedCls);
        groupBd.prev().removeCls(me.hdCollapsedCls);

        grid.determineScrollbars();
        grid.invalidateScroller();
        view.fireEvent('groupexpand');
    },

    
    collapse: function(groupBd) {
        var me = this,
            view = me.view,
            grid = view.up('gridpanel'),
            groupBdDom = Ext.getDom(groupBd);
            
        me.collapsedState[groupBdDom.id] = true;

        groupBd.addCls(me.collapsedCls);
        groupBd.prev().addCls(me.hdCollapsedCls);

        grid.determineScrollbars();
        grid.invalidateScroller();
        view.fireEvent('groupcollapse');
    },
    
    onGroupChange: function(){
        this.view.refresh();
    },

    
    onGroupClick: function(view, group, idx, foo, e) {
        var me = this,
            toggleCls = me.toggleCls,
            groupBd = Ext.fly(group.nextSibling, '_grouping');

        if (groupBd.hasCls(me.collapsedCls)) {
            me.expand(groupBd);
        } else {
            me.collapse(groupBd);
        }
    },

    
    getMetaRowTplFragments: function() {
        return {
            isRow: this.isRow,
            closeRow: this.closeRow
        };
    },

    
    
    isRow: function() {
        return '<tpl if="typeof rows === \'undefined\'">';
    },

    
    
    closeRow: function() {
        return '</tpl>';
    },

    
    mutateMetaRowTpl: function(metaRowTpl) {
        metaRowTpl.unshift('{[this.isRow()]}');
        metaRowTpl.push('{[this.closeRow()]}');
    },

    
    
    getAdditionalData: function(data, idx, record, orig) {
        var view = this.view,
            hCt  = view.headerCt,
            col  = hCt.items.getAt(0),
            o = {},
            tdAttrKey = col.id + '-tdAttr';

        
        o[tdAttrKey] = this.indentByDepth(data) + " " + (orig[tdAttrKey] ? orig[tdAttrKey] : '');
        o.collapsed = 'true';
        return o;
    },

    
    getGroupRows: function(group, records, preppedRecords, fullWidth) {
        var me = this,
            children = group.children,
            rows = group.rows = [],
            view = me.view;
        group.viewId = view.id;

        Ext.Array.each(records, function(record, idx) {
            if (Ext.Array.indexOf(children, record) != -1) {
                rows.push(Ext.apply(preppedRecords[idx], {
                    depth: 1
                }));
            }
        });
        delete group.children;
        group.fullWidth = fullWidth;
        if (me.collapsedState[view.id + '-gp-' + group.name]) {
            group.collapsedCls = me.collapsedCls;
            group.hdCollapsedCls = me.hdCollapsedCls;
        }

        return group;
    },

    
    collectData: function(records, preppedRecords, startIndex, fullWidth, o) {
        var me    = this,
            store = me.view.store,
            groups;
            
        if (!me.disabled && store.isGrouped()) {
            groups = store.getGroups();
            Ext.Array.each(groups, function(group, idx){
                me.getGroupRows(group, records, preppedRecords, fullWidth);
            }, me);
            return {
                rows: groups,
                fullWidth: fullWidth
            };
        }
        return o;
    },
    
    
    
    
    
    getFireEventArgs: function(type, view, featureTarget, e) {
        var returnArray = [type, view, featureTarget],
            groupBd     = Ext.fly(featureTarget.nextSibling, '_grouping'),
            groupBdId   = Ext.getDom(groupBd).id,
            prefix      = view.id + '-gp-',
            groupName   = groupBdId.substr(prefix.length);
        
        returnArray.push(groupName, e);
        
        return returnArray;
    }
});


Ext.define('Ext.grid.feature.GroupingSummary', {
    
    
    
    extend: 'Ext.grid.feature.Grouping',
    
    alias: 'feature.groupingsummary',
    
    mixins: {
        summary: 'Ext.grid.feature.AbstractSummary'
    },
    
    

     
   
   getFeatureTpl: function() {
        var tpl = this.callParent(arguments);
            
        if (this.showSummaryRow) {
            
            tpl = tpl.replace('</tpl>', '');
            tpl += '{[this.printSummaryRow(xindex)]}</tpl>';
        }
        return tpl;
    },
    
    
    getFragmentTpl: function() {
        var me = this,
            fragments = me.callParent();
            
        Ext.apply(fragments, me.getSummaryFragments());
        if (me.showSummaryRow) {
            
            me.summaryGroups = me.view.store.getGroups();
            me.summaryData = me.generateSummaryData();
        }
        return fragments;
    },
    
    
    getPrintData: function(index){
        var me = this,
            columns = me.view.headerCt.getColumnsForTpl(),
            i = 0,
            length = columns.length,
            data = [],
            name = me.summaryGroups[index - 1].name,
            active = me.summaryData[name],
            column;
            
        for (; i < length; ++i) {
            column = columns[i];
            column.gridSummaryValue = this.getColumnValue(column, active);
            data.push(column);
        }
        return data;
    },
    
    
    generateSummaryData: function(){
        var me = this,
            data = {},
            remoteData = {},
            store = me.view.store,
            groupField = this.getGroupField(),
            reader = store.proxy.reader,
            groups = me.summaryGroups,
            columns = me.view.headerCt.getColumnsForTpl(),
            i,
            length,
            fieldData,
            root,
            key,
            comp;
            
        for (i = 0, length = groups.length; i < length; ++i) {
            data[groups[i].name] = {};
        }
        
    
        if (me.remoteRoot && reader.rawData) {
            
            root = reader.root;
            reader.root = me.remoteRoot;
            reader.buildExtractors(true);
            Ext.Array.each(reader.getRoot(reader.rawData), function(value) {
                 data[value[groupField]] = value;
                 data[value[groupField]]._remote = true;
            });
            
            reader.root = root;
            reader.buildExtractors(true);
        }
        
        for (i = 0, length = columns.length; i < length; ++i) {
            comp = Ext.getCmp(columns[i].id);
            fieldData = me.getSummary(store, comp.summaryType, comp.dataIndex, true);
            
            for (key in fieldData) {
                if (fieldData.hasOwnProperty(key)) {
                    if (!data[key]._remote) {
                        data[key][comp.dataIndex] = fieldData[key];
                    }
                }
            }
        }
        return data;
    }
});


Ext.define('Ext.grid.feature.RowBody', {
    extend: 'Ext.grid.feature.Feature',
    alias: 'feature.rowbody',
    rowBodyHiddenCls: Ext.baseCSSPrefix + 'grid-row-body-hidden',
    rowBodyTrCls: Ext.baseCSSPrefix + 'grid-rowbody-tr',
    rowBodyTdCls: Ext.baseCSSPrefix + 'grid-cell-rowbody',
    rowBodyDivCls: Ext.baseCSSPrefix + 'grid-rowbody',

    eventPrefix: 'rowbody',
    eventSelector: '.' + Ext.baseCSSPrefix + 'grid-rowbody-tr',
    
    getRowBody: function(values) {
        return [
            '<tr class="' + this.rowBodyTrCls + ' {rowBodyCls}">',
                '<td class="' + this.rowBodyTdCls + '" colspan="{rowBodyColspan}">',
                    '<div class="' + this.rowBodyDivCls + '">{rowBody}</div>',
                '</td>',
            '</tr>'
        ].join('');
    },
    
    
    getMetaRowTplFragments: function() {
        return {
            getRowBody: this.getRowBody,
            rowBodyTrCls: this.rowBodyTrCls,
            rowBodyTdCls: this.rowBodyTdCls,
            rowBodyDivCls: this.rowBodyDivCls
        };
    },

    mutateMetaRowTpl: function(metaRowTpl) {
        metaRowTpl.push('{[this.getRowBody(values)]}');
    },

    
    getAdditionalData: function(data, idx, record, orig) {
        var headerCt = this.view.headerCt,
            colspan  = headerCt.getColumnCount();

        return {
            rowBody: "",
            rowBodyCls: this.rowBodyCls,
            rowBodyColspan: colspan
        };
    }
});

Ext.define('Ext.grid.feature.RowWrap', {
    extend: 'Ext.grid.feature.Feature',
    alias: 'feature.rowwrap',

    
    hasFeatureEvent: false,
    
    mutateMetaRowTpl: function(metaRowTpl) {        
        
        
        metaRowTpl[0] = metaRowTpl[0].replace(Ext.baseCSSPrefix + 'grid-row', '');
        metaRowTpl[0] = metaRowTpl[0].replace("{[this.embedRowCls()]}", "");
        
        metaRowTpl.unshift('<table class="' + Ext.baseCSSPrefix + 'grid-table ' + Ext.baseCSSPrefix + 'grid-table-resizer" style="width: {[this.embedFullWidth()]}px;">');
        
        metaRowTpl.unshift('<tr class="' + Ext.baseCSSPrefix + 'grid-row {[this.embedRowCls()]}"><td colspan="{[this.embedColSpan()]}"><div class="' + Ext.baseCSSPrefix + 'grid-rowwrap-div">');
        
        
        metaRowTpl.push('</table>');
        
        metaRowTpl.push('</div></td></tr>');
    },
    
    embedColSpan: function() {
        return '{colspan}';
    },
    
    embedFullWidth: function() {
        return '{fullWidth}';
    },
    
    getAdditionalData: function(data, idx, record, orig) {
        var headerCt = this.view.headerCt,
            colspan  = headerCt.getColumnCount(),
            fullWidth = headerCt.getFullWidth(),
            items    = headerCt.query('gridcolumn'),
            itemsLn  = items.length,
            i = 0,
            o = {
                colspan: colspan,
                fullWidth: fullWidth
            },
            id,
            tdClsKey,
            colResizerCls;

        for (; i < itemsLn; i++) {
            id = items[i].id;
            tdClsKey = id + '-tdCls';
            colResizerCls = Ext.baseCSSPrefix + 'grid-col-resizer-'+id;
            
            
            
            o[tdClsKey] = colResizerCls + " " + (orig[tdClsKey] ? orig[tdClsKey] : '');
            
            o[id+'-tdAttr'] = " style=\"width: " + (items[i].hidden ? 0 : items[i].getDesiredWidth()) + "px;\" ";
            if (orig[id+'-tdAttr']) {
                o[id+'-tdAttr'] += orig[id+'-tdAttr'];
            }
            
        }

        return o;
    },
    
    getMetaRowTplFragments: function() {
        return {
            embedFullWidth: this.embedFullWidth,
            embedColSpan: this.embedColSpan
        };
    }
    
});

Ext.define('Ext.grid.feature.Summary', {
    
    
    
    extend: 'Ext.grid.feature.AbstractSummary',
    
    alias: 'feature.summary',
    
    
    
    
    getFragmentTpl: function() {
        
        this.summaryData = this.generateSummaryData(); 
        return this.getSummaryFragments();
    },
    
    
    getTableFragments: function(){
        if (this.showSummaryRow) {
            return {
                closeRows: this.closeRows
            };
        }
    },
    
    
    closeRows: function() {
        return '</tpl>{[this.printSummaryRow()]}';
    },
    
    
    getPrintData: function(index){
        var me = this,
            columns = me.view.headerCt.getColumnsForTpl(),
            i = 0,
            length = columns.length,
            data = [],
            active = me.summaryData,
            column;
            
        for (; i < length; ++i) {
            column = columns[i];
            column.gridSummaryValue = this.getColumnValue(column, active);
            data.push(column);
        }
        return data;
    },
    
    
    generateSummaryData: function(){
        var me = this,
            data = {},
            store = me.view.store,
            columns = me.view.headerCt.getColumnsForTpl(),
            i = 0,
            length = columns.length,
            fieldData,
            key,
            comp;
            
        for (i = 0, length = columns.length; i < length; ++i) {
            comp = Ext.getCmp(columns[i].id);
            data[comp.dataIndex] = me.getSummary(store, comp.summaryType, comp.dataIndex, false);
        }
        return data;
    }
});

Ext.define('Ext.grid.header.DragZone', {
    extend: 'Ext.dd.DragZone',
    colHeaderCls: Ext.baseCSSPrefix + 'column-header',
    maxProxyWidth: 120,

    constructor: function(headerCt) {
        this.headerCt = headerCt;
        this.ddGroup =  this.getDDGroup();
        this.callParent([headerCt.el]);
        this.proxy.el.addCls(Ext.baseCSSPrefix + 'grid-col-dd');
    },

    getDDGroup: function() {
        return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
    },

    getDragData: function(e) {
        var header = e.getTarget('.'+this.colHeaderCls),
            headerCmp;

        if (header) {
            headerCmp = Ext.getCmp(header.id);
            if (!this.headerCt.dragging && headerCmp.draggable && !(headerCmp.isOnLeftEdge(e) || headerCmp.isOnRightEdge(e))) {
                var ddel = document.createElement('div');
                ddel.innerHTML = Ext.getCmp(header.id).text;
                return {
                    ddel: ddel,
                    header: headerCmp
                };
            }
        }
        return false;
    },

    onBeforeDrag: function() {
        return !(this.headerCt.dragging || this.disabled);
    },

    onInitDrag: function() {
        this.headerCt.dragging = true;
        this.callParent(arguments);
    },

    onDragDrop: function() {
        this.headerCt.dragging = false;
        this.callParent(arguments);
    },

    afterRepair: function() {
        this.callParent();
        this.headerCt.dragging = false;
    },

    getRepairXY: function() {
        return this.dragData.header.el.getXY();
    },
    
    disable: function() {
        this.disabled = true;
    },
    
    enable: function() {
        this.disabled = false;
    }
});


Ext.define('Ext.grid.header.DropZone', {
    extend: 'Ext.dd.DropZone',
    colHeaderCls: Ext.baseCSSPrefix + 'column-header',
    proxyOffsets: [-4, -9],

    constructor: function(headerCt){
        this.headerCt = headerCt;
        this.ddGroup = this.getDDGroup();
        this.callParent([headerCt.el]);
    },

    getDDGroup: function() {
        return 'header-dd-zone-' + this.headerCt.up('[scrollerOwner]').id;
    },

    getTargetFromEvent : function(e){
        return e.getTarget('.' + this.colHeaderCls);
    },

    getTopIndicator: function() {
        if (!this.topIndicator) {
            this.topIndicator = Ext.core.DomHelper.append(Ext.getBody(), {
                cls: "col-move-top",
                html: "&#160;"
            }, true);
        }
        return this.topIndicator;
    },

    getBottomIndicator: function() {
        if (!this.bottomIndicator) {
            this.bottomIndicator = Ext.core.DomHelper.append(Ext.getBody(), {
                cls: "col-move-bottom",
                html: "&#160;"
            }, true);
        }
        return this.bottomIndicator;
    },

    getLocation: function(e, t) {
        var x      = e.getXY()[0],
            region = Ext.fly(t).getRegion(),
            pos, header;

        if ((region.right - x) <= (region.right - region.left) / 2) {
            pos = "after";
        } else {
            pos = "before";
        }
        return {
            pos: pos,
            header: Ext.getCmp(t.id),
            node: t
        };
    },

    positionIndicator: function(draggedHeader, node, e){
        var location = this.getLocation(e, node),
            header = location.header,
            pos    = location.pos,
            nextHd = draggedHeader.nextSibling('gridcolumn:not([hidden])'),
            prevHd = draggedHeader.previousSibling('gridcolumn:not([hidden])'),
            region, topIndicator, bottomIndicator, topAnchor, bottomAnchor,
            topXY, bottomXY, headerCtEl, minX, maxX;

        
        if (!header.draggable && header.getIndex() == 0) {
            return false;
        }

        this.lastLocation = location;

        if ((draggedHeader !== header) &&
            ((pos === "before" && nextHd !== header) ||
            (pos === "after" && prevHd !== header)) &&
            !header.isDescendantOf(draggedHeader)) {

            
            
            
            var allDropZones = Ext.dd.DragDropManager.getRelated(this),
                ln = allDropZones.length,
                i  = 0,
                dropZone;

            for (; i < ln; i++) {
                dropZone = allDropZones[i];
                if (dropZone !== this && dropZone.invalidateDrop) {
                    dropZone.invalidateDrop();
                }
            }


            this.valid = true;
            topIndicator = this.getTopIndicator();
            bottomIndicator = this.getBottomIndicator();
            if (pos === 'before') {
                topAnchor = 'tl';
                bottomAnchor = 'bl';
            } else {
                topAnchor = 'tr';
                bottomAnchor = 'br';
            }
            topXY = header.el.getAnchorXY(topAnchor);
            bottomXY = header.el.getAnchorXY(bottomAnchor);

            
            headerCtEl = this.headerCt.el;
            minX = headerCtEl.getLeft();
            maxX = headerCtEl.getRight();

            topXY[0] = Ext.Number.constrain(topXY[0], minX, maxX);
            bottomXY[0] = Ext.Number.constrain(bottomXY[0], minX, maxX);

            
            
            topXY[0] -= 4;
            topXY[1] -= 9;
            bottomXY[0] -= 4;

            
            topIndicator.setXY(topXY);
            bottomIndicator.setXY(bottomXY);
            topIndicator.show();
            bottomIndicator.show();
        
        } else {
            this.invalidateDrop();
        }
    },

    invalidateDrop: function() {
        this.valid = false;
        this.hideIndicators();
    },

    onNodeOver: function(node, dragZone, e, data) {
        if (data.header.el.dom !== node) {
            this.positionIndicator(data.header, node, e);
        }
        return this.valid ? this.dropAllowed : this.dropNotAllowed;
    },

    hideIndicators: function() {
        this.getTopIndicator().hide();
        this.getBottomIndicator().hide();
    },

    onNodeOut: function() {
        this.hideIndicators();
    },

    onNodeDrop: function(node, dragZone, e, data) {
        if (this.valid) {
            this.invalidateDrop();
            var hd = data.header,
                lastLocation = this.lastLocation,
                fromCt  = hd.ownerCt,
                fromIdx = fromCt.items.indexOf(hd), 
                toCt    = lastLocation.header.ownerCt,
                toIdx   = toCt.items.indexOf(lastLocation.header),
                headerCt = this.headerCt,
                groupCt,
                scrollerOwner;

            if (lastLocation.pos === 'after') {
                toIdx++;
            }

            
            
            
            if (fromCt !== toCt && fromCt.lockableInjected && toCt.lockableInjected && toCt.lockedCt) {
                scrollerOwner = fromCt.up('[scrollerOwner]');
                scrollerOwner.lock(hd, toIdx);
            } else if (fromCt !== toCt && fromCt.lockableInjected && toCt.lockableInjected && fromCt.lockedCt) {
                scrollerOwner = fromCt.up('[scrollerOwner]');
                scrollerOwner.unlock(hd, toIdx);
            } else {
                
                
                if ((fromCt === toCt) && (toIdx > fromCt.items.indexOf(hd))) {
                    toIdx--;
                }

                
                if (fromCt !== toCt) {
                    fromCt.suspendLayout = true;
                    fromCt.remove(hd, false);
                    fromCt.suspendLayout = false;
                }

                
                if (fromCt.isGroupHeader) {
                    if (!fromCt.items.getCount()) {
                        groupCt = fromCt.ownerCt;
                        groupCt.suspendLayout = true;
                        groupCt.remove(fromCt, false);
                        fromCt.el.dom.parentNode.removeChild(fromCt.el.dom);
                        groupCt.suspendLayout = false;
                    } else {
                        fromCt.minWidth = fromCt.getWidth() - hd.getWidth();
                        fromCt.setWidth(fromCt.minWidth);
                    }
                }

                
                toCt.suspendLayout = true;
                if (fromCt === toCt) {
                    toCt.move(fromIdx, toIdx);
                } else {
                    toCt.insert(toIdx, hd);
                }
                toCt.suspendLayout = false;

                
                
                
                if (toCt.isGroupHeader) {
                    hd.savedFlex = hd.flex;
                    delete hd.flex;
                    hd.width = hd.getWidth();
                    
                    
                    toCt.minWidth = toCt.getWidth() + hd.getWidth() - (hd.savedFlex ? 1 : 0);
                    toCt.setWidth(toCt.minWidth);
                } else {
                    if (hd.savedFlex) {
                        hd.flex = hd.savedFlex;
                        delete hd.width;
                    }
                }


                
                headerCt.purgeCache();
                headerCt.doLayout();
                headerCt.onHeaderMoved(hd, fromIdx, toIdx);
                
                if (!fromCt.items.getCount()) {
                    fromCt.destroy();
                }
            }
        }
    }
});



Ext.define('Ext.grid.plugin.Editing', {
    alias: 'editing.editing',

    requires: [
        'Ext.grid.column.Column',
        'Ext.util.KeyNav'
    ],

    mixins: {
        observable: 'Ext.util.Observable'
    },

    
    clicksToEdit: 2,

    
    defaultFieldXType: 'textfield',

    
    editStyle: '',

    constructor: function(config) {
        var me = this;
        Ext.apply(me, config);

        me.addEvents(
            
            'beforeedit',

            
            'edit',

            
            'validateedit'
        );
        me.mixins.observable.constructor.call(me);
        
        me.relayEvents(me, ['afteredit'], 'after');
    },

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

        me.grid = grid;
        me.view = grid.view;
        me.initEvents();
        me.initFieldAccessors(me.view.getGridColumns());

        grid.relayEvents(me, ['beforeedit', 'edit', 'validateedit']);
        
        
        grid.isEditable = true;
        grid.editingPlugin = grid.view.editingPlugin = me;
    },

    
    destroy: function() {
        var me = this,
            grid = me.grid,
            headerCt = grid.headerCt,
            events = grid.events;

        Ext.destroy(me.keyNav);
        me.removeFieldAccessors(grid.getView().getGridColumns());

        
        me.clearListeners();

        delete me.grid.editingPlugin;
        delete me.grid.view.editingPlugin;
        delete me.grid;
        delete me.view;
        delete me.editor;
        delete me.keyNav;
    },

    
    getEditStyle: function() {
        return this.editStyle;
    },

    
    initFieldAccessors: function(column) {
        var me = this;

        if (Ext.isArray(column)) {
            Ext.Array.forEach(column, me.initFieldAccessors, me);
            return;
        }

        
        
        Ext.applyIf(column, {
            getEditor: function(record, defaultField) {
                return me.getColumnField(this, defaultField);
            },

            setEditor: function(field) {
                me.setColumnField(this, field);
            }
        });
    },

    
    removeFieldAccessors: function(column) {
        var me = this;

        if (Ext.isArray(column)) {
            Ext.Array.forEach(column, me.removeFieldAccessors, me);
            return;
        }

        delete column.getEditor;
        delete column.setEditor;
    },

    
    
    getColumnField: function(columnHeader, defaultField) {
        var field = columnHeader.field;

        if (!field && columnHeader.editor) {
            field = columnHeader.editor;
            delete columnHeader.editor;
        }

        if (!field && defaultField) {
            field = defaultField;
        }

        if (field) {
            if (Ext.isString(field)) {
                field = { xtype: field };
            }
            if (Ext.isObject(field) && !field.isFormField) {
                field = Ext.ComponentManager.create(field, this.defaultFieldXType);
                columnHeader.field = field;
            }

            Ext.apply(field, {
                name: columnHeader.dataIndex
            });

            return field;
        }
    },

    
    
    setColumnField: function(column, field) {
        if (Ext.isObject(field) && !field.isFormField) {
            field = Ext.ComponentManager.create(field, this.defaultFieldXType);
        }
        column.field = field;
    },

    
    initEvents: function() {
        var me = this;
        me.initEditTriggers();
        me.initCancelTriggers();
    },

    
    initCancelTriggers: Ext.emptyFn,

    
    initEditTriggers: function() {
        var me = this,
            view = me.view,
            clickEvent = me.clicksToEdit === 1 ? 'click' : 'dblclick';

        
        me.mon(view, 'cell' + clickEvent, me.startEditByClick, me);
        view.on('render', function() {
            me.keyNav = Ext.create('Ext.util.KeyNav', view.el, {
                enter: me.onEnterKey,
                esc: me.onEscKey,
                scope: me
            });
        }, me, { single: true });
    },

    
    onEnterKey: function(e) {
        var me = this,
            grid = me.grid,
            selModel = grid.getSelectionModel(),
            record,
            columnHeader = grid.headerCt.getHeaderAtIndex(0);

        
        
        if (selModel.getCurrentPosition) {
            pos = selModel.getCurrentPosition();
            record = grid.store.getAt(pos.row);
            columnHeader = grid.headerCt.getHeaderAtIndex(pos.column);
        }
        
        else {
            record = selModel.getLastSelected();
        }
        me.startEdit(record, columnHeader);
    },

    
    onEscKey: function(e) {
        this.cancelEdit();
    },

    
    startEditByClick: function(view, cell, colIdx, record, row, rowIdx, e) {
        this.startEdit(record, view.getHeaderAtIndex(colIdx));
    },

    
    beforeEdit: Ext.emptyFn,

    
    startEdit: function(record, columnHeader) {
        var me = this,
            context = me.getEditingContext(record, columnHeader);

        if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', context) === false || context.cancel) {
            return false;
        }

        me.context = context;
        me.editing = true;
    },

    
    getEditingContext: function(record, columnHeader) {
        var me = this,
            grid = me.grid,
            store = grid.store,
            rowIdx,
            colIdx,
            view = grid.getView(),
            value;

        
        if (Ext.isNumber(record)) {
            rowIdx = record;
            record = store.getAt(rowIdx);
        } else {
            rowIdx = store.indexOf(record);
        }
        if (Ext.isNumber(columnHeader)) {
            colIdx = columnHeader;
            columnHeader = grid.headerCt.getHeaderAtIndex(colIdx);
        } else {
            colIdx = columnHeader.getIndex();
        }

        value = record.get(columnHeader.dataIndex);
        return {
            grid: grid,
            record: record,
            field: columnHeader.dataIndex,
            value: value,
            row: view.getNode(rowIdx),
            column: columnHeader,
            rowIdx: rowIdx,
            colIdx: colIdx
        };
    },

    
    cancelEdit: function() {
        this.editing = false;
    },

    
    completeEdit: function() {
        var me = this;

        if (me.editing && me.validateEdit()) {
            me.fireEvent('edit', me.context);
        }

        delete me.context;
        me.editing = false;
    },

    
    validateEdit: function() {
        var me = this,
            context = me.context;

        return me.fireEvent('validateedit', me, context) !== false && !context.cancel;
    }
});

Ext.define('Ext.grid.plugin.CellEditing', {
    alias: 'plugin.cellediting',
    extend: 'Ext.grid.plugin.Editing',
    requires: ['Ext.grid.CellEditor'],

    constructor: function() {
        
        
        
        this.callParent(arguments);
        this.editors = Ext.create('Ext.util.MixedCollection', false, function(editor) {
            return editor.editorId;
        });
    },

    
    destroy: function() {
        var me = this;
        me.editors.each(Ext.destroy, Ext);
        me.editors.clear();
        me.callParent(arguments);
    },
    
    onBodyScroll: function() {
        var ed = this.getActiveEditor();
        if (ed && ed.field) {
            if (ed.field.triggerBlur) {
                ed.field.triggerBlur();
            } else {
                ed.field.blur();
            }
        }
    },

    
    
    initCancelTriggers: function() {
        var me   = this,
            grid = me.grid,
            view = grid.view;
            
        view.addElListener('mousewheel', me.cancelEdit, me);
        me.mon(view, 'bodyscroll', me.onBodyScroll, me);
        me.mon(grid, {
            columnresize: me.cancelEdit,
            columnmove: me.cancelEdit,
            scope: me
        });
    },

    
    startEdit: function(record, columnHeader) {
        var me = this,
            ed   = me.getEditor(record, columnHeader),
            value = record.get(columnHeader.dataIndex),
            context = me.getEditingContext(record, columnHeader);

        record = context.record;
        columnHeader = context.column;

        
        
        me.completeEdit();

        
        if (columnHeader && !columnHeader.getEditor(record)) {
            return false;
        }

        if (ed) {
            context.originalValue = context.value = value;
            if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', context) === false || context.cancel) {
                return false;
            }

            me.context = context;
            me.setActiveEditor(ed);
            me.setActiveRecord(record);
            me.setActiveColumn(columnHeader);

            
            Ext.defer(ed.startEdit, 15, ed, [me.getCell(record, columnHeader), value]);
        } else {
            
            
            
            
            me.grid.getView().el.focus((Ext.isWebKit || Ext.isIE) ? 10 : false);
        }
    },

    completeEdit: function() {
        var activeEd = this.getActiveEditor();
        if (activeEd) {
            activeEd.completeEdit();
        }
    },

    
    setActiveEditor: function(ed) {
        this.activeEditor = ed;
    },

    getActiveEditor: function() {
        return this.activeEditor;
    },

    setActiveColumn: function(column) {
        this.activeColumn = column;
    },

    getActiveColumn: function() {
        return this.activeColumn;
    },

    setActiveRecord: function(record) {
        this.activeRecord = record;
    },

    getActiveRecord: function() {
        return this.activeRecord;
    },

    getEditor: function(record, column) {
        var me = this,
            editors = me.editors,
            editorId = column.itemId || column.id,
            editor = editors.getByKey(editorId);

        if (editor) {
            return editor;
        } else {
            editor = column.getEditor(record);
            if (!editor) {
                return false;
            }

            
            if (!(editor instanceof Ext.grid.CellEditor)) {
                editor = Ext.create('Ext.grid.CellEditor', {
                    editorId: editorId,
                    field: editor
                });
            }
            editor.parentEl = me.grid.getEditorParent();
            
            editor.on({
                scope: me,
                specialkey: me.onSpecialKey,
                complete: me.onEditComplete,
                canceledit: me.cancelEdit
            });
            editors.add(editor);
            return editor;
        }
    },

    
    getCell: function(record, column) {
        return this.grid.getView().getCell(record, column);
    },

    onSpecialKey: function(ed, field, e) {
        var grid = this.grid,
            sm;
        if (e.getKey() === e.TAB) {
            e.stopEvent();
            sm = grid.getSelectionModel();
            if (sm.onEditorTab) {
                sm.onEditorTab(this, e);
            }
        }
    },

    onEditComplete : function(ed, value, startValue) {
        var me = this,
            grid = me.grid,
            sm = grid.getSelectionModel(),
            activeColumn = me.getActiveColumn(),
            dataIndex;

        if (activeColumn) {
            dataIndex = activeColumn.dataIndex;

            me.setActiveEditor(null);
            me.setActiveColumn(null);
            me.setActiveRecord(null);
            delete sm.wasEditing;
    
            if (!me.validateEdit()) {
                return;
            }
            
            
            if (value !== startValue) {
                me.context.record.set(dataIndex, value);
            
            } else {
                grid.getView().el.focus();
            }
            me.context.value = value;
            me.fireEvent('edit', me, me.context);
            

        }
    },

    
    cancelEdit: function() {
        var me = this,
            activeEd = me.getActiveEditor(),
            viewEl = me.grid.getView().el;

        me.setActiveEditor(null);
        me.setActiveColumn(null);
        me.setActiveRecord(null);
        if (activeEd) {
            activeEd.cancelEdit();
            viewEl.focus();
        }
    },

    
    startEditByPosition: function(position) {
        var me = this,
            grid = me.grid,
            sm = grid.getSelectionModel(),
            editRecord = grid.store.getAt(position.row),
            editColumnHeader = grid.headerCt.getHeaderAtIndex(position.column);

        if (sm.selectByPosition) {
            sm.selectByPosition(position);
        }
        me.startEdit(editRecord, editColumnHeader);
    }
});

Ext.define('Ext.grid.plugin.DragDrop', {
    extend: 'Ext.AbstractPlugin',
    alias: 'plugin.gridviewdragdrop',

    uses: [
        'Ext.view.DragZone',
        'Ext.grid.ViewDropZone'
    ],

    

    

    dragText : '{0} selected row{1}',

    
    ddGroup : "GridDD",

    

    

    
    enableDrop: true,

    
    enableDrag: true,

    init : function(view) {
        view.on('render', this.onViewRender, this, {single: true});
    },

    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },

    onViewRender : function(view) {
        var me = this;

        if (me.enableDrag) {
            me.dragZone = Ext.create('Ext.view.DragZone', {
                view: view,
                ddGroup: me.dragGroup || me.ddGroup,
                dragText: me.dragText
            });
        }

        if (me.enableDrop) {
            me.dropZone = Ext.create('Ext.grid.ViewDropZone', {
                view: view,
                ddGroup: me.dropGroup || me.ddGroup
            });
        }
    }
});

Ext.define('Ext.grid.plugin.HeaderReorderer', {
    extend: 'Ext.util.Observable',
    requires: ['Ext.grid.header.DragZone', 'Ext.grid.header.DropZone'],
    alias: 'plugin.gridheaderreorderer',

    init: function(headerCt) {
        this.headerCt = headerCt;
        headerCt.on('render', this.onHeaderCtRender, this);
    },

    
    destroy: function() {
        Ext.destroy(this.dragZone, this.dropZone);
    },

    onHeaderCtRender: function() {
        this.dragZone = Ext.create('Ext.grid.header.DragZone', this.headerCt);
        this.dropZone = Ext.create('Ext.grid.header.DropZone', this.headerCt);
        if (this.disabled) {
            this.dragZone.disable();
        }
    },
    
    enable: function() {
        this.disabled = false;
        if (this.dragZone) {
            this.dragZone.enable();
        }
    },
    
    disable: function() {
        this.disabled = true;
        if (this.dragZone) {
            this.dragZone.disable();
        }
    }
});

Ext.define('Ext.grid.plugin.HeaderResizer', {
    extend: 'Ext.util.Observable',
    requires: ['Ext.dd.DragTracker', 'Ext.util.Region'],
    alias: 'plugin.gridheaderresizer',
    
    disabled: false,

    
    configs: {
        dynamic: true
    },

    colHeaderCls: Ext.baseCSSPrefix + 'column-header',

    minColWidth: 40,
    maxColWidth: 1000,
    wResizeCursor: 'col-resize',
    eResizeCursor: 'col-resize',
    
    
    
    

    init: function(headerCt) {
        this.headerCt = headerCt;
        headerCt.on('render', this.afterHeaderRender, this, {single: true});
    },

    
    destroy: function() {
        if (this.tracker) {
            this.tracker.destroy();
        }
    },

    afterHeaderRender: function() {
        var headerCt = this.headerCt,
            el = headerCt.el;

        headerCt.mon(el, 'mousemove', this.onHeaderCtMouseMove, this);

        this.tracker = Ext.create('Ext.dd.DragTracker', {
            disabled: this.disabled,
            onBeforeStart: Ext.Function.bind(this.onBeforeStart, this),
            onStart: Ext.Function.bind(this.onStart, this),
            onDrag: Ext.Function.bind(this.onDrag, this),
            onEnd: Ext.Function.bind(this.onEnd, this),
            tolerance: 3,
            autoStart: 300,
            el: el
        });
    },

    
    
    
    onHeaderCtMouseMove: function(e, t) {
        if (this.headerCt.dragging) {
            if (this.activeHd) {
                this.activeHd.el.dom.style.cursor = '';
                delete this.activeHd;
            }
        } else {
            var headerEl = e.getTarget('.' + this.colHeaderCls, 3, true),
                overHeader, resizeHeader;

            if (headerEl){
                overHeader = Ext.getCmp(headerEl.id);

                
                if (overHeader.isOnLeftEdge(e)) {
                    resizeHeader = overHeader.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])');
                }
                
                else if (overHeader.isOnRightEdge(e)) {
                    resizeHeader = overHeader;
                }
                
                else {
                    resizeHeader = null;
                }

                
                if (resizeHeader) {
                    
                    
                    
                    if (resizeHeader.isGroupHeader) {
                        resizeHeader = resizeHeader.getVisibleGridColumns();
                        resizeHeader = resizeHeader[resizeHeader.length - 1];
                    }

                    if (resizeHeader && !resizeHeader.fixed) {
                        this.activeHd = resizeHeader;
                        overHeader.el.dom.style.cursor = this.eResizeCursor;
                    }
                
                } else {
                    overHeader.el.dom.style.cursor = '';
                    delete this.activeHd;
                }
            }
        }
    },

    
    onBeforeStart : function(e){
        var t = e.getTarget();
        
        this.dragHd = this.activeHd;

        if (!!this.dragHd && !Ext.fly(t).hasCls('x-column-header-trigger') && !this.headerCt.dragging) {
            
            this.tracker.constrainTo = this.getConstrainRegion();
            return true;
        } else {
            this.headerCt.dragging = false;
            return false;
        }
    },

    
    getConstrainRegion: function() {
        var dragHdEl = this.dragHd.el,
            region   = Ext.util.Region.getRegion(dragHdEl);

        return region.adjust(
            0,
            this.maxColWidth - dragHdEl.getWidth(),
            0,
            this.minColWidth
        );
    },

    
    
    onStart: function(e){
        var me       = this,
            dragHd   = me.dragHd,
            dragHdEl = dragHd.el,
            width    = dragHdEl.getWidth(),
            headerCt = me.headerCt,
            t        = e.getTarget();

        if (me.dragHd && !Ext.fly(t).hasCls('x-column-header-trigger')) {
            headerCt.dragging = true;
        }

        me.origWidth = width;

        
        if (!me.dynamic) {
            var xy           = dragHdEl.getXY(),
                gridSection  = headerCt.up('[scrollerOwner]'),
                dragHct      = me.dragHd.up(':not([isGroupHeader])'),
                firstSection = dragHct.up(),
                lhsMarker    = gridSection.getLhsMarker(),
                rhsMarker    = gridSection.getRhsMarker(),
                el           = rhsMarker.parent(),
                offsetLeft   = el.getLeft(true),
                offsetTop    = el.getTop(true),
                topLeft      = el.translatePoints(xy),
                markerHeight = firstSection.body.getHeight() + headerCt.getHeight(),
                top = topLeft.top - offsetTop;

            lhsMarker.setTop(top);
            rhsMarker.setTop(top);
            lhsMarker.setHeight(markerHeight);
            rhsMarker.setHeight(markerHeight);
            lhsMarker.setLeft(topLeft.left - offsetLeft);
            rhsMarker.setLeft(topLeft.left + width - offsetLeft);
        }
    },

    
    onDrag: function(e){
        if (!this.dynamic) {
            var xy          = this.tracker.getXY('point'),
                gridSection = this.headerCt.up('[scrollerOwner]'),
                rhsMarker   = gridSection.getRhsMarker(),
                el          = rhsMarker.parent(),
                topLeft     = el.translatePoints(xy),
                offsetLeft  = el.getLeft(true);

            rhsMarker.setLeft(topLeft.left - offsetLeft);
        
        } else {
            this.doResize();
        }
    },

    onEnd: function(e){
        this.headerCt.dragging = false;
        if (this.dragHd) {
            if (!this.dynamic) {
                var dragHd      = this.dragHd,
                    gridSection = this.headerCt.up('[scrollerOwner]'),
                    lhsMarker   = gridSection.getLhsMarker(),
                    rhsMarker   = gridSection.getRhsMarker(),
                    currWidth   = dragHd.getWidth(),
                    offset      = this.tracker.getOffset('point'),
                    offscreen   = -9999;

                
                lhsMarker.setLeft(offscreen);
                rhsMarker.setLeft(offscreen);
            }
            this.doResize();
        }
    },

    doResize: function() {
        if (this.dragHd) {
            var dragHd = this.dragHd,
                nextHd,
                offset = this.tracker.getOffset('point');

            
            if (dragHd.flex) {
                delete dragHd.flex;
            }

            
            
            if (this.headerCt.forceFit) {
                nextHd = dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])');
                if (nextHd) {
                    this.headerCt.componentLayout.layoutBusy = true;
                }
            }

            
            
            dragHd.minWidth = this.origWidth + offset[0];
            dragHd.setWidth(dragHd.minWidth);

            
            
            if (nextHd) {
                delete nextHd.flex;
                nextHd.setWidth(nextHd.getWidth() - offset[0]);
                this.headerCt.componentLayout.layoutBusy = false;
                this.headerCt.doComponentLayout();
            }
        }
    },
    
    disable: function() {
        this.disabled = true;
        if (this.tracker) {
            this.tracker.disable();
        }
    },
    
    enable: function() {
        this.disabled = false;
        if (this.tracker) {
            this.tracker.enable();
        }
    }
});

Ext.define('Ext.grid.plugin.RowEditing', {
    extend: 'Ext.grid.plugin.Editing',
    alias: 'plugin.rowediting',

    requires: [
        'Ext.grid.RowEditor'
    ],

    editStyle: 'row',

    
    autoCancel: true,

    

    
    errorSummary: true,

    
    
    

    constructor: function() {
        var me = this;
        me.callParent(arguments);

        if (!me.clicksToMoveEditor) {
            me.clicksToMoveEditor = me.clicksToEdit;
        }

        me.autoCancel = !!me.autoCancel;
    },

    
    destroy: function() {
        var me = this;
        Ext.destroy(me.editor);
        me.callParent(arguments);
    },

    
    startEdit: function(record, columnHeader) {
        var me = this,
            editor = me.getEditor();

        if (me.callParent(arguments) === false) {
            return false;
        }

        
        if (editor.beforeEdit() !== false) {
            editor.startEdit(me.context.record, me.context.column);
        }
    },

    
    cancelEdit: function() {
        var me = this;

        if (me.editing) {
            me.getEditor().cancelEdit();
            me.callParent(arguments);
        }
    },

    
    completeEdit: function() {
        var me = this;

        if (me.editing && me.validateEdit()) {
            me.editing = false;
            me.fireEvent('edit', me.context);
        }
    },

    
    validateEdit: function() {
        var me = this;
        return me.callParent(arguments) && me.getEditor().completeEdit();
    },

    
    getEditor: function() {
        var me = this;

        if (!me.editor) {
            me.editor = me.initEditor();
        }
        return me.editor;
    },

    
    initEditor: function() {
        var me = this,
            grid = me.grid,
            view = me.view,
            headerCt = grid.headerCt;

        return Ext.create('Ext.grid.RowEditor', {
            autoCancel: me.autoCancel,
            errorSummary: me.errorSummary,
            fields: headerCt.getGridColumns(),
            hidden: true,

            
            editingPlugin: me,
            renderTo: view.el
        });
    },

    
    initEditTriggers: function() {
        var me = this,
            grid = me.grid,
            view = me.view,
            headerCt = grid.headerCt,
            moveEditorEvent = me.clicksToMoveEditor === 1 ? 'click' : 'dblclick';

        me.callParent(arguments);

        if (me.clicksToMoveEditor !== me.clicksToEdit) {
            me.mon(view, 'cell' + moveEditorEvent, me.moveEditorByClick, me);
        }

        view.on('render', function() {
            
            me.mon(headerCt, {
                add: me.onColumnAdd,
                remove: me.onColumnRemove,
                columnresize: me.onColumnResize,
                columnhide: me.onColumnHide,
                columnshow: me.onColumnShow,
                columnmove: me.onColumnMove,
                scope: me
            });
        }, me, { single: true });
    },

    startEditByClick: function() {
        var me = this;
        if (!me.editing || me.clicksToMoveEditor === me.clicksToEdit) {
            me.callParent(arguments);
        }
    },

    moveEditorByClick: function() {
        var me = this;
        if (me.editing) {
            me.superclass.startEditByClick.apply(me, arguments);
        }
    },

    
    onColumnAdd: function(ct, column) {
        if (column.isHeader) {
            var me = this,
                editor;
            
            me.initFieldAccessors(column);
            editor = me.getEditor();
            
            if (editor && editor.onColumnAdd) {
                editor.onColumnAdd(column);
            }
        }
    },

    
    onColumnRemove: function(ct, column) {
        if (column.isHeader) {
            var me = this,
                editor = me.getEditor();
    
            if (editor && editor.onColumnRemove) {
                editor.onColumnRemove(column);
            }
            me.removeFieldAccessors(column);  
        }
    },

    
    onColumnResize: function(ct, column, width) {
        if (column.isHeader) {
            var me = this,
                editor = me.getEditor();
    
            if (editor && editor.onColumnResize) {
                editor.onColumnResize(column, width);
            }
        }
    },

    
    onColumnHide: function(ct, column) {
        
        var me = this,
            editor = me.getEditor();

        if (editor && editor.onColumnHide) {
            editor.onColumnHide(column);
        }
    },

    
    onColumnShow: function(ct, column) {
        
        var me = this,
            editor = me.getEditor();

        if (editor && editor.onColumnShow) {
            editor.onColumnShow(column);
        }
    },

    
    onColumnMove: function(ct, column, fromIdx, toIdx) {
        
        var me = this,
            editor = me.getEditor();

        if (editor && editor.onColumnMove) {
            editor.onColumnMove(column, fromIdx, toIdx);
        }
    },

    
    setColumnField: function(column, field) {
        var me = this;
        me.callParent(arguments);
        me.getEditor().setField(column.field, column);
    }
});

Ext.define('Ext.grid.property.Grid', {

    extend: 'Ext.grid.Panel',
    
    alias: 'widget.propertygrid',

    alternateClassName: 'Ext.grid.PropertyGrid',

    uses: [
       'Ext.grid.plugin.CellEditing',
       'Ext.grid.property.Store',
       'Ext.grid.property.HeaderContainer',
       'Ext.XTemplate',
       'Ext.grid.CellEditor',
       'Ext.form.field.Date',
       'Ext.form.field.Text',
       'Ext.form.field.Number'
    ],

   

    

    

    

    

    
    valueField: 'value',

    
    nameField: 'name',

    
    enableColumnMove: false,
    columnLines: true,
    stripeRows: false,
    trackMouseOver: false,
    clicksToEdit: 1,
    enableHdMenu: false,

    
    initComponent : function(){
        var me = this;

        me.addCls(Ext.baseCSSPrefix + 'property-grid');
        me.plugins = me.plugins || [];

        
        me.plugins.push(Ext.create('Ext.grid.plugin.CellEditing', {
            clicksToEdit: me.clicksToEdit,

            
            startEdit: function(record, column) {
                
                Ext.grid.plugin.CellEditing.prototype.startEdit.call(this, record, me.headerCt.child('#' + me.valueField));
            }
        }));

        me.selModel = {
            selType: 'cellmodel',
            onCellSelect: function(position) {
                if (position.column != 1) {
                    position.column = 1;
                    Ext.selection.CellModel.prototype.onCellSelect.call(this, position);
                }
            }
        };
        me.customRenderers = me.customRenderers || {};
        me.customEditors = me.customEditors || {};

        
        if (!me.store) {
            me.propStore = me.store = Ext.create('Ext.grid.property.Store', me, me.source);
        }

        me.store.sort('name', 'ASC');
        me.columns = Ext.create('Ext.grid.property.HeaderContainer', me, me.store);

        me.addEvents(
            
            'beforepropertychange',
            
            'propertychange'
        );
        me.callParent();

        
        me.getView().walkCells = this.walkCells;

        
        me.editors = {
            'date'    : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Date',   {selectOnFocus: true})}),
            'string'  : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Text',   {selectOnFocus: true})}),
            'number'  : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Number', {selectOnFocus: true})}),
            'boolean' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.ComboBox', {
                editable: false,
                store: [[ true, me.headerCt.trueText ], [false, me.headerCt.falseText ]]
            })})
        };

        
        this.store.on('update', me.onUpdate, me);
    },

    
    onUpdate : function(store, record, operation) {
        var me = this,
            v, oldValue;

        if (operation == Ext.data.Model.EDIT) {
            v = record.get(me.valueField);
            oldValue = record.modified.value;
            if (me.fireEvent('beforepropertychange', me.source, record.getId(), v, oldValue) !== false) {
                if (me.source) {
                    me.source[record.getId()] = v;
                }
                record.commit();
                me.fireEvent('propertychange', me.source, record.getId(), v, oldValue);
            } else {
                record.reject();
            }
        }
    },

    
    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
        if (direction == 'left') {
            direction = 'up';
        } else if (direction == 'right') {
            direction = 'down';
        }
        pos = Ext.view.Table.prototype.walkCells.call(this, pos, direction, e, preventWrap, verifierFn, scope);
        if (!pos.column) {
            pos.column = 1;
        }
        return pos;
    },

    
    
    getCellEditor : function(record, column) {
        var me = this,
            propName = record.get(me.nameField), 
            val = record.get(me.valueField),
            editor = me.customEditors[propName];

        
        
        if (editor) {
            if (!(editor instanceof Ext.grid.CellEditor)) {
                if (!(editor instanceof Ext.form.field.Base)) {
                    editor = Ext.ComponentManager.create(editor, 'textfield');
                }
                editor = me.customEditors[propName] = Ext.create('Ext.grid.CellEditor', { field: editor });
            }
        } else if (Ext.isDate(val)) {
            editor = me.editors.date;
        } else if (Ext.isNumber(val)) {
            editor = me.editors.number;
        } else if (Ext.isBoolean(val)) {
            editor = me.editors['boolean'];
        } else {
            editor = me.editors.string;
        }

        
        editor.editorId = propName;
        return editor;
    },

    beforeDestroy: function() {
        var me = this;
        me.callParent();
        me.destroyEditors(me.editors);
        me.destroyEditors(me.customEditors);
        delete me.source;
    },

    destroyEditors: function (editors) {
        for (var ed in editors) {
            if (editors.hasOwnProperty(ed)) {
                Ext.destroy(editors[ed]);
            }
        }
    },

    
    setSource: function(source) {
        this.source = source;
        this.propStore.setSource(source);
    },

    
    getSource: function() {
        return this.propStore.getSource();
    },

    
    setProperty: function(prop, value, create) {
        this.propStore.setValue(prop, value, create);
    },

    
    removeProperty: function(prop) {
        this.propStore.remove(prop);
    }

    
    
    
    
});

Ext.define('Ext.grid.property.HeaderContainer', {

    extend: 'Ext.grid.header.Container',

    alternateClassName: 'Ext.grid.PropertyColumnModel',

    
    nameText : 'Name',
    valueText : 'Value',
    dateFormat : 'm/j/Y',
    trueText: 'true',
    falseText: 'false',

    
    nameColumnCls: Ext.baseCSSPrefix + 'grid-property-name',
    
    constructor : function(grid, store) {

        this.grid = grid;
        this.store = store;
        this.callParent([{
            items: [{
                header: this.nameText,
                width: 115,
                sortable: true,
                dataIndex: grid.nameField,
                renderer: Ext.Function.bind(this.renderProp, this),
                itemId: grid.nameField,
                menuDisabled :true,
                tdCls: this.nameColumnCls
            }, {
                header: this.valueText,
                renderer: Ext.Function.bind(this.renderCell, this),
                getEditor: function(record) {
                    return grid.getCellEditor(record, this);
                },
                flex: 1,
                fixed: true,
                dataIndex: grid.valueField,
                itemId: grid.valueField,
                menuDisabled: true
            }]
        }]);
    },

    
    
    renderProp : function(v) {
        return this.getPropertyName(v);
    },

    
    
    renderCell : function(val, meta, rec) {
        var me = this,
            renderer = this.grid.customRenderers[rec.get(me.grid.nameField)],
            result = val;

        if (renderer) {
            return renderer.apply(this, arguments);
        }
        if (Ext.isDate(val)) {
            result = this.renderDate(val);
        } else if (Ext.isBoolean(val)) {
            result = this.renderBool(val);
        }
        return Ext.util.Format.htmlEncode(result);
    },

    
    renderDate : Ext.util.Format.date,

    
    renderBool : function(bVal) {
        return this[bVal ? 'trueText' : 'falseText'];
    },

    
    
    getPropertyName : function(name) {
        var pn = this.grid.propertyNames;
        return pn && pn[name] ? pn[name] : name;
    }
});

Ext.define('Ext.grid.property.Property', {
    extend: 'Ext.data.Model',

    alternateClassName: 'Ext.PropGridProperty',

    fields: [{
        name: 'name',
        type: 'string'
    }, {
        name: 'value'
    }],
    idProperty: 'name'
});

Ext.define('Ext.grid.property.Store', {

    extend: 'Ext.data.Store',

    alternateClassName: 'Ext.grid.PropertyStore',

    uses: ['Ext.data.reader.Reader', 'Ext.data.proxy.Proxy', 'Ext.data.ResultSet', 'Ext.grid.property.Property'],

    constructor : function(grid, source){
        var me = this;
        
        me.grid = grid;
        me.source = source;
        me.callParent([{
            data: source,
            model: Ext.grid.property.Property,
            proxy: me.getProxy()
        }]);
    },

    
    getProxy: function() {
        if (!this.proxy) {
            Ext.grid.property.Store.prototype.proxy = Ext.create('Ext.data.proxy.Memory', {
                model: Ext.grid.property.Property,
                reader: this.getReader()
            });
        }
        return this.proxy;
    },

    
    getReader: function() {
        if (!this.reader) {
            Ext.grid.property.Store.prototype.reader = Ext.create('Ext.data.reader.Reader', {
                model: Ext.grid.property.Property,

                buildExtractors: Ext.emptyFn,

                read: function(dataObject) {
                    return this.readRecords(dataObject);
                },

                readRecords: function(dataObject) {
                    var val,
                        propName,
                        result = {
                            records: [],
                            success: true
                        };

                    for (propName in dataObject) {
                        if (dataObject.hasOwnProperty(propName)) {
                            val = dataObject[propName];
                            if (this.isEditableValue(val)) {
                                result.records.push(new Ext.grid.property.Property({
                                    name: propName,
                                    value: val
                                }, propName));
                            }
                        }
                    }
                    result.total = result.count = result.records.length;
                    return Ext.create('Ext.data.ResultSet', result);
                },

                
                isEditableValue: function(val){
                    return Ext.isPrimitive(val) || Ext.isDate(val);
                }
            });
        }
        return this.reader;
    },

    
    setSource : function(dataObject) {
        var me = this;

        me.source = dataObject;
        me.suspendEvents();
        me.removeAll();
        me.proxy.data = dataObject;
        me.load();
        me.resumeEvents();
        me.fireEvent('datachanged', me);
    },

    
    getProperty : function(row) {
       return Ext.isNumber(row) ? this.getAt(row) : this.getById(row);
    },

    
    setValue : function(prop, value, create){
        var me = this,
            rec = me.getRec(prop);
            
        if (rec) {
            rec.set('value', value);
            me.source[prop] = value;
        } else if (create) {
            
            me.source[prop] = value;
            rec = new Ext.grid.property.Property({name: prop, value: value}, prop);
            me.store.add(rec);
        }
    },

    
    remove : function(prop) {
        var rec = this.getRec(prop);
        if (rec) {
            store.remove(rec);
            delete this.source[prop];
        }
    },

    
    getRec : function(prop) {
        return this.getById(prop);
    },

    
    getSource : function() {
        return this.source;
    }
});


Ext.define('Ext.layout.component.Body', {

    

    alias: ['layout.body'],

    extend: 'Ext.layout.component.Component',

    uses: ['Ext.layout.container.Container'],

    

    type: 'body',
    
    onLayout: function(width, height) {
        var me = this,
            owner = me.owner;

        
        me.setTargetSize(width, height);

        
        me.setBodySize.apply(me, arguments);

        
        if (owner && owner.layout && owner.layout.isLayout) {
            if (!Ext.isNumber(owner.height) || !Ext.isNumber(owner.width)) {
                owner.layout.bindToOwnerCtComponent = true;
            }
            else {
                owner.layout.bindToOwnerCtComponent = false;
            }
        }
        
        me.callParent(arguments);
    },

    
    setBodySize: function(width, height) {
        var me = this,
            owner = me.owner,
            frameSize = owner.frameSize,
            isNumber = Ext.isNumber;

        if (isNumber(width)) {
            width -= owner.el.getFrameWidth('lr') - frameSize.left - frameSize.right;
        }
        if (isNumber(height)) {
            height -= owner.el.getFrameWidth('tb') - frameSize.top - frameSize.bottom;
        }

        me.setElementSize(owner.body, width, height);
    }
});

Ext.define('Ext.layout.component.FieldSet', {
    extend: 'Ext.layout.component.Body',
    alias: ['layout.fieldset'],

    type: 'fieldset',

    doContainerLayout: function() {
        
        if (!this.owner.collapsed) {
            this.callParent();
        }
    }
});

Ext.define('Ext.layout.component.Tab', {

    alias: ['layout.tab'],

    extend: 'Ext.layout.component.Button',

    

    beforeLayout: function() {
        var me = this, dirty = me.lastClosable !== me.owner.closable;

        if (dirty) {
            delete me.adjWidth;
        }

        return this.callParent(arguments) || dirty;
    },

    onLayout: function () {
        var me = this;

        me.callParent(arguments);

        me.lastClosable = me.owner.closable;
    }
});


Ext.define('Ext.layout.component.field.File', {
    alias: ['layout.filefield'],
    extend: 'Ext.layout.component.field.Field',

    type: 'filefield',

    sizeBodyContents: function(width, height) {
        var me = this,
            owner = me.owner;

        if (!owner.buttonOnly) {
            
            
            me.setElementSize(owner.inputEl, Ext.isNumber(width) ? width - owner.button.getWidth() - owner.buttonMargin : width);
        }
    }
});


Ext.define('Ext.layout.component.field.Slider', {

    

    alias: ['layout.sliderfield'],

    extend: 'Ext.layout.component.field.Field',

    

    type: 'sliderfield',

    sizeBodyContents: function(width, height) {
        var owner = this.owner,
            thumbs = owner.thumbs,
            length = thumbs.length,
            inputEl = owner.inputEl,
            innerEl = owner.innerEl,
            endEl = owner.endEl,
            i = 0;

        
        for(; i < length; ++i) {
            thumbs[i].el.stopAnimation();
        }
        
        if (owner.vertical) {
            inputEl.setHeight(height);
            innerEl.setHeight(Ext.isNumber(height) ? height - inputEl.getPadding('t') - endEl.getPadding('b') : height);
        }
        else {
            inputEl.setWidth(width);
            innerEl.setWidth(Ext.isNumber(width) ? width - inputEl.getPadding('l') - endEl.getPadding('r') : width);
        }
        owner.syncThumbs();
    }
});



Ext.define('Ext.layout.container.Absolute', {

    

    alias: 'layout.absolute',
    extend: 'Ext.layout.container.Anchor',
    requires: ['Ext.chart.axis.Axis', 'Ext.fx.Anim'],
    alternateClassName: 'Ext.layout.AbsoluteLayout',

    

    itemCls: Ext.baseCSSPrefix + 'abs-layout-item',

    type: 'absolute',

    onLayout: function() {
        var me = this,
            target = me.getTarget(),
            targetIsBody = target.dom === document.body;

        
        if (!targetIsBody) {
            target.position();
        }
        me.paddingLeft = target.getPadding('l');
        me.paddingTop = target.getPadding('t');
        me.callParent(arguments);
    },

    
    adjustWidthAnchor: function(value, comp) {
        
        return value ? value - comp.getPosition(true)[0] : value;
    },

    
    adjustHeightAnchor: function(value, comp) {
        
        return value ? value - comp.getPosition(true)[1] : value;
    }
});

Ext.define('Ext.layout.container.Accordion', {
    extend: 'Ext.layout.container.VBox',
    alias: ['layout.accordion'],
    alternateClassName: 'Ext.layout.AccordionLayout',
    
    align: 'stretch',

    
    fill : true,
    
    autoWidth : true,
    
    titleCollapse : true,
    
    hideCollapseTool : false,
    
    collapseFirst : false,
    
    animate : true,
    
    activeOnTop : false,
    
    multi: false,

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        
        me.initialAnimate = me.animate;
        me.animate = false;

        
        if (me.fill === false) {
            me.itemCls = Ext.baseCSSPrefix + 'accordion-item';
        }
    },

    
    
    beforeLayout: function() {
        var me = this;

        me.callParent(arguments);
        if (me.fill) {
            if (!me.owner.el.dom.style.height) {
                return false;
            }
        } else {
            me.owner.componentLayout.monitorChildren = false;
            me.autoSize = true;
            me.owner.setAutoScroll(true);
        }
    },

    renderItems : function(items, target) {
        var me = this,
            ln = items.length,
            i = 0,
            comp,
            targetSize = me.getLayoutTargetSize(),
            renderedPanels = [],
            border;

        for (; i < ln; i++) {
            comp = items[i];
            if (!comp.rendered) {
                renderedPanels.push(comp);

                
                if (me.collapseFirst) {
                    comp.collapseFirst = me.collapseFirst;
                }
                if (me.hideCollapseTool) {
                    comp.hideCollapseTool = me.hideCollapseTool;
                    comp.titleCollapse = true;
                }
                else if (me.titleCollapse) {
                    comp.titleCollapse = me.titleCollapse;
                }

                delete comp.hideHeader;
                comp.collapsible = true;
                comp.title = comp.title || '&#160;';
                comp.setBorder(false);

                
                comp.width = targetSize.width;
                if (me.fill) {
                    delete comp.height;
                    delete comp.flex;

                    
                    if (me.expandedItem !== undefined) {
                        comp.collapsed = true;
                    }
                    
                    else if (comp.collapsed === false) {
                        comp.flex = 1;
                        me.expandedItem = i;
                    } else {
                        comp.collapsed = true;
                    }
                } else {
                    delete comp.flex;
                    comp.animCollapse = me.initialAnimate;
                    comp.autoHeight = true;
                    comp.autoScroll = false;
                }
            }
        }

        
        if (ln && me.expandedItem === undefined) {
            me.expandedItem = 0;
            comp = items[0];
            comp.collapsed = false;
            if (me.fill) {
                comp.flex = 1;
            }
        }
        
        
        me.callParent(arguments);
                
        
        ln = renderedPanels.length;
        for (i = 0; i < ln; i++) {
            comp = renderedPanels[i];

            
            delete comp.width;

            comp.header.addCls(Ext.baseCSSPrefix + 'accordion-hd');
            comp.body.addCls(Ext.baseCSSPrefix + 'accordion-body');
            
            
            if (me.fill) {
                me.owner.mon(comp, {
                    show: me.onComponentShow,
                    beforeexpand: me.onComponentExpand,
                    beforecollapse: me.onComponentCollapse,
                    scope: me
                });
            }
        }
    },

    onLayout: function() {
        var me = this;
        
        me.updatePanelClasses();
                
        if (me.fill) {
            me.callParent(arguments);
        } else {
            var targetSize = me.getLayoutTargetSize(),
                items = me.getVisibleItems(),
                len = items.length,
                i = 0, comp;

            for (; i < len; i++) {
                comp = items[i];
                if (comp.collapsed) {
                    items[i].setWidth(targetSize.width);
                } else {
                    items[i].setSize(null, null);
                }
            }
        }
        
        return me;
    },
    
    updatePanelClasses: function() {
        var children = this.getLayoutItems(),
            ln = children.length,
            siblingCollapsed = true,
            i, child;
            
        for (i = 0; i < ln; i++) {
            child = children[i];
            if (!siblingCollapsed) {
                child.header.addCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
            }
            else {
                child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-sibling-expanded');
            }
            if (i + 1 == ln && child.collapsed) {
                child.header.addCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
            }
            else {
                child.header.removeCls(Ext.baseCSSPrefix + 'accordion-hd-last-collapsed');
            }
            siblingCollapsed = child.collapsed;
        }
    },

    
    
    
    onComponentExpand: function(toExpand) {
        var me = this,
            it = me.owner.items.items,
            len = it.length,
            i = 0,
            comp;

        for (; i < len; i++) {
            comp = it[i];
            if (comp === toExpand && comp.collapsed) {
                me.setExpanded(comp);
            } else if (!me.multi && (comp.rendered && comp.header.rendered && comp !== toExpand && !comp.collapsed)) {
                me.setCollapsed(comp);
            }
        }
        
        me.animate = me.initialAnimate;
        me.layout();
        me.animate = false;
        return false;
    },

    onComponentCollapse: function(comp) {
        var me = this,
            toExpand = comp.next() || comp.prev(),
            expanded = me.multi ? me.owner.query('>panel:not([collapsed])') : [];

        
        
        if (me.multi) {
            me.setCollapsed(comp);

            
            
            if (expanded.length === 1 && expanded[0] === comp) {
                me.setExpanded(toExpand);
            }
            
            me.animate = me.initialAnimate;
            me.layout();
            me.animate = false;
        }
        
        else if (toExpand) {
            me.onComponentExpand(toExpand);
        }
        return false;
    },

    onComponentShow: function(comp) {
        
        this.onComponentExpand(comp);
    },

    setCollapsed: function(comp) {
        var otherDocks = comp.getDockedItems(),
            dockItem,
            len = otherDocks.length,
            i = 0;

        
        comp.hiddenDocked = [];
        for (; i < len; i++) {
            dockItem = otherDocks[i];
            if ((dockItem !== comp.header) && !dockItem.hidden) {
                dockItem.hidden = true;
                comp.hiddenDocked.push(dockItem);
            }
        }
        comp.addCls(comp.collapsedCls);
        comp.header.addCls(comp.collapsedHeaderCls);
        comp.height = comp.header.getHeight();
        comp.el.setHeight(comp.height);
        comp.collapsed = true;
        delete comp.flex;
        comp.fireEvent('collapse', comp);
        if (comp.collapseTool) {
            comp.collapseTool.setType('expand-' + comp.getOppositeDirection(comp.collapseDirection));
        }
    },

    setExpanded: function(comp) {
        var otherDocks = comp.hiddenDocked,
            len = otherDocks ? otherDocks.length : 0,
            i = 0;

        
        for (; i < len; i++) {
            otherDocks[i].show();
        }

        
        if (!comp.body.isVisible()) {
            comp.body.show();
        }
        delete comp.collapsed;
        delete comp.height;
        delete comp.componentLayout.lastComponentSize;
        comp.suspendLayout = false;
        comp.flex = 1;
        comp.removeCls(comp.collapsedCls);
        comp.header.removeCls(comp.collapsedHeaderCls);
        comp.fireEvent('expand', comp);
        if (comp.collapseTool) {
            comp.collapseTool.setType('collapse-' + comp.collapseDirection);
        }
        comp.setAutoScroll(comp.initialConfig.autoScroll);
    }
});

Ext.define('Ext.resizer.Splitter', {
    extend: 'Ext.Component',
    requires: ['Ext.XTemplate'],
    uses: ['Ext.resizer.SplitterTracker'],
    alias: 'widget.splitter',

    renderTpl: [
        '<tpl if="collapsible===true"><div class="' + Ext.baseCSSPrefix + 'collapse-el ' + Ext.baseCSSPrefix + 'layout-split-{collapseDir}">&nbsp;</div></tpl>'
    ],

    baseCls: Ext.baseCSSPrefix + 'splitter',
    collapsedCls: Ext.baseCSSPrefix + 'splitter-collapsed',

    
    collapsible: false,

    

    
    collapseOnDblClick: true,

    
    defaultSplitMin: 40,

    
    defaultSplitMax: 1000,

    width: 5,
    height: 5,

    
    collapseTarget: 'next',

    

    onRender: function() {
        var me = this,
            target = me.getCollapseTarget(),
            collapseDir = me.getCollapseDirection();

        Ext.applyIf(me.renderData, {
            collapseDir: collapseDir,
            collapsible: me.collapsible || target.collapsible
        });
        Ext.applyIf(me.renderSelectors, {
            collapseEl: '.' + Ext.baseCSSPrefix + 'collapse-el'
        });

        this.callParent(arguments);

        
        if (me.performCollapse !== false) {
            if (me.renderData.collapsible) {
                me.mon(me.collapseEl, 'click', me.toggleTargetCmp, me);
            }
            if (me.collapseOnDblClick) {
                me.mon(me.el, 'dblclick', me.toggleTargetCmp, me);
            }
        }

        
        me.mon(target, 'collapse', me.onTargetCollapse, me);
        me.mon(target, 'expand', me.onTargetExpand, me);

        me.el.addCls(me.baseCls + '-' + me.orientation);
        me.el.unselectable();

        me.tracker = Ext.create('Ext.resizer.SplitterTracker', {
            el: me.el
        });

        
        me.relayEvents(me.tracker, [ 'beforedragstart', 'dragstart', 'dragend' ]);
    },

    getCollapseDirection: function() {
        var me = this,
            idx,
            type = me.ownerCt.layout.type;

        
        
        
        
        
        
        
        if (me.collapseTarget.isComponent) {
            idx = Number(me.ownerCt.items.indexOf(me.collapseTarget) == me.ownerCt.items.indexOf(me) - 1) << 1 | Number(type == 'hbox');
        } else {
            idx = Number(me.collapseTarget == 'prev') << 1 | Number(type == 'hbox');
        }

        
        me.orientation = ['horizontal', 'vertical'][idx & 1];
        return ['bottom', 'right', 'top', 'left'][idx];
    },

    getCollapseTarget: function() {
        return this.collapseTarget.isComponent ? this.collapseTarget : this.collapseTarget == 'prev' ? this.previousSibling() : this.nextSibling();
    },

    onTargetCollapse: function(target) {
        this.el.addCls(this.collapsedCls);
    },

    onTargetExpand: function(target) {
        this.el.removeCls(this.collapsedCls);
    },

    toggleTargetCmp: function(e, t) {
        var cmp = this.getCollapseTarget();

        if (cmp.isVisible()) {
            
            if (cmp.collapsed) {
                cmp.expand(cmp.animCollapse);
            
            } else {
                cmp.collapse(this.renderData.collapseDir, cmp.animCollapse);
            }
        }
    },

    
    setSize: function() {
        var me = this;
        me.callParent(arguments);
        if (Ext.isIE) {
            me.el.repaint();
        }
    }
});


Ext.define('Ext.layout.container.Border', {

    alias: ['layout.border'],
    extend: 'Ext.layout.container.Container',
    requires: ['Ext.resizer.Splitter', 'Ext.container.Container', 'Ext.fx.Anim'],
    alternateClassName: 'Ext.layout.BorderLayout',

    targetCls: Ext.baseCSSPrefix + 'border-layout-ct',

    itemCls: Ext.baseCSSPrefix + 'border-item',

    bindToOwnerCtContainer: true,

    fixedLayout: false,

    percentageRe: /(\d+)%/,

    slideDirection: {
        north: 't',
        south: 'b',
        west: 'l',
        east: 'r'
    },

    constructor: function(config) {
        this.initialConfig = config;
        this.callParent(arguments);
    },

    onLayout: function() {
        var me = this;
        if (!me.borderLayoutInitialized) {
            me.initializeBorderLayout();
        }

        
        me.fixHeightConstraints();
        me.shadowLayout.onLayout();
        if (me.embeddedContainer) {
            me.embeddedContainer.layout.onLayout();
        }

        
        
        if (!me.initialCollapsedComplete) {
            Ext.iterate(me.regions, function(name, region){
                if (region.borderCollapse) {
                    me.onBeforeRegionCollapse(region, region.collapseDirection, false, 0);
                }
            });
            me.initialCollapsedComplete = true;
        }
    },

    isValidParent : function(item, target, position) {
        if (!this.borderLayoutInitialized) {
            this.initializeBorderLayout();
        }

        
        return this.shadowLayout.isValidParent(item, target, position);
    },

    beforeLayout: function() {
        if (!this.borderLayoutInitialized) {
            this.initializeBorderLayout();
        }

        
        this.shadowLayout.beforeLayout();
    },

    renderItems: function(items, target) {
        Ext.Error.raise('This should not be called');
    },

    renderItem: function(item) {
        Ext.Error.raise('This should not be called');
    },

    initializeBorderLayout: function() {
        var me = this,
            i = 0,
            items = me.getLayoutItems(),
            ln = items.length,
            regions = (me.regions = {}),
            vBoxItems = [],
            hBoxItems = [],
            horizontalFlex = 0,
            verticalFlex = 0,
            comp, percentage;

        
        me.splitters = {};

        
        for (; i < ln; i++) {
            comp = items[i];
            regions[comp.region] = comp;

            
            if (comp.region != 'center' && comp.collapsible && comp.collapseMode != 'header') {

                
                comp.borderCollapse = comp.collapsed;
                delete comp.collapsed;

                comp.on({
                    beforecollapse: me.onBeforeRegionCollapse,
                    beforeexpand: me.onBeforeRegionExpand,
                    destroy: me.onRegionDestroy,
                    scope: me
                });
                me.setupState(comp);
            }
        }
        if (!regions.center) {
            Ext.Error.raise("You must specify a center region when defining a BorderLayout.");
        }
        comp = regions.center;
        if (!comp.flex) {
            comp.flex = 1;
        }
        delete comp.width;
        comp.maintainFlex = true;

        
        comp = regions.west;
        if (comp) {
            comp.collapseDirection = Ext.Component.DIRECTION_LEFT;
            hBoxItems.push(comp);
            if (comp.split) {
                hBoxItems.push(me.splitters.west = me.createSplitter(comp));
            }
            percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
            if (percentage) {
                horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
                delete comp.width;
            }
        }
        comp = regions.north;
        if (comp) {
            comp.collapseDirection = Ext.Component.DIRECTION_TOP;
            vBoxItems.push(comp);
            if (comp.split) {
                vBoxItems.push(me.splitters.north = me.createSplitter(comp));
            }
            percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
            if (percentage) {
                verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
                delete comp.height;
            }
        }

        
        if (regions.north || regions.south) {
            if (regions.east || regions.west) {

                
                vBoxItems.push(me.embeddedContainer = Ext.create('Ext.container.Container', {
                    xtype: 'container',
                    region: 'center',
                    id: me.owner.id + '-embedded-center',
                    cls: Ext.baseCSSPrefix + 'border-item',
                    flex: regions.center.flex,
                    maintainFlex: true,
                    layout: {
                        type: 'hbox',
                        align: 'stretch'
                    }
                }));
                hBoxItems.push(regions.center);
            }
            
            else {
                vBoxItems.push(regions.center);
            }
        }
        
        else {
            hBoxItems.push(regions.center);
        }

        
        comp = regions.south;
        if (comp) {
            comp.collapseDirection = Ext.Component.DIRECTION_BOTTOM;
            if (comp.split) {
                vBoxItems.push(me.splitters.south = me.createSplitter(comp));
            }
            percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
            if (percentage) {
                verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
                delete comp.height;
            }
            vBoxItems.push(comp);
        }
        comp = regions.east;
        if (comp) {
            comp.collapseDirection = Ext.Component.DIRECTION_RIGHT;
            if (comp.split) {
                hBoxItems.push(me.splitters.east = me.createSplitter(comp));
            }
            percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
            if (percentage) {
                horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
                delete comp.width;
            }
            hBoxItems.push(comp);
        }

        
        
        
        
        if (regions.north || regions.south) {

            me.shadowContainer = Ext.create('Ext.container.Container', {
                ownerCt: me.owner,
                el: me.getTarget(),
                layout: Ext.applyIf({
                    type: 'vbox',
                    align: 'stretch'
                }, me.initialConfig)
            });
            me.createItems(me.shadowContainer, vBoxItems);

            
            if (me.splitters.north) {
                me.splitters.north.ownerCt = me.shadowContainer;
            }
            if (me.splitters.south) {
                me.splitters.south.ownerCt = me.shadowContainer;
            }

            
            if (me.embeddedContainer) {
                me.embeddedContainer.ownerCt = me.shadowContainer;
                me.createItems(me.embeddedContainer, hBoxItems);

                
                if (me.splitters.east) {
                    me.splitters.east.ownerCt = me.embeddedContainer;
                }
                if (me.splitters.west) {
                    me.splitters.west.ownerCt = me.embeddedContainer;
                }

                
                
                
                
                
                Ext.each([me.splitters.north, me.splitters.south], function (splitter) {
                    if (splitter) {
                        splitter.on('beforedragstart', me.fixHeightConstraints, me);
                    }
                });

                
                if (horizontalFlex) {
                    regions.center.flex -= horizontalFlex;
                }
                
                if (verticalFlex) {
                    me.embeddedContainer.flex -= verticalFlex;
                }
            } else {
                
                if (verticalFlex) {
                    regions.center.flex -= verticalFlex;
                }
            }
        }
        
        
        else {
            me.shadowContainer = Ext.create('Ext.container.Container', {
                ownerCt: me.owner,
                el: me.getTarget(),
                layout: Ext.applyIf({
                    type: (hBoxItems.length == 1) ? 'fit' : 'hbox',
                    align: 'stretch'
                }, me.initialConfig)
            });
            me.createItems(me.shadowContainer, hBoxItems);

            
            if (me.splitters.east) {
                me.splitters.east.ownerCt = me.shadowContainer;
            }
            if (me.splitters.west) {
                me.splitters.west.ownerCt = me.shadowContainer;
            }

            
            if (horizontalFlex) {
                regions.center.flex -= verticalFlex;
            }
        }

        
        for (i = 0, items = me.shadowContainer.items.items, ln = items.length; i < ln; i++) {
            items[i].shadowOwnerCt = me.shadowContainer;
        }
        if (me.embeddedContainer) {
            for (i = 0, items = me.embeddedContainer.items.items, ln = items.length; i < ln; i++) {
                items[i].shadowOwnerCt = me.embeddedContainer;
            }
        }

        
        me.shadowLayout = me.shadowContainer.getLayout();

        me.borderLayoutInitialized = true;
    },

    setupState: function(comp){
        var getState = comp.getState;
        comp.getState = function(){
            
            var state = getState.call(comp) || {},
                region = comp.region;

            state.collapsed = !!comp.collapsed;
            if (region == 'west' || region == 'east') {
                state.width = comp.getWidth();
            } else {
                state.height = comp.getHeight();
            }
            return state;
        };
        comp.addStateEvents(['collapse', 'expand', 'resize']);
    },

    
    createItems: function(container, items){
        
        
        delete container.items;
        container.initItems();
        container.items.addAll(items);
    },

    
    
    createSplitter: function(comp) {
        var me = this,
            interceptCollapse = (comp.collapseMode != 'header'),
            resizer;

        resizer = Ext.create('Ext.resizer.Splitter', {
            hidden: !!comp.hidden,
            collapseTarget: comp,
            performCollapse: !interceptCollapse,
            listeners: interceptCollapse ? {
                click: {
                    fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
                    element: 'collapseEl'
                }
            } : null
        });

        
        if (comp.collapseMode == 'mini') {
            comp.placeholder = resizer;
        }

        
        comp.on({
            hide: me.onRegionVisibilityChange,
            show: me.onRegionVisibilityChange,
            scope: me
        });
        return resizer;
    },

    
    
    fixHeightConstraints: function () {
        var me = this,
            ct = me.embeddedContainer,
            maxHeight = 1e99, minHeight = -1;

        if (!ct) {
            return;
        }

        ct.items.each(function (item) {
            if (Ext.isNumber(item.maxHeight)) {
                maxHeight = Math.max(maxHeight, item.maxHeight);
            }
            if (Ext.isNumber(item.minHeight)) {
                minHeight = Math.max(minHeight, item.minHeight);
            }
        });

        ct.maxHeight = maxHeight;
        ct.minHeight = minHeight;
    },

    
    onRegionVisibilityChange: function(comp){
        this.splitters[comp.region][comp.hidden ? 'hide' : 'show']();
        this.layout();
    },

    
    
    
    onSplitterCollapseClick: function(comp) {
        if (comp.collapsed) {
            this.onPlaceHolderToolClick(null, null, null, {client: comp});
        } else {
            comp.collapse();
        }
    },

    
    getPlaceholder: function(comp) {
        var me = this,
            placeholder = comp.placeholder,
            shadowContainer = comp.shadowOwnerCt,
            shadowLayout = shadowContainer.layout,
            oppositeDirection = Ext.panel.Panel.prototype.getOppositeDirection(comp.collapseDirection),
            horiz = (comp.region == 'north' || comp.region == 'south');

        
        if (comp.collapseMode == 'header') {
            return;
        }

        
        if (!placeholder) {
            if (comp.collapseMode == 'mini') {
                placeholder = Ext.create('Ext.resizer.Splitter', {
                    id: 'collapse-placeholder-' + comp.id,
                    collapseTarget: comp,
                    performCollapse: false,
                    listeners: {
                        click: {
                            fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
                            element: 'collapseEl'
                        }
                    }
                });
                placeholder.addCls(placeholder.collapsedCls);
            } else {
                placeholder = {
                    id: 'collapse-placeholder-' + comp.id,
                    margins: comp.initialConfig.margins || Ext.getClass(comp).prototype.margins,
                    xtype: 'header',
                    orientation: horiz ? 'horizontal' : 'vertical',
                    title: comp.title,
                    textCls: comp.headerTextCls,
                    iconCls: comp.iconCls,
                    baseCls: comp.baseCls + '-header',
                    ui: comp.ui,
                    indicateDrag: comp.draggable,
                    cls: Ext.baseCSSPrefix + 'region-collapsed-placeholder ' + Ext.baseCSSPrefix + 'region-collapsed-' + comp.collapseDirection + '-placeholder',
                    listeners: comp.floatable ? {
                        click: {
                            fn: function(e) {
                                me.floatCollapsedPanel(e, comp);
                            },
                            element: 'el'
                        }
                    } : null
                };
                
                if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) && !horiz) {
                    placeholder.width = 25;
                }
                if (!comp.hideCollapseTool) {
                    placeholder[horiz ? 'tools' : 'items'] = [{
                        xtype: 'tool',
                        client: comp,
                        type: 'expand-' + oppositeDirection,
                        handler: me.onPlaceHolderToolClick,
                        scope: me
                    }];
                }
            }
            placeholder = me.owner.createComponent(placeholder);
            if (comp.isXType('panel')) {
                comp.on({
                    titlechange: me.onRegionTitleChange,
                    iconchange: me.onRegionIconChange,
                    scope: me
                });
            }
        }

        
        comp.placeholder = placeholder;
        placeholder.comp = comp;

        return placeholder;
    },

    
    onRegionTitleChange: function(comp, newTitle) {
        comp.placeholder.setTitle(newTitle);
    },

    
    onRegionIconChange: function(comp, newIconCls) {
        comp.placeholder.setIconCls(newIconCls);
    },

    
    calculateChildBox: function(comp) {
        var me = this;
        if (me.shadowContainer.items.contains(comp)) {
            return me.shadowContainer.layout.calculateChildBox(comp);
        }
        else if (me.embeddedContainer && me.embeddedContainer.items.contains(comp)) {
            return me.embeddedContainer.layout.calculateChildBox(comp);
        }
    },

    
    onBeforeRegionCollapse: function(comp, direction, animate) {
        var me = this,
            compEl = comp.el,
            width,
            miniCollapse = comp.collapseMode == 'mini',
            shadowContainer = comp.shadowOwnerCt,
            shadowLayout = shadowContainer.layout,
            placeholder = comp.placeholder,
            sl = me.owner.suspendLayout,
            scsl = shadowContainer.suspendLayout,
            isNorthOrWest = (comp.region == 'north' || comp.region == 'west'); 

        
        me.owner.suspendLayout = true;
        shadowContainer.suspendLayout = true;

        
        shadowLayout.layoutBusy = true;
        if (shadowContainer.componentLayout) {
            shadowContainer.componentLayout.layoutBusy = true;
        }
        me.shadowContainer.layout.layoutBusy = true;
        me.layoutBusy = true;
        me.owner.componentLayout.layoutBusy = true;

        
        if (!placeholder) {
            placeholder = me.getPlaceholder(comp);
        }

        
        if (placeholder.shadowOwnerCt === shadowContainer) {
            placeholder.show();
        }
        
        
        
        else {
            shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
            placeholder.shadowOwnerCt = shadowContainer;
            placeholder.ownerCt = me.owner;
        }

        
        
        
        comp.hidden = true;

        if (!placeholder.rendered) {
            shadowLayout.renderItem(placeholder, shadowLayout.innerCt);

            
            
            
            
            
            if (comp.region == 'north' || comp.region == 'south') {
                placeholder.setCalculatedSize(comp.getWidth());
            } else {
                placeholder.setCalculatedSize(undefined, comp.getHeight());
            }
        }

        
        function afterCollapse() {
            
            me.owner.suspendLayout = sl;
            shadowContainer.suspendLayout = scsl;
            delete shadowLayout.layoutBusy;
            if (shadowContainer.componentLayout) {
                delete shadowContainer.componentLayout.layoutBusy;
            }
            delete me.shadowContainer.layout.layoutBusy;
            delete me.layoutBusy;
            delete me.owner.componentLayout.layoutBusy;

            
            comp.collapsed = true;
            comp.fireEvent('collapse', comp);
        }

        
        if (comp.animCollapse && me.initialCollapsedComplete) {
            shadowLayout.layout();
            compEl.dom.style.zIndex = 100;

            
            if (!miniCollapse) {
                placeholder.el.hide();
            }
            compEl.slideOut(me.slideDirection[comp.region], {
                duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
                listeners: {
                    afteranimate: function() {
                        compEl.show().setLeftTop(-10000, -10000);
                        compEl.dom.style.zIndex = '';

                        
                       if (!miniCollapse) {
                            placeholder.el.slideIn(me.slideDirection[comp.region], {
                                easing: 'linear',
                                duration: 100
                            });
                        }
                        afterCollapse();
                    }
                }
            });
        } else {
            compEl.setLeftTop(-10000, -10000);
            shadowLayout.layout();
            afterCollapse();
        }

        return false;
    },

    
    onBeforeRegionExpand: function(comp, animate) {
        this.onPlaceHolderToolClick(null, null, null, {client: comp});
        return false;
    },

    
    onPlaceHolderToolClick: function(e, target, owner, tool) {
        var me = this,
            comp = tool.client,

            
            hidePlaceholder = (comp.collapseMode != 'mini') || !comp.split,
            compEl = comp.el,
            toCompBox,
            placeholder = comp.placeholder,
            placeholderEl = placeholder.el,
            shadowContainer = comp.shadowOwnerCt,
            shadowLayout = shadowContainer.layout,
            curSize,
            sl = me.owner.suspendLayout,
            scsl = shadowContainer.suspendLayout,
            isFloating;

        
        
        
        if (comp.getActiveAnimation()) {
            comp.stopAnimation();
        }

        
        
        
        if (comp.slideOutAnim) {
            
            compEl.un(comp.panelMouseMon);
            placeholderEl.un(comp.placeholderMouseMon);

            delete comp.slideOutAnim;
            delete comp.panelMouseMon;
            delete comp.placeholderMouseMon;

            
            isFloating = true;
        }

        
        me.owner.suspendLayout = true;
        shadowContainer.suspendLayout = true;

        
        shadowLayout.layoutBusy = true;
        if (shadowContainer.componentLayout) {
            shadowContainer.componentLayout.layoutBusy = true;
        }
        me.shadowContainer.layout.layoutBusy = true;
        me.layoutBusy = true;
        me.owner.componentLayout.layoutBusy = true;

        
        
        comp.hidden = false;
        comp.collapsed = false;
        if (hidePlaceholder) {
            placeholder.hidden = true;
        }
        toCompBox = shadowLayout.calculateChildBox(comp);

        
        if (comp.collapseTool) {
            comp.collapseTool.show();
        }

        
        if (comp.animCollapse && !isFloating) {
            compEl.setStyle('visibility', 'hidden');
        }
        compEl.setLeftTop(toCompBox.left, toCompBox.top);

        
        
        curSize = comp.getSize();
        if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
            me.setItemSize(comp, toCompBox.width, toCompBox.height);
        }

        
        function afterExpand() {
            
            me.owner.suspendLayout = sl;
            shadowContainer.suspendLayout = scsl;
            delete shadowLayout.layoutBusy;
            if (shadowContainer.componentLayout) {
                delete shadowContainer.componentLayout.layoutBusy;
            }
            delete me.shadowContainer.layout.layoutBusy;
            delete me.layoutBusy;
            delete me.owner.componentLayout.layoutBusy;

            
            comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');

            
            comp.fireEvent('expand', comp);
        }

        
        if (hidePlaceholder) {
            placeholder.el.hide();
        }

        
        
        if (comp.animCollapse && !isFloating) {
            compEl.dom.style.zIndex = 100;
            compEl.slideIn(me.slideDirection[comp.region], {
                duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
                listeners: {
                    afteranimate: function() {
                        compEl.dom.style.zIndex = '';
                        comp.hidden = false;
                        shadowLayout.onLayout();
                        afterExpand();
                    }
                }
            });
        } else {
            shadowLayout.onLayout();
            afterExpand();
        }
    },

    floatCollapsedPanel: function(e, comp) {

        if (comp.floatable === false) {
            return;
        }

        var me = this,
            compEl = comp.el,
            placeholder = comp.placeholder,
            placeholderEl = placeholder.el,
            shadowContainer = comp.shadowOwnerCt,
            shadowLayout = shadowContainer.layout,
            placeholderBox = shadowLayout.getChildBox(placeholder),
            scsl = shadowContainer.suspendLayout,
            curSize, toCompBox, compAnim;

        
        if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
            return;
        }

        
        
        if (compEl.getActiveAnimation()) {
            return;
        }

        
        
        if (comp.slideOutAnim) {
            me.slideOutFloatedComponent(comp);
            return;
        }

        
        
        function onMouseLeaveFloated(e) {
            var slideRegion = compEl.getRegion().union(placeholderEl.getRegion()).adjust(1, -1, -1, 1);

            
            if (!slideRegion.contains(e.getPoint())) {
                me.slideOutFloatedComponent(comp);
            }
        }

        
        comp.placeholderMouseMon = placeholderEl.monitorMouseLeave(500, onMouseLeaveFloated);

        
        shadowContainer.suspendLayout = true;

        
        me.layoutBusy = true;
        me.owner.componentLayout.layoutBusy = true;

        
        
        if (comp.collapseTool) {
            comp.collapseTool.hide();
        }

        
        comp.hidden = false;
        comp.collapsed = false;
        placeholder.hidden = true;

        
        toCompBox = shadowLayout.calculateChildBox(comp);
        placeholder.hidden = false;

        
        if (comp.region == 'north' || comp.region == 'west') {
            toCompBox[shadowLayout.parallelBefore] += placeholderBox[shadowLayout.parallelPrefix] - 1;
        } else {
            toCompBox[shadowLayout.parallelBefore] -= (placeholderBox[shadowLayout.parallelPrefix] - 1);
        }
        compEl.setStyle('visibility', 'hidden');
        compEl.setLeftTop(toCompBox.left, toCompBox.top);

        
        
        curSize = comp.getSize();
        if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
            me.setItemSize(comp, toCompBox.width, toCompBox.height);
        }

        
        compAnim = {
            listeners: {
                afteranimate: function() {
                    shadowContainer.suspendLayout = scsl;
                    delete me.layoutBusy;
                    delete me.owner.componentLayout.layoutBusy;

                    
                    compAnim.listeners = {
                        afterAnimate: function() {
                            compEl.show().removeCls(Ext.baseCSSPrefix + 'border-region-slide-in').setLeftTop(-10000, -10000);

                            
                            comp.hidden = true;
                            comp.collapsed = true;
                            delete comp.slideOutAnim;
                            delete comp.panelMouseMon;
                            delete comp.placeholderMouseMon;
                        }
                    };
                    comp.slideOutAnim = compAnim;
                }
            },
            duration: 500
        };

        
        compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');

        
        compEl.slideIn(me.slideDirection[comp.region], compAnim);

        
        comp.panelMouseMon = compEl.monitorMouseLeave(500, onMouseLeaveFloated);

    },

    slideOutFloatedComponent: function(comp) {
        var compEl = comp.el,
            slideOutAnim;

        
        compEl.un(comp.panelMouseMon);
        comp.placeholder.el.un(comp.placeholderMouseMon);

        
        compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);

        delete comp.slideOutAnim;
        delete comp.panelMouseMon;
        delete comp.placeholderMouseMon;
    },

    
    onRegionDestroy: function(comp) {
        var placeholder = comp.placeholder;
        if (placeholder) {
            delete placeholder.ownerCt;
            placeholder.destroy();
        }
    },

    
    onDestroy: function() {
        var me = this,
            shadowContainer = me.shadowContainer,
            embeddedContainer = me.embeddedContainer;

        if (shadowContainer) {
            delete shadowContainer.ownerCt;
            Ext.destroy(shadowContainer);
        }

        if (embeddedContainer) {
            delete embeddedContainer.ownerCt;
            Ext.destroy(embeddedContainer);
        }
        delete me.regions;
        delete me.splitters;
        delete me.shadowContainer;
        delete me.embeddedContainer;
        me.callParent(arguments);
    }
});


Ext.define('Ext.layout.container.Card', {

    

    alias: ['layout.card'],
    alternateClassName: 'Ext.layout.CardLayout',

    extend: 'Ext.layout.container.AbstractCard',

    

    setActiveItem: function(newCard) {
        var me = this,
            owner = me.owner,
            oldCard = me.activeItem,
            newIndex;

        
        me.layoutBusy = true;

        newCard = me.parseActiveItem(newCard);
        newIndex = owner.items.indexOf(newCard);

        
        if (newIndex == -1) {
            newIndex = owner.items.items.length;
            owner.add(newCard);
        }

        
        if (newCard && oldCard != newCard) {
            
            if (!newCard.rendered) {
                me.renderItem(newCard, me.getRenderTarget(), owner.items.length);
                me.configureItem(newCard, 0);
            }

            me.activeItem = newCard;

            
            if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) {
                me.layoutBusy = false;
                return false;
            }
            if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) {
                me.layoutBusy = false;
                return false;
            }

            
            if (!me.sizeAllCards) {
                me.setItemBox(newCard, me.getTargetBox());
            }
            else {
                
                me.onLayout();
            }

            if (oldCard) {
                if (me.hideInactive) {
                    oldCard.hide();
                }
                oldCard.fireEvent('deactivate', oldCard, newCard);
            }

            
            if (newCard.hidden) {
                newCard.show();
            }

            newCard.fireEvent('activate', newCard, oldCard);

            me.layoutBusy = false;

            if (!me.sizeAllCards) {
                if (!owner.componentLayout.layoutBusy) {
                    me.onLayout();
                }
            }
            return newCard;
        }

        me.layoutBusy = false;
        return false;
    }
});

Ext.define('Ext.layout.container.Column', {

    extend: 'Ext.layout.container.Auto',
    alias: ['layout.column'],
    alternateClassName: 'Ext.layout.ColumnLayout',

    type: 'column',

    itemCls: Ext.baseCSSPrefix + 'column',

    targetCls: Ext.baseCSSPrefix + 'column-layout-ct',

    scrollOffset: 0,

    bindToOwnerCtComponent: false,

    getRenderTarget : function() {
        if (!this.innerCt) {

            
            
            this.innerCt = this.getTarget().createChild({
                cls: Ext.baseCSSPrefix + 'column-inner'
            });

            
            
            
            this.clearEl = this.innerCt.createChild({
                cls: Ext.baseCSSPrefix + 'clear',
                role: 'presentation'
            });
        }
        return this.innerCt;
    },

    
    onLayout : function() {
        var me = this,
            target = me.getTarget(),
            items = me.getLayoutItems(),
            len = items.length,
            item,
            i,
            parallelMargins = [],
            itemParallelMargins,
            size,
            availableWidth,
            columnWidth;

        size = me.getLayoutTargetSize();
        if (size.width < len * 10) { 
            return;
        }

        
        
        
        if (me.adjustmentPass) {
            if (Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks) {
                size.width = me.adjustedWidth;
            }
        } else {
            i = target.getStyle('overflow');
            if (i && i != 'hidden') {
                me.autoScroll = true;
                if (!(Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks)) {
                    target.setStyle('overflow', 'hidden');
                    size = me.getLayoutTargetSize();
                }
            }
        }

        availableWidth = size.width - me.scrollOffset;
        me.innerCt.setWidth(availableWidth);

        
        
        for (i = 0; i < len; i++) {
            item = items[i];
            itemParallelMargins = parallelMargins[i] = item.getEl().getMargin('lr');
            if (!item.columnWidth) {
                availableWidth -= (item.getWidth() + itemParallelMargins);
            }
        }

        availableWidth = availableWidth < 0 ? 0 : availableWidth;
        for (i = 0; i < len; i++) {
            item = items[i];
            if (item.columnWidth) {
                columnWidth = Math.floor(item.columnWidth * availableWidth) - parallelMargins[i];
                if (item.getWidth() != columnWidth) {
                    me.setItemSize(item, columnWidth, item.height);
                }
            }
        }

        
        if (!me.adjustmentPass && me.autoScroll) {

            
            target.setStyle('overflow', 'auto');
            me.adjustmentPass = (target.dom.scrollHeight > size.height);
            if (Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks) {
                me.adjustedWidth = size.width - Ext.getScrollBarWidth();
            } else {
                target.setStyle('overflow', 'auto');
            }

            
            if (me.adjustmentPass) {
                me.onLayout();
            }
        }
        delete me.adjustmentPass;
    }
});


Ext.define('Ext.layout.container.Table', {

    

    alias: ['layout.table'],
    extend: 'Ext.layout.container.Auto',
    alternateClassName: 'Ext.layout.TableLayout',

    

    

    
    monitorResize:false,

    type: 'table',

    
    
    autoSize: true,

    clearEl: true, 

    targetCls: Ext.baseCSSPrefix + 'table-layout-ct',
    tableCls: Ext.baseCSSPrefix + 'table-layout',
    cellCls: Ext.baseCSSPrefix + 'table-layout-cell',

    
    tableAttrs:null,

    
    renderItems: function(items) {
        var tbody = this.getTable().tBodies[0],
            rows = tbody.rows,
            i = 0,
            len = items.length,
            cells, curCell, rowIdx, cellIdx, item, trEl, tdEl, itemCt;

        
        cells = this.calculateCells(items);

        
        
        
        for (; i < len; i++) {
            curCell = cells[i];
            rowIdx = curCell.rowIdx;
            cellIdx = curCell.cellIdx;
            item = items[i];

            
            trEl = rows[rowIdx];
            if (!trEl) {
                trEl = tbody.insertRow(rowIdx);
            }

            
            itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx));
            if (this.needsDivWrap()) { 
                itemCt = tdEl.first() || tdEl.createChild({tag: 'div'});
                itemCt.setWidth(null);
            }

            
            if (!item.rendered) {
                this.renderItem(item, itemCt, 0);
            }
            else if (!this.isValidParent(item, itemCt, 0)) {
                this.moveItem(item, itemCt, 0);
            }

            
            tdEl.set({
                colSpan: item.colspan || 1,
                rowSpan: item.rowspan || 1,
                id: item.cellId || '',
                cls: this.cellCls + ' ' + (item.cellCls || '')
            });

            
            if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) {
                cellIdx++;
                while (trEl.cells[cellIdx]) {
                    trEl.deleteCell(cellIdx);
                }
            }
        }

        
        rowIdx++;
        while (tbody.rows[rowIdx]) {
            tbody.deleteRow(rowIdx);
        }
    },

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

        if (this.needsDivWrap()) {
            
            Ext.Array.forEach(this.getLayoutItems(), function(item) {
                Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth());
            });
        }
    },

    
    calculateCells: function(items) {
        var cells = [],
            rowIdx = 0,
            colIdx = 0,
            cellIdx = 0,
            totalCols = this.columns || Infinity,
            rowspans = [], 
            i = 0, j,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];

            
            while (colIdx >= totalCols || rowspans[colIdx] > 0) {
                if (colIdx >= totalCols) {
                    
                    colIdx = 0;
                    cellIdx = 0;
                    rowIdx++;

                    
                    for (j = 0; j < totalCols; j++) {
                        if (rowspans[j] > 0) {
                            rowspans[j]--;
                        }
                    }
                } else {
                    colIdx++;
                }
            }

            
            cells.push({
                rowIdx: rowIdx,
                cellIdx: cellIdx
            });

            
            rowspans[colIdx] = item.rowspan || 1;
            colIdx += item.colspan || 1;
            cellIdx++;
        }

        return cells;
    },

    
    getTable: function() {
        var table = this.table;
        if (!table) {
            table = this.table = this.getTarget().createChild(
                Ext.apply({
                    tag: 'table',
                    role: 'presentation',
                    cls: this.tableCls,
                    cellspacing: 0, 
                    cn: {tag: 'tbody'}
                }, this.tableAttrs),
                null, true
            );
        }
        return table;
    },

    
    needsDivWrap: function() {
        return Ext.isOpera10_5;
    }
});

Ext.define('Ext.menu.Item', {
    extend: 'Ext.Component',
    alias: 'widget.menuitem',
    alternateClassName: 'Ext.menu.TextItem',
    
    

    
    activeCls: Ext.baseCSSPrefix + 'menu-item-active',
    
    
    ariaRole: 'menuitem',
    
    
    canActivate: true,
    
    
    clickHideDelay: 1,
    
    
    destroyMenu: true,
    
    
    disabledCls: Ext.baseCSSPrefix + 'menu-item-disabled',
    
    
     
     
    
    
    hideOnClick: true,
    
    
     
    
    
    isMenuItem: true,
    
    
    
    
    menuAlign: 'tl-tr?',
    
    
    menuExpandDelay: 200,
    
    
    menuHideDelay: 200,
    
    
    
    renderTpl: [
        '<tpl if="plain">',
            '{text}',
        '</tpl>',
        '<tpl if="!plain">',
            '<a class="' + Ext.baseCSSPrefix + 'menu-item-link" href="{href}" <tpl if="hrefTarget">target="{hrefTarget}"</tpl> hidefocus="true" unselectable="on">',
                '<img src="{icon}" class="' + Ext.baseCSSPrefix + 'menu-item-icon {iconCls}" />',
                '<span class="' + Ext.baseCSSPrefix + 'menu-item-text" <tpl if="menu">style="margin-right: 17px;"</tpl> >{text}</span>',
                '<tpl if="menu">',
                    '<img src="' + Ext.BLANK_IMAGE_URL + '" class="' + Ext.baseCSSPrefix + 'menu-item-arrow" />',
                '</tpl>',
            '</a>',
        '</tpl>'
    ],
    
    maskOnDisable: false,
    
    
    
    activate: function() {
        var me = this;
        
        if (!me.activated && me.canActivate && me.rendered && !me.isDisabled() && me.isVisible()) {
            me.el.addCls(me.activeCls);
            me.focus();
            me.activated = true;
            me.fireEvent('activate', me);
        }
    },
    
    blur: function() {
        this.$focused = false;
        this.callParent(arguments);
    },
    
    deactivate: function() {
        var me = this;
        
        if (me.activated) {
            me.el.removeCls(me.activeCls);
            me.blur();
            me.hideMenu();
            me.activated = false;
            me.fireEvent('deactivate', me);
        }
    },
    
    deferExpandMenu: function() {
        var me = this;
        
        if (!me.menu.rendered || !me.menu.isVisible()) {
            me.parentMenu.activeChild = me.menu;
            me.menu.parentItem = me;
            me.menu.parentMenu = me.menu.ownerCt = me.parentMenu;
            me.menu.showBy(me, me.menuAlign);
        }
    },
    
    deferHideMenu: function() {
        if (this.menu.isVisible()) {
            this.menu.hide();
        }
    },
    
    deferHideParentMenus: function() {
        Ext.menu.Manager.hideAll();
    },
    
    expandMenu: function(delay) {
        var me = this;
        
        if (me.menu) {
            clearTimeout(me.hideMenuTimer);
            if (delay === 0) {
                me.deferExpandMenu();
            } else {
                me.expandMenuTimer = Ext.defer(me.deferExpandMenu, Ext.isNumber(delay) ? delay : me.menuExpandDelay, me);
            }
        }
    },
    
    focus: function() {
        this.$focused = true;
        this.callParent(arguments);
    },
    
    getRefItems: function(deep){
        var menu = this.menu,
            items;
        
        if (menu) {
            items = menu.getRefItems(deep);
            items.unshift(menu);
        }   
        return items || [];   
    },
    
    hideMenu: function(delay) {
        var me = this;
        
        if (me.menu) {
            clearTimeout(me.expandMenuTimer);
            me.hideMenuTimer = Ext.defer(me.deferHideMenu, Ext.isNumber(delay) ? delay : me.menuHideDelay, me);
        }
    },
    
    initComponent: function() {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls = [prefix + 'menu-item'];
        
        me.addEvents(
            
            'activate',
            
            
            'click',
            
            
            'deactivate'
        );
        
        if (me.plain) {
            cls.push(prefix + 'menu-item-plain');
        }
        
        if (me.cls) {
            cls.push(me.cls);
        }
        
        me.cls = cls.join(' ');
        
        if (me.menu) {
            me.menu = Ext.menu.Manager.get(me.menu);
        }
        
        me.callParent(arguments);
    },
    
    onClick: function(e) {
        var me = this;
        
        if (!me.href) {
            e.stopEvent();
        }
        
        if (me.disabled) {
            return;
        }
        
        if (me.hideOnClick) {
            me.deferHideParentMenusTimer = Ext.defer(me.deferHideParentMenus, me.clickHideDelay, me);
        }
        
        Ext.callback(me.handler, me.scope || me, [me, e]);
        me.fireEvent('click', me, e);
        
        if (!me.hideOnClick) {
            me.focus();
        }
    },
    
    onDestroy: function() {
        var me = this;
        
        clearTimeout(me.expandMenuTimer);
        clearTimeout(me.hideMenuTimer);
        clearTimeout(me.deferHideParentMenusTimer);
        
        if (me.menu) {
            delete me.menu.parentItem;
            delete me.menu.parentMenu;
            delete me.menu.ownerCt;
            if (me.destroyMenu !== false) {
                me.menu.destroy();
            }
        }
        me.callParent(arguments);
    },
    
    onRender: function(ct, pos) {
        var me = this,
            prefix = '.' + Ext.baseCSSPrefix;
        
        Ext.applyIf(me.renderData, {
            href: me.href || '#',
            hrefTarget: me.hrefTarget,
            icon: me.icon || Ext.BLANK_IMAGE_URL,
            iconCls: me.iconCls,
            menu: Ext.isDefined(me.menu),
            plain: me.plain,
            text: me.text
        });
        
        Ext.applyIf(me.renderSelectors, {
            itemEl: prefix + 'menu-item-link',
            iconEl: prefix + 'menu-item-icon',
            textEl: prefix + 'menu-item-text',
            arrowEl: prefix + 'menu-item-arrow'
        });
        
        me.callParent(arguments);
    },
    
    
    setHandler: function(fn, scope) {
        this.handler = fn || null;
        this.scope = scope;
    },
    
    
    setIconCls: function(iconCls) {
        var me = this;
        
        if (me.iconEl) {
            if (me.iconCls) {
                me.iconEl.removeCls(me.iconCls);
            }
            
            if (iconCls) {
                me.iconEl.addCls(iconCls);
            }
        }
        
        me.iconCls = iconCls;
    },
    
    
    setText: function(text) {
        var me = this,
            el = me.textEl || me.el,
            newWidth;
        
        if (text && el) {
            el.update(text);
                
            if (me.textEl) {
                
                newWidth = me.textEl.getWidth() + me.iconEl.getWidth() + 25 + (me.arrowEl ? me.arrowEl.getWidth() : 0);
                if (newWidth > me.itemEl.getWidth()) {
                    me.parentMenu.setWidth(newWidth);
                }
            }
        } else if (el) {
            el.update('');
        }
        
        me.text = text;
    }
});



Ext.define('Ext.menu.CheckItem', {
    extend: 'Ext.menu.Item',
    alias: 'widget.menucheckitem',

    
    checkedCls: Ext.baseCSSPrefix + 'menu-item-checked',
    
    uncheckedCls: Ext.baseCSSPrefix + 'menu-item-unchecked',
    
    groupCls: Ext.baseCSSPrefix + 'menu-group-icon',

    
    hideOnClick: false,

    afterRender: function() {
        var me = this;
        this.callParent();
        me.checked = !me.checked;
        me.setChecked(!me.checked, true);
    },

    initComponent: function() {
        var me = this;
        me.addEvents(
            
            'beforecheckchange',

            
            'checkchange'
        );

        me.callParent(arguments);

        Ext.menu.Manager.registerCheckable(me);

        if (me.group) {
            if (!me.iconCls) {
                me.iconCls = me.groupCls;
            }
            if (me.initialConfig.hideOnClick !== false) {
                me.hideOnClick = true;
            }
        }
    },

    
    disableCheckChange: function() {
        var me = this;

        me.iconEl.addCls(me.disabledCls);
        me.checkChangeDisabled = true;
    },

    
    enableCheckChange: function() {
        var me = this;

        me.iconEl.removeCls(me.disabledCls);
        me.checkChangeDisabled = false;
    },

    onClick: function(e) {
        var me = this;
        if(!me.disabled && !me.checkChangeDisabled && !(me.checked && me.group)) {
            me.setChecked(!me.checked);
        }
        this.callParent([e]);
    },

    onDestroy: function() {
        Ext.menu.Manager.unregisterCheckable(this);
        this.callParent(arguments);
    },

    
    setChecked: function(checked, suppressEvents) {
        var me = this;
        if (me.checked !== checked && (suppressEvents || me.fireEvent('beforecheckchange', me, checked) !== false)) {
            if (me.el) {
                me.el[checked  ? 'addCls' : 'removeCls'](me.checkedCls)[!checked ? 'addCls' : 'removeCls'](me.uncheckedCls);
            }
            me.checked = checked;
            Ext.menu.Manager.onCheckChange(me, checked);
            if (!suppressEvents) {
                Ext.callback(me.checkHandler, me.scope, [me, checked]);
                me.fireEvent('checkchange', me, checked);
            }
        }
    }
});


Ext.define('Ext.menu.KeyNav', {
    extend: 'Ext.util.KeyNav',

    requires: ['Ext.FocusManager'],
    
    constructor: function(menu) {
        var me = this;

        me.menu = menu;
        me.callParent([menu.el, {
            down: me.down,
            enter: me.enter,
            esc: me.escape,
            left: me.left,
            right: me.right,
            space: me.enter,
            tab: me.tab,
            up: me.up
        }]);
    },

    down: function(e) {
        var me = this,
            fi = me.menu.focusedItem;

        if (fi && e.getKey() == Ext.EventObject.DOWN && me.isWhitelisted(fi)) {
            return true;
        }
        me.focusNextItem(1);
    },

    enter: function(e) {
        var menu = this.menu;

        if (menu.activeItem) {
            menu.onClick(e);
        }
    },

    escape: function(e) {
        Ext.menu.Manager.hideAll();
    },

    focusNextItem: function(step) {
        var menu = this.menu,
            items = menu.items,
            focusedItem = menu.focusedItem,
            startIdx = focusedItem ? items.indexOf(focusedItem) : -1,
            idx = startIdx + step;

        while (idx != startIdx) {
            if (idx < 0) {
                idx = items.length - 1;
            } else if (idx >= items.length) {
                idx = 0;
            }

            var item = items.getAt(idx);
            if (menu.canActivateItem(item)) {
                menu.setActiveItem(item);
                break;
            }
            idx += step;
        }
    },

    isWhitelisted: function(item) {
        return Ext.FocusManager.isWhitelisted(item);
    },

    left: function(e) {
        var menu = this.menu,
            fi = menu.focusedItem,
            ai = menu.activeItem;

        if (fi && this.isWhitelisted(fi)) {
            return true;
        }

        menu.hide();
        if (menu.parentMenu) {
            menu.parentMenu.focus();
        }
    },

    right: function(e) {
        var menu = this.menu,
            fi = menu.focusedItem,
            ai = menu.activeItem,
            am;

        if (fi && this.isWhitelisted(fi)) {
            return true;
        }

        if (ai) {
            am = menu.activeItem.menu;
            if (am) {
                ai.expandMenu(0);
                Ext.defer(function() {
                    am.setActiveItem(am.items.getAt(0));
                }, 25);
            }
        }
    },

    tab: function(e) {
        var me = this;

        if (e.shiftKey) {
            me.up(e);
        } else {
            me.down(e);
        }
    },

    up: function(e) {
        var me = this,
            fi = me.menu.focusedItem;

        if (fi && e.getKey() == Ext.EventObject.UP && me.isWhitelisted(fi)) {
            return true;
        }
        me.focusNextItem(-1);
    }
});

Ext.define('Ext.menu.Separator', {
    extend: 'Ext.menu.Item',
    alias: 'widget.menuseparator',
    
    
    
    
    canActivate: false,
    
    
     
    
     
    
     
    focusable: false,
     
    
    
    
    
    
    hideOnClick: false,
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    plain: true,
    
    
    separatorCls: Ext.baseCSSPrefix + 'menu-item-separator',
    
    
    text: '&#160;',
    
    onRender: function(ct, pos) {
        var me = this,
            sepCls = me.separatorCls;
            
        me.cls += ' ' + sepCls;
        
        Ext.applyIf(me.renderSelectors, {
            itemSepEl: '.' + sepCls
        });
        
        me.callParent(arguments);
    }
});

Ext.define('Ext.menu.Menu', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.menu',
    requires: [
        'Ext.layout.container.Fit',
        'Ext.layout.container.VBox',
        'Ext.menu.CheckItem',
        'Ext.menu.Item',
        'Ext.menu.KeyNav',
        'Ext.menu.Manager',
        'Ext.menu.Separator'
    ],

    
    allowOtherMenus: false,

    
    ariaRole: 'menu',

    

    
    defaultAlign: 'tl-bl?',

    
    floating: true,

    
    constrain: false,

    
    hidden: true,

    
    ignoreParentClicks: false,

    isMenu: true,

    

    
    showSeparator : true,

    
    minWidth: 120,

    

    initComponent: function() {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls = [prefix + 'menu'],
            bodyCls = me.bodyCls ? [me.bodyCls] : [];

        me.addEvents(
            
            'click',

            
            'mouseenter',

            
            'mouseleave',

            
            'mouseover'
        );

        Ext.menu.Manager.register(me);

        
        if (me.plain) {
            cls.push(prefix + 'menu-plain');
        }
        me.cls = cls.join(' ');

        
        bodyCls.unshift(prefix + 'menu-body');
        me.bodyCls = bodyCls.join(' ');

        
        
        
        
        me.layout = {
            type: 'vbox',
            align: 'stretchmax',
            autoSize: true,
            clearInnerCtOnLayout: true,
            overflowHandler: 'Scroller'
        };

        
        if (me.floating === false && me.initialConfig.hidden !== true) {
            me.hidden = false;
        }

        me.callParent(arguments);

        me.on('beforeshow', function() {
            var hasItems = !!me.items.length;
            
            
            
            if (hasItems && me.rendered) {
                me.el.setStyle('visibility', null);
            }
            return hasItems;
        });
    },

    afterRender: function(ct) {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            space = '&#160;';

        me.callParent(arguments);

        
        if (me.showSeparator) {
            me.iconSepEl = me.layout.getRenderTarget().insertFirst({
                cls: prefix + 'menu-icon-separator',
                html: space
            });
        }

        me.focusEl = me.el.createChild({
            cls: prefix + 'menu-focus',
            tabIndex: '-1',
            html: space
        });

        me.mon(me.el, {
            click: me.onClick,
            mouseover: me.onMouseOver,
            scope: me
        });
        me.mouseMonitor = me.el.monitorMouseLeave(100, me.onMouseLeave, me);

        if (me.showSeparator && ((!Ext.isStrict && Ext.isIE) || Ext.isIE6)) {
            me.iconSepEl.setHeight(me.el.getHeight());
        }

        me.keyNav = Ext.create('Ext.menu.KeyNav', me);
    },

    afterLayout: function() {
        var me = this;
        me.callParent(arguments);

        
        
        
        
        if ((!Ext.iStrict && Ext.isIE) || Ext.isIE6) {
            var innerCt = me.layout.getRenderTarget(),
                innerCtWidth = 0,
                dis = me.dockedItems,
                l = dis.length,
                i = 0,
                di, clone, newWidth;

            innerCtWidth = innerCt.getWidth();

            newWidth = innerCtWidth + me.body.getBorderWidth('lr') + me.body.getPadding('lr');

            
            me.body.setWidth(newWidth);

            
            for (; i < l, di = dis.getAt(i); i++) {
                if (di.dock == 'left' || di.dock == 'right') {
                    newWidth += di.getWidth();
                }
            }
            me.el.setWidth(newWidth);
        }
    },

    
    canActivateItem: function(item) {
        return item && !item.isDisabled() && item.isVisible() && (item.canActivate || item.getXTypes().indexOf('menuitem') < 0);
    },

    
    deactivateActiveItem: function() {
        var me = this;

        if (me.activeItem) {
            me.activeItem.deactivate();
            if (!me.activeItem.activated) {
                delete me.activeItem;
            }
        }
        if (me.focusedItem) {
            me.focusedItem.blur();
            if (!me.focusedItem.$focused) {
                delete me.focusedItem;
            }
        }
    },

    
    getFocusEl: function() {
        return this.focusEl;
    },

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

    
    getItemFromEvent: function(e) {
        return this.getChildByElement(e.getTarget());
    },

    lookupComponent: function(cmp) {
        var me = this;

        if (Ext.isString(cmp)) {
            cmp = me.lookupItemFromString(cmp);
        } else if (Ext.isObject(cmp)) {
            cmp = me.lookupItemFromObject(cmp);
        }

        
        
        cmp.minWidth = cmp.minWidth || me.minWidth;

        return cmp;
    },

    
    lookupItemFromObject: function(cmp) {
        var me = this,
            prefix = Ext.baseCSSPrefix,
            cls,
            intercept;

        if (!cmp.isComponent) {
            if (!cmp.xtype) {
                cmp = Ext.create('Ext.menu.' + (Ext.isBoolean(cmp.checked) ? 'Check': '') + 'Item', cmp);
            } else {
                cmp = Ext.ComponentManager.create(cmp, cmp.xtype);
            }
        }

        if (cmp.isMenuItem) {
            cmp.parentMenu = me;
        }

        if (!cmp.isMenuItem && !cmp.dock) {
            cls = [prefix + 'menu-item', prefix + 'menu-item-cmp'];
            intercept = Ext.Function.createInterceptor;

            
            cmp.focus = intercept(cmp.focus, function() {
                this.$focused = true;
            }, cmp);
            cmp.blur = intercept(cmp.blur, function() {
                this.$focused = false;
            }, cmp);

            if (!me.plain && (cmp.indent === true || cmp.iconCls === 'no-icon')) {
                cls.push(prefix + 'menu-item-indent');
            }

            if (cmp.rendered) {
                cmp.el.addCls(cls);
            } else {
                cmp.cls = (cmp.cls ? cmp.cls : '') + ' ' + cls.join(' ');
            }
            cmp.isMenuItem = true;
        }
        return cmp;
    },

    
    lookupItemFromString: function(cmp) {
        return (cmp == 'separator' || cmp == '-') ?
            Ext.createWidget('menuseparator')
            : Ext.createWidget('menuitem', {
                canActivate: false,
                hideOnClick: false,
                plain: true,
                text: cmp
            });
    },

    onClick: function(e) {
        var me = this,
            item;

        if (me.disabled) {
            e.stopEvent();
            return;
        }

        if ((e.getTarget() == me.focusEl.dom) || e.within(me.layout.getRenderTarget())) {
            item = me.getItemFromEvent(e) || me.activeItem;

            if (item) {
                if (item.getXTypes().indexOf('menuitem') >= 0) {
                    if (!item.menu || !me.ignoreParentClicks) {
                        item.onClick(e);
                    } else {
                        e.stopEvent();
                    }
                }
            }
            me.fireEvent('click', me, item, e);
        }
    },

    onDestroy: function() {
        var me = this;

        Ext.menu.Manager.unregister(me);
        if (me.rendered) {
            me.el.un(me.mouseMonitor);
            me.keyNav.destroy();
            delete me.keyNav;
        }
        me.callParent(arguments);
    },

    onMouseLeave: function(e) {
        var me = this;

        me.deactivateActiveItem();

        if (me.disabled) {
            return;
        }

        me.fireEvent('mouseleave', me, e);
    },

    onMouseOver: function(e) {
        var me = this,
            fromEl = e.getRelatedTarget(),
            mouseEnter = !me.el.contains(fromEl),
            item = me.getItemFromEvent(e);

        if (mouseEnter && me.parentMenu) {
            me.parentMenu.setActiveItem(me.parentItem);
            me.parentMenu.mouseMonitor.mouseenter();
        }

        if (me.disabled) {
            return;
        }

        if (item) {
            me.setActiveItem(item);
            if (item.activated && item.expandMenu) {
                item.expandMenu();
            }
        }
        if (mouseEnter) {
            me.fireEvent('mouseenter', me, e);
        }
        me.fireEvent('mouseover', me, item, e);
    },

    setActiveItem: function(item) {
        var me = this;

        if (item && (item != me.activeItem && item != me.focusedItem)) {
            me.deactivateActiveItem();
            if (me.canActivateItem(item)) {
                if (item.activate) {
                    item.activate();
                    if (item.activated) {
                        me.activeItem = item;
                        me.focusedItem = item;
                        me.focus();
                    }
                } else {
                    item.focus();
                    me.focusedItem = item;
                }
            }
            item.el.scrollIntoView(me.layout.getRenderTarget());
        }
    },

    
    showBy: function(cmp, pos, off) {
        var me = this,
            xy,
            region;

        if (me.floating && cmp) {
            me.layout.autoSize = true;
            me.show();

            
            cmp = cmp.el || cmp;

            
            xy = me.el.getAlignToXY(cmp, pos || me.defaultAlign, off);
            if (me.floatParent) {
                region = me.floatParent.getTargetEl().getViewRegion();
                xy[0] -= region.x;
                xy[1] -= region.y;
            }
            me.showAt(xy);
        }
        return me;
    },
    
    
    showAt: function(){
        this.callParent(arguments);
        if (this.floating) {
            this.doConstrain();
        }    
    },

    doConstrain : function() {
        var me = this,
            y = me.el.getY(),
            max, full,
            vector,
            returnY = y, normalY, parentEl, scrollTop, viewHeight;

        delete me.height;
        me.setSize();
        full = me.getHeight();
        if (me.floating) {
            parentEl = Ext.fly(me.el.dom.parentNode);
            scrollTop = parentEl.getScroll().top;
            viewHeight = parentEl.getViewSize().height;
            
            
            normalY = y - scrollTop;
            max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
            if (full > viewHeight) {
                max = viewHeight;
                
                returnY = y - normalY;
            } else if (max < full) {
                returnY = y - (full - max);
                max = full;
            }
        }else{
            max = me.getHeight();
        }
        
        if (me.maxHeight){
            max = Math.min(me.maxHeight, max);
        }
        if (full > max && max > 0){
            me.layout.autoSize = false;
            me.setHeight(max);
            if (me.showSeparator){
                me.iconSepEl.setHeight(me.layout.getRenderTarget().dom.scrollHeight);
            }
        }
        vector = me.getConstrainVector();
        if (vector) {
            me.setPosition(me.getPosition()[0] + vector[0]);
        }
        me.el.setY(returnY);
    }
});

 Ext.define('Ext.menu.ColorPicker', {
     extend: 'Ext.menu.Menu',

     alias: 'widget.colormenu',

     requires: [
        'Ext.picker.Color'
     ],

    
    hideOnClick : true,

    
    pickerId : null,

    

    

    

    

    initComponent : function(){
        var me = this;

        Ext.apply(me, {
            plain: true,
            showSeparator: false,
            items: Ext.applyIf({
                cls: Ext.baseCSSPrefix + 'menu-color-item',
                id: me.pickerId,
                xtype: 'colorpicker'
            }, me.initialConfig)
        });

        me.callParent(arguments);

        me.picker = me.down('colorpicker');

        
        me.relayEvents(me.picker, ['select']);

        if (me.hideOnClick) {
            me.on('select', me.hidePickerOnSelect, me);
        }
    },

    
    hidePickerOnSelect: function() {
        Ext.menu.Manager.hideAll();
    }
 });

 Ext.define('Ext.menu.DatePicker', {
     extend: 'Ext.menu.Menu',

     alias: 'widget.datemenu',

     requires: [
        'Ext.picker.Date'
     ],

    
    hideOnClick : true,

    
    pickerId : null,

    

    

    

    

    initComponent : function(){
        var me = this;

        Ext.apply(me, {
            showSeparator: false,
            plain: true,
            items: Ext.applyIf({
                cls: Ext.baseCSSPrefix + 'menu-date-item',
                id: me.pickerId,
                xtype: 'datepicker'
            }, me.initialConfig)
        });

        me.callParent(arguments);

        me.picker = me.down('datepicker');
        
        me.relayEvents(me.picker, ['select']);

        if (me.hideOnClick) {
            me.on('select', me.hidePickerOnSelect, me);
        }
    },

    hidePickerOnSelect: function() {
        Ext.menu.Manager.hideAll();
    }
 });

Ext.define('Ext.panel.Tool', {
    extend: 'Ext.Component',
    requires: ['Ext.tip.QuickTipManager'],
    alias: 'widget.tool',

    baseCls: Ext.baseCSSPrefix + 'tool',
    disabledCls: Ext.baseCSSPrefix + 'tool-disabled',
    toolPressedCls: Ext.baseCSSPrefix + 'tool-pressed',
    toolOverCls: Ext.baseCSSPrefix + 'tool-over',
    ariaRole: 'button',
    renderTpl: ['<img src="{blank}" class="{baseCls}-{type}" role="presentation"/>'],
    
    
    
    
    
    
    
    
    
    
    stopEvent: true,

    initComponent: function() {
        var me = this;
        me.addEvents(
            
            'click'
        );
        
        var types = [
            'close', 
            'collapse', 
            'down', 
            'expand', 
            'gear', 
            'help', 
            'left', 
            'maximize', 
            'minimize', 
            'minus', 
            'move', 
            'next', 
            'pin', 
            'plus', 
            'prev', 
            'print', 
            'refresh', 
            'resize', 
            'restore', 
            'right', 
            'save', 
            'search', 
            'toggle',
            'unpin', 
            'up'
        ];
        
        if (me.id && Ext.Array.indexOf(types, me.id) > -1 && Ext.global.console) {
            Ext.global.console.warn('When specifying a tool you should use the type option, the id can conflict now that tool is a Component');
        }
        
        me.type = me.type || me.id;

        Ext.applyIf(me.renderData, {
            baseCls: me.baseCls,
            blank: Ext.BLANK_IMAGE_URL,
            type: me.type
        });
        me.renderSelectors.toolEl = '.' + me.baseCls + '-' + me.type;
        me.callParent();
    },

    
    afterRender: function() {
        var me = this;
        me.callParent(arguments);
        if (me.qtip) {
            if (Ext.isObject(me.qtip)) {
                Ext.tip.QuickTipManager.register(Ext.apply({
                    target: me.id
                }, me.qtip));
            }
            else {
                me.toolEl.dom.qtip = me.qtip;
            }
        }

        me.mon(me.toolEl, {
            click: me.onClick,
            mousedown: me.onMouseDown,
            mouseover: me.onMouseOver,
            mouseout: me.onMouseOut,
            scope: me
        });
    },

    
    setType: function(type) {
        var me = this;
        
        me.type = type;
        if (me.rendered) {
            me.toolEl.dom.className = me.baseCls + '-' + type;
        }
        return me;
    },

    
    bindTo: function(component) {
        this.owner = component;
    },

    
    onClick: function(e, target) {
        var me = this,
            owner;
            
        if (me.disabled) {
            return false;
        }
        owner = me.owner || me.ownerCt;

        
        me.el.removeCls(me.toolPressedCls);
        me.el.removeCls(me.toolOverCls);

        if (me.stopEvent !== false) {
            e.stopEvent();
        }

        Ext.callback(me.handler, me.scope || me, [e, target, owner, me]);
        me.fireEvent('click', me, e);
        return true;
    },
    
    
    onDestroy: function(){
        if (Ext.isObject(this.tooltip)) {
            Ext.tip.QuickTipManager.unregister(this.id);
        }    
        this.callParent();
    },

    
    onMouseDown: function() {
        if (this.disabled) {
            return false;
        }

        this.el.addCls(this.toolPressedCls);
    },

    
    onMouseOver: function() {
        if (this.disabled) {
            return false;
        }
        this.el.addCls(this.toolOverCls);
    },

    
    onMouseOut: function() {
        this.el.removeCls(this.toolOverCls);
    }
});

Ext.define('Ext.resizer.Handle', {
    extend: 'Ext.Component',
    handleCls: '',
    baseHandleCls: Ext.baseCSSPrefix + 'resizable-handle',
    
    
    region: '',

    onRender: function() {
        this.addCls(
            this.baseHandleCls,
            this.baseHandleCls + '-' + this.region,
            this.handleCls
        );
        this.callParent(arguments);
        this.el.unselectable();
    }
});


Ext.define('Ext.resizer.Resizer', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    uses: ['Ext.resizer.ResizeTracker', 'Ext.Component'],

    alternateClassName: 'Ext.Resizable',

    handleCls: Ext.baseCSSPrefix + 'resizable-handle',
    pinnedCls: Ext.baseCSSPrefix + 'resizable-pinned',
    overCls:   Ext.baseCSSPrefix + 'resizable-over',
    proxyCls:  Ext.baseCSSPrefix + 'resizable-proxy',
    wrapCls:   Ext.baseCSSPrefix + 'resizable-wrap',

    
    dynamic: true,

    
    handles: 's e se',

    
    height : null,

    
    width : null,

    
    minHeight : 20,

    
    minWidth : 20,

    
    maxHeight : 10000,

    
    maxWidth : 10000,

    
    pinned: false,

    
    preserveRatio: false,

    
    transparent: false,

    

    possiblePositions: {
        n:  'north',
        s:  'south',
        e:  'east',
        w:  'west',
        se: 'southeast',
        sw: 'southwest',
        nw: 'northwest',
        ne: 'northeast'
    },

    

    

    constructor: function(config) {
        var me = this,
            target,
            tag,
            handles = me.handles,
            handleCls,
            possibles,
            len,
            i = 0,
            pos;

        this.addEvents(
            
            'beforeresize',
            
            'resizedrag',
            
            'resize'
        );

        if (Ext.isString(config) || Ext.isElement(config) || config.dom) {
            target = config;
            config = arguments[1] || {};
            config.target = target;
        }
        
        me.mixins.observable.constructor.call(me, config);

        
        
        target = me.target;
        if (target) {
            if (target.isComponent) {
                me.el = target.getEl();
                if (target.minWidth) {
                    me.minWidth = target.minWidth;
                }
                if (target.minHeight) {
                    me.minHeight = target.minHeight;
                }
                if (target.maxWidth) {
                    me.maxWidth = target.maxWidth;
                }
                if (target.maxHeight) {
                    me.maxHeight = target.maxHeight;
                }
                if (target.floating) {
                    if (!this.hasOwnProperty('handles')) {
                        this.handles = 'n ne e se s sw w nw';
                    }
                }
            } else {
                me.el = me.target = Ext.get(target);
            }
        }
        
        else {
            me.target = me.el = Ext.get(me.el);
        }

        
        
        
        tag = me.el.dom.tagName;
        if (tag == 'TEXTAREA' || tag == 'IMG') {
            
            me.originalTarget = me.target;
            me.target = me.el = me.el.wrap({
                cls: me.wrapCls,
                id: me.el.id + '-rzwrap'
            });

            
            me.el.setPositioning(me.originalTarget.getPositioning());
            me.originalTarget.clearPositioning();
            var box = me.originalTarget.getBox();
            me.el.setBox(box);
        }

        
        
        me.el.position();
        if (me.pinned) {
            me.el.addCls(me.pinnedCls);
        }

        
        me.resizeTracker = Ext.create('Ext.resizer.ResizeTracker', {
            disabled: me.disabled,
            target: me.target,
            constrainTo: me.constrainTo,
            overCls: me.overCls,
            throttle: me.throttle,
            originalTarget: me.originalTarget,
            delegate: '.' + me.handleCls,
            dynamic: me.dynamic,
            preserveRatio: me.preserveRatio,
            minHeight: me.minHeight,
            maxHeight: me.maxHeight,
            minWidth: me.minWidth,
            maxWidth: me.maxWidth
        });

        
        me.resizeTracker.on('mousedown', me.onBeforeResize, me);
        me.resizeTracker.on('drag', me.onResize, me);
        me.resizeTracker.on('dragend', me.onResizeEnd, me);

        if (me.handles == 'all') {
            me.handles = 'n s e w ne nw se sw';
        }

        handles = me.handles = me.handles.split(/ |\s*?[,;]\s*?/);
        possibles = me.possiblePositions;
        len = handles.length;
        handleCls = me.handleCls + ' ' + (this.target.isComponent ? (me.target.baseCls + '-handle ') : '') + me.handleCls + '-';

        for(; i < len; i++){
            
            if (handles[i] && possibles[handles[i]]) {
                pos = possibles[handles[i]];
                

                me[pos] = Ext.create('Ext.Component', {
                    owner: this,
                    region: pos,
                    cls: handleCls + pos,
                    renderTo: me.el
                });
                me[pos].el.unselectable();
                if (me.transparent) {
                    me[pos].el.setOpacity(0);
                }
            }
        }

        
        if (Ext.isNumber(me.width)) {
            me.width = Ext.Number.constrain(me.width, me.minWidth, me.maxWidth);
        }
        if (Ext.isNumber(me.height)) {
            me.height = Ext.Number.constrain(me.height, me.minHeight, me.maxHeight);
        }

        
        if (me.width != null || me.height != null) {
            if (me.originalTarget) {
                me.originalTarget.setWidth(me.width);
                me.originalTarget.setHeight(me.height);
            }
            me.resizeTo(me.width, me.height);
        }

        me.forceHandlesHeight();
    },

    disable: function() {
        this.resizeTracker.disable();
    },

    enable: function() {
        this.resizeTracker.enable();
    },

    
    onBeforeResize: function(tracker, e) {
        var b = this.target.getBox();
        return this.fireEvent('beforeresize', this, b.width, b.height, e);
    },

    
    onResize: function(tracker, e) {
        var me = this,
            b = me.target.getBox();
        me.forceHandlesHeight();
        return me.fireEvent('resizedrag', me, b.width, b.height, e);
    },

    
    onResizeEnd: function(tracker, e) {
        var me = this,
            b = me.target.getBox();
        me.forceHandlesHeight();
        return me.fireEvent('resize', me, b.width, b.height, e);
    },

    
    resizeTo : function(width, height){
        this.target.setSize(width, height);
        this.fireEvent('resize', this, width, height, null);
    },

    
    getEl : function() {
        return this.el;
    },

    
    getTarget: function() {
        return this.target;
    },

    destroy: function() {
        var h;
        for (var i = 0, l = this.handles.length; i < l; i++) {
            h = this[this.possiblePositions[this.handles[i]]];
            delete h.owner;
            Ext.destroy(h);
        }
    },

    
    forceHandlesHeight : function() {
        var me = this,
            handle;
        if (Ext.isIE6) {
            handle = me.east; 
            if (handle) {
                handle.setHeight(me.el.getHeight());
            }
            handle = me.west; 
            if (handle) {
                handle.setHeight(me.el.getHeight());
            }
            me.el.repaint();
        }
    }
});


Ext.define('Ext.resizer.ResizeTracker', {
    extend: 'Ext.dd.DragTracker',
    dynamic: true,
    preserveRatio: false,

    
    constrainTo: null,

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

        if (!config.el) {
            if (config.target.isComponent) {
                me.el = config.target.getEl();
            } else {
                me.el = config.target;
            }
        }
        this.callParent(arguments);

        
        if (me.preserveRatio && me.minWidth && me.minHeight) {
            var widthRatio = me.minWidth / me.el.getWidth(),
                heightRatio = me.minHeight / me.el.getHeight();

            
            
            
            if (heightRatio > widthRatio) {
                me.minWidth = me.el.getWidth() * heightRatio;
            } else {
                me.minHeight = me.el.getHeight() * widthRatio;
            }
        }

        
        
        if (me.throttle) {
            var throttledResizeFn = Ext.Function.createThrottled(function() {
                    Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments);
                }, me.throttle);

            me.resize = function(box, direction, atEnd) {
                if (atEnd) {
                    Ext.resizer.ResizeTracker.prototype.resize.apply(me, arguments);
                } else {
                    throttledResizeFn.apply(null, arguments);
                }
            };
        }
    },

    onBeforeStart: function(e) {
        
        this.startBox = this.el.getBox();
    },

    
    getDynamicTarget: function() {
        var d = this.target;
        if (this.dynamic) {
            return d;
        } else if (!this.proxy) {
            this.proxy = d.isComponent ? d.getProxy().addCls(Ext.baseCSSPrefix + 'resizable-proxy') : d.createProxy({tag: 'div', cls: Ext.baseCSSPrefix + 'resizable-proxy', id: d.id + '-rzproxy'}, Ext.getBody());
            this.proxy.removeCls(Ext.baseCSSPrefix + 'proxy-el');
        }
        this.proxy.show();
        return this.proxy;
    },

    onStart: function(e) {
        
        this.activeResizeHandle = Ext.getCmp(this.getDragTarget().id);

        
        if (!this.dynamic) {
            this.resize(this.startBox, {
                horizontal: 'none',
                vertical: 'none'
            });
        }
    },

    onDrag: function(e) {
        
        if (this.dynamic || this.proxy) {
            this.updateDimensions(e);
        }
    },

    updateDimensions: function(e, atEnd) {
        var me = this,
            region = me.activeResizeHandle.region,
            offset = me.getOffset(me.constrainTo ? 'dragTarget' : null),
            box = me.startBox,
            ratio,
            widthAdjust = 0,
            heightAdjust = 0,
            adjustX = 0,
            adjustY = 0,
            dragRatio,
            horizDir = offset[0] < 0 ? 'right' : 'left',
            vertDir = offset[1] < 0 ? 'down' : 'up',
            oppositeCorner,
            axis; 

        switch (region) {
            case 'south':
                heightAdjust = offset[1];
                axis = 2;
                break;
            case 'north':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                axis = 2;
                break;
            case 'east':
                widthAdjust = offset[0];
                axis = 1;
                break;
            case 'west':
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                axis = 1;
                break;
            case 'northeast':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                widthAdjust = offset[0];
                oppositeCorner = [box.x, box.y + box.height];
                axis = 3;
                break;
            case 'southeast':
                heightAdjust = offset[1];
                widthAdjust = offset[0];
                oppositeCorner = [box.x, box.y];
                axis = 3;
                break;
            case 'southwest':
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                heightAdjust = offset[1];
                oppositeCorner = [box.x + box.width, box.y];
                axis = 3;
                break;
            case 'northwest':
                heightAdjust = -offset[1];
                adjustY = -heightAdjust;
                widthAdjust = -offset[0];
                adjustX = -widthAdjust;
                oppositeCorner = [box.x + box.width, box.y + box.height];
                axis = 3;
                break;
        }

        var newBox = {
            width: box.width + widthAdjust,
            height: box.height + heightAdjust,
            x: box.x + adjustX,
            y: box.y + adjustY
        };

        
        if (newBox.width < me.minWidth || newBox.width > me.maxWidth) {
            newBox.width = Ext.Number.constrain(newBox.width, me.minWidth, me.maxWidth);
            newBox.x = me.lastX || newBox.x;
        } else {
            me.lastX = newBox.x;
        }
        if (newBox.height < me.minHeight || newBox.height > me.maxHeight) {
            newBox.height = Ext.Number.constrain(newBox.height, me.minHeight, me.maxHeight);
            newBox.y = me.lastY || newBox.y;
        } else {
            me.lastY = newBox.y;
        }

        
        if (me.preserveRatio || e.shiftKey) {
            var newHeight,
                newWidth;

            ratio = me.startBox.width / me.startBox.height;

            
            newHeight = Math.min(Math.max(me.minHeight, newBox.width / ratio), me.maxHeight);
            newWidth = Math.min(Math.max(me.minWidth, newBox.height * ratio), me.maxWidth);

            
            if (axis == 1) {
                newBox.height = newHeight;
            }

            
            else if (axis == 2) {
                newBox.width = newWidth;
            }

            
            else {
                
                
                dragRatio = Math.abs(oppositeCorner[0] - this.lastXY[0]) / Math.abs(oppositeCorner[1] - this.lastXY[1]);

                
                if (dragRatio > ratio) {
                    newBox.height = newHeight;
                } else {
                    newBox.width = newWidth;
                }

                
                if (region == 'northeast') {
                    newBox.y = box.y - (newBox.height - box.height);
                } else if (region == 'northwest') {
                    newBox.y = box.y - (newBox.height - box.height);
                    newBox.x = box.x - (newBox.width - box.width);
                } else if (region == 'southwest') {
                    newBox.x = box.x - (newBox.width - box.width);
                }
            }
        }

        if (heightAdjust === 0) {
            vertDir = 'none';
        }
        if (widthAdjust === 0) {
            horizDir = 'none';
        }
        me.resize(newBox, {
            horizontal: horizDir,
            vertical: vertDir
        }, atEnd);
    },

    getResizeTarget: function(atEnd) {
        return atEnd ? this.target : this.getDynamicTarget();
    },

    resize: function(box, direction, atEnd) {
        var target = this.getResizeTarget(atEnd);
        if (target.isComponent) {
            if (target.floating) {
                target.setPagePosition(box.x, box.y);
            }
            target.setSize(box.width, box.height);
        } else {
            target.setBox(box);
            
            if (this.originalTarget) {
                this.originalTarget.setBox(box);
            }
        }
    },

    onEnd: function(e) {
        this.updateDimensions(e, true);
        if (this.proxy) {
            this.proxy.hide();
        }
    }
});


Ext.define('Ext.resizer.SplitterTracker', {
    extend: 'Ext.dd.DragTracker',
    requires: ['Ext.util.Region'],
    enabled: true,
    
    overlayCls: Ext.baseCSSPrefix + 'resizable-overlay',

    getPrevCmp: function() {
        var splitter = this.getSplitter();
        return splitter.previousSibling();
    },

    getNextCmp: function() {
        var splitter = this.getSplitter();
        return splitter.nextSibling();
    },

    
    
    onBeforeStart: function(e) {
        var me = this,
            prevCmp = me.getPrevCmp(),
            nextCmp = me.getNextCmp();

        
        if (nextCmp.collapsed || prevCmp.collapsed) {
            return false;
        }
        
        me.prevBox  = prevCmp.getEl().getBox();
        me.nextBox  = nextCmp.getEl().getBox();
        me.constrainTo = me.calculateConstrainRegion();
    },

    
    onStart: function(e) {
        var splitter = this.getSplitter(),
            overlay;
            
        splitter.addCls(splitter.baseCls + '-active');
        overlay = this.overlay =  Ext.getBody().createChild({
            cls: this.overlayCls, 
            html: '&#160;'
        });
        overlay.unselectable();
        overlay.setSize(Ext.core.Element.getViewWidth(true), Ext.core.Element.getViewHeight(true));
        overlay.show();
    },

    
    calculateConstrainRegion: function() {
        var me         = this,
            splitter   = me.getSplitter(),
            splitWidth = splitter.getWidth(),
            defaultMin = splitter.defaultSplitMin,
            orient     = splitter.orientation,
            prevBox    = me.prevBox,
            prevCmp    = me.getPrevCmp(),
            nextBox    = me.nextBox,
            nextCmp    = me.getNextCmp(),
            
            
            
            prevConstrainRegion, nextConstrainRegion;

        
        if (orient === 'vertical') {

            
            
            prevConstrainRegion = Ext.create('Ext.util.Region',
                prevBox.y,
                
                
                (prevCmp.maxWidth ? prevBox.x + prevCmp.maxWidth : nextBox.right - (nextCmp.minWidth || defaultMin)) + splitWidth,
                prevBox.bottom,
                prevBox.x + (prevCmp.minWidth || defaultMin)
            );
            
            nextConstrainRegion = Ext.create('Ext.util.Region',
                nextBox.y,
                nextBox.right - (nextCmp.minWidth || defaultMin),
                nextBox.bottom,
                
                
                (nextCmp.maxWidth ? nextBox.right - nextCmp.maxWidth : prevBox.x + (prevBox.minWidth || defaultMin)) - splitWidth
            );
        } else {
            
            prevConstrainRegion = Ext.create('Ext.util.Region',
                prevBox.y + (prevCmp.minHeight || defaultMin),
                prevBox.right,
                
                
                (prevCmp.maxHeight ? prevBox.y + prevCmp.maxHeight : nextBox.bottom - (nextCmp.minHeight || defaultMin)) + splitWidth,
                prevBox.x
            );
            
            nextConstrainRegion = Ext.create('Ext.util.Region',
                
                
                (nextCmp.maxHeight ? nextBox.bottom - nextCmp.maxHeight : prevBox.y + (prevCmp.minHeight || defaultMin)) - splitWidth,
                nextBox.right,
                nextBox.bottom - (nextCmp.minHeight || defaultMin),
                nextBox.x
            );
        }

        
        return prevConstrainRegion.intersect(nextConstrainRegion);
    },

    
    performResize: function(e) {
        var me       = this,
            offset   = me.getOffset('dragTarget'),
            splitter = me.getSplitter(),
            orient   = splitter.orientation,
            prevCmp  = me.getPrevCmp(),
            nextCmp  = me.getNextCmp(),
            owner    = splitter.ownerCt,
            layout   = owner.getLayout();

        
        owner.suspendLayout = true;

        if (orient === 'vertical') {
            if (prevCmp) {
                if (!prevCmp.maintainFlex) {
                    delete prevCmp.flex;
                    prevCmp.setSize(me.prevBox.width + offset[0], prevCmp.getHeight());
                }
            }
            if (nextCmp) {
                if (!nextCmp.maintainFlex) {
                    delete nextCmp.flex;
                    nextCmp.setSize(me.nextBox.width - offset[0], nextCmp.getHeight());
                }
            }
        
        } else {
            if (prevCmp) {
                if (!prevCmp.maintainFlex) {
                    delete prevCmp.flex;
                    prevCmp.setSize(prevCmp.getWidth(), me.prevBox.height + offset[1]);
                }
            }
            if (nextCmp) {
                if (!nextCmp.maintainFlex) {
                    delete nextCmp.flex;
                    nextCmp.setSize(prevCmp.getWidth(), me.nextBox.height - offset[1]);
                }
            }
        }
        delete owner.suspendLayout;
        layout.onLayout();
    },

    
    onEnd: function(e) {
        var me = this,
            splitter = me.getSplitter();
            
        splitter.removeCls(splitter.baseCls + '-active');
         if (me.overlay) {
             me.overlay.remove();
             delete me.overlay;
        }
        me.performResize();
    },

    
    
    onDrag: function(e) {
        var me        = this,
            offset    = me.getOffset('dragTarget'),
            splitter  = me.getSplitter(),
            splitEl   = splitter.getEl(),
            orient    = splitter.orientation;

        if (orient === "vertical") {
            splitEl.setX(me.startRegion.left + offset[0]);
        } else {
            splitEl.setY(me.startRegion.top + offset[1]);
        }
    },

    getSplitter: function() {
        return Ext.getCmp(this.getDragCt().id);
    }
});

Ext.define('Ext.selection.CellModel', {
    extend: 'Ext.selection.Model',
    alias: 'selection.cellmodel',
    requires: ['Ext.util.KeyNav'],
    
    
    enableKeyNav: true,
    
    
    preventWrap: false,

    constructor: function(){
        this.addEvents(
            
            'deselect',
            
            
            'select'
        );
        this.callParent(arguments);    
    },

    bindComponent: function(view) {
        var me = this;
        me.primaryView = view;
        me.views = me.views || [];
        me.views.push(view);
        me.bind(view.getStore(), true);

        view.on({
            cellmousedown: me.onMouseDown,
            refresh: me.onViewRefresh,
            scope: me
        });

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },

    initKeyNav: function(view) {
        var me = this;
        
        if (!view.rendered) {
            view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
            return;
        }

        view.el.set({
            tabIndex: -1
        });

        
        
        me.keyNav = Ext.create('Ext.util.KeyNav', view.el, {
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            tab: me.onKeyTab,
            scope: me
        });
    },
    
    getHeaderCt: function() {
        return this.primaryView.headerCt;
    },

    onKeyUp: function(e, t) {
        this.move('up', e);
    },

    onKeyDown: function(e, t) {
        this.move('down', e);
    },

    onKeyLeft: function(e, t) {
        this.move('left', e);
    },
    
    onKeyRight: function(e, t) {
        this.move('right', e);
    },
    
    move: function(dir, e) {
        var me = this,
            pos = me.primaryView.walkCells(me.getCurrentPosition(), dir, e, me.preventWrap);
        if (pos) {
            me.setCurrentPosition(pos);
        }
        return pos;
    },

    
    getCurrentPosition: function() {
        return this.position;
    },
    
    
    setCurrentPosition: function(pos) {
        var me = this;
        
        if (me.position) {
            me.onCellDeselect(me.position);
        }
        if (pos) {
            me.onCellSelect(pos);
        }
        me.position = pos;
    },

    
    onMouseDown: function(view, cell, cellIndex, record, row, rowIndex, e) {
        this.setCurrentPosition({
            row: rowIndex,
            column: cellIndex
        });
    },

    
    
    onCellSelect: function(position) {
        var me = this,
            store = me.view.getStore(),
            record = store.getAt(position.row);

        me.doSelect(record);
        me.primaryView.onCellSelect(position);
        
        me.primaryView.onCellFocus(position);
        me.fireEvent('select', me, record, position.row, position.column);
    },

    
    
    onCellDeselect: function(position) {
        var me = this,
            store = me.view.getStore(),
            record = store.getAt(position.row);

        me.doDeselect(record);
        me.primaryView.onCellDeselect(position);
        me.fireEvent('deselect', me, record, position.row, position.column);
    },

    onKeyTab: function(e, t) {
        var me = this,
            direction = e.shiftKey ? 'left' : 'right',
            editingPlugin = me.view.editingPlugin,
            position = me.move(direction, e);

        if (editingPlugin && position && me.wasEditing) {
            editingPlugin.startEditByPosition(position);
        }
        delete me.wasEditing;
    },

    onEditorTab: function(editingPlugin, e) {
        var me = this,
            direction = e.shiftKey ? 'left' : 'right',
            position  = me.move(direction, e);

        if (position) {
            editingPlugin.startEditByPosition(position);
            me.wasEditing = true;
        }
    },

    refresh: function() {
        var pos = this.getCurrentPosition();
        if (pos) {
            this.onCellSelect(pos);
        }
    },

    onViewRefresh: function() {
        var pos = this.getCurrentPosition();
        if (pos) {
            this.onCellDeselect(pos);
            this.setCurrentPosition(null);
        }
    },

    selectByPosition: function(position) {
        this.setCurrentPosition(position);
    }
});

Ext.define('Ext.selection.RowModel', {
    extend: 'Ext.selection.Model',
    alias: 'selection.rowmodel',
    requires: ['Ext.util.KeyNav'],
    
    
    deltaScroll: 5,
    
    
    enableKeyNav: true,
    
    constructor: function(){
        this.addEvents(
            
            'deselect',
            
            
            'select'
        );
        this.callParent(arguments);    
    },

    bindComponent: function(view) {
        var me = this;
        
        me.views = me.views || [];
        me.views.push(view);
        me.bind(view.getStore(), true);

        view.on({
            itemmousedown: me.onRowMouseDown,
            scope: me
        });

        if (me.enableKeyNav) {
            me.initKeyNav(view);
        }
    },

    initKeyNav: function(view) {
        var me = this;
        
        if (!view.rendered) {
            view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
            return;
        }

        view.el.set({
            tabIndex: -1
        });

        
        
        me.keyNav = new Ext.util.KeyNav(view.el, {
            up: me.onKeyUp,
            down: me.onKeyDown,
            right: me.onKeyRight,
            left: me.onKeyLeft,
            pageDown: me.onKeyPageDown,
            pageUp: me.onKeyPageUp,
            home: me.onKeyHome,
            end: me.onKeyEnd,
            scope: me
        });
        view.el.on(Ext.EventManager.getKeyEvent(), me.onKeyPress, me);
    },

    
    
    
    getRowsVisible: function() {
        var rowsVisible = false,
            view = this.views[0],
            row = view.getNode(0),
            rowHeight, gridViewHeight;

        if (row) {
            rowHeight = Ext.fly(row).getHeight();
            gridViewHeight = view.el.getHeight();
            rowsVisible = Math.floor(gridViewHeight / rowHeight);
        }

        return rowsVisible;
    },

    
    onKeyEnd: function(e, t) {
        var me = this,
            last = me.store.getAt(me.store.getCount() - 1);
            
        if (last) {
            if (e.shiftKey) {
                me.selectRange(last, me.lastFocused || 0);
                me.setLastFocused(last);
            } else if (e.ctrlKey) {
                me.setLastFocused(last);
            } else {
                me.doSelect(last);
            }
        }
    },

    
    onKeyHome: function(e, t) {
        var me = this,
            first = me.store.getAt(0);
            
        if (first) {
            if (e.shiftKey) {
                me.selectRange(first, me.lastFocused || 0);
                me.setLastFocused(first);
            } else if (e.ctrlKey) {
                me.setLastFocused(first);
            } else {
                me.doSelect(first, false);
            }
        }
    },

    
    onKeyPageUp: function(e, t) {
        var me = this,
            rowsVisible = me.getRowsVisible(),
            selIdx,
            prevIdx,
            prevRecord,
            currRec;
            
        if (rowsVisible) {
            selIdx = me.lastFocused ? me.store.indexOf(me.lastFocused) : 0;
            prevIdx = selIdx - rowsVisible;
            if (prevIdx < 0) {
                prevIdx = 0;
            }
            prevRecord = me.store.getAt(prevIdx);
            if (e.shiftKey) {
                currRec = me.store.getAt(selIdx);
                me.selectRange(prevRecord, currRec, e.ctrlKey, 'up');
                me.setLastFocused(prevRecord);
            } else if (e.ctrlKey) {
                e.preventDefault();
                me.setLastFocused(prevRecord);
            } else {
                me.doSelect(prevRecord);
            }

        }
    },

    
    onKeyPageDown: function(e, t) {
        var me = this,
            rowsVisible = me.getRowsVisible(),
            selIdx,
            nextIdx,
            nextRecord,
            currRec;
            
        if (rowsVisible) {
            selIdx = me.lastFocused ? me.store.indexOf(me.lastFocused) : 0;
            nextIdx = selIdx + rowsVisible;
            if (nextIdx >= me.store.getCount()) {
                nextIdx = me.store.getCount() - 1;
            }
            nextRecord = me.store.getAt(nextIdx);
            if (e.shiftKey) {
                currRec = me.store.getAt(selIdx);
                me.selectRange(nextRecord, currRec, e.ctrlKey, 'down');
                me.setLastFocused(nextRecord);
            } else if (e.ctrlKey) {
                
                
                e.preventDefault();
                me.setLastFocused(nextRecord);
            } else {
                me.doSelect(nextRecord);
            }
        }
    },

    
    
    onKeyPress: function(e, t) {
        if (e.getKey() === e.SPACE) {
            e.stopEvent();
            var me = this,
                record = me.lastFocused;
                
            if (record) {
                if (me.isSelected(record)) {
                    me.doDeselect(record, false);
                } else {
                    me.doSelect(record, true);
                }
            }
        }
    },

    
    
    
    onKeyUp: function(e, t) {
        var me = this,
            view = me.views[0],
            idx  = me.store.indexOf(me.lastFocused),
            record;
            
        if (idx > 0) {
            
            
            record = me.store.getAt(idx - 1);
            if (e.shiftKey && me.lastFocused) {
                if (me.isSelected(me.lastFocused) && me.isSelected(record)) {
                    me.doDeselect(me.lastFocused, true);
                    me.setLastFocused(record);
                } else if (!me.isSelected(me.lastFocused)) {
                    me.doSelect(me.lastFocused, true);
                    me.doSelect(record, true);
                } else {
                    me.doSelect(record, true);
                }
            } else if (e.ctrlKey) {
                me.setLastFocused(record);
            } else {
                me.doSelect(record);
                
            }
        }
        
        
        
        
        
        
        
    },

    
    
    
    onKeyDown: function(e, t) {
        var me = this,
            view = me.views[0],
            idx  = me.store.indexOf(me.lastFocused),
            record;
            
        
        
        if (idx + 1 < me.store.getCount()) {
            record = me.store.getAt(idx + 1);
            if (me.selected.getCount() === 0) {
                me.doSelect(record);
                
            } else if (e.shiftKey && me.lastFocused) {
                if (me.isSelected(me.lastFocused) && me.isSelected(record)) {
                    me.doDeselect(me.lastFocused, true);
                    me.setLastFocused(record);
                } else if (!me.isSelected(me.lastFocused)) {
                    me.doSelect(me.lastFocused, true);
                    me.doSelect(record, true);
                } else {
                    me.doSelect(record, true);
                }
            } else if (e.ctrlKey) {
                me.setLastFocused(record);
            } else {
                me.doSelect(record);
                
            }
        }
    },
    
    scrollByDeltaX: function(delta) {
        var view    = this.views[0],
            section = view.up(),
            hScroll = section.horizontalScroller;
            
        if (hScroll) {
            hScroll.scrollByDeltaX(delta);
        }
    },
    
    onKeyLeft: function(e, t) {
        this.scrollByDeltaX(-this.deltaScroll);
    },
    
    onKeyRight: function(e, t) {
        this.scrollByDeltaX(this.deltaScroll);
    },

    
    
    onRowMouseDown: function(view, record, item, index, e) {
        view.el.focus();
        this.selectWithEvent(record, e);
    },

    
    
    onSelectChange: function(record, isSelected, suppressEvent) {
        var me      = this,
            views   = me.views,
            viewsLn = views.length,
            store   = me.store,
            rowIdx  = store.indexOf(record),
            i = 0;
            
        for (; i < viewsLn; i++) {
            if (isSelected) {
                views[i].onRowSelect(rowIdx, suppressEvent);
                if (!suppressEvent) {
                    me.fireEvent('select', me, record, rowIdx);
                }
            } else {
                views[i].onRowDeselect(rowIdx, suppressEvent);
                if (!suppressEvent) {
                    me.fireEvent('deselect', me, record, rowIdx);
                }
            }
        }
    },

    
    
    onLastFocusChanged: function(oldFocused, newFocused, supressFocus) {
        var views   = this.views,
            viewsLn = views.length,
            store   = this.store,
            rowIdx,
            i = 0;
            
        if (oldFocused) {
            rowIdx = store.indexOf(oldFocused);
            if (rowIdx != -1) {
                for (; i < viewsLn; i++) {
                    views[i].onRowFocus(rowIdx, false);
                }
            }
        }

        if (newFocused) {
            rowIdx = store.indexOf(newFocused);
            if (rowIdx != -1) {
                for (i = 0; i < viewsLn; i++) {
                    views[i].onRowFocus(rowIdx, true, supressFocus);
                }
            }
        }
    },
    
    onEditorTab: function(editingPlugin, e) {
        var me = this,
            view = me.views[0],
            record = editingPlugin.getActiveRecord(),
            header = editingPlugin.getActiveColumn(),
            position = view.getPosition(record, header),
            direction = e.shiftKey ? 'left' : 'right',
            newPosition  = view.walkCells(position, direction, e, this.preventWrap);
            
        if (newPosition) {
            editingPlugin.startEditByPosition(newPosition);
        }
    },
    
    selectByPosition: function(position) {
        var record = this.store.getAt(position.row);
        this.select(record);
    }
});

Ext.define('Ext.selection.CheckboxModel', {
    alias: 'selection.checkboxmodel',
    extend: 'Ext.selection.RowModel',

    
    mode: 'MULTI',

    
    injectCheckbox: 0,

    
    checkOnly: false,

    
    checkerOnCls: Ext.baseCSSPrefix + 'grid-hd-checker-on',

    bindComponent: function() {
        this.sortable = false;
        this.callParent(arguments);

        var view     = this.views[0],
            headerCt = view.headerCt;

        if (this.injectCheckbox !== false) {
            if (this.injectCheckbox == 'first') {
                this.injectCheckbox = 0;
            } else if (this.injectCheckbox == 'last') {
                this.injectCheckbox = headerCt.getColumnCount();
            }
            headerCt.add(this.injectCheckbox,  this.getHeaderConfig());
        }
        headerCt.on('headerclick', this.onHeaderClick, this);
    },

    
    toggleUiHeader: function(isChecked) {
        var view     = this.views[0],
            headerCt = view.headerCt,
            checkHd  = headerCt.child('gridcolumn[isCheckerHd]');

        if (checkHd) {
            if (isChecked) {
                checkHd.el.addCls(this.checkerOnCls);
            } else {
                checkHd.el.removeCls(this.checkerOnCls);
            }
        }
    },

    
    onHeaderClick: function(headerCt, header, e) {
        if (header.isCheckerHd) {
            e.stopEvent();
            var isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
            if (isChecked) {
                
                this.deselectAll(true);
            } else {
                
                this.selectAll(true);
            }
        }
    },

    
    getHeaderConfig: function() {
        return {
            isCheckerHd: true,
            text : '&#160;',
            width: 24,
            sortable: false,
            fixed: true,
            hideable: false,
            menuDisabled: true,
            dataIndex: '',
            cls: Ext.baseCSSPrefix + 'column-header-checkbox ',
            renderer: Ext.Function.bind(this.renderer, this)
        };
    },

    
    renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
        metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
        return '<div class="' + Ext.baseCSSPrefix + 'grid-row-checker">&#160;</div>';
    },

    
    onRowMouseDown: function(view, record, item, index, e) {
        view.el.focus();
        var me = this,
            checker = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-checker');

        
        if (me.checkOnly && !checker) {
            return;
        }

        if (checker) {
            var mode = me.getSelectionMode();
            
            
            if (mode !== 'SINGLE') {
                me.setSelectionMode('SIMPLE');
            }
            me.selectWithEvent(record, e);
            me.setSelectionMode(mode);
        } else {
            me.selectWithEvent(record, e);
        }
    },

    
    onSelectChange: function(record, isSelected) {
        this.callParent([record, isSelected]);
        
        var hdSelectStatus = this.selected.getCount() === this.store.getCount();
        this.toggleUiHeader(hdSelectStatus);
    }
});


Ext.define('Ext.selection.TreeModel', {
    extend: 'Ext.selection.RowModel',
    alias: 'selection.treemodel',
    
    
    
    
    pruneRemoved: false,
    
    onKeyRight: function(e, t) {
        var focused = this.getLastFocused(),
            view    = this.view;
            
        if (focused) {
            
            
            
            if (focused.isExpanded()) {
                this.onKeyDown(e, t);
            
            } else if (!focused.isLeaf()) {
                view.expand(focused);
            }
        }
    },
    
    onKeyLeft: function(e, t) {
        var focused = this.getLastFocused(),
            view    = this.view,
            viewSm  = view.getSelectionModel(),
            parentNode, parentRecord;

        if (focused) {
            parentNode = focused.parentNode;
            
            if (focused.isExpanded()) {
                view.collapse(focused);
            
            
            } else if (parentNode && !parentNode.isRoot()) {
                
                if (e.shiftKey) {
                    viewSm.selectRange(parentNode, focused, e.ctrlKey, 'up');
                    viewSm.setLastFocused(parentNode);
                
                } else if (e.ctrlKey) {
                    viewSm.setLastFocused(parentNode);
                
                } else {
                    viewSm.select(parentNode);
                }
            }
        }
    },
    
    onKeyPress: function(e, t) {
        var selected, checked;
        
        if (e.getKey() === e.SPACE || e.getKey() === e.ENTER) {
            e.stopEvent();
            selected = this.getLastSelected();
            if (selected && selected.isLeaf()) {
                checked = selected.get('checked');
                if (Ext.isBoolean(checked)) {
                    selected.set('checked', !checked);
                }
            }
        } else {
            this.callParent(arguments);
        }
    }
});


Ext.define('Ext.slider.Thumb', {
    requires: ['Ext.dd.DragTracker', 'Ext.util.Format'],
    
    topZIndex: 10000,
    
    constructor: function(config) {
        var me = this;
        
        
        Ext.apply(me, config || {}, {
            cls: Ext.baseCSSPrefix + 'slider-thumb',

            
            constrain: false
        });
        me.callParent([config]);

        if (me.slider.vertical) {
            Ext.apply(me, Ext.slider.Thumb.Vertical);
        }
    },

    
    render: function() {
        var me = this;
        
        me.el = me.slider.innerEl.insertFirst({cls: me.cls});
        if (me.disabled) {
            me.disable();
        }
        me.initEvents();
    },
    
    
    move: function(v, animate){
        if(!animate){
            this.el.setLeft(v);
        }else{
            Ext.create('Ext.fx.Anim', {
                target: this.el,
                duration: 350,
                to: {
                    left: v
                }
            });
        }
    },

    
    bringToFront: function() {
        this.el.setStyle('zIndex', this.topZIndex);
    },
    
    
    sendToBack: function() {
        this.el.setStyle('zIndex', '');
    },
    
    
    enable: function() {
        var me = this;
        
        me.disabled = false;
        if (me.el) {
            me.el.removeCls(me.slider.disabledCls);
        }
    },

    
    disable: function() {
        var me = this;
        
        me.disabled = true;
        if (me.el) {
            me.el.addCls(me.slider.disabledCls);
        }
    },

    
    initEvents: function() {
        var me = this,
            el = me.el;

        me.tracker = Ext.create('Ext.dd.DragTracker', {
            onBeforeStart: Ext.Function.bind(me.onBeforeDragStart, me),
            onStart      : Ext.Function.bind(me.onDragStart, me),
            onDrag       : Ext.Function.bind(me.onDrag, me),
            onEnd        : Ext.Function.bind(me.onDragEnd, me),
            tolerance    : 3,
            autoStart    : 300,
            overCls      : Ext.baseCSSPrefix + 'slider-thumb-over'
        });

        me.tracker.initEl(el);
    },

    
    onBeforeDragStart : function(e) {
        if (this.disabled) {
            return false;
        } else {
            this.slider.promoteThumb(this);
            return true;
        }
    },

    
    onDragStart: function(e){
        var me = this;
        
        me.el.addCls(Ext.baseCSSPrefix + 'slider-thumb-drag');
        me.dragging = true;
        me.dragStartValue = me.value;

        me.slider.fireEvent('dragstart', me.slider, e, me);
    },

    
    onDrag: function(e) {
        var me       = this,
            slider   = me.slider,
            index    = me.index,
            newValue = me.getNewValue(),
            above,
            below;

        if (me.constrain) {
            above = slider.thumbs[index + 1];
            below = slider.thumbs[index - 1];

            if (below !== undefined && newValue <= below.value) {
                newValue = below.value;
            }
            
            if (above !== undefined && newValue >= above.value) {
                newValue = above.value;
            }
        }

        slider.setValue(index, newValue, false);
        slider.fireEvent('drag', slider, e, me);
    },

    getNewValue: function() {
        var slider = this.slider,
            pos = slider.innerEl.translatePoints(this.tracker.getXY());

        return Ext.util.Format.round(slider.reverseValue(pos.left), slider.decimalPrecision);
    },

    
    onDragEnd: function(e) {
        var me     = this,
            slider = me.slider,
            value  = me.value;

        me.el.removeCls(Ext.baseCSSPrefix + 'slider-thumb-drag');

        me.dragging = false;
        slider.fireEvent('dragend', slider, e);

        if (me.dragStartValue != value) {
            slider.fireEvent('changecomplete', slider, value, me);
        }
    },

    destroy: function() {
        Ext.destroy(this.tracker);
    },
    statics: {
        
        Vertical: {
            getNewValue: function() {
                var slider   = this.slider,
                    innerEl  = slider.innerEl,
                    pos      = innerEl.translatePoints(this.tracker.getXY()),
                    bottom   = innerEl.getHeight() - pos.top;

                return Ext.util.Format.round(slider.reverseValue(bottom), slider.decimalPrecision);
            },
            move: function(v, animate) {
                if (!animate) {
                    this.el.setBottom(v);
                } else {
                    Ext.create('Ext.fx.Anim', {
                        target: this.el,
                        duration: 350,
                        to: {
                            bottom: v
                        }
                    });
                }
            }
        }
    }
});


Ext.define('Ext.slider.Tip', {
    extend: 'Ext.tip.Tip',
    minWidth: 10,
    alias: 'widget.slidertip',
    offsets : [0, -10],
    
    isSliderTip: true,

    init: function(slider) {
        var me = this;
        
        slider.on({
            scope    : me,
            dragstart: me.onSlide,
            drag     : me.onSlide,
            dragend  : me.hide,
            destroy  : me.destroy
        });
    },
    
    onSlide : function(slider, e, thumb) {
        var me = this;
        me.show();
        me.update(me.getText(thumb));
        me.doComponentLayout();
        me.el.alignTo(thumb.el, 'b-t?', me.offsets);
    },

    
    getText : function(thumb) {
        return String(thumb.value);
    }
});

Ext.define('Ext.slider.Multi', {
    extend: 'Ext.form.field.Base',
    alias: 'widget.multislider',
    alternateClassName: 'Ext.slider.MultiSlider',

    requires: [
        'Ext.slider.Thumb',
        'Ext.slider.Tip',
        'Ext.Number',
        'Ext.util.Format',
        'Ext.Template',
        'Ext.layout.component.field.Slider'
    ],

    fieldSubTpl: [
        '<div class="' + Ext.baseCSSPrefix + 'slider {fieldCls} {vertical}" aria-valuemin="{minValue}" aria-valuemax="{maxValue}" aria-valuenow="{value}" aria-valuetext="{value}">',
            '<div class="' + Ext.baseCSSPrefix + 'slider-end" role="presentation">',
                '<div class="' + Ext.baseCSSPrefix + 'slider-inner" role="presentation">',
                    '<a class="' + Ext.baseCSSPrefix + 'slider-focus" href="#" tabIndex="-1" hidefocus="on" role="presentation"></a>',
                '</div>',
            '</div>',
        '</div>',
        {
            disableFormats: true,
            compiled: true
        }
    ],

    

    

    
    vertical: false,
    
    minValue: 0,
    
    maxValue: 100,
    
    decimalPrecision: 0,
    
    keyIncrement: 1,
    
    increment: 0,

    
    clickRange: [5,15],

    
    clickToChange : true,
    
    animate: true,

    
    dragging: false,

    
    constrainThumbs: true,

    componentLayout: 'sliderfield',

    
    useTips : true,

    
    tipText : null,

    ariaRole: 'slider',

    
    initValue: function() {
        var me = this,
            extValue = Ext.value,
            
            values = extValue(me.values, [extValue(me.value, extValue(me.minValue, 0))]),
            i = 0,
            len = values.length;

        
        me.originalValue = values;

        
        for (; i < len; i++) {
            me.addThumb(values[i]);
        }
    },

    
    initComponent : function() {
        var me = this,
            tipPlug,
            hasTip;
        
        
        me.thumbs = [];

        me.keyIncrement = Math.max(me.increment, me.keyIncrement);

        me.addEvents(
            
            'beforechange',

            
            'change',

            
            'changecomplete',

            
            'dragstart',

            
            'drag',

            
            'dragend'
        );

        if (me.vertical) {
            Ext.apply(me, Ext.slider.Multi.Vertical);
        }

        me.callParent();

        
        if (me.useTips) {
            tipPlug = me.tipText ? {getText: me.tipText} : {};
            me.plugins = me.plugins || [];
            Ext.each(me.plugins, function(plug){
                if (plug.isSliderTip) {
                    hasTip = true;
                    return false;
                }
            });
            if (!hasTip) {
                me.plugins.push(Ext.create('Ext.slider.Tip', tipPlug));
            }
        }
    },

    
    addThumb: function(value) {
        var me = this,
            thumb = Ext.create('Ext.slider.Thumb', {
            value    : value,
            slider   : me,
            index    : me.thumbs.length,
            constrain: me.constrainThumbs
        });
        me.thumbs.push(thumb);

        
        if (me.rendered) {
            thumb.render();
        }

        return thumb;
    },

    
    promoteThumb: function(topThumb) {
        var thumbs = this.thumbs,
            ln = thumbs.length,
            zIndex, thumb, i;
            
        for (i = 0; i < ln; i++) {
            thumb = thumbs[i];

            if (thumb == topThumb) {
                thumb.bringToFront();
            } else {
                thumb.sendToBack();
            }
        }
    },

    
    onRender : function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb;

        Ext.applyIf(me.subTplData, {
            vertical: me.vertical ? Ext.baseCSSPrefix + 'slider-vert' : Ext.baseCSSPrefix + 'slider-horz',
            minValue: me.minValue,
            maxValue: me.maxValue,
            value: me.value
        });

        Ext.applyIf(me.renderSelectors, {
            endEl: '.' + Ext.baseCSSPrefix + 'slider-end',
            innerEl: '.' + Ext.baseCSSPrefix + 'slider-inner',
            focusEl: '.' + Ext.baseCSSPrefix + 'slider-focus'
        });

        me.callParent(arguments);

        
        for (; i < len; i++) {
            thumbs[i].render();
        }

        
        thumb = me.innerEl.down('.' + Ext.baseCSSPrefix + 'slider-thumb');
        me.halfThumb = (me.vertical ? thumb.getHeight() : thumb.getWidth()) / 2;

    },

    
    onChange : function(slider, v) {
        this.setValue(v, undefined, true);
    },

    
    initEvents : function() {
        var me = this;
        
        me.mon(me.el, {
            scope    : me,
            mousedown: me.onMouseDown,
            keydown  : me.onKeyDown,
            change : me.onChange
        });

        me.focusEl.swallowEvent("click", true);
    },

    
    onMouseDown : function(e) {
        var me = this,
            thumbClicked = false,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            local;
            
        if (me.disabled) {
            return;
        }

        
        for (; i < len; i++) {
            thumbClicked = thumbClicked || e.target == thumbs[i].el.dom;
        }

        if (me.clickToChange && !thumbClicked) {
            local = me.innerEl.translatePoints(e.getXY());
            me.onClickChange(local);
        }
        me.focus();
    },

    
    onClickChange : function(local) {
        var me = this,
            thumb, index;
            
        if (local.top > me.clickRange[0] && local.top < me.clickRange[1]) {
            
            thumb = me.getNearest(local, 'left');
            if (!thumb.disabled) {
                index = thumb.index;
                me.setValue(index, Ext.util.Format.round(me.reverseValue(local.left), me.decimalPrecision), undefined, true);
            }
        }
    },

    
    getNearest: function(local, prop) {
        var me = this,
            localValue = prop == 'top' ? me.innerEl.getHeight() - local[prop] : local[prop],
            clickValue = me.reverseValue(localValue),
            nearestDistance = (me.maxValue - me.minValue) + 5, 
            index = 0,
            nearest = null,
            thumbs = me.thumbs,
            i = 0,
            len = thumbs.length,
            thumb,
            value,
            dist;

        for (; i < len; i++) {
            thumb = me.thumbs[i];
            value = thumb.value;
            dist  = Math.abs(value - clickValue);

            if (Math.abs(dist <= nearestDistance)) {
                nearest = thumb;
                index = i;
                nearestDistance = dist;
            }
        }
        return nearest;
    },

    
    onKeyDown : function(e) {
        
        var me = this,
            k,
            val;
        
        if(me.disabled || me.thumbs.length !== 1) {
            e.preventDefault();
            return;
        }
        k = e.getKey();
        
        switch(k) {
            case e.UP:
            case e.RIGHT:
                e.stopEvent();
                val = e.ctrlKey ? me.maxValue : me.getValue(0) + me.keyIncrement;
                me.setValue(0, val, undefined, true);
            break;
            case e.DOWN:
            case e.LEFT:
                e.stopEvent();
                val = e.ctrlKey ? me.minValue : me.getValue(0) - me.keyIncrement;
                me.setValue(0, val, undefined, true);
            break;
            default:
                e.preventDefault();
        }
    },

    
    doSnap : function(value) {
        var newValue = value,
            inc = this.increment,
            m;
            
        if (!(inc && value)) {
            return value;
        }
        m = value % inc;
        if (m !== 0) {
            newValue -= m;
            if (m * 2 >= inc) {
                newValue += inc;
            } else if (m * 2 < -inc) {
                newValue -= inc;
            }
        }
        return Ext.Number.constrain(newValue, this.minValue,  this.maxValue);
    },

    
    afterRender : function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb,
            v;
            
        me.callParent(arguments);

        for (; i < len; i++) {
            thumb = thumbs[i];

            if (thumb.value !== undefined) {
                v = me.normalizeValue(thumb.value);
                if (v !== thumb.value) {
                    
                    me.setValue(i, v, false);
                } else {
                    thumb.move(me.translateValue(v), false);
                }
            }
        }
    },

    
    getRatio : function() {
        var w = this.innerEl.getWidth(),
            v = this.maxValue - this.minValue;
        return v === 0 ? w : (w/v);
    },

    
    normalizeValue : function(v) {
        var me = this;
        
        v = me.doSnap(v);
        v = Ext.util.Format.round(v, me.decimalPrecision);
        v = Ext.Number.constrain(v, me.minValue, me.maxValue);
        return v;
    },

    
    setMinValue : function(val) {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            t;
            
        me.minValue = val;
        me.inputEl.dom.setAttribute('aria-valuemin', val);

        for (; i < len; ++i) {
            t = thumbs[i];
            t.value = t.value < val ? val : t.value;
        }
        me.syncThumbs();
    },

    
    setMaxValue : function(val) {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            t;
            
        me.maxValue = val;
        me.inputEl.dom.setAttribute('aria-valuemax', val);

        for (; i < len; ++i) {
            t = thumbs[i];
            t.value = t.value > val ? val : t.value;
        }
        me.syncThumbs();
    },

    
    setValue : function(index, value, animate, changeComplete) {
        var me = this,
            thumb = me.thumbs[index];

        
        value = me.normalizeValue(value);

        if (value !== thumb.value && me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) {
            thumb.value = value;
            if (me.rendered) {
                
                
                me.inputEl.set({
                    'aria-valuenow': value,
                    'aria-valuetext': value
                });

                thumb.move(me.translateValue(value), Ext.isDefined(animate) ? animate !== false : me.animate);

                me.fireEvent('change', me, value, thumb);
                if (changeComplete) {
                    me.fireEvent('changecomplete', me, value, thumb);
                }
            }
        }
    },

    
    translateValue : function(v) {
        var ratio = this.getRatio();
        return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
    },

    
    reverseValue : function(pos) {
        var ratio = this.getRatio();
        return (pos + (this.minValue * ratio)) / ratio;
    },

    
    focus : function() {
        this.focusEl.focus(10);
    },

    
    onDisable: function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb,
            el,
            xy;
            
        me.callParent();

        for (; i < len; i++) {
            thumb = thumbs[i];
            el = thumb.el;

            thumb.disable();

            if(Ext.isIE) {
                
                
                xy = el.getXY();
                el.hide();

                me.innerEl.addCls(me.disabledCls).dom.disabled = true;

                if (!me.thumbHolder) {
                    me.thumbHolder = me.endEl.createChild({cls: Ext.baseCSSPrefix + 'slider-thumb ' + me.disabledCls});
                }

                me.thumbHolder.show().setXY(xy);
            }
        }
    },

    
    onEnable: function() {
        var me = this,
            i = 0,
            thumbs = me.thumbs,
            len = thumbs.length,
            thumb,
            el;
            
        this.callParent();

        for (; i < len; i++) {
            thumb = thumbs[i];
            el = thumb.el;

            thumb.enable();

            if (Ext.isIE) {
                me.innerEl.removeCls(me.disabledCls).dom.disabled = false;

                if (me.thumbHolder) {
                    me.thumbHolder.hide();
                }

                el.show();
                me.syncThumbs();
            }
        }
    },

    
    syncThumbs : function() {
        if (this.rendered) {
            var thumbs = this.thumbs,
                length = thumbs.length,
                i = 0;

            for (; i < length; i++) {
                thumbs[i].move(this.translateValue(thumbs[i].value));
            }
        }
    },

    
    getValue : function(index) {
        return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues();
    },

    
    getValues: function() {
        var values = [],
            i = 0,
            thumbs = this.thumbs,
            len = thumbs.length;

        for (; i < len; i++) {
            values.push(thumbs[i].value);
        }

        return values;
    },

    getSubmitValue: function() {
        var me = this;
        return (me.disabled || !me.submitValue) ? null : me.getValue();
    },

    reset: function() {
        var me = this,
            Array = Ext.Array;
        Array.forEach(Array.from(me.originalValue), function(val, i) {
            me.setValue(i, val);
        });
        me.clearInvalid();
        
        delete me.wasValid;
    },

    
    beforeDestroy : function() {
        var me = this;
        
        Ext.destroyMembers(me.innerEl, me.endEl, me.focusEl);
        Ext.each(me.thumbs, function(thumb) {
            Ext.destroy(thumb);
        }, me);

        me.callParent();
    },

    statics: {
        
        Vertical: {
            getRatio : function() {
                var h = this.innerEl.getHeight(),
                    v = this.maxValue - this.minValue;
                return h/v;
            },

            onClickChange : function(local) {
                var me = this,
                    thumb, index, bottom;

                if (local.left > me.clickRange[0] && local.left < me.clickRange[1]) {
                    thumb = me.getNearest(local, 'top');
                    if (!thumb.disabled) {
                        index = thumb.index;
                        bottom =  me.reverseValue(me.innerEl.getHeight() - local.top);

                        me.setValue(index, Ext.util.Format.round(me.minValue + bottom, me.decimalPrecision), undefined, true);
                    }
                }
            }
        }
    }
});


Ext.define('Ext.slider.Single', {
    extend: 'Ext.slider.Multi',
    alias: ['widget.slider', 'widget.sliderfield'],
    alternateClassName: ['Ext.Slider', 'Ext.form.SliderField', 'Ext.slider.SingleSlider', 'Ext.slider.Slider'],

    
    getValue: function() {
        
        return this.callParent([0]);
    },

    
    setValue: function(value, animate) {
        var args = Ext.toArray(arguments),
            len  = args.length;

        
        
        
        if (len == 1 || (len <= 3 && typeof arguments[1] != 'number')) {
            args.unshift(0);
        }

        return this.callParent(args);
    },

    
    getNearest : function(){
        
        return this.thumbs[0];
    }
});


Ext.define('Ext.tab.Tab', {
    extend: 'Ext.button.Button',
    alias: 'widget.tab',
    
    requires: [
        'Ext.layout.component.Tab',
        'Ext.util.KeyNav'
    ],

    componentLayout: 'tab',

    isTab: true,

    baseCls: Ext.baseCSSPrefix + 'tab',

    
    activeCls: 'active',
    
    

    
    closableCls: 'closable',

    
    closable: true,

    
    closeText: 'Close Tab',

    
    active: false,

    

    scale: false,

    position: 'top',
    
    initComponent: function() {
        var me = this;

        me.addEvents(
            
            'activate',

            
            'deactivate',

            
            'beforeclose',

            
            'close'
        );
        
        me.callParent(arguments);

        if (me.card) {
            me.setCard(me.card);
        }
    },

    
    onRender: function() {
        var me = this;
        
        me.addClsWithUI(me.position);
        
        
        

        me.syncClosableUI();

        me.callParent(arguments);
        
        if (me.active) {
            me.activate(true);
        }

        me.syncClosableElements();
        
        me.keyNav = Ext.create('Ext.util.KeyNav', me.el, {
            enter: me.onEnterKey,
            del: me.onDeleteKey,
            scope: me
        });
    },
    
    
    enable : function(silent) {
        var me = this;

        me.callParent(arguments);
        
        me.removeClsWithUI(me.position + '-disabled');

        return me;
    },

    
    disable : function(silent) {
        var me = this;
        
        me.callParent(arguments);
        
        me.addClsWithUI(me.position + '-disabled');

        return me;
    },
    
    
    onDestroy: function() {
        var me = this;

        if (me.closeEl) {
            me.closeEl.un('click', Ext.EventManager.preventDefault);
            me.closeEl = null;
        }

        Ext.destroy(me.keyNav);
        delete me.keyNav;

        me.callParent(arguments);
    },

    
    setClosable: function(closable) {
        var me = this;

        
        closable = (!arguments.length || !!closable);

        if (me.closable != closable) {
            me.closable = closable;

            
            if (me.card) {
                me.card.closable = closable;
            }

            me.syncClosableUI();

            if (me.rendered) {
                me.syncClosableElements();

                
                me.doComponentLayout();
                if (me.ownerCt) {
                    me.ownerCt.doLayout();
                }
            }
        }
    },

    
    syncClosableElements: function () {
        var me = this;

        if (me.closable) {
            if (!me.closeEl) {
                me.closeEl = me.el.createChild({
                    tag: 'a',
                    cls: me.baseCls + '-close-btn',
                    href: '#',
                    html: me.closeText,
                    title: me.closeText
                }).on('click', Ext.EventManager.preventDefault);  
            }
        } else {
            var closeEl = me.closeEl;
            if (closeEl) {
                closeEl.un('click', Ext.EventManager.preventDefault);
                closeEl.remove();
                me.closeEl = null;
            }
        }
    },

    
    syncClosableUI: function () {
        var me = this, classes = [me.closableCls, me.closableCls + '-' + me.position];

        if (me.closable) {
            me.addClsWithUI(classes);
        } else {
            me.removeClsWithUI(classes);
        }
    },

    
    setCard: function(card) {
        var me = this;

        me.card = card;
        me.setText(me.title || card.title);
        me.setIconCls(me.iconCls || card.iconCls);
    },

    
    onCloseClick: function() {
        var me = this;

        if (me.fireEvent('beforeclose', me) !== false) {
            if (me.tabBar) {
                if (me.tabBar.closeTab(me) === false) {
                    
                    return;
                }
            } else {
                
                me.fireEvent('close', me);
            }
        }
    },
    
    
    fireClose: function(){
        this.fireEvent('close', this);
    },
    
    
    onEnterKey: function(e) {
        var me = this;
        
        if (me.tabBar) {
            me.tabBar.onClick(e, me.el);
        }
    },
    
   
    onDeleteKey: function(e) {
        var me = this;
        
        if (me.closable) {
            me.onCloseClick();
        }
    },
    
    
    activate : function(supressEvent) {
        var me = this;
        
        me.active = true;
        me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);

        if (supressEvent !== true) {
            me.fireEvent('activate', me);
        }
    },

    
    deactivate : function(supressEvent) {
        var me = this;
        
        me.active = false;
        me.removeClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);
        
        if (supressEvent !== true) {
            me.fireEvent('deactivate', me);
        }
    }
});


Ext.define('Ext.tab.Bar', {
    extend: 'Ext.panel.Header',
    alias: 'widget.tabbar',
    baseCls: Ext.baseCSSPrefix + 'tab-bar',

    requires: [
        'Ext.tab.Tab',
        'Ext.FocusManager'
    ],

    
    defaultType: 'tab',

    
    plain: false,

    
    renderTpl: [
        '<div class="{baseCls}-body<tpl if="ui"> {baseCls}-body-{ui}<tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl></tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>></div>',
        '<div class="{baseCls}-strip<tpl if="ui"> {baseCls}-strip-{ui}<tpl for="uiCls"> {parent.baseCls}-strip-{parent.ui}-{.}</tpl></tpl>"></div>'
    ],

    
    minTabWidth: 30,

    
    maxTabWidth: undefined,

    
    initComponent: function() {
        var me = this,
            keys;

        if (me.plain) {
            me.setUI(me.ui + '-plain');
        }
        
        me.addClsWithUI(me.dock);

        me.addEvents(
            
            'change'
        );

        Ext.applyIf(me.renderSelectors, {
            body : '.' + me.baseCls + '-body',
            strip: '.' + me.baseCls + '-strip'
        });
        me.callParent(arguments);

        
        me.layout.align = (me.orientation == 'vertical') ? 'left' : 'top';
        me.layout.overflowHandler = Ext.create('Ext.layout.container.boxOverflow.Scroller', me.layout);
        me.items.removeAt(me.items.getCount() 
