Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / pkgs / classes.js
diff --git a/pkgs/classes.js b/pkgs/classes.js
new file mode 100644 (file)
index 0000000..3e8f2d9
--- /dev/null
@@ -0,0 +1,106524 @@
+/*
+Ext JS - JavaScript Library
+Copyright (c) 2006-2011, Sencha Inc.
+All rights reserved.
+licensing@sencha.com
+*/
+/**
+ * @class Ext.util.Observable
+ * Base class that provides a common interface for publishing events. Subclasses are expected to
+ * to have a property "events" with all the events defined, and, optionally, a property "listeners"
+ * with configured listeners defined.<br>
+ * For example:
+ * <pre><code>
+Employee = Ext.extend(Ext.util.Observable, {
+    constructor: function(config){
+        this.name = config.name;
+        this.addEvents({
+            "fired" : true,
+            "quit" : true
+        });
+
+        // Copy configured listeners into *this* object so that the base class&#39;s
+        // constructor will add them.
+        this.listeners = config.listeners;
+
+        // Call our superclass constructor to complete construction process.
+        Employee.superclass.constructor.call(this, config)
+    }
+});
+</code></pre>
+ * This could then be used like this:<pre><code>
+var newEmployee = new Employee({
+    name: employeeName,
+    listeners: {
+        quit: function() {
+            // By default, "this" will be the object that fired the event.
+            alert(this.name + " has quit!");
+        }
+    }
+});
+</code></pre>
+ */
+
+Ext.define('Ext.util.Observable', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.util.Event'],
+
+    statics: {
+        /**
+         * Removes <b>all</b> added captures from the Observable.
+         * @param {Observable} o The Observable to release
+         * @static
+         */
+        releaseCapture: function(o) {
+            o.fireEvent = this.prototype.fireEvent;
+        },
+
+        /**
+         * Starts capture on the specified Observable. All events will be passed
+         * to the supplied function with the event name + standard signature of the event
+         * <b>before</b> the event is fired. If the supplied function returns false,
+         * the event will not fire.
+         * @param {Observable} o The Observable to capture events from.
+         * @param {Function} fn The function to call when an event is fired.
+         * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
+         * @static
+         */
+        capture: function(o, fn, scope) {
+            o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
+        },
+
+        /**
+Sets observability on the passed class constructor.
+
+This makes any event fired on any instance of the passed class also fire a single event through
+the __class__ allowing for central handling of events on many instances at once.
+
+Usage:
+
+    Ext.util.Observable.observe(Ext.data.Connection);
+    Ext.data.Connection.on('beforerequest', function(con, options) {
+        console.log('Ajax request made to ' + options.url);
+    });
+
+         * @param {Function} c The class constructor to make observable.
+         * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
+         * @static
+         * @markdown
+         */
+        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;
+            }
+        }
+    },
+
+    /* End Definitions */
+
+    /**
+    * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
+    * object during initialization.  This should be a valid listeners config object as specified in the
+    * {@link #addListener} example for attaching multiple handlers at once.</p>
+    * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
+    * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
+    * is usually only done when extra value can be added. For example the {@link Ext.view.View DataView}'s
+    * <b><code>{@link Ext.view.View#click click}</code></b> event passing the node clicked on. To access DOM
+    * events directly from a child element of a Component, we need to specify the <code>element</code> option to
+    * identify the Component property to add a DOM listener to:
+    * <pre><code>
+new Ext.panel.Panel({
+    width: 400,
+    height: 200,
+    dockedItems: [{
+        xtype: 'toolbar'
+    }],
+    listeners: {
+        click: {
+            element: 'el', //bind to the underlying el property on the panel
+            fn: function(){ console.log('click el'); }
+        },
+        dblclick: {
+            element: 'body', //bind to the underlying body property on the panel
+            fn: function(){ console.log('dblclick body'); }
+        }
+    }
+});
+</code></pre>
+    * </p>
+    */
+    // @private
+    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);
+        }
+    },
+
+    // @private
+    eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal)$/,
+
+    /**
+     * <p>Adds listeners to any Observable object (or Element) which are automatically removed when this Component
+     * is destroyed.
+     * @param {Observable/Element} item The item to which to add a listener/listeners.
+     * @param {Object/String} ename The event name, or an object containing event name properties.
+     * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
+     * is the handler function.
+     * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
+     * is the scope (<code>this</code> reference) in which the handler function is executed.
+     * @param {Object} opt Optional. If the <code>ename</code> parameter was an event name, this
+     * is the {@link Ext.util.Observable#addListener addListener} options.
+     */
+    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);
+        }
+    },
+
+    /**
+     * Removes listeners that were added by the {@link #mon} method.
+     * @param {Observable|Element} item The item from which to remove a listener/listeners.
+     * @param {Object|String} ename The event name, or an object containing event name properties.
+     * @param {Function} fn Optional. If the <code>ename</code> parameter was an event name, this
+     * is the handler function.
+     * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this
+     * is the scope (<code>this</code> reference) in which the handler function is executed.
+     */
+     removeManagedListener : function(item, ename, fn, scope) {
+        var me = this,
+            options,
+            config,
+            managedListeners,
+            managedListener,
+            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() : [];
+        length = managedListeners.length;
+
+        for (i = 0; i < length; i++) {
+            managedListener = managedListeners[i];
+            if (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope)) {
+                Ext.Array.remove(me.managedListeners, managedListener);
+                item.un(managedListener.ename, managedListener.fn, managedListener.scope);
+            }
+        }
+    },
+
+    /**
+     * <p>Fires the specified event with the passed parameters (minus the event name).</p>
+     * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
+     * by calling {@link #enableBubble}.</p>
+     * @param {String} eventName The name of the event to fire.
+     * @param {Object...} args Variable number of parameters are passed to handlers.
+     * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
+     */
+    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;
+    },
+
+    /**
+     * Appends an event handler to this object.
+     * @param {String}   eventName The name of the event to listen for. May also be an object who's property names are event names. See
+     * @param {Function} handler The method the event invokes.
+     * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
+     * <b>If omitted, defaults to the object which fired the event.</b>
+     * @param {Object}   options (optional) An object containing handler configuration.
+     * properties. This may contain any of the following properties:<ul>
+     * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
+     * <b>If omitted, defaults to the object which fired the event.</b></div></li>
+     * <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
+     * <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
+     * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
+     * by the specified number of milliseconds. If the event fires again within that time, the original
+     * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
+     * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
+     * if the event was bubbled up from a child Observable.</div></li>
+     * <li><b>element</b> : String<div class="sub-desc"><b>This option is only valid for listeners bound to {@link Ext.Component Components}.</b>
+     * The name of a Component property which references an element to add a listener to.
+     * <p>This option is useful during Component construction to add DOM event listeners to elements of {@link Ext.Component Components} which
+     * will exist only after the Component is rendered. For example, to add a click listener to a Panel's body:<pre><code>
+new Ext.panel.Panel({
+    title: 'The title',
+    listeners: {
+        click: this.handlePanelClick,
+        element: 'body'
+    }
+});
+</code></pre></p>
+     * <p>When added in this way, the options available are the options applicable to {@link Ext.core.Element#addListener}</p></div></li>
+     * </ul><br>
+     * <p>
+     * <b>Combining Options</b><br>
+     * Using the options argument, it is possible to combine different types of listeners:<br>
+     * <br>
+     * A delayed, one-time listener.
+     * <pre><code>
+myPanel.on('hide', this.handleClick, this, {
+single: true,
+delay: 100
+});</code></pre>
+     * <p>
+     * <b>Attaching multiple handlers in 1 call</b><br>
+     * The method also allows for a single argument to be passed which is a config object containing properties
+     * which specify multiple events. For example:<pre><code>
+myGridPanel.on({
+    cellClick: this.onCellClick,
+    mouseover: this.onMouseOver,
+    mouseout: this.onMouseOut,
+    scope: this // Important. Ensure "this" is correct during handler execution
+});
+</code></pre>.
+     * <p>
+     */
+    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 : {});
+        }
+    },
+
+    /**
+     * Removes an event handler.
+     * @param {String}   eventName The type of event the handler was associated with.
+     * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
+     * @param {Object}   scope     (optional) The scope originally specified for the handler.
+     */
+    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.isEvent) {
+                event.removeListener(fn, scope);
+            }
+        }
+    },
+
+    /**
+     * Removes all listeners for this object including the managed listeners
+     */
+    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();
+    },
+
+    //<debug>
+    purgeListeners : function() {
+        console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
+        return this.clearListeners.apply(this, arguments);
+    },
+    //</debug>
+
+    /**
+     * Removes all managed listeners for this object.
+     */
+    clearManagedListeners : function() {
+        var managedListeners = this.managedListeners || [],
+            i = 0,
+            len = managedListeners.length,
+            managedListener;
+
+        for (; i < len; i++) {
+            managedListener = managedListeners[i];
+            managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
+        }
+
+        this.managedListeners = [];
+    },
+
+    //<debug>
+    purgeManagedListeners : function() {
+        console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
+        return this.clearManagedListeners.apply(this, arguments);
+    },
+    //</debug>
+
+    /**
+     * Adds the specified events to the list of events which this Observable may fire.
+     * @param {Object/String} o Either an object with event names as properties with a value of <code>true</code>
+     * or the first event name string if multiple event names are being passed as separate parameters.
+     * @param {String} [additional] Optional additional event names if multiple event names are being passed as separate parameters.
+     * Usage:<pre><code>
+this.addEvents('storeloaded', 'storecleared');
+</code></pre>
+     */
+    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);
+        }
+    },
+
+    /**
+     * Checks to see if this object has any listeners for a specified event
+     * @param {String} eventName The name of the event to check for
+     * @return {Boolean} True if the event is being listened for, else false
+     */
+    hasListener: function(ename) {
+        var event = this.events[ename.toLowerCase()];
+        return event && event.isEvent === true && event.listeners.length > 0;
+    },
+
+    /**
+     * Suspend the firing of all events. (see {@link #resumeEvents})
+     * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
+     * after the {@link #resumeEvents} call instead of discarding all suspended events;
+     */
+    suspendEvents: function(queueSuspended) {
+        this.eventsSuspended = true;
+        if (queueSuspended && !this.eventQueue) {
+            this.eventQueue = [];
+        }
+    },
+
+    /**
+     * Resume firing events. (see {@link #suspendEvents})
+     * If events were suspended using the <code><b>queueSuspended</b></code> parameter, then all
+     * events fired during event suspension will be sent to any listeners now.
+     */
+    resumeEvents: function() {
+        var me = this,
+            queued = me.eventQueue || [];
+
+        me.eventsSuspended = false;
+        delete me.eventQueue;
+
+        Ext.each(queued,
+        function(e) {
+            me.fireEvent.apply(me, e);
+        });
+    },
+
+    /**
+     * Relays selected events from the specified Observable as if the events were fired by <code><b>this</b></code>.
+     * @param {Object} origin The Observable whose events this object is to relay.
+     * @param {Array} events Array of event names to relay.
+     */
+    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));
+        }
+    },
+
+    /**
+     * @private
+     * Creates an event handling function which refires the event from this object as the passed event name.
+     * @param newName
+     * @returns {Function}
+     */
+    createRelayer: function(newName){
+        var me = this;
+        return function(){
+            return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
+        };
+    },
+
+    /**
+     * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
+     * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
+     * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component#getBubbleTarget}. The default
+     * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
+     * access the required target more quickly.</p>
+     * <p>Example:</p><pre><code>
+Ext.override(Ext.form.field.Base, {
+//  Add functionality to Field&#39;s initComponent to enable the change event to bubble
+initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
+    this.enableBubble('change');
+}),
+
+//  We know that we want Field&#39;s events to bubble directly to the FormPanel.
+getBubbleTarget : function() {
+    if (!this.formPanel) {
+        this.formPanel = this.findParentByType('form');
+    }
+    return this.formPanel;
+}
+});
+
+var myForm = new Ext.formPanel({
+title: 'User Details',
+items: [{
+    ...
+}],
+listeners: {
+    change: function() {
+        // Title goes red if form has been modified.
+        myForm.header.setStyle('color', 'red');
+    }
+}
+});
+</code></pre>
+     * @param {String/Array} events The event name to bubble, or an Array of event names.
+     */
+    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() {
+    /**
+     * Removes an event handler (shorthand for {@link #removeListener}.)
+     * @param {String}   eventName     The type of event the handler was associated with.
+     * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
+     * @param {Object}   scope         (optional) The scope originally specified for the handler.
+     * @method un
+     */
+
+    /**
+     * Appends an event handler to this object (shorthand for {@link #addListener}.)
+     * @param {String}   eventName     The type of event to listen for
+     * @param {Function} handler       The method the event invokes
+     * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
+     * <b>If omitted, defaults to the object which fired the event.</b>
+     * @param {Object}   options       (optional) An object containing handler configuration.
+     * @method on
+     */
+
+    this.createAlias({
+        on: 'addListener',
+        un: 'removeListener',
+        mon: 'addManagedListener',
+        mun: 'removeManagedListener'
+    });
+
+    //deprecated, will be removed in 5.0
+    this.observeClass = this.observe;
+
+    Ext.apply(Ext.util.Observable.prototype, function(){
+        // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
+        // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
+        // private
+        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 {
+            // these are considered experimental
+            // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
+            // adds an 'interceptor' called before the original method
+            beforeMethod : function(method, fn, scope){
+                getMethodEvent.call(this, method).before.push({
+                    fn: fn,
+                    scope: scope
+                });
+            },
+
+            // adds a 'sequence' called after the original method
+            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);
+                    }
+                });
+            }
+        };
+    }());
+});
+
+/**
+ * @class Ext.util.Animate
+ * This animation class is a mixin.
+ * 
+ * Ext.util.Animate provides an API for the creation of animated transitions of properties and styles.  
+ * This class is used as a mixin and currently applied to {@link Ext.core.Element}, {@link Ext.CompositeElement}, 
+ * {@link Ext.draw.Sprite}, {@link Ext.draw.CompositeSprite}, and {@link Ext.Component}.  Note that Components 
+ * have a limited subset of what attributes can be animated such as top, left, x, y, height, width, and 
+ * opacity (color, paddings, and margins can not be animated).
+ * 
+ * ## Animation Basics
+ * 
+ * All animations require three things - `easing`, `duration`, and `to` (the final end value for each property) 
+ * you wish to animate. Easing and duration are defaulted values specified below.
+ * Easing describes how the intermediate values used during a transition will be calculated. 
+ * {@link Ext.fx.Anim#easing Easing} allows for a transition to change speed over its duration.
+ * You may use the defaults for easing and duration, but you must always set a 
+ * {@link Ext.fx.Anim#to to} property which is the end value for all animations.  
+ * 
+ * Popular element 'to' configurations are:
+ * 
+ *  - opacity
+ *  - x
+ *  - y
+ *  - color
+ *  - height
+ *  - width 
+ * 
+ * Popular sprite 'to' configurations are:
+ * 
+ *  - translation
+ *  - path
+ *  - scale
+ *  - stroke
+ *  - rotation
+ * 
+ * The default duration for animations is 250 (which is a 1/4 of a second).  Duration is denoted in 
+ * milliseconds.  Therefore 1 second is 1000, 1 minute would be 60000, and so on. The default easing curve 
+ * used for all animations is 'ease'.  Popular easing functions are included and can be found in {@link Ext.fx.Anim#easing Easing}.
+ * 
+ * For example, a simple animation to fade out an element with a default easing and duration:
+ * 
+ *     var p1 = Ext.get('myElementId');
+ * 
+ *     p1.animate({
+ *         to: {
+ *             opacity: 0
+ *         }
+ *     });
+ * 
+ * To make this animation fade out in a tenth of a second:
+ * 
+ *     var p1 = Ext.get('myElementId');
+ * 
+ *     p1.animate({
+ *        duration: 100,
+ *         to: {
+ *             opacity: 0
+ *         }
+ *     });
+ * 
+ * ## Animation Queues
+ * 
+ * By default all animations are added to a queue which allows for animation via a chain-style API.
+ * For example, the following code will queue 4 animations which occur sequentially (one right after the other):
+ * 
+ *     p1.animate({
+ *         to: {
+ *             x: 500
+ *         }
+ *     }).animate({
+ *         to: {
+ *             y: 150
+ *         }
+ *     }).animate({
+ *         to: {
+ *             backgroundColor: '#f00'  //red
+ *         }
+ *     }).animate({
+ *         to: {
+ *             opacity: 0
+ *         }
+ *     });
+ * 
+ * You can change this behavior by calling the {@link Ext.util.Animate#syncFx syncFx} method and all 
+ * subsequent animations for the specified target will be run concurrently (at the same time).
+ * 
+ *     p1.syncFx();  //this will make all animations run at the same time
+ * 
+ *     p1.animate({
+ *         to: {
+ *             x: 500
+ *         }
+ *     }).animate({
+ *         to: {
+ *             y: 150
+ *         }
+ *     }).animate({
+ *         to: {
+ *             backgroundColor: '#f00'  //red
+ *         }
+ *     }).animate({
+ *         to: {
+ *             opacity: 0
+ *         }
+ *     });
+ * 
+ * This works the same as:
+ * 
+ *     p1.animate({
+ *         to: {
+ *             x: 500,
+ *             y: 150,
+ *             backgroundColor: '#f00'  //red
+ *             opacity: 0
+ *         }
+ *     });
+ * 
+ * The {@link Ext.util.Animate#stopAnimation stopAnimation} method can be used to stop any 
+ * currently running animations and clear any queued animations. 
+ * 
+ * ## Animation Keyframes
+ *
+ * You can also set up complex animations with {@link Ext.fx.Anim#keyframe keyframe} which follows the 
+ * CSS3 Animation configuration pattern. Note rotation, translation, and scaling can only be done for sprites. 
+ * The previous example can be written with the following syntax:
+ * 
+ *     p1.animate({
+ *         duration: 1000,  //one second total
+ *         keyframes: {
+ *             25: {     //from 0 to 250ms (25%)
+ *                 x: 0
+ *             },
+ *             50: {   //from 250ms to 500ms (50%)
+ *                 y: 0
+ *             },
+ *             75: {  //from 500ms to 750ms (75%)
+ *                 backgroundColor: '#f00'  //red
+ *             },
+ *             100: {  //from 750ms to 1sec
+ *                 opacity: 0
+ *             }
+ *         }
+ *     });
+ * 
+ * ## Animation Events
+ * 
+ * Each animation you create has events for {@link Ext.fx.Anim#beforeanimation beforeanimation}, 
+ * {@link Ext.fx.Anim#afteranimate afteranimate}, and {@link Ext.fx.Anim#lastframe lastframe}.  
+ * Keyframed animations adds an additional {@link Ext.fx.Animator#keyframe keyframe} event which 
+ * fires for each keyframe in your animation.
+ * 
+ * All animations support the {@link Ext.util.Observable#listeners listeners} configuration to attact functions to these events.
+ *    
+ *     startAnimate: function() {
+ *         var p1 = Ext.get('myElementId');
+ *         p1.animate({
+ *            duration: 100,
+ *             to: {
+ *                 opacity: 0
+ *             },
+ *             listeners: {
+ *                 beforeanimate:  function() {
+ *                     // Execute my custom method before the animation
+ *                     this.myBeforeAnimateFn();
+ *                 },
+ *                 afteranimate: function() {
+ *                     // Execute my custom method after the animation
+ *                     this.myAfterAnimateFn();
+ *                 },
+ *                 scope: this
+ *         });
+ *     },
+ *     myBeforeAnimateFn: function() {
+ *       // My custom logic
+ *     },
+ *     myAfterAnimateFn: function() {
+ *       // My custom logic
+ *     }
+ * 
+ * Due to the fact that animations run asynchronously, you can determine if an animation is currently 
+ * running on any target by using the {@link Ext.util.Animate#getActiveAnimation getActiveAnimation} 
+ * method.  This method will return false if there are no active animations or return the currently 
+ * running {@link Ext.fx.Anim} instance.
+ * 
+ * In this example, we're going to wait for the current animation to finish, then stop any other 
+ * queued animations before we fade our element's opacity to 0:
+ * 
+ *     var curAnim = p1.getActiveAnimation();
+ *     if (curAnim) {
+ *         curAnim.on('afteranimate', function() {
+ *             p1.stopAnimation();
+ *             p1.animate({
+ *                 to: {
+ *                     opacity: 0
+ *                 }
+ *             });
+ *         });
+ *     }
+ * 
+ * @docauthor Jamie Avins <jamie@sencha.com>
+ */
+Ext.define('Ext.util.Animate', {
+
+    uses: ['Ext.fx.Manager', 'Ext.fx.Anim'],
+
+    /**
+     * <p>Perform custom animation on this object.<p>
+     * <p>This method is applicable to both the the {@link Ext.Component Component} class and the {@link Ext.core.Element Element} class.
+     * It performs animated transitions of certain properties of this object over a specified timeline.</p>
+     * <p>The sole parameter is an object which specifies start property values, end property values, and properties which
+     * describe the timeline. Of the properties listed below, only <b><code>to</code></b> is mandatory.</p>
+     * <p>Properties include<ul>
+     * <li><code>from</code> <div class="sub-desc">An object which specifies start values for the properties being animated.
+     * If not supplied, properties are animated from current settings. The actual properties which may be animated depend upon
+     * ths object being animated. See the sections below on Element and Component animation.<div></li>
+     * <li><code>to</code> <div class="sub-desc">An object which specifies end values for the properties being animated.</div></li>
+     * <li><code>duration</code><div class="sub-desc">The duration <b>in milliseconds</b> for which the animation will run.</div></li>
+     * <li><code>easing</code> <div class="sub-desc">A string value describing an easing type to modify the rate of change from the default linear to non-linear. Values may be one of:<code><ul>
+     * <li>ease</li>
+     * <li>easeIn</li>
+     * <li>easeOut</li>
+     * <li>easeInOut</li>
+     * <li>backIn</li>
+     * <li>backOut</li>
+     * <li>elasticIn</li>
+     * <li>elasticOut</li>
+     * <li>bounceIn</li>
+     * <li>bounceOut</li>
+     * </ul></code></div></li>
+     * <li><code>keyframes</code> <div class="sub-desc">This is an object which describes the state of animated properties at certain points along the timeline.
+     * it is an object containing properties who's names are the percentage along the timeline being described and who's values specify the animation state at that point.</div></li>
+     * <li><code>listeners</code> <div class="sub-desc">This is a standard {@link Ext.util.Observable#listeners listeners} configuration object which may be used
+     * to inject behaviour at either the <code>beforeanimate</code> event or the <code>afteranimate</code> event.</div></li>
+     * </ul></p>
+     * <h3>Animating an {@link Ext.core.Element Element}</h3>
+     * When animating an Element, the following properties may be specified in <code>from</code>, <code>to</code>, and <code>keyframe</code> objects:<ul>
+     * <li><code>x</code> <div class="sub-desc">The page X position in pixels.</div></li>
+     * <li><code>y</code> <div class="sub-desc">The page Y position in pixels</div></li>
+     * <li><code>left</code> <div class="sub-desc">The element's CSS <code>left</code> value. Units must be supplied.</div></li>
+     * <li><code>top</code> <div class="sub-desc">The element's CSS <code>top</code> value. Units must be supplied.</div></li>
+     * <li><code>width</code> <div class="sub-desc">The element's CSS <code>width</code> value. Units must be supplied.</div></li>
+     * <li><code>height</code> <div class="sub-desc">The element's CSS <code>height</code> value. Units must be supplied.</div></li>
+     * <li><code>scrollLeft</code> <div class="sub-desc">The element's <code>scrollLeft</code> value.</div></li>
+     * <li><code>scrollTop</code> <div class="sub-desc">The element's <code>scrollLeft</code> value.</div></li>
+     * <li><code>opacity</code> <div class="sub-desc">The element's <code>opacity</code> value. This must be a value between <code>0</code> and <code>1</code>.</div></li>
+     * </ul>
+     * <p><b>Be aware than animating an Element which is being used by an Ext Component without in some way informing the Component about the changed element state
+     * will result in incorrect Component behaviour. This is because the Component will be using the old state of the element. To avoid this problem, it is now possible to
+     * directly animate certain properties of Components.</b></p>
+     * <h3>Animating a {@link Ext.Component Component}</h3>
+     * When animating an Element, the following properties may be specified in <code>from</code>, <code>to</code>, and <code>keyframe</code> objects:<ul>
+     * <li><code>x</code> <div class="sub-desc">The Component's page X position in pixels.</div></li>
+     * <li><code>y</code> <div class="sub-desc">The Component's page Y position in pixels</div></li>
+     * <li><code>left</code> <div class="sub-desc">The Component's <code>left</code> value in pixels.</div></li>
+     * <li><code>top</code> <div class="sub-desc">The Component's <code>top</code> value in pixels.</div></li>
+     * <li><code>width</code> <div class="sub-desc">The Component's <code>width</code> value in pixels.</div></li>
+     * <li><code>width</code> <div class="sub-desc">The Component's <code>width</code> value in pixels.</div></li>
+     * <li><code>dynamic</code> <div class="sub-desc">Specify as true to update the Component's layout (if it is a Container) at every frame
+     * of the animation. <i>Use sparingly as laying out on every intermediate size change is an expensive operation</i>.</div></li>
+     * </ul>
+     * <p>For example, to animate a Window to a new size, ensuring that its internal layout, and any shadow is correct:</p>
+     * <pre><code>
+myWindow = Ext.create('Ext.window.Window', {
+    title: 'Test Component animation',
+    width: 500,
+    height: 300,
+    layout: {
+        type: 'hbox',
+        align: 'stretch'
+    },
+    items: [{
+        title: 'Left: 33%',
+        margins: '5 0 5 5',
+        flex: 1
+    }, {
+        title: 'Left: 66%',
+        margins: '5 5 5 5',
+        flex: 2
+    }]
+});
+myWindow.show();
+myWindow.header.el.on('click', function() {
+    myWindow.animate({
+        to: {
+            width: (myWindow.getWidth() == 500) ? 700 : 500,
+            height: (myWindow.getHeight() == 300) ? 400 : 300,
+        }
+    });
+});
+</code></pre>
+     * <p>For performance reasons, by default, the internal layout is only updated when the Window reaches its final <code>"to"</code> size. If dynamic updating of the Window's child
+     * Components is required, then configure the animation with <code>dynamic: true</code> and the two child items will maintain their proportions during the animation.</p>
+     * @param {Object} config An object containing properties which describe the animation's start and end states, and the timeline of the animation.
+     * @return {Object} this
+     */
+    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;
+    },
+
+    // @private - process the passed fx configuration.
+    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);
+    },
+
+    /**
+     * Stops any running effects and clears this object's internal effects queue if it contains
+     * any additional effects that haven't started yet.
+     * @return {Ext.core.Element} The Element
+     */
+    stopFx: Ext.Function.alias(Ext.util.Animate, 'stopAnimation'),
+
+    /**
+     * @deprecated 4.0 Replaced by {@link #stopAnimation}
+     * Stops any running effects and clears this object's internal effects queue if it contains
+     * any additional effects that haven't started yet.
+     * @return {Ext.core.Element} The Element
+     */
+    stopAnimation: function() {
+        Ext.fx.Manager.stopAnimation(this.id);
+    },
+
+    /**
+     * Ensures that all effects queued after syncFx is called on this object are
+     * run concurrently.  This is the opposite of {@link #sequenceFx}.
+     * @return {Ext.core.Element} The Element
+     */
+    syncFx: function() {
+        Ext.fx.Manager.setFxDefaults(this.id, {
+            concurrent: true
+        });
+    },
+
+    /**
+     * Ensures that all effects queued after sequenceFx is called on this object are
+     * run in sequence.  This is the opposite of {@link #syncFx}.
+     * @return {Ext.core.Element} The Element
+     */
+    sequenceFx: function() {
+        Ext.fx.Manager.setFxDefaults(this.id, {
+            concurrent: false
+        });
+    },
+
+    /**
+     * @deprecated 4.0 Replaced by {@link #getActiveAnimation}
+     * Returns thq current animation if this object has any effects actively running or queued, else returns false.
+     * @return {Mixed} anim if element has active effects, else false
+     */
+    hasActiveFx: Ext.Function.alias(Ext.util.Animate, 'getActiveAnimation'),
+
+    /**
+     * Returns thq current animation if this object has any effects actively running or queued, else returns false.
+     * @return {Mixed} anim if element has active effects, else false
+     */
+    getActiveAnimation: function() {
+        return Ext.fx.Manager.getActiveAnimation(this.id);
+    }
+});
+
+// Apply Animate mixin manually until Element is defined in the proper 4.x way
+Ext.applyIf(Ext.core.Element.prototype, Ext.util.Animate.prototype);
+/**
+ * @class Ext.state.Provider
+ * <p>Abstract base class for state provider implementations. The provider is responsible
+ * for setting values  and extracting values to/from the underlying storage source. The 
+ * storage source can vary and the details should be implemented in a subclass. For example
+ * a provider could use a server side database or the browser localstorage where supported.</p>
+ *
+ * <p>This class provides methods for encoding and decoding <b>typed</b> variables including 
+ * dates and defines the Provider interface. By default these methods put the value and the
+ * type information into a delimited string that can be stored. These should be overridden in 
+ * a subclass if you want to change the format of the encoded value and subsequent decoding.</p>
+ */
+Ext.define('Ext.state.Provider', {
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+    
+    /**
+     * @cfg {String} prefix A string to prefix to items stored in the underlying state store. 
+     * Defaults to <tt>'ext-'</tt>
+     */
+    prefix: 'ext-',
+    
+    constructor : function(config){
+        config = config || {};
+        var me = this;
+        Ext.apply(me, config);
+        /**
+         * @event statechange
+         * Fires when a state change occurs.
+         * @param {Provider} this This state provider
+         * @param {String} key The state key which was changed
+         * @param {String} value The encoded value for the state
+         */
+        me.addEvents("statechange");
+        me.state = {};
+        me.mixins.observable.constructor.call(me);
+    },
+    
+    /**
+     * Returns the current value for a key
+     * @param {String} name The key name
+     * @param {Mixed} defaultValue A default value to return if the key's value is not found
+     * @return {Mixed} The state data
+     */
+    get : function(name, defaultValue){
+        return typeof this.state[name] == "undefined" ?
+            defaultValue : this.state[name];
+    },
+
+    /**
+     * Clears a value from the state
+     * @param {String} name The key name
+     */
+    clear : function(name){
+        var me = this;
+        delete me.state[name];
+        me.fireEvent("statechange", me, name, null);
+    },
+
+    /**
+     * Sets the value for a key
+     * @param {String} name The key name
+     * @param {Mixed} value The value to set
+     */
+    set : function(name, value){
+        var me = this;
+        me.state[name] = value;
+        me.fireEvent("statechange", me, name, value);
+    },
+
+    /**
+     * Decodes a string previously encoded with {@link #encodeValue}.
+     * @param {String} value The value to decode
+     * @return {Mixed} The decoded value
+     */
+    decodeValue : function(value){
+
+        // a -> Array
+        // n -> Number
+        // d -> Date
+        // b -> Boolean
+        // s -> String
+        // o -> Object
+        // -> Empty (null)
+
+        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; // non state
+        }
+        
+        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;
+        }
+    },
+
+    /**
+     * Encodes a value including type information.  Decode with {@link #decodeValue}.
+     * @param {Mixed} value The value to encode
+     * @return {String} The encoded 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);
+    }
+});
+/**
+ * @class Ext.util.HashMap
+ * <p>
+ * Represents a collection of a set of key and value pairs. Each key in the HashMap
+ * must be unique, the same key cannot exist twice. Access to items is provided via
+ * the key only. Sample usage:
+ * <pre><code>
+var map = new Ext.util.HashMap();
+map.add('key1', 1);
+map.add('key2', 2);
+map.add('key3', 3);
+
+map.each(function(key, value, length){
+    console.log(key, value, length);
+});
+ * </code></pre>
+ * </p>
+ *
+ * <p>The HashMap is an unordered class,
+ * there is no guarantee when iterating over the items that they will be in any particular
+ * order. If this is required, then use a {@link Ext.util.MixedCollection}.
+ * </p>
+ * @constructor
+ * @param {Object} config The configuration options
+ */
+Ext.define('Ext.util.HashMap', {
+
+    /**
+     * @cfg {Function} keyFn A function that is used to retrieve a default key for a passed object.
+     * A default is provided that returns the <b>id</b> property on the object. This function is only used
+     * if the add method is called with a single argument.
+     */
+
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+
+    constructor: function(config) {
+        var me = this;
+
+        me.addEvents(
+            /**
+             * @event add
+             * Fires when a new item is added to the hash
+             * @param {Ext.util.HashMap} this.
+             * @param {String} key The key of the added item.
+             * @param {Object} value The value of the added item.
+             */
+            'add',
+            /**
+             * @event clear
+             * Fires when the hash is cleared.
+             * @param {Ext.util.HashMap} this.
+             */
+            'clear',
+            /**
+             * @event remove
+             * Fires when an item is removed from the hash.
+             * @param {Ext.util.HashMap} this.
+             * @param {String} key The key of the removed item.
+             * @param {Object} value The value of the removed item.
+             */
+            'remove',
+            /**
+             * @event replace
+             * Fires when an item is replaced in the hash.
+             * @param {Ext.util.HashMap} this.
+             * @param {String} key The key of the replaced item.
+             * @param {Object} value The new value for the item.
+             * @param {Object} old The old value for the item.
+             */
+            'replace'
+        );
+
+        me.mixins.observable.constructor.call(me, config);
+        me.clear(true);
+    },
+
+    /**
+     * Gets the number of items in the hash.
+     * @return {Number} The number of items in the hash.
+     */
+    getCount: function() {
+        return this.length;
+    },
+
+    /**
+     * Implementation for being able to extract the key from an object if only
+     * a single argument is passed.
+     * @private
+     * @param {String} key The key
+     * @param {Object} value The value
+     * @return {Array} [key, value]
+     */
+    getData: function(key, value) {
+        // if we have no value, it means we need to get the key from the object
+        if (value === undefined) {
+            value = key;
+            key = this.getKey(value);
+        }
+
+        return [key, value];
+    },
+
+    /**
+     * Extracts the key from an object. This is a default implementation, it may be overridden
+     * @private
+     * @param {Object} o The object to get the key from
+     * @return {String} The key to use.
+     */
+    getKey: function(o) {
+        return o.id;
+    },
+
+    /**
+     * Adds an item to the collection. Fires the {@link #add} event when complete.
+     * @param {String} key <p>The key to associate with the item, or the new item.</p>
+     * <p>If a {@link #getKey} implementation was specified for this HashMap,
+     * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
+     * the HashMap will be able to <i>derive</i> the key for the new item.
+     * In this case just pass the new item in this parameter.</p>
+     * @param {Object} o The item to add.
+     * @return {Object} The item added.
+     */
+    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;
+    },
+
+    /**
+     * Replaces an item in the hash. If the key doesn't exist, the
+     * {@link #add} method will be used.
+     * @param {String} key The key of the item.
+     * @param {Object} value The new value for the item.
+     * @return {Object} The new value of the item.
+     */
+    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 an item from the hash.
+     * @param {Object} o The value of the item to remove.
+     * @return {Boolean} True if the item was successfully removed.
+     */
+    remove: function(o) {
+        var key = this.findKey(o);
+        if (key !== undefined) {
+            return this.removeAtKey(key);
+        }
+        return false;
+    },
+
+    /**
+     * Remove an item from the hash.
+     * @param {String} key The key to remove.
+     * @return {Boolean} True if the item was successfully removed.
+     */
+    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;
+    },
+
+    /**
+     * Retrieves an item with a particular key.
+     * @param {String} key The key to lookup.
+     * @return {Object} The value at that key. If it doesn't exist, <tt>undefined</tt> is returned.
+     */
+    get: function(key) {
+        return this.map[key];
+    },
+
+    /**
+     * Removes all items from the hash.
+     * @return {Ext.util.HashMap} this
+     */
+    clear: function(/* private */ initial) {
+        var me = this;
+        me.map = {};
+        me.length = 0;
+        if (initial !== true) {
+            me.fireEvent('clear', me);
+        }
+        return me;
+    },
+
+    /**
+     * Checks whether a key exists in the hash.
+     * @param {String} key The key to check for.
+     * @return {Boolean} True if they key exists in the hash.
+     */
+    containsKey: function(key) {
+        return this.map[key] !== undefined;
+    },
+
+    /**
+     * Checks whether a value exists in the hash.
+     * @param {Object} value The value to check for.
+     * @return {Boolean} True if the value exists in the dictionary.
+     */
+    contains: function(value) {
+        return this.containsKey(this.findKey(value));
+    },
+
+    /**
+     * Return all of the keys in the hash.
+     * @return {Array} An array of keys.
+     */
+    getKeys: function() {
+        return this.getArray(true);
+    },
+
+    /**
+     * Return all of the values in the hash.
+     * @return {Array} An array of values.
+     */
+    getValues: function() {
+        return this.getArray(false);
+    },
+
+    /**
+     * Gets either the keys/values in an array from the hash.
+     * @private
+     * @param {Boolean} isKey True to extract the keys, otherwise, the value
+     * @return {Array} An array of either keys/values from the hash.
+     */
+    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;
+    },
+
+    /**
+     * Executes the specified function once for each item in the hash.
+     * 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 hash</p></li>
+     * </ul></div>
+     * @param {Function} fn The function to execute.
+     * @param {Object} scope The scope to execute in. Defaults to <tt>this</tt>.
+     * @return {Ext.util.HashMap} this
+     */
+    each: function(fn, scope) {
+        // copy items so they may be removed during iteration.
+        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;
+    },
+
+    /**
+     * Performs a shallow copy on this hash.
+     * @return {Ext.util.HashMap} The new hash object.
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * Find the key for a value.
+     * @param {Object} value The value to find.
+     * @return {Object} The value of the item. Returns <tt>undefined</tt> if not found.
+     */
+    findKey: function(value) {
+        var key,
+            map = this.map;
+
+        for (key in map) {
+            if (map.hasOwnProperty(key) && map[key] === value) {
+                return key;
+            }
+        }
+        return undefined;
+    }
+});
+
+/**
+ * @class Ext.Template
+ * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
+ * for greater performance.</p>
+ * An instance of this class may be created by passing to the constructor either
+ * a single argument, or multiple arguments:
+ * <div class="mdetail-params"><ul>
+ * <li><b>single argument</b> : String/Array
+ * <div class="sub-desc">
+ * The single argument may be either a String or an Array:<ul>
+ * <li><tt>String</tt> : </li><pre><code>
+var t = new Ext.Template("&lt;div>Hello {0}.&lt;/div>");
+t.{@link #append}('some-element', ['foo']);
+   </code></pre>
+ * <li><tt>Array</tt> : </li>
+ * An Array will be combined with <code>join('')</code>.
+<pre><code>
+var t = new Ext.Template([
+    '&lt;div name="{id}"&gt;',
+        '&lt;span class="{cls}"&gt;{name:trim} {value:ellipsis(10)}&lt;/span&gt;',
+    '&lt;/div&gt;',
+]);
+t.{@link #compile}();
+t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
+   </code></pre>
+ * </ul></div></li>
+ * <li><b>multiple arguments</b> : String, Object, Array, ...
+ * <div class="sub-desc">
+ * Multiple arguments will be combined with <code>join('')</code>.
+ * <pre><code>
+var t = new Ext.Template(
+    '&lt;div name="{id}"&gt;',
+        '&lt;span class="{cls}"&gt;{name} {value}&lt;/span&gt;',
+    '&lt;/div&gt;',
+    // a configuration object:
+    {
+        compiled: true,      // {@link #compile} immediately
+    }
+);
+   </code></pre>
+ * <p><b>Notes</b>:</p>
+ * <div class="mdetail-params"><ul>
+ * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
+ * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
+ * when no formatting is required.</li>
+ * </ul></div>
+ * </div></li>
+ * </ul></div>
+ * @param {Mixed} config
+ */
+
+Ext.define('Ext.Template', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.core.DomHelper', 'Ext.util.Format'],
+
+    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
+         * @param {Object} config A configuration object
+         * @return {Ext.Template} The created template
+         * @static
+         */
+        from: function(el, config) {
+            el = Ext.getDom(el);
+            return new this(el.value || el.innerHTML, config || '');
+        }
+    },
+
+    /* End Definitions */
+
+    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);
+            }
+        }
+
+        // @private
+        me.html = buffer.join('');
+
+        if (me.compiled) {
+            me.compile();
+        }
+    },
+    isTemplate: true,
+    /**
+     * @cfg {Boolean} disableFormats true to disable format functions in the template. If the template doesn't contain format functions, setting
+     * disableFormats to true will reduce apply time (defaults to false)
+     */
+    disableFormats: false,
+
+    re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
+    /**
+     * Returns an HTML fragment of this template with the specified values applied.
+     * @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'})
+     * @return {String} The HTML fragment
+     * @hide repeat doc
+     */
+    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);
+    },
+
+    /**
+     * Sets the HTML used as the template and optionally compiles it.
+     * @param {String} html
+     * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
+     * @return {Ext.Template} this
+     */
+    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
+         * @method query
+         */
+        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
+         * @method query
+         */
+        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
+                        }
+                        //<debug>
+                        // Exhausted all matches: It's an error
+                        if (i === (length - 1)) {
+                            Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
+                        }
+                        //</debug>
+                    }
+                }
+
+                // 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);
+        
+        //<debug>
+        if (me.property == undefined && me.sorterFn == undefined) {
+            Ext.Error.raise("A Sorter requires either a property or a sorter function");
+        }
+        //</debug>
+        
+        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) {
+        //<debug>
+        if (!this.target) {
+            Ext.Error.raise('A valid target is required when loading content');
+        }
+        //</debug>
+
+        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
+        });
+
+        //<debug>
+        if (!options.url) {
+            Ext.Error.raise('You must specify the URL from which content should be loaded');
+        }
+        //</debug>
+
+        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];
+
+        //<debug>
+        if (Constructor == undefined) {
+            Ext.Error.raise("The '" + type + "' type has not been registered with this manager");
+        }
+        //</debug>
+
+        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 || {});
+        }
+    },
+
+    /* End Definitions */
+
+    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;
+    },
+
+    // @private
+    applySubTemplate: function(id, values, parent, xindex, xcount) {
+        var me = this, t = me.tpls[id];
+        return t.compiled.call(me, values, parent, xindex, xcount);
+    },
+    /**
+     * @cfg {RegExp} codeRe The regular expression used to match code variables (default: matches <tt>{[expression]}</tt>).
+     */
+    codeRe: /\{\[((?:\\\]|.|\n)*?)\]\}/g,
+
+    re: /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?\}/g,
+
+    // @private
+    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;
+            // name is what is inside the {}
+            // Name begins with xtpl, use a Sub Template
+            if (name.substr(0, 4) == 'xtpl') {
+                return "',this.applySubTemplate(" + name.substr(4) + ", values, parent, xindex, xcount),'";
+            }
+            // name = "." - Just use the values object.
+            if (name == '.') {
+                // filter to not include arrays/objects/nulls
+                v = 'Ext.Array.indexOf(["string", "number", "boolean"], typeof values) > -1 || Ext.isDate(values) ? values : ""';
+            }
+
+            // name = "#" - Use the xindex
+            else if (name == '#') {
+                v = 'xindex';
+            }
+            else if (name.substr(0, 7) == "parent.") {
+                v = name;
+            }
+            // name has a . in it - Use object literal notation, starting from values
+            else if (name.indexOf('.') != -1) {
+                v = "values." + name;
+            }
+
+            // name is a property of values
+            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) {
+            // Single quotes get escaped when the template is compiled, however we want to undo this when running 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;
+    },
+
+    /**
+     * Returns an HTML fragment of this template with the specified values applied.
+     * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
+     * @return {String} The HTML fragment
+     */
+    applyTemplate: function(values) {
+        return this.master.compiled.call(this, values, {}, 1, 1);
+    },
+
+    /**
+     * Compile the template to a function for optimized performance.  Recommended if the template will be used frequently.
+     * @return {Function} The compiled function
+     */
+    compile: function() {
+        return this;
+    }
+}, function() {
+    /**
+     * Alias for {@link #applyTemplate}
+     * Returns an HTML fragment of this template with the specified values applied.
+     * @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'})
+     * @return {String} The HTML fragment
+     * @member Ext.XTemplate
+     * @method apply
+     */
+    this.createAlias('apply', 'applyTemplate');
+});
+
+/**
+ * @class Ext.util.AbstractMixedCollection
+ */
+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(
+            /**
+             * @event clear
+             * Fires when the collection is cleared.
+             */
+            'clear',
+
+            /**
+             * @event add
+             * Fires when an item is added to the collection.
+             * @param {Number} index The index at which the item was added.
+             * @param {Object} o The item added.
+             * @param {String} key The key associated with the added item.
+             */
+            'add',
+
+            /**
+             * @event replace
+             * Fires when an item is replaced in the collection.
+             * @param {String} key he key associated with the new added.
+             * @param {Object} old The item being replaced.
+             * @param {Object} new The new item.
+             */
+            'replace',
+
+            /**
+             * @event remove
+             * Fires when an item is removed from the collection.
+             * @param {Object} o The item being removed.
+             * @param {String} key (optional) The key associated with the removed item.
+             */
+            'remove'
+        );
+
+        me.allowFunctions = allowFunctions === true;
+
+        if (keyFn) {
+            me.getKey = keyFn;
+        }
+
+        me.mixins.observable.constructor.call(me);
+    },
+    
+    /**
+     * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
+     * function should add function references to the collection. Defaults to
+     * <tt>false</tt>.
+     */
+    allowFunctions : false,
+
+    /**
+     * Adds an item to the collection. Fires the {@link #add} event when complete.
+     * @param {String} key <p>The key to associate with the item, or the new item.</p>
+     * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
+     * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
+     * the MixedCollection will be able to <i>derive</i> the key for the new item.
+     * In this case just pass the new item in this parameter.</p>
+     * @param {Object} o The item to add.
+     * @return {Object} The item added.
+     */
+    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;
+    },
+
+    /**
+      * MixedCollection has a generic way to fetch keys if you implement getKey.  The default implementation
+      * simply returns <b><code>item.id</code></b> but you can provide your own implementation
+      * to return a different value as in the following examples:<pre><code>
+// normal way
+var mc = new Ext.util.MixedCollection();
+mc.add(someEl.dom.id, someEl);
+mc.add(otherEl.dom.id, otherEl);
+//and so on
+
+// using getKey
+var mc = new Ext.util.MixedCollection();
+mc.getKey = function(el){
+   return el.dom.id;
+};
+mc.add(someEl);
+mc.add(otherEl);
+
+// or via the constructor
+var mc = new Ext.util.MixedCollection(false, function(el){
+   return el.dom.id;
+});
+mc.add(someEl);
+mc.add(otherEl);
+     * </code></pre>
+     * @param {Object} item The item for which to find the key.
+     * @return {Object} The key for the passed item.
+     */
+    getKey : function(o){
+         return o.id;
+    },
+
+    /**
+     * Replaces an item in the collection. Fires the {@link #replace} event when complete.
+     * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
+     * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
+     * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
+     * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
+     * with one having the same key value, then just pass the replacement item in this parameter.</p>
+     * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
+     * with that key.
+     * @return {Object}  The new item.
+     */
+    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;
+    },
+
+    /**
+     * Adds all elements of an Array or an Object to the collection.
+     * @param {Object/Array} objs An Object containing properties which will be added
+     * to the collection, or an Array of values, each of which are added to the collection.
+     * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
+     * has been set to <tt>true</tt>.
+     */
+    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]);
+                    }
+                }
+            }
+        }
+    },
+
+    /**
+     * Executes the specified function once for every item in the collection, passing the following arguments:
+     * <div class="mdetail-params"><ul>
+     * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
+     * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
+     * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
+     * </ul></div>
+     * The function should return a boolean value. Returning false from the function will stop the iteration.
+     * @param {Function} fn The function to execute for each item.
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
+     */
+    each : function(fn, scope){
+        var items = [].concat(this.items), // each safe for removal
+            i = 0,
+            len = items.length,
+            item;
+
+        for (; i < len; i++) {
+            item = items[i];
+            if (fn.call(scope || item, item, i, len) === false) {
+                break;
+            }
+        }
+    },
+
+    /**
+     * Executes the specified function once for every key in the collection, passing each
+     * key, and its associated item as the first two parameters.
+     * @param {Function} fn The function to execute for each item.
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
+     */
+    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);
+        }
+    },
+
+    /**
+     * Returns the first item in the collection which elicits a true return value from the
+     * passed selection function.
+     * @param {Function} fn The selection function to execute for each item.
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
+     * @return {Object} The first item in the collection which returned true from the selection function.
+     */
+    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;
+    },
+
+    //<deprecated since="0.99">
+    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);
+    },
+    //</deprecated>
+
+    /**
+     * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
+     * @param {Number} index The index to insert the item at.
+     * @param {String} key The key to associate with the new item, or the item itself.
+     * @param {Object} o (optional) If the second parameter was a key, the new item.
+     * @return {Object} The item inserted.
+     */
+    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 an item from the collection.
+     * @param {Object} o The item to remove.
+     * @return {Object} The item removed or false if no item was removed.
+     */
+    remove : function(o){
+        return this.removeAt(this.indexOf(o));
+    },
+
+    /**
+     * Remove all items in the passed array from the collection.
+     * @param {Array} items An array of items to be removed.
+     * @return {Ext.util.MixedCollection} this object
+     */
+    removeAll : function(items){
+        Ext.each(items || [], function(item) {
+            this.remove(item);
+        }, this);
+
+        return this;
+    },
+
+    /**
+     * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
+     * @param {Number} index The index within the collection of the item to remove.
+     * @return {Object} The item removed or false if no item was removed.
+     */
+    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;
+    },
+
+    /**
+     * Removed an item associated with the passed key fom the collection.
+     * @param {String} key The key of the item to remove.
+     * @return {Object} The item removed or false if no item was removed.
+     */
+    removeAtKey : function(key){
+        return this.removeAt(this.indexOfKey(key));
+    },
+
+    /**
+     * Returns the number of items in the collection.
+     * @return {Number} the number of items in the collection.
+     */
+    getCount : function(){
+        return this.length;
+    },
+
+    /**
+     * Returns index within the collection of the passed Object.
+     * @param {Object} o The item to find the index of.
+     * @return {Number} index of the item. Returns -1 if not found.
+     */
+    indexOf : function(o){
+        return Ext.Array.indexOf(this.items, o);
+    },
+
+    /**
+     * Returns index within the collection of the passed key.
+     * @param {String} key The key to find the index of.
+     * @return {Number} index of the key.
+     */
+    indexOfKey : function(key){
+        return Ext.Array.indexOf(this.keys, key);
+    },
+
+    /**
+     * Returns the item associated with the passed key OR index.
+     * Key has priority over index.  This is the equivalent
+     * of calling {@link #key} first, then if nothing matched calling {@link #getAt}.
+     * @param {String/Number} key The key or index of the item.
+     * @return {Object} If the item is found, returns the item.  If the item was not found, returns <tt>undefined</tt>.
+     * If an item was found, but is a Class, returns <tt>null</tt>.
+     */
+    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; // for prototype!
+    },
+
+    /**
+     * Returns the item at the specified index.
+     * @param {Number} index The index of the item.
+     * @return {Object} The item at the specified index.
+     */
+    getAt : function(index) {
+        return this.items[index];
+    },
+
+    /**
+     * Returns the item associated with the passed key.
+     * @param {String/Number} key The key of the item.
+     * @return {Object} The item associated with the passed key.
+     */
+    getByKey : function(key) {
+        return this.map[key];
+    },
+
+    /**
+     * Returns true if the collection contains the passed Object as an item.
+     * @param {Object} o  The Object to look for in the collection.
+     * @return {Boolean} True if the collection contains the Object as an item.
+     */
+    contains : function(o){
+        return Ext.Array.contains(this.items, o);
+    },
+
+    /**
+     * Returns true if the collection contains the passed Object as a key.
+     * @param {String} key The key to look for in the collection.
+     * @return {Boolean} True if the collection contains the Object as a key.
+     */
+    containsKey : function(key){
+        return typeof this.map[key] != 'undefined';
+    },
+
+    /**
+     * Removes all items from the collection.  Fires the {@link #clear} event when complete.
+     */
+    clear : function(){
+        var me = this;
+
+        me.length = 0;
+        me.items = [];
+        me.keys = [];
+        me.map = {};
+        me.fireEvent('clear');
+    },
+
+    /**
+     * Returns the first item in the collection.
+     * @return {Object} the first item in the collection..
+     */
+    first : function() {
+        return this.items[0];
+    },
+
+    /**
+     * Returns the last item in the collection.
+     * @return {Object} the last item in the collection..
+     */
+    last : function() {
+        return this.items[this.length - 1];
+    },
+
+    /**
+     * Collects all of the values of the given property and returns their sum
+     * @param {String} property The property to sum by
+     * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when
+     * summing fields in records, where the fields are all stored inside the 'data' object
+     * @param {Number} start (optional) The record index to start at (defaults to <tt>0</tt>)
+     * @param {Number} end (optional) The record index to end at (defaults to <tt>-1</tt>)
+     * @return {Number} The total
+     */
+    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;
+    },
+
+    /**
+     * Collects unique values of a particular property in this MixedCollection
+     * @param {String} property The property to collect on
+     * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when
+     * summing fields in records, where the fields are all stored inside the 'data' object
+     * @param {Boolean} allowBlank (optional) Pass true to allow null, undefined or empty string values
+     * @return {Array} The unique values
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * Extracts all of the given property values from the items in the MC. Mainly used as a supporting method for
+     * functions like sum and collect.
+     * @param {String} property The property to extract
+     * @param {String} root Optional 'root' property to extract the first argument from. This is used mainly when
+     * extracting field data from Model instances, where the fields are stored inside the 'data' object
+     * @return {Array} The extracted values
+     */
+    extractValues: function(property, root) {
+        var values = this.items;
+
+        if (root) {
+            values = Ext.Array.pluck(values, root);
+        }
+
+        return Ext.Array.pluck(values, property);
+    },
+
+    /**
+     * Returns a range of items in this collection
+     * @param {Number} startIndex (optional) The starting index. Defaults to 0.
+     * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
+     * @return {Array} An array of items
+     */
+    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;
+    },
+
+    /**
+     * <p>Filters the objects in this collection by a set of {@link Ext.util.Filter Filter}s, or by a single
+     * property/value pair with optional parameters for substring matching and case sensitivity. See
+     * {@link Ext.util.Filter Filter} for an example of using Filter objects (preferred). Alternatively,
+     * MixedCollection can be easily filtered by property like this:</p>
+<pre><code>
+//create a simple store with a few people defined
+var people = new Ext.util.MixedCollection();
+people.addAll([
+    {id: 1, age: 25, name: 'Ed'},
+    {id: 2, age: 24, name: 'Tommy'},
+    {id: 3, age: 24, name: 'Arne'},
+    {id: 4, age: 26, name: 'Aaron'}
+]);
+
+//a new MixedCollection containing only the items where age == 24
+var middleAged = people.filter('age', 24);
+</code></pre>
+     *
+     *
+     * @param {Array/String} property A property on your objects, or an array of {@link Ext.util.Filter Filter} objects
+     * @param {String/RegExp} value Either string that the property values
+     * should start with or a RegExp to test against the property
+     * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
+     * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
+     * @return {MixedCollection} The new filtered collection
+     */
+    filter : function(property, value, anyMatch, caseSensitive) {
+        var filters = [],
+            filterFn;
+
+        //support for the simple case of filtering by property/value
+        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);
+        }
+
+        //at this point we have an array of zero or more Ext.util.Filter objects to filter with,
+        //so here we construct a function that combines these filters by ANDing them together
+        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);
+    },
+
+    /**
+     * Filter by a function. Returns a <i>new</i> collection that has been filtered.
+     * The passed function will be called with each object in the collection.
+     * If the function returns true, the value is included otherwise it is filtered.
+     * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
+     * @return {MixedCollection} The new filtered collection
+     */
+    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;
+    },
+
+    /**
+     * Finds the index of the first matching object in this collection by a specific property/value.
+     * @param {String} property The name of a property on your objects.
+     * @param {String/RegExp} value A string that the property values
+     * should start with or a RegExp to test against the property.
+     * @param {Number} start (optional) The index to start searching at (defaults to 0).
+     * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
+     * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
+     * @return {Number} The matched index or -1
+     */
+    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);
+    },
+
+    /**
+     * Find the index of the first matching object in this collection by a function.
+     * If the function returns <i>true</i> it is considered a match.
+     * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
+     * @param {Number} start (optional) The index to start searching at (defaults to 0).
+     * @return {Number} The matched index or -1
+     */
+    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;
+    },
+
+    /**
+     * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
+     * and by Ext.data.Store#filter
+     * @private
+     * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
+     * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
+     * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
+     * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
+     */
+    createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
+        if (!value.exec) { // not a regex
+            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;
+    },
+
+    /**
+     * Creates a shallow copy of this collection
+     * @return {MixedCollection}
+     */
+    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;
+    }
+});
+
+/**
+ * @class Ext.util.Sortable
+
+A mixin which allows a data component to be sorted. This is used by e.g. {@link Ext.data.Store} and {@link Ext.data.TreeStore}.
+
+**NOTE**: This mixin is mainly for internal library use and most users should not need to use it directly. It
+is more likely you will want to use one of the component classes that import this mixin, such as
+{@link Ext.data.Store} or {@link Ext.data.TreeStore}.
+ * @markdown
+ * @docauthor Tommy Maintz <tommy@sencha.com>
+ */
+Ext.define("Ext.util.Sortable", {
+    /**
+     * @property isSortable
+     * @type Boolean
+     * Flag denoting that this object is sortable. Always true.
+     */
+    isSortable: true,
+    
+    /**
+     * The default sort direction to use if one is not specified (defaults to "ASC")
+     * @property defaultSortDirection
+     * @type String
+     */
+    defaultSortDirection: "ASC",
+    
+    requires: [
+        'Ext.util.Sorter'
+    ],
+
+    /**
+     * The property in each item that contains the data to sort. (defaults to null)
+     * @type String
+     */    
+    sortRoot: null,
+    
+    /**
+     * Performs initialization of this mixin. Component classes using this mixin should call this method
+     * during their own initialization.
+     */
+    initSortable: function() {
+        var me = this,
+            sorters = me.sorters;
+        
+        /**
+         * The collection of {@link Ext.util.Sorter Sorters} currently applied to this Store
+         * @property sorters
+         * @type Ext.util.MixedCollection
+         */
+        me.sorters = Ext.create('Ext.util.AbstractMixedCollection', false, function(item) {
+            return item.id || item.property;
+        });
+        
+        if (sorters) {
+            me.sorters.addAll(me.decodeSorters(sorters));
+        }
+    },
+
+    /**
+     * <p>Sorts the data in the Store by one or more of its properties. Example usage:</p>
+<pre><code>
+//sort by a single field
+myStore.sort('myField', 'DESC');
+
+//sorting by multiple fields
+myStore.sort([
+    {
+        property : 'age',
+        direction: 'ASC'
+    },
+    {
+        property : 'name',
+        direction: 'DESC'
+    }
+]);
+</code></pre>
+     * <p>Internally, Store converts the passed arguments into an array of {@link Ext.util.Sorter} instances, and delegates the actual
+     * sorting to its internal {@link Ext.util.MixedCollection}.</p>
+     * <p>When passing a single string argument to sort, Store maintains a ASC/DESC toggler per field, so this code:</p>
+<pre><code>
+store.sort('myField');
+store.sort('myField');
+     </code></pre>
+     * <p>Is equivalent to this code, because Store handles the toggling automatically:</p>
+<pre><code>
+store.sort('myField', 'ASC');
+store.sort('myField', 'DESC');
+</code></pre>
+     * @param {String|Array} sorters Either a string name of one of the fields in this Store's configured {@link Ext.data.Model Model},
+     * or an Array of sorter configurations.
+     * @param {String} direction The overall direction to sort the data by. Defaults to "ASC".
+     */
+    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) {
+                //construct an amalgamated sorter function which combines all of the Sorters passed
+                sorterFn = function(r1, r2) {
+                    var result = sorters[0].sort(r1, r2),
+                        length = sorters.length,
+                        i;
+
+                        //if we have more than one sorter, OR any additional sorter functions together
+                        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,
+        
+    /**
+     * @private
+     * Normalizes an array of sorter objects, ensuring that they are all Ext.util.Sorter instances
+     * @param {Array} sorters The sorters array
+     * @return {Array} Array of Ext.util.Sorter objects
+     */
+    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"
+                });
+
+                //support for 3.x style sorters where a function can be defined as 'fn'
+                if (config.fn) {
+                    config.sorterFn = config.fn;
+                }
+
+                //support a function to be passed as a sorter definition
+                if (typeof config == 'function') {
+                    config = {
+                        sorterFn: config
+                    };
+                }
+
+                // ensure sortType gets pushed on if necessary
+                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;
+    },
+    
+    /**
+     * Returns an object describing the current sort state of this Store.
+     * @return {Object} The sort state of the Store. An object with two properties:<ul>
+     * <li><b>field</b> : String<p class="sub-desc">The name of the field by which the Records are sorted.</p></li>
+     * <li><b>direction</b> : String<p class="sub-desc">The sort order, 'ASC' or 'DESC' (case-sensitive).</p></li>
+     * </ul>
+     * See <tt>{@link #sortInfo}</tt> for additional details.
+     */
+    getSortState : function() {
+        return this.sortInfo;
+    }
+});
+/**
+ * @class Ext.util.MixedCollection
+ * <p>
+ * Represents a collection of a set of key and value pairs. Each key in the MixedCollection
+ * must be unique, the same key cannot exist twice. This collection is ordered, items in the
+ * collection can be accessed by index  or via the key. Newly added items are added to
+ * the end of the collection. This class is similar to {@link Ext.util.HashMap} however it
+ * is heavier and provides more functionality. Sample usage:
+ * <pre><code>
+var coll = new Ext.util.MixedCollection();
+coll.add('key1', 'val1');
+coll.add('key2', 'val2');
+coll.add('key3', 'val3');
+
+console.log(coll.get('key1')); // prints 'val1'
+console.log(coll.indexOfKey('key3')); // prints 2
+ * </code></pre>
+ *
+ * <p>
+ * The MixedCollection also has support for sorting and filtering of the values in the collection.
+ * <pre><code>
+var coll = new Ext.util.MixedCollection();
+coll.add('key1', 100);
+coll.add('key2', -100);
+coll.add('key3', 17);
+coll.add('key4', 0);
+var biggerThanZero = coll.filterBy(function(value){
+    return value > 0;
+});
+console.log(biggerThanZero.getCount()); // prints 2
+ * </code></pre>
+ * </p>
+ *
+ * @constructor
+ * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
+ * function should add function references to the collection. Defaults to
+ * <tt>false</tt>.
+ * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
+ * and return the key value for that item.  This is used when available to look up the key on items that
+ * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
+ * equivalent to providing an implementation for the {@link #getKey} method.
+ */
+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);
+    },
+
+    /**
+     * @private
+     * Performs the actual sorting based on a direction and a sorting function. Internally,
+     * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
+     * the sorted array data back into this.items and this.keys
+     * @param {String} property Property to sort by ('key', 'value', or 'index')
+     * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
+     * @param {Function} fn (optional) Comparison function that defines the sort order.
+     * Defaults to sorting by numeric value.
+     */
+    _sort : function(property, dir, fn){
+        var me = this,
+            i, len,
+            dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
+
+            //this is a temporary array used to apply the sorting function
+            c     = [],
+            keys  = me.keys,
+            items = me.items;
+
+        //default to a simple sorter function if one is not provided
+        fn = fn || function(a, b) {
+            return a - b;
+        };
+
+        //copy all the items into a temporary array, which we will sort
+        for(i = 0, len = items.length; i < len; i++){
+            c[c.length] = {
+                key  : keys[i],
+                value: items[i],
+                index: i
+            };
+        }
+
+        //sort the temporary array
+        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;
+        });
+
+        //copy the temporary array back into the main this.items and this.keys objects
+        for(i = 0, len = c.length; i < len; i++){
+            items[i] = c[i].value;
+            keys[i]  = c[i].key;
+        }
+
+        me.fireEvent('sort', me);
+    },
+
+    /**
+     * Sorts the collection by a single sorter function
+     * @param {Function} sorterFn The function to sort by
+     */
+    sortBy: function(sorterFn) {
+        var me     = this,
+            items  = me.items,
+            keys   = me.keys,
+            length = items.length,
+            temp   = [],
+            i;
+
+        //first we create a copy of the items array so that we can sort it
+        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;
+        });
+
+        //copy the temporary array back into the main this.items and this.keys objects
+        for (i = 0; i < length; i++) {
+            items[i] = temp[i].value;
+            keys[i]  = temp[i].key;
+        }
+        
+        me.fireEvent('sort', me, items, keys);
+    },
+
+    /**
+     * Reorders each of the items based on a mapping from old index to new index. Internally this
+     * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
+     * @param {Object} mapping Mapping from old item index to new item index
+     */
+    reorder: function(mapping) {
+        var me = this,
+            items = me.items,
+            index = 0,
+            length = items.length,
+            order = [],
+            remaining = [],
+            oldIndex;
+
+        me.suspendEvents();
+
+        //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
+        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);
+    },
+
+    /**
+     * Sorts this collection by <b>key</b>s.
+     * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
+     * @param {Function} fn (optional) Comparison function that defines the sort order.
+     * Defaults to sorting by case insensitive string.
+     */
+    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);
+        });
+    }
+});
+
+/**
+ * @class Ext.data.StoreManager
+ * @extends Ext.util.MixedCollection
+ * <p>Contains a collection of all stores that are created that have an identifier.
+ * An identifier can be assigned by setting the {@link Ext.data.AbstractStore#storeId storeId} 
+ * property. When a store is in the StoreManager, it can be referred to via it's identifier:
+ * <pre><code>
+Ext.create('Ext.data.Store', {
+    model: 'SomeModel',
+    storeId: 'myStore'
+});
+
+var store = Ext.data.StoreManager.lookup('myStore');
+ * </code></pre>
+ * Also note that the {@link #lookup} method is aliased to {@link Ext#getStore} for convenience.</p>
+ * <p>
+ * If a store is registered with the StoreManager, you can also refer to the store by it's identifier when
+ * registering it with any Component that consumes data from a store:
+ * <pre><code>
+Ext.create('Ext.data.Store', {
+    model: 'SomeModel',
+    storeId: 'myStore'
+});
+
+Ext.create('Ext.view.View', {
+    store: 'myStore',
+    // other configuration here
+});
+ * </code></pre>
+ * </p>
+ * @singleton
+ * @docauthor Evan Trimboli <evan@sencha.com>
+ * TODO: Make this an AbstractMgr
+ */
+Ext.define('Ext.data.StoreManager', {
+    extend: 'Ext.util.MixedCollection',
+    alternateClassName: ['Ext.StoreMgr', 'Ext.data.StoreMgr', 'Ext.StoreManager'],
+    singleton: true,
+    uses: ['Ext.data.ArrayStore'],
+    
+    /**
+     * @cfg {Object} listeners @hide
+     */
+
+    /**
+     * Registers one or more Stores with the StoreManager. You do not normally need to register stores
+     * manually.  Any store initialized with a {@link Ext.data.Store#storeId} will be auto-registered. 
+     * @param {Ext.data.Store} store1 A Store instance
+     * @param {Ext.data.Store} store2 (optional)
+     * @param {Ext.data.Store} etc... (optional)
+     */
+    register : function() {
+        for (var i = 0, s; (s = arguments[i]); i++) {
+            this.add(s);
+        }
+    },
+
+    /**
+     * Unregisters one or more Stores with the StoreManager
+     * @param {String/Object} id1 The id of the Store, or a Store instance
+     * @param {String/Object} id2 (optional)
+     * @param {String/Object} etc... (optional)
+     */
+    unregister : function() {
+        for (var i = 0, s; (s = arguments[i]); i++) {
+            this.remove(this.lookup(s));
+        }
+    },
+
+    /**
+     * Gets a registered Store by id
+     * @param {String/Object} id The id of the Store, or a Store instance, or a store configuration
+     * @return {Ext.data.Store}
+     */
+    lookup : function(store) {
+        // handle the case when we are given an array or an array of arrays.
+        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)) {
+            // store id
+            return this.get(store);
+        } else {
+            // store instance or store config
+            return Ext.data.AbstractStore.create(store);
+        }
+    },
+
+    // getKey implementation for MixedCollection
+    getKey : function(o) {
+         return o.storeId;
+    }
+}, function() {    
+    /**
+     * <p>Creates a new store for the given id and config, then registers it with the {@link Ext.data.StoreManager Store Mananger}. 
+     * Sample usage:</p>
+    <pre><code>
+    Ext.regStore('AllUsers', {
+        model: 'User'
+    });
+
+    //the store can now easily be used throughout the application
+    new Ext.List({
+        store: 'AllUsers',
+        ... other config
+    });
+    </code></pre>
+     * @param {String} id The id to set on the new store
+     * @param {Object} config The store config
+     * @param {Constructor} cls The new Component class.
+     * @member Ext
+     * @method regStore
+     */
+    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);
+    };
+
+    /**
+     * Gets a registered Store by id (shortcut to {@link #lookup})
+     * @param {String/Object} id The id of the Store, or a Store instance
+     * @return {Ext.data.Store}
+     * @member Ext
+     * @method getStore
+     */
+    Ext.getStore = function(name) {
+        return Ext.data.StoreManager.lookup(name);
+    };
+});
+
+/**
+ * @class Ext.LoadMask
+ * A simple utility class for generically masking elements while loading data.  If the {@link #store}
+ * config option is specified, the masking will be automatically synchronized with the store's loading
+ * process and the mask element will be cached for reuse.
+ * <p>Example usage:</p>
+ * <pre><code>
+// Basic mask:
+var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
+myMask.show();
+</code></pre>
+
+ * @constructor
+ * Create a new LoadMask
+ * @param {Mixed} el The element, element ID, or DOM node you wish to mask. Also, may be a Component who's element you wish to mask.
+ * @param {Object} config The config object
+ */
+
+Ext.define('Ext.LoadMask', {
+
+    /* Begin Definitions */
+
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+
+    requires: ['Ext.data.StoreManager'],
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Ext.data.Store} store
+     * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
+     * hidden on either load success, or load fail.
+     */
+
+    /**
+     * @cfg {String} msg
+     * The text to display in a centered loading message box (defaults to 'Loading...')
+     */
+    msg : 'Loading...',
+    /**
+     * @cfg {String} msgCls
+     * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
+     */
+    msgCls : Ext.baseCSSPrefix + 'mask-loading',
+    
+    /**
+     * @cfg {Boolean} useMsg
+     * Whether or not to use a loading message class or simply mask the bound element.
+     */
+    useMsg: true,
+
+    /**
+     * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
+     * @type Boolean
+     */
+    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);
+    },
+
+    /**
+     * @private
+     * Called if we were configured with a Component, and that Component was not yet rendered. Collects the element to mask.
+     */
+    onComponentRender: function(comp) {
+        this.el = comp.getContentTarget();
+    },
+
+    /**
+     * @private
+     * Called when this LoadMask's Component is resized. The isMasked method also re-centers any displayed message.
+     */
+    onComponentResize: function(comp, w, h) {
+        this.el.isMasked();
+    },
+
+    /**
+     * Changes the data store bound to this LoadMask.
+     * @param {Store} store The store to bind to this LoadMask
+     */
+    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();
+        }
+    },
+
+    /**
+     * Disables the mask to prevent it from being displayed
+     */
+    disable : function() {
+        var me = this;
+
+       me.disabled = true;
+       if (me.loading) {
+           me.onLoad();
+       }
+    },
+
+    /**
+     * Enables the mask so that it can be displayed
+     */
+    enable : function() {
+        this.disabled = false;
+    },
+
+    /**
+     * Method to determine whether this LoadMask is currently disabled.
+     * @return {Boolean} the disabled state of this LoadMask.
+     */
+    isDisabled : function() {
+        return this.disabled;
+    },
+
+    // private
+    onLoad : function() {
+        var me = this;
+
+        me.loading = false;
+        me.el.unmask();
+        me.fireEvent('hide', me, me.el, me.store);
+    },
+
+    // private
+    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 this LoadMask over the configured Element.
+     */
+    show: function() {
+        this.onBeforeLoad();
+    },
+
+    /**
+     * Hide this LoadMask.
+     */
+    hide: function() {
+        this.onLoad();
+    },
+
+    // private
+    destroy : function() {
+        this.hide();
+        this.clearListeners();
+    }
+});
+
+/**
+ * @class Ext.ComponentLoader
+ * @extends Ext.ElementLoader
+ * 
+ * This class is used to load content via Ajax into a {@link Ext.Component}. In general 
+ * this class will not be instanced directly, rather a loader configuration will be passed to the
+ * constructor of the {@link Ext.Component}.
+ * 
+ * ## HTML Renderer
+ * By default, the content loaded will be processed as raw html. The response text
+ * from the request is taken and added to the component. This can be used in
+ * conjunction with the {@link #scripts} option to execute any inline scripts in
+ * the resulting content. Using this renderer has the same effect as passing the
+ * {@link Ext.Component#html} configuration option.
+ * 
+ * ## Data Renderer
+ * This renderer allows content to be added by using JSON data and a {@link Ext.XTemplate}.
+ * The content received from the response is passed to the {@link Ext.Component#update} method.
+ * This content is run through the attached {@link Ext.Component#tpl} and the data is added to
+ * the Component. Using this renderer has the same effect as using the {@link Ext.Component#data}
+ * configuration in conjunction with a {@link Ext.Component#tpl}.
+ * 
+ * ## Component Renderer
+ * This renderer can only be used with a {@link Ext.Container} and subclasses. It allows for
+ * Components to be loaded remotely into a Container. The response is expected to be a single/series of
+ * {@link Ext.Component} configuration objects. When the response is received, the data is decoded
+ * and then passed to {@link Ext.Container#add}. Using this renderer has the same effect as specifying
+ * the {@link Ext.Container#items} configuration on a Container. 
+ * 
+ * ## Custom Renderer
+ * A custom function can be passed to handle any other special case, see the {@link #renderer} option.
+ * 
+ * ## Example Usage
+ *     new Ext.Component({
+ *         tpl: '{firstName} - {lastName}',
+ *         loader: {
+ *             url: 'myPage.php',
+ *             renderer: 'data',
+ *             params: {
+ *                 userId: 1
+ *             }
+ *         }
+ *     });
+ */
+Ext.define('Ext.ComponentLoader', {
+
+    /* Begin Definitions */
+    
+    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 = [];
+
+                //<debug>
+                if (!target.isContainer) {
+                    Ext.Error.raise({
+                        target: target,
+                        msg: 'Components can only be loaded into a container'
+                    });
+                }
+                //</debug>
+
+                try {
+                    items = Ext.decode(response.responseText);
+                } catch (e) {
+                    success = false;
+                }
+
+                if (success) {
+                    if (active.removeAll) {
+                        target.removeAll();
+                    }
+                    target.add(items);
+                }
+                return success;
+            }
+        }
+    },
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Ext.Component/String} target The target {@link Ext.Component} for the loader. Defaults to <tt>null</tt>.
+     * If a string is passed it will be looked up via the id.
+     */
+    target: null,
+
+    /**
+     * @cfg {Mixed} loadMask True or a {@link Ext.LoadMask} configuration to enable masking during loading. Defaults to <tt>false</tt>.
+     */
+    loadMask: false,
+    
+    /**
+     * @cfg {Boolean} scripts True to parse any inline script tags in the response. This only used when using the html
+     * {@link #renderer}.
+     */
+
+    /**
+     * @cfg {String/Function} renderer
+
+The type of content that is to be loaded into, which can be one of 3 types:
+
++ **html** : Loads raw html content, see {@link Ext.Component#html}
++ **data** : Loads raw html content, see {@link Ext.Component#data}
++ **component** : Loads child {Ext.Component} instances. This option is only valid when used with a Container.
+
+Defaults to `html`.
+
+Alternatively, you can pass a function which is called with the following parameters.
+
++ loader - Loader instance
++ response - The server response
++ active - The active request
+
+The function must return false is loading is not successful. Below is a sample of using a custom renderer:
+
+    new Ext.Component({
+        loader: {
+            url: 'myPage.php',
+            renderer: function(loader, response, active) {
+                var text = response.responseText;
+                loader.getTarget().update('The response is ' + text);
+                return true;
+            }
+        }
+    });
+     * @markdown
+     */
+    renderer: 'html',
+
+    /**
+     * Set a {Ext.Component} as the target of this loader. Note that if the target is changed,
+     * any active requests will be aborted.
+     * @param {String/Ext.Component} target The component to be the target of this loader. If a string is passed
+     * it will be looked up via its id.
+     */
+    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;
+    },
+    
+    // inherit docs
+    removeMask: function(){
+        this.target.setLoading(false);
+    },
+    
+    /**
+     * Add the mask on the target
+     * @private
+     * @param {Mixed} mask The mask configuration
+     */
+    addMask: function(mask){
+        this.target.setLoading(mask);
+    },
+
+    /**
+     * Get the target of this loader.
+     * @return {Ext.Component} target The target, null if none exists.
+     */
+    
+    setOptions: function(active, options){
+        active.removeAll = Ext.isDefined(options.removeAll) ? options.removeAll : this.removeAll;
+    },
+
+    /**
+     * 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;
+        }
+
+        var renderers = this.statics().Renderer;
+        switch (renderer) {
+            case 'component':
+                return renderers.Component;
+            case 'data':
+                return renderers.Data;
+            default:
+                return Ext.ElementLoader.Renderer.Html;
+        }
+    }
+});
+
+/**
+ * @class Ext.layout.component.Auto
+ * @extends Ext.layout.component.Component
+ * @private
+ *
+ * <p>The AutoLayout is the default layout manager delegated by {@link Ext.Component} to
+ * render any child Elements when no <tt>{@link Ext.Component#layout layout}</tt> is configured.</p>
+ */
+
+Ext.define('Ext.layout.component.Auto', {
+
+    /* Begin Definitions */
+
+    alias: 'layout.autocomponent',
+
+    extend: 'Ext.layout.component.Component',
+
+    /* End Definitions */
+
+    type: 'autocomponent',
+
+    onLayout : function(width, height) {
+        this.setTargetSize(width, height);
+    }
+});
+/**
+ * @class Ext.AbstractComponent
+ * <p>An abstract base class which provides shared methods for Components across the Sencha product line.</p>
+ * <p>Please refer to sub class's documentation</p>
+ * @constructor
+ */
+
+Ext.define('Ext.AbstractComponent', {
+
+    /* Begin Definitions */
+
+    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'
+    ],
+
+    // Please remember to add dependencies whenever you use it
+    // I had to fix these many times already
+    uses: [
+        'Ext.ZIndexManager'
+    ],
+
+    statics: {
+        AUTO_ID: 1000
+    },
+
+    /* End Definitions */
+
+    isComponent: true,
+
+    getAutoId: function() {
+        return ++Ext.AbstractComponent.AUTO_ID;
+    },
+
+    /**
+     * @cfg {String} id
+     * <p>The <b><u>unique id of this component instance</u></b> (defaults to an {@link #getId auto-assigned id}).</p>
+     * <p>It should not be necessary to use this configuration except for singleton objects in your application.
+     * Components created with an id may be accessed globally using {@link Ext#getCmp Ext.getCmp}.</p>
+     * <p>Instead of using assigned ids, use the {@link #itemId} config, and {@link Ext.ComponentQuery ComponentQuery} which
+     * provides selector-based searching for Sencha Components analogous to DOM querying. The {@link Ext.container.Container Container}
+     * class contains {@link Ext.container.Container#down shortcut methods} to query its descendant Components by selector.</p>
+     * <p>Note that this id will also be used as the element id for the containing HTML element
+     * that is rendered to the page for this component. This allows you to write id-based CSS
+     * rules to style the specific instance of this component uniquely, and also to select
+     * sub-elements using this component's id as the parent.</p>
+     * <p><b>Note</b>: to avoid complications imposed by a unique <tt>id</tt> also see <code>{@link #itemId}</code>.</p>
+     * <p><b>Note</b>: to access the container of a Component see <code>{@link #ownerCt}</code>.</p>
+     */
+
+    /**
+     * @cfg {String} itemId
+     * <p>An <tt>itemId</tt> can be used as an alternative way to get a reference to a component
+     * when no object reference is available.  Instead of using an <code>{@link #id}</code> with
+     * {@link Ext}.{@link Ext#getCmp getCmp}, use <code>itemId</code> with
+     * {@link Ext.container.Container}.{@link Ext.container.Container#getComponent getComponent} which will retrieve
+     * <code>itemId</code>'s or <tt>{@link #id}</tt>'s. Since <code>itemId</code>'s are an index to the
+     * container's internal MixedCollection, the <code>itemId</code> is scoped locally to the container --
+     * avoiding potential conflicts with {@link Ext.ComponentManager} which requires a <b>unique</b>
+     * <code>{@link #id}</code>.</p>
+     * <pre><code>
+var c = new Ext.panel.Panel({ //
+    {@link Ext.Component#height height}: 300,
+    {@link #renderTo}: document.body,
+    {@link Ext.container.Container#layout layout}: 'auto',
+    {@link Ext.container.Container#items items}: [
+        {
+            itemId: 'p1',
+            {@link Ext.panel.Panel#title title}: 'Panel 1',
+            {@link Ext.Component#height height}: 150
+        },
+        {
+            itemId: 'p2',
+            {@link Ext.panel.Panel#title title}: 'Panel 2',
+            {@link Ext.Component#height height}: 150
+        }
+    ]
+})
+p1 = c.{@link Ext.container.Container#getComponent getComponent}('p1'); // not the same as {@link Ext#getCmp Ext.getCmp()}
+p2 = p1.{@link #ownerCt}.{@link Ext.container.Container#getComponent getComponent}('p2'); // reference via a sibling
+     * </code></pre>
+     * <p>Also see <tt>{@link #id}</tt>, <code>{@link #query}</code>, <code>{@link #down}</code> and <code>{@link #child}</code>.</p>
+     * <p><b>Note</b>: to access the container of an item see <tt>{@link #ownerCt}</tt>.</p>
+     */
+
+    /**
+     * This Component's owner {@link Ext.container.Container Container} (defaults to undefined, and is set automatically when
+     * this Component is added to a Container).  Read-only.
+     * <p><b>Note</b>: to access items within the Container see <tt>{@link #itemId}</tt>.</p>
+     * @type Ext.Container
+     * @property ownerCt
+     */
+
+    /**
+     * @cfg {Mixed} autoEl
+     * <p>A tag name or {@link Ext.core.DomHelper DomHelper} spec used to create the {@link #getEl Element} which will
+     * encapsulate this Component.</p>
+     * <p>You do not normally need to specify this. For the base classes {@link Ext.Component} and {@link Ext.container.Container},
+     * this defaults to <b><tt>'div'</tt></b>. The more complex Sencha classes use a more complex
+     * DOM structure specified by their own {@link #renderTpl}s.</p>
+     * <p>This is intended to allow the developer to create application-specific utility Components encapsulated by
+     * different DOM elements. Example usage:</p><pre><code>
+{
+    xtype: 'component',
+    autoEl: {
+        tag: 'img',
+        src: 'http://www.example.com/example.jpg'
+    }
+}, {
+    xtype: 'component',
+    autoEl: {
+        tag: 'blockquote',
+        html: 'autoEl is cool!'
+    }
+}, {
+    xtype: 'container',
+    autoEl: 'ul',
+    cls: 'ux-unordered-list',
+    items: {
+        xtype: 'component',
+        autoEl: 'li',
+        html: 'First list item'
+    }
+}
+</code></pre>
+     */
+
+    /**
+     * @cfg {Mixed} renderTpl
+     * <p>An {@link Ext.XTemplate XTemplate} used to create the internal structure inside this Component's
+     * encapsulating {@link #getEl Element}.</p>
+     * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}
+     * and {@link Ext.container.Container}, this defaults to <b><code>null</code></b> which means that they will be initially rendered
+     * with no internal structure; they render their {@link #getEl Element} empty. The more specialized ExtJS and Touch classes
+     * which use a more complex DOM structure, provide their own template definitions.</p>
+     * <p>This is intended to allow the developer to create application-specific utility Components with customized
+     * internal structure.</p>
+     * <p>Upon rendering, any created child elements may be automatically imported into object properties using the
+     * {@link #renderSelectors} option.</p>
+     */
+    renderTpl: null,
+
+    /**
+     * @cfg {Object} renderSelectors
+
+An object containing properties specifying {@link Ext.DomQuery DomQuery} selectors which identify child elements
+created by the render process.
+
+After the Component's internal structure is rendered according to the {@link #renderTpl}, this object is iterated through,
+and the found Elements are added as properties to the Component using the `renderSelector` property name.
+
+For example, a Component which rendered an image, and description into its element might use the following properties
+coded into its prototype:
+
+    renderTpl: '&lt;img src="{imageUrl}" class="x-image-component-img">&lt;div class="x-image-component-desc">{description}&gt;/div&lt;',
+
+    renderSelectors: {
+        image: 'img.x-image-component-img',
+        descEl: 'div.x-image-component-desc'
+    }
+
+After rendering, the Component would have a property <code>image</code> referencing its child `img` Element,
+and a property `descEl` referencing the `div` Element which contains the description.
+
+     * @markdown
+     */
+
+    /**
+     * @cfg {Mixed} renderTo
+     * <p>Specify the id of the element, a DOM element or an existing Element that this component
+     * will be rendered into.</p><div><ul>
+     * <li><b>Notes</b> : <ul>
+     * <div class="sub-desc">Do <u>not</u> use this option if the Component is to be a child item of
+     * a {@link Ext.container.Container Container}. It is the responsibility of the
+     * {@link Ext.container.Container Container}'s {@link Ext.container.Container#layout layout manager}
+     * to render and manage its child items.</div>
+     * <div class="sub-desc">When using this config, a call to render() is not required.</div>
+     * </ul></li>
+     * </ul></div>
+     * <p>See <code>{@link #render}</code> also.</p>
+     */
+
+    /**
+     * @cfg {Boolean} frame
+     * <p>Specify as <code>true</code> to have the Component inject framing elements within the Component at render time to
+     * provide a graphical rounded frame around the Component content.</p>
+     * <p>This is only necessary when running on outdated, or non standard-compliant browsers such as Microsoft's Internet Explorer
+     * prior to version 9 which do not support rounded corners natively.</p>
+     * <p>The extra space taken up by this framing is available from the read only property {@link #frameSize}.</p>
+     */
+
+    /**
+     * <p>Read-only property indicating the width of any framing elements which were added within the encapsulating element
+     * to provide graphical, rounded borders. See the {@link #frame} config.</p>
+     * <p> This is an object containing the frame width in pixels for all four sides of the Component containing
+     * the following properties:</p><div class="mdetail-params"><ul>
+     * <li><code>top</code> The width of the top framing element in pixels.</li>
+     * <li><code>right</code> The width of the right framing element in pixels.</li>
+     * <li><code>bottom</code> The width of the bottom framing element in pixels.</li>
+     * <li><code>left</code> The width of the left framing element in pixels.</li>
+     * </ul></div>
+     * @property frameSize
+     * @type {Object}
+     */
+
+    /**
+     * @cfg {String/Object} componentLayout
+     * <p>The sizing and positioning of a Component's internal Elements is the responsibility of
+     * the Component's layout manager which sizes a Component's internal structure in response to the Component being sized.</p>
+     * <p>Generally, developers will not use this configuration as all provided Components which need their internal
+     * elements sizing (Such as {@link Ext.form.field.Base input fields}) come with their own componentLayout managers.</p>
+     * <p>The {@link Ext.layout.container.Auto default layout manager} will be used on instances of the base Ext.Component class
+     * which simply sizes the Component's encapsulating element to the height and width specified in the {@link #setSize} method.</p>
+     */
+
+    /**
+     * @cfg {Mixed} tpl
+     * An <bold>{@link Ext.Template}</bold>, <bold>{@link Ext.XTemplate}</bold>
+     * or an array of strings to form an Ext.XTemplate.
+     * Used in conjunction with the <code>{@link #data}</code> and
+     * <code>{@link #tplWriteMode}</code> configurations.
+     */
+
+    /**
+     * @cfg {Mixed} data
+     * The initial set of data to apply to the <code>{@link #tpl}</code> to
+     * update the content area of the Component.
+     */
+
+    /**
+     * @cfg {String} tplWriteMode The Ext.(X)Template method to use when
+     * updating the content area of the Component. Defaults to <code>'overwrite'</code>
+     * (see <code>{@link Ext.XTemplate#overwrite}</code>).
+     */
+    tplWriteMode: 'overwrite',
+
+    /**
+     * @cfg {String} baseCls
+     * The base CSS class to apply to this components's element. This will also be prepended to
+     * elements within this component like Panel's body will get a class x-panel-body. This means
+     * that if you create a subclass of Panel, and you want it to get all the Panels styling for the
+     * element and the body, you leave the baseCls x-panel and use componentCls to add specific styling for this
+     * component.
+     */
+    baseCls: Ext.baseCSSPrefix + 'component',
+
+    /**
+     * @cfg {String} componentCls
+     * CSS Class to be added to a components root level element to give distinction to it
+     * via styling.
+     */
+
+    /**
+     * @cfg {String} cls
+     * An optional extra CSS class that will be added to this component's Element (defaults to '').  This can be
+     * useful for adding customized styles to the component or any of its children using standard CSS rules.
+     */
+
+    /**
+     * @cfg {String} overCls
+     * An optional extra CSS class that will be added to this component's Element when the mouse moves
+     * over the Element, and removed when the mouse moves out. (defaults to '').  This can be
+     * useful for adding customized 'active' or 'hover' styles to the component or any of its children using standard CSS rules.
+     */
+
+    /**
+     * @cfg {String} disabledCls
+     * CSS class to add when the Component is disabled. Defaults to 'x-item-disabled'.
+     */
+    disabledCls: Ext.baseCSSPrefix + 'item-disabled',
+
+    /**
+     * @cfg {String/Array} ui
+     * A set style for a component. Can be a string or an Array of multiple strings (UIs)
+     */
+    ui: 'default',
+    
+    /**
+     * @cfg {Array} uiCls
+     * An array of of classNames which are currently applied to this component
+     * @private
+     */
+    uiCls: [],
+    
+    /**
+     * @cfg {String} style
+     * A custom style specification to be applied to this component's Element.  Should be a valid argument to
+     * {@link Ext.core.Element#applyStyles}.
+     * <pre><code>
+        new Ext.panel.Panel({
+            title: 'Some Title',
+            renderTo: Ext.getBody(),
+            width: 400, height: 300,
+            layout: 'form',
+            items: [{
+                xtype: 'textarea',
+                style: {
+                    width: '95%',
+                    marginBottom: '10px'
+                }
+            },
+            new Ext.button.Button({
+                text: 'Send',
+                minWidth: '100',
+                style: {
+                    marginBottom: '10px'
+                }
+            })
+            ]
+        });
+     </code></pre>
+     */
+
+    /**
+     * @cfg {Number} width
+     * The width of this component in pixels.
+     */
+
+    /**
+     * @cfg {Number} height
+     * The height of this component in pixels.
+     */
+
+    /**
+     * @cfg {Number/String} border
+     * Specifies the border for this component. The border can be a single numeric value to apply to all sides or
+     * it can be a CSS style specification for each style, for example: '10 5 3 10'.
+     */
+
+    /**
+     * @cfg {Number/String} padding
+     * Specifies the padding for this component. The padding can be a single numeric value to apply to all sides or
+     * it can be a CSS style specification for each style, for example: '10 5 3 10'.
+     */
+
+    /**
+     * @cfg {Number/String} margin
+     * Specifies the margin for this component. The margin can be a single numeric value to apply to all sides or
+     * it can be a CSS style specification for each style, for example: '10 5 3 10'.
+     */
+
+    /**
+     * @cfg {Boolean} hidden
+     * Defaults to false.
+     */
+    hidden: false,
+
+    /**
+     * @cfg {Boolean} disabled
+     * Defaults to false.
+     */
+    disabled: false,
+
+    /**
+     * @cfg {Boolean} draggable
+     * Allows the component to be dragged.
+     */
+
+    /**
+     * Read-only property indicating whether or not the component can be dragged
+     * @property draggable
+     * @type {Boolean}
+     */
+    draggable: false,
+
+    /**
+     * @cfg {Boolean} floating
+     * Create the Component as a floating and use absolute positioning.
+     * Defaults to false.
+     */
+    floating: false,
+
+    /**
+     * @cfg {String} hideMode
+     * A String which specifies how this Component's encapsulating DOM element will be hidden.
+     * Values may be<div class="mdetail-params"><ul>
+     * <li><code>'display'</code> : The Component will be hidden using the <code>display: none</code> style.</li>
+     * <li><code>'visibility'</code> : The Component will be hidden using the <code>visibility: hidden</code> style.</li>
+     * <li><code>'offsets'</code> : The Component will be hidden by absolutely positioning it out of the visible area of the document. This
+     * is useful when a hidden Component must maintain measurable dimensions. Hiding using <code>display</code> results
+     * in a Component having zero dimensions.</li></ul></div>
+     * Defaults to <code>'display'</code>.
+     */
+    hideMode: 'display',
+
+    /**
+     * @cfg {String} contentEl
+     * <p>Optional. Specify an existing HTML element, or the <code>id</code> of an existing HTML element to use as the content
+     * for this component.</p>
+     * <ul>
+     * <li><b>Description</b> :
+     * <div class="sub-desc">This config option is used to take an existing HTML element and place it in the layout element
+     * of a new component (it simply moves the specified DOM element <i>after the Component is rendered</i> to use as the content.</div></li>
+     * <li><b>Notes</b> :
+     * <div class="sub-desc">The specified HTML element is appended to the layout element of the component <i>after any configured
+     * {@link #html HTML} has been inserted</i>, and so the document will not contain this element at the time the {@link #render} event is fired.</div>
+     * <div class="sub-desc">The specified HTML element used will not participate in any <code><b>{@link Ext.container.Container#layout layout}</b></code>
+     * scheme that the Component may use. It is just HTML. Layouts operate on child <code><b>{@link Ext.container.Container#items items}</b></code>.</div>
+     * <div class="sub-desc">Add either the <code>x-hidden</code> or the <code>x-hide-display</code> CSS class to
+     * prevent a brief flicker of the content before it is rendered to the panel.</div></li>
+     * </ul>
+     */
+
+    /**
+     * @cfg {String/Object} html
+     * An HTML fragment, or a {@link Ext.core.DomHelper DomHelper} specification to use as the layout element
+     * content (defaults to ''). The HTML content is added after the component is rendered,
+     * so the document will not contain this HTML at the time the {@link #render} event is fired.
+     * This content is inserted into the body <i>before</i> any configured {@link #contentEl} is appended.
+     */
+
+    /**
+     * @cfg {String} styleHtmlContent
+     * True to automatically style the html inside the content target of this component (body for panels).
+     * Defaults to false.
+     */
+    styleHtmlContent: false,
+
+    /**
+     * @cfg {String} styleHtmlCls
+     * The class that is added to the content target when you set styleHtmlContent to true.
+     * Defaults to 'x-html'
+     */
+    styleHtmlCls: Ext.baseCSSPrefix + 'html',
+
+    /**
+     * @cfg {Number} minHeight
+     * <p>The minimum value in pixels which this Component will set its height to.</p>
+     * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
+     */
+    /**
+     * @cfg {Number} minWidth
+     * <p>The minimum value in pixels which this Component will set its width to.</p>
+     * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
+     */
+    /**
+     * @cfg {Number} maxHeight
+     * <p>The maximum value in pixels which this Component will set its height to.</p>
+     * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
+     */
+    /**
+     * @cfg {Number} maxWidth
+     * <p>The maximum value in pixels which this Component will set its width to.</p>
+     * <p><b>Warning:</b> This will override any size management applied by layout managers.</p>
+     */
+
+    /**
+     * @cfg {Ext.ComponentLoader/Object} loader
+     * A configuration object or an instance of a {@link Ext.ComponentLoader} to load remote
+     * content for this Component.
+     */
+
+     // @private
+     allowDomMove: true,
+
+     /**
+      * @cfg {Boolean} autoShow True to automatically show the component upon creation.
+      * This config option may only be used for {@link #floating} components or components
+      * that use {@link #autoRender}. Defaults to <tt>false</tt>.
+      */
+     autoShow: false,
+
+    /**
+     * @cfg {Mixed} autoRender
+     * <p>This config is intended mainly for {@link #floating} Components which may or may not be shown. Instead
+     * of using {@link #renderTo} in the configuration, and rendering upon construction, this allows a Component
+     * to render itself upon first <i>{@link #show}</i>.</p>
+     * <p>Specify as <code>true</code> to have this Component render to the document body upon first show.</p>
+     * <p>Specify as an element, or the ID of an element to have this Component render to a specific element upon first show.</p>
+     * <p><b>This defaults to <code>true</code> for the {@link Ext.window.Window Window} class.</b></p>
+     */
+     autoRender: false,
+
+     needsLayout: false,
+
+    /**
+     * @cfg {Object/Array} plugins
+     * An object or array of objects that will provide custom functionality for this component.  The only
+     * requirement for a valid plugin is that it contain an init method that accepts a reference of type Ext.Component.
+     * When a component is created, if any plugins are available, the component will call the init method on each
+     * plugin, passing a reference to itself.  Each plugin can then call methods or respond to events on the
+     * component as needed to provide its functionality.
+     */
+
+    /**
+     * Read-only property indicating whether or not the component has been rendered.
+     * @property rendered
+     * @type {Boolean}
+     */
+    rendered: false,
+
+    weight: 0,
+
+    trimRe: /^\s+|\s+$/g,
+    spacesRe: /\s+/,
+    
+    
+    /**
+     * This is an internal flag that you use when creating custom components.
+     * By default this is set to true which means that every component gets a mask when its disabled.
+     * Components like FieldContainer, FieldSet, Field, Button, Tab override this property to false
+     * since they want to implement custom disable logic.
+     * @property maskOnDisable
+     * @type {Boolean}
+     */     
+    maskOnDisable: true,
+
+    constructor : function(config) {
+        var me = this,
+            i, len;
+
+        config = config || {};
+        me.initialConfig = config;
+        Ext.apply(me, config);
+
+        me.addEvents(
+            /**
+             * @event beforeactivate
+             * Fires before a Component has been visually activated.
+             * Returning false from an event listener can prevent the activate
+             * from occurring.
+             * @param {Ext.Component} this
+             */
+             'beforeactivate',
+            /**
+             * @event activate
+             * Fires after a Component has been visually activated.
+             * @param {Ext.Component} this
+             */
+             'activate',
+            /**
+             * @event beforedeactivate
+             * Fires before a Component has been visually deactivated.
+             * Returning false from an event listener can prevent the deactivate
+             * from occurring.
+             * @param {Ext.Component} this
+             */
+             'beforedeactivate',
+            /**
+             * @event deactivate
+             * Fires after a Component has been visually deactivated.
+             * @param {Ext.Component} this
+             */
+             'deactivate',
+            /**
+             * @event added
+             * Fires after a Component had been added to a Container.
+             * @param {Ext.Component} this
+             * @param {Ext.container.Container} container Parent Container
+             * @param {Number} pos position of Component
+             */
+             'added',
+            /**
+             * @event disable
+             * Fires after the component is disabled.
+             * @param {Ext.Component} this
+             */
+             'disable',
+            /**
+             * @event enable
+             * Fires after the component is enabled.
+             * @param {Ext.Component} this
+             */
+             'enable',
+            /**
+             * @event beforeshow
+             * Fires before the component is shown when calling the {@link #show} method.
+             * Return false from an event handler to stop the show.
+             * @param {Ext.Component} this
+             */
+             'beforeshow',
+            /**
+             * @event show
+             * Fires after the component is shown when calling the {@link #show} method.
+             * @param {Ext.Component} this
+             */
+             'show',
+            /**
+             * @event beforehide
+             * Fires before the component is hidden when calling the {@link #hide} method.
+             * Return false from an event handler to stop the hide.
+             * @param {Ext.Component} this
+             */
+             'beforehide',
+            /**
+             * @event hide
+             * Fires after the component is hidden.
+             * Fires after the component is hidden when calling the {@link #hide} method.
+             * @param {Ext.Component} this
+             */
+             'hide',
+            /**
+             * @event removed
+             * Fires when a component is removed from an Ext.container.Container
+             * @param {Ext.Component} this
+             * @param {Ext.container.Container} ownerCt Container which holds the component
+             */
+             'removed',
+            /**
+             * @event beforerender
+             * Fires before the component is {@link #rendered}. Return false from an
+             * event handler to stop the {@link #render}.
+             * @param {Ext.Component} this
+             */
+             'beforerender',
+            /**
+             * @event render
+             * Fires after the component markup is {@link #rendered}.
+             * @param {Ext.Component} this
+             */
+             'render',
+            /**
+             * @event afterrender
+             * <p>Fires after the component rendering is finished.</p>
+             * <p>The afterrender event is fired after this Component has been {@link #rendered}, been postprocesed
+             * by any afterRender method defined for the Component.</p>
+             * @param {Ext.Component} this
+             */
+             'afterrender',
+            /**
+             * @event beforedestroy
+             * Fires before the component is {@link #destroy}ed. Return false from an event handler to stop the {@link #destroy}.
+             * @param {Ext.Component} this
+             */
+             'beforedestroy',
+            /**
+             * @event destroy
+             * Fires after the component is {@link #destroy}ed.
+             * @param {Ext.Component} this
+             */
+             'destroy',
+            /**
+             * @event resize
+             * Fires after the component is resized.
+             * @param {Ext.Component} this
+             * @param {Number} adjWidth The box-adjusted width that was set
+             * @param {Number} adjHeight The box-adjusted height that was set
+             */
+             'resize',
+            /**
+             * @event move
+             * Fires after the component is moved.
+             * @param {Ext.Component} this
+             * @param {Number} x The new x position
+             * @param {Number} y The new y position
+             */
+             '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();
+
+        // ititComponent gets a chance to change the id property before registering
+        Ext.ComponentManager.register(me);
+
+        // Dont pass the config so that it is not applied to 'this' again
+        me.mixins.observable.constructor.call(me);
+        me.mixins.state.constructor.call(me, config);
+
+        // Move this into Observable?
+        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();
+        }
+        
+        //<debug>
+        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;
+        }
+        //</debug>
+    },
+
+    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;
+        }
+        // Special processing for animating Component dimensions.
+        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 any dimensions are being increased, we must resize the internal structure
+            // of the Component, but then clip it by sizing its encapsulating element back to original dimensions.
+            // The animation will then progressively reveal the larger content.
+            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);
+    },
+
+    /**
+     * <p>This method finds the topmost active layout who's processing will eventually determine the size and position of this
+     * Component.<p>
+     * <p>This method is useful when dynamically adding Components into Containers, and some processing must take place after the
+     * final sizing and positioning of the Component has been performed.</p>
+     * @returns
+     */
+    findLayoutController: function() {
+        return this.findParentBy(function(c) {
+            // Return true if we are at the root of the Container tree
+            // or this Container's layout is busy but the next one up is not.
+            return !c.ownerCt || (c.layout.layoutBusy && !c.ownerCt.layout.layoutBusy);
+        });
+    },
+
+    onShow : function() {
+        // Layout if needed
+        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;
+    },
+
+
+    // @private
+    initPlugin : function(plugin) {
+        plugin.init(this);
+
+        return plugin;
+    },
+
+    /**
+     * Handles autoRender.
+     * Floating Components may have an ownerCt. If they are asking to be constrained, constrain them within that
+     * ownerCt, and have their z-index managed locally. Floating Components are always rendered to document.body
+     */
+    doAutoRender: function() {
+        var me = this;
+        if (me.floating) {
+            me.render(document.body);
+        } else {
+            me.render(Ext.isBoolean(me.autoRender) ? Ext.getBody() : me.autoRender);
+        }
+    },
+
+    // @private
+    render : function(container, position) {
+        var me = this;
+
+        if (!me.rendered && me.fireEvent('beforerender', me) !== false) {
+            // If this.el is defined, we want to make sure we are dealing with
+            // an Ext Element.
+            if (me.el) {
+                me.el = Ext.get(me.el);
+            }
+
+            // Perform render-time processing for floating Components
+            if (me.floating) {
+                me.onFloatRender();
+            }
+
+            container = me.initContainer(container);
+
+            me.onRender(container, position);
+
+            // Tell the encapsulating element to hide itself in the way the Component is configured to hide
+            // This means DISPLAY, VISIBILITY or OFFSETS.
+            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) {
+                // Hiding during the render process should not perform any ancillary
+                // actions that the full hide process does; It is not hiding, it begins in a hidden state.'
+                // So just make the element hidden according to the configured hideMode
+                me.el.hide();
+            }
+
+            if (me.disabled) {
+                // pass silent so the event doesn't fire the first time.
+                me.disable(true);
+            }
+        }
+        return me;
+    },
+
+    // @private
+    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 this component's el is the body element, we add the reset class to the html tag
+            if (el.dom == Ext.getBody().dom) {
+                el.parent().addCls(Ext.baseCSSPrefix + 'reset');
+            }
+            else {
+                // Else we wrap this element in an element that adds the reset class.
+                me.resetEl = el.wrap({
+                    cls: Ext.baseCSSPrefix + 'reset'
+                });
+            }
+        }
+
+        el.addCls(cls);
+        el.setStyle(styles);
+
+        // Here we check if the component has a height set through style or css.
+        // If it does then we set the this.height to that value and it won't be
+        // considered an auto height component
+        // if (this.height === undefined) {
+        //     var height = el.getHeight();
+        //     // This hopefully means that the panel has an explicit height set in style or css
+        //     if (height - el.getPadding('tb') - el.getBorderWidth('tb') > 0) {
+        //         this.height = height;
+        //     }
+        // }
+
+        me.el = el;
+        
+        me.rendered = true;
+        me.addUIToElement(true);
+        //loop through all exisiting uiCls and update the ui in them
+        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);
+    },
+
+    // @private
+    afterRender : function() {
+        var me = this,
+            pos,
+            xy;
+
+        me.getComponentLayout();
+
+        // Set the size if a size is configured, or if this is the outermost Container
+        if (!me.ownerCt || (me.height || me.width)) {
+            me.setSize(me.width, me.height);
+        }
+
+        // For floaters, calculate x and y if they aren't defined by aligning
+        // the sized element to the center of either the the container or the ownerCt
+        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>'
+    ],
+    
+    /**
+     * @private
+     */
+    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) {
+            // Here we render the frameTpl to this component. This inserts the 9point div or the table framing.
+            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)));
+
+            // The frameBody is returned in getTargetEl, so that layouts render items to the correct target.=
+            me.frameBody = me.el.down('.' + me.frameCls + '-mc');
+            
+            // Add the render selectors for each of the frame elements
+            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) {                
+                // Reapply render selectors
+                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();
+                
+                // Store the class names set on the new mc
+                newMCClassName = this.frameMC.dom.className;
+                
+                // Replace the new mc with the old mc
+                oldFrameMC.insertAfter(this.frameMC);
+                this.frameMC.remove();
+                
+                // Restore the reference to the old frame mc as the framebody
+                this.frameBody = this.frameMC = oldFrameMC;
+                
+                // Apply the new mc classes to the old mc element
+                oldFrameMC.dom.className = newMCClassName;
+                
+                // Remove the old framing
+                if (wasTable) {
+                    me.el.query('> table')[1].remove();
+                }                                
+                else {
+                    if (oldFrameTL) {
+                        oldFrameTL.remove();
+                    }
+                    if (oldFrameBL) {
+                        oldFrameBL.remove();
+                    }
+                    oldFrameML.remove();
+                }
+            }
+            else {
+                // We were framed but not anymore. Move all content from the old frame to the body
+                
+            }
+        }
+        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;
+
+        // Some browsers dont support background-position-x and y, so for those
+        // browsers let's split background-position into two parts.
+        if (!left && !top) {
+            info = me.el.getStyle('background-position').split(' ');
+            left = info[0];
+            top = info[1];
+        }
+        
+        // We actually pass a string in the form of '[type][tl][tr]px [type][br][bl]px' as
+        // the background position of this.el from the css to indicate to IE that this component needs
+        // framing. We parse it here and change the markup accordingly.
+        if (parseInt(left, 10) >= 1000000 && parseInt(top, 10) >= 1000000) {
+            max = Math.max;
+            
+            frameInfo = {
+                // Table markup starts with 110, div markup with 100.
+                table: left.substr(0, 3) == '110',
+                
+                // Determine if we are dealing with a horizontal or vertical component
+                vertical: top.substr(0, 3) == '110',
+                
+                // Get and parse the different border radius sizes
+                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);
+
+            // Just to be sure we set the background image of the el to none.
+            me.el.setStyle('background-image', 'none');
+        }        
+        
+        // This happens when you set frame: true explicitly without using the x-frame mixin in sass.
+        // This way IE can't figure out what sizes to use and thus framing can't work.
+        if (me.frame === true && !frameInfo) {
+            //<debug error>
+            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.");
+            //</debug>
+        }
+        
+        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;
+    },
+    
+    /**
+     * @private
+     */
+    getFrameTpl : function(table) {
+        return table ? this.getTpl('frameTableTpl') : this.getTpl('frameTpl');
+    },
+
+    /**
+     * <p>Creates an array of class names from the configurations to add to this Component's <code>el</code> on render.</p>
+     * <p>Private, but (possibly) used by ComponentQuery for selection by class name if Component is not rendered.</p>
+     * @return {Array} An array of class names with which the Component's element will be rendered.
+     * @private
+     */
+    initCls: function() {
+        var me = this,
+            cls = [];
+
+        cls.push(me.baseCls);
+
+        //<deprecated since=0.99>
+        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;
+        }
+        //</deprecated>
+
+        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);
+    },
+    
+    /**
+     * Sets the UI for the component. This will remove any existing UIs on the component. It will also
+     * loop through any uiCls set on the component and rename them so they include the new UI
+     * @param {String} ui The new UI for the component
+     */
+    setUI: function(ui) {
+        var me = this,
+            oldUICls = Ext.Array.clone(me.uiCls),
+            newUICls = [],
+            cls,
+            i;
+        
+        //loop through all exisiting uiCls and update the ui in them
+        for (i = 0; i < oldUICls.length; i++) {
+            cls = oldUICls[i];
+            
+            me.removeClsWithUI(cls);
+            newUICls.push(cls);
+        }
+        
+        //remove the UI from the element
+        me.removeUIFromElement();
+        
+        //set the UI
+        me.ui = ui;
+        
+        //add the new UI to the elemend
+        me.addUIToElement();
+        
+        //loop through all exisiting uiCls and update the ui in them
+        for (i = 0; i < newUICls.length; i++) {
+            cls = newUICls[i];
+            
+            me.addClsWithUI(cls);
+        }
+    },
+    
+    /**
+     * Adds a cls to the uiCls array, which will also call {@link #addUIClsToElement} and adds
+     * to all elements of this component.
+     * @param {String/Array} cls A string or an array of strings to add to the uiCls
+     */
+    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]);
+            }
+        }
+    },
+    
+    /**
+     * Removes a cls to the uiCls array, which will also call {@link #removeUIClsToElement} and removes
+     * it from all elements of this component.
+     * @param {String/Array} cls A string or an array of strings to remove to the uiCls
+     */
+    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]);
+            }
+        }
+    },
+    
+    /**
+     * Checks if there is currently a specified uiCls
+     * @param {String} cls The cls to check
+     */
+    hasUICls: function(cls) {
+        var me = this,
+            uiCls = me.uiCls || [];
+        
+        return Ext.Array.contains(uiCls, cls);
+    },
+    
+    /**
+     * Method which adds a specified UI + uiCls to the components element.
+     * Can be overridden to remove the UI from more than just the components element.
+     * @param {String} ui The UI to remove from the element
+     * @private
+     */
+    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) {
+            // define each element of the frame
+            var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
+                i, el;
+            
+            // loop through each of them, and if they are defined add the ui
+            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]);
+                }
+            }
+        }
+    },
+    
+    /**
+     * Method which removes a specified UI + uiCls from the components element.
+     * The cls which is added to the element will be: `this.baseCls + '-' + ui`
+     * @param {String} ui The UI to add to the element
+     * @private
+     */
+    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) {
+            // define each element of the frame
+            var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
+                i, el;
+            
+            // loop through each of them, and if they are defined add the ui
+            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]);
+                }
+            }
+        }
+    },
+    
+    /**
+     * Method which adds a specified UI to the components element.
+     * @private
+     */
+    addUIToElement: function(force) {
+        var me = this;
+        
+        me.addCls(me.baseCls + '-' + me.ui);
+        
+        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
+            // define each element of the frame
+            var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
+                i, el;
+            
+            // loop through each of them, and if they are defined add the ui
+            for (i = 0; i < els.length; i++) {
+                el = me['frame' + els[i].toUpperCase()];
+                
+                if (el) {
+                    el.addCls(me.baseCls + '-' + me.ui + '-' + els[i]);
+                }
+            }
+        }
+    },
+    
+    /**
+     * Method which removes a specified UI from the components element.
+     * @private
+     */
+    removeUIFromElement: function() {
+        var me = this;
+        
+        me.removeCls(me.baseCls + '-' + me.ui);
+        
+        if (me.rendered && me.frame && !Ext.supports.CSS3BorderRadius) {
+            // define each element of the frame
+            var els = ['tl', 'tc', 'tr', 'ml', 'mc', 'mr', 'bl', 'bc', 'br'],
+                i, el;
+            
+            // loop through each of them, and if they are defined add the ui
+            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;
+    },
+
+    /**
+     * This function takes the position argument passed to onRender and returns a
+     * DOM element that you can use in the insertBefore.
+     * @param {String/Number/Element/HTMLElement} position Index, element id or element you want
+     * to put this component before.
+     * @return {HTMLElement} DOM element that you can use in the insertBefore
+     */
+    getInsertPosition: function(position) {
+        // Convert the position to an element to insert before
+        if (position !== undefined) {
+            if (Ext.isNumber(position)) {
+                position = this.container.dom.childNodes[position];
+            }
+            else {
+                position = Ext.getDom(position);
+            }
+        }
+
+        return position;
+    },
+
+    /**
+     * Adds ctCls to container.
+     * @return {Ext.core.Element} The initialized container
+     * @private
+     */
+    initContainer: function(container) {
+        var me = this;
+
+        // If you render a component specifying the el, we get the container
+        // of the el, and make sure we dont move the el around in the dom
+        // during the render
+        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;
+    },
+
+    /**
+     * Initialized the renderData to be used when rendering the renderTpl.
+     * @return {Object} Object with keys and values that are going to be applied to the renderTpl
+     * @private
+     */
+    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
+        });
+    },
+
+    /**
+     * @private
+     */
+    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];
+    },
+
+    /**
+     * Initializes the renderTpl.
+     * @return {Ext.XTemplate} The renderTpl XTemplate instance.
+     * @private
+     */
+    initRenderTpl: function() {
+        return this.getTpl('renderTpl');
+    },
+
+    /**
+     * Function description
+     * @return {String} A CSS style string with style, padding, margin and border.
+     * @private
+     */
+    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);
+        }
+
+        // Convert the padding, margin and border properties from a space seperated string
+        // into a proper style string
+        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;
+    },
+
+    /**
+     * Initializes this components contents. It checks for the properties
+     * html, contentEl and tpl/data.
+     * @private
+     */
+    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) {
+            // Make sure this.tpl is an instantiated XTemplate
+            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;
+            }
+        }
+    },
+
+    // @private
+    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);
+                    }
+                }
+            }
+        }
+    },
+
+    /**
+     * Sets references to elements inside the component. E.g body -> x-panel-body
+     * @private
+     */
+    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));
+            }
+        }
+    },
+
+    /**
+     * Tests whether this Component matches the selector string.
+     * @param {String} selector The selector string to test against.
+     * @return {Boolean} True if this Component matches the selector.
+     */
+    is: function(selector) {
+        return Ext.ComponentQuery.is(this, selector);
+    },
+
+    /**
+     * <p>Walks up the <code>ownerCt</code> axis looking for an ancestor Container which matches
+     * the passed simple selector.</p>
+     * <p>Example:<pre><code>
+var owningTabPanel = grid.up('tabpanel');
+</code></pre>
+     * @param {String} selector Optional. The simple selector to test.
+     * @return {Container} The matching ancestor Container (or <code>undefined</code> if no match was found).
+     */
+    up: function(selector) {
+        var result = this.ownerCt;
+        if (selector) {
+            for (; result; result = result.ownerCt) {
+                if (Ext.ComponentQuery.is(result, selector)) {
+                    return result;
+                }
+            }
+        }
+        return result;
+    },
+
+    /**
+     * <p>Returns the next sibling of this Component.</p>
+     * <p>Optionally selects the next sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery} selector.</p>
+     * <p>May also be refered to as <code><b>next()</b></code></p>
+     * <p>Note that this is limited to siblings, and if no siblings of the item match, <code>null</code> is returned. Contrast with {@link #nextNode}</p>
+     * @param {String} selector Optional A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following items.
+     * @returns The next sibling (or the next sibling which matches the selector). Returns null if there is no matching sibling.
+     */
+    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;
+    },
+
+    /**
+     * <p>Returns the previous sibling of this Component.</p>
+     * <p>Optionally selects the previous sibling which matches the passed {@link Ext.ComponentQuery ComponentQuery} selector.</p>
+     * <p>May also be refered to as <code><b>prev()</b></code></p>
+     * <p>Note that this is limited to siblings, and if no siblings of the item match, <code>null</code> is returned. Contrast with {@link #previousNode}</p>
+     * @param {String} selector Optional. A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding items.
+     * @returns The previous sibling (or the previous sibling which matches the selector). Returns null if there is no matching sibling.
+     */
+    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;
+    },
+
+    /**
+     * <p>Returns the previous node in the Component tree in tree traversal order.</p>
+     * <p>Note that this is not limited to siblings, and if invoked upon a node with no matching siblings, will
+     * walk the tree in reverse order to attempt to find a match. Contrast with {@link #previousSibling}.</p>
+     * @param {String} selector Optional. A {@link Ext.ComponentQuery ComponentQuery} selector to filter the preceding nodes.
+     * @returns The previous node (or the previous node which matches the selector). Returns null if there is no matching node.
+     */
+    previousNode: function(selector, includeSelf) {
+        var node = this,
+            result,
+            it, len, i;
+
+        // If asked to include self, test me
+        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);
+        }
+    },
+
+    /**
+     * <p>Returns the next node in the Component tree in tree traversal order.</p>
+     * <p>Note that this is not limited to siblings, and if invoked upon a node with no matching siblings, will
+     * walk the tree to attempt to find a match. Contrast with {@link #pnextSibling}.</p>
+     * @param {String} selector Optional A {@link Ext.ComponentQuery ComponentQuery} selector to filter the following nodes.
+     * @returns The next node (or the next node which matches the selector). Returns null if there is no matching node.
+     */
+    nextNode: function(selector, includeSelf) {
+        var node = this,
+            result,
+            it, len, i;
+
+        // If asked to include self, test me
+        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);
+        }
+    },
+
+    /**
+     * Retrieves the id of this component.
+     * Will autogenerate an id if one has not already been set.
+     */
+    getId : function() {
+        return this.id || (this.id = 'ext-comp-' + (this.getAutoId()));
+    },
+
+    getItemId : function() {
+        return this.itemId || this.id;
+    },
+
+    /**
+     * Retrieves the top level element representing this component.
+     */
+    getEl : function() {
+        return this.el;
+    },
+
+    /**
+     * This is used to determine where to insert the 'html', 'contentEl' and 'items' in this component.
+     * @private
+     */
+    getTargetEl: function() {
+        return this.frameBody || this.el;
+    },
+
+    /**
+     * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
+     * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
+     * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
+     * to participate in determination of inherited xtypes.</b></p>
+     * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
+     * <p>Example usage:</p>
+     * <pre><code>
+var t = new Ext.form.field.Text();
+var isText = t.isXType('textfield');        // true
+var isBoxSubclass = t.isXType('field');       // true, descended from Ext.form.field.Base
+var isBoxInstance = t.isXType('field', true); // false, not a direct Ext.form.field.Base instance
+</code></pre>
+     * @param {String} xtype The xtype to check for this Component
+     * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
+     * the default), or true to check whether this Component is directly of the specified xtype.
+     * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
+     */
+    isXType: function(xtype, shallow) {
+        //assume a string by default
+        if (Ext.isFunction(xtype)) {
+            xtype = xtype.xtype;
+            //handle being passed the class, e.g. Ext.Component
+        } else if (Ext.isObject(xtype)) {
+            xtype = xtype.statics().xtype;
+            //handle being passed an instance
+        }
+
+        return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1: this.self.xtype == xtype;
+    },
+
+    /**
+     * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
+     * available xtypes, see the {@link Ext.Component} header.</p>
+     * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
+     * to participate in determination of inherited xtypes.</b></p>
+     * <p>Example usage:</p>
+     * <pre><code>
+var t = new Ext.form.field.Text();
+alert(t.getXTypes());  // alerts 'component/field/textfield'
+</code></pre>
+     * @return {String} The xtype hierarchy string
+     */
+    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 the content area of a component.
+     * @param {Mixed} htmlOrData
+     * If this component has been configured with a template via the tpl config
+     * then it will use this argument as data to populate the template.
+     * If this component was not configured with a template, the components
+     * content area will be updated via Ext.core.Element update
+     * @param {Boolean} loadScripts
+     * (optional) Only legitimate when using the html configuration. Defaults to false
+     * @param {Function} callback
+     * (optional) Only legitimate when using the html configuration. Callback to execute when scripts have finished loading
+     */
+    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();
+        }
+    },
+
+    /**
+     * Convenience function to hide or show this component by boolean.
+     * @param {Boolean} visible True to show, false to hide
+     * @return {Ext.Component} this
+     */
+    setVisible : function(visible) {
+        return this[visible ? 'show': 'hide']();
+    },
+
+    /**
+     * Returns true if this component is visible.
+     * @param {Boolean} deep. <p>Optional. Pass <code>true</code> to interrogate the visibility status of all
+     * parent Containers to determine whether this Component is truly visible to the user.</p>
+     * <p>Generally, to determine whether a Component is hidden, the no argument form is needed. For example
+     * when creating dynamically laid out UIs in a hidden Container before showing them.</p>
+     * @return {Boolean} True if this component is visible, false otherwise.
+     */
+    isVisible: function(deep) {
+        var me = this,
+            child = me,
+            visible = !me.hidden,
+            ancestor = me.ownerCt;
+
+        // Clear hiddenOwnerCt property
+        me.hiddenAncestor = false;
+        if (me.destroyed) {
+            return false;
+        }
+
+        if (deep && visible && me.rendered && ancestor) {
+            while (ancestor) {
+                // If any ancestor is hidden, then this is hidden.
+                // If an ancestor Panel (only Panels have a collapse method) is collapsed,
+                // then its layoutTarget (body) is hidden, so this is hidden unless its within a
+                // docked item; they are still visible when collapsed (Unless they themseves are hidden)
+                if (ancestor.hidden || (ancestor.collapsed &&
+                        !(ancestor.getDockedItems && Ext.Array.contains(ancestor.getDockedItems(), child)))) {
+                    // Store hiddenOwnerCt property if needed
+                    me.hiddenAncestor = ancestor;
+                    visible = false;
+                    break;
+                }
+                child = ancestor;
+                ancestor = ancestor.ownerCt;
+            }
+        }
+        return visible;
+    },
+
+    /**
+     * Enable the component
+     * @param {Boolean} silent
+     * Passing false will supress the 'enable' event from being fired.
+     */
+    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 the component.
+     * @param {Boolean} silent
+     * Passing true, will supress the 'disable' event from being fired.
+     */
+    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;
+    },
+    
+    // @private
+    onEnable: function() {
+        if (this.maskOnDisable) {
+            this.el.unmask();
+        }        
+    },
+
+    // @private
+    onDisable : function() {
+        if (this.maskOnDisable) {
+            this.el.mask();
+        }
+    },
+    
+    /**
+     * Method to determine whether this Component is currently disabled.
+     * @return {Boolean} the disabled state of this Component.
+     */
+    isDisabled : function() {
+        return this.disabled;
+    },
+
+    /**
+     * Enable or disable the component.
+     * @param {Boolean} disabled
+     */
+    setDisabled : function(disabled) {
+        return this[disabled ? 'disable': 'enable']();
+    },
+
+    /**
+     * Method to determine whether this Component is currently set to hidden.
+     * @return {Boolean} the hidden state of this Component.
+     */
+    isHidden : function() {
+        return this.hidden;
+    },
+
+    /**
+     * Adds a CSS class to the top level element representing this component.
+     * @param {String} cls The CSS class name to add
+     * @return {Ext.Component} Returns the Component to allow method chaining.
+     */
+    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;
+    },
+
+    /**
+     * @deprecated 4.0 Replaced by {link:#addCls}
+     * Adds a CSS class to the top level element representing this component.
+     * @param {String} cls The CSS class name to add
+     * @return {Ext.Component} Returns the Component to allow method chaining.
+     */
+    addClass : function() {
+        return this.addCls.apply(this, arguments);
+    },
+
+    /**
+     * Removes a CSS class from the top level element representing this component.
+     * @returns {Ext.Component} Returns the Component to allow method chaining.
+     */
+    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;
+    },
+
+    //<debug>
+    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);
+    },
+    //</debug>
+
+    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];
+                        }
+                    }
+                }
+            }
+
+            // At this point we have a variable called element,
+            // and a listeners object that can be passed to on
+            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);
+    },
+
+    // @TODO: implement removelistener to support the dom event stuff
+
+    /**
+     * Provides the link for Observable's fireEvent method to bubble up the ownership hierarchy.
+     * @return {Ext.container.Container} the Container which owns this Component.
+     */
+    getBubbleTarget : function() {
+        return this.ownerCt;
+    },
+
+    /**
+     * Method to determine whether this Component is floating.
+     * @return {Boolean} the floating state of this component.
+     */
+    isFloating : function() {
+        return this.floating;
+    },
+
+    /**
+     * Method to determine whether this Component is draggable.
+     * @return {Boolean} the draggable state of this component.
+     */
+    isDraggable : function() {
+        return !!this.draggable;
+    },
+
+    /**
+     * Method to determine whether this Component is droppable.
+     * @return {Boolean} the droppable state of this component.
+     */
+    isDroppable : function() {
+        return !!this.droppable;
+    },
+
+    /**
+     * @private
+     * Method to manage awareness of when components are added to their
+     * respective Container, firing an added event.
+     * References are established at add time rather than at render time.
+     * @param {Ext.container.Container} container Container which holds the component
+     * @param {number} pos Position at which the component was added
+     */
+    onAdded : function(container, pos) {
+        this.ownerCt = container;
+        this.fireEvent('added', this, container, pos);
+    },
+
+    /**
+     * @private
+     * Method to manage awareness of when components are removed from their
+     * respective Container, firing an removed event. References are properly
+     * cleaned up after removing a component from its owning container.
+     */
+    onRemoved : function() {
+        var me = this;
+
+        me.fireEvent('removed', me, me.ownerCt);
+        delete me.ownerCt;
+    },
+
+    // @private
+    beforeDestroy : Ext.emptyFn,
+    // @private
+    // @private
+    onResize : Ext.emptyFn,
+
+    /**
+     * Sets the width and height of this Component. This method fires the {@link #resize} event. This method can accept
+     * either width and height as separate arguments, or you can pass a size object like <code>{width:10, height:20}</code>.
+     * @param {Mixed} width The new width to set. This may be one of:<div class="mdetail-params"><ul>
+     * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.core.Element#defaultUnit}s (by default, pixels).</li>
+     * <li>A String used to set the CSS width style.</li>
+     * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
+     * <li><code>undefined</code> to leave the width unchanged.</li>
+     * </ul></div>
+     * @param {Mixed} height The new height to set (not required if a size object is passed as the first arg).
+     * This may be one of:<div class="mdetail-params"><ul>
+     * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.core.Element#defaultUnit}s (by default, pixels).</li>
+     * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
+     * <li><code>undefined</code> to leave the height unchanged.</li>
+     * </ul></div>
+     * @return {Ext.Component} this
+     */
+    setSize : function(width, height) {
+        var me = this,
+            layoutCollection;
+
+        // support for standard size objects
+        if (Ext.isObject(width)) {
+            height = width.height;
+            width  = width.width;
+        }
+
+        // Constrain within configured maxima
+        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 an ownerCt is hidden, add my reference onto the layoutOnShow stack.  Set the needsLayout flag.
+            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;
+
+        // support for standard size objects
+        if (Ext.isObject(width)) {
+            ownerCt = width.ownerCt;
+            height = width.height;
+            width  = width.width;
+        }
+
+        // Constrain within configured maxima
+        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 an ownerCt is hidden, add my reference onto the layoutOnShow stack.  Set the needsLayout flag.
+            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;
+    },
+
+    /**
+     * This method needs to be called whenever you change something on this component that requires the Component's
+     * layout to be recalculated.
+     * @return {Ext.container.Container} this
+     */
+    doComponentLayout : function(width, height, isSetSize, ownerCt) {
+        var me = this,
+            componentLayout = me.getComponentLayout();
+
+        // collapsed state is not relevant here, so no testing done.
+        // Only Panels have a collapse method, and that just sets the width/height such that only
+        // a single docked Header parallel to the collapseTo side are visible, and the Panel body is hidden.
+        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;
+    },
+
+    // @private
+    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;
+    },
+
+    /**
+     * @param {Number} adjWidth The box-adjusted width that was set
+     * @param {Number} adjHeight The box-adjusted height that was set
+     * @param {Boolean} isSetSize Whether or not the height/width are stored on the component permanently
+     * @param {Ext.Component} layoutOwner Component which sent the layout. Only used when isSetSize is false.
+     */
+    afterComponentLayout: function(width, height, isSetSize, layoutOwner) {
+        this.fireEvent('resize', this, width, height);
+    },
+
+    /**
+     * Occurs before componentLayout is run. Returning false from this method will prevent the componentLayout
+     * from being executed.
+     * @param {Number} adjWidth The box-adjusted width that was set
+     * @param {Number} adjHeight The box-adjusted height that was set
+     * @param {Boolean} isSetSize Whether or not the height/width are stored on the component permanently
+     * @param {Ext.Component} layoutOwner Component which sent the layout. Only used when isSetSize is false.
+     */
+    beforeComponentLayout: function(width, height, isSetSize, layoutOwner) {
+        return true;
+    },
+
+    /**
+     * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
+     * This method fires the {@link #move} event.
+     * @param {Number} left The new left
+     * @param {Number} top The new top
+     * @return {Ext.Component} this
+     */
+    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;
+    },
+
+    /* @private
+     * Called after the component is moved, this method is empty by default but can be implemented by any
+     * subclass that needs to perform custom logic after a move occurs.
+     * @param {Number} x The new x position
+     * @param {Number} y The new y position
+     */
+    onPosition: Ext.emptyFn,
+
+    /**
+     * Sets the width of the component.  This method fires the {@link #resize} event.
+     * @param {Number} width The new width to setThis may be one of:<div class="mdetail-params"><ul>
+     * <li>A Number specifying the new width in the {@link #getEl Element}'s {@link Ext.core.Element#defaultUnit}s (by default, pixels).</li>
+     * <li>A String used to set the CSS width style.</li>
+     * </ul></div>
+     * @return {Ext.Component} this
+     */
+    setWidth : function(width) {
+        return this.setSize(width);
+    },
+
+    /**
+     * Sets the height of the component.  This method fires the {@link #resize} event.
+     * @param {Number} height The new height to set. This may be one of:<div class="mdetail-params"><ul>
+     * <li>A Number specifying the new height in the {@link #getEl Element}'s {@link Ext.core.Element#defaultUnit}s (by default, pixels).</li>
+     * <li>A String used to set the CSS height style.</li>
+     * <li><i>undefined</i> to leave the height unchanged.</li>
+     * </ul></div>
+     * @return {Ext.Component} this
+     */
+    setHeight : function(height) {
+        return this.setSize(undefined, height);
+    },
+
+    /**
+     * Gets the current size of the component's underlying element.
+     * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
+     */
+    getSize : function() {
+        return this.el.getSize();
+    },
+
+    /**
+     * Gets the current width of the component's underlying element.
+     * @return {Number}
+     */
+    getWidth : function() {
+        return this.el.getWidth();
+    },
+
+    /**
+     * Gets the current height of the component's underlying element.
+     * @return {Number}
+     */
+    getHeight : function() {
+        return this.el.getHeight();
+    },
+
+    /**
+     * Gets the {@link Ext.ComponentLoader} for this Component.
+     * @return {Ext.ComponentLoader} The loader instance, null if it doesn't exist.
+     */
+    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;
+    },
+
+    /**
+     * This method allows you to show or hide a LoadMask on top of this component.
+     * @param {Boolean/Object/String} load True to show the default LoadMask, a config object
+     * that will be passed to the LoadMask constructor, or a message String to show. False to
+     * hide the current LoadMask.
+     * @param {Boolean} targetEl True to mask the targetEl of this Component instead of the this.el.
+     * For example, setting this to true on a Panel will cause only the body to be masked. (defaults to false)
+     * @return {Ext.LoadMask} The LoadMask instance that has just been shown.
+     */
+    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;
+    },
+
+    /**
+     * Sets the dock position of this component in its parent panel. Note that
+     * this only has effect if this item is part of the dockedItems collection
+     * of a parent that has a DockLayout (note that any Panel has a DockLayout
+     * by default)
+     * @return {Component} this
+     */
+    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);
+    },
+
+    /**
+     * Destroys the Component.
+     */
+    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;
+                    // A zIndexManager is stamped into a *floating* Component when it is added to a Container.
+                    // If it has no zIndexManager at render time, it is assigned to the global Ext.WindowManager instance.
+                    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();
+
+                // Attempt to destroy all plugins
+                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;
+            }
+        }
+    },
+
+    /**
+     * Retrieves a plugin by its pluginId which has been bound to this
+     * component.
+     * @returns {Ext.AbstractPlugin} pluginInstance
+     */
+    getPlugin: function(pluginId) {
+        var i = 0,
+            plugins = this.plugins,
+            ln = plugins.length;
+        for (; i < ln; i++) {
+            if (plugins[i].pluginId === pluginId) {
+                return plugins[i];
+            }
+        }
+    },
+    
+    /**
+     * Determines whether this component is the descendant of a particular container.
+     * @param {Ext.Container} container
+     * @returns {Boolean} isDescendant
+     */
+    isDescendantOf: function(container) {
+        return !!this.findParentBy(function(p){
+            return p === container;
+        });
+    }
+}, function() {
+    this.createAlias({
+        on: 'addListener',
+        prev: 'previousSibling',
+        next: 'nextSibling'
+    });
+});
+
+/**
+ * @class Ext.AbstractPlugin
+ * @extends Object
+ *
+ * Plugins are injected 
+ */
+Ext.define('Ext.AbstractPlugin', {
+    disabled: false,
+    
+    constructor: function(config) {
+        //<debug>
+        if (!config.cmp && Ext.global.console) {
+            Ext.global.console.warn("Attempted to attach a plugin ");
+        }
+        //</debug>
+        Ext.apply(this, config);
+    },
+    
+    getCmp: function() {
+        return this.cmp;
+    },
+
+    /**
+     * The init method is invoked after initComponent has been run for the
+     * component which we are injecting the plugin into.
+     */
+    init: Ext.emptyFn,
+
+    /**
+     * The destroy method is invoked by the owning Component at the time the Component is being destroyed.
+     * Use this method to clean up an resources.
+     */
+    destroy: Ext.emptyFn,
+
+    /**
+     * Enable the plugin and set the disabled flag to false.
+     */
+    enable: function() {
+        this.disabled = false;
+    },
+
+    /**
+     * Disable the plugin and set the disabled flag to true.
+     */
+    disable: function() {
+        this.disabled = true;
+    }
+});
+
+/**
+ * @class Ext.data.Connection
+ * The Connection class encapsulates a connection to the page's originating domain, allowing requests to be made either
+ * to a configured URL, or to a URL specified at request time.
+ *
+ * Requests made by this class are asynchronous, and will return immediately. No data from the server will be available
+ * to the statement immediately following the {@link #request} call. To process returned data, use a success callback
+ * in the request options object, or an {@link #requestcomplete event listener}.
+ *
+ * <p><u>File Uploads</u></p>
+ *
+ * File uploads are not performed using normal "Ajax" techniques, that is they are not performed using XMLHttpRequests.
+ * Instead the form is submitted in the standard manner with the DOM &lt;form&gt; element temporarily modified to have its
+ * target set to refer to a dynamically generated, hidden &lt;iframe&gt; which is inserted into the document but removed
+ * after the return data has been gathered.
+ *
+ * The server response is parsed by the browser to create the document for the IFRAME. If the server is using JSON to
+ * send the return object, then the Content-Type header must be set to "text/html" in order to tell the browser to
+ * insert the text unchanged into the document body.
+ *
+ * Characters which are significant to an HTML parser must be sent as HTML entities, so encode "&lt;" as "&amp;lt;", "&amp;" as
+ * "&amp;amp;" etc.
+ *
+ * The response text is retrieved from the document, and a fake XMLHttpRequest object is created containing a
+ * responseText property in order to conform to the requirements of event handlers and callbacks.
+ *
+ * Be aware that file upload packets are sent with the content type multipart/form and some server technologies
+ * (notably JEE) may require some custom processing in order to retrieve parameter names and parameter values from the
+ * packet content.
+ *
+ * Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.
+ */
+Ext.define('Ext.data.Connection', {
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+
+    statics: {
+        requestId: 0
+    },
+
+    url: null,
+    async: true,
+    method: null,
+    username: '',
+    password: '',
+
+    /**
+     * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
+     * @type Boolean
+     */
+    disableCaching: true,
+
+    /**
+     * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
+     * through a cache buster. Defaults to '_dc'
+     * @type String
+     */
+    disableCachingParam: '_dc',
+
+    /**
+     * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+     */
+    timeout : 30000,
+
+    /**
+     * @param {Object} extraParams (Optional) Any parameters to be appended to the request.
+     */
+
+    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(
+            /**
+             * @event beforerequest
+             * Fires before a network request is made to retrieve a data object.
+             * @param {Connection} conn This Connection object.
+             * @param {Object} options The options config object passed to the {@link #request} method.
+             */
+            'beforerequest',
+            /**
+             * @event requestcomplete
+             * Fires if the request was successfully completed.
+             * @param {Connection} conn This Connection object.
+             * @param {Object} response The XHR object containing the response data.
+             * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
+             * for details.
+             * @param {Object} options The options config object passed to the {@link #request} method.
+             */
+            'requestcomplete',
+            /**
+             * @event requestexception
+             * Fires if an error HTTP status was returned from the server.
+             * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
+             * for details of HTTP status codes.
+             * @param {Connection} conn This Connection object.
+             * @param {Object} response The XHR object containing the response data.
+             * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
+             * for details.
+             * @param {Object} options The options config object passed to the {@link #request} method.
+             */
+            'requestexception'
+        );
+        this.requests = {};
+        this.mixins.observable.constructor.call(this);
+    },
+
+    /**
+     * <p>Sends an HTTP request to a remote server.</p>
+     * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
+     * return before the response has been received. Process any returned data
+     * in a callback function.</p>
+     * <pre><code>
+Ext.Ajax.request({
+url: 'ajax_demo/sample.json',
+success: function(response, opts) {
+  var obj = Ext.decode(response.responseText);
+  console.dir(obj);
+},
+failure: function(response, opts) {
+  console.log('server-side failure with status code ' + response.status);
+}
+});
+     * </code></pre>
+     * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
+     * @param {Object} options An object which may contain the following properties:<ul>
+     * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
+     * which to send the request, or a function to call which returns a URL string. The scope of the
+     * function is specified by the <tt>scope</tt> option. Defaults to the configured
+     * <tt>{@link #url}</tt>.</div></li>
+     * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
+     * An object containing properties which are used as parameters to the
+     * request, a url encoded string or a function to call to get either. The scope of the function
+     * is specified by the <tt>scope</tt> option.</div></li>
+     * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
+     * for the request. Defaults to the configured method, or if no method was configured,
+     * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
+     * the method name is case-sensitive and should be all caps.</div></li>
+     * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
+     * function to be called upon receipt of the HTTP response. The callback is
+     * called regardless of success or failure and is passed the following
+     * parameters:<ul>
+     * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
+     * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
+     * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
+     * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
+     * accessing elements of the response.</div></li>
+     * </ul></div></li>
+     * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
+     * to be called upon success of the request. The callback is passed the following
+     * parameters:<ul>
+     * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
+     * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
+     * </ul></div></li>
+     * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
+     * to be called upon failure of the request. The callback is passed the
+     * following parameters:<ul>
+     * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
+     * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
+     * </ul></div></li>
+     * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
+     * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
+     * specified as functions from which to draw values, then this also serves as the scope for those function calls.
+     * Defaults to the browser window.</div></li>
+     * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>
+     * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>
+     * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>
+     * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
+     * with the <tt>form</tt> option</b>.
+     * <p>True if the form object is a file upload (will be set automatically if the form was
+     * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
+     * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
+     * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
+     * DOM <tt>&lt;form></tt> element temporarily modified to have its
+     * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
+     * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
+     * but removed after the return data has been gathered.</p>
+     * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
+     * server is using JSON to send the return object, then the
+     * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
+     * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
+     * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
+     * is created containing a <tt>responseText</tt> property in order to conform to the
+     * requirements of event handlers and callbacks.</p>
+     * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
+     * and some server technologies (notably JEE) may require some custom processing in order to
+     * retrieve parameter names and parameter values from the packet content.</p>
+     * </div></li>
+     * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
+     * headers to set for the request.</div></li>
+     * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
+     * to use for the post. Note: This will be used instead of params for the post
+     * data. Any params will be appended to the URL.</div></li>
+     * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
+     * data to use as the post. Note: This will be used instead of params for the post
+     * data. Any params will be appended to the URL.</div></li>
+     * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
+     * to add a unique cache-buster param to GET requests.</div></li>
+     * </ul></p>
+     * <p>The options object may also contain any other property which might be needed to perform
+     * postprocessing in a callback because it is passed to callback functions.</p>
+     * @return {Object} request The request object. This may be used
+     * to cancel the request.
+     */
+    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 autoabort is set, cancel the current transactions
+            if (options.autoAbort === true || me.autoAbort) {
+                me.abort();
+            }
+
+            // create a connection object
+            xhr = this.getXhrInstance();
+
+            async = options.async !== false ? (options.async || me.async) : false;
+
+            // open the request
+            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);
+
+            // create the transaction object
+            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;
+
+            // bind our statechange listener
+            if (async) {
+                xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
+            }
+
+            // start the 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 a form using a hidden iframe.
+     * @param {Mixed} form The form to upload
+     * @param {String} url The url to post to
+     * @param {String} params Any extra parameters to pass
+     * @param {Object} options The initial options
+     */
+    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;
+
+        /*
+         * Originally this behaviour was modified for Opera 10 to apply the secure URL after
+         * the frame had been added to the document. It seems this has since been corrected in
+         * Opera so the behaviour has been reverted, the URL will be set before being added.
+         */
+        Ext.fly(frame).set({
+            id: id,
+            name: id,
+            cls: Ext.baseCSSPrefix + 'hide-display',
+            src: Ext.SSL_SECURE_URL
+        });
+
+        document.body.appendChild(frame);
+
+        // This is required so that IE doesn't pop the response up in a new window.
+        if (document.frames) {
+           document.frames[id].name = id;
+        }
+
+        Ext.fly(form).set({
+            target: id,
+            method: 'POST',
+            enctype: encoding,
+            encoding: encoding,
+            action: url || buf.action
+        });
+
+        // add dynamic params
+        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,
+            // bogus response object
+            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)) { // json response wrapped in textarea
+                        response.responseText = firstChild.value;
+                    } else {
+                        response.responseText = doc.body.innerHTML;
+                    }
+                }
+                //in IE the document may still have a body even if returns XML.
+                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);
+    },
+
+    /**
+     * Detect whether the form is intended to be used for an upload.
+     * @private
+     */
+    isFormUpload: function(options){
+        var form = this.getForm(options);
+        if (form) {
+            return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
+        }
+        return false;
+    },
+
+    /**
+     * Get the form object from options.
+     * @private
+     * @param {Object} options The request options
+     * @return {HTMLElement} The form, null if not passed
+     */
+    getForm: function(options){
+        return Ext.getDom(options.form) || null;
+    },
+
+    /**
+     * Set various options such as the url, params for the request
+     * @param {Object} options The initial options
+     * @param {Object} scope The scope to execute in
+     * @return {Object} The params for the request
+     */
+    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;
+
+
+        // allow params to be a method that returns the params object
+        if (Ext.isFunction(params)) {
+            params = params.call(scope, options);
+        }
+
+        // allow url to be a method that returns the actual url
+        if (Ext.isFunction(url)) {
+            url = url.call(scope, options);
+        }
+
+        url = this.setupUrl(options, url);
+
+        //<debug>
+        if (!url) {
+            Ext.Error.raise({
+                options: options,
+                msg: 'No URL specified'
+            });
+        }
+        //</debug>
+
+        // check for xml or json data, and make sure json data is encoded
+        data = options.rawData || options.xmlData || jsonData || null;
+        if (jsonData && !Ext.isPrimitive(jsonData)) {
+            data = Ext.encode(data);
+        }
+
+        // make sure params are a url encoded string and include any extraParams if specified
+        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);
+
+        // decide the proper method for this request
+        method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
+        this.setupMethod(options, method);
+
+
+        disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false;
+        // if the method is get append date to prevent caching
+        if (method === 'GET' && disableCache) {
+            url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime()));
+        }
+
+        // if the method is get or there is json/xml data append the params to the url
+        if ((method == 'GET' || data) && params) {
+            url = Ext.urlAppend(url, params);
+            params = null;
+        }
+
+        // allow params to be forced into the url
+        if (urlParams) {
+            url = Ext.urlAppend(url, urlParams);
+        }
+
+        return {
+            url: url,
+            method: method,
+            data: data || params || null
+        };
+    },
+
+    /**
+     * Template method for overriding url
+     * @private
+     * @param {Object} options
+     * @param {String} url
+     * @return {String} The modified url
+     */
+    setupUrl: function(options, url){
+        var form = this.getForm(options);
+        if (form) {
+            url = url || form.action;
+        }
+        return url;
+    },
+
+
+    /**
+     * Template method for overriding params
+     * @private
+     * @param {Object} options
+     * @param {String} params
+     * @return {String} The modified params
+     */
+    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;
+    },
+
+    /**
+     * Template method for overriding method
+     * @private
+     * @param {Object} options
+     * @param {String} method
+     * @return {String} The modified method
+     */
+    setupMethod: function(options, method){
+        if (this.isFormUpload(options)) {
+            return 'POST';
+        }
+        return method;
+    },
+
+    /**
+     * Setup all the headers for the request
+     * @private
+     * @param {Object} xhr The xhr object
+     * @param {Object} options The options for the request
+     * @param {Object} data The data for the request
+     * @param {Object} params The params for the request
+     */
+    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;
+        }
+        // set up all the request headers on the xhr object
+        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;
+    },
+
+    /**
+     * Creates the appropriate XHR transport for the browser.
+     * @private
+     */
+    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;
+    })(),
+
+    /**
+     * Determine whether this object has a request outstanding.
+     * @param {Object} request (Optional) defaults to the last transaction
+     * @return {Boolean} True if there is an outstanding request.
+     */
+    isLoading : function(request) {
+        if (!(request && request.xhr)) {
+            return false;
+        }
+        // if there is a connection and readyState is not 0 or 4
+        var state = request.xhr.readyState;
+        return !(state === 0 || state == 4);
+    },
+
+    /**
+     * Aborts any outstanding request.
+     * @param {Object} request (Optional) defaults to the last request
+     */
+    abort : function(request) {
+        var me = this,
+            requests = me.requests,
+            id;
+
+        if (request && me.isLoading(request)) {
+            /**
+             * Clear out the onreadystatechange here, this allows us
+             * greater control, the browser may/may not fire the function
+             * depending on a series of conditions.
+             */
+            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]);
+                }
+            }
+        }
+    },
+
+    /**
+     * Fires when the state of the xhr changes
+     * @private
+     * @param {Object} request The request
+     */
+    onStateChange : function(request) {
+        if (request.xhr.readyState == 4) {
+            this.clearTimeout(request);
+            this.onComplete(request);
+            this.cleanup(request);
+        }
+    },
+
+    /**
+     * Clear the timeout on the request
+     * @private
+     * @param {Object} The request
+     */
+    clearTimeout: function(request){
+        clearTimeout(request.timeout);
+        delete request.timeout;
+    },
+
+    /**
+     * Clean up any left over information from the request
+     * @private
+     * @param {Object} The request
+     */
+    cleanup: function(request){
+        request.xhr = null;
+        delete request.xhr;
+    },
+
+    /**
+     * To be called when the request has come back from the server
+     * @private
+     * @param {Object} request
+     * @return {Object} The response
+     */
+    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;
+    },
+
+    /**
+     * Check if the response status was successful
+     * @param {Number} status The status code
+     * @return {Object} An object containing success/status state
+     */
+    parseStatus: function(status) {
+        // see: https://prototype.lighthouseapp.com/projects/8886/tickets/129-ie-mangles-http-response-status-code-204-to-1223
+        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
+        };
+    },
+
+    /**
+     * Create the response object
+     * @private
+     * @param {Object} request
+     */
+    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
+        };
+
+        // If we don't explicitly tear down the xhr reference, IE6/IE7 will hold this in the closure of the
+        // functions created with getResponseHeader/getAllResponseHeaders
+        xhr = null;
+        return response;
+    },
+
+    /**
+     * Create the exception object
+     * @private
+     * @param {Object} request
+     */
+    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
+        };
+    }
+});
+
+/**
+ * @class Ext.Ajax
+ * @singleton
+ * @markdown
+ * @extends Ext.data.Connection
+
+A singleton instance of an {@link Ext.data.Connection}. This class
+is used to communicate with your server side code. It can be used as follows:
+
+    Ext.Ajax.request({
+        url: 'page.php',
+        params: {
+            id: 1
+        },
+        success: function(response){
+            var text = response.responseText;
+            // process server response here
+        }
+    });
+
+Default options for all requests can be set be changing a property on the Ext.Ajax class:
+
+    Ext.Ajax.timeout = 60000; // 60 seconds
+
+Any options specified in the request method for the Ajax request will override any
+defaults set on the Ext.Ajax class. In the code sample below, the timeout for the
+request will be 60 seconds.
+
+    Ext.Ajax.timeout = 120000; // 120 seconds
+    Ext.Ajax.request({
+        url: 'page.aspx',
+        timeout: 60000
+    });
+
+In general, this class will be used for all Ajax requests in your application.
+The main reason for creating a separate {@link Ext.data.Connection} is for a
+series of requests that share common settings that are different to all other
+requests in the application.
+
+ */
+Ext.define('Ext.Ajax', {
+    extend: 'Ext.data.Connection',
+    singleton: true,
+
+    /**
+     * @cfg {String} url @hide
+     */
+    /**
+     * @cfg {Object} extraParams @hide
+     */
+    /**
+     * @cfg {Object} defaultHeaders @hide
+     */
+    /**
+     * @cfg {String} method (Optional) @hide
+     */
+    /**
+     * @cfg {Number} timeout (Optional) @hide
+     */
+    /**
+     * @cfg {Boolean} autoAbort (Optional) @hide
+     */
+
+    /**
+     * @cfg {Boolean} disableCaching (Optional) @hide
+     */
+
+    /**
+     * @property  disableCaching
+     * True to add a unique cache-buster param to GET requests. (defaults to true)
+     * @type Boolean
+     */
+    /**
+     * @property  url
+     * The default URL to be used for requests to the server. (defaults to undefined)
+     * If the server receives all requests through one URL, setting this once is easier than
+     * entering it on every request.
+     * @type String
+     */
+    /**
+     * @property  extraParams
+     * An object containing properties which are used as extra parameters to each request made
+     * by this object (defaults to undefined). Session information and other data that you need
+     * to pass with each request are commonly put here.
+     * @type Object
+     */
+    /**
+     * @property  defaultHeaders
+     * An object containing request headers which are added to each request made by this object
+     * (defaults to undefined).
+     * @type Object
+     */
+    /**
+     * @property  method
+     * The default HTTP method to be used for requests. Note that this is case-sensitive and
+     * should be all caps (defaults to undefined; if not set but params are present will use
+     * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
+     * @type String
+     */
+    /**
+     * @property  timeout
+     * The timeout in milliseconds to be used for requests. (defaults to 30000)
+     * @type Number
+     */
+
+    /**
+     * @property  autoAbort
+     * Whether a new request should abort any pending requests. (defaults to false)
+     * @type Boolean
+     */
+    autoAbort : false
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.Association
+ * @extends Object
+ *
+ * <p>Associations enable you to express relationships between different {@link Ext.data.Model Models}. Let's say we're
+ * writing an ecommerce system where Users can make Orders - there's a relationship between these Models that we can
+ * express like this:</p>
+ *
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'email'],
+
+    hasMany: {model: 'Order', name: 'orders'}
+});
+
+Ext.define('Order', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'user_id', 'status', 'price'],
+
+    belongsTo: 'User'
+});
+</code></pre>
+ *
+ * <p>We've set up two models - User and Order - and told them about each other. You can set up as many associations on
+ * each Model as you need using the two default types - {@link Ext.data.HasManyAssociation hasMany} and
+ * {@link Ext.data.BelongsToAssociation belongsTo}. There's much more detail on the usage of each of those inside their
+ * documentation pages. If you're not familiar with Models already, {@link Ext.data.Model there is plenty on those too}.</p>
+ *
+ * <p><u>Further Reading</u></p>
+ *
+ * <ul style="list-style-type: disc; padding-left: 20px;">
+ *   <li>{@link Ext.data.HasManyAssociation hasMany associations}
+ *   <li>{@link Ext.data.BelongsToAssociation belongsTo associations}
+ *   <li>{@link Ext.data.Model using Models}
+ * </ul>
+ * 
+ * <b>Self association models</b>
+ * <p>We can also have models that create parent/child associations between the same type. Below is an example, where
+ * groups can be nested inside other groups:</p>
+ * <pre><code>
+
+// Server Data
+{
+    "groups": {
+        "id": 10,
+        "parent_id": 100,
+        "name": "Main Group",
+        "parent_group": {
+            "id": 100,
+            "parent_id": null,
+            "name": "Parent Group"
+        },
+        "child_groups": [{
+            "id": 2,
+            "parent_id": 10,
+            "name": "Child Group 1"
+        },{
+            "id": 3,
+            "parent_id": 10,
+            "name": "Child Group 2"
+        },{
+            "id": 4,
+            "parent_id": 10,
+            "name": "Child Group 3"
+        }]
+    }
+}
+
+// Client code
+Ext.define('Group', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'parent_id', 'name'],
+    proxy: {
+        type: 'ajax',
+        url: 'data.json',
+        reader: {
+            type: 'json',
+            root: 'groups'
+        }
+    },
+    associations: [{
+        type: 'hasMany',
+        model: 'Group',
+        primaryKey: 'id',
+        foreignKey: 'parent_id',
+        autoLoad: true,
+        associationKey: 'child_groups' // read child data from child_groups
+    }, {
+        type: 'belongsTo',
+        model: 'Group',
+        primaryKey: 'id',
+        foreignKey: 'parent_id',
+        autoLoad: true,
+        associationKey: 'parent_group' // read parent data from parent_group
+    }]
+});
+
+
+Ext.onReady(function(){
+    
+    Group.load(10, {
+        success: function(group){
+            console.log(group.getGroup().get('name'));
+            
+            group.groups().each(function(rec){
+                console.log(rec.get('name'));
+            });
+        }
+    });
+    
+});
+ * </code></pre>
+ *
+ * @constructor
+ * @param {Object} config Optional config object
+ */
+Ext.define('Ext.data.Association', {
+    /**
+     * @cfg {String} ownerModel The string name of the model that owns the association. Required
+     */
+
+    /**
+     * @cfg {String} associatedModel The string name of the model that is being associated with. Required
+     */
+
+    /**
+     * @cfg {String} primaryKey The name of the primary key on the associated model. Defaults to 'id'.
+     * In general this will be the {@link Ext.data.Model#idProperty} of the Model.
+     */
+    primaryKey: 'id',
+
+    /**
+     * @cfg {Ext.data.reader.Reader} reader A special reader to read associated data
+     */
+    
+    /**
+     * @cfg {String} associationKey The name of the property in the data to read the association from.
+     * Defaults to the name of the associated model.
+     */
+
+    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);
+                    //TODO Add this back when it's fixed
+//                    case 'polymorphic':
+//                        return Ext.create('Ext.data.PolymorphicAssociation', association);
+                    default:
+                        //<debug>
+                        Ext.Error.raise('Unknown Association type: "' + association.type + '"');
+                        //</debug>
+                }
+            }
+            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;
+
+        //<debug>
+        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 + ")");
+        }
+        //</debug>
+
+        this.ownerModel = ownerModel;
+        this.associatedModel = associatedModel;
+
+        /**
+         * The name of the model that 'owns' the association
+         * @property ownerName
+         * @type String
+         */
+
+        /**
+         * The name of the model is on the other end of the association (e.g. if a User model hasMany Orders, this is 'Order')
+         * @property associatedName
+         * @type String
+         */
+
+        Ext.applyIf(this, {
+            ownerName : ownerName,
+            associatedName: associatedName
+        });
+    },
+
+    /**
+     * Get a specialized reader for reading associated data
+     * @return {Ext.data.reader.Reader} The reader, null if not supplied
+     */
+    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;
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.ModelManager
+ * @extends Ext.AbstractManager
+
+The ModelManager keeps track of all {@link Ext.data.Model} types defined in your application.
+
+__Creating Model Instances__
+Model instances can be created by using the {@link #create} function. It is also possible to do
+this by using the Model type directly. The following snippets are equivalent:
+
+    Ext.define('User', {
+        extend: 'Ext.data.Model',
+        fields: ['first', 'last']
+    });
+    
+    // method 1, create through the manager
+    Ext.ModelManager.create({
+        first: 'Ed',
+        last: 'Spencer'
+    }, 'User');
+    
+    // method 2, create on the type directly
+    new User({
+        first: 'Ed',
+        last: 'Spencer'
+    });
+    
+__Accessing Model Types__
+A reference to a Model type can be obtained by using the {@link #getModel} function. Since models types
+are normal classes, you can access the type directly. The following snippets are equivalent:
+
+    Ext.define('User', {
+        extend: 'Ext.data.Model',
+        fields: ['first', 'last']
+    });
+    
+    // method 1, access model type through the manager
+    var UserType = Ext.ModelManager.getModel('User');
+    
+    // method 2, reference the type directly
+    var UserType = User;
+
+ * @markdown
+ * @singleton
+ */
+Ext.define('Ext.ModelManager', {
+    extend: 'Ext.AbstractManager',
+    alternateClassName: 'Ext.ModelMgr',
+    requires: ['Ext.data.Association'],
+    
+    singleton: true,
+    
+    typeName: 'mtype',
+    
+    /**
+     * Private stack of associations that must be created once their associated model has been defined
+     * @property associationStack
+     * @type Array
+     */
+    associationStack: [],
+    
+    /**
+     * Registers a model definition. All model plugins marked with isDefault: true are bootstrapped
+     * immediately, as are any addition plugins defined in the model config.
+     * @private
+     */
+    registerType: function(name, config) {
+        var proto = config.prototype,
+            model;
+        if (proto && proto.isModel) {
+            // registering an already defined model
+            model = config;
+        } else {
+            // passing in a configuration
+            if (!config.extend) {
+                config.extend = 'Ext.data.Model';
+            }
+            model = Ext.define(name, config);
+        }
+        this.types[name] = model;
+        return model;
+    },
+    
+    /**
+     * @private
+     * Private callback called whenever a model has just been defined. This sets up any associations
+     * that were waiting for the given model to be defined
+     * @param {Function} model The model that was just created
+     */
+    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);
+        }
+    },
+    
+    /**
+     * Registers an association where one of the models defined doesn't exist yet.
+     * The ModelManager will check when new models are registered if it can link them
+     * together
+     * @private
+     * @param {Ext.data.Association} association The association
+     */
+    registerDeferredAssociation: function(association){
+        this.associationStack.push(association);
+    },
+    
+    /**
+     * Returns the {@link Ext.data.Model} for a given model name
+     * @param {String/Object} id The id of the model or the model instance.
+     */
+    getModel: function(id) {
+        var model = id;
+        if (typeof model == 'string') {
+            model = this.types[model];
+        }
+        return model;
+    },
+    
+    /**
+     * Creates a new instance of a Model using the given data.
+     * @param {Object} data Data to initialize the Model's fields with
+     * @param {String} name The name of the model to create
+     * @param {Number} id Optional unique id of the Model instance (see {@link Ext.data.Model})
+     */
+    create: function(config, name, id) {
+        var con = typeof name == 'function' ? name : this.types[name || config.name];
+        
+        return new con(config, id);
+    }
+}, function() {
+    
+    /**
+     * Creates a new Model class from the specified config object. See {@link Ext.data.Model} for full examples.
+     * 
+     * @param {Object} config A configuration object for the Model you wish to create.
+     * @return {Ext.data.Model} The newly registered Model
+     * @member Ext
+     * @method regModel
+     */
+    Ext.regModel = function() {
+        //<debug>
+        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: []});.');
+        }
+        //</debug>
+        return this.ModelManager.registerType.apply(this.ModelManager, arguments);
+    };
+});
+
+/**
+ * @class Ext.app.Controller
+ * @constructor
+ * 
+ * Controllers are the glue that binds an application together. All they really do is listen for events (usually from
+ * views) and take some action. Here's how we might create a Controller to manage Users:
+ * 
+ *     Ext.define('MyApp.controller.Users', {
+ *         extend: 'Ext.app.Controller',
+ * 
+ *         init: function() {
+ *             console.log('Initialized Users! This happens before the Application launch function is called');
+ *         }
+ *     });
+ * 
+ * The init function is a special method that is called when your application boots. It is called before the 
+ * {@link Ext.app.Application Application}'s launch function is executed so gives a hook point to run any code before
+ * your Viewport is created.
+ * 
+ * The init function is a great place to set up how your controller interacts with the view, and is usually used in 
+ * conjunction with another Controller function - {@link Ext.app.Controller#control control}. The control function 
+ * makes it easy to listen to events on your view classes and take some action with a handler function. Let's update
+ * our Users controller to tell us when the panel is rendered:
+ * 
+ *     Ext.define('MyApp.controller.Users', {
+ *         extend: 'Ext.app.Controller',
+ * 
+ *         init: function() {
+ *             this.control({
+ *                 'viewport > panel': {
+ *                     render: this.onPanelRendered
+ *                 }
+ *             });
+ *         },
+ * 
+ *         onPanelRendered: function() {
+ *             console.log('The panel was rendered');
+ *         }
+ *     });
+ * 
+ * We've updated the init function to use this.control to set up listeners on views in our application. The control
+ * function uses the new ComponentQuery engine to quickly and easily get references to components on the page. If you
+ * are not familiar with ComponentQuery yet, be sure to check out THIS GUIDE for a full explanation. In brief though,
+ * it allows us to pass a CSS-like selector that will find every matching component on the page.
+ * 
+ * In our init function above we supplied 'viewport > panel', which translates to "find me every Panel that is a direct
+ * child of a Viewport". We then supplied an object that maps event names (just 'render' in this case) to handler 
+ * functions. The overall effect is that whenever any component that matches our selector fires a 'render' event, our 
+ * onPanelRendered function is called.
+ * 
+ * <u>Using refs</u>
+ * 
+ * One of the most useful parts of Controllers is the new ref system. These use the new {@link Ext.ComponentQuery} to
+ * make it really easy to get references to Views on your page. Let's look at an example of this now:
+ * 
+ * Ext.define('MyApp.controller.Users', {
+     extend: 'Ext.app.Controller',
+
+     refs: [
+         {
+             ref: 'list',
+             selector: 'grid'
+         }
+     ],
+
+     init: function() {
+         this.control({
+             'button': {
+                 click: this.refreshGrid
+             }
+         });
+     },
+
+     refreshGrid: function() {
+         this.getList().store.load();
+     }
+ });
+ * 
+ * This example assumes the existence of a {@link Ext.grid.Panel Grid} on the page, which contains a single button to 
+ * refresh the Grid when clicked. In our refs array, we set up a reference to the grid. There are two parts to this - 
+ * the 'selector', which is a {@link Ext.ComponentQuery ComponentQuery} selector which finds any grid on the page and
+ * assigns it to the reference 'list'.
+ * 
+ * By giving the reference a name, we get a number of things for free. The first is the getList function that we use in
+ * the refreshGrid method above. This is generated automatically by the Controller based on the name of our ref, which 
+ * was capitalized and prepended with get to go from 'list' to 'getList'.
+ * 
+ * The way this works is that the first time getList is called by your code, the ComponentQuery selector is run and the
+ * first component that matches the selector ('grid' in this case) will be returned. All future calls to getList will 
+ * use a cached reference to that grid. Usually it is advised to use a specific ComponentQuery selector that will only
+ * match a single View in your application (in the case above our selector will match any grid on the page).
+ * 
+ * Bringing it all together, our init function is called when the application boots, at which time we call this.control
+ * to listen to any click on a {@link Ext.button.Button button} and call our refreshGrid function (again, this will 
+ * match any button on the page so we advise a more specific selector than just 'button', but have left it this way for
+ * simplicity). When the button is clicked we use out getList function to refresh the grid.
+ * 
+ * You can create any number of refs and control any number of components this way, simply adding more functions to 
+ * your Controller as you go. For an example of real-world usage of Controllers see the Feed Viewer example in the 
+ * examples/app/feed-viewer folder in the SDK download.
+ * 
+ * <u>Generated getter methods</u>
+ * 
+ * Refs aren't the only thing that generate convenient getter methods. Controllers often have to deal with Models and 
+ * Stores so the framework offers a couple of easy ways to get access to those too. Let's look at another example:
+ * 
+ * Ext.define('MyApp.controller.Users', {
+     extend: 'Ext.app.Controller',
+
+     models: ['User'],
+     stores: ['AllUsers', 'AdminUsers'],
+
+     init: function() {
+         var User = this.getUserModel(),
+             allUsers = this.getAllUsersStore();
+
+         var ed = new User({name: 'Ed'});
+         allUsers.add(ed);
+     }
+ });
+ * 
+ * By specifying Models and Stores that the Controller cares about, it again dynamically loads them from the appropriate
+ * locations (app/model/User.js, app/store/AllUsers.js and app/store/AdminUsers.js in this case) and creates getter 
+ * functions for them all. The example above will create a new User model instance and add it to the AllUsers Store.
+ * Of course, you could do anything in this function but in this case we just did something simple to demonstrate the 
+ * functionality.
+ * 
+ * <u>Further Reading</u>
+ * 
+ * For more information about writing Ext JS 4 applications, please see the <a href="../guide/application_architecture">
+ * application architecture guide</a>. Also see the {@link Ext.app.Application} documentation.
+ * 
+ * @markdown
+ * @docauthor Ed Spencer
+ */  
+Ext.define('Ext.app.Controller', {
+    /**
+     * @cfg {Object} id The id of this controller. You can use this id when dispatching.
+     */
+
+    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);
+        }
+    },
+
+    // Template method
+    init: function(application) {},
+    // Template method
+    onLaunch: function(application) {},
+
+    createGetters: function(type, refs) {
+        type = Ext.String.capitalize(type);
+        Ext.Array.each(refs, function(ref) {
+            var fn = 'get',
+                parts = ref.split('.');
+
+            // Handle namespaced class names. E.g. feed.Add becomes getFeedAddView etc.
+            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);
+            }
+            // Execute it right away
+            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);
+    }
+});
+
+/**
+ * @class Ext.data.SortTypes
+ * This class defines a series of static methods that are used on a
+ * {@link Ext.data.Field} for performing sorting. The methods cast the 
+ * underlying values into a data type that is appropriate for sorting on
+ * that particular field.  If a {@link Ext.data.Field#type} is specified, 
+ * the sortType will be set to a sane default if the sortType is not 
+ * explicitly defined on the field. The sortType will make any necessary
+ * modifications to the value and return it.
+ * <ul>
+ * <li><b>asText</b> - Removes any tags and converts the value to a string</li>
+ * <li><b>asUCText</b> - Removes any tags and converts the value to an uppercase string</li>
+ * <li><b>asUCText</b> - Converts the value to an uppercase string</li>
+ * <li><b>asDate</b> - Converts the value into Unix epoch time</li>
+ * <li><b>asFloat</b> - Converts the value to a floating point number</li>
+ * <li><b>asInt</b> - Converts the value to an integer number</li>
+ * </ul>
+ * <p>
+ * It is also possible to create a custom sortType that can be used throughout
+ * an application.
+ * <pre><code>
+Ext.apply(Ext.data.SortTypes, {
+    asPerson: function(person){
+        // expects an object with a first and last name property
+        return person.lastName.toUpperCase() + person.firstName.toLowerCase();
+    }    
+});
+
+Ext.define('Employee', {
+    extend: 'Ext.data.Model',
+    fields: [{
+        name: 'person',
+        sortType: 'asPerson'
+    }, {
+        name: 'salary',
+        type: 'float' // sortType set to asFloat
+    }]
+});
+ * </code></pre>
+ * </p>
+ * @singleton
+ * @docauthor Evan Trimboli <evan@sencha.com>
+ */
+Ext.define('Ext.data.SortTypes', {
+    
+    singleton: true,
+    
+    /**
+     * Default sort that does nothing
+     * @param {Mixed} s The value being converted
+     * @return {Mixed} The comparison value
+     */
+    none : function(s) {
+        return s;
+    },
+
+    /**
+     * The regular expression used to strip tags
+     * @type {RegExp}
+     * @property
+     */
+    stripTagsRE : /<\/?[^>]+>/gi,
+
+    /**
+     * Strips all HTML tags to sort on text only
+     * @param {Mixed} s The value being converted
+     * @return {String} The comparison value
+     */
+    asText : function(s) {
+        return String(s).replace(this.stripTagsRE, "");
+    },
+
+    /**
+     * Strips all HTML tags to sort on text only - Case insensitive
+     * @param {Mixed} s The value being converted
+     * @return {String} The comparison value
+     */
+    asUCText : function(s) {
+        return String(s).toUpperCase().replace(this.stripTagsRE, "");
+    },
+
+    /**
+     * Case insensitive string
+     * @param {Mixed} s The value being converted
+     * @return {String} The comparison value
+     */
+    asUCString : function(s) {
+        return String(s).toUpperCase();
+    },
+
+    /**
+     * Date sorting
+     * @param {Mixed} s The value being converted
+     * @return {Number} The comparison value
+     */
+    asDate : function(s) {
+        if(!s){
+            return 0;
+        }
+        if(Ext.isDate(s)){
+            return s.getTime();
+        }
+        return Date.parse(String(s));
+    },
+
+    /**
+     * Float sorting
+     * @param {Mixed} s The value being converted
+     * @return {Float} The comparison value
+     */
+    asFloat : function(s) {
+        var val = parseFloat(String(s).replace(/,/g, ""));
+        return isNaN(val) ? 0 : val;
+    },
+
+    /**
+     * Integer sorting
+     * @param {Mixed} s The value being converted
+     * @return {Number} The comparison value
+     */
+    asInt : function(s) {
+        var val = parseInt(String(s).replace(/,/g, ""), 10);
+        return isNaN(val) ? 0 : val;
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.Errors
+ * @extends Ext.util.MixedCollection
+ * 
+ * <p>Wraps a collection of validation error responses and provides convenient functions for
+ * accessing and errors for specific fields.</p>
+ * 
+ * <p>Usually this class does not need to be instantiated directly - instances are instead created
+ * automatically when {@link Ext.data.Model#validate validate} on a model instance:</p>
+ * 
+<pre><code>
+//validate some existing model instance - in this case it returned 2 failures messages
+var errors = myModel.validate();
+
+errors.isValid(); //false
+
+errors.length; //2
+errors.getByField('name');  // [{field: 'name',  error: 'must be present'}]
+errors.getByField('title'); // [{field: 'title', error: 'is too short'}]
+</code></pre>
+ */
+Ext.define('Ext.data.Errors', {
+    extend: 'Ext.util.MixedCollection',
+    
+    /**
+     * Returns true if there are no errors in the collection
+     * @return {Boolean} 
+     */
+    isValid: function() {
+        return this.length === 0;
+    },
+    
+    /**
+     * Returns all of the errors for the given field
+     * @param {String} fieldName The field to get errors for
+     * @return {Array} All errors for the given field
+     */
+    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;
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.Operation
+ * @extends Object
+ * 
+ * <p>Represents a single read or write operation performed by a {@link Ext.data.proxy.Proxy Proxy}.
+ * Operation objects are used to enable communication between Stores and Proxies. Application
+ * developers should rarely need to interact with Operation objects directly.</p>
+ * 
+ * <p>Several Operations can be batched together in a {@link Ext.data.Batch batch}.</p>
+ * 
+ * @constructor
+ * @param {Object} config Optional config object
+ */
+Ext.define('Ext.data.Operation', {
+    /**
+     * @cfg {Boolean} synchronous True if this Operation is to be executed synchronously (defaults to true). This
+     * property is inspected by a {@link Ext.data.Batch Batch} to see if a series of Operations can be executed in
+     * parallel or not.
+     */
+    synchronous: true,
+    
+    /**
+     * @cfg {String} action The action being performed by this Operation. Should be one of 'create', 'read', 'update' or 'destroy'
+     */
+    action: undefined,
+    
+    /**
+     * @cfg {Array} filters Optional array of filter objects. Only applies to 'read' actions.
+     */
+    filters: undefined,
+    
+    /**
+     * @cfg {Array} sorters Optional array of sorter objects. Only applies to 'read' actions.
+     */
+    sorters: undefined,
+    
+    /**
+     * @cfg {Object} group Optional grouping configuration. Only applies to 'read' actions where grouping is desired.
+     */
+    group: undefined,
+    
+    /**
+     * @cfg {Number} start The start index (offset), used in paging when running a 'read' action.
+     */
+    start: undefined,
+    
+    /**
+     * @cfg {Number} limit The number of records to load. Used on 'read' actions when paging is being used.
+     */
+    limit: undefined,
+    
+    /**
+     * @cfg {Ext.data.Batch} batch The batch that this Operation is a part of (optional)
+     */
+    batch: undefined,
+        
+    /**
+     * Read-only property tracking the start status of this Operation. Use {@link #isStarted}.
+     * @property started
+     * @type Boolean
+     * @private
+     */
+    started: false,
+    
+    /**
+     * Read-only property tracking the run status of this Operation. Use {@link #isRunning}.
+     * @property running
+     * @type Boolean
+     * @private
+     */
+    running: false,
+    
+    /**
+     * Read-only property tracking the completion status of this Operation. Use {@link #isComplete}.
+     * @property complete
+     * @type Boolean
+     * @private
+     */
+    complete: false,
+    
+    /**
+     * Read-only property tracking whether the Operation was successful or not. This starts as undefined and is set to true
+     * or false by the Proxy that is executing the Operation. It is also set to false by {@link #setException}. Use
+     * {@link #wasSuccessful} to query success status.
+     * @property success
+     * @type Boolean
+     * @private
+     */
+    success: undefined,
+    
+    /**
+     * Read-only property tracking the exception status of this Operation. Use {@link #hasException} and see {@link #getError}.
+     * @property exception
+     * @type Boolean
+     * @private
+     */
+    exception: false,
+    
+    /**
+     * The error object passed when {@link #setException} was called. This could be any object or primitive.
+     * @property error
+     * @type Mixed
+     * @private
+     */
+    error: undefined,
+    
+    constructor: function(config) {
+        Ext.apply(this, config || {});
+    },
+    
+    /**
+     * Marks the Operation as started
+     */
+    setStarted: function() {
+        this.started = true;
+        this.running = true;
+    },
+    
+    /**
+     * Marks the Operation as completed
+     */
+    setCompleted: function() {
+        this.complete = true;
+        this.running  = false;
+    },
+    
+    /**
+     * Marks the Operation as successful
+     */
+    setSuccessful: function() {
+        this.success = true;
+    },
+    
+    /**
+     * Marks the Operation as having experienced an exception. Can be supplied with an option error message/object.
+     * @param {Mixed} error Optional error string/object
+     */
+    setException: function(error) {
+        this.exception = true;
+        this.success = false;
+        this.running = false;
+        this.error = error;
+    },
+    
+    /**
+     * Returns true if this Operation encountered an exception (see also {@link #getError})
+     * @return {Boolean} True if there was an exception
+     */
+    hasException: function() {
+        return this.exception === true;
+    },
+    
+    /**
+     * Returns the error string or object that was set using {@link #setException}
+     * @return {Mixed} The error object
+     */
+    getError: function() {
+        return this.error;
+    },
+    
+    /**
+     * Returns an array of Ext.data.Model instances as set by the Proxy.
+     * @return {Array} Any loaded Records
+     */
+    getRecords: function() {
+        var resultSet = this.getResultSet();
+        
+        return (resultSet === undefined ? this.records : resultSet.records);
+    },
+    
+    /**
+     * Returns the ResultSet object (if set by the Proxy). This object will contain the {@link Ext.data.Model model} instances
+     * as well as meta data such as number of instances fetched, number available etc
+     * @return {Ext.data.ResultSet} The ResultSet object
+     */
+    getResultSet: function() {
+        return this.resultSet;
+    },
+    
+    /**
+     * Returns true if the Operation has been started. Note that the Operation may have started AND completed,
+     * see {@link #isRunning} to test if the Operation is currently running.
+     * @return {Boolean} True if the Operation has started
+     */
+    isStarted: function() {
+        return this.started === true;
+    },
+    
+    /**
+     * Returns true if the Operation has been started but has not yet completed.
+     * @return {Boolean} True if the Operation is currently running
+     */
+    isRunning: function() {
+        return this.running === true;
+    },
+    
+    /**
+     * Returns true if the Operation has been completed
+     * @return {Boolean} True if the Operation is complete
+     */
+    isComplete: function() {
+        return this.complete === true;
+    },
+    
+    /**
+     * Returns true if the Operation has completed and was successful
+     * @return {Boolean} True if successful
+     */
+    wasSuccessful: function() {
+        return this.isComplete() && this.success === true;
+    },
+    
+    /**
+     * @private
+     * Associates this Operation with a Batch
+     * @param {Ext.data.Batch} batch The batch
+     */
+    setBatch: function(batch) {
+        this.batch = batch;
+    },
+    
+    /**
+     * Checks whether this operation should cause writing to occur.
+     * @return {Boolean} Whether the operation should cause a write to occur.
+     */
+    allowWrite: function() {
+        return this.action != 'read';
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.validations
+ * @extends Object
+ * 
+ * <p>This singleton contains a set of validation functions that can be used to validate any type
+ * of data. They are most often used in {@link Ext.data.Model Models}, where they are automatically
+ * set up and executed.</p>
+ */
+Ext.define('Ext.data.validations', {
+    singleton: true,
+    
+    /**
+     * The default error message used when a presence validation fails
+     * @property presenceMessage
+     * @type String
+     */
+    presenceMessage: 'must be present',
+    
+    /**
+     * The default error message used when a length validation fails
+     * @property lengthMessage
+     * @type String
+     */
+    lengthMessage: 'is the wrong length',
+    
+    /**
+     * The default error message used when a format validation fails
+     * @property formatMessage
+     * @type Boolean
+     */
+    formatMessage: 'is the wrong format',
+    
+    /**
+     * The default error message used when an inclusion validation fails
+     * @property inclusionMessage
+     * @type String
+     */
+    inclusionMessage: 'is not included in the list of acceptable values',
+    
+    /**
+     * The default error message used when an exclusion validation fails
+     * @property exclusionMessage
+     * @type String
+     */
+    exclusionMessage: 'is not an acceptable value',
+    
+    /**
+     * Validates that the given value is present
+     * @param {Object} config Optional config object
+     * @param {Mixed} value The value to validate
+     * @return {Boolean} True if validation passed
+     */
+    presence: function(config, value) {
+        if (value === undefined) {
+            value = config;
+        }
+        
+        return !!value;
+    },
+    
+    /**
+     * Returns true if the given value is between the configured min and max values
+     * @param {Object} config Optional config object
+     * @param {String} value The value to validate
+     * @return {Boolean} True if the value passes validation
+     */
+    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;
+        }
+    },
+    
+    /**
+     * Returns true if the given value passes validation against the configured {@link #matcher} regex
+     * @param {Object} config Optional config object
+     * @param {String} value The value to validate
+     * @return {Boolean} True if the value passes the format validation
+     */
+    format: function(config, value) {
+        return !!(config.matcher && config.matcher.test(value));
+    },
+    
+    /**
+     * Validates that the given value is present in the configured {@link #list}
+     * @param {String} value The value to validate
+     * @return {Boolean} True if the value is present in the list
+     */
+    inclusion: function(config, value) {
+        return config.list && Ext.Array.indexOf(config.list,value) != -1;
+    },
+    
+    /**
+     * Validates that the given value is present in the configured {@link #list}
+     * @param {Object} config Optional config object
+     * @param {String} value The value to validate
+     * @return {Boolean} True if the value is not present in the list
+     */
+    exclusion: function(config, value) {
+        return config.list && Ext.Array.indexOf(config.list,value) == -1;
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.ResultSet
+ * @extends Object
+ * 
+ * <p>Simple wrapper class that represents a set of records returned by a Proxy.</p>
+ * 
+ * @constructor
+ * Creates the new ResultSet
+ */
+Ext.define('Ext.data.ResultSet', {
+    /**
+     * @cfg {Boolean} loaded
+     * True if the records have already been loaded. This is only meaningful when dealing with
+     * SQL-backed proxies
+     */
+    loaded: true,
+    
+    /**
+     * @cfg {Number} count
+     * The number of records in this ResultSet. Note that total may differ from this number
+     */
+    count: 0,
+    
+    /**
+     * @cfg {Number} total
+     * The total number of records reported by the data source. This ResultSet may form a subset of
+     * those records (see count)
+     */
+    total: 0,
+    
+    /**
+     * @cfg {Boolean} success
+     * True if the ResultSet loaded successfully, false if any errors were encountered
+     */
+    success: false,
+    
+    /**
+     * @cfg {Array} records The array of record instances. Required
+     */
+
+    constructor: function(config) {
+        Ext.apply(this, config);
+        
+        /**
+         * DEPRECATED - will be removed in Ext JS 5.0. This is just a copy of this.total - use that instead
+         * @property totalRecords
+         * @type Mixed
+         */
+        this.totalRecords = this.total;
+        
+        if (config.count === undefined) {
+            this.count = this.records.length;
+        }
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.writer.Writer
+ * @extends Object
+ * 
+ * <p>Base Writer class used by most subclasses of {@link Ext.data.proxy.Server}. This class is
+ * responsible for taking a set of {@link Ext.data.Operation} objects and a {@link Ext.data.Request}
+ * object and modifying that request based on the Operations.</p>
+ * 
+ * <p>For example a Ext.data.writer.Json would format the Operations and their {@link Ext.data.Model} 
+ * instances based on the config options passed to the JsonWriter's constructor.</p>
+ * 
+ * <p>Writers are not needed for any kind of local storage - whether via a
+ * {@link Ext.data.proxy.WebStorage Web Storage proxy} (see {@link Ext.data.proxy.LocalStorage localStorage}
+ * and {@link Ext.data.proxy.SessionStorage sessionStorage}) or just in memory via a
+ * {@link Ext.data.proxy.Memory MemoryProxy}.</p>
+ * 
+ * @constructor
+ * @param {Object} config Optional config object
+ */
+Ext.define('Ext.data.writer.Writer', {
+    alias: 'writer.base',
+    alternateClassName: ['Ext.data.DataWriter', 'Ext.data.Writer'],
+    
+    /**
+     * @cfg {Boolean} writeAllFields True to write all fields from the record to the server. If set to false it
+     * will only send the fields that were modified. Defaults to <tt>true</tt>. Note that any fields that have
+     * {@link Ext.data.Field#persist} set to false will still be ignored.
+     */
+    writeAllFields: true,
+    
+    /**
+     * @cfg {String} nameProperty This property is used to read the key for each value that will be sent to the server.
+     * For example:
+     * <pre><code>
+Ext.define('Person', {
+    extend: 'Ext.data.Model',
+    fields: [{
+        name: 'first',
+        mapping: 'firstName'
+    }, {
+        name: 'last',
+        mapping: 'lastName'
+    }, {
+        name: 'age'
+    }]
+});
+new Ext.data.writer.Writer({
+    writeAllFields: true,
+    nameProperty: 'mapping'
+});
+
+// This will be sent to the server
+{
+    firstName: 'first name value',
+    lastName: 'last name value',
+    age: 1
+}
+
+     * </code></pre>
+     * Defaults to <tt>name</tt>. If the value is not present, the field name will always be used.
+     */
+    nameProperty: 'name',
+
+    constructor: function(config) {
+        Ext.apply(this, config);
+    },
+
+    /**
+     * Prepares a Proxy's Ext.data.Request object
+     * @param {Ext.data.Request} request The request object
+     * @return {Ext.data.Request} The modified request object
+     */
+    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);
+    },
+
+    /**
+     * Formats the data for each record before sending it to the server. This
+     * method should be overridden to format the data in a way that differs from the default.
+     * @param {Object} record The record that we are writing to the server.
+     * @return {Object} An object literal of name/value keys to be written to the server.
+     * By default this method returns the data property on the record.
+     */
+    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 {
+            // Only write the changes
+            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) {
+                // always include the id for non phantoms
+                data[record.idProperty] = record.getId();
+            }
+        }
+        return data;
+    }
+});
+
+/**
+ * @class Ext.util.Floating
+ * A mixin to add floating capability to a Component
+ */
+Ext.define('Ext.util.Floating', {
+
+    uses: ['Ext.Layer', 'Ext.window.Window'],
+
+    /**
+     * @cfg {Boolean} focusOnToFront
+     * Specifies whether the floated component should be automatically {@link #focus focused} when it is
+     * {@link #toFront brought to the front}. Defaults to true.
+     */
+    focusOnToFront: true,
+
+    /**
+     * @cfg {String/Boolean} shadow Specifies whether the floating component should be given a shadow. Set to
+     * <tt>true</tt> to automatically create an {@link Ext.Shadow}, or a string indicating the
+     * shadow's display {@link Ext.Shadow#mode}. Set to <tt>false</tt> to disable the shadow.
+     * (Defaults to <tt>'sides'</tt>.)
+     */
+    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;
+
+        // Remove listeners from previous floatParent
+        if (me.floatParent) {
+            me.mun(me.floatParent, {
+                hide: me.onFloatParentHide,
+                show: me.onFloatParentShow,
+                scope: me
+            });
+        }
+
+        me.floatParent = floatParent;
+
+        // Floating Components as children of Containers must hide when their parent hides.
+        if (floatParent) {
+            me.mon(me.floatParent, {
+                hide: me.onFloatParentHide,
+                show: me.onFloatParentShow,
+                scope: me
+            });
+        }
+
+        // If a floating Component is configured to be constrained, but has no configured
+        // constrainTo setting, set its constrainTo to be it's ownerCt before rendering.
+        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();
+        }
+    },
+
+    /**
+     * @private
+     * <p>Finds the ancestor Container responsible for allocating zIndexes for the passed Component.</p>
+     * <p>That will be the outermost floating Container (a Container which has no ownerCt and has floating:true).</p>
+     * <p>If we have no ancestors, or we walk all the way up to the document body, there's no zIndexParent,
+     * and the global Ext.WindowManager will be used.</p>
+     */
+    getZIndexParent: function() {
+        var p = this.ownerCt,
+            c;
+
+        if (p) {
+            while (p) {
+                c = p;
+                p = p.ownerCt;
+            }
+            if (c.floating) {
+                return c;
+            }
+        }
+    },
+
+    // private
+    // z-index is managed by the zIndexManager and may be overwritten at any time.
+    // Returns the next z-index to be used.
+    // If this is a Container, then it will have rebased any managed floating Components,
+    // and so the next available z-index will be approximately 10000 above that.
+    setZIndex: function(index) {
+        var me = this;
+        this.el.setZIndex(index);
+
+        // Next item goes 10 above;
+        index += 10;
+
+        // When a Container with floating items has its z-index set, it rebases any floating items it is managing.
+        // The returned value is a round number approximately 10000 above the last z-index used.
+        if (me.floatingItems) {
+            index = Math.floor(me.floatingItems.setBase(index) / 100) * 100 + 10000;
+        }
+        return index;
+    },
+
+    /**
+     * <p>Moves this floating Component into a constrain region.</p>
+     * <p>By default, this Component is constrained to be within the container it was added to, or the element
+     * it was rendered to.</p>
+     * <p>An alternative constraint may be passed.</p>
+     * @param {Mixed} constrainTo Optional. The Element or {@link Ext.util.Region Region} into which this Component is to be constrained.
+     */
+    doConstrain: function(constrainTo) {
+        var me = this,
+            constrainEl,
+            vector,
+            xy;
+
+        if (me.constrain || me.constrainHeader) {
+            if (me.constrainHeader) {
+                constrainEl = me.header.el;
+            } else {
+                constrainEl = me.el;
+            }
+            vector = constrainEl.getConstrainVector(constrainTo || (me.floatParent && me.floatParent.getTargetEl()) || me.container);
+            if (vector) {
+                xy = me.getPosition();
+                xy[0] += vector[0];
+                xy[1] += vector[1];
+                me.setPosition(xy);
+            }
+        }
+    },
+
+    /**
+     * Aligns this floating Component to the specified element
+     * @param {Mixed} element The element or {@link Ext.Component} to align to. If passing a component, it must
+     * be a omponent instance. If a string id is passed, it will be used as an element id.
+     * @param {String} position (optional, defaults to "tl-bl?") The position to align to (see {@link Ext.core.Element#alignTo} for more details).
+     * @param {Array} offsets (optional) Offset the positioning by [x, y]
+     * @return {Component} this
+     */
+    alignTo: function(element, position, offsets) {
+        if (element.isComponent) {
+            element = element.getEl();
+        }
+        var xy = this.el.getAlignToXY(element, position, offsets);
+        this.setPagePosition(xy);
+        return this;
+    },
+
+    /**
+     * <p>Brings this floating Component to the front of any other visible, floating Components managed by the same {@link Ext.ZIndexManager ZIndexManager}</p>
+     * <p>If this Component is modal, inserts the modal mask just below this Component in the z-index stack.</p>
+     * @param {Boolean} preventFocus (optional) Specify <code>true</code> to prevent the Component from being focused.
+     * @return {Component} this
+     */
+    toFront: function(preventFocus) {
+        var me = this;
+
+        // Find the floating Component which provides the base for this Component's zIndexing.
+        // That must move to front to then be able to rebase its zIndex stack and move this to the front
+        if (me.zIndexParent) {
+            me.zIndexParent.toFront(true);
+        }
+        if (me.zIndexManager.bringToFront(me)) {
+            if (!Ext.isDefined(preventFocus)) {
+                preventFocus = !me.focusOnToFront;
+            }
+            if (!preventFocus) {
+                // Kick off a delayed focus request.
+                // If another floating Component is toFronted before the delay expires
+                // this will not receive focus.
+                me.focus(false, true);
+            }
+        }
+        return me;
+    },
+
+    /**
+     * <p>This method is called internally by {@link Ext.ZIndexManager} to signal that a floating
+     * Component has either been moved to the top of its zIndex stack, or pushed from the top of its zIndex stack.</p>
+     * <p>If a <i>Window</i> is superceded by another Window, deactivating it hides its shadow.</p>
+     * <p>This method also fires the {@link #activate} or {@link #deactivate} event depending on which action occurred.</p>
+     * @param {Boolean} active True to activate the Component, false to deactivate it (defaults to false)
+     * @param {Component} newActive The newly active Component which is taking over topmost zIndex position.
+     */
+    setActive: function(active, newActive) {
+        if (active) {
+            if ((this instanceof Ext.window.Window) && !this.maximized) {
+                this.el.enableShadow(true);
+            }
+            this.fireEvent('activate', this);
+        } else {
+            // Only the *Windows* in a zIndex stack share a shadow. All other types of floaters
+            // can keep their shadows all the time
+            if ((this instanceof Ext.window.Window) && (newActive instanceof Ext.window.Window)) {
+                this.el.disableShadow();
+            }
+            this.fireEvent('deactivate', this);
+        }
+    },
+
+    /**
+     * Sends this Component to the back of (lower z-index than) any other visible windows
+     * @return {Component} this
+     */
+    toBack: function() {
+        this.zIndexManager.sendToBack(this);
+        return this;
+    },
+
+    /**
+     * Center this Component in its container.
+     * @return {Component} this
+     */
+    center: function() {
+        var xy = this.el.getAlignToXY(this.container, 'c-c');
+        this.setPagePosition(xy);
+        return this;
+    },
+
+    // private
+    syncShadow : function(){
+        if (this.floating) {
+            this.el.sync(true);
+        }
+    },
+
+    // private
+    fitContainer: function() {
+        var parent = this.floatParent,
+            container = parent ? parent.getTargetEl() : this.container,
+            size = container.getViewSize(false);
+
+        this.setSize(size);
+    }
+});
+/**
+ * @class Ext.layout.container.AbstractContainer
+ * @extends Ext.layout.Layout
+ * Please refer to sub classes documentation
+ */
+
+Ext.define('Ext.layout.container.AbstractContainer', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.layout.Layout',
+
+    /* End Definitions */
+
+    type: 'container',
+
+    fixedLayout: true,
+
+    // @private
+    managedHeight: true,
+    // @private
+    managedWidth: true,
+
+    /**
+     * @cfg {Boolean} bindToOwnerCtComponent
+     * Flag to notify the ownerCt Component on afterLayout of a change
+     */
+    bindToOwnerCtComponent: false,
+
+    /**
+     * @cfg {Boolean} bindToOwnerCtContainer
+     * Flag to notify the ownerCt Container on afterLayout of a change
+     */
+    bindToOwnerCtContainer: false,
+
+    /**
+     * @cfg {String} itemCls
+     * <p>An optional extra CSS class that will be added to the container. This can be useful for adding
+     * customized styles to the container or any of its children using standard CSS rules. See
+     * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.</p>
+     * </p>
+     */
+
+    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);
+    },
+
+    /**
+    * Set the size of an item within the Container.  We should always use setCalculatedSize.
+    * @private
+    */
+    setItemSize: function(item, width, height) {
+        if (Ext.isObject(width)) {
+            height = width.height;
+            width = width.width;
+        }
+        item.setCalculatedSize(width, height, this.owner);
+    },
+
+    /**
+     * <p>Returns an array of child components either for a render phase (Performed in the beforeLayout method of the layout's
+     * base class), or the layout phase (onLayout).</p>
+     * @return {Array} of child components
+     */
+    getLayoutItems: function() {
+        return this.owner && this.owner.items && this.owner.items.items || [];
+    },
+
+    afterLayout: function() {
+        this.owner.afterLayout(this);
+    },
+    /**
+     * Returns the owner component's resize element.
+     * @return {Ext.core.Element}
+     */
+     getTarget: function() {
+         return this.owner.getTargetEl();
+     },
+    /**
+     * <p>Returns the element into which rendering must take place. Defaults to the owner Container's {@link Ext.AbstractComponent#targetEl}.</p>
+     * May be overridden in layout managers which implement an inner element.
+     * @return {Ext.core.Element}
+     */
+     getRenderTarget: function() {
+         return this.owner.getTargetEl();
+     }
+});
+
+/**
+ * @class Ext.ZIndexManager
+ * <p>A class that manages a group of {@link Ext.Component#floating} Components and provides z-order management,
+ * and Component activation behavior, including masking below the active (topmost) Component.</p>
+ * <p>{@link Ext.Component#floating Floating} Components which are rendered directly into the document (Such as {@link Ext.window.Window Window}s which are
+ * {@link Ext.Component#show show}n are managed by a {@link Ext.WindowManager global instance}.</p>
+ * <p>{@link Ext.Component#floating Floating} Components which are descendants of {@link Ext.Component#floating floating} <i>Containers</i>
+ * (For example a {Ext.view.BoundList BoundList} within an {@link Ext.window.Window Window}, or a {@link Ext.menu.Menu Menu}),
+ * are managed by a ZIndexManager owned by that floating Container. So ComboBox dropdowns within Windows will have managed z-indices
+ * guaranteed to be correct, relative to the Window.</p>
+ * @constructor
+ */
+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) {
+
+            // This is the ZIndexManager for an Ext.container.Container, base its zseed on the zIndex of the Container's element
+            if (container.isContainer) {
+                container.on('resize', me._onContainerResize, me);
+                me.zseed = Ext.Number.from(container.getEl().getStyle('zIndex'), me.getNextZSeed());
+                // The containing element we will be dealing with (eg masking) is the content target
+                me.targetEl = container.getTargetEl();
+                me.container = container;
+            }
+            // This is the ZIndexManager for a DOM element
+            else {
+                Ext.EventManager.onWindowResize(me._onContainerResize, me);
+                me.zseed = me.getNextZSeed();
+                me.targetEl = Ext.get(container);
+            }
+        }
+        // No container passed means we are the global WindowManager. Our target is the doc body.
+        // DOM must be ready to collect that ref.
+        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();
+    },
+
+    // private
+    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) {
+
+                // Setting the zIndex of a Component returns the topmost zIndex consumed by
+                // that Component.
+                // If it's just a plain floating Component such as a BoundList, then the
+                // return value is the passed value plus 10, ready for the next item.
+                // If a floating *Container* has its zIndex set, it re-orders its managed
+                // floating children, starting from that new base, and returns a value 10000 above
+                // the highest zIndex which it allocates.
+                zIndex = comp.setZIndex(zIndex);
+            }
+        }
+        this._activateLast();
+        return zIndex;
+    },
+
+    // private
+    _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);
+                }
+            }
+        }
+    },
+
+    // private
+    _activateLast: function(justHidden) {
+        var comp,
+            lastActivated = false,
+            i;
+
+        // Go down through the z-index stack.
+        // Activate the next visible one down.
+        // Keep going down to find the next visible modal one to shift the modal mask down under
+        for (i = this.zIndexStack.length-1; i >= 0; --i) {
+            comp = this.zIndexStack[i];
+            if (!comp.hidden) {
+                if (!lastActivated) {
+                    this._setActiveChild(comp);
+                    lastActivated = true;
+                }
+
+                // Move any modal mask down to just under the next modal floater down the stack
+                if (comp.modal) {
+                    this._showModalMask(comp.el.getStyle('zIndex') - 4);
+                    return;
+                }
+            }
+        }
+
+        // none to activate, so there must be no modal mask.
+        // And clear the currently active property
+        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));
+        }
+    },
+
+    /**
+     * <p>Registers a floating {@link Ext.Component} with this ZIndexManager. This should not
+     * need to be called under normal circumstances. Floating Components (such as Windows, BoundLists and Menus) are automatically registered
+     * with a {@link Ext.Component#zIndexManager zIndexManager} at render time.</p>
+     * <p>Where this may be useful is moving Windows between two ZIndexManagers. For example,
+     * to bring the Ext.MessageBox dialog under the same manager as the Desktop's
+     * ZIndexManager in the desktop sample app:</p><code><pre>
+MyDesktop.getDesktop().getManager().register(Ext.MessageBox);
+</pre></code>
+     * @param {Component} comp The Component to register.
+     */
+    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);
+    },
+
+    /**
+     * <p>Unregisters a {@link Ext.Component} from this ZIndexManager. This should not
+     * need to be called. Components are automatically unregistered upon destruction.
+     * See {@link #register}.</p>
+     * @param {Component} comp The Component to unregister.
+     */
+    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);
+
+            // Destruction requires that the topmost visible floater be activated. Same as hiding.
+            this._activateLast(comp);
+        }
+    },
+
+    /**
+     * Gets a registered Component by id.
+     * @param {String/Object} id The id of the Component or a {@link Ext.Component} instance
+     * @return {Ext.Component}
+     */
+    get : function(id) {
+        return typeof id == "object" ? id : this.list[id];
+    },
+
+   /**
+     * Brings the specified Component to the front of any other active Components in this ZIndexManager.
+     * @param {String/Object} comp The id of the Component or a {@link Ext.Component} instance
+     * @return {Boolean} True if the dialog was brought to the front, else false
+     * if it was already in front
+     */
+    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;
+    },
+
+    /**
+     * Sends the specified Component to the back of other active Components in this ZIndexManager.
+     * @param {String/Object} comp The id of the Component or a {@link Ext.Component} instance
+     * @return {Ext.Component} The Component
+     */
+    sendToBack : function(comp) {
+        comp = this.get(comp);
+        Ext.Array.remove(this.zIndexStack, comp);
+        this.zIndexStack.unshift(comp);
+        this.assignZIndices();
+        return comp;
+    },
+
+    /**
+     * Hides all Components managed by this ZIndexManager.
+     */
+    hideAll : function() {
+        for (var id in this.list) {
+            if (this.list[id].isComponent && this.list[id].isVisible()) {
+                this.list[id].hide();
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Temporarily hides all currently visible managed Components. This is for when
+     * dragging a Window which may manage a set of floating descendants in its ZIndexManager;
+     * they should all be hidden just for the duration of the drag.
+     */
+    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();
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Restores temporarily hidden managed Components to visibility.
+     */
+    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;
+    },
+
+    /**
+     * Gets the currently-active Component in this ZIndexManager.
+     * @return {Ext.Component} The active Component
+     */
+    getActive : function() {
+        return this.front;
+    },
+
+    /**
+     * Returns zero or more Components in this ZIndexManager using the custom search function passed to this method.
+     * The function should accept a single {@link Ext.Component} reference as its only argument and should
+     * return true if the Component matches the search criteria, otherwise it should return false.
+     * @param {Function} fn The search function
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component being tested.
+     * that gets passed to the function if not specified)
+     * @return {Array} An array of zero or more matching windows
+     */
+    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;
+    },
+
+    /**
+     * Executes the specified function once for every Component in this ZIndexManager, passing each
+     * Component as the only parameter. Returning false from the function will stop the iteration.
+     * @param {Function} fn The function to execute for each item
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Component in the iteration.
+     */
+    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;
+            }
+        }
+    },
+
+    /**
+     * Executes the specified function once for every Component in this ZIndexManager, passing each
+     * Component as the only parameter. Returning false from the function will stop the iteration.
+     * The components are passed to the function starting at the bottom and proceeding to the top.
+     * @param {Function} fn The function to execute for each item
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
+     * is executed. Defaults to the current Component in the iteration.
+     */
+    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;
+            }
+        }
+    },
+
+    /**
+     * Executes the specified function once for every Component in this ZIndexManager, passing each
+     * Component as the only parameter. Returning false from the function will stop the iteration.
+     * The components are passed to the function starting at the top and proceeding to the bottom.
+     * @param {Function} fn The function to execute for each item
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
+     * is executed. Defaults to the current Component in the iteration.
+     */
+    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() {
+    /**
+     * @class Ext.WindowManager
+     * @extends Ext.ZIndexManager
+     * <p>The default global floating Component group that is available automatically.</p>
+     * <p>This manages instances of floating Components which were rendered programatically without
+     * being added to a {@link Ext.container.Container Container}, and for floating Components which were added into non-floating Containers.</p>
+     * <p><i>Floating</i> Containers create their own instance of ZIndexManager, and floating Components added at any depth below
+     * there are managed by that ZIndexManager.</p>
+     * @singleton
+     */
+    Ext.WindowManager = Ext.WindowMgr = new this();
+});
+
+/**
+ * @class Ext.layout.container.boxOverflow.None
+ * @extends Object
+ * @private
+ * Base class for Box Layout overflow handlers. These specialized classes are invoked when a Box Layout
+ * (either an HBox or a VBox) has child items that are either too wide (for HBox) or too tall (for VBox)
+ * for its container.
+ */
+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,
+
+    /**
+     * @private
+     * Normalizes an item reference, string id or numerical index into a reference to the item
+     * @param {Ext.Component|String|Number} item The item reference, id or index
+     * @return {Ext.Component} The item
+     */
+    getItem: function(item) {
+        return this.layout.owner.getComponent(item);
+    }
+});
+/**
+ * @class Ext.util.KeyMap
+ * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
+ * The constructor accepts the same config object as defined by {@link #addBinding}.
+ * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
+ * combination it will call the function with this signature (if the match is a multi-key
+ * combination the callback will still be called only once): (String key, Ext.EventObject e)
+ * A KeyMap can also handle a string representation of keys.<br />
+ * Usage:
+ <pre><code>
+// map one key by key code
+var map = new Ext.util.KeyMap("my-element", {
+    key: 13, // or Ext.EventObject.ENTER
+    fn: myHandler,
+    scope: myObject
+});
+
+// map multiple keys to one action by string
+var map = new Ext.util.KeyMap("my-element", {
+    key: "a\r\n\t",
+    fn: myHandler,
+    scope: myObject
+});
+
+// map multiple keys to multiple actions by strings and array of codes
+var map = new Ext.util.KeyMap("my-element", [
+    {
+        key: [10,13],
+        fn: function(){ alert("Return was pressed"); }
+    }, {
+        key: "abc",
+        fn: function(){ alert('a, b or c was pressed'); }
+    }, {
+        key: "\t",
+        ctrl:true,
+        shift:true,
+        fn: function(){ alert('Control + shift + tab was pressed.'); }
+    }
+]);
+</code></pre>
+ * <b>Note: A KeyMap starts enabled</b>
+ * @constructor
+ * @param {Mixed} el The element to bind to
+ * @param {Object} binding The binding (see {@link #addBinding})
+ * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
+ */
+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',
+
+    /**
+     * Add a new binding to this KeyMap. The following config object properties are supported:
+     * <pre>
+Property            Type             Description
+----------          ---------------  ----------------------------------------------------------------------
+key                 String/Array     A single keycode or an array of keycodes to handle
+shift               Boolean          True to handle key only when shift is pressed, False to handle the key only when shift is not pressed (defaults to undefined)
+ctrl                Boolean          True to handle key only when ctrl is pressed, False to handle the key only when ctrl is not pressed (defaults to undefined)
+alt                 Boolean          True to handle key only when alt is pressed, False to handle the key only when alt is not pressed (defaults to undefined)
+handler             Function         The function to call when KeyMap finds the expected key combination
+fn                  Function         Alias of handler (for backwards-compatibility)
+scope               Object           The scope of the callback function
+defaultEventAction  String           A default action to apply to the event. Possible values are: stopEvent, stopPropagation, preventDefault. If no value is set no action is performed. 
+</pre>
+     *
+     * Usage:
+     * <pre><code>
+// Create a KeyMap
+var map = new Ext.util.KeyMap(document, {
+    key: Ext.EventObject.ENTER,
+    fn: handleKey,
+    scope: this
+});
+
+//Add a new binding to the existing KeyMap later
+map.addBinding({
+    key: 'abc',
+    shift: true,
+    fn: handleKey,
+    scope: this
+});
+</code></pre>
+     * @param {Object/Array} binding A single KeyMap config or an array of configs
+     */
+    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));
+    },
+    
+    /**
+     * Process any keydown events on the element
+     * @private
+     * @param {Ext.EventObject} event
+     */
+    handleKeyDown: function(event) {
+        if (this.enabled) { //just in case
+            var bindings = this.bindings,
+                i = 0,
+                len = bindings.length;
+                
+            event = this.processEvent(event);
+            for(; i < len; ++i){
+                this.processBinding(bindings[i], event);
+            }
+        }
+    },
+    
+    /**
+     * Ugly hack to allow this class to be tested. Currently WebKit gives
+     * no way to raise a key event properly with both
+     * a) A keycode
+     * b) The alt/ctrl/shift modifiers
+     * So we have to simulate them here. Yuk! 
+     * This is a stub method intended to be overridden by tests.
+     * More info: https://bugs.webkit.org/show_bug.cgi?id=16735
+     * @private
+     */
+    processEvent: function(event){
+        return event;
+    },
+    
+    /**
+     * Process a particular binding and fire the handler if necessary.
+     * @private
+     * @param {Object} binding The binding information
+     * @param {Ext.EventObject} 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;
+                }
+            }
+        }
+    },
+    
+    /**
+     * Check if the modifiers on the event match those on the binding
+     * @private
+     * @param {Object} binding
+     * @param {Ext.EventObject} event
+     * @return {Boolean} True if the event matches the binding
+     */
+    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;
+    },
+
+    /**
+     * Shorthand for adding a single key listener
+     * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
+     * following options:
+     * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
+     * @param {Function} fn The function to call
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
+     */
+    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
+        });
+    },
+
+    /**
+     * Returns true if this KeyMap is enabled
+     * @return {Boolean}
+     */
+    isEnabled : function(){
+        return this.enabled;
+    },
+
+    /**
+     * Enables this KeyMap
+     */
+    enable: function(){
+        if(!this.enabled){
+            this.el.on(this.eventName, this.handleKeyDown, this);
+            this.enabled = true;
+        }
+    },
+
+    /**
+     * Disable this KeyMap
+     */
+    disable: function(){
+        if(this.enabled){
+            this.el.removeListener(this.eventName, this.handleKeyDown, this);
+            this.enabled = false;
+        }
+    },
+
+    /**
+     * Convenience function for setting disabled/enabled by boolean.
+     * @param {Boolean} disabled
+     */
+    setDisabled : function(disabled){
+        if (disabled) {
+            this.disable();
+        } else {
+            this.enable();
+        }
+    },
+    
+    /**
+     * Destroys the KeyMap instance and removes all handlers.
+     * @param {Boolean} removeEl True to also remove the attached element
+     */
+    destroy: function(removeEl){
+        var me = this;
+        
+        me.bindings = [];
+        me.disable();
+        if (removeEl === true) {
+            me.el.remove();
+        }
+        delete me.el;
+    }
+});
+/**
+ * @class Ext.util.ClickRepeater
+ * @extends Ext.util.Observable
+ *
+ * A wrapper class which can be applied to any element. Fires a "click" event while the
+ * mouse is pressed. The interval between firings may be specified in the config but
+ * defaults to 20 milliseconds.
+ *
+ * Optionally, a CSS class may be applied to the element during the time it is pressed.
+ *
+ * @constructor
+ * @param {Mixed} el The element to listen on
+ * @param {Object} config
+ */
+
+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(
+        /**
+         * @event mousedown
+         * Fires when the mouse button is depressed.
+         * @param {Ext.util.ClickRepeater} this
+         * @param {Ext.EventObject} e
+         */
+        "mousedown",
+        /**
+         * @event click
+         * Fires on a specified interval during the time the element is pressed.
+         * @param {Ext.util.ClickRepeater} this
+         * @param {Ext.EventObject} e
+         */
+        "click",
+        /**
+         * @event mouseup
+         * Fires when the mouse key is released.
+         * @param {Ext.util.ClickRepeater} this
+         * @param {Ext.EventObject} e
+         */
+        "mouseup"
+        );
+
+        if(!this.disabled){
+            this.disabled = true;
+            this.enable();
+        }
+
+        // allow inline handler
+        if(this.handler){
+            this.on("click", this.handler,  this.scope || this);
+        }
+
+        this.callParent();
+    },
+
+    /**
+     * @cfg {Mixed} el The element to act as a button.
+     */
+
+    /**
+     * @cfg {String} pressedCls A CSS class name to be applied to the element while pressed.
+     */
+
+    /**
+     * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
+     * "interval" and "delay" are ignored.
+     */
+
+    /**
+     * @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
+     */
+    interval : 20,
+
+    /**
+     * @cfg {Number} delay The initial delay before the repeating event begins firing.
+     * Similar to an autorepeat key delay.
+     */
+    delay: 250,
+
+    /**
+     * @cfg {Boolean} preventDefault True to prevent the default click event
+     */
+    preventDefault : true,
+    /**
+     * @cfg {Boolean} stopDefault True to stop the default click event
+     */
+    stopDefault : false,
+
+    timer : 0,
+
+    /**
+     * Enables the repeater and allows events to fire.
+     */
+    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;
+    },
+
+    /**
+     * Disables the repeater and stops events from firing.
+     */
+    disable: function(/* private */ 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;
+    },
+
+    /**
+     * Convenience function for setting disabled/enabled by boolean.
+     * @param {Boolean} disabled
+     */
+    setDisabled: function(disabled){
+        this[disabled ? 'disable' : 'enable']();
+    },
+
+    eventOptions: function(e){
+        if(this.preventDefault){
+            e.preventDefault();
+        }
+        if(this.stopDefault){
+            e.stopEvent();
+        }
+    },
+
+    // private
+    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);
+    },
+
+    // private
+    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);
+
+        // Do not honor delay or interval if acceleration wanted.
+        if (this.accelerate) {
+            this.delay = 400;
+        }
+
+        // Re-wrap the event object in a non-shared object, so it doesn't lose its context if
+        // the global shared EventObject gets a new Event put into it before the timer fires.
+        e = new Ext.EventObjectImpl(e);
+
+        this.timer =  Ext.defer(this.click, this.delay || this.interval, this, [e]);
+    },
+
+    // private
+    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;
+    },
+
+    // private
+    handleMouseOut : function(){
+        clearTimeout(this.timer);
+        if(this.pressedCls){
+            this.el.removeCls(this.pressedCls);
+        }
+        this.el.on("mouseover", this.handleMouseReturn, this);
+    },
+
+    // private
+    handleMouseReturn : function(){
+        this.el.un("mouseover", this.handleMouseReturn, this);
+        if(this.pressedCls){
+            this.el.addCls(this.pressedCls);
+        }
+        this.click();
+    },
+
+    // private
+    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);
+    }
+});
+
+/**
+ * Component layout for buttons
+ * @class Ext.layout.component.Button
+ * @extends Ext.layout.component.Component
+ * @private
+ */
+Ext.define('Ext.layout.component.Button', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.button'],
+
+    extend: 'Ext.layout.component.Component',
+
+    /* End Definitions */
+
+    type: 'button',
+
+    cellClsRE: /-btn-(tl|br)\b/,
+    htmlRE: /<.*>/,
+
+    beforeLayout: function() {
+        return this.callParent(arguments) || this.lastText !== this.owner.text;
+    },
+
+    /**
+     * Set the dimensions of the inner &lt;button&gt; element to match the
+     * component dimensions.
+     */
+    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)) {
+            // In IE7 strict mode button elements with width:auto get strange extra side margins within
+            // the wrapping table cell, but they go away if the width is explicitly set. So we measure
+            // the size of the text and set the width to match.
+            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 {
+                // Remove any previous fixed widths
+                ownerEl.setWidth(null);
+                btnEl.setWidth(null);
+                btnInnerEl.setWidth(null);
+            }
+
+            // Handle maxWidth/minWidth config
+            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');
+        }
+
+        // Button text may contain markup that would force it to wrap to more than one line (e.g. 'Button<br>Label').
+        // When this happens, we cannot use the line-height set above for vertical centering; we instead reset the
+        // line-height to normal, measure the rendered text height, and add padding-top to center the text block
+        // vertically within the button's height. This is more expensive than the basic line-height approach so
+        // we only do it if the text contains markup.
+        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, {
+                // Width adjustment must take into account the arrow area. The btnWrap is the <em> which has padding to accommodate the arrow.
+                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();
+    }
+});
+/**
+ * @class Ext.util.TextMetrics
+ * <p>
+ * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
+ * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
+ * should not contain any HTML, otherwise it may not be measured correctly.</p> 
+ * <p>The measurement works by copying the relevant CSS styles that can affect the font related display, 
+ * then checking the size of an element that is auto-sized. Note that if the text is multi-lined, you must 
+ * provide a <b>fixed width</b> when doing the measurement.</p>
+ * 
+ * <p>
+ * If multiple measurements are being done on the same element, you create a new instance to initialize 
+ * to avoid the overhead of copying the styles to the element repeatedly.
+ * </p>
+ */
+Ext.define('Ext.util.TextMetrics', {
+    statics: {
+        shared: null,
+        /**
+         * Measures the size of the specified text
+         * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
+         * that can affect the size of the rendered text
+         * @param {String} text The text to measure
+         * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
+         * in order to accurately measure the text height
+         * @return {Object} An object containing the text's size {width: (width), height: (height)}
+         */
+        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 the TextMetrics instance created by {@link #measure}.
+          */
+         destroy: function(){
+             var me = this;
+             Ext.destroy(me.shared);
+             me.shared = null;
+         }
+    },
+    
+    /**
+     * @constructor
+     * @param {Mixed} bindTo The element to bind to.
+     * @param {Number} fixedWidth A fixed width to apply to the measuring element.
+     */
+    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);
+        }
+    },
+    
+    /**
+     * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
+     * Returns the size of the specified text based on the internal element's style and width properties
+     * @param {String} text The text to measure
+     * @return {Object} An object containing the text's size {width: (width), height: (height)}
+     */
+    getSize: function(text){
+        var measure = this.measure,
+            size;
+        
+        measure.update(text);
+        size = measure.getSize();
+        measure.update('');
+        return size;
+    },
+    
+    /**
+     * Binds this TextMetrics instance to a new element
+     * @param {Mixed} el The element
+     */
+    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')
+        );
+    },
+    
+    /**
+     * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
+     * to set a fixed width in order to accurately measure the text height.
+     * @param {Number} width The width to set on the element
+     */
+     setFixedWidth : function(width){
+         this.measure.setWidth(width);
+     },
+     
+     /**
+      * Returns the measured width of the specified text
+      * @param {String} text The text to measure
+      * @return {Number} width The width in pixels
+      */
+     getWidth : function(text){
+         this.measure.dom.style.width = 'auto';
+         return this.getSize(text).width;
+     },
+     
+     /**
+      * Returns the measured height of the specified text
+      * @param {String} text The text to measure
+      * @return {Number} height The height in pixels
+      */
+     getHeight : function(text){
+         return this.getSize(text).height;
+     },
+     
+     /**
+      * Destroy this instance
+      */
+     destroy: function(){
+         var me = this;
+         me.measure.remove();
+         delete me.el;
+         delete me.measure;
+     }
+}, function(){
+    Ext.core.Element.addMethods({
+        /**
+         * Returns the width in pixels of the passed text, or the width of the text in this Element.
+         * @param {String} text The text to measure. Defaults to the innerHTML of the element.
+         * @param {Number} min (Optional) The minumum value to return.
+         * @param {Number} max (Optional) The maximum value to return.
+         * @return {Number} The text width in pixels.
+         * @member Ext.core.Element getTextWidth
+         */
+        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);
+        }
+    });
+});
+
+/**
+ * @class Ext.layout.container.boxOverflow.Scroller
+ * @extends Ext.layout.container.boxOverflow.None
+ * @private
+ */
+Ext.define('Ext.layout.container.boxOverflow.Scroller', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.layout.container.boxOverflow.None',
+    requires: ['Ext.util.ClickRepeater', 'Ext.core.Element'],
+    alternateClassName: 'Ext.layout.boxOverflow.Scroller',
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+    
+    /* End Definitions */
+
+    /**
+     * @cfg {Boolean} animateScroll
+     * True to animate the scrolling of items within the layout (defaults to true, ignored if enableScroll is false)
+     */
+    animateScroll: false,
+
+    /**
+     * @cfg {Number} scrollIncrement
+     * The number of pixels to scroll by on scroller click (defaults to 24)
+     */
+    scrollIncrement: 20,
+
+    /**
+     * @cfg {Number} wheelIncrement
+     * The number of pixels to increment on mouse wheel scrolling (defaults to <tt>3</tt>).
+     */
+    wheelIncrement: 10,
+
+    /**
+     * @cfg {Number} scrollRepeatInterval
+     * Number of milliseconds between each scroll while a scroller button is held down (defaults to 20)
+     */
+    scrollRepeatInterval: 60,
+
+    /**
+     * @cfg {Number} scrollDuration
+     * Number of milliseconds that each scroll animation lasts (defaults to 400)
+     */
+    scrollDuration: 400,
+
+    /**
+     * @cfg {String} beforeCtCls
+     * CSS class added to the beforeCt element. This is the element that holds any special items such as scrollers,
+     * which must always be present at the leftmost edge of the Container
+     */
+
+    /**
+     * @cfg {String} afterCtCls
+     * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
+     * which must always be present at the rightmost edge of the Container
+     */
+
+    /**
+     * @cfg {String} scrollerCls
+     * CSS class added to both scroller elements if enableScroll is used
+     */
+    scrollerCls: Ext.baseCSSPrefix + 'box-scroller',
+
+    /**
+     * @cfg {String} beforeScrollerCls
+     * CSS class added to the left scroller element if enableScroll is used
+     */
+
+    /**
+     * @cfg {String} afterScrollerCls
+     * CSS class added to the right scroller element if enableScroll is used
+     */
+    
+    constructor: function(layout, config) {
+        this.layout = layout;
+        Ext.apply(this, config || {});
+        
+        this.addEvents(
+            /**
+             * @event scroll
+             * @param {Ext.layout.container.boxOverflow.Scroller} scroller The layout scroller
+             * @param {Number} newPosition The new position of the scroller
+             * @param {Boolean/Object} animate If animating or not. If true, it will be a animation configuration, else it will be false
+             */
+            '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 };
+    },
+
+    /**
+     * @private
+     * Creates the beforeCt and afterCt elements if they have not already been created
+     */
+    createInnerElements: function() {
+        var me = this,
+            target = me.layout.getRenderTarget();
+
+        //normal items will be rendered to the innerCt. beforeCt and afterCt allow for fixed positioning of
+        //special items such as scrollers or dropdown menu triggers
+        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();
+        }
+    },
+
+    /**
+     * @private
+     * Sets up an listener to scroll on the layout's innerCt mousewheel event
+     */
+    createWheelListener: function() {
+        this.layout.innerCt.on({
+            scope     : this,
+            mousewheel: function(e) {
+                e.stopEvent();
+
+                this.scrollBy(e.getWheelDelta() * this.wheelIncrement * -1, false);
+            }
+        });
+    },
+
+    /**
+     * @private
+     */
+    clearOverflow: function() {
+        this.hideScrollers();
+    },
+
+    /**
+     * @private
+     * Shows the scroller elements in the beforeCt and afterCt. Creates the scrollers first if they are not already
+     * present. 
+     */
+    showScrollers: function() {
+        this.createScrollers();
+        this.beforeScroller.show();
+        this.afterScroller.show();
+        this.updateScrollButtons();
+        
+        this.layout.owner.addClsWithUI('scroller');
+    },
+
+    /**
+     * @private
+     * Hides the scroller elements in the beforeCt and afterCt
+     */
+    hideScrollers: function() {
+        if (this.beforeScroller != undefined) {
+            this.beforeScroller.hide();
+            this.afterScroller.hide();
+            
+            this.layout.owner.removeClsWithUI('scroller');
+        }
+    },
+
+    /**
+     * @private
+     * Creates the clickable scroller elements and places them into the beforeCt and afterCt
+     */
+    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
+            });
+
+            /**
+             * @property beforeScroller
+             * @type Ext.core.Element
+             * The left scroller element. Only created when needed.
+             */
+            this.beforeScroller = before;
+
+            /**
+             * @property afterScroller
+             * @type Ext.core.Element
+             * The left scroller element. Only created when needed.
+             */
+            this.afterScroller = after;
+        }
+    },
+
+    /**
+     * @private
+     */
+    destroy: function() {
+        Ext.destroy(this.beforeRepeater, this.afterRepeater, this.beforeScroller, this.afterScroller, this.beforeCt, this.afterCt);
+    },
+
+    /**
+     * @private
+     * Scrolls left or right by the number of pixels specified
+     * @param {Number} delta Number of pixels to scroll to the right by. Use a negative number to scroll left
+     */
+    scrollBy: function(delta, animate) {
+        this.scrollTo(this.getScrollPosition() + delta, animate);
+    },
+
+    /**
+     * @private
+     * @return {Object} Object passed to scrollTo when scrolling
+     */
+    getScrollAnim: function() {
+        return {
+            duration: this.scrollDuration, 
+            callback: this.updateScrollButtons, 
+            scope   : this
+        };
+    },
+
+    /**
+     * @private
+     * Enables or disables each scroller button based on the current scroll position
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * Returns true if the innerCt scroll is already at its left-most point
+     * @return {Boolean} True if already at furthest left point
+     */
+    atExtremeBefore: function() {
+        return this.getScrollPosition() === 0;
+    },
+
+    /**
+     * @private
+     * Scrolls to the left by the configured amount
+     */
+    scrollLeft: function() {
+        this.scrollBy(-this.scrollIncrement, false);
+    },
+
+    /**
+     * @private
+     * Scrolls to the right by the configured amount
+     */
+    scrollRight: function() {
+        this.scrollBy(this.scrollIncrement, false);
+    },
+
+    /**
+     * Returns the current scroll position of the innerCt element
+     * @return {Number} The current scroll position
+     */
+    getScrollPosition: function(){
+        var layout = this.layout;
+        return parseInt(layout.innerCt.dom['scroll' + layout.parallelBeforeCap], 10) || 0;
+    },
+
+    /**
+     * @private
+     * Returns the maximum value we can scrollTo
+     * @return {Number} The max scroll value
+     */
+    getMaxScrollPosition: function() {
+        var layout = this.layout;
+        return layout.innerCt.dom['scroll' + layout.parallelPrefixCap] - this.layout.innerCt['get' + layout.parallelPrefixCap]();
+    },
+
+    /**
+     * @private
+     * Returns true if the innerCt scroll is already at its right-most point
+     * @return {Boolean} True if already at furthest right point
+     */
+    atExtremeAfter: function() {
+        return this.getScrollPosition() >= this.getMaxScrollPosition();
+    },
+
+    /**
+     * @private
+     * Scrolls to the given position. Performs bounds checking.
+     * @param {Number} position The position to scroll to. This is constrained.
+     * @param {Boolean} animate True to animate. If undefined, falls back to value of this.animateScroll
+     */
+    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);
+        }
+    },
+
+    /**
+     * Scrolls to the given component.
+     * @param {String|Number|Ext.Component} item The item to scroll to. Can be a numerical index, component id 
+     * or a reference to the component itself.
+     * @param {Boolean} animate True to animate the scrolling
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * @private
+     * For a given item in the container, return an object with information on whether the item is visible
+     * with the current innerCt scroll value.
+     * @param {Ext.Component} item The item
+     * @return {Object} Values for fullyVisible, hiddenStart and hiddenEnd
+     */
+    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
+        };
+    }
+});
+/**
+ * @class Ext.util.Offset
+ * @ignore
+ */
+Ext.define('Ext.util.Offset', {
+
+    /* Begin Definitions */
+
+    statics: {
+        fromObject: function(obj) {
+            return new this(obj.x, obj.y);
+        }
+    },
+
+    /* End Definitions */
+
+    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) {
+        //<debug>
+        if(!(offset instanceof this.statics())) {
+            Ext.Error.raise('Offset must be an instance of Ext.util.Offset');
+        }
+        //</debug>
+
+        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;
+    }
+});
+
+/**
+ * @class Ext.util.KeyNav
+ * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
+ * navigation keys to function calls that will get called when the keys are pressed, providing an easy
+ * way to implement custom navigation schemes for any UI component.</p>
+ * <p>The following are all of the possible keys that can be implemented: enter, space, left, right, up, down, tab, esc,
+ * pageUp, pageDown, del, backspace, home, end.  Usage:</p>
+ <pre><code>
+var nav = new Ext.util.KeyNav("my-element", {
+    "left" : function(e){
+        this.moveLeft(e.ctrlKey);
+    },
+    "right" : function(e){
+        this.moveRight(e.ctrlKey);
+    },
+    "enter" : function(e){
+        this.save();
+    },
+    scope : this
+});
+</code></pre>
+ * @constructor
+ * @param {Mixed} el The element to bind to
+ * @param {Object} config The config
+ */
+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 || {});
+    },
+    
+    /**
+     * Sets up a configuration for the KeyNav.
+     * @private
+     * @param {Mixed} el The element to bind to
+     * @param {Object}A configuration object as specified in the constructor.
+     */
+    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();
+        }
+    },
+    
+    /**
+     * Method for filtering out the map argument
+     * @private
+     * @param {Ext.util.KeyMap} map
+     * @param {Ext.EventObject} event
+     * @param {Object} options Contains the handler to call
+     */
+    handleEvent: function(map, event, handler){
+        return handler.call(this, event);
+    },
+    
+    /**
+     * @cfg {Boolean} disabled
+     * True to disable this KeyNav instance (defaults to false)
+     */
+    disabled: false,
+    
+    /**
+     * @cfg {String} defaultEventAction
+     * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key.  Valid values are
+     * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
+     * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
+     */
+    defaultEventAction: "stopEvent",
+    
+    /**
+     * @cfg {Boolean} forceKeyDown
+     * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
+     * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
+     * handle keydown instead of keypress.
+     */
+    forceKeyDown: false,
+    
+    /**
+     * Destroy this KeyNav (this is the same as calling disable).
+     * @param {Boolean} removeEl True to remove the element associated with this KeyNav.
+     */
+    destroy: function(removeEl){
+        this.map.destroy(removeEl);
+        delete this.map;
+    },
+
+    /**
+     * Enable this KeyNav
+     */
+    enable: function() {
+        this.map.enable();
+        this.disabled = false;
+    },
+
+    /**
+     * Disable this KeyNav
+     */
+    disable: function() {
+        this.map.disable();
+        this.disabled = true;
+    },
+    
+    /**
+     * Convenience function for setting disabled/enabled by boolean.
+     * @param {Boolean} disabled
+     */
+    setDisabled : function(disabled){
+        this.map.setDisabled(disabled);
+        this.disabled = disabled;
+    },
+    
+    /**
+     * Determines the event to bind to listen for keys. Depends on the {@link #forceKeyDown} setting,
+     * as well as the useKeyDown option on the EventManager.
+     * @return {String} The type of event to listen for.
+     */
+    getKeyEvent: function(forceKeyDown){
+        return (forceKeyDown || Ext.EventManager.useKeyDown) ? 'keydown' : 'keypress';
+    }
+});
+
+/**
+ * @class Ext.fx.Queue
+ * Animation Queue mixin to handle chaining and queueing by target.
+ * @private
+ */
+
+Ext.define('Ext.fx.Queue', {
+
+    requires: ['Ext.util.HashMap'],
+
+    constructor: function() {
+        this.targets = Ext.create('Ext.util.HashMap');
+        this.fxQueue = {};
+    },
+
+    // @private
+    getFxDefaults: function(targetId) {
+        var target = this.targets.get(targetId);
+        if (target) {
+            return target.fxDefaults;
+        }
+        return {};
+    },
+
+    // @private
+    setFxDefaults: function(targetId, obj) {
+        var target = this.targets.get(targetId);
+        if (target) {
+            target.fxDefaults = Ext.apply(target.fxDefaults || {}, obj);
+        }
+    },
+
+    // @private
+    stopAnimation: function(targetId) {
+        var me = this,
+            queue = me.getFxQueue(targetId),
+            ln = queue.length;
+        while (ln) {
+            queue[ln - 1].end();
+            ln--;
+        }
+    },
+
+    /**
+     * @private
+     * Returns current animation object if the element has any effects actively running or queued, else returns false.
+     */
+    getActiveAnimation: function(targetId) {
+        var queue = this.getFxQueue(targetId);
+        return (queue && !!queue.length) ? queue[0] : false;
+    },
+
+    // @private
+    hasFxBlock: function(targetId) {
+        var queue = this.getFxQueue(targetId);
+        return queue && queue[0] && queue[0].block;
+    },
+
+    // @private get fx queue for passed target, create if needed.
+    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] = [];
+            // GarbageCollector will need to clean up Elements since they aren't currently observable
+            if (target.type != 'element') {
+                target.target.on('destroy', function() {
+                    me.fxQueue[targetId] = [];
+                });
+            }
+        }
+        return me.fxQueue[targetId];
+    },
+
+    // @private
+    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);
+    }
+});
+/**
+ * @class Ext.fx.target.Target
+
+This class specifies a generic target for an animation. It provides a wrapper around a
+series of different types of objects to allow for a generic animation API.
+A target can be a single object or a Composite object containing other objects that are 
+to be animated. This class and it's subclasses are generally not created directly, the 
+underlying animation will create the appropriate Ext.fx.target.Target object by passing 
+the instance to be animated.
+
+The following types of objects can be animated:
+- {@link #Ext.fx.target.Component Components}
+- {@link #Ext.fx.target.Element Elements}
+- {@link #Ext.fx.target.Sprite Sprites}
+
+ * @markdown
+ * @abstract
+ * @constructor
+ * @param {Mixed} target The object to be animated
+ */
+
+Ext.define('Ext.fx.target.Target', {
+
+    isAnimTarget: true,
+
+    constructor: function(target) {
+        this.target = target;
+        this.id = this.getId();
+    },
+    
+    getId: function() {
+        return this.target.id;
+    }
+});
+
+/**
+ * @class Ext.fx.target.Sprite
+ * @extends Ext.fx.target.Target
+
+This class represents a animation target for a {@link Ext.draw.Sprite}. In general this class will not be
+created directly, the {@link Ext.draw.Sprite} will be passed to the animation and
+and the appropriate target will be created.
+
+ * @markdown
+ */
+
+Ext.define('Ext.fx.target.Sprite', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.fx.target.Target',
+
+    /* End Definitions */
+
+    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();
+    }
+});
+
+/**
+ * @class Ext.fx.target.CompositeSprite
+ * @extends Ext.fx.target.Sprite
+
+This class represents a animation target for a {@link Ext.draw.CompositeSprite}. It allows
+each {@link Ext.draw.Sprite} in the group to be animated as a whole. In general this class will not be
+created directly, the {@link Ext.draw.CompositeSprite} will be passed to the animation and
+and the appropriate target will be created.
+
+ * @markdown
+ */
+
+Ext.define('Ext.fx.target.CompositeSprite', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.fx.target.Sprite',
+
+    /* End Definitions */
+
+    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;
+    }
+});
+
+/**
+ * @class Ext.fx.target.Component
+ * @extends Ext.fx.target.Target
+ * 
+ * This class represents a animation target for a {@link Ext.Component}. In general this class will not be
+ * created directly, the {@link Ext.Component} will be passed to the animation and
+ * and the appropriate target will be created.
+ */
+Ext.define('Ext.fx.target.Component', {
+
+    /* Begin Definitions */
+   
+    extend: 'Ext.fx.target.Target',
+    
+    /* End Definitions */
+
+    type: 'component',
+
+    // Methods to call to retrieve unspecified "from" values from a target 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'
+    },
+
+    // Read the named attribute from the target Component. Use the defined getter for the attribute
+    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];
+                    // We REALLY want a single function call, so push these down to merge them: eg
+                    // meth.setPagePosition.target = <targetComponent>
+                    // meth.setPagePosition['x'] = 100
+                    // meth.setPagePosition['y'] = 100
+                    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;
+                    // Dimensions not being animated MUST NOT be autosized. They must remain at current value.
+                    w = (o.width === undefined) ? o.target.getWidth() : parseInt(o.width, 10);
+                    h = (o.height === undefined) ? o.target.getHeight() : parseInt(o.height, 10);
+
+                    // Only set the size of the Component on the last frame, or if the animation was
+                    // configured with dynamic: true.
+                    // In other cases, we just set the target element size.
+                    // This will result in either clipping if animating a reduction in size, or the revealing of
+                    // the inner elements of the Component if animating an increase in size.
+                    // Component's animate function initially resizes to the larger size before resizing the
+                    // outer element to clip the contents.
+                    if (isLastFrame || me.dynamic) {
+                        o.target.componentLayout.childrenChanged = true;
+
+                        // Flag if we are being called by an animating layout: use setCalculatedSize
+                        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);
+                }
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.fx.CubicBezier
+ * @ignore
+ */
+Ext.define('Ext.fx.CubicBezier', {
+
+    /* Begin Definitions */
+
+    singleton: true,
+
+    /* End Definitions */
+
+    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;
+    }
+});
+/**
+ * @class Ext.draw.Color
+ * @extends Object
+ *
+ * Represents an RGB color and provides helper functions get
+ * color components in HSL color space.
+ */
+Ext.define('Ext.draw.Color', {
+
+    /* Begin Definitions */
+
+    /* End Definitions */
+
+    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*/,
+
+    /**
+     * @cfg {Number} lightnessFactor
+     *
+     * The default factor to compute the lighter or darker color. Defaults to 0.2.
+     */
+    lightnessFactor: 0.2,
+
+    /**
+     * @constructor
+     * @param {Number} red Red component (0..255)
+     * @param {Number} green Green component (0..255)
+     * @param {Number} blue Blue component (0..255)
+     */
+    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);
+    },
+
+    /**
+     * Get the red component of the color, in the range 0..255.
+     * @return {Number}
+     */
+    getRed: function() {
+        return this.r;
+    },
+
+    /**
+     * Get the green component of the color, in the range 0..255.
+     * @return {Number}
+     */
+    getGreen: function() {
+        return this.g;
+    },
+
+    /**
+     * Get the blue component of the color, in the range 0..255.
+     * @return {Number}
+     */
+    getBlue: function() {
+        return this.b;
+    },
+
+    /**
+     * Get the RGB values.
+     * @return {Array}
+     */
+    getRGB: function() {
+        var me = this;
+        return [me.r, me.g, me.b];
+    },
+
+    /**
+     * Get the equivalent HSL components of the color.
+     * @return {Array}
+     */
+    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);
+
+        // min==max means achromatic (hue is undefined)
+        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];
+    },
+
+    /**
+     * Return a new color that is lighter than this color.
+     * @param {Number} factor Lighter factor (0..1), default to 0.2
+     * @return Ext.draw.Color
+     */
+    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]);
+    },
+
+    /**
+     * Return a new color that is darker than this color.
+     * @param {Number} factor Darker factor (0..1), default to 0.2
+     * @return Ext.draw.Color
+     */
+    getDarker: function(factor) {
+        factor = factor || this.lightnessFactor;
+        return this.getLighter(-factor);
+    },
+
+    /**
+     * Return the color in the hex format, i.e. '#rrggbb'.
+     * @return {String}
+     */
+    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('');
+    },
+
+    /**
+     * Convert a color to hexadecimal format.
+     *
+     * @param {String|Array} color The color value (i.e 'rgb(255, 255, 255)', 'color: #ffffff').
+     * Can also be an Array, in this case the function handles the first member.
+     * @returns {String} The color in hexadecimal format.
+     */
+    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 '';
+        }
+    },
+
+    /**
+     * Parse the string and create a new color.
+     *
+     * Supported formats: '#rrggbb', '#rgb', and 'rgb(r,g,b)'.
+     *
+     * If the string is not recognized, an undefined will be returned instead.
+     *
+     * @param {String} str Color in string.
+     * @returns Ext.draw.Color
+     */
+    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);
+    },
+
+    /**
+     * Returns the gray value (0 to 255) of the color.
+     *
+     * The gray value is calculated using the formula r*0.3 + g*0.59 + b*0.11.
+     *
+     * @returns {Number}
+     */
+    getGrayscale: function() {
+        // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
+        return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
+    },
+
+    /**
+     * Create a new color based on the specified HSL values.
+     *
+     * @param {Number} h Hue component (0..359)
+     * @param {Number} s Saturation component (0..1)
+     * @param {Number} l Lightness component (0..1)
+     * @returns Ext.draw.Color
+     */
+    fromHSL: function(h, s, l) {
+        var C, X, m, i, rgb = [],
+            abs = Math.abs,
+            floor = Math.floor;
+
+        if (s == 0 || h == null) {
+            // achromatic
+            rgb = [l, l, l];
+        }
+        else {
+            // http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
+            // C is the chroma
+            // X is the second largest component
+            // m is the lightness adjustment
+            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;
+
+    //These functions are both static and instance. TODO: find a more elegant way of copying them
+    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);
+        }
+    });
+});
+
+/**
+ * @class Ext.dd.StatusProxy
+ * A specialized drag proxy that supports a drop status icon, {@link Ext.Layer} styles and auto-repair.  This is the
+ * default drag proxy used by all Ext.dd components.
+ * @constructor
+ * @param {Object} config
+ */
+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;
+    },
+    /**
+     * @cfg {String} dropAllowed
+     * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
+     */
+    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
+    /**
+     * @cfg {String} dropNotAllowed
+     * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
+     */
+    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',
+
+    /**
+     * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
+     * over the current target element.
+     * @param {String} cssClass The css class for the new drop status indicator image
+     */
+    setStatus : function(cssClass){
+        cssClass = cssClass || this.dropNotAllowed;
+        if(this.dropStatus != cssClass){
+            this.el.replaceCls(this.dropStatus, cssClass);
+            this.dropStatus = cssClass;
+        }
+    },
+
+    /**
+     * Resets the status indicator to the default dropNotAllowed value
+     * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
+     */
+    reset : function(clearGhost){
+        this.el.dom.className = Ext.baseCSSPrefix + 'dd-drag-proxy ' + this.dropNotAllowed;
+        this.dropStatus = this.dropNotAllowed;
+        if(clearGhost){
+            this.ghost.update("");
+        }
+    },
+
+    /**
+     * Updates the contents of the ghost element
+     * @param {String/HTMLElement} html The html that will replace the current innerHTML of the ghost element, or a
+     * DOM node to append as the child of the ghost element (in which case the innerHTML will be cleared first).
+     */
+    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');
+        }
+    },
+
+    /**
+     * Returns the underlying proxy {@link Ext.Layer}
+     * @return {Ext.Layer} el
+    */
+    getEl : function(){
+        return this.el;
+    },
+
+    /**
+     * Returns the ghost element
+     * @return {Ext.core.Element} el
+     */
+    getGhost : function(){
+        return this.ghost;
+    },
+
+    /**
+     * Hides the proxy
+     * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
+     */
+    hide : function(clear) {
+        this.proxy.hide();
+        if (clear) {
+            this.reset(true);
+        }
+    },
+
+    /**
+     * Stops the repair animation if it's currently running
+     */
+    stop : function(){
+        if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
+            this.anim.stop();
+        }
+    },
+
+    /**
+     * Displays this proxy
+     */
+    show : function() {
+        this.proxy.show();
+        this.proxy.toFront();
+    },
+
+    /**
+     * Force the Layer to sync its shadow and shim positions to the element
+     */
+    sync : function(){
+        this.proxy.el.sync();
+    },
+
+    /**
+     * Causes the proxy to return to its position of origin via an animation.  Should be called after an
+     * invalid drop operation by the item being dragged.
+     * @param {Array} xy The XY position of the element ([x, y])
+     * @param {Function} callback The function to call after the repair is complete.
+     * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
+     */
+    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();
+        }
+    },
+
+    // private
+    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);
+    }
+});
+/**
+ * @class Ext.panel.Proxy
+ * @extends Object
+ * A custom drag proxy implementation specific to {@link Ext.panel.Panel}s. This class
+ * is primarily used internally for the Panel's drag drop implementation, and
+ * should never need to be created directly.
+ * @constructor
+ * @param panel The {@link Ext.panel.Panel} to proxy for
+ * @param config Configuration options
+ */
+Ext.define('Ext.panel.Proxy', {
+    
+    alternateClassName: 'Ext.dd.PanelProxy',
+    
+    constructor: function(panel, config){
+        /**
+         * @property panel
+         * @type Ext.panel.Panel
+         */
+        this.panel = panel;
+        this.id = this.panel.id +'-ddproxy';
+        Ext.apply(this, config);
+    },
+
+    /**
+     * @cfg {Boolean} insertProxy True to insert a placeholder proxy element
+     * while dragging the panel, false to drag with no proxy (defaults to true).
+     * Most Panels are not absolute positioned and therefore we need to reserve
+     * this space.
+     */
+    insertProxy: true,
+
+    // private overrides
+    setStatus: Ext.emptyFn,
+    reset: Ext.emptyFn,
+    update: Ext.emptyFn,
+    stop: Ext.emptyFn,
+    sync: Ext.emptyFn,
+
+    /**
+     * Gets the proxy's element
+     * @return {Element} The proxy's element
+     */
+    getEl: function(){
+        return this.ghost.el;
+    },
+
+    /**
+     * Gets the proxy's ghost Panel
+     * @return {Panel} The proxy's ghost Panel
+     */
+    getGhost: function(){
+        return this.ghost;
+    },
+
+    /**
+     * Gets the proxy element. This is the element that represents where the
+     * Panel was before we started the drag operation.
+     * @return {Element} The proxy's element
+     */
+    getProxy: function(){
+        return this.proxy;
+    },
+
+    /**
+     * Hides the proxy
+     */
+    hide : function(){
+        if (this.ghost) {
+            if (this.proxy) {
+                this.proxy.remove();
+                delete this.proxy;
+            }
+
+            // Unghost the Panel, do not move the Panel to where the ghost was
+            this.panel.unghost(null, false);
+            delete this.ghost;
+        }
+    },
+
+    /**
+     * Shows the proxy
+     */
+    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) {
+                // bc Panels aren't absolute positioned we need to take up the space
+                // of where the panel previously was
+                this.proxy = this.panel.el.insertSibling({cls: Ext.baseCSSPrefix + 'panel-dd-spacer'});
+                this.proxy.setSize(panelSize);
+            }
+        }
+    },
+
+    // private
+    repair: function(xy, callback, scope) {
+        this.hide();
+        if (typeof callback == "function") {
+            callback.call(scope || this);
+        }
+    },
+
+    /**
+     * Moves the proxy to a different position in the DOM.  This is typically
+     * called while dragging the Panel to keep the proxy sync'd to the Panel's
+     * location.
+     * @param {HTMLElement} parentNode The proxy's parent DOM node
+     * @param {HTMLElement} before (optional) The sibling node before which the
+     * proxy should be inserted (defaults to the parent's last child if not
+     * specified)
+     */
+    moveProxy : function(parentNode, before){
+        if (this.proxy) {
+            parentNode.insertBefore(this.proxy.dom, before);
+        }
+    }
+});
+/**
+ * @class Ext.layout.component.AbstractDock
+ * @extends Ext.layout.component.Component
+ * @private
+ * This ComponentLayout handles docking for Panels. It takes care of panels that are
+ * part of a ContainerLayout that sets this Panel's size and Panels that are part of
+ * an AutoContainerLayout in which this panel get his height based of the CSS or
+ * or its content.
+ */
+
+Ext.define('Ext.layout.component.AbstractDock', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.layout.component.Component',
+
+    /* End Definitions */
+
+    type: 'dock',
+
+    /**
+     * @private
+     * @property autoSizing
+     * @type boolean
+     * This flag is set to indicate this layout may have an autoHeight/autoWidth.
+     */
+    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;
+    },
+    
+    /**
+     * @protected
+     * @param {Ext.Component} owner The Panel that owns this DockLayout
+     * @param {Ext.core.Element} target The target in which we are going to render the docked items
+     * @param {Array} args The arguments passed to the ComponentLayout.layout method
+     */
+    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;
+
+        // We start of by resetting all the layouts info
+        var info = me.info = {
+            boxes: [],
+            size: {
+                width: width,
+                height: height
+            },
+            bodyBox: {}
+        };
+
+        Ext.applyIf(info, me.getTargetInfo());
+
+        // We need to bind to the ownerCt whenever we do not have a user set height or width.
+        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;
+            }
+        }
+
+        // Determine if we have an autoHeight or autoWidth.
+        if (height === undefined || height === null || width === undefined || width === null) {
+            padding = info.padding;
+            border = info.border;
+            frameSize = me.frameSize;
+
+            // Auto-everything, clear out any style height/width and read from css
+            if ((height === undefined || height === null) && (width === undefined || width === null)) {
+                autoHeight = true;
+                autoWidth = true;
+                me.setTargetSize(null);
+                me.setBodyBox({width: null, height: null});
+            }
+            // Auto-height
+            else if (height === undefined || height === null) {
+                autoHeight = true;
+                // Clear any sizing that we already set in a previous layout
+                me.setTargetSize(width);
+                me.setBodyBox({width: width - padding.left - border.left - padding.right - border.right - frameSize.left - frameSize.right, height: null});
+            // Auto-width
+            }
+            else {
+                autoWidth = true;
+                // Clear any sizing that we already set in a previous layout
+                me.setTargetSize(null, height);
+                me.setBodyBox({width: null, height: height - padding.top - padding.bottom - border.top - border.bottom - frameSize.top - frameSize.bottom});
+            }
+
+            // Run the container
+            if (layout && layout.isLayout) {
+                // Auto-Sized so have the container layout notify the component layout.
+                layout.bindToOwnerCtComponent = true;
+                layout.layout();
+
+                // If this is an autosized container layout, then we must compensate for a
+                // body that is being autosized.  We do not want to adjust the body's size
+                // to accommodate the dock items, but rather we will want to adjust the
+                // target's size.
+                //
+                // This is necessary because, particularly in a Box layout, all child items
+                // are set with absolute dimensions that are not flexible to the size of its
+                // innerCt/target.  So once they are laid out, they are sized for good. By
+                // shrinking the body box to accommodate dock items, we're merely cutting off
+                // parts of the body.  Not good.  Instead, the target's size should expand
+                // to fit the dock items in.  This is valid because the target container is
+                // suppose to be autosized to fit everything accordingly.
+                info.autoSizedCtLayout = layout.autoSize === true;
+            }
+
+            // The dockItems method will add all the top and bottom docked items height
+            // to the info.panelSize height. That's why we have to call setSize after
+            // we dock all the items to actually set the panel's width and height.
+            // We have to do this because the panel body and docked items will be position
+            // absolute which doesn't stretch the panel.
+            me.dockItems(autoWidth, autoHeight);
+            me.setTargetSize(info.size.width, info.size.height);
+        }
+        else {
+            me.setTargetSize(width, height);
+            me.dockItems();
+        }
+        me.callParent(arguments);
+    },
+
+    /**
+     * @protected
+     * This method will first update all the information about the docked items,
+     * body dimensions and position, the panel's total size. It will then
+     * set all these values on the docked items and panel body.
+     * @param {Array} items Array containing all the docked items
+     * @param {Boolean} autoBoxes Set this to true if the Panel is part of an
+     * AutoContainerLayout
+     */
+    dockItems : function(autoWidth, autoHeight) {
+        this.calculateDockBoxes(autoWidth, autoHeight);
+
+        // Both calculateAutoBoxes and calculateSizedBoxes are changing the
+        // information about the body, panel size, and boxes for docked items
+        // inside a property called info.
+        var info = this.info,
+            boxes = info.boxes,
+            ln = boxes.length,
+            dock, i;
+
+        // We are going to loop over all the boxes that were calculated
+        // and set the position of each item the box belongs to.
+        for (i = 0; i < ln; i++) {
+            dock = boxes[i];
+            dock.item.setPosition(dock.x, dock.y);
+            if ((autoWidth || autoHeight) && dock.layout && dock.layout.isLayout) {
+                // Auto-Sized so have the container layout notify the component layout.
+                dock.layout.bindToOwnerCtComponent = true;
+            }
+        }
+
+        // Don't adjust body width/height if the target is using an auto container layout.
+        // But, we do want to adjust the body size if the container layout is auto sized.
+        if (!info.autoSizedCtLayout) {
+            if (autoWidth) {
+                info.bodyBox.width = null;
+            }
+            if (autoHeight) {
+                info.bodyBox.height = null;
+            }
+        }
+
+        // If the bodyBox has been adjusted because of the docked items
+        // we will update the dimensions and position of the panel's body.
+        this.setBodyBox(info.bodyBox);
+    },
+
+    /**
+     * @protected
+     * This method will set up some initial information about the panel size and bodybox
+     * and then loop over all the items you pass it to take care of stretching, aligning,
+     * dock position and all calculations involved with adjusting the body box.
+     * @param {Array} items Array containing all the docked items we have to layout
+     */
+    calculateDockBoxes : function(autoWidth, autoHeight) {
+        // We want to use the Panel's el width, and the Panel's body height as the initial
+        // size we are going to use in calculateDockBoxes. We also want to account for
+        // the border of the panel.
+        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 this Panel is inside an AutoContainerLayout, we will base all the calculations
+        // around the height of the body and the width of the panel.
+        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
+        };
+
+        // Loop over all the docked items
+        for (i = 0; i < ln; i++) {
+            item = items[i];
+            // The initBox method will take care of stretching and alignment
+            // In some cases it will also layout the dock items to be able to
+            // get a width or height measurement
+            box = me.initBox(item);
+
+            if (autoHeight === true) {
+                box = me.adjustAutoBox(box, i);
+            }
+            else {
+                box = me.adjustSizedBox(box, i);
+            }
+
+            // Save our box. This allows us to loop over all docked items and do all
+            // calculations first. Then in one loop we will actually size and position
+            // all the docked items that have changed.
+            info.boxes.push(box);
+        }
+    },
+
+    /**
+     * @protected
+     * This method will adjust the position of the docked item and adjust the body box
+     * accordingly.
+     * @param {Object} box The box containing information about the width and height
+     * of this docked item
+     * @param {Number} index The index position of this docked item
+     * @return {Object} The adjusted 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 this is not an overlaying docked item, we have to adjust the body box
+        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;
+    },
+
+    /**
+     * @protected
+     * This method will adjust the position of the docked item inside an AutoContainerLayout
+     * and adjust the body box accordingly.
+     * @param {Object} box The box containing information about the width and height
+     * of this docked item
+     * @param {Number} index The index position of this docked item
+     * @return {Object} The adjusted 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') {
+            // This can affect the previously set left and right and bottom docked items
+            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;
+    },
+
+    /**
+     * @protected
+     * This method will create a box object, with a reference to the item, the type of dock
+     * (top, left, bottom, right). It will also take care of stretching and aligning of the
+     * docked items.
+     * @param {Ext.Component} item The docked item we want to initialize the box for
+     * @return {Object} The initial box containing width and height and other useful information
+     */
+    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
+            };
+        // First we are going to take care of stretch and align properties for all four dock scenarios.
+        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);
+
+                // At this point IE will report the left/right-docked toolbar as having a width equal to the
+                // container's full width. Forcing a repaint kicks it into shape so it reports the correct width.
+                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 we haven't calculated the width or height of the docked item yet
+        // do so, since we need this for our upcoming calculations
+        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;
+    },
+
+    /**
+     * @protected
+     * Returns an array containing all the <b>visible</b> docked items inside this layout's owner Panel
+     * @return {Array} An array containing all the <b>visible</b> docked items of the Panel
+     */
+    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;
+    },
+
+    /**
+     * @protected
+     * Render the top and left docked items before any existing DOM nodes in our render target,
+     * and then render the right and bottom docked items after. This is important, for such things
+     * as tab stops and ARIA readers, that the DOM nodes are in a meaningful order.
+     * Our collection of docked items will already be ordered via Panel.getDockedItems().
+     */
+    renderItems: function(items, target) {
+        var cns = target.dom.childNodes,
+            cnsLn = cns.length,
+            ln = items.length,
+            domLn = 0,
+            i, j, cn, item;
+
+        // Calculate the number of DOM nodes in our target that are not our docked items
+        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++;
+            }
+        }
+
+        // Now we go through our docked items and render/move them
+        for (i = 0, j = 0; i < ln; i++, j++) {
+            item = items[i];
+
+            // If we're now at the right/bottom docked item, we jump ahead in our
+            // DOM position, just past the existing DOM nodes.
+            //
+            // TODO: This is affected if users provide custom weight values to their
+            // docked items, which puts it out of (t,l,r,b) order. Avoiding a second
+            // sort operation here, for now, in the name of performance. getDockedItems()
+            // needs the sort operation not just for this layout-time rendering, but
+            // also for getRefItems() to return a logical ordering (FocusManager, CQ, et al).
+            if (i === j && (item.dock === 'right' || item.dock === 'bottom')) {
+                j += domLn;
+            }
+
+            // Same logic as Layout.renderItems()
+            if (item && !item.rendered) {
+                this.renderItem(item, target, j);
+            }
+            else if (!this.isValidParent(item, target, j)) {
+                this.moveItem(item, target, j);
+            }
+        }
+    },
+
+    /**
+     * @protected
+     * This function will be called by the dockItems method. Since the body is positioned absolute,
+     * we need to give it dimensions and a position so that it is in the middle surrounded by
+     * docked items
+     * @param {Object} box An object containing new x, y, width and height values for the
+     * Panel's body
+     */
+    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;
+        
+        // Panel collapse effectively hides the Panel's body, so this is a no-op.
+        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);
+        }
+    },
+
+    /**
+     * @protected
+     * We are overriding the Ext.layout.Layout configureItem method to also add a class that
+     * indicates the position of the docked item. We use the itemCls (x-docked) as a prefix.
+     * An example of a class added to a dock: right item is x-docked-right
+     * @param {Ext.Component} item The item we are configuring
+     */
+    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;
+    }
+});
+/**
+ * @class Ext.app.EventBus
+ * @private
+ *
+ * Class documentation for the MVC classes will be present before 4.0 final, in the mean time please refer to the MVC
+ * guide
+ */
+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) {
+            // Loop over all the selectors that are bound to this event
+            for (selector in selectors) {
+                // Check if the target matches the selector
+                if (target.is(selector)) {
+                    // Loop over all the controllers that are bound to this selector   
+                    controllers = selectors[selector];
+                    for (id in controllers) {
+                        // Loop over all the events that are bound to this selector on this controller
+                        events = controllers[id];
+                        for (i = 0, ln = events.length; i < ln; i++) {
+                            event = events[i];
+                            // Fire the event!
+                            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);
+                
+                // Normalize the listener                
+                if (Ext.isObject(listener)) {
+                    options = listener;
+                    listener = options.fn;
+                    scope = options.scope || controller;
+                    delete options.fn;
+                    delete options.scope;
+                }
+                
+                event.addListener(listener, scope, options);
+
+                // Create the bus tree if it is not there yet
+                bus[ev] = bus[ev] || {};
+                bus[ev][selector] = bus[ev][selector] || {};
+                bus[ev][selector][controller.id] = bus[ev][selector][controller.id] || [];            
+                
+                // Push our listener in our bus
+                bus[ev][selector][controller.id].push(event);
+            });
+        });
+    }
+});
+/**
+ * @class Ext.data.Types
+ * <p>This is s static class containing the system-supplied data types which may be given to a {@link Ext.data.Field Field}.<p/>
+ * <p>The properties in this class are used as type indicators in the {@link Ext.data.Field Field} class, so to
+ * test whether a Field is of a certain type, compare the {@link Ext.data.Field#type type} property against properties
+ * of this class.</p>
+ * <p>Developers may add their own application-specific data types to this class. Definition names must be UPPERCASE.
+ * each type definition must contain three properties:</p>
+ * <div class="mdetail-params"><ul>
+ * <li><code>convert</code> : <i>Function</i><div class="sub-desc">A function to convert raw data values from a data block into the data
+ * to be stored in the Field. The function is passed the collowing parameters:
+ * <div class="mdetail-params"><ul>
+ * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
+ * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
+ * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
+ * Depending on the Reader type, this could be an Array ({@link Ext.data.reader.Array ArrayReader}), an object
+ * ({@link Ext.data.reader.Json JsonReader}), or an XML element.</div></li>
+ * </ul></div></div></li>
+ * <li><code>sortType</code> : <i>Function</i> <div class="sub-desc">A function to convert the stored data into comparable form, as defined by {@link Ext.data.SortTypes}.</div></li>
+ * <li><code>type</code> : <i>String</i> <div class="sub-desc">A textual data type name.</div></li>
+ * </ul></div>
+ * <p>For example, to create a VELatLong field (See the Microsoft Bing Mapping API) containing the latitude/longitude value of a datapoint on a map from a JsonReader data block
+ * which contained the properties <code>lat</code> and <code>long</code>, you would define a new data type like this:</p>
+ *<pre><code>
+// Add a new Field data type which stores a VELatLong object in the Record.
+Ext.data.Types.VELATLONG = {
+    convert: function(v, data) {
+        return new VELatLong(data.lat, data.long);
+    },
+    sortType: function(v) {
+        return v.Latitude;  // When sorting, order by latitude
+    },
+    type: 'VELatLong'
+};
+</code></pre>
+ * <p>Then, when declaring a Model, use <pre><code>
+var types = Ext.data.Types; // allow shorthand type access
+Ext.define('Unit',
+    extend: 'Ext.data.Model', 
+    fields: [
+        { name: 'unitName', mapping: 'UnitName' },
+        { name: 'curSpeed', mapping: 'CurSpeed', type: types.INT },
+        { name: 'latitude', mapping: 'lat', type: types.FLOAT },
+        { name: 'latitude', mapping: 'lat', type: types.FLOAT },
+        { name: 'position', type: types.VELATLONG }
+    ]
+});
+</code></pre>
+ * @singleton
+ */
+Ext.define('Ext.data.Types', {
+    singleton: true,
+    requires: ['Ext.data.SortTypes']
+}, function() {
+    var st = Ext.data.SortTypes;
+    
+    Ext.apply(Ext.data.Types, {
+        /**
+         * @type Regexp
+         * @property stripRe
+         * A regular expression for stripping non-numeric characters from a numeric value. Defaults to <tt>/[\$,%]/g</tt>.
+         * This should be overridden for localization.
+         */
+        stripRe: /[\$,%]/g,
+        
+        /**
+         * @type Object.
+         * @property AUTO
+         * This data type means that no conversion is applied to the raw data before it is placed into a Record.
+         */
+        AUTO: {
+            convert: function(v) {
+                return v;
+            },
+            sortType: st.none,
+            type: 'auto'
+        },
+
+        /**
+         * @type Object.
+         * @property STRING
+         * This data type means that the raw data is converted into a String before it is placed into a Record.
+         */
+        STRING: {
+            convert: function(v) {
+                var defaultValue = this.useNull ? null : '';
+                return (v === undefined || v === null) ? defaultValue : String(v);
+            },
+            sortType: st.asUCString,
+            type: 'string'
+        },
+
+        /**
+         * @type Object.
+         * @property INT
+         * This data type means that the raw data is converted into an integer before it is placed into a Record.
+         * <p>The synonym <code>INTEGER</code> is equivalent.</p>
+         */
+        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'
+        },
+        
+        /**
+         * @type Object.
+         * @property FLOAT
+         * This data type means that the raw data is converted into a number before it is placed into a Record.
+         * <p>The synonym <code>NUMBER</code> is equivalent.</p>
+         */
+        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'
+        },
+        
+        /**
+         * @type Object.
+         * @property BOOL
+         * <p>This data type means that the raw data is converted into a boolean before it is placed into
+         * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
+         * <p>The synonym <code>BOOLEAN</code> is equivalent.</p>
+         */
+        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'
+        },
+        
+        /**
+         * @type Object.
+         * @property DATE
+         * This data type means that the raw data is converted into a Date before it is placed into a Record.
+         * The date format is specified in the constructor of the {@link Ext.data.Field} to which this type is
+         * being applied.
+         */
+        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, {
+        /**
+         * @type Object.
+         * @property BOOLEAN
+         * <p>This data type means that the raw data is converted into a boolean before it is placed into
+         * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
+         * <p>The synonym <code>BOOL</code> is equivalent.</p>
+         */
+        BOOLEAN: this.BOOL,
+        
+        /**
+         * @type Object.
+         * @property INTEGER
+         * This data type means that the raw data is converted into an integer before it is placed into a Record.
+         * <p>The synonym <code>INT</code> is equivalent.</p>
+         */
+        INTEGER: this.INT,
+        
+        /**
+         * @type Object.
+         * @property NUMBER
+         * This data type means that the raw data is converted into a number before it is placed into a Record.
+         * <p>The synonym <code>FLOAT</code> is equivalent.</p>
+         */
+        NUMBER: this.FLOAT    
+    });
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.Field
+ * @extends Object
+ * 
+ * <p>Fields are used to define what a Model is. They aren't instantiated directly - instead, when we create a class 
+ * that extends {@link Ext.data.Model}, it will automatically create a Field instance for each field configured in a 
+ * {@link Ext.data.Model Model}. For example, we might set up a model like this:</p>
+ * 
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: [
+        'name', 'email',
+        {name: 'age', type: 'int'},
+        {name: 'gender', type: 'string', defaultValue: 'Unknown'}
+    ]
+});
+</code></pre>
+ * 
+ * <p>Four fields will have been created for the User Model - name, email, age and gender. Note that we specified a
+ * couple of different formats here; if we only pass in the string name of the field (as with name and email), the
+ * field is set up with the 'auto' type. It's as if we'd done this instead:</p>
+ * 
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'name', type: 'auto'},
+        {name: 'email', type: 'auto'},
+        {name: 'age', type: 'int'},
+        {name: 'gender', type: 'string', defaultValue: 'Unknown'}
+    ]
+});
+</code></pre>
+ * 
+ * <p><u>Types and conversion</u></p>
+ * 
+ * <p>The {@link #type} is important - it's used to automatically convert data passed to the field into the correct
+ * format. In our example above, the name and email fields used the 'auto' type and will just accept anything that is
+ * passed into them. The 'age' field had an 'int' type however, so if we passed 25.4 this would be rounded to 25.</p>
+ * 
+ * <p>Sometimes a simple type isn't enough, or we want to perform some processing when we load a Field's data. We can
+ * do this using a {@link #convert} function. Here, we're going to create a new field based on another:</p>
+ * 
+<code><pre>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: [
+        'name', 'email',
+        {name: 'age', type: 'int'},
+        {name: 'gender', type: 'string', defaultValue: 'Unknown'},
+
+        {
+            name: 'firstName',
+            convert: function(value, record) {
+                var fullName  = record.get('name'),
+                    splits    = fullName.split(" "),
+                    firstName = splits[0];
+
+                return firstName;
+            }
+        }
+    ]
+});
+</code></pre>
+ * 
+ * <p>Now when we create a new User, the firstName is populated automatically based on the name:</p>
+ * 
+<code><pre>
+var ed = Ext.ModelManager.create({name: 'Ed Spencer'}, 'User');
+
+console.log(ed.get('firstName')); //logs 'Ed', based on our convert function
+</code></pre>
+ * 
+ * <p>In fact, if we log out all of the data inside ed, we'll see this:</p>
+ * 
+<code><pre>
+console.log(ed.data);
+
+//outputs this:
+{
+    age: 0,
+    email: "",
+    firstName: "Ed",
+    gender: "Unknown",
+    name: "Ed Spencer"
+}
+</code></pre>
+ * 
+ * <p>The age field has been given a default of zero because we made it an int type. As an auto field, email has
+ * defaulted to an empty string. When we registered the User model we set gender's {@link #defaultValue} to 'Unknown'
+ * so we see that now. Let's correct that and satisfy ourselves that the types work as we expect:</p>
+ * 
+<code><pre>
+ed.set('gender', 'Male');
+ed.get('gender'); //returns 'Male'
+
+ed.set('age', 25.4);
+ed.get('age'); //returns 25 - we wanted an int, not a float, so no decimal places allowed
+</code></pre>
+ * 
+ */
+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;
+        }
+
+        // named sortTypes are supported, here we look them up
+        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;
+        }
+    },
+    
+    /**
+     * @cfg {String} name
+     * The name by which the field is referenced within the Model. This is referenced by, for example,
+     * the <code>dataIndex</code> property in column definition objects passed to {@link Ext.grid.property.HeaderContainer}.
+     * <p>Note: In the simplest case, if no properties other than <code>name</code> are required, a field
+     * definition may consist of just a String for the field name.</p>
+     */
+    
+    /**
+     * @cfg {Mixed} type
+     * (Optional) The data type for automatic conversion from received data to the <i>stored</i> value if <code>{@link Ext.data.Field#convert convert}</code>
+     * has not been specified. This may be specified as a string value. Possible values are
+     * <div class="mdetail-params"><ul>
+     * <li>auto (Default, implies no conversion)</li>
+     * <li>string</li>
+     * <li>int</li>
+     * <li>float</li>
+     * <li>boolean</li>
+     * <li>date</li></ul></div>
+     * <p>This may also be specified by referencing a member of the {@link Ext.data.Types} class.</p>
+     * <p>Developers may create their own application-specific data types by defining new members of the
+     * {@link Ext.data.Types} class.</p>
+     */
+    
+    /**
+     * @cfg {Function} convert
+     * (Optional) A function which converts the value provided by the Reader into an object that will be stored
+     * in the Model. It is passed the following parameters:<div class="mdetail-params"><ul>
+     * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
+     * the configured <code>{@link Ext.data.Field#defaultValue defaultValue}</code>.</div></li>
+     * <li><b>rec</b> : Ext.data.Model<div class="sub-desc">The data object containing the Model as read so far by the 
+     * Reader. Note that the Model may not be fully populated at this point as the fields are read in the order that 
+     * they are defined in your {@link #fields} array.</div></li>
+     * </ul></div>
+     * <pre><code>
+// example of convert function
+function fullName(v, record){
+    return record.name.last + ', ' + record.name.first;
+}
+
+function location(v, record){
+    return !record.city ? '' : (record.city + ', ' + record.state);
+}
+
+Ext.define('Dude', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'fullname',  convert: fullName},
+        {name: 'firstname', mapping: 'name.first'},
+        {name: 'lastname',  mapping: 'name.last'},
+        {name: 'city', defaultValue: 'homeless'},
+        'state',
+        {name: 'location',  convert: location}
+    ]
+});
+
+// create the data store
+var store = new Ext.data.Store({
+    reader: {
+        type: 'json',
+        model: 'Dude',
+        idProperty: 'key',
+        root: 'daRoot',
+        totalProperty: 'total'
+    }
+});
+
+var myData = [
+    { key: 1,
+      name: { first: 'Fat',    last:  'Albert' }
+      // notice no city, state provided in data object
+    },
+    { key: 2,
+      name: { first: 'Barney', last:  'Rubble' },
+      city: 'Bedrock', state: 'Stoneridge'
+    },
+    { key: 3,
+      name: { first: 'Cliff',  last:  'Claven' },
+      city: 'Boston',  state: 'MA'
+    }
+];
+     * </code></pre>
+     */
+    /**
+     * @cfg {String} dateFormat
+     * <p>(Optional) Used when converting received data into a Date when the {@link #type} is specified as <code>"date"</code>.</p>
+     * <p>A format string for the {@link Ext.Date#parse Ext.Date.parse} function, or "timestamp" if the
+     * value provided by the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a
+     * javascript millisecond timestamp. See {@link Date}</p>
+     */
+    dateFormat: null,
+    
+    /**
+     * @cfg {Boolean} useNull
+     * <p>(Optional) Use when converting received data into a Number type (either int or float). If the value cannot be parsed,
+     * null will be used if useNull is true, otherwise the value will be 0. Defaults to <tt>false</tt>
+     */
+    useNull: false,
+    
+    /**
+     * @cfg {Mixed} defaultValue
+     * (Optional) The default value used <b>when a Model is being created by a {@link Ext.data.reader.Reader Reader}</b>
+     * when the item referenced by the <code>{@link Ext.data.Field#mapping mapping}</code> does not exist in the data
+     * object (i.e. undefined). (defaults to "")
+     */
+    defaultValue: "",
+    /**
+     * @cfg {String/Number} mapping
+     * <p>(Optional) A path expression for use by the {@link Ext.data.reader.Reader} implementation
+     * that is creating the {@link Ext.data.Model Model} to extract the Field value from the data object.
+     * If the path expression is the same as the field name, the mapping may be omitted.</p>
+     * <p>The form of the mapping expression depends on the Reader being used.</p>
+     * <div class="mdetail-params"><ul>
+     * <li>{@link Ext.data.reader.Json}<div class="sub-desc">The mapping is a string containing the javascript
+     * expression to reference the data from an element of the data item's {@link Ext.data.reader.Json#root root} Array. Defaults to the field name.</div></li>
+     * <li>{@link Ext.data.reader.Xml}<div class="sub-desc">The mapping is an {@link Ext.DomQuery} path to the data
+     * item relative to the DOM element that represents the {@link Ext.data.reader.Xml#record record}. Defaults to the field name.</div></li>
+     * <li>{@link Ext.data.reader.Array}<div class="sub-desc">The mapping is a number indicating the Array index
+     * of the field's value. Defaults to the field specification's Array position.</div></li>
+     * </ul></div>
+     * <p>If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
+     * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
+     * return the desired data.</p>
+     */
+    mapping: null,
+    /**
+     * @cfg {Function} sortType
+     * (Optional) A function which converts a Field's value to a comparable value in order to ensure
+     * correct sort ordering. Predefined functions are provided in {@link Ext.data.SortTypes}. A custom
+     * sort example:<pre><code>
+// current sort     after sort we want
+// +-+------+          +-+------+
+// |1|First |          |1|First |
+// |2|Last  |          |3|Second|
+// |3|Second|          |2|Last  |
+// +-+------+          +-+------+
+
+sortType: function(value) {
+   switch (value.toLowerCase()) // native toLowerCase():
+   {
+      case 'first': return 1;
+      case 'second': return 2;
+      default: return 3;
+   }
+}
+     * </code></pre>
+     */
+    sortType : null,
+    /**
+     * @cfg {String} sortDir
+     * (Optional) Initial direction to sort (<code>"ASC"</code> or  <code>"DESC"</code>).  Defaults to
+     * <code>"ASC"</code>.
+     */
+    sortDir : "ASC",
+    /**
+     * @cfg {Boolean} allowBlank
+     * @private
+     * (Optional) Used for validating a {@link Ext.data.Model model}, defaults to <code>true</code>.
+     * An empty value here will cause {@link Ext.data.Model}.{@link Ext.data.Model#isValid isValid}
+     * to evaluate to <code>false</code>.
+     */
+    allowBlank : true,
+    
+    /**
+     * @cfg {Boolean} persist
+     * False to exclude this field from the {@link Ext.data.Model#modified} fields in a model. This 
+     * will also exclude the field from being written using a {@link Ext.data.writer.Writer}. This option
+     * is useful when model fields are used to keep state on the client but do not need to be persisted
+     * to the server. Defaults to <tt>true</tt>.
+     */
+    persist: true
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.reader.Reader
+ * @extends Object
+ * 
+ * <p>Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link Ext.data.Store Store}
+ * - usually in response to an AJAX request. This is normally handled transparently by passing some configuration to either the 
+ * {@link Ext.data.Model Model} or the {@link Ext.data.Store Store} in question - see their documentation for further details.</p>
+ * 
+ * <p><u>Loading Nested Data</u></p>
+ * 
+ * <p>Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.Association associations}
+ * configured on each Model. Below is an example demonstrating the flexibility of these associations in a fictional CRM system which
+ * manages a User, their Orders, OrderItems and Products. First we'll define the models:
+ * 
+<pre><code>
+Ext.define("User", {
+    extend: 'Ext.data.Model',
+    fields: [
+        'id', 'name'
+    ],
+
+    hasMany: {model: 'Order', name: 'orders'},
+
+    proxy: {
+        type: 'rest',
+        url : 'users.json',
+        reader: {
+            type: 'json',
+            root: 'users'
+        }
+    }
+});
+
+Ext.define("Order", {
+    extend: 'Ext.data.Model',
+    fields: [
+        'id', 'total'
+    ],
+
+    hasMany  : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
+    belongsTo: 'User'
+});
+
+Ext.define("OrderItem", {
+    extend: 'Ext.data.Model',
+    fields: [
+        'id', 'price', 'quantity', 'order_id', 'product_id'
+    ],
+
+    belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
+});
+
+Ext.define("Product", {
+    extend: 'Ext.data.Model',
+    fields: [
+        'id', 'name'
+    ],
+
+    hasMany: 'OrderItem'
+});
+</code></pre>
+ * 
+ * <p>This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems. Finally,
+ * each OrderItem has a single Product. This allows us to consume data like this:</p>
+ * 
+<pre><code>
+{
+    "users": [
+        {
+            "id": 123,
+            "name": "Ed",
+            "orders": [
+                {
+                    "id": 50,
+                    "total": 100,
+                    "order_items": [
+                        {
+                            "id"      : 20,
+                            "price"   : 40,
+                            "quantity": 2,
+                            "product" : {
+                                "id": 1000,
+                                "name": "MacBook Pro"
+                            }
+                        },
+                        {
+                            "id"      : 21,
+                            "price"   : 20,
+                            "quantity": 3,
+                            "product" : {
+                                "id": 1001,
+                                "name": "iPhone"
+                            }
+                        }
+                    ]
+                }
+            ]
+        }
+    ]
+}
+</code></pre>
+ * 
+ * <p>The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the Orders
+ * for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case), and finally
+ * the Product associated with each OrderItem. Now we can read the data and use it as follows:
+ * 
+<pre><code>
+var store = new Ext.data.Store({
+    model: "User"
+});
+
+store.load({
+    callback: function() {
+        //the user that was loaded
+        var user = store.first();
+
+        console.log("Orders for " + user.get('name') + ":")
+
+        //iterate over the Orders for each User
+        user.orders().each(function(order) {
+            console.log("Order ID: " + order.getId() + ", which contains items:");
+
+            //iterate over the OrderItems for each Order
+            order.orderItems().each(function(orderItem) {
+                //we know that the Product data is already loaded, so we can use the synchronous getProduct
+                //usually, we would use the asynchronous version (see {@link Ext.data.BelongsToAssociation})
+                var product = orderItem.getProduct();
+
+                console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
+            });
+        });
+    }
+});
+</code></pre>
+ * 
+ * <p>Running the code above results in the following:</p>
+ * 
+<pre><code>
+Orders for Ed:
+Order ID: 50, which contains items:
+2 orders of MacBook Pro
+3 orders of iPhone
+</code></pre>
+ * 
+ * @constructor
+ * @param {Object} config Optional config object
+ */
+Ext.define('Ext.data.reader.Reader', {
+    requires: ['Ext.data.ResultSet'],
+    alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],
+    
+    /**
+     * @cfg {String} idProperty Name of the property within a row object
+     * that contains a record identifier value.  Defaults to <tt>The id of the model</tt>.
+     * If an idProperty is explicitly specified it will override that of the one specified
+     * on the model
+     */
+
+    /**
+     * @cfg {String} totalProperty Name of the property from which to
+     * retrieve the total number of records in the dataset. This is only needed
+     * if the whole dataset is not passed in one go, but is being paged from
+     * the remote server.  Defaults to <tt>total</tt>.
+     */
+    totalProperty: 'total',
+
+    /**
+     * @cfg {String} successProperty Name of the property from which to
+     * retrieve the success attribute. Defaults to <tt>success</tt>.  See
+     * {@link Ext.data.proxy.Proxy}.{@link Ext.data.proxy.Proxy#exception exception}
+     * for additional information.
+     */
+    successProperty: 'success',
+
+    /**
+     * @cfg {String} root <b>Required</b>.  The name of the property
+     * which contains the Array of row objects.  Defaults to <tt>undefined</tt>.
+     * An exception will be thrown if the root property is undefined. The data
+     * packet value for this property should be an empty array to clear the data
+     * or show no data.
+     */
+    root: '',
+    
+    /**
+     * @cfg {String} messageProperty The name of the property which contains a response message.
+     * This property is optional.
+     */
+    
+    /**
+     * @cfg {Boolean} implicitIncludes True to automatically parse models nested within other models in a response
+     * object. See the Ext.data.reader.Reader intro docs for full explanation. Defaults to true.
+     */
+    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();
+        }
+    },
+
+    /**
+     * Sets a new model for the reader.
+     * @private
+     * @param {Object} model The model to set.
+     * @param {Boolean} setOnProxy True to also set on the Proxy, if one is configured
+     */
+    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);
+        }
+    },
+
+    /**
+     * Reads the given response object. This method normalizes the different types of response object that may be passed
+     * to it, before handing off the reading of records to the {@link #readRecords} function.
+     * @param {Object} response The response object. This may be either an XMLHttpRequest object or a plain JS object
+     * @return {Ext.data.ResultSet} The parsed ResultSet object
+     */
+    read: function(response) {
+        var data = response;
+        
+        if (response && response.responseText) {
+            data = this.getResponseData(response);
+        }
+        
+        if (data) {
+            return this.readRecords(data);
+        } else {
+            return this.nullResultSet;
+        }
+    },
+
+    /**
+     * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call
+     * this function before running its own logic and returning the Ext.data.ResultSet instance. For most
+     * Readers additional processing should not be needed.
+     * @param {Mixed} data The raw data object
+     * @return {Ext.data.ResultSet} A ResultSet object
+     */
+    readRecords: function(data) {
+        var me  = this;
+        
+        /*
+         * We check here whether the number of fields has changed since the last read.
+         * This works around an issue when a Model is used for both a Tree and another
+         * source, because the tree decorates the model with extra fields and it causes
+         * issues because the readers aren't notified.
+         */
+        if (me.fieldCount !== me.getFields().length) {
+            me.buildExtractors(true);
+        }
+        
+        /**
+         * The raw data object that was last passed to readRecords. Stored for further processing if needed
+         * @property rawData
+         * @type Mixed
+         */
+        me.rawData = data;
+
+        data = me.getData(data);
+
+        // If we pass an array as the data, we dont use getRoot on the data.
+        // Instead the root equals to the 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
+        });
+    },
+
+    /**
+     * Returns extracted, type-cast rows of data.  Iterates to call #extractValues for each row
+     * @param {Object[]/Object} data-root from server response
+     * @private
+     */
+    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);
+            record.raw = node;
+            records.push(record);
+                
+            if (me.implicitIncludes) {
+                me.readAssociated(record, node);
+            }
+        }
+
+        return records;
+    },
+    
+    /**
+     * @private
+     * Loads a record's associations from the data object. This prepopulates hasMany and belongsTo associations
+     * on the record provided.
+     * @param {Ext.data.Model} record The record to load associations for
+     * @param {Mixed} data The data object
+     * @return {String} Return value description
+     */
+    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 the associated model has a Reader already, use that, otherwise attempt to create a sensible one
+                    if (proxy) {
+                        reader = proxy.getReader();
+                    } else {
+                        reader = new this.constructor({
+                            model: association.associatedName
+                        });
+                    }
+                }
+                association.read(record, reader, associationData);
+            }  
+        }
+    },
+    
+    /**
+     * @private
+     * Used internally by {@link #readAssociated}. Given a data object (which could be json, xml etc) for a specific
+     * record, this should return the relevant part of that data for the given association name. This is only really
+     * needed to support the XML Reader, which has to do a query to get the associated data object
+     * @param {Mixed} data The raw data object
+     * @param {String} associationName The name of the association to get data for (uses associationKey if present)
+     * @return {Mixed} The root
+     */
+    getAssociatedDataRoot: function(data, associationName) {
+        return data[associationName];
+    },
+    
+    getFields: function() {
+        return this.model.prototype.fields.items;
+    },
+
+    /**
+     * @private
+     * Given an object representing a single model instance's data, iterates over the model's fields and
+     * builds an object with the value for each field.
+     * @param {Object} data The data object to convert
+     * @return {Object} Data object suitable for use with a model constructor
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * By default this function just returns what is passed to it. It can be overridden in a subclass
+     * to return something else. See XmlReader for an example.
+     * @param {Object} data The data object
+     * @return {Object} The normalized data object
+     */
+    getData: function(data) {
+        return data;
+    },
+
+    /**
+     * @private
+     * This will usually need to be implemented in a subclass. Given a generic data object (the type depends on the type
+     * of data we are reading), this function should return the object as configured by the Reader's 'root' meta data config.
+     * See XmlReader's getRoot implementation for an example. By default the same data object will simply be returned.
+     * @param {Mixed} data The data object
+     * @return {Mixed} The same data object
+     */
+    getRoot: function(data) {
+        return data;
+    },
+
+    /**
+     * Takes a raw response object (as passed to this.read) and returns the useful data segment of it. This must be implemented by each subclass
+     * @param {Object} response The responce object
+     * @return {Object} The useful data from the response
+     */
+    getResponseData: function(response) {
+        //<debug>
+        Ext.Error.raise("getResponseData must be implemented in the Ext.data.reader.Reader subclass");
+        //</debug>
+    },
+
+    /**
+     * @private
+     * Reconfigures the meta data tied to this Reader
+     */
+    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);
+        }
+    },
+    
+    /**
+     * Get the idProperty to use for extracting data
+     * @private
+     * @return {String} The id property
+     */
+    getIdProperty: function(){
+        var prop = this.idProperty;
+        if (Ext.isEmpty(prop)) {
+            prop = this.model.prototype.idProperty;
+        }
+        return prop;
+    },
+
+    /**
+     * @private
+     * This builds optimized functions for retrieving record data and meta data from an object.
+     * Subclasses may need to implement their own getRoot function.
+     * @param {Boolean} force True to automatically remove existing extractor functions first (defaults to false)
+     */
+    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;
+        }   
+
+        //build the extractors for all the meta data
+        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();
+    },
+
+    /**
+     * @private
+     */
+    buildFieldExtractors: function() {
+        //now build the extractors for all the fields
+        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, {
+        // Private. Empty ResultSet to return when response is falsy (null|undefined|empty string)
+        nullResultSet: Ext.create('Ext.data.ResultSet', {
+            total  : 0,
+            count  : 0,
+            records: [],
+            success: true
+        })
+    });
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.reader.Json
+ * @extends Ext.data.reader.Reader
+ * 
+ * <p>The JSON Reader is used by a Proxy to read a server response that is sent back in JSON format. This usually
+ * happens as a result of loading a Store - for example we might create something like this:</p>
+ * 
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'email']
+});
+
+var store = new Ext.data.Store({
+    model: 'User',
+    proxy: {
+        type: 'ajax',
+        url : 'users.json',
+        reader: {
+            type: 'json'
+        }
+    }
+});
+</code></pre>
+ * 
+ * <p>The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're
+ * not already familiar with them.</p>
+ * 
+ * <p>We created the simplest type of JSON Reader possible by simply telling our {@link Ext.data.Store Store}'s 
+ * {@link Ext.data.proxy.Proxy Proxy} that we want a JSON Reader. The Store automatically passes the configured model to the
+ * Store, so it is as if we passed this instead:
+ * 
+<pre><code>
+reader: {
+    type : 'json',
+    model: 'User'
+}
+</code></pre>
+ * 
+ * <p>The reader we set up is ready to read data from our server - at the moment it will accept a response like this:</p>
+ * 
+<pre><code>
+[
+    {
+        "id": 1,
+        "name": "Ed Spencer",
+        "email": "ed@sencha.com"
+    },
+    {
+        "id": 2,
+        "name": "Abe Elias",
+        "email": "abe@sencha.com"
+    }
+]
+</code></pre>
+ * 
+ * <p><u>Reading other JSON formats</u></p>
+ * 
+ * <p>If you already have your JSON format defined and it doesn't look quite like what we have above, you can usually
+ * pass JsonReader a couple of configuration options to make it parse your format. For example, we can use the 
+ * {@link #root} configuration to parse data that comes back like this:</p>
+ * 
+<pre><code>
+{
+    "users": [
+       {
+           "id": 1,
+           "name": "Ed Spencer",
+           "email": "ed@sencha.com"
+       },
+       {
+           "id": 2,
+           "name": "Abe Elias",
+           "email": "abe@sencha.com"
+       }
+    ]
+}
+</code></pre>
+ * 
+ * <p>To parse this we just pass in a {@link #root} configuration that matches the 'users' above:</p>
+ * 
+<pre><code>
+reader: {
+    type: 'json',
+    root: 'users'
+}
+</code></pre>
+ * 
+ * <p>Sometimes the JSON structure is even more complicated. Document databases like CouchDB often provide metadata
+ * around each record inside a nested structure like this:</p>
+ * 
+<pre><code>
+{
+    "total": 122,
+    "offset": 0,
+    "users": [
+        {
+            "id": "ed-spencer-1",
+            "value": 1,
+            "user": {
+                "id": 1,
+                "name": "Ed Spencer",
+                "email": "ed@sencha.com"
+            }
+        }
+    ]
+}
+</code></pre>
+ * 
+ * <p>In the case above the record data is nested an additional level inside the "users" array as each "user" item has
+ * additional metadata surrounding it ('id' and 'value' in this case). To parse data out of each "user" item in the 
+ * JSON above we need to specify the {@link #record} configuration like this:</p>
+ * 
+<pre><code>
+reader: {
+    type  : 'json',
+    root  : 'users',
+    record: 'user'
+}
+</code></pre>
+ * 
+ * <p><u>Response metadata</u></p>
+ * 
+ * <p>The server can return additional data in its response, such as the {@link #totalProperty total number of records} 
+ * and the {@link #successProperty success status of the response}. These are typically included in the JSON response
+ * like this:</p>
+ * 
+<pre><code>
+{
+    "total": 100,
+    "success": true,
+    "users": [
+        {
+            "id": 1,
+            "name": "Ed Spencer",
+            "email": "ed@sencha.com"
+        }
+    ]
+}
+</code></pre>
+ * 
+ * <p>If these properties are present in the JSON response they can be parsed out by the JsonReader and used by the
+ * Store that loaded it. We can set up the names of these properties by specifying a final pair of configuration 
+ * options:</p>
+ * 
+<pre><code>
+reader: {
+    type : 'json',
+    root : 'users',
+    totalProperty  : 'total',
+    successProperty: 'success'
+}
+</code></pre>
+ * 
+ * <p>These final options are not necessary to make the Reader work, but can be useful when the server needs to report
+ * an error or if it needs to indicate that there is a lot of data available of which only a subset is currently being
+ * returned.</p>
+ */
+Ext.define('Ext.data.reader.Json', {
+    extend: 'Ext.data.reader.Reader',
+    alternateClassName: 'Ext.data.JsonReader',
+    alias : 'reader.json',
+    
+    root: '',
+    
+    /**
+     * @cfg {String} record The optional location within the JSON response that the record data itself can be found at.
+     * See the JsonReader intro docs for more details. This is not often needed and defaults to undefined.
+     */
+    
+    /**
+     * @cfg {Boolean} useSimpleAccessors True to ensure that field names/mappings are treated as literals when
+     * reading values. Defalts to <tt>false</tt>.
+     * For example, by default, using the mapping "foo.bar.baz" will try and read a property foo from the root, then a property bar
+     * from foo, then a property baz from bar. Setting the simple accessors to true will read the property with the name 
+     * "foo.bar.baz" direct from the root object.
+     */
+    useSimpleAccessors: false,
+    
+    /**
+     * Reads a JSON object and returns a ResultSet. Uses the internal getTotal and getSuccess extractors to
+     * retrieve meta data from the response, and extractData to turn the JSON data into model instances.
+     * @param {Object} data The raw JSON data
+     * @return {Ext.data.ResultSet} A ResultSet containing model instances and meta data about the results
+     */
+    readRecords: function(data) {
+        //this has to be before the call to super because we use the meta data in the superclass readRecords
+        if (data.metaData) {
+            this.onMetaChange(data.metaData);
+        }
+
+        /**
+         * DEPRECATED - will be removed in Ext JS 5.0. This is just a copy of this.rawData - use that instead
+         * @property jsonData
+         * @type Mixed
+         */
+        this.jsonData = data;
+        return this.callParent([data]);
+    },
+
+    //inherit docs
+    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()
+            });
+        }
+        //<debug>
+        if (!data) {
+            Ext.Error.raise('JSON object not found');
+        }
+        //</debug>
+
+        return data;
+    },
+
+    //inherit docs
+    buildExtractors : function() {
+        var me = this;
+        
+        me.callParent(arguments);
+
+        if (me.root) {
+            me.getRoot = me.createAccessor(me.root);
+        } else {
+            me.getRoot = function(root) {
+                return root;
+            };
+        }
+    },
+    
+    /**
+     * @private
+     * We're just preparing the data for the superclass by pulling out the record objects we want. If a {@link #record}
+     * was specified we have to pull those out of the larger JSON object, which is most of what this function is doing
+     * @param {Object} root The JSON root node
+     * @return {Array} The records
+     */
+    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]);
+    },
+
+    /**
+     * @private
+     * Returns an accessor function for the given property string. Gives support for properties such as the following:
+     * 'someProperty'
+     * 'some.property'
+     * 'some["property"]'
+     * This is used by buildExtractors to create optimized extractor functions when casting raw data into model instances.
+     */
+    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];
+            };
+        };
+    }()
+});
+/**
+ * @class Ext.data.writer.Json
+ * @extends Ext.data.writer.Writer
+ * @ignore
+ */
+Ext.define('Ext.data.writer.Json', {
+    extend: 'Ext.data.writer.Writer',
+    alternateClassName: 'Ext.data.JsonWriter',
+    alias: 'writer.json',
+    
+    /**
+     * @cfg {String} root The key under which the records in this Writer will be placed. Defaults to <tt>undefined</tt>.
+     * Example generated request, using root: 'records':
+<pre><code>
+{'records': [{name: 'my record'}, {name: 'another record'}]}
+</code></pre>
+     */
+    root: undefined,
+    
+    /**
+     * @cfg {Boolean} encode True to use Ext.encode() on the data before sending. Defaults to <tt>false</tt>.
+     * The encode option should only be set to true when a {@link #root} is defined, because the values will be
+     * sent as part of the request parameters as opposed to a raw post. The root will be the name of the parameter
+     * sent to the server.
+     */
+    encode: false,
+    
+    /**
+     * @cfg {Boolean} allowSingle False to ensure that records are always wrapped in an array, even if there is only
+     * one record being sent. When there is more than one record, they will always be encoded into an array.
+     * Defaults to <tt>true</tt>. Example:
+     * <pre><code>
+// with allowSingle: true
+"root": {
+    "first": "Mark",
+    "last": "Corrigan"
+}
+
+// with allowSingle: false
+"root": [{
+    "first": "Mark",
+    "last": "Corrigan"
+}]
+     * </code></pre>
+     */
+    allowSingle: true,
+    
+    //inherit docs
+    writeRecords: function(request, data) {
+        var root = this.root;
+        
+        if (this.allowSingle && data.length == 1) {
+            // convert to single object format
+            data = data[0];
+        }
+        
+        if (this.encode) {
+            if (root) {
+                // sending as a param, need to encode
+                request.params[root] = Ext.encode(data);
+            } else {
+                //<debug>
+                Ext.Error.raise('Must specify a root when using encode');
+                //</debug>
+            }
+        } else {
+            // send as jsonData
+            request.jsonData = request.jsonData || {};
+            if (root) {
+                request.jsonData[root] = data;
+            } else {
+                request.jsonData = data;
+            }
+        }
+        return request;
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.Proxy
+ * 
+ * <p>Proxies are used by {@link Ext.data.Store Stores} to handle the loading and saving of {@link Ext.data.Model Model} data.
+ * Usually developers will not need to create or interact with proxies directly.</p>
+ * <p><u>Types of Proxy</u></p>
+ * 
+ * <p>There are two main types of Proxy - {@link Ext.data.proxy.Client Client} and {@link Ext.data.proxy.Server Server}. The Client proxies
+ * save their data locally and include the following subclasses:</p>
+ * 
+ * <ul style="list-style-type: disc; padding-left: 25px">
+ * <li>{@link Ext.data.proxy.LocalStorage LocalStorageProxy} - saves its data to localStorage if the browser supports it</li>
+ * <li>{@link Ext.data.proxy.SessionStorage SessionStorageProxy} - saves its data to sessionStorage if the browsers supports it</li>
+ * <li>{@link Ext.data.proxy.Memory MemoryProxy} - holds data in memory only, any data is lost when the page is refreshed</li>
+ * </ul>
+ * 
+ * <p>The Server proxies save their data by sending requests to some remote server. These proxies include:</p>
+ * 
+ * <ul style="list-style-type: disc; padding-left: 25px">
+ * <li>{@link Ext.data.proxy.Ajax Ajax} - sends requests to a server on the same domain</li>
+ * <li>{@link Ext.data.proxy.JsonP JsonP} - uses JSON-P to send requests to a server on a different domain</li>
+ * <li>{@link Ext.data.proxy.Direct Direct} - uses {@link Ext.direct} to send requests</li>
+ * </ul>
+ * 
+ * <p>Proxies operate on the principle that all operations performed are either Create, Read, Update or Delete. These four operations 
+ * are mapped to the methods {@link #create}, {@link #read}, {@link #update} and {@link #destroy} respectively. Each Proxy subclass 
+ * implements these functions.</p>
+ * 
+ * <p>The CRUD methods each expect an {@link Ext.data.Operation Operation} object as the sole argument. The Operation encapsulates 
+ * information about the action the Store wishes to perform, the {@link Ext.data.Model model} instances that are to be modified, etc.
+ * See the {@link Ext.data.Operation Operation} documentation for more details. Each CRUD method also accepts a callback function to be 
+ * called asynchronously on completion.</p>
+ * 
+ * <p>Proxies also support batching of Operations via a {@link Ext.data.Batch batch} object, invoked by the {@link #batch} method.</p>
+ * 
+ * @constructor
+ * Creates the Proxy
+ * @param {Object} config Optional config object
+ */
+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'
+    },
+    
+    /**
+     * @cfg {String} batchOrder
+     * Comma-separated ordering 'create', 'update' and 'destroy' actions when batching. Override this
+     * to set a different order for the batched CRUD actions to be executed in. Defaults to 'create,update,destroy'
+     */
+    batchOrder: 'create,update,destroy',
+    
+    /**
+     * @cfg {Boolean} batchActions True to batch actions of a particular type when synchronizing the store.
+     * Defaults to <tt>true</tt>.
+     */
+    batchActions: true,
+    
+    /**
+     * @cfg {String} defaultReaderType The default registered reader type. Defaults to 'json'
+     * @private
+     */
+    defaultReaderType: 'json',
+    
+    /**
+     * @cfg {String} defaultWriterType The default registered writer type. Defaults to 'json'
+     * @private
+     */
+    defaultWriterType: 'json',
+    
+    /**
+     * @cfg {String/Ext.data.Model} model The name of the Model to tie to this Proxy. Can be either the string name of
+     * the Model, or a reference to the Model constructor. Required.
+     */
+    
+    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);
+        }
+    },
+    
+    /**
+     * Sets the model associated with this proxy. This will only usually be called by a Store
+     * @param {String|Ext.data.Model} model The new model. Can be either the model name string,
+     * or a reference to the model's constructor
+     * @param {Boolean} setOnStore Sets the new model on the associated Store, if one is present
+     */
+    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);
+        }
+    },
+    
+    /**
+     * Returns the model attached to this Proxy
+     * @return {Ext.data.Model} The model
+     */
+    getModel: function() {
+        return this.model;
+    },
+    
+    /**
+     * Sets the Proxy's Reader by string, config object or Reader instance
+     * @param {String|Object|Ext.data.reader.Reader} reader The new Reader, which can be either a type string, a configuration object
+     * or an Ext.data.reader.Reader instance
+     * @return {Ext.data.reader.Reader} The attached Reader object
+     */
+    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;
+    },
+    
+    /**
+     * Returns the reader currently attached to this proxy instance
+     * @return {Ext.data.reader.Reader} The Reader instance
+     */
+    getReader: function() {
+        return this.reader;
+    },
+    
+    /**
+     * Sets the Proxy's Writer by string, config object or Writer instance
+     * @param {String|Object|Ext.data.writer.Writer} writer The new Writer, which can be either a type string, a configuration object
+     * or an Ext.data.writer.Writer instance
+     * @return {Ext.data.writer.Writer} The attached Writer object
+     */
+    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;
+    },
+    
+    /**
+     * Returns the writer currently attached to this proxy instance
+     * @return {Ext.data.writer.Writer} The Writer instance
+     */
+    getWriter: function() {
+        return this.writer;
+    },
+    
+    /**
+     * Performs the given create operation.
+     * @param {Ext.data.Operation} operation The Operation to perform
+     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
+     * @param {Object} scope Scope to execute the callback function in
+     */
+    create: Ext.emptyFn,
+    
+    /**
+     * Performs the given read operation.
+     * @param {Ext.data.Operation} operation The Operation to perform
+     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
+     * @param {Object} scope Scope to execute the callback function in
+     */
+    read: Ext.emptyFn,
+    
+    /**
+     * Performs the given update operation.
+     * @param {Ext.data.Operation} operation The Operation to perform
+     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
+     * @param {Object} scope Scope to execute the callback function in
+     */
+    update: Ext.emptyFn,
+    
+    /**
+     * Performs the given destroy operation.
+     * @param {Ext.data.Operation} operation The Operation to perform
+     * @param {Function} callback Callback function to be called when the Operation has completed (whether successful or not)
+     * @param {Object} scope Scope to execute the callback function in
+     */
+    destroy: Ext.emptyFn,
+    
+    /**
+     * Performs a batch of {@link Ext.data.Operation Operations}, in the order specified by {@link #batchOrder}. Used internally by
+     * {@link Ext.data.Store}'s {@link Ext.data.Store#sync sync} method. Example usage:
+     * <pre><code>
+     * myProxy.batch({
+     *     create : [myModel1, myModel2],
+     *     update : [myModel3],
+     *     destroy: [myModel4, myModel5]
+     * });
+     * </code></pre>
+     * Where the myModel* above are {@link Ext.data.Model Model} instances - in this case 1 and 2 are new instances and have not been 
+     * saved before, 3 has been saved previously but needs to be updated, and 4 and 5 have already been saved but should now be destroyed.
+     * @param {Object} operations Object containing the Model instances to act upon, keyed by action name
+     * @param {Object} listeners Optional listeners object passed straight through to the Batch - see {@link Ext.data.Batch}
+     * @return {Ext.data.Batch} The newly created Ext.data.Batch object
+     */
+    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.proxy.ProxyMgr.registerType('proxy', this);
+    
+    //backwards compatibility
+    Ext.data.DataProxy = this;
+    // Ext.deprecate('platform', '2.0', function() {
+    //     Ext.data.DataProxy = this;
+    // }, this);
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.Server
+ * @extends Ext.data.proxy.Proxy
+ * 
+ * <p>ServerProxy is a superclass of {@link Ext.data.proxy.JsonP JsonPProxy} and {@link Ext.data.proxy.Ajax AjaxProxy},
+ * and would not usually be used directly.</p>
+ * 
+ * <p>ServerProxy should ideally be named HttpProxy as it is a superclass for all HTTP proxies - for Ext JS 4.x it has been 
+ * called ServerProxy to enable any 3.x applications that reference the HttpProxy to continue to work (HttpProxy is now an 
+ * alias of AjaxProxy).</p>
+ */
+Ext.define('Ext.data.proxy.Server', {
+    extend: 'Ext.data.proxy.Proxy',
+    alias : 'proxy.server',
+    alternateClassName: 'Ext.data.ServerProxy',
+    uses  : ['Ext.data.Request'],
+    
+    /**
+     * @cfg {String} url The URL from which to request the data object.
+     */
+    
+    /**
+     * @cfg {Object/String/Ext.data.reader.Reader} reader The Ext.data.reader.Reader to use to decode the server's response. This can
+     * either be a Reader instance, a config object or just a valid Reader type name (e.g. 'json', 'xml').
+     */
+    
+    /**
+     * @cfg {Object/String/Ext.data.writer.Writer} writer The Ext.data.writer.Writer to use to encode any request sent to the server.
+     * This can either be a Writer instance, a config object or just a valid Writer type name (e.g. 'json', 'xml').
+     */
+    
+    /**
+     * @cfg {String} pageParam The name of the 'page' parameter to send in a request. Defaults to 'page'. Set this to
+     * undefined if you don't want to send a page parameter
+     */
+    pageParam: 'page',
+    
+    /**
+     * @cfg {String} startParam The name of the 'start' parameter to send in a request. Defaults to 'start'. Set this
+     * to undefined if you don't want to send a start parameter
+     */
+    startParam: 'start',
+
+    /**
+     * @cfg {String} limitParam The name of the 'limit' parameter to send in a request. Defaults to 'limit'. Set this
+     * to undefined if you don't want to send a limit parameter
+     */
+    limitParam: 'limit',
+    
+    /**
+     * @cfg {String} groupParam The name of the 'group' parameter to send in a request. Defaults to 'group'. Set this
+     * to undefined if you don't want to send a group parameter
+     */
+    groupParam: 'group',
+    
+    /**
+     * @cfg {String} sortParam The name of the 'sort' parameter to send in a request. Defaults to 'sort'. Set this
+     * to undefined if you don't want to send a sort parameter
+     */
+    sortParam: 'sort',
+    
+    /**
+     * @cfg {String} filterParam The name of the 'filter' parameter to send in a request. Defaults to 'filter'. Set 
+     * this to undefined if you don't want to send a filter parameter
+     */
+    filterParam: 'filter',
+    
+    /**
+     * @cfg {String} directionParam The name of the direction parameter to send in a request. <strong>This is only used when simpleSortMode is set to true.</strong>
+     * Defaults to 'dir'.
+     */
+    directionParam: 'dir',
+    
+    /**
+     * @cfg {Boolean} simpleSortMode Enabling simpleSortMode in conjunction with remoteSort will only send one sort property and a direction when a remote sort is requested.
+     * The directionParam and sortParam will be sent with the property name and either 'ASC' or 'DESC'
+     */
+    simpleSortMode: false,
+    
+    /**
+     * @cfg {Boolean} noCache (optional) Defaults to true. Disable caching by adding a unique parameter
+     * name to the request.
+     */
+    noCache : true,
+    
+    /**
+     * @cfg {String} cacheString The name of the cache param added to the url when using noCache (defaults to "_dc")
+     */
+    cacheString: "_dc",
+    
+    /**
+     * @cfg {Number} timeout (optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
+     */
+    timeout : 30000,
+    
+    /**
+     * @cfg {Object} api
+     * Specific urls to call on CRUD action methods "read", "create", "update" and "destroy".
+     * Defaults to:<pre><code>
+api: {
+    read    : undefined,
+    create  : undefined,
+    update  : undefined,
+    destroy : undefined
+}
+     * </code></pre>
+     * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>
+     * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the
+     * configured {@link Ext.data.Store}.{@link Ext.data.proxy.Server#url url}.</p><br>
+     * <p>For example:</p>
+     * <pre><code>
+api: {
+    load :    '/controller/load',
+    create :  '/controller/new',
+    save :    '/controller/update',
+    destroy : '/controller/destroy_action'
+}
+     * </code></pre>
+     * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request
+     * will be directed to the configured <tt>{@link Ext.data.proxy.Server#url url}</tt>.</p>
+     */
+    
+    /**
+     * @ignore
+     */
+    constructor: function(config) {
+        var me = this;
+        
+        config = config || {};
+        this.addEvents(
+            /**
+             * @event exception
+             * Fires when the server returns an exception
+             * @param {Ext.data.proxy.Proxy} this
+             * @param {Object} response The response from the AJAX request
+             * @param {Ext.data.Operation} operation The operation that triggered request
+             */
+            'exception'
+        );
+        me.callParent([config]);
+        
+        /**
+         * @cfg {Object} extraParams Extra parameters that will be included on every request. Individual requests with params
+         * of the same name will override these params when they are in conflict.
+         */
+        me.extraParams = config.extraParams || {};
+        
+        me.api = config.api || {};
+        
+        //backwards compatibility, will be deprecated in 5.0
+        me.nocache = me.noCache;
+    },
+    
+    //in a ServerProxy all four CRUD operations are executed in the same manner, so we delegate to doRequest in each case
+    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);
+    },
+    
+    /**
+     * Creates and returns an Ext.data.Request object based on the options passed by the {@link Ext.data.Store Store}
+     * that this Proxy is attached to.
+     * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
+     * @return {Ext.data.Request} The request object
+     */
+    buildRequest: function(operation) {
+        var params = Ext.applyIf(operation.params || {}, this.extraParams || {}),
+            request;
+        
+        //copy any sorters, filters etc into the params so they can be sent over the wire
+        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);
+        
+        /*
+         * Save the request on the Operation. Operations don't usually care about Request and Response data, but in the
+         * ServerProxy and any of its subclasses we add both request and response as they may be useful for further processing
+         */
+        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);
+                    }
+                }
+                
+                //see comment in buildRequest for why we include the response object here
+                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);              
+        }
+            
+        //this callback is the one that was passed to the 'read' or 'write' function above
+        if (typeof callback == 'function') {
+            callback.call(scope || me, operation);
+        }
+            
+        me.afterRequest(request, success);
+    },
+    
+    /**
+     * Sets up an exception on the operation
+     * @private
+     * @param {Ext.data.Operation} operation The operation
+     * @param {Object} response The response
+     */
+    setException: function(operation, response){
+        operation.setException({
+            status: response.status,
+            statusText: response.statusText
+        });     
+    },
+    
+    /**
+     * Template method to allow subclasses to specify how to get the response for the reader.
+     * @private
+     * @param {Object} response The server response
+     * @return {Mixed} The response data to be used by the reader
+     */
+    extractResponseData: function(response){
+        return response; 
+    },
+    
+    /**
+     * Encode any values being sent to the server. Can be overridden in subclasses.
+     * @private
+     * @param {Array} An array of sorters/filters.
+     * @return {Mixed} The encoded value
+     */
+    applyEncoding: function(value){
+        return Ext.encode(value);
+    },
+    
+    /**
+     * Encodes the array of {@link Ext.util.Sorter} objects into a string to be sent in the request url. By default, 
+     * this simply JSON-encodes the sorter data
+     * @param {Array} sorters The array of {@link Ext.util.Sorter Sorter} objects
+     * @return {String} The encoded sorters
+     */
+    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);
+        
+    },
+    
+    /**
+     * Encodes the array of {@link Ext.util.Filter} objects into a string to be sent in the request url. By default, 
+     * this simply JSON-encodes the filter data
+     * @param {Array} sorters The array of {@link Ext.util.Filter Filter} objects
+     * @return {String} The encoded filters
+     */
+    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);
+    },
+    
+    /**
+     * @private
+     * Copy any sorters, filters etc into the params so they can be sent over the wire
+     */
+    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) {
+            // Grouper is a subclass of sorter, so we can just use the sorter method
+            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;
+    },
+    
+    /**
+     * Generates a url based on a given Ext.data.Request object. By default, ServerProxy's buildUrl will
+     * add the cache-buster param to the end of the url. Subclasses may need to perform additional modifications
+     * to the url.
+     * @param {Ext.data.Request} request The request object
+     * @return {String} The url
+     */
+    buildUrl: function(request) {
+        var me = this,
+            url = me.getUrl(request);
+        
+        //<debug>
+        if (!url) {
+            Ext.Error.raise("You are using a ServerProxy but have not supplied it with a url.");
+        }
+        //</debug>
+        
+        if (me.noCache) {
+            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.cacheString, Ext.Date.now()));
+        }
+        
+        return url;
+    },
+    
+    /**
+     * Get the url for the request taking into account the order of priority,
+     * - The request
+     * - The api
+     * - The url
+     * @private
+     * @param {Ext.data.Request} request The request
+     * @return {String} The url
+     */
+    getUrl: function(request){
+        return request.url || this.api[request.action] || this.url;
+    },
+    
+    /**
+     * In ServerProxy subclasses, the {@link #create}, {@link #read}, {@link #update} and {@link #destroy} methods all pass
+     * through to doRequest. Each ServerProxy subclass must implement the doRequest method - see {@link Ext.data.proxy.JsonP}
+     * and {@link Ext.data.proxy.Ajax} for examples. This method carries the same signature as each of the methods that delegate to it.
+     * @param {Ext.data.Operation} operation The Ext.data.Operation object
+     * @param {Function} callback The callback function to call when the Operation has completed
+     * @param {Object} scope The scope in which to execute the callback
+     */
+    doRequest: function(operation, callback, scope) {
+        //<debug>
+        Ext.Error.raise("The doRequest function has not been implemented on your Ext.data.proxy.Server subclass. See src/data/ServerProxy.js for details");
+        //</debug>
+    },
+    
+    /**
+     * Optional callback function which can be used to clean up after a request has been completed.
+     * @param {Ext.data.Request} request The Request object
+     * @param {Boolean} success True if the request was successful
+     */
+    afterRequest: Ext.emptyFn,
+    
+    onDestroy: function() {
+        Ext.destroy(this.reader, this.writer);
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.Ajax
+ * @extends Ext.data.proxy.Server
+ * 
+ * <p>AjaxProxy is one of the most widely-used ways of getting data into your application. It uses AJAX requests to 
+ * load data from the server, usually to be placed into a {@link Ext.data.Store Store}. Let's take a look at a typical
+ * setup. Here we're going to set up a Store that has an AjaxProxy. To prepare, we'll also set up a 
+ * {@link Ext.data.Model Model}:</p>
+ * 
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'email']
+});
+
+//The Store contains the AjaxProxy as an inline configuration
+var store = new Ext.data.Store({
+    model: 'User',
+    proxy: {
+        type: 'ajax',
+        url : 'users.json'
+    }
+});
+
+store.load();
+</code></pre>
+ * 
+ * <p>Our example is going to load user data into a Store, so we start off by defining a {@link Ext.data.Model Model}
+ * with the fields that we expect the server to return. Next we set up the Store itself, along with a {@link #proxy}
+ * configuration. This configuration was automatically turned into an Ext.data.proxy.Ajax instance, with the url we
+ * specified being passed into AjaxProxy's constructor. It's as if we'd done this:</p>
+ * 
+<pre><code>
+new Ext.data.proxy.Ajax({
+    url: 'users.json',
+    model: 'User',
+    reader: 'json'
+});
+</code></pre>
+ * 
+ * <p>A couple of extra configurations appeared here - {@link #model} and {@link #reader}. These are set by default 
+ * when we create the proxy via the Store - the Store already knows about the Model, and Proxy's default 
+ * {@link Ext.data.reader.Reader Reader} is {@link Ext.data.reader.Json JsonReader}.</p>
+ * 
+ * <p>Now when we call store.load(), the AjaxProxy springs into action, making a request to the url we configured
+ * ('users.json' in this case). As we're performing a read, it sends a GET request to that url (see {@link #actionMethods}
+ * to customize this - by default any kind of read will be sent as a GET request and any kind of write will be sent as a
+ * POST request).</p>
+ * 
+ * <p><u>Limitations</u></p>
+ * 
+ * <p>AjaxProxy cannot be used to retrieve data from other domains. If your application is running on http://domainA.com
+ * it cannot load data from http://domainB.com because browsers have a built-in security policy that prohibits domains
+ * talking to each other via AJAX.</p>
+ * 
+ * <p>If you need to read data from another domain and can't set up a proxy server (some software that runs on your own
+ * domain's web server and transparently forwards requests to http://domainB.com, making it look like they actually came
+ * from http://domainA.com), you can use {@link Ext.data.proxy.JsonP} and a technique known as JSON-P (JSON with 
+ * Padding), which can help you get around the problem so long as the server on http://domainB.com is set up to support
+ * JSON-P responses. See {@link Ext.data.proxy.JsonP JsonPProxy}'s introduction docs for more details.</p>
+ * 
+ * <p><u>Readers and Writers</u></p>
+ * 
+ * <p>AjaxProxy can be configured to use any type of {@link Ext.data.reader.Reader Reader} to decode the server's response. If
+ * no Reader is supplied, AjaxProxy will default to using a {@link Ext.data.reader.Json JsonReader}. Reader configuration
+ * can be passed in as a simple object, which the Proxy automatically turns into a {@link Ext.data.reader.Reader Reader}
+ * instance:</p>
+ * 
+<pre><code>
+var proxy = new Ext.data.proxy.Ajax({
+    model: 'User',
+    reader: {
+        type: 'xml',
+        root: 'users'
+    }
+});
+
+proxy.getReader(); //returns an {@link Ext.data.reader.Xml XmlReader} instance based on the config we supplied
+</code></pre>
+ * 
+ * <p><u>Url generation</u></p>
+ * 
+ * <p>AjaxProxy automatically inserts any sorting, filtering, paging and grouping options into the url it generates for
+ * each request. These are controlled with the following configuration options:</p>
+ * 
+ * <ul style="list-style-type: disc; padding-left: 20px;">
+ *     <li>{@link #pageParam} - controls how the page number is sent to the server 
+ *     (see also {@link #startParam} and {@link #limitParam})</li>
+ *     <li>{@link #sortParam} - controls how sort information is sent to the server</li>
+ *     <li>{@link #groupParam} - controls how grouping information is sent to the server</li>
+ *     <li>{@link #filterParam} - controls how filter information is sent to the server</li>
+ * </ul>
+ * 
+ * <p>Each request sent by AjaxProxy is described by an {@link Ext.data.Operation Operation}. To see how we can 
+ * customize the generated urls, let's say we're loading the Proxy with the following Operation:</p>
+ * 
+<pre><code>
+var operation = new Ext.data.Operation({
+    action: 'read',
+    page  : 2
+});
+</code></pre>
+ * 
+ * <p>Now we'll issue the request for this Operation by calling {@link #read}:</p>
+ * 
+<pre><code>
+var proxy = new Ext.data.proxy.Ajax({
+    url: '/users'
+});
+
+proxy.read(operation); //GET /users?page=2
+</code></pre>
+ * 
+ * <p>Easy enough - the Proxy just copied the page property from the Operation. We can customize how this page data is
+ * sent to the server:</p>
+ * 
+<pre><code>
+var proxy = new Ext.data.proxy.Ajax({
+    url: '/users',
+    pagePage: 'pageNumber'
+});
+
+proxy.read(operation); //GET /users?pageNumber=2
+</code></pre>
+ * 
+ * <p>Alternatively, our Operation could have been configured to send start and limit parameters instead of page:</p>
+ * 
+<pre><code>
+var operation = new Ext.data.Operation({
+    action: 'read',
+    start : 50,
+    limit : 25
+});
+
+var proxy = new Ext.data.proxy.Ajax({
+    url: '/users'
+});
+
+proxy.read(operation); //GET /users?start=50&limit=25
+</code></pre>
+ * 
+ * <p>Again we can customize this url:</p>
+ * 
+<pre><code>
+var proxy = new Ext.data.proxy.Ajax({
+    url: '/users',
+    startParam: 'startIndex',
+    limitParam: 'limitIndex'
+});
+
+proxy.read(operation); //GET /users?startIndex=50&limitIndex=25
+</code></pre>
+ * 
+ * <p>AjaxProxy will also send sort and filter information to the server. Let's take a look at how this looks with a
+ * more expressive Operation object:</p>
+ * 
+<pre><code>
+var operation = new Ext.data.Operation({
+    action: 'read',
+    sorters: [
+        new Ext.util.Sorter({
+            property : 'name',
+            direction: 'ASC'
+        }),
+        new Ext.util.Sorter({
+            property : 'age',
+            direction: 'DESC'
+        })
+    ],
+    filters: [
+        new Ext.util.Filter({
+            property: 'eyeColor',
+            value   : 'brown'
+        })
+    ]
+});
+</code></pre>
+ * 
+ * <p>This is the type of object that is generated internally when loading a {@link Ext.data.Store Store} with sorters
+ * and filters defined. By default the AjaxProxy will JSON encode the sorters and filters, resulting in something like
+ * this (note that the url is escaped before sending the request, but is left unescaped here for clarity):</p>
+ * 
+<pre><code>
+var proxy = new Ext.data.proxy.Ajax({
+    url: '/users'
+});
+
+proxy.read(operation); //GET /users?sort=[{"property":"name","direction":"ASC"},{"property":"age","direction":"DESC"}]&filter=[{"property":"eyeColor","value":"brown"}]
+</code></pre>
+ * 
+ * <p>We can again customize how this is created by supplying a few configuration options. Let's say our server is set 
+ * up to receive sorting information is a format like "sortBy=name#ASC,age#DESC". We can configure AjaxProxy to provide
+ * that format like this:</p>
+ * 
+ <pre><code>
+ var proxy = new Ext.data.proxy.Ajax({
+     url: '/users',
+     sortParam: 'sortBy',
+     filterParam: 'filterBy',
+
+     //our custom implementation of sorter encoding - turns our sorters into "name#ASC,age#DESC"
+     encodeSorters: function(sorters) {
+         var length   = sorters.length,
+             sortStrs = [],
+             sorter, i;
+
+         for (i = 0; i < length; i++) {
+             sorter = sorters[i];
+
+             sortStrs[i] = sorter.property + '#' + sorter.direction
+         }
+
+         return sortStrs.join(",");
+     }
+ });
+
+ proxy.read(operation); //GET /users?sortBy=name#ASC,age#DESC&filterBy=[{"property":"eyeColor","value":"brown"}]
+ </code></pre>
+ * 
+ * <p>We can also provide a custom {@link #encodeFilters} function to encode our filters.</p>
+ * 
+ * @constructor
+ * 
+ * <p>Note that if this HttpProxy is being used by a {@link Ext.data.Store Store}, then the
+ * Store's call to {@link #load} will override any specified <tt>callback</tt> and <tt>params</tt>
+ * options. In this case, use the Store's {@link Ext.data.Store#events events} to modify parameters,
+ * or react to loading events. The Store's {@link Ext.data.Store#baseParams baseParams} may also be
+ * used to pass parameters known at instantiation time.</p>
+ * 
+ * <p>If an options parameter is passed, the singleton {@link Ext.Ajax} object will be used to make
+ * the request.</p>
+ */
+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'],
+    
+    /**
+     * @property actionMethods
+     * Mapping of action name to HTTP request method. In the basic AjaxProxy these are set to 'GET' for 'read' actions and 'POST' 
+     * for 'create', 'update' and 'destroy' actions. The {@link Ext.data.proxy.Rest} maps these to the correct RESTful methods.
+     */
+    actionMethods: {
+        create : 'POST',
+        read   : 'GET',
+        update : 'POST',
+        destroy: 'POST'
+    },
+    
+    /**
+     * @cfg {Object} headers Any headers to add to the Ajax request. Defaults to <tt>undefined</tt>.
+     */
+    
+    /**
+     * @ignore
+     */
+    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 // explicitly set it to false, ServerProxy handles caching
+        });
+        
+        Ext.Ajax.request(request);
+        
+        return request;
+    },
+    
+    /**
+     * Returns the HTTP method name for a given request. By default this returns based on a lookup on {@link #actionMethods}.
+     * @param {Ext.data.Request} request The request object
+     * @return {String} The HTTP method to use (should be one of 'GET', 'POST', 'PUT' or 'DELETE')
+     */
+    getMethod: function(request) {
+        return this.actionMethods[request.action];
+    },
+    
+    /**
+     * @private
+     * TODO: This is currently identical to the JsonPProxy version except for the return function's signature. There is a lot
+     * of code duplication inside the returned function so we need to find a way to DRY this up.
+     * @param {Ext.data.Request} request The Request object
+     * @param {Ext.data.Operation} operation The Operation being executed
+     * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
+     * passed to doRequest
+     * @param {Object} scope The scope in which to execute the callback function
+     * @return {Function} The callback function
+     */
+    createRequestCallback: function(request, operation, callback, scope) {
+        var me = this;
+        
+        return function(options, success, response) {
+            me.processResponse(success, operation, request, response, callback, scope);
+        };
+    }
+}, function() {
+    //backwards compatibility, remove in Ext JS 5.0
+    Ext.data.HttpProxy = this;
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.Model
+ *
+ * <p>A Model represents some object that your application manages. For example, one might define a Model for Users, Products,
+ * Cars, or any other real-world object that we want to model in the system. Models are registered via the {@link Ext.ModelManager model manager},
+ * and are used by {@link Ext.data.Store stores}, which are in turn used by many of the data-bound components in Ext.</p>
+ *
+ * <p>Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:</p>
+ *
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'name',  type: 'string'},
+        {name: 'age',   type: 'int'},
+        {name: 'phone', type: 'string'},
+        {name: 'alive', type: 'boolean', defaultValue: true}
+    ],
+
+    changeName: function() {
+        var oldName = this.get('name'),
+            newName = oldName + " The Barbarian";
+
+        this.set('name', newName);
+    }
+});
+</code></pre>
+*
+* <p>The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link Ext.ModelManager ModelManager}, and all
+* other functions and properties are copied to the new Model's prototype.</p>
+*
+* <p>Now we can create instances of our User model and call any model logic we defined:</p>
+*
+<pre><code>
+var user = Ext.ModelManager.create({
+    name : 'Conan',
+    age  : 24,
+    phone: '555-555-5555'
+}, 'User');
+
+user.changeName();
+user.get('name'); //returns "Conan The Barbarian"
+</code></pre>
+ *
+ * <p><u>Validations</u></p>
+ *
+ * <p>Models have built-in support for validations, which are executed against the validator functions in
+ * {@link Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to models:</p>
+ *
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'name',     type: 'string'},
+        {name: 'age',      type: 'int'},
+        {name: 'phone',    type: 'string'},
+        {name: 'gender',   type: 'string'},
+        {name: 'username', type: 'string'},
+        {name: 'alive',    type: 'boolean', defaultValue: true}
+    ],
+
+    validations: [
+        {type: 'presence',  field: 'age'},
+        {type: 'length',    field: 'name',     min: 2},
+        {type: 'inclusion', field: 'gender',   list: ['Male', 'Female']},
+        {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
+        {type: 'format',    field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
+    ]
+});
+</code></pre>
+ *
+ * <p>The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
+ * object:</p>
+ *
+<pre><code>
+var instance = Ext.ModelManager.create({
+    name: 'Ed',
+    gender: 'Male',
+    username: 'edspencer'
+}, 'User');
+
+var errors = instance.validate();
+</code></pre>
+ *
+ * <p><u>Associations</u></p>
+ *
+ * <p>Models can have associations with other Models via {@link Ext.data.BelongsToAssociation belongsTo} and
+ * {@link Ext.data.HasManyAssociation hasMany} associations. For example, let's say we're writing a blog administration
+ * application which deals with Users, Posts and Comments. We can express the relationships between these models like this:</p>
+ *
+<pre><code>
+Ext.define('Post', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'user_id'],
+
+    belongsTo: 'User',
+    hasMany  : {model: 'Comment', name: 'comments'}
+});
+
+Ext.define('Comment', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'user_id', 'post_id'],
+
+    belongsTo: 'Post'
+});
+
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id'],
+
+    hasMany: [
+        'Post',
+        {model: 'Comment', name: 'comments'}
+    ]
+});
+</code></pre>
+ *
+ * <p>See the docs for {@link Ext.data.BelongsToAssociation} and {@link Ext.data.HasManyAssociation} for details on the usage
+ * and configuration of associations. Note that associations can also be specified like this:</p>
+ *
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id'],
+
+    associations: [
+        {type: 'hasMany', model: 'Post',    name: 'posts'},
+        {type: 'hasMany', model: 'Comment', name: 'comments'}
+    ]
+});
+</code></pre>
+ *
+ * <p><u>Using a Proxy</u></p>
+ *
+ * <p>Models are great for representing types of data and relationships, but sooner or later we're going to want to
+ * load or save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.proxy.Proxy Proxy},
+ * which can be set directly on the Model:</p>
+ *
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'email'],
+
+    proxy: {
+        type: 'rest',
+        url : '/users'
+    }
+});
+</code></pre>
+ *
+ * <p>Here we've set up a {@link Ext.data.proxy.Rest Rest Proxy}, which knows how to load and save data to and from a
+ * RESTful backend. Let's see how this works:</p>
+ *
+<pre><code>
+var user = Ext.ModelManager.create({name: 'Ed Spencer', email: 'ed@sencha.com'}, 'User');
+
+user.save(); //POST /users
+</code></pre>
+ *
+ * <p>Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this
+ * Model's data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't
+ * have an id, and performs the appropriate action - in this case issuing a POST request to the url we configured
+ * (/users). We configure any Proxy on any Model and always follow this API - see {@link Ext.data.proxy.Proxy} for a full
+ * list.</p>
+ *
+ * <p>Loading data via the Proxy is equally easy:</p>
+ *
+<pre><code>
+//get a reference to the User model class
+var User = Ext.ModelManager.getModel('User');
+
+//Uses the configured RestProxy to make a GET request to /users/123
+User.load(123, {
+    success: function(user) {
+        console.log(user.getId()); //logs 123
+    }
+});
+</code></pre>
+ *
+ * <p>Models can also be updated and destroyed easily:</p>
+ *
+<pre><code>
+//the user Model we loaded in the last snippet:
+user.set('name', 'Edward Spencer');
+
+//tells the Proxy to save the Model. In this case it will perform a PUT request to /users/123 as this Model already has an id
+user.save({
+    success: function() {
+        console.log('The User was updated');
+    }
+});
+
+//tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
+user.destroy({
+    success: function() {
+        console.log('The User was destroyed!');
+    }
+});
+</code></pre>
+ *
+ * <p><u>Usage in Stores</u></p>
+ *
+ * <p>It is very common to want to load a set of Model instances to be displayed and manipulated in the UI. We do this
+ * by creating a {@link Ext.data.Store Store}:</p>
+ *
+<pre><code>
+var store = new Ext.data.Store({
+    model: 'User'
+});
+
+//uses the Proxy we set up on Model to load the Store data
+store.load();
+</code></pre>
+ *
+ * <p>A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain
+ * a set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the
+ * {@link Ext.data.Store Store docs} for more information on Stores.</p>
+ *
+ * @constructor
+ * @param {Object} data An object containing keys corresponding to this model's fields, and their associated values
+ * @param {Number} id Optional unique ID to assign to this model instance
+ */
+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 = [];
+
+            // Save modelName on class and its prototype
+            cls.modelName = name;
+            prototype.modelName = name;
+
+            // Merge the validations of the superclass and the new subclass
+            if (superValidations) {
+                validations = superValidations.concat(validations);
+            }
+
+            data.validations = validations;
+
+            // Merge the fields of the superclass and the new subclass
+            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;
+
+            //associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
+            //we support that here
+            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);
+
+                // Fire the onModelDefined template method on ModelManager
+                Ext.ModelManager.onModelDefined(cls);
+            });
+        }
+    },
+
+    inheritableStatics: {
+        /**
+         * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext#createByAlias Ext.createByAlias}
+         * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
+         * @static
+         */
+        setProxy: function(proxy) {
+            //make sure we have an Ext.data.proxy.Proxy object
+            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;
+        },
+
+        /**
+         * Returns the configured Proxy for this Model
+         * @return {Ext.data.proxy.Proxy} The proxy
+         */
+        getProxy: function() {
+            return this.proxy;
+        },
+
+        /**
+         * <b>Static</b>. Asynchronously loads a model instance by id. Sample usage:
+    <pre><code>
+    MyApp.User = Ext.define('User', {
+        extend: 'Ext.data.Model',
+        fields: [
+            {name: 'id', type: 'int'},
+            {name: 'name', type: 'string'}
+        ]
+    });
+
+    MyApp.User.load(10, {
+        scope: this,
+        failure: function(record, operation) {
+            //do something if the load failed
+        },
+        success: function(record, operation) {
+            //do something if the load succeeded
+        },
+        callback: function(record, operation) {
+            //do something whether the load succeeded or failed
+        }
+    });
+    </code></pre>
+         * @param {Number} id The id of the model to load
+         * @param {Object} config Optional config object containing success, failure and callback functions, plus optional scope
+         * @member Ext.data.Model
+         * @method load
+         * @static
+         */
+        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',
+
+        /**
+         * Generates a sequential id. This method is typically called when a record is {@link #create}d
+         * and {@link #Record no id has been specified}. The id will automatically be assigned
+         * to the record. The returned id takes the form:
+         * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
+         * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.PREFIX</tt>
+         * (defaults to <tt>'ext-record'</tt>)</p></li>
+         * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.AUTO_ID</tt>
+         * (defaults to <tt>1</tt> initially)</p></li>
+         * </ul></div>
+         * @param {Ext.data.Model} rec The record being created.  The record does not exist, it's a {@link #phantom}.
+         * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
+         * @static
+         */
+        id: function(rec) {
+            var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
+            rec.phantom = true;
+            rec.internalId = id;
+            return id;
+        }
+    },
+    
+    /**
+     * Internal flag used to track whether or not the model instance is currently being edited. Read-only
+     * @property editing
+     * @type Boolean
+     */
+    editing : false,
+
+    /**
+     * Readonly flag - true if this Record has been modified.
+     * @type Boolean
+     */
+    dirty : false,
+
+    /**
+     * @cfg {String} persistanceProperty The property on this Persistable object that its data is saved to.
+     * Defaults to 'data' (e.g. all persistable data resides in this.data.)
+     */
+    persistanceProperty: 'data',
+
+    evented: false,
+    isModel: true,
+
+    /**
+     * <tt>true</tt> when the record does not yet exist in a server-side database (see
+     * {@link #setDirty}).  Any record which has a real database pk set as its id property
+     * is NOT a phantom -- it's real.
+     * @property phantom
+     * @type {Boolean}
+     */
+    phantom : false,
+
+    /**
+     * @cfg {String} idProperty The name of the field treated as this Model's unique id (defaults to 'id').
+     */
+    idProperty: 'id',
+
+    /**
+     * The string type of the default Model Proxy. Defaults to 'ajax'
+     * @property defaultProxyType
+     * @type String
+     */
+    defaultProxyType: 'ajax',
+
+    /**
+     * An array of the fields defined on this model
+     * @property fields
+     * @type {Array}
+     */
+
+    constructor: function(data, id) {
+        data = data || {};
+        
+        var me = this,
+            fields,
+            length,
+            field,
+            name,
+            i,
+            isArray = Ext.isArray(data),
+            newData = isArray ? {} : null; // to hold mapped array data if needed
+
+        /**
+         * An internal unique ID for each Model instance, used to identify Models that don't have an ID yet
+         * @property internalId
+         * @type String
+         * @private
+         */
+        me.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);
+
+        Ext.applyIf(me, {
+            data: {}    
+        });
+        
+        /**
+         * Key: value pairs of all fields whose values have changed
+         * @property modified
+         * @type Object
+         */
+        me.modified = {};
+
+        me[me.persistanceProperty] = {};
+
+        me.mixins.observable.constructor.call(me);
+
+        //add default field values if present
+        fields = me.fields.items;
+        length = fields.length;
+
+        for (i = 0; i < length; i++) {
+            field = fields[i];
+            name  = field.name;
+
+            if (isArray){ 
+                // Have to map array data so the values get assigned to the named fields
+                // rather than getting set as the field names with undefined values.
+                newData[name] = data[i];
+            }
+            else if (data[name] === undefined) {
+                data[name] = field.defaultValue;
+            }
+        }
+
+        me.set(newData || data);
+        // clear any dirty/modified since we're initializing
+        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);
+    },
+    
+    /**
+     * Returns the value of the given field
+     * @param {String} fieldName The field to fetch the value for
+     * @return {Mixed} The value
+     */
+    get: function(field) {
+        return this[this.persistanceProperty][field];
+    },
+    
+    /**
+     * Sets the given field to the given value, marks the instance as dirty
+     * @param {String|Object} fieldName The field to set, or an object containing key/value pairs
+     * @param {Mixed} value The value to set
+     */
+    set: function(fieldName, value) {
+        var me = this,
+            fields = me.fields,
+            modified = me.modified,
+            convertFields = [],
+            field, key, i, currentValue;
+
+        /*
+         * If we're passed an object, iterate over that object. NOTE: we pull out fields with a convert function and
+         * set those last so that all other possible data is set before the convert function is called
+         */
+        if (arguments.length == 1 && Ext.isObject(fieldName)) {
+            for (key in fieldName) {
+                if (fieldName.hasOwnProperty(key)) {
+                
+                    //here we check for the custom convert function. Note that if a field doesn't have a convert function,
+                    //we default it to its type's convert function, so we have to check that here. This feels rather dirty.
+                    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();
+            }
+        }
+    },
+    
+    /**
+     * Checks if two values are equal, taking into account certain
+     * special factors, for example dates.
+     * @private
+     * @param {Object} a The first value
+     * @param {Object} b The second value
+     * @return {Boolean} True if the values are equal
+     */
+    isEqual: function(a, b){
+        if (Ext.isDate(a) && Ext.isDate(b)) {
+            return a.getTime() === b.getTime();
+        }
+        return a === b;
+    },
+    
+    /**
+     * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
+     * are relayed to the containing store. When an edit has begun, it must be followed
+     * by either {@link #endEdit} or {@link #cancelEdit}.
+     */
+    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);
+        }
+    },
+    
+    /**
+     * Cancels all changes made in the current edit operation.
+     */
+    cancelEdit : function(){
+        var me = this;
+        if (me.editing) {
+            me.editing = false;
+            // reset the modified state, nothing changed since the edit began
+            me.modified = me.modifiedSave;
+            me[me.persistanceProperty] = me.dataSave;
+            me.dirty = me.dirtySave;
+            delete me.modifiedSave;
+            delete me.dataSave;
+            delete me.dirtySave;
+        }
+    },
+    
+    /**
+     * End an edit. If any data was modified, the containing store is notified
+     * (ie, the store's <code>update</code> event will fire).
+     * @param {Boolean} silent True to not notify the store of the change
+     */
+    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();
+            }
+        }
+    },
+    
+    /**
+     * Gets a hash of only the fields that have been modified since this Model was created or commited.
+     * @return Object
+     */
+    getChanges : function(){
+        var modified = this.modified,
+            changes  = {},
+            field;
+
+        for (field in modified) {
+            if (modified.hasOwnProperty(field)){
+                changes[field] = this.get(field);
+            }
+        }
+
+        return changes;
+    },
+    
+    /**
+     * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
+     * since the load or last commit.
+     * @param {String} fieldName {@link Ext.data.Field#name}
+     * @return {Boolean}
+     */
+    isModified : function(fieldName) {
+        return this.modified.hasOwnProperty(fieldName);
+    },
+    
+    /**
+     * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>.  This method
+     * is used interally when adding <code>{@link #phantom}</code> records to a
+     * {@link Ext.data.Store#writer writer enabled store}.</p>
+     * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
+     * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
+     * have a create action composed for it during {@link Ext.data.Store#save store save}
+     * operations.</p>
+     */
+    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);
+    },
+
+    //<debug>
+    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);
+    },
+    //</debug>
+    
+    /**
+     * Usually called by the {@link Ext.data.Store} to which this model instance has been {@link #join joined}.
+     * Rejects all changes made to the model instance since either creation, or the last commit operation.
+     * Modified fields are reverted to their original values.
+     * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
+     * to have their code notified of reject operations.</p>
+     * @param {Boolean} silent (optional) True to skip notification of the owning
+     * store of the change (defaults to false)
+     */
+    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();
+        }
+    },
+
+    /**
+     * Usually called by the {@link Ext.data.Store} which owns the model instance.
+     * Commits all changes made to the instance since either creation or the last commit operation.
+     * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
+     * to have their code notified of commit operations.</p>
+     * @param {Boolean} silent (optional) True to skip notification of the owning
+     * store of the change (defaults to false)
+     */
+    commit : function(silent) {
+        var me = this;
+        
+        me.dirty = false;
+        me.editing = false;
+
+        me.modified = {};
+
+        if (silent !== true) {
+            me.afterCommit();
+        }
+    },
+
+    /**
+     * Creates a copy (clone) of this Model instance.
+     * @param {String} id (optional) A new id, defaults to the id
+     * of the instance being copied. See <code>{@link #id}</code>.
+     * To generate a phantom instance with a new id use:<pre><code>
+var rec = record.copy(); // clone the record
+Ext.data.Model.id(rec); // automatically generate a unique sequential id
+     * </code></pre>
+     * @return {Record}
+     */
+    copy : function(newId) {
+        var me = this;
+        
+        return new me.self(Ext.apply({}, me[me.persistanceProperty]), newId || me.internalId);
+    },
+
+    /**
+     * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext#createByAlias Ext.createByAlias}
+     * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
+     * @static
+     */
+    setProxy: function(proxy) {
+        //make sure we have an Ext.data.proxy.Proxy object
+        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;
+    },
+
+    /**
+     * Returns the configured Proxy for this Model
+     * @return {Ext.data.proxy.Proxy} The proxy
+     */
+    getProxy: function() {
+        return this.proxy;
+    },
+
+    /**
+     * Validates the current data against all of its configured {@link #validations} and returns an
+     * {@link Ext.data.Errors Errors} object
+     * @return {Ext.data.Errors} The errors object
+     */
+    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;
+    },
+
+    /**
+     * Checks if the model is valid. See {@link #validate}.
+     * @return {Boolean} True if the model is valid.
+     */
+    isValid: function(){
+        return this.validate().isValid();
+    },
+
+    /**
+     * Saves the model instance using the configured proxy
+     * @param {Object} options Options to pass to the proxy
+     * @return {Ext.data.Model} The Model instance
+     */
+    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];
+                //we need to make sure we've set the updated data here. Ideally this will be redundant once the
+                //ModelCache is in place
+                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;
+    },
+
+    /**
+     * Destroys the model using the configured proxy
+     * @param {Object} options Options to pass to the proxy
+     * @return {Ext.data.Model} The Model instance
+     */
+    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;
+    },
+
+    /**
+     * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}
+     * @return {Number} The id
+     */
+    getId: function() {
+        return this.get(this.idProperty);
+    },
+
+    /**
+     * Sets the model instance's id field to the given id
+     * @param {Number} id The new id
+     */
+    setId: function(id) {
+        this.set(this.idProperty, id);
+    },
+
+    /**
+     * Tells this model instance that it has been added to a store
+     * @param {Ext.data.Store} store The store that the model has been added to
+     */
+    join : function(store) {
+        /**
+         * The {@link Ext.data.Store} to which this Record belongs.
+         * @property store
+         * @type {Ext.data.Store}
+         */
+        this.store = store;
+    },
+
+    /**
+     * Tells this model instance that it has been removed from the store
+     */
+    unjoin: function() {
+        delete this.store;
+    },
+
+    /**
+     * @private
+     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
+     * afterEdit method is called
+     */
+    afterEdit : function() {
+        this.callStore('afterEdit');
+    },
+
+    /**
+     * @private
+     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
+     * afterReject method is called
+     */
+    afterReject : function() {
+        this.callStore("afterReject");
+    },
+
+    /**
+     * @private
+     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
+     * afterCommit method is called
+     */
+    afterCommit: function() {
+        this.callStore('afterCommit');
+    },
+
+    /**
+     * @private
+     * Helper function used by afterEdit, afterReject and afterCommit. Calls the given method on the
+     * {@link Ext.data.Store store} that this instance has {@link #join joined}, if any. The store function
+     * will always be called with the model instance as its single argument.
+     * @param {String} fn The function to call on the store
+     */
+    callStore: function(fn) {
+        var store = this.store;
+
+        if (store !== undefined && typeof store[fn] == "function") {
+            store[fn](this);
+        }
+    },
+
+    /**
+     * Gets all of the data from this Models *loaded* associations.
+     * It does this recursively - for example if we have a User which
+     * hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
+     * {
+     *     orders: [
+     *         {
+     *             id: 123,
+     *             status: 'shipped',
+     *             orderItems: [
+     *                 ...
+     *             ]
+     *         }
+     *     ]
+     * }
+     * @return {Object} The nested data set for the Model's loaded associations
+     */
+    getAssociatedData: function(){
+        return this.prepareAssociatedData(this, [], null);
+    },
+
+    /**
+     * @private
+     * This complex-looking method takes a given Model instance and returns an object containing all data from
+     * all of that Model's *loaded* associations. See (@link #getAssociatedData}
+     * @param {Ext.data.Model} record The Model instance
+     * @param {Array} ids PRIVATE. The set of Model instance internalIds that have already been loaded
+     * @param {String} associationType (optional) The name of the type of association to limit to.
+     * @return {Object} The nested data set for the Model's loaded associations
+     */
+    prepareAssociatedData: function(record, ids, associationType) {
+        //we keep track of all of the internalIds of the models that we have loaded so far in here
+        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') {
+
+                //this is the hasMany store filled with the associated data
+                associatedStore = record[association.storeName];
+
+                //we will use this to contain each associated record's data
+                associationData[association.name] = [];
+
+                //if it's loaded, put it into the association data
+                if (associatedStore && associatedStore.data.length > 0) {
+                    associatedRecords = associatedStore.data.items;
+                    associatedRecordCount = associatedRecords.length;
+
+                    //now we're finally iterating over the records in the association. We do this recursively
+                    for (j = 0; j < associatedRecordCount; j++) {
+                        associatedRecord = associatedRecords[j];
+                        // Use the id, since it is prefixed with the model name, guaranteed to be unique
+                        id = associatedRecord.id;
+
+                        //when we load the associations for a specific model instance we add it to the set of loaded ids so that
+                        //we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
+                        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;
+    }
+});
+
+/**
+ * @class Ext.Component
+ * @extends Ext.AbstractComponent
+ * <p>Base class for all Ext components.  All subclasses of Component may participate in the automated
+ * Ext component lifecycle of creation, rendering and destruction which is provided by the {@link Ext.container.Container Container} class.
+ * Components may be added to a Container through the {@link Ext.container.Container#items items} config option at the time the Container is created,
+ * or they may be added dynamically via the {@link Ext.container.Container#add add} method.</p>
+ * <p>The Component base class has built-in support for basic hide/show and enable/disable and size control behavior.</p>
+ * <p>All Components are registered with the {@link Ext.ComponentManager} on construction so that they can be referenced at any time via
+ * {@link Ext#getCmp Ext.getCmp}, passing the {@link #id}.</p>
+ * <p>All user-developed visual widgets that are required to participate in automated lifecycle and size management should subclass Component.</p>
+ * <p>See the <a href="http://sencha.com/learn/Tutorial:Creating_new_UI_controls">Creating new UI controls</a> tutorial for details on how
+ * and to either extend or augment ExtJs base classes to create custom Components.</p>
+ * <p>Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the
+ * xtype like {@link #getXType} and {@link #isXType}. This is the list of all valid xtypes:</p>
+ * <pre>
+xtype            Class
+-------------    ------------------
+button           {@link Ext.button.Button}
+buttongroup      {@link Ext.container.ButtonGroup}
+colorpalette     {@link Ext.picker.Color}
+component        {@link Ext.Component}
+container        {@link Ext.container.Container}
+cycle            {@link Ext.button.Cycle}
+dataview         {@link Ext.view.View}
+datepicker       {@link Ext.picker.Date}
+editor           {@link Ext.Editor}
+editorgrid       {@link Ext.grid.plugin.Editing}
+grid             {@link Ext.grid.Panel}
+multislider      {@link Ext.slider.Multi}
+panel            {@link Ext.panel.Panel}
+progress         {@link Ext.ProgressBar}
+slider           {@link Ext.slider.Single}
+spacer           {@link Ext.toolbar.Spacer}
+splitbutton      {@link Ext.button.Split}
+tabpanel         {@link Ext.tab.Panel}
+treepanel        {@link Ext.tree.Panel}
+viewport         {@link Ext.container.Viewport}
+window           {@link Ext.window.Window}
+
+Toolbar components
+---------------------------------------
+paging           {@link Ext.toolbar.Paging}
+toolbar          {@link Ext.toolbar.Toolbar}
+tbfill           {@link Ext.toolbar.Fill}
+tbitem           {@link Ext.toolbar.Item}
+tbseparator      {@link Ext.toolbar.Separator}
+tbspacer         {@link Ext.toolbar.Spacer}
+tbtext           {@link Ext.toolbar.TextItem}
+
+Menu components
+---------------------------------------
+menu             {@link Ext.menu.Menu}
+menucheckitem    {@link Ext.menu.CheckItem}
+menuitem         {@link Ext.menu.Item}
+menuseparator    {@link Ext.menu.Separator}
+menutextitem     {@link Ext.menu.Item}
+
+Form components
+---------------------------------------
+form             {@link Ext.form.Panel}
+checkbox         {@link Ext.form.field.Checkbox}
+combo            {@link Ext.form.field.ComboBox}
+datefield        {@link Ext.form.field.Date}
+displayfield     {@link Ext.form.field.Display}
+field            {@link Ext.form.field.Base}
+fieldset         {@link Ext.form.FieldSet}
+hidden           {@link Ext.form.field.Hidden}
+htmleditor       {@link Ext.form.field.HtmlEditor}
+label            {@link Ext.form.Label}
+numberfield      {@link Ext.form.field.Number}
+radio            {@link Ext.form.field.Radio}
+radiogroup       {@link Ext.form.RadioGroup}
+textarea         {@link Ext.form.field.TextArea}
+textfield        {@link Ext.form.field.Text}
+timefield        {@link Ext.form.field.Time}
+trigger          {@link Ext.form.field.Trigger}
+
+Chart components
+---------------------------------------
+chart            {@link Ext.chart.Chart}
+barchart         {@link Ext.chart.series.Bar}
+columnchart      {@link Ext.chart.series.Column}
+linechart        {@link Ext.chart.series.Line}
+piechart         {@link Ext.chart.series.Pie}
+
+</pre><p>
+ * It should not usually be necessary to instantiate a Component because there are provided subclasses which implement specialized Component
+ * use cases which over most application needs. However it is possible to instantiate a base Component, and it will be renderable,
+ * or will particpate in layouts as the child item of a Container:
+{@img Ext.Component/Ext.Component.png Ext.Component component}
+<pre><code>
+    Ext.create('Ext.Component', {
+        html: 'Hello world!',
+        width: 300,
+        height: 200,
+        padding: 20,
+        style: {
+            color: '#FFFFFF',
+            backgroundColor:'#000000'
+        },
+        renderTo: Ext.getBody()
+    });
+</code></pre>
+ *</p>
+ *<p>The Component above creates its encapsulating <code>div</code> upon render, and use the configured HTML as content. More complex
+ * internal structure may be created using the {@link #renderTpl} configuration, although to display database-derived mass
+ * data, it is recommended that an ExtJS data-backed Component such as a {Ext.view.DataView DataView}, or {Ext.grid.Panel GridPanel},
+ * or {@link Ext.tree.Panel TreePanel} be used.</p>
+ * @constructor
+ * @param {Ext.core.Element/String/Object} config The configuration options may be specified as either:
+ * <div class="mdetail-params"><ul>
+ * <li><b>an element</b> :
+ * <p class="sub-desc">it is set as the internal element and its id used as the component id</p></li>
+ * <li><b>a string</b> :
+ * <p class="sub-desc">it is assumed to be the id of an existing element and is used as the component id</p></li>
+ * <li><b>anything else</b> :
+ * <p class="sub-desc">it is assumed to be a standard config object and is applied to the component</p></li>
+ * </ul></div>
+ */
+
+Ext.define('Ext.Component', {
+
+    /* Begin Definitions */
+
+    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: {
+        // Collapse/expand directions
+        DIRECTION_TOP: 'top',
+        DIRECTION_RIGHT: 'right',
+        DIRECTION_BOTTOM: 'bottom',
+        DIRECTION_LEFT: 'left'
+    },
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Mixed} resizable
+     * <p>Specify as <code>true</code> to apply a {@link Ext.resizer.Resizer Resizer} to this Component
+     * after rendering.</p>
+     * <p>May also be specified as a config object to be passed to the constructor of {@link Ext.resizer.Resizer Resizer}
+     * to override any defaults. By default the Component passes its minimum and maximum size, and uses
+     * <code>{@link Ext.resizer.Resizer#dynamic}: false</code></p>
+     */
+
+    /**
+     * @cfg {String} resizeHandles
+     * A valid {@link Ext.resizer.Resizer} handles config string (defaults to 'all').  Only applies when resizable = true.
+     */
+    resizeHandles: 'all',
+
+    /**
+     * @cfg {Boolean} autoScroll
+     * <code>true</code> to use overflow:'auto' on the components layout element and show scroll bars automatically when
+     * necessary, <code>false</code> to clip any overflowing content (defaults to <code>false</code>).
+     */
+
+    /**
+     * @cfg {Boolean} floating
+     * <p>Specify as true to float the Component outside of the document flow using CSS absolute positioning.</p>
+     * <p>Components such as {@link Ext.window.Window Window}s and {@link Ext.menu.Menu Menu}s are floating
+     * by default.</p>
+     * <p>Floating Components that are programatically {@link Ext.Component#render rendered} will register themselves with the global
+     * {@link Ext.WindowManager ZIndexManager}</p>
+     * <h3 class="pa">Floating Components as child items of a Container</h3>
+     * <p>A floating Component may be used as a child item of a Container. This just allows the floating Component to seek a ZIndexManager by
+     * examining the ownerCt chain.</p>
+     * <p>When configured as floating, Components acquire, at render time, a {@link Ext.ZIndexManager ZIndexManager} which manages a stack
+     * of related floating Components. The ZIndexManager brings a single floating Component to the top of its stack when
+     * the Component's {@link #toFront} method is called.</p>
+     * <p>The ZIndexManager is found by traversing up the {@link #ownerCt} chain to find an ancestor which itself is floating. This is so that
+     * descendant floating Components of floating <i>Containers</i> (Such as a ComboBox dropdown within a Window) can have its zIndex managed relative
+     * to any siblings, but always <b>above</b> that floating ancestor Container.</p>
+     * <p>If no floating ancestor is found, a floating Component registers itself with the default {@link Ext.WindowManager ZIndexManager}.</p>
+     * <p>Floating components <i>do not participate in the Container's layout</i>. Because of this, they are not rendered until you explicitly
+     * {@link #show} them.</p>
+     * <p>After rendering, the ownerCt reference is deleted, and the {@link #floatParent} property is set to the found floating ancestor Container.
+     * If no floating ancestor Container was found the {@link #floatParent} property will not be set.</p>
+     */
+    floating: false,
+
+    /**
+     * @cfg {Boolean} toFrontOnShow
+     * <p>True to automatically call {@link #toFront} when the {@link #show} method is called
+     * on an already visible, floating component (default is <code>true</code>).</p>
+     */
+    toFrontOnShow: true,
+
+    /**
+     * <p>Optional. Only present for {@link #floating} Components after they have been rendered.</p>
+     * <p>A reference to the ZIndexManager which is managing this Component's z-index.</p>
+     * <p>The {@link Ext.ZIndexManager ZIndexManager} maintains a stack of floating Component z-indices, and also provides a single modal
+     * mask which is insert just beneath the topmost visible modal floating Component.</p>
+     * <p>Floating Components may be {@link #toFront brought to the front} or {@link #toBack sent to the back} of the z-index stack.</p>
+     * <p>This defaults to the global {@link Ext.WindowManager ZIndexManager} for floating Components that are programatically
+     * {@link Ext.Component#render rendered}.</p>
+     * <p>For {@link #floating} Components which are added to a Container, the ZIndexManager is acquired from the first ancestor Container found
+     * which is floating, or if not found the global {@link Ext.WindowManager ZIndexManager} is used.</p>
+     * <p>See {@link #floating} and {@link #floatParent}</p>
+     * @property zIndexManager
+     * @type Ext.ZIndexManager
+     */
+
+     /**
+      * <p>Optional. Only present for {@link #floating} Components which were inserted as descendant items of floating Containers.</p>
+      * <p>Floating Components that are programatically {@link Ext.Component#render rendered} will not have a <code>floatParent</code> property.</p>
+      * <p>For {@link #floating} Components which are child items of a Container, the floatParent will be the floating ancestor Container which is
+      * responsible for the base z-index value of all its floating descendants. It provides a {@link Ext.ZIndexManager ZIndexManager} which provides
+      * z-indexing services for all its descendant floating Components.</p>
+      * <p>For example, the dropdown {@link Ext.view.BoundList BoundList} of a ComboBox which is in a Window will have the Window as its
+      * <code>floatParent</code></p>
+      * <p>See {@link #floating} and {@link #zIndexManager}</p>
+      * @property floatParent
+      * @type Ext.Container
+      */
+
+    /**
+     * @cfg {Mixed} draggable
+     * <p>Specify as true to make a {@link #floating} Component draggable using the Component's encapsulating element as the drag handle.</p>
+     * <p>This may also be specified as a config object for the {@link Ext.util.ComponentDragger ComponentDragger} which is instantiated to perform dragging.</p>
+     * <p>For example to create a Component which may only be dragged around using a certain internal element as the drag handle,
+     * use the delegate option:</p>
+     * <code><pre>
+new Ext.Component({
+    constrain: true,
+    floating:true,
+    style: {
+        backgroundColor: '#fff',
+        border: '1px solid black'
+    },
+    html: '&lt;h1 style="cursor:move"&gt;The title&lt;/h1&gt;&lt;p&gt;The content&lt;/p&gt;',
+    draggable: {
+        delegate: 'h1'
+    }
+}).show();
+</pre></code>
+     */
+
+    /**
+     * @cfg {Boolean} maintainFlex
+     * <p><b>Only valid when a sibling element of a {@link Ext.resizer.Splitter Splitter} within a {@link Ext.layout.container.VBox VBox} or
+     * {@link Ext.layout.container.HBox HBox} layout.</b></p>
+     * <p>Specifies that if an immediate sibling Splitter is moved, the Component on the <i>other</i> side is resized, and this
+     * Component maintains its configured {@link Ext.layout.container.Box#flex flex} value.</p>
+     */
+
+    hideMode: 'display',
+    // Deprecate 5.0
+    hideParent: false,
+
+    ariaRole: 'presentation',
+
+    bubbleEvents: [],
+
+    actionMode: 'el',
+    monPropRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
+
+    //renderTpl: new Ext.XTemplate(
+    //    '<div id="{id}" class="{baseCls} {cls} {cmpCls}<tpl if="typeof ui !== \'undefined\'"> {uiBase}-{ui}</tpl>"<tpl if="typeof style !== \'undefined\'"> style="{style}"</tpl>></div>', {
+    //        compiled: true,
+    //        disableFormats: true
+    //    }
+    //),
+    constructor: function(config) {
+        config = config || {};
+        if (config.initialConfig) {
+
+            // Being initialized from an Ext.Action instance...
+            if (config.isAction) {
+                this.baseAction = config;
+            }
+            config = config.initialConfig;
+            // component cloning / action set up
+        }
+        else if (config.tagName || config.dom || Ext.isString(config)) {
+            // element object
+            config = {
+                applyTo: config,
+                id: config.id || config
+            };
+        }
+
+        this.callParent([config]);
+
+        // If we were configured from an instance of Ext.Action, (or configured with a baseAction option),
+        // register this Component as one of its items
+        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 = [];
+    },
+
+    // private
+    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()]);
+        }
+
+        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);
+        }
+    },
+
+    /**
+     * Sets the overflow on the content element of the component.
+     * @param {Boolean} scroll True to allow the Component to auto scroll.
+     * @return {Ext.Component} this
+     */
+    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)) {
+                // The scrollable container element must be non-statically positioned or IE6/7 will make
+                // positioned children stay in place rather than scrolling with the rest of the content
+                targetEl.position();
+            }
+        }
+        me.autoScroll = scroll;
+        return me;
+    },
+
+    // private
+    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);
+
+        // Add extra configs if Component is specified to be constrained
+        if (me.constrain || me.constrainDelegate) {
+            ddConfig.constrain = me.constrain;
+            ddConfig.constrainDelegate = me.constrainDelegate;
+        }
+
+        this.dd = Ext.create('Ext.util.ComponentDragger', this, ddConfig);
+    },
+
+    /**
+     * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
+     * This method fires the {@link #move} event.
+     * @param {Number} left The new left
+     * @param {Number} top The new top
+     * @param {Mixed} animate If true, the Component is <i>animated</i> into its new position. You may also pass an animation configuration.
+     * @return {Ext.Component} this
+     */
+    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;
+    },
+
+    /**
+     * @private Template method called after a Component has been positioned.
+     */
+    afterSetPosition: function(ax, ay) {
+        this.onPosition(ax, ay);
+        this.fireEvent('move', this, ax, ay);
+    },
+
+    showAt: function(x, y, animate) {
+        // A floating Component is positioned relative to its ownerCt if any.
+        if (this.floating) {
+            this.setPosition(x, y, animate);
+        } else {
+            this.setPagePosition(x, y, animate);
+        }
+        this.show();
+    },
+
+    /**
+     * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
+     * This method fires the {@link #move} event.
+     * @param {Number} x The new x position
+     * @param {Number} y The new y position
+     * @param {Mixed} animate If passed, the Component is <i>animated</i> into its new position. If this parameter
+     * is a number, it is used as the animation duration in milliseconds.
+     * @return {Ext.Component} this
+     */
+    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) {
+            // Floating Components being positioned in their ownerCt have to be made absolute
+            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;
+    },
+
+    /**
+     * Gets the current box measurements of the component's underlying element.
+     * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
+     * @return {Object} box An object in the format {x, y, width, height}
+     */
+    getBox : function(local){
+        var pos = this.getPosition(local);
+        var s = this.getSize();
+        s.x = pos[0];
+        s.y = pos[1];
+        return s;
+    },
+
+    /**
+     * Sets the current box measurements of the component's underlying element.
+     * @param {Object} box An object in the format {x, y, width, height}
+     * @return {Ext.Component} this
+     */
+    updateBox : function(box){
+        this.setSize(box.width, box.height);
+        this.setPagePosition(box.x, box.y);
+        return this;
+    },
+
+    // Include margins
+    getOuterSize: function() {
+        var el = this.el;
+        return {
+            width: el.getWidth() + el.getMargin('lr'),
+            height: el.getHeight() + el.getMargin('tb')
+        };
+    },
+
+    // private
+    adjustSize: function(w, h) {
+        if (this.autoWidth) {
+            w = 'auto';
+        }
+
+        if (this.autoHeight) {
+            h = 'auto';
+        }
+
+        return {
+            width: w,
+            height: h
+        };
+    },
+
+    // private
+    adjustPosition: function(x, y) {
+
+        // Floating Components being positioned in their ownerCt have to be made absolute
+        if (this.floating && this.floatParent) {
+            var o = this.floatParent.getTargetEl().getViewRegion();
+            x += o.left;
+            y += o.top;
+        }
+
+        return {
+            x: x,
+            y: y
+        };
+    },
+
+    /**
+     * Gets the current XY position of the component's underlying element.
+     * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
+     * @return {Array} The XY position of the element (e.g., [100, 200])
+     */
+    getPosition: function(local) {
+        var el = this.el,
+            xy;
+
+        if (local === true) {
+            return [el.getLeft(true), el.getTop(true)];
+        }
+        xy = this.xy || el.getXY();
+
+        // Floating Components in an ownerCt have to have their positions made relative
+        if (this.floating && this.floatParent) {
+            var o = this.floatParent.getTargetEl().getViewRegion();
+            xy[0] -= o.left;
+            xy[1] -= o.top;
+        }
+        return xy;
+    },
+
+    // Todo: add in xtype prefix support
+    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();
+    },
+
+    /**
+     * <p>Shows this Component, rendering it first if {@link #autoRender} or {{@link "floating} are <code>true</code>.</p>
+     * <p>After being shown, a {@link #floating} Component (such as a {@link Ext.window.Window}), is activated it and brought to the front of
+     * its {@link #ZIndexManager z-index stack}.</p>
+     * @param {String/Element} animateTarget Optional, and <b>only valid for {@link #floating} Components such as
+     * {@link Ext.window.Window Window}s or {@link Ext.tip.ToolTip ToolTip}s, or regular Components which have been configured
+     * with <code>floating: true</code>.</b> The target from which the Component should
+     * animate from while opening (defaults to null with no animation)
+     * @param {Function} callback (optional) A callback function to call after the Component is displayed. Only necessary if animation was specified.
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Component.
+     * @return {Component} this
+     */
+    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;
+
+            // Render on first show if there is an autoRender config, or if this is a floater (Window, Menu, BoundList etc).
+            if (!this.rendered && (this.autoRender || this.floating)) {
+                this.doAutoRender();
+            }
+            if (this.rendered) {
+                this.beforeShow();
+                this.onShow.apply(this, arguments);
+
+                // Notify any owning Container unless it's suspended.
+                // Floating Components do not participate in layouts.
+                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,
+
+    // Private. Override in subclasses where more complex behaviour is needed.
+    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;
+
+        // Default to configured animate target if none passed
+        animateTarget = animateTarget || me.animateTarget;
+
+        // Need to be able to ghost the Component
+        if (!me.ghost) {
+            animateTarget = null;
+        }
+        // If we're animating, kick of an animation of the ghost from the target to the *Element* current box
+        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);
+    },
+
+    /**
+     * Hides this Component, setting it to invisible using the configured {@link #hideMode}.
+     * @param {String/Element/Component} animateTarget Optional, and <b>only valid for {@link #floating} Components such as
+     * {@link Ext.window.Window Window}s or {@link Ext.tip.ToolTip ToolTip}s, or regular Components which have been configured
+     * with <code>floating: true</code>.</b>.
+     * The target to which the Component should animate while hiding (defaults to null with no animation)
+     * @param {Function} callback (optional) A callback function to call after the Component is hidden.
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to this Component.
+     * @return {Ext.Component} this
+     */
+    hide: function() {
+
+        // Clear the flag which is set if a floatParent was hidden while this is visible.
+        // If a hide operation was subsequently called, that pending show must be hidden.
+        this.showOnParentShow = false;
+
+        if (!(this.rendered && !this.isVisible()) && this.fireEvent('beforehide', this) !== false) {
+            this.hidden = true;
+            if (this.rendered) {
+                this.onHide.apply(this, arguments);
+
+                // Notify any owning Container unless it's suspended.
+                // Floating Components do not participate in layouts.
+                if (this.ownerCt && !this.floating && !(this.ownerCt.suspendLayout || this.ownerCt.layout.layoutBusy)) {
+                    this.ownerCt.doLayout();
+                }
+            }
+        }
+        return this;
+    },
+
+    // Possibly animate down to a target element.
+    onHide: function(animateTarget, cb, scope) {
+        var me = this,
+            ghostPanel,
+            toBox;
+
+        // Default to configured animate target if none passed
+        animateTarget = animateTarget || me.animateTarget;
+
+        // Need to be able to ghost the Component
+        if (!me.ghost) {
+            animateTarget = null;
+        }
+        // If we're animating, kick off an animation of the ghost down to the target
+        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);
+    },
+
+    /**
+     * @private
+     * Template method to contribute functionality at destroy time.
+     */
+    onDestroy: function() {
+        var me = this;
+
+        // Ensure that any ancillary components are destroyed.
+        if (me.rendered) {
+            Ext.destroy(
+                me.proxy,
+                me.resizer
+            );
+            // Different from AbstractComponent
+            if (me.actionMode == 'container' || me.removeMode == 'container') {
+                me.container.remove();
+            }
+        }
+        me.callParent();
+    },
+
+    deleteMembers: function() {
+        var args = arguments,
+            len = args.length,
+            i = 0;
+        for (; i < len; ++i) {
+            delete this[args[i]];
+        }
+    },
+
+    /**
+     * Try to focus this component.
+     * @param {Boolean} selectText (optional) If applicable, true to also select the text in this component
+     * @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds).
+     * @return {Ext.Component} this
+     */
+    focus: function(selectText, delay) {
+        var me = this,
+                focusEl;
+
+        if (delay) {
+            me.focusTask.delay(Ext.isNumber(delay) ? delay: 10, null, me, [selectText, false]);
+            return me;
+        }
+
+        if (me.rendered && !me.isDestroyed) {
+            // getFocusEl could return a Component.
+            focusEl = me.getFocusEl();
+            focusEl.focus();
+            if (focusEl.dom && selectText === true) {
+                focusEl.dom.select();
+            }
+
+            // Focusing a floating Component brings it to the front of its stack.
+            // this is performed by its zIndexManager. Pass preventFocus true to avoid recursion.
+            if (me.floating) {
+                me.toFront(true);
+            }
+        }
+        return me;
+    },
+
+    /**
+     * @private
+     * Returns the focus holder element associated with this Component. By default, this is the Component's encapsulating
+     * element. Subclasses which use embedded focusable elements (such as Window and Button) should override this for use
+     * by the {@link #focus} method.
+     * @returns {Ext.core.Element} the focus holing element.
+     */
+    getFocusEl: function() {
+        return this.el;
+    },
+
+    // private
+    blur: function() {
+        if (this.rendered) {
+            this.getFocusEl().blur();
+        }
+        return this;
+    },
+
+    getEl: function() {
+        return this.el;
+    },
+
+    // Deprecate 5.0
+    getResizeEl: function() {
+        return this.el;
+    },
+
+    // Deprecate 5.0
+    getPositionEl: function() {
+        return this.el;
+    },
+
+    // Deprecate 5.0
+    getActionEl: function() {
+        return this.el;
+    },
+
+    // Deprecate 5.0
+    getVisibilityEl: function() {
+        return this.el;
+    },
+
+    // Deprecate 5.0
+    onResize: Ext.emptyFn,
+
+    // private
+    getBubbleTarget: function() {
+        return this.ownerCt;
+    },
+
+    // private
+    getContentTarget: function() {
+        return this.el;
+    },
+
+    /**
+     * Clone the current component using the original config values passed into this instance by default.
+     * @param {Object} overrides A new config containing any properties to override in the cloned version.
+     * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
+     * @return {Ext.Component} clone The cloned copy of this component
+     */
+    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);
+
+        // prevent dup id
+        return new self(cfg);
+    },
+
+    /**
+     * Gets the xtype for this component as registered with {@link Ext.ComponentManager}. For a list of all
+     * available xtypes, see the {@link Ext.Component} header. Example usage:
+     * <pre><code>
+var t = new Ext.form.field.Text();
+alert(t.getXType());  // alerts 'textfield'
+</code></pre>
+     * @return {String} The xtype
+     */
+    getXType: function() {
+        return this.self.xtype;
+    },
+
+    /**
+     * Find a container above this component at any level by a custom function. If the passed function returns
+     * true, the container will be returned.
+     * @param {Function} fn The custom function to call with the arguments (container, this component).
+     * @return {Ext.container.Container} The first Container for which the custom function returns true
+     */
+    findParentBy: function(fn) {
+        var p;
+
+        // Iterate up the ownerCt chain until there's no ownerCt, or we find an ancestor which matches using the selector function.
+        for (p = this.ownerCt; p && !fn(p, this); p = p.ownerCt);
+        return p || null;
+    },
+
+    /**
+     * <p>Find a container above this component at any level by xtype or class</p>
+     * <p>See also the {@link Ext.Component#up up} method.</p>
+     * @param {String/Class} xtype The xtype string for a component, or the class of the component directly
+     * @return {Ext.container.Container} The first Container which matches the given xtype or class
+     */
+    findParentByType: function(xtype) {
+        return Ext.isFunction(xtype) ?
+            this.findParentBy(function(p) {
+                return p.constructor === xtype;
+            })
+        :
+            this.up(xtype);
+    },
+
+    /**
+     * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope (<i>this</i>) of
+     * function call will be the scope provided or the current component. The arguments to the function
+     * will be the args provided or the current component. If the function returns false at any point,
+     * the bubble is stopped.
+     * @param {Function} fn The function to call
+     * @param {Object} scope (optional) The scope of the function (defaults to current node)
+     * @param {Array} args (optional) The args to call the function with (default to passing the current component)
+     * @return {Ext.Component} this
+     */
+    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;
+    }
+
+}, function() {
+
+    // A single focus delayer for all Components.
+    this.prototype.focusTask = Ext.create('Ext.util.DelayedTask', this.prototype.focus);
+
+});
+
+/**
+* @class Ext.layout.container.Container
+* @extends Ext.layout.container.AbstractContainer
+* @private
+* <p>This class is intended to be extended or created via the <tt><b>{@link Ext.container.Container#layout layout}</b></tt>
+* configuration property.  See <tt><b>{@link Ext.container.Container#layout}</b></tt> for additional details.</p>
+*/
+Ext.define('Ext.layout.container.Container', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.layout.container.AbstractContainer',
+    alternateClassName: 'Ext.layout.ContainerLayout',
+    
+    /* End Definitions */
+
+    layoutItem: function(item, box) {
+        box = box || {};
+        if (item.componentLayout.initialized !== true) {
+            this.setItemSize(item, box.width || item.width || undefined, box.height || item.height || undefined);
+            // item.doComponentLayout(box.width || item.width || undefined, box.height || item.height || undefined);
+        }
+    },
+
+    getLayoutTargetSize : function() {
+        var target = this.getTarget(),
+            ret;
+
+        if (target) {
+            ret = target.getViewSize();
+
+            // IE in will sometimes return a width of 0 on the 1st pass of getViewSize.
+            // Use getStyleSize to verify the 0 width, the adjustment pass will then work properly
+            // with 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);
+    },
+
+    /**
+     * @protected
+     * Returns all items that are rendered
+     * @return {Array} All matching items
+     */
+    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;
+    },
+
+    /**
+     * @protected
+     * Returns all items that are both rendered and visible
+     * @return {Array} All matching items
+     */
+    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;
+    }
+});
+/**
+ * @class Ext.layout.container.Auto
+ * @extends Ext.layout.container.Container
+ *
+ * <p>The AutoLayout is the default layout manager delegated by {@link Ext.container.Container} to
+ * render any child Components when no <tt>{@link Ext.container.Container#layout layout}</tt> is configured into
+ * a <tt>{@link Ext.container.Container Container}.</tt>.  AutoLayout provides only a passthrough of any layout calls
+ * to any child containers.</p>
+ * {@img Ext.layout.container.Auto/Ext.layout.container.Auto.png Ext.layout.container.Auto container layout}
+ * Example usage:
+       Ext.create('Ext.Panel', {
+               width: 500,
+               height: 280,
+               title: "AutoLayout Panel",
+               layout: 'auto',
+               renderTo: document.body,
+               items: [{
+                       xtype: 'panel',
+                       title: 'Top Inner Panel',
+                       width: '75%',
+                       height: 90
+               },{
+                       xtype: 'panel',
+                       title: 'Bottom Inner Panel',
+                       width: '75%',
+                       height: 90
+               }]
+       });
+ */
+
+Ext.define('Ext.layout.container.Auto', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.auto', 'layout.autocontainer'],
+
+    extend: 'Ext.layout.container.Container',
+
+    /* End Definitions */
+
+    type: 'autocontainer',
+
+    fixedLayout: false,
+
+    bindToOwnerCtComponent: true,
+
+    // @private
+    onLayout : function(owner, target) {
+        var me = this,
+            items = me.getLayoutItems(),
+            ln = items.length,
+            i;
+
+        // Ensure the Container is only primed with the clear element if there are child items.
+        if (ln) {
+            // Auto layout uses natural HTML flow to arrange the child items.
+            // To ensure that all browsers (I'm looking at you IE!) add the bottom margin of the last child to the
+            // containing element height, we create a zero-sized element with style clear:both to force a "new line"
+            if (!me.clearEl) {
+                me.clearEl = me.getRenderTarget().createChild({
+                    cls: Ext.baseCSSPrefix + 'clear',
+                    role: 'presentation'
+                });
+            }
+
+            // Auto layout allows CSS to size its child items.
+            for (i = 0; i < ln; i++) {
+                me.setItemSize(items[i]);
+            }
+        }
+    }
+});
+/**
+ * @class Ext.container.AbstractContainer
+ * @extends Ext.Component
+ * <p>An abstract base class which provides shared methods for Containers across the Sencha product line.</p>
+ * Please refer to sub class's documentation
+ */
+Ext.define('Ext.container.AbstractContainer', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.Component',
+
+    requires: [
+        'Ext.util.MixedCollection',
+        'Ext.layout.container.Auto',
+        'Ext.ZIndexManager'
+    ],
+
+    /* End Definitions */
+    /**
+     * @cfg {String/Object} layout
+     * <p><b>*Important</b>: In order for child items to be correctly sized and
+     * positioned, typically a layout manager <b>must</b> be specified through
+     * the <code>layout</code> configuration option.</p>
+     * <br><p>The sizing and positioning of child {@link #items} is the responsibility of
+     * the Container's layout manager which creates and manages the type of layout
+     * you have in mind.  For example:</p>
+     * <p>If the {@link #layout} configuration is not explicitly specified for
+     * a general purpose container (e.g. Container or Panel) the
+     * {@link Ext.layout.container.Auto default layout manager} will be used
+     * which does nothing but render child components sequentially into the
+     * Container (no sizing or positioning will be performed in this situation).</p>
+     * <br><p><b><code>layout</code></b> may be specified as either as an Object or
+     * as a String:</p><div><ul class="mdetail-params">
+     *
+     * <li><u>Specify as an Object</u></li>
+     * <div><ul class="mdetail-params">
+     * <li>Example usage:</li>
+     * <pre><code>
+layout: {
+    type: 'vbox',
+    align: 'left'
+}
+       </code></pre>
+     *
+     * <li><code><b>type</b></code></li>
+     * <br/><p>The layout type to be used for this container.  If not specified,
+     * a default {@link Ext.layout.container.Auto} will be created and used.</p>
+     * <br/><p>Valid layout <code>type</code> values are:</p>
+     * <div class="sub-desc"><ul class="mdetail-params">
+     * <li><code><b>{@link Ext.layout.container.Auto Auto}</b></code> &nbsp;&nbsp;&nbsp; <b>Default</b></li>
+     * <li><code><b>{@link Ext.layout.container.Card card}</b></code></li>
+     * <li><code><b>{@link Ext.layout.container.Fit fit}</b></code></li>
+     * <li><code><b>{@link Ext.layout.container.HBox hbox}</b></code></li>
+     * <li><code><b>{@link Ext.layout.container.VBox vbox}</b></code></li>
+     * <li><code><b>{@link Ext.layout.container.Anchor anchor}</b></code></li>
+     * <li><code><b>{@link Ext.layout.container.Table table}</b></code></li>
+     * </ul></div>
+     *
+     * <li>Layout specific configuration properties</li>
+     * <br/><p>Additional layout specific configuration properties may also be
+     * specified. For complete details regarding the valid config options for
+     * each layout type, see the layout class corresponding to the <code>type</code>
+     * specified.</p>
+     *
+     * </ul></div>
+     *
+     * <li><u>Specify as a String</u></li>
+     * <div><ul class="mdetail-params">
+     * <li>Example usage:</li>
+     * <pre><code>
+layout: {
+    type: 'vbox',
+    padding: '5',
+    align: 'left'
+}
+       </code></pre>
+     * <li><code><b>layout</b></code></li>
+     * <br/><p>The layout <code>type</code> to be used for this container (see list
+     * of valid layout type values above).</p><br/>
+     * <br/><p>Additional layout specific configuration properties. For complete
+     * details regarding the valid config options for each layout type, see the
+     * layout class corresponding to the <code>layout</code> specified.</p>
+     * </ul></div></ul></div>
+     */
+
+    /**
+     * @cfg {String/Number} activeItem
+     * A string component id or the numeric index of the component that should be initially activated within the
+     * container's layout on render.  For example, activeItem: 'item-1' or activeItem: 0 (index 0 = the first
+     * item in the container's collection).  activeItem only applies to layout styles that can display
+     * items one at a time (like {@link Ext.layout.container.Card} and {@link Ext.layout.container.Fit}).
+     */
+    /**
+     * @cfg {Object/Array} items
+     * <p>A single item, or an array of child Components to be added to this container</p>
+     * <p><b>Unless configured with a {@link #layout}, a Container simply renders child Components serially into
+     * its encapsulating element and performs no sizing or positioning upon them.</b><p>
+     * <p>Example:</p>
+     * <pre><code>
+// specifying a single item
+items: {...},
+layout: 'fit',    // The single items is sized to fit
+
+// specifying multiple items
+items: [{...}, {...}],
+layout: 'hbox', // The items are arranged horizontally
+       </code></pre>
+     * <p>Each item may be:</p>
+     * <ul>
+     * <li>A {@link Ext.Component Component}</li>
+     * <li>A Component configuration object</li>
+     * </ul>
+     * <p>If a configuration object is specified, the actual type of Component to be
+     * instantiated my be indicated by using the {@link Ext.Component#xtype xtype} option.</p>
+     * <p>Every Component class has its own {@link Ext.Component#xtype xtype}.</p>
+     * <p>If an {@link Ext.Component#xtype xtype} is not explicitly
+     * specified, the {@link #defaultType} for the Container is used, which by default is usually <code>panel</code>.</p>
+     * <p><b>Notes</b>:</p>
+     * <p>Ext uses lazy rendering. Child Components will only be rendered
+     * should it become necessary. Items are automatically laid out when they are first
+     * shown (no sizing is done while hidden), or in response to a {@link #doLayout} call.</p>
+     * <p>Do not specify <code>{@link Ext.panel.Panel#contentEl contentEl}</code> or 
+     * <code>{@link Ext.panel.Panel#html html}</code> with <code>items</code>.</p>
+     */
+    /**
+     * @cfg {Object|Function} defaults
+     * <p>This option is a means of applying default settings to all added items whether added through the {@link #items}
+     * config or via the {@link #add} or {@link #insert} methods.</p>
+     * <p>If an added item is a config object, and <b>not</b> an instantiated Component, then the default properties are
+     * unconditionally applied. If the added item <b>is</b> an instantiated Component, then the default properties are
+     * applied conditionally so as not to override existing properties in the item.</p>
+     * <p>If the defaults option is specified as a function, then the function will be called using this Container as the
+     * scope (<code>this</code> reference) and passing the added item as the first parameter. Any resulting object
+     * from that call is then applied to the item as default properties.</p>
+     * <p>For example, to automatically apply padding to the body of each of a set of
+     * contained {@link Ext.panel.Panel} items, you could pass: <code>defaults: {bodyStyle:'padding:15px'}</code>.</p>
+     * <p>Usage:</p><pre><code>
+defaults: {               // defaults are applied to items, not the container
+    autoScroll:true
+},
+items: [
+    {
+        xtype: 'panel',   // defaults <b>do not</b> have precedence over
+        id: 'panel1',     // options in config objects, so the defaults
+        autoScroll: false // will not be applied here, panel1 will be autoScroll:false
+    },
+    new Ext.panel.Panel({       // defaults <b>do</b> have precedence over options
+        id: 'panel2',     // options in components, so the defaults
+        autoScroll: false // will be applied here, panel2 will be autoScroll:true.
+    })
+]</code></pre>
+     */
+
+    /** @cfg {Boolean} suspendLayout
+     * If true, suspend calls to doLayout.  Useful when batching multiple adds to a container and not passing them
+     * as multiple arguments or an array.
+     */
+    suspendLayout : false,
+
+    /** @cfg {Boolean} autoDestroy
+     * If true the container will automatically destroy any contained component that is removed from it, else
+     * destruction must be handled manually.
+     * Defaults to true.
+     */
+    autoDestroy : true,
+
+     /** @cfg {String} defaultType
+      * <p>The default {@link Ext.Component xtype} of child Components to create in this Container when
+      * a child item is specified as a raw configuration object, rather than as an instantiated Component.</p>
+      * <p>Defaults to <code>'panel'</code>.</p>
+      */
+    defaultType: 'panel',
+
+    isContainer : true,
+
+    baseCls: Ext.baseCSSPrefix + 'container',
+
+    /**
+     * @cfg {Array} bubbleEvents
+     * <p>An array of events that, when fired, should be bubbled to any parent container.
+     * See {@link Ext.util.Observable#enableBubble}.
+     * Defaults to <code>['add', 'remove']</code>.
+     */
+    bubbleEvents: ['add', 'remove'],
+    
+    // @private
+    initComponent : function(){
+        var me = this;
+        me.addEvents(
+            /**
+             * @event afterlayout
+             * Fires when the components in this container are arranged by the associated layout manager.
+             * @param {Ext.container.Container} this
+             * @param {ContainerLayout} layout The ContainerLayout implementation for this container
+             */
+            'afterlayout',
+            /**
+             * @event beforeadd
+             * Fires before any {@link Ext.Component} is added or inserted into the container.
+             * A handler can return false to cancel the add.
+             * @param {Ext.container.Container} this
+             * @param {Ext.Component} component The component being added
+             * @param {Number} index The index at which the component will be added to the container's items collection
+             */
+            'beforeadd',
+            /**
+             * @event beforeremove
+             * Fires before any {@link Ext.Component} is removed from the container.  A handler can return
+             * false to cancel the remove.
+             * @param {Ext.container.Container} this
+             * @param {Ext.Component} component The component being removed
+             */
+            'beforeremove',
+            /**
+             * @event add
+             * @bubbles
+             * Fires after any {@link Ext.Component} is added or inserted into the container.
+             * @param {Ext.container.Container} this
+             * @param {Ext.Component} component The component that was added
+             * @param {Number} index The index at which the component was added to the container's items collection
+             */
+            'add',
+            /**
+             * @event remove
+             * @bubbles
+             * Fires after any {@link Ext.Component} is removed from the container.
+             * @param {Ext.container.Container} this
+             * @param {Ext.Component} component The component that was removed
+             */
+            'remove',
+            /**
+             * @event beforecardswitch
+             * Fires before this container switches the active card. This event
+             * is only available if this container uses a CardLayout. Note that
+             * TabPanel and Carousel both get a CardLayout by default, so both
+             * will have this event.
+             * A handler can return false to cancel the card switch.
+             * @param {Ext.container.Container} this
+             * @param {Ext.Component} newCard The card that will be switched to
+             * @param {Ext.Component} oldCard The card that will be switched from
+             * @param {Number} index The index of the card that will be switched to
+             * @param {Boolean} animated True if this cardswitch will be animated
+             */
+            'beforecardswitch',
+            /**
+             * @event cardswitch
+             * Fires after this container switches the active card. If the card
+             * is switched using an animation, this event will fire after the
+             * animation has finished. This event is only available if this container
+             * uses a CardLayout. Note that TabPanel and Carousel both get a CardLayout
+             * by default, so both will have this event.
+             * @param {Ext.container.Container} this
+             * @param {Ext.Component} newCard The card that has been switched to
+             * @param {Ext.Component} oldCard The card that has been switched from
+             * @param {Number} index The index of the card that has been switched to
+             * @param {Boolean} animated True if this cardswitch was animated
+             */
+            'cardswitch'
+        );
+
+        // layoutOnShow stack
+        me.layoutOnShow = Ext.create('Ext.util.MixedCollection');
+        me.callParent();
+        me.initItems();
+    },
+
+    // @private
+    initItems : function() {
+        var me = this,
+            items = me.items;
+
+        /**
+         * The MixedCollection containing all the child items of this container.
+         * @property items
+         * @type Ext.util.MixedCollection
+         */
+        me.items = Ext.create('Ext.util.MixedCollection', false, me.getComponentId);
+
+        if (items) {
+            if (!Ext.isArray(items)) {
+                items = [items];
+            }
+
+            me.add(items);
+        }
+    },
+
+    // @private
+    afterRender : function() {
+        this.getLayout();
+        this.callParent();
+    },
+
+    // @private
+    setLayout : function(layout) {
+        var currentLayout = this.layout;
+
+        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
+            currentLayout.setOwner(null);
+        }
+
+        this.layout = layout;
+        layout.setOwner(this);
+    },
+
+    /**
+     * Returns the {@link Ext.layout.container.AbstractContainer layout} instance currently associated with this Container.
+     * If a layout has not been instantiated yet, that is done first
+     * @return {Ext.layout.container.AbstractContainer} The layout
+     */
+    getLayout : function() {
+        var me = this;
+        if (!me.layout || !me.layout.isLayout) {
+            me.setLayout(Ext.layout.Layout.create(me.layout, 'autocontainer'));
+        }
+
+        return me.layout;
+    },
+
+    /**
+     * Manually force this container's layout to be recalculated.  The framwork uses this internally to refresh layouts
+     * form most cases.
+     * @return {Ext.container.Container} this
+     */
+    doLayout : function() {
+        var me = this,
+            layout = me.getLayout();
+
+        if (me.rendered && layout && !me.suspendLayout) {
+            // If either dimension is being auto-set, then it requires a ComponentLayout to be run.
+            if ((!Ext.isNumber(me.width) || !Ext.isNumber(me.height)) && me.componentLayout.type !== 'autocomponent') {
+                // Only run the ComponentLayout if it is not already in progress
+                if (me.componentLayout.layoutBusy !== true) {
+                    me.doComponentLayout();
+                    if (me.componentLayout.layoutCancelled === true) {
+                        layout.layout();
+                    }
+                }
+            }
+            // Both dimensions defined, run a ContainerLayout
+            else {
+                // Only run the ContainerLayout if it is not already in progress
+                if (layout.layoutBusy !== true) {
+                    layout.layout();
+                }
+            }
+        }
+
+        return me;
+    },
+
+    // @private
+    afterLayout : function(layout) {
+        this.fireEvent('afterlayout', this, layout);
+    },
+
+    // @private
+    prepareItems : function(items, applyDefaults) {
+        if (!Ext.isArray(items)) {
+            items = [items];
+        }
+
+        // Make sure defaults are applied and item is initialized
+        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;
+    },
+
+    // @private
+    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;
+    },
+
+    // @private
+    lookupComponent : function(comp) {
+        return Ext.isString(comp) ? Ext.ComponentManager.get(comp) : this.createComponent(comp);
+    },
+
+    // @private
+    createComponent : function(config, defaultType) {
+        // // add in ownerCt at creation time but then immediately
+        // // remove so that onBeforeAdd can handle it
+        // var component = Ext.create(Ext.apply({ownerCt: this}, config), defaultType || this.defaultType);
+        //
+        // delete component.initialConfig.ownerCt;
+        // delete component.ownerCt;
+
+        return Ext.ComponentManager.create(config, defaultType || this.defaultType);
+    },
+
+    // @private - used as the key lookup function for the items collection
+    getComponentId : function(comp) {
+        return comp.getItemId();
+    },
+
+    /**
+
+Adds {@link Ext.Component Component}(s) to this Container.
+
+##Description:##
+
+- Fires the {@link #beforeadd} event before adding.
+- The Container's {@link #defaults default config values} will be applied
+  accordingly (see `{@link #defaults}` for details).
+- Fires the `{@link #add}` event after the component has been added.
+
+##Notes:##
+
+If the Container is __already rendered__ when `add`
+is called, it will render the newly added Component into its content area.
+
+__**If**__ the Container was configured with a size-managing {@link #layout} manager, the Container
+will recalculate its internal layout at this time too.
+
+Note that the default layout manager simply renders child Components sequentially into the content area and thereafter performs no sizing.
+
+If adding multiple new child Components, pass them as an array to the `add` method, so that only one layout recalculation is performed.
+
+    tb = new {@link Ext.toolbar.Toolbar}({
+        renderTo: document.body
+    });  // toolbar is rendered
+    tb.add([{text:'Button 1'}, {text:'Button 2'}]); // add multiple items. ({@link #defaultType} for {@link Ext.toolbar.Toolbar Toolbar} is 'button')
+
+##Warning:## 
+
+Components directly managed by the BorderLayout layout manager
+may not be removed or added.  See the Notes for {@link Ext.layout.container.Border BorderLayout}
+for more details.
+
+     * @param {...Object/Array} Component
+     * Either one or more Components to add or an Array of Components to add.
+     * See `{@link #items}` for additional information.
+     *
+     * @return {Ext.Component/Array} The Components that were added.
+     * @markdown
+     */
+    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];
+            // Suspend Layouts while we add multiple items to the container
+            me.suspendLayout = true;
+            for (i = 0, ln = items.length; i < ln; i++) {
+                item = items[i];
+                
+                //<debug>
+                if (!item) {
+                    Ext.Error.raise("Trying to add a null item as a child of Container with itemId/id: " + me.getItemId());
+                }
+                //</debug>
+                
+                if (index != -1) {
+                    item = me.add(index + i, item);
+                } else {
+                    item = me.add(item);
+                }
+                results.push(item);
+            }
+            // Resume Layouts now that all items have been added and do a single layout for all the items just added
+            me.suspendLayout = false;
+            me.doLayout();
+            return results;
+        }
+
+        cmp = me.prepareItems(args[0], true)[0];
+
+        // Floating Components are not added into the items collection
+        // But they do get an upward ownerCt link so that they can traverse
+        // up to their z-index parent.
+        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;
+    },
+
+    /**
+     * @private
+     * <p>Called by Component#doAutoRender</p>
+     * <p>Register a Container configured <code>floating: true</code> with this Container's {@link Ext.ZIndexManager ZIndexManager}.</p>
+     * <p>Components added in ths way will not participate in the layout, but will be rendered
+     * upon first show in the way that {@link Ext.window.Window Window}s are.</p>
+     * <p></p>
+     */
+    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,
+
+    /**
+     * Inserts a Component into this Container at a specified index. Fires the
+     * {@link #beforeadd} event before inserting, then fires the {@link #add} event after the
+     * Component has been inserted.
+     * @param {Number} index The index at which the Component will be inserted
+     * into the Container's items collection
+     * @param {Ext.Component} component The child Component to insert.<br><br>
+     * Ext uses lazy rendering, and will only render the inserted Component should
+     * it become necessary.<br><br>
+     * A Component config object may be passed in order to avoid the overhead of
+     * constructing a real Component object if lazy rendering might mean that the
+     * inserted Component will not be rendered immediately. To take advantage of
+     * this 'lazy instantiation', set the {@link Ext.Component#xtype} config
+     * property to the registered type of the Component wanted.<br><br>
+     * For a list of all available xtypes, see {@link Ext.Component}.
+     * @return {Ext.Component} component The Component (or config object) that was
+     * inserted with the Container's default config values applied.
+     */
+    insert : function(index, comp) {
+        return this.add(index, comp);
+    },
+
+    /**
+     * Moves a Component within the Container
+     * @param {Number} fromIdx The index the Component you wish to move is currently at.
+     * @param {Number} toIdx The new index for the Component.
+     * @return {Ext.Component} component The Component (or config object) that was moved.
+     */
+    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;
+    },
+
+    // @private
+    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);
+        }
+    },
+
+    /**
+     * Removes a component from this container.  Fires the {@link #beforeremove} event before removing, then fires
+     * the {@link #remove} event after the component has been removed.
+     * @param {Component/String} component The component reference or id to remove.
+     * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
+     * Defaults to the value of this Container's {@link #autoDestroy} config.
+     * @return {Ext.Component} component The Component that was removed.
+     */
+    remove : function(comp, autoDestroy) {
+        var me = this,
+            c = me.getComponent(comp);
+        //<debug>
+            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.");
+            }
+        //</debug>
+
+        if (c && me.fireEvent('beforeremove', me, c) !== false) {
+            me.doRemove(c, autoDestroy);
+            me.fireEvent('remove', me, c);
+        }
+
+        return c;
+    },
+
+    // @private
+    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();
+        }
+    },
+
+    /**
+     * Removes all components from this container.
+     * @param {Boolean} autoDestroy (optional) True to automatically invoke the removed Component's {@link Ext.Component#destroy} function.
+     * Defaults to the value of this Container's {@link #autoDestroy} config.
+     * @return {Array} Array of the destroyed components
+     */
+    removeAll : function(autoDestroy) {
+        var me = this,
+            removeItems = me.items.items.slice(),
+            items = [],
+            i = 0,
+            len = removeItems.length,
+            item;
+
+        // Suspend Layouts while we remove multiple items from the container
+        me.suspendLayout = true;
+        for (; i < len; i++) {
+            item = removeItems[i];
+            me.remove(item, autoDestroy);
+
+            if (item.ownerCt !== me) {
+                items.push(item);
+            }
+        }
+
+        // Resume Layouts now that all items have been removed and do a single layout
+        me.suspendLayout = false;
+        me.doLayout();
+        return items;
+    },
+
+    // Used by ComponentQuery to retrieve all of the items
+    // which can potentially be considered a child of this Container.
+    // This should be overriden by components which have child items
+    // that are not contained in items. For example dockedItems, menu, etc
+    // IMPORTANT note for maintainers:
+    //  Items are returned in tree traversal order. Each item is appended to the result array
+    //  followed by the results of that child's getRefItems call.
+    //  Floating child items are appended after internal child 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));
+            }
+        }
+
+        // Append floating items to the list.
+        // These will only be present after they are rendered.
+        if (me.floatingItems && me.floatingItems.accessList) {
+            result.push.apply(result, me.floatingItems.accessList);
+        }
+
+        return result;
+    },
+
+    /**
+     * Cascades down the component/container heirarchy from this component (passed in the first call), calling the specified function with
+     * each component. The scope (<code>this</code> reference) of the
+     * function call will be the scope provided or the current component. The arguments to the function
+     * will be the args provided or the current component. If the function returns false at any point,
+     * the cascade is stopped on that branch.
+     * @param {Function} fn The function to call
+     * @param {Object} scope (optional) The scope of the function (defaults to current component)
+     * @param {Array} args (optional) The args to call the function with. The current component always passed as the last argument.
+     * @return {Ext.Container} this
+     */
+    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;
+    },
+
+    /**
+     * Examines this container's <code>{@link #items}</code> <b>property</b>
+     * and gets a direct child component of this container.
+     * @param {String/Number} comp This parameter may be any of the following:
+     * <div><ul class="mdetail-params">
+     * <li>a <b><code>String</code></b> : representing the <code>{@link Ext.Component#itemId itemId}</code>
+     * or <code>{@link Ext.Component#id id}</code> of the child component </li>
+     * <li>a <b><code>Number</code></b> : representing the position of the child component
+     * within the <code>{@link #items}</code> <b>property</b></li>
+     * </ul></div>
+     * <p>For additional information see {@link Ext.util.MixedCollection#get}.
+     * @return Ext.Component The component (if found).
+     */
+    getComponent : function(comp) {
+        if (Ext.isObject(comp)) {
+            comp = comp.getItemId();
+        }
+
+        return this.items.get(comp);
+    },
+
+    /**
+     * Retrieves all descendant components which match the passed selector.
+     * Executes an Ext.ComponentQuery.query using this container as its root.
+     * @param {String} selector Selector complying to an Ext.ComponentQuery selector
+     * @return {Array} Ext.Component's which matched the selector
+     */
+    query : function(selector) {
+        return Ext.ComponentQuery.query(selector, this);
+    },
+
+    /**
+     * Retrieves the first direct child of this container which matches the passed selector.
+     * The passed in selector must comply with an Ext.ComponentQuery selector.
+     * @param {String} selector An Ext.ComponentQuery selector
+     * @return Ext.Component
+     */
+    child : function(selector) {
+        return this.query('> ' + selector)[0] || null;
+    },
+
+    /**
+     * Retrieves the first descendant of this container which matches the passed selector.
+     * The passed in selector must comply with an Ext.ComponentQuery selector.
+     * @param {String} selector An Ext.ComponentQuery selector
+     * @return Ext.Component
+     */
+    down : function(selector) {
+        return this.query(selector)[0] || null;
+    },
+
+    // inherit docs
+    show : function() {
+        this.callParent(arguments);
+        this.performDeferredLayouts();
+        return this;
+    },
+
+    // Lay out any descendant containers who queued a layout operation during the time this was hidden
+    // This is also called by Panel after it expands because descendants of a collapsed Panel allso queue any layout ops.
+    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
+    // Enable all immediate children that was previously disabled
+    onEnable: function() {
+        Ext.Array.each(this.query('[isFormField]'), function(item) {
+            if (item.resetDisable) {
+                item.enable();
+                delete item.resetDisable;             
+            }
+        });
+        this.callParent();
+    },
+    
+    // @private
+    // Disable all immediate children that was previously disabled
+    onDisable: function() {
+        Ext.Array.each(this.query('[isFormField]'), function(item) {
+            if (item.resetDisable !== false && !item.disabled) {
+                item.disable();
+                item.resetDisable = true;
+            }
+        });
+        this.callParent();
+    },
+
+    /**
+     * Occurs before componentLayout is run. Returning false from this method will prevent the containerLayout
+     * from being executed.
+     */
+    beforeLayout: function() {
+        return true;
+    },
+
+    // @private
+    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();
+    }
+});
+/**
+ * @class Ext.container.Container
+ * @extends Ext.container.AbstractContainer
+ * <p>Base class for any {@link Ext.Component} that may contain other Components. Containers handle the
+ * basic behavior of containing items, namely adding, inserting and removing items.</p>
+ *
+ * <p>The most commonly used Container classes are {@link Ext.panel.Panel}, {@link Ext.window.Window} and {@link Ext.tab.Panel}.
+ * If you do not need the capabilities offered by the aforementioned classes you can create a lightweight
+ * Container to be encapsulated by an HTML element to your specifications by using the
+ * <code><b>{@link Ext.Component#autoEl autoEl}</b></code> config option.</p>
+ *
+ * {@img Ext.Container/Ext.Container.png Ext.Container component} 
+ * <p>The code below illustrates how to explicitly create a Container:<pre><code>
+// explicitly create a Container
+Ext.create('Ext.container.Container', {
+    layout: {
+        type: 'hbox'
+    },
+    width: 400,
+    renderTo: Ext.getBody(),
+    border: 1,
+    style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
+    defaults: {
+        labelWidth: 80,
+        // implicitly create Container by specifying xtype
+        xtype: 'datefield',
+        flex: 1,
+        style: {
+            padding: '10px'
+        }
+    },
+    items: [{
+        xtype: 'datefield',
+        name: 'startDate',
+        fieldLabel: 'Start date'
+    },{
+        xtype: 'datefield',
+        name: 'endDate',
+        fieldLabel: 'End date'
+    }]
+});
+</code></pre></p>
+ *
+ * <p><u><b>Layout</b></u></p>
+ * <p>Container classes delegate the rendering of child Components to a layout
+ * manager class which must be configured into the Container using the
+ * <code><b>{@link #layout}</b></code> configuration property.</p>
+ * <p>When either specifying child <code>{@link #items}</code> of a Container,
+ * or dynamically {@link #add adding} Components to a Container, remember to
+ * consider how you wish the Container to arrange those child elements, and
+ * whether those child elements need to be sized using one of Ext's built-in
+ * <b><code>{@link #layout}</code></b> schemes. By default, Containers use the
+ * {@link Ext.layout.container.Auto Auto} scheme which only
+ * renders child components, appending them one after the other inside the
+ * Container, and <b>does not apply any sizing</b> at all.</p>
+ * <p>A common mistake is when a developer neglects to specify a
+ * <b><code>{@link #layout}</code></b> (e.g. widgets like GridPanels or
+ * TreePanels are added to Containers for which no <code><b>{@link #layout}</b></code>
+ * has been specified). If a Container is left to use the default
+ * {Ext.layout.container.Auto Auto} scheme, none of its
+ * child components will be resized, or changed in any way when the Container
+ * is resized.</p>
+ * <p>Certain layout managers allow dynamic addition of child components.
+ * Those that do include {@link Ext.layout.container.Card},
+ * {@link Ext.layout.container.Anchor}, {@link Ext.layout.container.VBox}, {@link Ext.layout.container.HBox}, and
+ * {@link Ext.layout.container.Table}. For example:<pre><code>
+//  Create the GridPanel.
+var myNewGrid = new Ext.grid.Panel({
+    store: myStore,
+    headers: myHeaders,
+    title: 'Results', // the title becomes the title of the tab
+});
+
+myTabPanel.add(myNewGrid); // {@link Ext.tab.Panel} implicitly uses {@link Ext.layout.container.Card Card}
+myTabPanel.{@link Ext.tab.Panel#setActiveTab setActiveTab}(myNewGrid);
+ * </code></pre></p>
+ * <p>The example above adds a newly created GridPanel to a TabPanel. Note that
+ * a TabPanel uses {@link Ext.layout.container.Card} as its layout manager which
+ * means all its child items are sized to {@link Ext.layout.container.Fit fit}
+ * exactly into its client area.
+ * <p><b><u>Overnesting is a common problem</u></b>.
+ * An example of overnesting occurs when a GridPanel is added to a TabPanel
+ * by wrapping the GridPanel <i>inside</i> a wrapping Panel (that has no
+ * <code><b>{@link #layout}</b></code> specified) and then add that wrapping Panel
+ * to the TabPanel. The point to realize is that a GridPanel <b>is</b> a
+ * Component which can be added directly to a Container. If the wrapping Panel
+ * has no <code><b>{@link #layout}</b></code> configuration, then the overnested
+ * GridPanel will not be sized as expected.<p>
+ *
+ * <p><u><b>Adding via remote configuration</b></u></p>
+ *
+ * <p>A server side script can be used to add Components which are generated dynamically on the server.
+ * An example of adding a GridPanel to a TabPanel where the GridPanel is generated by the server
+ * based on certain parameters:
+ * </p><pre><code>
+// execute an Ajax request to invoke server side script:
+Ext.Ajax.request({
+    url: 'gen-invoice-grid.php',
+    // send additional parameters to instruct server script
+    params: {
+        startDate: Ext.getCmp('start-date').getValue(),
+        endDate: Ext.getCmp('end-date').getValue()
+    },
+    // process the response object to add it to the TabPanel:
+    success: function(xhr) {
+        var newComponent = eval(xhr.responseText); // see discussion below
+        myTabPanel.add(newComponent); // add the component to the TabPanel
+        myTabPanel.setActiveTab(newComponent);
+    },
+    failure: function() {
+        Ext.Msg.alert("Grid create failed", "Server communication failure");
+    }
+});
+</code></pre>
+ * <p>The server script needs to return a JSON representation of a configuration object, which, when decoded
+ * will return a config object with an {@link Ext.Component#xtype xtype}. The server might return the following
+ * JSON:</p><pre><code>
+{
+    "xtype": 'grid',
+    "title": 'Invoice Report',
+    "store": {
+        "model": 'Invoice',
+        "proxy": {
+            "type": 'ajax',
+            "url": 'get-invoice-data.php',
+            "reader": {
+                "type": 'json'
+                "record": 'transaction',
+                "idProperty": 'id',
+                "totalRecords": 'total'
+            })
+        },
+        "autoLoad": {
+            "params": {
+                "startDate": '01/01/2008',
+                "endDate": '01/31/2008'
+            }
+        }
+    },
+    "headers": [
+        {"header": "Customer", "width": 250, "dataIndex": 'customer', "sortable": true},
+        {"header": "Invoice Number", "width": 120, "dataIndex": 'invNo', "sortable": true},
+        {"header": "Invoice Date", "width": 100, "dataIndex": 'date', "renderer": Ext.util.Format.dateRenderer('M d, y'), "sortable": true},
+        {"header": "Value", "width": 120, "dataIndex": 'value', "renderer": 'usMoney', "sortable": true}
+    ]
+}
+</code></pre>
+ * <p>When the above code fragment is passed through the <code>eval</code> function in the success handler
+ * of the Ajax request, the result will be a config object which, when added to a Container, will cause instantiation
+ * of a GridPanel. <b>Be sure that the Container is configured with a layout which sizes and positions the child items to your requirements.</b></p>
+ * <p>Note: since the code above is <i>generated</i> by a server script, the <code>autoLoad</code> params for
+ * the Store, the user's preferred date format, the metadata to allow generation of the Model layout, and the ColumnModel
+ * can all be generated into the code since these are all known on the server.</p>
+ *
+ * @xtype container
+ */
+Ext.define('Ext.container.Container', {
+    extend: 'Ext.container.AbstractContainer',
+    alias: 'widget.container',
+    alternateClassName: 'Ext.Container',
+
+    /**
+     * Return the immediate child Component in which the passed element is located.
+     * @param el The element to test.
+     * @return {Component} The child item which contains the passed element.
+     */
+    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;
+    }
+});
+
+/**
+ * @class Ext.toolbar.Fill
+ * @extends Ext.Component
+ * A non-rendering placeholder item which instructs the Toolbar's Layout to begin using
+ * the right-justified button container.
+ *
+ * {@img Ext.toolbar.Fill/Ext.toolbar.Fill.png Toolbar Fill}
+ * Example usage:
+<pre><code>
+    Ext.create('Ext.panel.Panel', {
+        title: 'Toolbar Fill Example',
+        width: 300,
+        height: 200,
+        tbar : [
+            'Item 1',
+            {xtype: 'tbfill'}, // or '->'
+            'Item 2'
+        ],
+        renderTo: Ext.getBody()
+    });
+</code></pre>
+ * @constructor
+ * Creates a new Fill
+ * @xtype tbfill
+ */
+Ext.define('Ext.toolbar.Fill', {
+    extend: 'Ext.Component',
+    alias: 'widget.tbfill',
+    alternateClassName: 'Ext.Toolbar.Fill',
+    isFill : true,
+    flex: 1
+});
+/**
+ * @class Ext.toolbar.Item
+ * @extends Ext.Component
+ * The base class that other non-interacting Toolbar Item classes should extend in order to
+ * get some basic common toolbar item functionality.
+ * @constructor
+ * Creates a new Item
+ * @param {HTMLElement} el
+ * @xtype tbitem
+ */
+Ext.define('Ext.toolbar.Item', {
+    extend: 'Ext.Component',
+    alias: 'widget.tbitem',
+    alternateClassName: 'Ext.Toolbar.Item',
+    enable:Ext.emptyFn,
+    disable:Ext.emptyFn,
+    focus:Ext.emptyFn
+    /**
+     * @cfg {String} overflowText Text to be used for the menu if the item is overflowed.
+     */
+});
+/**
+ * @class Ext.toolbar.Separator
+ * @extends Ext.toolbar.Item
+ * A simple class that adds a vertical separator bar between toolbar items
+ * (css class:<tt>'x-toolbar-separator'</tt>). 
+ * {@img Ext.toolbar.Separator/Ext.toolbar.Separator.png Toolbar Separator}
+ * Example usage:
+ * <pre><code>
+    Ext.create('Ext.panel.Panel', {
+        title: 'Toolbar Seperator Example',
+        width: 300,
+        height: 200,
+        tbar : [
+            'Item 1',
+            {xtype: 'tbseparator'}, // or '-'
+            'Item 2'
+        ],
+        renderTo: Ext.getBody()
+    }); 
+</code></pre>
+ * @constructor
+ * Creates a new Separator
+ * @xtype tbseparator
+ */
+Ext.define('Ext.toolbar.Separator', {
+    extend: 'Ext.toolbar.Item',
+    alias: 'widget.tbseparator',
+    alternateClassName: 'Ext.Toolbar.Separator',
+    baseCls: Ext.baseCSSPrefix + 'toolbar-separator',
+    focusable: false
+});
+/**
+ * @class Ext.menu.Manager
+ * Provides a common registry of all menus on a page.
+ * @singleton
+ */
+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);
+    },
+
+    /**
+     * Hides all menus that are currently visible
+     * @return {Boolean} success True if any active menus were hidden.
+     */
+    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();
+        }
+    },
+
+    // private
+    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();
+        }
+    },
+
+    // private
+    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
+            });
+        }
+    },
+
+    /**
+     * Returns a {@link Ext.menu.Menu} object
+     * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
+     * be used to generate and return a new Menu this.
+     * @return {Ext.menu.Menu} The specified menu, or null if none are found
+     */
+    get: function(menu) {
+        var menus = this.menus;
+        
+        if (typeof menu == 'string') { // menu id
+            if (!menus) {  // not initialized, no menus to return
+                return null;
+            }
+            return menus[menu];
+        } else if (menu.isMenu) {  // menu instance
+            return menu;
+        } else if (Ext.isArray(menu)) { // array of menu items
+            return Ext.create('Ext.menu.Menu', {items:menu});
+        } else { // otherwise, must be a config
+            return Ext.ComponentManager.create(menu, 'menu');
+        }
+    },
+
+    // private
+    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
+        });
+    },
+
+    // private
+    registerCheckable: function(menuItem) {
+        var groups  = this.groups,
+            groupId = menuItem.group;
+
+        if (groupId) {
+            if (!groups[groupId]) {
+                groups[groupId] = [];
+            }
+
+            groups[groupId].push(menuItem);
+        }
+    },
+
+    // private
+    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);
+                }
+            }
+        }
+    }
+});
+/**
+ * @class Ext.button.Button
+ * @extends Ext.Component
+
+Create simple buttons with this component. Customisations include {@link #config-iconAlign aligned}
+{@link #config-iconCls icons}, {@link #config-menu dropdown menus}, {@link #config-tooltip tooltips}
+and {@link #config-scale sizing options}. Specify a {@link #config-handler handler} to run code when
+a user clicks the button, or use {@link #config-listeners listeners} for other events such as
+{@link #events-mouseover mouseover}.
+
+{@img Ext.button.Button/Ext.button.Button1.png Ext.button.Button component}
+Example usage:
+
+    Ext.create('Ext.Button', {
+        text: 'Click me',
+        renderTo: Ext.getBody(),        
+        handler: function() {
+            alert('You clicked the button!')
+        }
+    });
+
+The {@link #handler} configuration can also be updated dynamically using the {@link #setHandler} method.
+Example usage:
+
+    Ext.create('Ext.Button', {
+        text    : 'Dyanmic Handler Button',
+        renderTo: Ext.getBody(),
+        handler : function() {
+            //this button will spit out a different number every time you click it.
+            //so firstly we must check if that number is already set:
+            if (this.clickCount) {
+                //looks like the property is already set, so lets just add 1 to that number and alert the user
+                this.clickCount++;
+                alert('You have clicked the button "' + this.clickCount + '" times.\n\nTry clicking it again..');
+            } else {
+                //if the clickCount property is not set, we will set it and alert the user
+                this.clickCount = 1;
+                alert('You just clicked the button for the first time!\n\nTry pressing it again..');
+            }
+        }
+    });
+
+A button within a container:
+
+    Ext.create('Ext.Container', {
+        renderTo: Ext.getBody(),
+        items   : [
+            {
+                xtype: 'button',
+                text : 'My Button'
+            }
+        ]
+    });
+
+A useful option of Button is the {@link #scale} configuration. This configuration has three different options:
+* `'small'`
+* `'medium'`
+* `'large'`
+
+{@img Ext.button.Button/Ext.button.Button2.png Ext.button.Button component}
+Example usage:
+
+    Ext.create('Ext.Button', {
+        renderTo: document.body,
+        text    : 'Click me',
+        scale   : 'large'
+    });
+
+Buttons can also be toggled. To enable this, you simple set the {@link #enableToggle} property to `true`.
+{@img Ext.button.Button/Ext.button.Button3.png Ext.button.Button component}
+Example usage:
+
+    Ext.create('Ext.Button', {
+        renderTo: Ext.getBody(),
+        text: 'Click Me',
+        enableToggle: true
+    });
+
+You can assign a menu to a button by using the {@link #menu} configuration. This standard configuration can either be a reference to a {@link Ext.menu.Menu menu}
+object, a {@link Ext.menu.Menu menu} id or a {@link Ext.menu.Menu menu} config blob. When assigning a menu to a button, an arrow is automatically added to the button.
+You can change the alignment of the arrow using the {@link #arrowAlign} configuration on button.
+{@img Ext.button.Button/Ext.button.Button4.png Ext.button.Button component}
+Example usage:
+
+    Ext.create('Ext.Button', {
+        text      : 'Menu button',
+        renderTo  : Ext.getBody(),        
+        arrowAlign: 'bottom',
+        menu      : [
+            {text: 'Item 1'},
+            {text: 'Item 2'},
+            {text: 'Item 3'},
+            {text: 'Item 4'}
+        ]
+    });
+
+Using listeners, you can easily listen to events fired by any component, using the {@link #listeners} configuration or using the {@link #addListener} method.
+Button has a variety of different listeners:
+* `click`
+* `toggle`
+* `mouseover`
+* `mouseout`
+* `mouseshow`
+* `menuhide`
+* `menutriggerover`
+* `menutriggerout`
+
+Example usage:
+
+    Ext.create('Ext.Button', {
+        text     : 'Button',
+        renderTo : Ext.getBody(),
+        listeners: {
+            click: function() {
+                //this == the button, as we are in the local scope
+                this.setText('I was clicked!');
+            },
+            mouseover: function() {
+                //set a new config which says we moused over, if not already set
+                if (!this.mousedOver) {
+                    this.mousedOver = true;
+                    alert('You moused over a button!\n\nI wont do this again.');
+                }
+            }
+        }
+    });
+
+ * @constructor
+ * Create a new button
+ * @param {Object} config The config object
+ * @xtype button
+ * @markdown
+ * @docauthor Robert Dougan <rob@sencha.com>
+ */
+Ext.define('Ext.button.Button', {
+
+    /* Begin Definitions */
+    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',
+    /* End Definitions */
+
+    isButton: true,
+    componentLayout: 'button',
+
+    /**
+     * Read-only. True if this button is hidden
+     * @type Boolean
+     */
+    hidden: false,
+
+    /**
+     * Read-only. True if this button is disabled
+     * @type Boolean
+     */
+    disabled: false,
+
+    /**
+     * Read-only. True if this button is pressed (only if enableToggle = true)
+     * @type Boolean
+     */
+    pressed: false,
+
+    /**
+     * @cfg {String} text The button text to be used as innerHTML (html tags are accepted)
+     */
+
+    /**
+     * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
+     * CSS property of the button by default, so if you want a mixed icon/text button, set cls:'x-btn-text-icon')
+     */
+
+    /**
+     * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event).
+     * The handler is passed the following parameters:<div class="mdetail-params"><ul>
+     * <li><code>b</code> : Button<div class="sub-desc">This Button.</div></li>
+     * <li><code>e</code> : EventObject<div class="sub-desc">The click event.</div></li>
+     * </ul></div>
+     */
+
+    /**
+     * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width).
+     * See also {@link Ext.panel.Panel}.<tt>{@link Ext.panel.Panel#minButtonWidth minButtonWidth}</tt>.
+     */
+
+    /**
+     * @cfg {String/Object} tooltip The tooltip for the button - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object
+     */
+
+    /**
+     * @cfg {Boolean} hidden True to start hidden (defaults to false)
+     */
+
+    /**
+     * @cfg {Boolean} disabled True to start disabled (defaults to false)
+     */
+
+    /**
+     * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
+     */
+
+    /**
+     * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed)
+     */
+
+    /**
+     * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
+     * a {@link Ext.util.ClickRepeater ClickRepeater} config object (defaults to false).
+     */
+
+    /**
+     * @cfg {Number} tabIndex Set a DOM tabIndex for this button (defaults to undefined)
+     */
+
+    /**
+     * @cfg {Boolean} allowDepress
+     * False to not allow a pressed Button to be depressed (defaults to undefined). Only valid when {@link #enableToggle} is true.
+     */
+
+    /**
+     * @cfg {Boolean} enableToggle
+     * True to enable pressed/not pressed toggling (defaults to false)
+     */
+    enableToggle: false,
+
+    /**
+     * @cfg {Function} toggleHandler
+     * Function called when a Button with {@link #enableToggle} set to true is clicked. Two arguments are passed:<ul class="mdetail-params">
+     * <li><b>button</b> : Ext.button.Button<div class="sub-desc">this Button object</div></li>
+     * <li><b>state</b> : Boolean<div class="sub-desc">The next state of the Button, true means pressed.</div></li>
+     * </ul>
+     */
+
+    /**
+     * @cfg {Mixed} menu
+     * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
+     */
+
+    /**
+     * @cfg {String} menuAlign
+     * The position to align the menu to (see {@link Ext.core.Element#alignTo} for more details, defaults to 'tl-bl?').
+     */
+    menuAlign: 'tl-bl?',
+
+    /**
+     * @cfg {String} overflowText If used in a {@link Ext.toolbar.Toolbar Toolbar}, the
+     * text to be used if this item is shown in the overflow menu. See also
+     * {@link Ext.toolbar.Item}.<code>{@link Ext.toolbar.Item#overflowText overflowText}</code>.
+     */
+
+    /**
+     * @cfg {String} iconCls
+     * A css class which sets a background image to be used as the icon for this button
+     */
+
+    /**
+     * @cfg {String} type
+     * submit, reset or button - defaults to 'button'
+     */
+    type: 'button',
+
+    /**
+     * @cfg {String} clickEvent
+     * The DOM event that will fire the handler of the button. This can be any valid event name (dblclick, contextmenu).
+     * Defaults to <tt>'click'</tt>.
+     */
+    clickEvent: 'click',
+    
+    /**
+     * @cfg {Boolean} preventDefault
+     * True to prevent the default action when the {@link #clickEvent} is processed. Defaults to true.
+     */
+    preventDefault: true,
+
+    /**
+     * @cfg {Boolean} handleMouseEvents
+     * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
+     */
+    handleMouseEvents: true,
+
+    /**
+     * @cfg {String} tooltipType
+     * The type of tooltip to use. Either 'qtip' (default) for QuickTips or 'title' for title attribute.
+     */
+    tooltipType: 'qtip',
+
+    /**
+     * @cfg {String} baseCls
+     * The base CSS class to add to all buttons. (Defaults to 'x-btn')
+     */
+    baseCls: Ext.baseCSSPrefix + 'btn',
+
+    /**
+     * @cfg {String} pressedCls
+     * The CSS class to add to a button when it is in the pressed state. (Defaults to 'x-btn-pressed')
+     */
+    pressedCls: 'pressed',
+    
+    /**
+     * @cfg {String} overCls
+     * The CSS class to add to a button when it is in the over (hovered) state. (Defaults to 'x-btn-over')
+     */
+    overCls: 'over',
+    
+    /**
+     * @cfg {String} focusCls
+     * The CSS class to add to a button when it is in the focussed state. (Defaults to 'x-btn-focus')
+     */
+    focusCls: 'focus',
+    
+    /**
+     * @cfg {String} menuActiveCls
+     * The CSS class to add to a button when it's menu is active. (Defaults to 'x-btn-menu-active')
+     */
+    menuActiveCls: 'menu-active',
+
+    ariaRole: 'button',
+
+    // inherited
+    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"' +
+                    // the autocomplete="off" is required to prevent Firefox from remembering
+                    // the button's disabled state between page reloads.
+                    '<tpl if="tabIndex"> tabIndex="{tabIndex}"</tpl> role="button" autocomplete="off">' +
+                    '<span class="{baseCls}-inner" style="{innerSpanStyle}">{text}</span>' +
+                '</button>' +
+            '</tpl>' +
+        '</em>' ,
+
+    /**
+     * @cfg {String} scale
+     * <p>(Optional) The size of the Button. Three values are allowed:</p>
+     * <ul class="mdetail-params">
+     * <li>'small'<div class="sub-desc">Results in the button element being 16px high.</div></li>
+     * <li>'medium'<div class="sub-desc">Results in the button element being 24px high.</div></li>
+     * <li>'large'<div class="sub-desc">Results in the button element being 32px high.</div></li>
+     * </ul>
+     * <p>Defaults to <b><tt>'small'</tt></b>.</p>
+     */
+    scale: 'small',
+    
+    /**
+     * @private An array of allowed scales.
+     */
+    allowedScales: ['small', 'medium', 'large'],
+    
+    /**
+     * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the
+     * <code>{@link #handler}</code> and <code>{@link #toggleHandler}</code> is
+     * executed. Defaults to this Button.
+     */
+
+    /**
+     * @cfg {String} iconAlign
+     * <p>(Optional) The side of the Button box to render the icon. Four values are allowed:</p>
+     * <ul class="mdetail-params">
+     * <li>'top'<div class="sub-desc"></div></li>
+     * <li>'right'<div class="sub-desc"></div></li>
+     * <li>'bottom'<div class="sub-desc"></div></li>
+     * <li>'left'<div class="sub-desc"></div></li>
+     * </ul>
+     * <p>Defaults to <b><tt>'left'</tt></b>.</p>
+     */
+    iconAlign: 'left',
+
+    /**
+     * @cfg {String} arrowAlign
+     * <p>(Optional) The side of the Button box to render the arrow if the button has an associated {@link #menu}.
+     * Two values are allowed:</p>
+     * <ul class="mdetail-params">
+     * <li>'right'<div class="sub-desc"></div></li>
+     * <li>'bottom'<div class="sub-desc"></div></li>
+     * </ul>
+     * <p>Defaults to <b><tt>'right'</tt></b>.</p>
+     */
+    arrowAlign: 'right',
+
+    /**
+     * @cfg {String} arrowCls
+     * <p>(Optional) The className used for the inner arrow element if the button has a menu.</p>
+     */
+    arrowCls: 'arrow',
+
+    /**
+     * @cfg {Ext.Template} template (Optional)
+     * <p>A {@link Ext.Template Template} used to create the Button's DOM structure.</p>
+     * Instances, or subclasses which need a different DOM structure may provide a different
+     * template layout in conjunction with an implementation of {@link #getTemplateArgs}.
+     * @type Ext.Template
+     * @property template
+     */
+
+    /**
+     * @cfg {String} cls
+     * A CSS class string to apply to the button's main element.
+     */
+
+    /**
+     * @property menu
+     * @type Menu
+     * The {@link Ext.menu.Menu Menu} object associated with this Button when configured with the {@link #menu} config option.
+     */
+
+    /**
+     * @cfg {Boolean} autoWidth
+     * By default, if a width is not specified the button will attempt to stretch horizontally to fit its content.
+     * If the button is being managed by a width sizing layout (hbox, fit, anchor), set this to false to prevent
+     * the button from doing this automatic sizing.
+     * Defaults to <tt>undefined</tt>.
+     */
+     
+    maskOnDisable: false,
+
+    // inherit docs
+    initComponent: function() {
+        var me = this;
+        me.callParent(arguments);
+
+        me.addEvents(
+            /**
+             * @event click
+             * Fires when this button is clicked
+             * @param {Button} this
+             * @param {EventObject} e The click event
+             */
+            'click',
+
+            /**
+             * @event toggle
+             * Fires when the 'pressed' state of this button changes (only if enableToggle = true)
+             * @param {Button} this
+             * @param {Boolean} pressed
+             */
+            'toggle',
+
+            /**
+             * @event mouseover
+             * Fires when the mouse hovers over the button
+             * @param {Button} this
+             * @param {Event} e The event object
+             */
+            'mouseover',
+
+            /**
+             * @event mouseout
+             * Fires when the mouse exits the button
+             * @param {Button} this
+             * @param {Event} e The event object
+             */
+            'mouseout',
+
+            /**
+             * @event menushow
+             * If this button has a menu, this event fires when it is shown
+             * @param {Button} this
+             * @param {Menu} menu
+             */
+            'menushow',
+
+            /**
+             * @event menuhide
+             * If this button has a menu, this event fires when it is hidden
+             * @param {Button} this
+             * @param {Menu} menu
+             */
+            'menuhide',
+
+            /**
+             * @event menutriggerover
+             * If this button has a menu, this event fires when the mouse enters the menu triggering element
+             * @param {Button} this
+             * @param {Menu} menu
+             * @param {EventObject} e
+             */
+            'menutriggerover',
+
+            /**
+             * @event menutriggerout
+             * If this button has a menu, this event fires when the mouse leaves the menu triggering element
+             * @param {Button} this
+             * @param {Menu} menu
+             * @param {EventObject} e
+             */
+            'menutriggerout'
+        );
+
+        if (me.menu) {
+            // Flag that we'll have a splitCls
+            me.split = true;
+
+            // retrieve menu by id or instantiate instance if needed
+            me.menu = Ext.menu.Manager.get(me.menu);
+            me.menu.ownerCt = me;
+        }
+
+        // Accept url as a synonym for href
+        if (me.url) {
+            me.href = me.url;
+        }
+
+        // preventDefault defaults to false for links
+        if (me.href && !me.hasOwnProperty('preventDefault')) {
+            me.preventDefault = false;
+        }
+
+        if (Ext.isString(me.toggleGroup)) {
+            me.enableToggle = true;
+        }
+
+    },
+
+    // private
+    initAria: function() {
+        this.callParent();
+        var actionEl = this.getActionEl();
+        if (this.menu) {
+            actionEl.dom.setAttribute('aria-haspopup', true);
+        }
+    },
+
+    // inherit docs
+    getActionEl: function() {
+        return this.btnEl;
+    },
+
+    // inherit docs
+    getFocusEl: function() {
+        return this.btnEl;
+    },
+
+    // private
+    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);
+            }
+            
+            // Check whether the button has an icon or not, and if it has an icon, what is th alignment
+            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);
+        }
+    },
+    
+    // private
+    onRender: function(ct, position) {
+        // classNames for the button
+        var me = this,
+            repeater, btn;
+            
+        // Apply the renderData to the template args
+        Ext.applyIf(me.renderData, me.getTemplateArgs());
+
+        // Extract the button and the button wrapping element
+        Ext.applyIf(me.renderSelectors, {
+            btnEl  : me.href ? 'a' : 'button',
+            btnWrap: 'em',
+            btnInnerEl: '.' + me.baseCls + '-inner'
+        });
+        
+        if (me.scale) {
+            me.ui = me.ui + '-' + me.scale;
+        }
+
+        // Render internal structure
+        me.callParent(arguments);
+
+        // If it is a split button + has a toolip for the arrow
+        if (me.split && me.arrowTooltip) {
+            me.arrowEl.dom[me.tooltipType] = me.arrowTooltip;
+        }
+
+        // Add listeners to the focus and blur events on the element
+        me.mon(me.btnEl, {
+            scope: me,
+            focus: me.onFocus,
+            blur : me.onBlur
+        });
+
+        // Set btn as a local variable for easy access
+        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);
+        }
+
+        // Add the mouse events to the button
+        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
+                });
+            }
+        }
+
+        // Check if the button has a menu
+        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
+            });
+        }
+
+        // Check if it is a repeat button
+        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);
+        }
+
+        // Register the button in the toggle manager
+        Ext.ButtonToggleManager.register(me);
+    },
+
+    /**
+     * <p>This method returns an object which provides substitution parameters for the {@link #renderTpl XTemplate} used
+     * to create this Button's DOM structure.</p>
+     * <p>Instances or subclasses which use a different Template to create a different DOM structure may need to provide their
+     * own implementation of this method.</p>
+     * <p>The default implementation which provides data for the default {@link #template} returns an Object containing the
+     * following properties:</p><div class="mdetail-params"><ul>
+     * <li><code>type</code> : The &lt;button&gt;'s {@link #type}</li>
+     * <li><code>splitCls</code> : A CSS class to determine the presence and position of an arrow icon. (<code>'x-btn-arrow'</code> or <code>'x-btn-arrow-bottom'</code> or <code>''</code>)</li>
+     * <li><code>cls</code> : A CSS class name applied to the Button's main &lt;tbody&gt; element which determines the button's scale and icon alignment.</li>
+     * <li><code>text</code> : The {@link #text} to display ion the Button.</li>
+     * <li><code>tabIndex</code> : The tab index within the input flow.</li>
+     * </ul></div>
+     * @return {Array} Substitution data for a Template.
+    */
+    getTemplateArgs: function() {
+        var me = this,
+            persistentPadding = me.getPersistentBtnPadding(),
+            innerSpanStyle = '';
+
+        // Create negative margin offsets to counteract persistent button padding if needed
+        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
+        };
+    },
+
+    /**
+     * @private
+     * If there is a configured href for this Button, returns the href with parameters appended.
+     * @returns The href string with parameters appended.
+     */
+    getHref: function() {
+        var me = this;
+        return me.href ? Ext.urlAppend(me.href, me.params + Ext.Object.toQueryString(Ext.apply(Ext.apply({}, me.baseParams)))) : false;
+    },
+
+    /**
+     * <p><b>Only valid if the Button was originally configured with a {@link #url}</b></p>
+     * <p>Sets the href of the link dynamically according to the params passed, and any {@link #baseParams} configured.</p>
+     * @param {Object} Parameters to use in the href URL.
+     */
+    setParams: function(p) {
+        this.params = p;
+        this.btnEl.dom.href = this.getHref();
+    },
+
+    getSplitCls: function() {
+        var me = this;
+        return me.split ? (me.baseCls + '-' + me.arrowCls) + ' ' + (me.baseCls + '-' + me.arrowCls + '-' + me.arrowAlign) : '';
+    },
+
+    // private
+    afterRender: function() {
+        var me = this;
+        me.useSetClass = true;
+        me.setButtonCls();
+        me.doc = Ext.getDoc();
+        this.callParent(arguments);
+    },
+
+    /**
+     * Sets the CSS class that provides a background image to use as the button's icon.  This method also changes
+     * the value of the {@link #iconCls} config internally.
+     * @param {String} cls The CSS class providing the icon image
+     * @return {Ext.button.Button} this
+     */
+    setIconCls: function(cls) {
+        var me = this,
+            btnInnerEl = me.btnInnerEl;
+        if (btnInnerEl) {
+            // Remove the previous iconCls from the button
+            btnInnerEl.removeCls(me.iconCls);
+            btnInnerEl.addCls(cls || '');
+            me.setButtonCls();
+        }
+        me.iconCls = cls;
+        return me;
+    },
+
+    /**
+     * Sets the tooltip for this Button.
+     * @param {String/Object} tooltip. This may be:<div class="mdesc-details"><ul>
+     * <li><b>String</b> : A string to be used as innerHTML (html tags are accepted) to show in a tooltip</li>
+     * <li><b>Object</b> : A configuration object for {@link Ext.tip.QuickTipManager#register}.</li>
+     * </ul></div>
+     * @return {Ext.button.Button} this
+     */
+    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;
+    },
+
+    // private
+    getRefItems: function(deep){
+        var menu = this.menu,
+            items;
+
+        if (menu) {
+            items = menu.getRefItems(deep);
+            items.unshift(menu);
+        }
+        return items || [];
+    },
+
+    // private
+    clearTip: function() {
+        if (Ext.isObject(this.tooltip)) {
+            Ext.tip.QuickTipManager.unregister(this.btnEl);
+        }
+    },
+
+    // private
+    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);
+    },
+
+    // private
+    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();
+    },
+
+    /**
+     * Assigns this Button's click handler
+     * @param {Function} handler The function to call when the button is clicked
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
+     * Defaults to this Button.
+     * @return {Ext.button.Button} this
+     */
+    setHandler: function(handler, scope) {
+        this.handler = handler;
+        this.scope = scope;
+        return this;
+    },
+
+    /**
+     * Sets this Button's text
+     * @param {String} text The button text
+     * @return {Ext.button.Button} this
+     */
+    setText: function(text) {
+        var me = this;
+        me.text = text;
+        if (me.el) {
+            me.btnInnerEl.update(text || '&#160;');
+            me.setButtonCls();
+        }
+        me.doComponentLayout();
+        return me;
+    },
+
+    /**
+     * Sets the background image (inline style) of the button.  This method also changes
+     * the value of the {@link #icon} config internally.
+     * @param {String} icon The path to an image to display in the button
+     * @return {Ext.button.Button} this
+     */
+    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;
+    },
+
+    /**
+     * Gets the text for this Button
+     * @return {String} The button text
+     */
+    getText: function() {
+        return this.text;
+    },
+
+    /**
+     * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
+     * @param {Boolean} state (optional) Force a particular state
+     * @param {Boolean} supressEvent (optional) True to stop events being fired when calling this method.
+     * @return {Ext.button.Button} this
+     */
+    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;
+    },
+
+    /**
+     * Show this button's menu (if it has one)
+     */
+    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;
+    },
+
+    /**
+     * Hide this button's menu (if it has one)
+     */
+    hideMenu: function() {
+        if (this.hasVisibleMenu()) {
+            this.menu.hide();
+        }
+        return this;
+    },
+
+    /**
+     * Returns true if the button has a menu and it is visible
+     * @return {Boolean}
+     */
+    hasVisibleMenu: function() {
+        var menu = this.menu;
+        return menu && menu.rendered && menu.isVisible();
+    },
+
+    // private
+    onRepeatClick: function(repeat, e) {
+        this.onClick(e);
+    },
+
+    // private
+    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();
+        }
+    },
+
+    /**
+     * @private mouseover handler called when a mouseover event occurs anywhere within the encapsulating element.
+     * The targets are interrogated to see what is being entered from where.
+     * @param e
+     */
+    onMouseOver: function(e) {
+        var me = this;
+        if (!me.disabled && !e.within(me.el, true, true)) {
+            me.onMouseEnter(e);
+        }
+    },
+
+    /**
+     * @private mouseout handler called when a mouseout event occurs anywhere within the encapsulating element -
+     * or the mouse leaves the encapsulating element.
+     * The targets are interrogated to see what is being exited to where.
+     * @param e
+     */
+    onMouseOut: function(e) {
+        var me = this;
+        if (!e.within(me.el, true, true)) {
+            if (me.overMenuTrigger) {
+                me.onMenuTriggerOut(e);
+            }
+            me.onMouseLeave(e);
+        }
+    },
+
+    /**
+     * @private mousemove handler called when the mouse moves anywhere within the encapsulating element.
+     * The position is checked to determine if the mouse is entering or leaving the trigger area. Using
+     * mousemove to check this is more resource intensive than we'd like, but it is necessary because
+     * the trigger area does not line up exactly with sub-elements so we don't always get mouseover/out
+     * events when needed. In the future we should consider making the trigger a separate element that
+     * is absolutely positioned and sized over the trigger area.
+     */
+    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);
+                }
+            }
+        }
+    },
+
+    /**
+     * @private Measures the size of the trigger area for menu and split buttons. Will be a width for
+     * a right-aligned trigger and a height for a bottom-aligned trigger. Cached after first measurement.
+     */
+    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;
+    },
+
+    /**
+     * @private virtual mouseenter handler called when it is detected that the mouseout event
+     * signified the mouse entering the encapsulating element.
+     * @param e
+     */
+    onMouseEnter: function(e) {
+        var me = this;
+        me.addClsWithUI(me.overCls);
+        me.fireEvent('mouseover', me, e);
+    },
+
+    /**
+     * @private virtual mouseleave handler called when it is detected that the mouseover event
+     * signified the mouse entering the encapsulating element.
+     * @param e
+     */
+    onMouseLeave: function(e) {
+        var me = this;
+        me.removeClsWithUI(me.overCls);
+        me.fireEvent('mouseout', me, e);
+    },
+
+    /**
+     * @private virtual mouseenter handler called when it is detected that the mouseover event
+     * signified the mouse entering the arrow area of the button - the <em>.
+     * @param e
+     */
+    onMenuTriggerOver: function(e) {
+        var me = this;
+        me.overMenuTrigger = true;
+        me.fireEvent('menutriggerover', me, me.menu, e);
+    },
+
+    /**
+     * @private virtual mouseleave handler called when it is detected that the mouseout event
+     * signified the mouse leaving the arrow area of the button - the <em>.
+     * @param e
+     */
+    onMenuTriggerOut: function(e) {
+        var me = this;
+        delete me.overMenuTrigger;
+        me.fireEvent('menutriggerout', me, me.menu, e);
+    },
+    
+    // inherit docs
+    enable : function(silent) {
+        var me = this;
+
+        me.callParent(arguments);
+        
+        me.removeClsWithUI('disabled');
+
+        return me;
+    },
+
+    // inherit docs
+    disable : function(silent) {
+        var me = this;
+        
+        me.callParent(arguments);
+        
+        me.addClsWithUI('disabled');
+
+        return me;
+    },
+    
+    /**
+     * Method to change the scale of the button. See {@link #scale} for allowed configurations.
+     * @param {String} scale The scale to change to.
+     */
+    setScale: function(scale) {
+        var me = this,
+            ui = me.ui.replace('-' + me.scale, '');
+        
+        //check if it is an allowed 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);
+    },
+    
+    // inherit docs
+    setUI: function(ui) {
+        var me = this;
+        
+        //we need to append the scale to the UI, if not already done
+        if (me.scale && !ui.match(me.scale)) {
+            ui = ui + '-' + me.scale;
+        }
+        
+        me.callParent([ui]);
+        
+        // Set all the state classNames, as they need to include the UI
+        // me.disabledCls += ' ' + me.baseCls + '-' + me.ui + '-disabled';
+    },
+    
+    // private
+    onFocus: function(e) {
+        var me = this;
+        if (!me.disabled) {
+            me.addClsWithUI(me.focusCls);
+        }
+    },
+
+    // private
+    onBlur: function(e) {
+        var me = this;
+        me.removeClsWithUI(me.focusCls);
+    },
+
+    // private
+    onMouseDown: function(e) {
+        var me = this;
+        if (!me.disabled && e.button === 0) {
+            me.addClsWithUI(me.pressedCls);
+            me.doc.on('mouseup', me.onMouseUp, me);
+        }
+    },
+    // private
+    onMouseUp: function(e) {
+        var me = this;
+        if (e.button === 0) {
+            if (!me.pressed) {
+                me.removeClsWithUI(me.pressedCls);
+            }
+            me.doc.un('mouseup', me.onMouseUp, me);
+        }
+    },
+    // private
+    onMenuShow: function(e) {
+        var me = this;
+        me.ignoreNextClick = 0;
+        me.addClsWithUI(me.menuActiveCls);
+        me.fireEvent('menushow', me, me.menu);
+    },
+
+    // private
+    onMenuHide: function(e) {
+        var me = this;
+        me.removeClsWithUI(me.menuActiveCls);
+        me.ignoreNextClick = Ext.defer(me.restoreClick, 250, me);
+        me.fireEvent('menuhide', me, me.menu);
+    },
+
+    // private
+    restoreClick: function() {
+        this.ignoreNextClick = 0;
+    },
+
+    // private
+    onDownKey: function() {
+        var me = this;
+
+        if (!me.disabled) {
+            if (me.menu) {
+                me.showMenu();
+            }
+        }
+    },
+
+    /**
+     * @private Some browsers (notably Safari and older Chromes on Windows) add extra "padding" inside the button
+     * element that cannot be removed. This method returns the size of that padding with a one-time detection.
+     * @return Array [top, right, bottom, left]
+     */
+    getPersistentBtnPadding: function() {
+        var cls = Ext.button.Button,
+            padding = cls.persistentPadding,
+            btn, leftTop, btnEl, btnInnerEl;
+
+        if (!padding) {
+            padding = cls.persistentPadding = [0, 0, 0, 0]; //set early to prevent recursion
+
+            if (!Ext.isIE) { //short-circuit IE as it sometimes gives false positive for padding
+                // Create auto-size button offscreen and measure its insides
+                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); //clear any hard dimensions on the button el to see what it does naturally
+
+                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);
+                }
+            }
+        }
+    }
+    // Private utility class used by Button
+    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);
+            }
+        },
+
+        /**
+        * Gets the pressed button in the passed group or null
+        * @param {String} group
+        * @return Button
+        */
+        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;
+        }
+    };
+});
+
+/**
+ * @class Ext.layout.container.boxOverflow.Menu
+ * @extends Ext.layout.container.boxOverflow.None
+ * @private
+ */
+Ext.define('Ext.layout.container.boxOverflow.Menu', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.layout.container.boxOverflow.None',
+    requires: ['Ext.toolbar.Separator', 'Ext.button.Button'],
+    alternateClassName: 'Ext.layout.boxOverflow.Menu',
+    
+    /* End Definitions */
+
+    /**
+     * @cfg {String} afterCtCls
+     * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
+     * which must always be present at the rightmost edge of the Container
+     */
+
+    /**
+     * @property noItemsMenuText
+     * @type String
+     * HTML fragment to render into the toolbar overflow menu if there are no items to display
+     */
+    noItemsMenuText : '<div class="' + Ext.baseCSSPrefix + 'toolbar-no-items">(None)</div>',
+
+    constructor: function(layout) {
+        var me = this;
+
+        me.callParent(arguments);
+
+        // Before layout, we need to re-show all items which we may have hidden due to a previous overflow.
+        layout.beforeLayout = Ext.Function.createInterceptor(layout.beforeLayout, this.clearOverflow, this);
+
+        me.afterCtCls = me.afterCtCls || Ext.baseCSSPrefix + 'box-menu-' + layout.parallelAfter;
+        /**
+         * @property menuItems
+         * @type Array
+         * Array of all items that are currently hidden and should go into the dropdown menu
+         */
+        me.menuItems = [];
+    },
+
+    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]();
+
+        // Center the menuTrigger button.
+        // TODO: Should we emulate align: 'middle' like this, or should we 'stretchmax' the menuTrigger?
+        posArgs[layout.perpendicularSizeIndex] = (calculations.meta.maxSize - me.menuTrigger['get' + layout.perpendicularPrefixCap]()) / 2;
+        me.menuTrigger.setPosition.apply(me.menuTrigger, posArgs);
+
+        return { targetSize: newSize };
+    },
+
+    /**
+     * @private
+     * Called by the layout, when it determines that there is no overflow.
+     * Also called as an interceptor to the layout's onLayout method to reshow
+     * previously hidden overflowing items.
+     */
+    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;
+    },
+
+    /**
+     * @private
+     */
+    showTrigger: function() {
+        this.menuTrigger.show();
+    },
+
+    /**
+     * @private
+     */
+    hideTrigger: function() {
+        if (this.menuTrigger != undefined) {
+            this.menuTrigger.hide();
+        }
+    },
+
+    /**
+     * @private
+     * Called before the overflow menu is shown. This constructs the menu's items, caching them for as long as it can.
+     */
+    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];
+
+            // Do not show a separator as a first item
+            if (!i && (item instanceof Ext.toolbar.Separator)) {
+                continue;
+            }
+            if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
+                menu.add('-');
+            }
+
+            me.addComponentToMenu(menu, item);
+            prev = item;
+        }
+
+        // put something so the menu isn't empty if no compatible items found
+        if (menu.items.length < 1) {
+            menu.add(me.noItemsMenuText);
+        }
+    },
+    
+    /**
+     * @private
+     * Returns a menu config for a given component. This config is used to create a menu item
+     * to be added to the expander menu
+     * @param {Ext.Component} component The component to create the config for
+     * @param {Boolean} hideOnClick Passed through to the menu item
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
+     * @param {Ext.menu.Menu} menu The menu to add to
+     * @param {Ext.Component} component The component to add
+     */
+    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)));
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
+     * splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
+     */
+    clearMenu : function() {
+        var menu = this.moreMenu;
+        if (menu && menu.items) {
+            menu.items.each(function(item) {
+                if (item.menu) {
+                    delete item.menu;
+                }
+            });
+        }
+    },
+
+    /**
+     * @private
+     * Creates the overflow trigger and menu used when enableOverflow is set to true and the items
+     * in the layout are too wide to fit in the space available
+     */
+    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();
+
+            /**
+             * @private
+             * @property menu
+             * @type Ext.menu.Menu
+             * The expand menu - holds items for every item that cannot be shown
+             * because the container is currently not large enough.
+             */
+            me.menu = Ext.create('Ext.menu.Menu', {
+                hideMode: 'offsets',
+                listeners: {
+                    scope: me,
+                    beforeshow: me.beforeMenuShow
+                }
+            });
+
+            /**
+             * @private
+             * @property menuTrigger
+             * @type Ext.button.Button
+             * The expand button which triggers the overflow menu to be shown
+             */
+            me.menuTrigger = Ext.create('Ext.button.Button', {
+                ownerCt : me.layout.owner, // To enable the Menu to ascertain a valid zIndexManager owner in the same tree
+                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();
+
+        // Hide all items which are off the end, and store them to allow them to be restored
+        // before each layout operation.
+        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();
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Creates the beforeCt, innerCt and afterCt elements if they have not already been created
+     * @param {Ext.container.Container} container The Container attached to this Layout instance
+     * @param {Ext.core.Element} target The target Element
+     */
+    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');
+        }
+    },
+
+    /**
+     * @private
+     */
+    destroy: function() {
+        Ext.destroy(this.menu, this.menuTrigger);
+    }
+});
+/**
+ * @class Ext.util.Region
+ * @extends Object
+ *
+ * Represents a rectangular region and provides a number of utility methods
+ * to compare regions.
+ */
+
+Ext.define('Ext.util.Region', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.util.Offset'],
+
+    statics: {
+        /**
+         * @static
+         * @param {Mixed} el A string, DomElement or Ext.core.Element representing an element
+         * on the page.
+         * @returns {Ext.util.Region} region
+         * Retrieves an Ext.util.Region for a particular element.
+         */
+        getRegion: function(el) {
+            return Ext.fly(el).getPageBox(true);
+        },
+
+        /**
+         * @static
+         * @param {Object} o An object with top, right, bottom, left properties
+         * @return {Ext.util.Region} region The region constructed based on the passed object
+         */
+        from: function(o) {
+            return new this(o.top, o.right, o.bottom, o.left);
+        }
+    },
+
+    /* End Definitions */
+
+    /**
+     * @constructor
+     * @param {Number} top Top
+     * @param {Number} right Right
+     * @param {Number} bottom Bottom
+     * @param {Number} left 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;
+    },
+
+    /**
+     * Checks if this region completely contains the region that is passed in.
+     * @param {Ext.util.Region} region
+     */
+    contains : function(region) {
+        var me = this;
+        return (region.x >= me.x &&
+                region.right <= me.right &&
+                region.y >= me.y &&
+                region.bottom <= me.bottom);
+
+    },
+
+    /**
+     * Checks if this region intersects the region passed in.
+     * @param {Ext.util.Region} region
+     * @return {Ext.util.Region/Boolean} Returns the intersected region or false if there is no intersection.
+     */
+    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;
+        }
+    },
+
+    /**
+     * Returns the smallest region that contains the current AND targetRegion.
+     * @param {Ext.util.Region} region
+     */
+    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);
+    },
+
+    /**
+     * Modifies the current region to be constrained to the targetRegion.
+     * @param {Ext.util.Region} targetRegion
+     */
+    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;
+    },
+
+    /**
+     * Modifies the current region to be adjusted by offsets.
+     * @param {Number} top top offset
+     * @param {Number} right right offset
+     * @param {Number} bottom bottom offset
+     * @param {Number} left left offset
+     */
+    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;
+    },
+
+    /**
+     * Get the offset amount of a point outside the region
+     * @param {String} axis optional
+     * @param {Ext.util.Point} p the point
+     * @return {Ext.util.Offset}
+     */
+    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;
+        }
+
+    },
+
+    /**
+     * Get the offset amount on the x-axis
+     * @param {Number} p the offset
+     * @return {Number}
+     */
+    getOutOfBoundOffsetX: function(p) {
+        if (p <= this.x) {
+            return this.x - p;
+        } else if (p >= this.right) {
+            return this.right - p;
+        }
+
+        return 0;
+    },
+
+    /**
+     * Get the offset amount on the y-axis
+     * @param {Number} p the offset
+     * @return {Number}
+     */
+    getOutOfBoundOffsetY: function(p) {
+        if (p <= this.y) {
+            return this.y - p;
+        } else if (p >= this.bottom) {
+            return this.bottom - p;
+        }
+
+        return 0;
+    },
+
+    /**
+     * Check whether the point / offset is out of bound
+     * @param {String} axis optional
+     * @param {Ext.util.Point/Number} p the point / offset
+     * @return {Boolean}
+     */
+    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));
+        }
+    },
+
+    /**
+     * Check whether the offset is out of bound in the x-axis
+     * @param {Number} p the offset
+     * @return {Boolean}
+     */
+    isOutOfBoundX: function(p) {
+        return (p < this.x || p > this.right);
+    },
+
+    /**
+     * Check whether the offset is out of bound in the y-axis
+     * @param {Number} p the offset
+     * @return {Boolean}
+     */
+    isOutOfBoundY: function(p) {
+        return (p < this.y || p > this.bottom);
+    },
+
+    /*
+     * Restrict a point within the region by a certain factor.
+     * @param {String} axis Optional
+     * @param {Ext.util.Point/Ext.util.Offset/Object} p
+     * @param {Number} factor
+     * @return {Ext.util.Point/Ext.util.Offset/Object/Number}
+     */
+    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);
+            }
+        }
+    },
+
+    /*
+     * Restrict an offset within the region by a certain factor, on the x-axis
+     * @param {Number} p
+     * @param {Number} factor The factor, optional, defaults to 1
+     * @return
+     */
+    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;
+    },
+
+    /*
+     * Restrict an offset within the region by a certain factor, on the y-axis
+     * @param {Number} p
+     * @param {Number} factor The factor, optional, defaults to 1
+     */
+    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;
+    },
+
+    /*
+     * Get the width / height of this region
+     * @return {Object} an object with width and height properties
+     */
+    getSize: function() {
+        return {
+            width: this.right - this.x,
+            height: this.bottom - this.y
+        };
+    },
+
+    /**
+     * Copy a new instance
+     * @return {Ext.util.Region}
+     */
+    copy: function() {
+        return new this.self(this.y, this.right, this.bottom, this.x);
+    },
+
+    /**
+     * Copy the values of another Region to this Region
+     * @param {Region} The region to copy from.
+     * @return {Ext.util.Point} this This point
+     */
+    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;
+    },
+
+    /**
+     * Dump this to an eye-friendly string, great for debugging
+     * @return {String}
+     */
+    toString: function() {
+        return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
+    },
+
+
+    /**
+     * Translate this region by the given offset amount
+     * @param {Ext.util.Offset/Object} offset Object containing the <code>x</code> and <code>y</code> properties.
+     * Or the x value is using the two argument form.
+     * @param {Number} The y value unless using an Offset object.
+     * @return {Ext.util.Region} this This Region
+     */
+    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 all the properties of this region
+     * @return {Ext.util.Region} this This Region
+     */
+    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;
+    },
+
+    /**
+     * Check whether this region is equivalent to the given region
+     * @param {Ext.util.Region} region The region to compare with
+     * @return {Boolean}
+     */
+    equals: function(region) {
+        return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left);
+    }
+});
+
+/*
+ * This is a derivative of the similarly named class in the YUI Library.
+ * The original license:
+ * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+
+/**
+ * @class Ext.dd.DragDropManager
+ * DragDropManager is a singleton that tracks the element interaction for
+ * all DragDrop items in the window.  Generally, you will not call
+ * this class directly, but it does have helper methods that could
+ * be useful in your DragDrop implementations.
+ * @singleton
+ */
+Ext.define('Ext.dd.DragDropManager', {
+    singleton: true,
+
+    requires: ['Ext.util.Region'],
+
+    uses: ['Ext.tip.QuickTipManager'],
+
+    // shorter ClassName, to save bytes and use internally
+    alternateClassName: ['Ext.dd.DragDropMgr', 'Ext.dd.DDM'],
+    
+    /**
+     * Two dimensional Array of registered DragDrop objects.  The first
+     * dimension is the DragDrop item group, the second the DragDrop
+     * object.
+     * @property ids
+     * @type String[]
+     * @private
+     * @static
+     */
+    ids: {},
+
+    /**
+     * Array of element ids defined as drag handles.  Used to determine
+     * if the element that generated the mousedown event is actually the
+     * handle and not the html element itself.
+     * @property handleIds
+     * @type String[]
+     * @private
+     * @static
+     */
+    handleIds: {},
+
+    /**
+     * the DragDrop object that is currently being dragged
+     * @property dragCurrent
+     * @type DragDrop
+     * @private
+     * @static
+     **/
+    dragCurrent: null,
+
+    /**
+     * the DragDrop object(s) that are being hovered over
+     * @property dragOvers
+     * @type Array
+     * @private
+     * @static
+     */
+    dragOvers: {},
+
+    /**
+     * the X distance between the cursor and the object being dragged
+     * @property deltaX
+     * @type int
+     * @private
+     * @static
+     */
+    deltaX: 0,
+
+    /**
+     * the Y distance between the cursor and the object being dragged
+     * @property deltaY
+     * @type int
+     * @private
+     * @static
+     */
+    deltaY: 0,
+
+    /**
+     * Flag to determine if we should prevent the default behavior of the
+     * events we define. By default this is true, but this can be set to
+     * false if you need the default behavior (not recommended)
+     * @property preventDefault
+     * @type boolean
+     * @static
+     */
+    preventDefault: true,
+
+    /**
+     * Flag to determine if we should stop the propagation of the events
+     * we generate. This is true by default but you may want to set it to
+     * false if the html element contains other features that require the
+     * mouse click.
+     * @property stopPropagation
+     * @type boolean
+     * @static
+     */
+    stopPropagation: true,
+
+    /**
+     * Internal flag that is set to true when drag and drop has been
+     * intialized
+     * @property initialized
+     * @private
+     * @static
+     */
+    initialized: false,
+
+    /**
+     * All drag and drop can be disabled.
+     * @property locked
+     * @private
+     * @static
+     */
+    locked: false,
+
+    /**
+     * Called the first time an element is registered.
+     * @method init
+     * @private
+     * @static
+     */
+    init: function() {
+        this.initialized = true;
+    },
+
+    /**
+     * In point mode, drag and drop interaction is defined by the
+     * location of the cursor during the drag/drop
+     * @property POINT
+     * @type int
+     * @static
+     */
+    POINT: 0,
+
+    /**
+     * In intersect mode, drag and drop interaction is defined by the
+     * overlap of two or more drag and drop objects.
+     * @property INTERSECT
+     * @type int
+     * @static
+     */
+    INTERSECT: 1,
+
+    /**
+     * The current drag and drop mode.  Default: POINT
+     * @property mode
+     * @type int
+     * @static
+     */
+    mode: 0,
+
+    /**
+     * Runs method on all drag and drop objects
+     * @method _execOnAll
+     * @private
+     * @static
+     */
+    _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);
+            }
+        }
+    },
+
+    /**
+     * Drag and drop initialization.  Sets up the global event handlers
+     * @method _onLoad
+     * @private
+     * @static
+     */
+    _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);
+        // Event.on(window,   "mouseout",    this._test);
+
+    },
+
+    /**
+     * Reset constraints on all drag and drop objs
+     * @method _onResize
+     * @private
+     * @static
+     */
+    _onResize: function(e) {
+        this._execOnAll("resetConstraints", []);
+    },
+
+    /**
+     * Lock all drag and drop functionality
+     * @method lock
+     * @static
+     */
+    lock: function() { this.locked = true; },
+
+    /**
+     * Unlock all drag and drop functionality
+     * @method unlock
+     * @static
+     */
+    unlock: function() { this.locked = false; },
+
+    /**
+     * Is drag and drop locked?
+     * @method isLocked
+     * @return {boolean} True if drag and drop is locked, false otherwise.
+     * @static
+     */
+    isLocked: function() { return this.locked; },
+
+    /**
+     * Location cache that is set for all drag drop objects when a drag is
+     * initiated, cleared when the drag is finished.
+     * @property locationCache
+     * @private
+     * @static
+     */
+    locationCache: {},
+
+    /**
+     * Set useCache to false if you want to force object the lookup of each
+     * drag and drop linked element constantly during a drag.
+     * @property useCache
+     * @type boolean
+     * @static
+     */
+    useCache: true,
+
+    /**
+     * The number of pixels that the mouse needs to move after the
+     * mousedown before the drag is initiated.  Default=3;
+     * @property clickPixelThresh
+     * @type int
+     * @static
+     */
+    clickPixelThresh: 3,
+
+    /**
+     * The number of milliseconds after the mousedown event to initiate the
+     * drag if we don't get a mouseup event. Default=350
+     * @property clickTimeThresh
+     * @type int
+     * @static
+     */
+    clickTimeThresh: 350,
+
+    /**
+     * Flag that indicates that either the drag pixel threshold or the
+     * mousdown time threshold has been met
+     * @property dragThreshMet
+     * @type boolean
+     * @private
+     * @static
+     */
+    dragThreshMet: false,
+
+    /**
+     * Timeout used for the click time threshold
+     * @property clickTimeout
+     * @type Object
+     * @private
+     * @static
+     */
+    clickTimeout: null,
+
+    /**
+     * The X position of the mousedown event stored for later use when a
+     * drag threshold is met.
+     * @property startX
+     * @type int
+     * @private
+     * @static
+     */
+    startX: 0,
+
+    /**
+     * The Y position of the mousedown event stored for later use when a
+     * drag threshold is met.
+     * @property startY
+     * @type int
+     * @private
+     * @static
+     */
+    startY: 0,
+
+    /**
+     * Each DragDrop instance must be registered with the DragDropManager.
+     * This is executed in DragDrop.init()
+     * @method regDragDrop
+     * @param {DragDrop} oDD the DragDrop object to register
+     * @param {String} sGroup the name of the group this element belongs to
+     * @static
+     */
+    regDragDrop: function(oDD, sGroup) {
+        if (!this.initialized) { this.init(); }
+
+        if (!this.ids[sGroup]) {
+            this.ids[sGroup] = {};
+        }
+        this.ids[sGroup][oDD.id] = oDD;
+    },
+
+    /**
+     * Removes the supplied dd instance from the supplied group. Executed
+     * by DragDrop.removeFromGroup, so don't call this function directly.
+     * @method removeDDFromGroup
+     * @private
+     * @static
+     */
+    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];
+        }
+    },
+
+    /**
+     * Unregisters a drag and drop item.  This is executed in
+     * DragDrop.unreg, use that method instead of calling this directly.
+     * @method _remove
+     * @private
+     * @static
+     */
+    _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];
+    },
+
+    /**
+     * Each DragDrop handle element must be registered.  This is done
+     * automatically when executing DragDrop.setHandleElId()
+     * @method regHandle
+     * @param {String} sDDId the DragDrop id this element is a handle for
+     * @param {String} sHandleId the id of the element that is the drag
+     * handle
+     * @static
+     */
+    regHandle: function(sDDId, sHandleId) {
+        if (!this.handleIds[sDDId]) {
+            this.handleIds[sDDId] = {};
+        }
+        this.handleIds[sDDId][sHandleId] = sHandleId;
+    },
+
+    /**
+     * Utility function to determine if a given element has been
+     * registered as a drag drop item.
+     * @method isDragDrop
+     * @param {String} id the element id to check
+     * @return {boolean} true if this element is a DragDrop item,
+     * false otherwise
+     * @static
+     */
+    isDragDrop: function(id) {
+        return ( this.getDDById(id) ) ? true : false;
+    },
+
+    /**
+     * Returns the drag and drop instances that are in all groups the
+     * passed in instance belongs to.
+     * @method getRelated
+     * @param {DragDrop} p_oDD the obj to get related data for
+     * @param {boolean} bTargetsOnly if true, only return targetable objs
+     * @return {DragDrop[]} the related instances
+     * @static
+     */
+    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;
+    },
+
+    /**
+     * Returns true if the specified dd target is a legal target for
+     * the specifice drag obj
+     * @method isLegalTarget
+     * @param {DragDrop} oDD the drag obj
+     * @param {DragDrop} oTargetDD the target
+     * @return {boolean} true if the target is a legal target for the
+     * dd obj
+     * @static
+     */
+    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;
+    },
+
+    /**
+     * My goal is to be able to transparently determine if an object is
+     * typeof DragDrop, and the exact subclass of DragDrop.  typeof
+     * returns "object", oDD.constructor.toString() always returns
+     * "DragDrop" and not the name of the subclass.  So for now it just
+     * evaluates a well-known variable in DragDrop.
+     * @method isTypeOfDD
+     * @param {Object} the object to evaluate
+     * @return {boolean} true if typeof oDD = DragDrop
+     * @static
+     */
+    isTypeOfDD: function (oDD) {
+        return (oDD && oDD.__ygDragDrop);
+    },
+
+    /**
+     * Utility function to determine if a given element has been
+     * registered as a drag drop handle for the given Drag Drop object.
+     * @method isHandle
+     * @param {String} id the element id to check
+     * @return {boolean} true if this element is a DragDrop handle, false
+     * otherwise
+     * @static
+     */
+    isHandle: function(sDDId, sHandleId) {
+        return ( this.handleIds[sDDId] &&
+                        this.handleIds[sDDId][sHandleId] );
+    },
+
+    /**
+     * Returns the DragDrop instance for a given id
+     * @method getDDById
+     * @param {String} id the id of the DragDrop object
+     * @return {DragDrop} the drag drop object, null if it is not found
+     * @static
+     */
+    getDDById: function(id) {
+        for (var i in this.ids) {
+            if (this.ids[i][id]) {
+                return this.ids[i][id];
+            }
+        }
+        return null;
+    },
+
+    /**
+     * Fired after a registered DragDrop object gets the mousedown event.
+     * Sets up the events required to track the object being dragged
+     * @method handleMouseDown
+     * @param {Event} e the event
+     * @param oDD the DragDrop object being dragged
+     * @private
+     * @static
+     */
+    handleMouseDown: function(e, oDD) {
+        if(Ext.tip.QuickTipManager){
+            Ext.tip.QuickTipManager.ddDisable();
+        }
+        if(this.dragCurrent){
+            // the original browser mouseup wasn't handled (e.g. outside FF browser window)
+            // so clean up first to avoid breaking the next drag
+            this.handleMouseUp(e);
+        }
+        
+        this.currentTarget = e.getTarget();
+        this.dragCurrent = oDD;
+
+        var el = oDD.getEl();
+
+        // track start position
+        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 );
+    },
+
+    /**
+     * Fired when either the drag pixel threshol or the mousedown hold
+     * time threshold has been met.
+     * @method startDrag
+     * @param x {int} the X position of the original mousedown
+     * @param y {int} the Y position of the original mousedown
+     * @static
+     */
+    startDrag: function(x, y) {
+        clearTimeout(this.clickTimeout);
+        if (this.dragCurrent) {
+            this.dragCurrent.b4StartDrag(x, y);
+            this.dragCurrent.startDrag(x, y);
+        }
+        this.dragThreshMet = true;
+    },
+
+    /**
+     * Internal function to handle the mouseup event.  Will be invoked
+     * from the context of the document.
+     * @method handleMouseUp
+     * @param {Event} e the event
+     * @private
+     * @static
+     */
+    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);
+    },
+
+    /**
+     * Utility to stop event propagation and event default, if these
+     * features are turned on.
+     * @method stopEvent
+     * @param {Event} e the event as returned by this.getEvent()
+     * @static
+     */
+    stopEvent: function(e){
+        if(this.stopPropagation) {
+            e.stopPropagation();
+        }
+
+        if (this.preventDefault) {
+            e.preventDefault();
+        }
+    },
+
+    /**
+     * Internal function to clean up event handlers after the drag
+     * operation is complete
+     * @method stopDrag
+     * @param {Event} e the event
+     * @private
+     * @static
+     */
+    stopDrag: function(e) {
+        // Fire the drag end event for the item that was dragged
+        if (this.dragCurrent) {
+            if (this.dragThreshMet) {
+                this.dragCurrent.b4EndDrag(e);
+                this.dragCurrent.endDrag(e);
+            }
+
+            this.dragCurrent.onMouseUp(e);
+        }
+
+        this.dragCurrent = null;
+        this.dragOvers = {};
+    },
+
+    /**
+     * Internal function to handle the mousemove event.  Will be invoked
+     * from the context of the html element.
+     *
+     * @TODO figure out what we can do about mouse events lost when the
+     * user drags objects beyond the window boundary.  Currently we can
+     * detect this in internet explorer by verifying that the mouse is
+     * down during the mousemove event.  Firefox doesn't give us the
+     * button state on the mousemove event.
+     * @method handleMouseMove
+     * @param {Event} e the event
+     * @private
+     * @static
+     */
+    handleMouseMove: function(e) {
+        if (! this.dragCurrent) {
+            return true;
+        }
+        // var button = e.which || e.button;
+
+        // check for IE mouseup outside of page boundary
+        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;
+    },
+
+    /**
+     * Iterates over all of the DragDrop elements to find ones we are
+     * hovering over or dropping on
+     * @method fireEvents
+     * @param {Event} e the event
+     * @param {boolean} isDrop is this a drop op or a mouseover op?
+     * @private
+     * @static
+     */
+    fireEvents: function(e, isDrop) {
+        var dc = this.dragCurrent;
+
+        // If the user did the mouse up outside of the window, we could
+        // get here even though we have ended the drag.
+        if (!dc || dc.isLocked()) {
+            return;
+        }
+
+        var pt = e.getPoint();
+
+        // cache the previous dragOver array
+        var oldOvers = [];
+
+        var outEvts   = [];
+        var overEvts  = [];
+        var dropEvts  = [];
+        var enterEvts = [];
+
+        // Check to see if the object(s) we were hovering over is no longer
+        // being hovered over so we can fire the onDragOut event
+        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)) {
+                        // look for drop interactions
+                        if (isDrop) {
+                            dropEvts.push( oDD );
+                        // look for drag enter and drag over interactions
+                        } else {
+
+                            // initial drag over: dragEnter fires
+                            if (!oldOvers[oDD.id]) {
+                                enterEvts.push( oDD );
+                            // subsequent drag overs: dragOver fires
+                            } 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 {
+            // fire dragout events
+            var len = 0;
+            for (i=0, len=outEvts.length; i<len; ++i) {
+                dc.b4DragOut(e, outEvts[i].id);
+                dc.onDragOut(e, outEvts[i].id);
+            }
+
+            // fire enter events
+            for (i=0,len=enterEvts.length; i<len; ++i) {
+                // dc.b4DragEnter(e, oDD.id);
+                dc.onDragEnter(e, enterEvts[i].id);
+            }
+
+            // fire over events
+            for (i=0,len=overEvts.length; i<len; ++i) {
+                dc.b4DragOver(e, overEvts[i].id);
+                dc.onDragOver(e, overEvts[i].id);
+            }
+
+            // fire drop events
+            for (i=0, len=dropEvts.length; i<len; ++i) {
+                dc.b4DragDrop(e, dropEvts[i].id);
+                dc.onDragDrop(e, dropEvts[i].id);
+            }
+
+        }
+
+        // notify about a drop that did not find a target
+        if (isDrop && !dropEvts.length) {
+            dc.onInvalidDrop(e);
+        }
+
+    },
+
+    /**
+     * Helper function for getting the best match from the list of drag
+     * and drop objects returned by the drag and drop events when we are
+     * in INTERSECT mode.  It returns either the first object that the
+     * cursor is over, or the object that has the greatest overlap with
+     * the dragged element.
+     * @method getBestMatch
+     * @param  {DragDrop[]} dds The array of drag and drop objects
+     * targeted
+     * @return {DragDrop}       The best single match
+     * @static
+     */
+    getBestMatch: function(dds) {
+        var winner = null;
+        // Return null if the input is not what we expect
+        //if (!dds || !dds.length || dds.length == 0) {
+           // winner = null;
+        // If there is only one item, it wins
+        //} else if (dds.length == 1) {
+
+        var len = dds.length;
+
+        if (len == 1) {
+            winner = dds[0];
+        } else {
+            // Loop through the targeted items
+            for (var i=0; i<len; ++i) {
+                var dd = dds[i];
+                // If the cursor is over the object, it wins.  If the
+                // cursor is over multiple matches, the first one we come
+                // to wins.
+                if (dd.cursorIsOver) {
+                    winner = dd;
+                    break;
+                // Otherwise the object with the most overlap wins
+                } else {
+                    if (!winner ||
+                        winner.overlap.getArea() < dd.overlap.getArea()) {
+                        winner = dd;
+                    }
+                }
+            }
+        }
+
+        return winner;
+    },
+
+    /**
+     * Refreshes the cache of the top-left and bottom-right points of the
+     * drag and drop objects in the specified group(s).  This is in the
+     * format that is stored in the drag and drop instance, so typical
+     * usage is:
+     * <code>
+     * Ext.dd.DragDropManager.refreshCache(ddinstance.groups);
+     * </code>
+     * Alternatively:
+     * <code>
+     * Ext.dd.DragDropManager.refreshCache({group1:true, group2:true});
+     * </code>
+     * @TODO this really should be an indexed array.  Alternatively this
+     * method could accept both.
+     * @method refreshCache
+     * @param {Object} groups an associative array of groups to refresh
+     * @static
+     */
+    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)) {
+                // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
+                    var loc = this.getLocation(oDD);
+                    if (loc) {
+                        this.locationCache[oDD.id] = loc;
+                    } else {
+                        delete this.locationCache[oDD.id];
+                        // this will unregister the drag and drop object if
+                        // the element is not in a usable state
+                        // oDD.unreg();
+                    }
+                }
+            }
+        }
+    },
+
+    /**
+     * This checks to make sure an element exists and is in the DOM.  The
+     * main purpose is to handle cases where innerHTML is used to remove
+     * drag and drop objects from the DOM.  IE provides an 'unspecified
+     * error' when trying to access the offsetParent of such an element
+     * @method verifyEl
+     * @param {HTMLElement} el the element to check
+     * @return {boolean} true if the element looks usable
+     * @static
+     */
+    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;
+    },
+
+    /**
+     * Returns a Region object containing the drag and drop element's position
+     * and size, including the padding configured for it
+     * @method getLocation
+     * @param {DragDrop} oDD the drag and drop object to get the
+     *                       location for
+     * @return {Ext.util.Region} a Region object representing the total area
+     *                             the element occupies, including any padding
+     *                             the instance is configured for.
+     * @static
+     */
+    getLocation: function(oDD) {
+        if (! this.isTypeOfDD(oDD)) {
+            return null;
+        }
+
+        //delegate getLocation method to the
+        //drag and drop target.
+        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);
+    },
+
+    /**
+     * Checks the cursor location to see if it over the target
+     * @method isOverTarget
+     * @param {Ext.util.Point} pt The point to evaluate
+     * @param {DragDrop} oTarget the DragDrop object we are inspecting
+     * @return {boolean} true if the mouse is over the target
+     * @private
+     * @static
+     */
+    isOverTarget: function(pt, oTarget, intersect) {
+        // use cache if available
+        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 );
+
+        // DragDrop is using this as a sanity check for the initial mousedown
+        // in this case we are done.  In POINT mode, if the drag obj has no
+        // contraints, we are also done. Otherwise we need to evaluate the
+        // location of the target as related to the actual location of the
+        // dragged element.
+        var dc = this.dragCurrent;
+        if (!dc || !dc.getTargetCoord ||
+                (!intersect && !dc.constrainX && !dc.constrainY)) {
+            return oTarget.cursorIsOver;
+        }
+
+        oTarget.overlap = null;
+
+        // Get the current location of the drag element, this is the
+        // location of the mouse event less the delta that represents
+        // where the original mousedown happened on the element.  We
+        // need to consider constraints and ticks as well.
+        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;
+        }
+    },
+
+    /**
+     * unload event handler
+     * @method _onUnload
+     * @private
+     * @static
+     */
+    _onUnload: function(e, me) {
+        Ext.dd.DragDropManager.unregAll();
+    },
+
+    /**
+     * Cleans up the drag and drop events and objects.
+     * @method unregAll
+     * @private
+     * @static
+     */
+    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 = {};
+    },
+
+    /**
+     * A cache of DOM elements
+     * @property elementCache
+     * @private
+     * @static
+     */
+    elementCache: {},
+
+    /**
+     * Get the wrapper for the DOM element specified
+     * @method getElWrapper
+     * @param {String} id the id of the element to get
+     * @return {Ext.dd.DDM.ElementWrapper} the wrapped element
+     * @private
+     * @deprecated This wrapper isn't that useful
+     * @static
+     */
+    getElWrapper: function(id) {
+        var oWrapper = this.elementCache[id];
+        if (!oWrapper || !oWrapper.el) {
+            oWrapper = this.elementCache[id] =
+                new this.ElementWrapper(Ext.getDom(id));
+        }
+        return oWrapper;
+    },
+
+    /**
+     * Returns the actual DOM element
+     * @method getElement
+     * @param {String} id the id of the elment to get
+     * @return {Object} The element
+     * @deprecated use Ext.lib.Ext.getDom instead
+     * @static
+     */
+    getElement: function(id) {
+        return Ext.getDom(id);
+    },
+
+    /**
+     * Returns the style property for the DOM element (i.e.,
+     * document.getElById(id).style)
+     * @method getCss
+     * @param {String} id the id of the elment to get
+     * @return {Object} The style property of the element
+     * @static
+     */
+    getCss: function(id) {
+        var el = Ext.getDom(id);
+        return (el) ? el.style : null;
+    },
+
+    /**
+     * Inner class for cached elements
+     * @class Ext.dd.DragDropManager.ElementWrapper
+     * @for DragDropManager
+     * @private
+     * @deprecated
+     */
+    ElementWrapper: function(el) {
+            /**
+             * The element
+             * @property el
+             */
+            this.el = el || null;
+            /**
+             * The element id
+             * @property id
+             */
+            this.id = this.el && el.id;
+            /**
+             * A reference to the style property
+             * @property css
+             */
+            this.css = this.el && el.style;
+        },
+
+    /**
+     * Returns the X position of an html element
+     * @method getPosX
+     * @param el the element for which to get the position
+     * @return {int} the X coordinate
+     * @for DragDropManager
+     * @static
+     */
+    getPosX: function(el) {
+        return Ext.core.Element.getX(el);
+    },
+
+    /**
+     * Returns the Y position of an html element
+     * @method getPosY
+     * @param el the element for which to get the position
+     * @return {int} the Y coordinate
+     * @static
+     */
+    getPosY: function(el) {
+        return Ext.core.Element.getY(el);
+    },
+
+    /**
+     * Swap two nodes.  In IE, we use the native method, for others we
+     * emulate the IE behavior
+     * @method swapNode
+     * @param n1 the first node to swap
+     * @param n2 the other node to swap
+     * @static
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * Returns the current scroll position
+     * @method getScroll
+     * @private
+     * @static
+     */
+    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
+        };
+    },
+
+    /**
+     * Returns the specified element style property
+     * @method getStyle
+     * @param {HTMLElement} el          the element
+     * @param {string}      styleProp   the style property
+     * @return {string} The value of the style property
+     * @static
+     */
+    getStyle: function(el, styleProp) {
+        return Ext.fly(el).getStyle(styleProp);
+    },
+
+    /**
+     * Gets the scrollTop
+     * @method getScrollTop
+     * @return {int} the document's scrollTop
+     * @static
+     */
+    getScrollTop: function () {
+        return this.getScroll().top;
+    },
+
+    /**
+     * Gets the scrollLeft
+     * @method getScrollLeft
+     * @return {int} the document's scrollTop
+     * @static
+     */
+    getScrollLeft: function () {
+        return this.getScroll().left;
+    },
+
+    /**
+     * Sets the x/y position of an element to the location of the
+     * target element.
+     * @method moveToEl
+     * @param {HTMLElement} moveEl      The element to move
+     * @param {HTMLElement} targetEl    The position reference element
+     * @static
+     */
+    moveToEl: function (moveEl, targetEl) {
+        var aCoord = Ext.core.Element.getXY(targetEl);
+        Ext.core.Element.setXY(moveEl, aCoord);
+    },
+
+    /**
+     * Numeric array sort function
+     * @method numericSort
+     * @static
+     */
+    numericSort: function(a, b) {
+        return (a - b);
+    },
+
+    /**
+     * Internal counter
+     * @property _timeoutCount
+     * @private
+     * @static
+     */
+    _timeoutCount: 0,
+
+    /**
+     * Trying to make the load order less important.  Without this we get
+     * an error if this file is loaded before the Event Utility.
+     * @method _addListeners
+     * @private
+     * @static
+     */
+    _addListeners: function() {
+        if ( document ) {
+            this._onLoad();
+        } else {
+            if (this._timeoutCount > 2000) {
+            } else {
+                setTimeout(this._addListeners, 10);
+                if (document && document.body) {
+                    this._timeoutCount += 1;
+                }
+            }
+        }
+    },
+
+    /**
+     * Recursively searches the immediate parent and all child nodes for
+     * the handle element in order to determine wheter or not it was
+     * clicked.
+     * @method handleWasClicked
+     * @param node the html element to inspect
+     * @static
+     */
+    handleWasClicked: function(node, id) {
+        if (this.isHandle(id, node.id)) {
+            return true;
+        } else {
+            // check to see if this is a text node child of the one we want
+            var p = node.parentNode;
+
+            while (p) {
+                if (this.isHandle(id, p.id)) {
+                    return true;
+                } else {
+                    p = p.parentNode;
+                }
+            }
+        }
+
+        return false;
+    }
+}, function() {
+    this._addListeners();
+});
+
+/**
+ * @class Ext.layout.container.Box
+ * @extends Ext.layout.container.Container
+ * <p>Base Class for HBoxLayout and VBoxLayout Classes. Generally it should not need to be used directly.</p>
+ */
+
+Ext.define('Ext.layout.container.Box', {
+
+    /* Begin Definitions */
+
+    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'
+    ],
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Mixed} animate
+     * <p>If truthy, child Component are <i>animated</i> into position whenever the Container
+     * is layed out. If this option is numeric, it is used as the animation duration in milliseconds.</p>
+     * <p>May be set as a property at any time.</p>
+     */
+
+    /**
+     * @cfg {Object} defaultMargins
+     * <p>If the individual contained items do not have a <tt>margins</tt>
+     * property specified or margin specified via CSS, the default margins from this property will be
+     * applied to each item.</p>
+     * <br><p>This property may be specified as an object containing margins
+     * to apply in the format:</p><pre><code>
+{
+    top: (top margin),
+    right: (right margin),
+    bottom: (bottom margin),
+    left: (left margin)
+}</code></pre>
+     * <p>This property may also be specified as a string containing
+     * space-separated, numeric margin values. The order of the sides associated
+     * with each value matches the way CSS processes margin values:</p>
+     * <div class="mdetail-params"><ul>
+     * <li>If there is only one value, it applies to all sides.</li>
+     * <li>If there are two values, the top and bottom borders are set to the
+     * first value and the right and left are set to the second.</li>
+     * <li>If there are three values, the top is set to the first value, the left
+     * and right are set to the second, and the bottom is set to the third.</li>
+     * <li>If there are four values, they apply to the top, right, bottom, and
+     * left, respectively.</li>
+     * </ul></div>
+     * <p>Defaults to:</p><pre><code>
+     * {top:0, right:0, bottom:0, left:0}
+     * </code></pre>
+     */
+    defaultMargins: {
+        top: 0,
+        right: 0,
+        bottom: 0,
+        left: 0
+    },
+
+    /**
+     * @cfg {String} padding
+     * <p>Sets the padding to be applied to all child items managed by this layout.</p>
+     * <p>This property must be specified as a string containing
+     * space-separated, numeric padding values. The order of the sides associated
+     * with each value matches the way CSS processes padding values:</p>
+     * <div class="mdetail-params"><ul>
+     * <li>If there is only one value, it applies to all sides.</li>
+     * <li>If there are two values, the top and bottom borders are set to the
+     * first value and the right and left are set to the second.</li>
+     * <li>If there are three values, the top is set to the first value, the left
+     * and right are set to the second, and the bottom is set to the third.</li>
+     * <li>If there are four values, they apply to the top, right, bottom, and
+     * left, respectively.</li>
+     * </ul></div>
+     * <p>Defaults to: <code>"0"</code></p>
+     */
+    padding: '0',
+    // documented in subclasses
+    pack: 'start',
+
+    /**
+     * @cfg {String} pack
+     * Controls how the child items of the container are packed together. Acceptable configuration values
+     * for this property are:
+     * <div class="mdetail-params"><ul>
+     * <li><b><tt>start</tt></b> : <b>Default</b><div class="sub-desc">child items are packed together at
+     * <b>left</b> side of container</div></li>
+     * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are packed together at
+     * <b>mid-width</b> of container</div></li>
+     * <li><b><tt>end</tt></b> : <div class="sub-desc">child items are packed together at <b>right</b>
+     * side of container</div></li>
+     * </ul></div>
+     */
+    /**
+     * @cfg {Number} flex
+     * This configuration option is to be applied to <b>child <tt>items</tt></b> of the container managed
+     * by this layout. Each child item with a <tt>flex</tt> property will be flexed <b>horizontally</b>
+     * according to each item's <b>relative</b> <tt>flex</tt> value compared to the sum of all items with
+     * a <tt>flex</tt> value specified.  Any child items that have either a <tt>flex = 0</tt> or
+     * <tt>flex = undefined</tt> will not be 'flexed' (the initial size will not be changed).
+     */
+
+    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 is used to adjust the availableWidth, typically used
+    // to reserve space for a scrollbar
+    availableSpaceOffset: 0,
+    
+    // whether or not to reserve the availableSpaceOffset in layout calculations
+    reserveOffset: true,
+    
+    /**
+     * @cfg {Boolean} clearInnerCtOnLayout
+     */
+    clearInnerCtOnLayout: false,
+
+    flexSortFn: function (a, b) {
+        var maxParallelPrefix = 'max' + this.parallelPrefixCap,
+            infiniteValue = Infinity;
+        a = a.component[maxParallelPrefix] || infiniteValue;
+        b = b.component[maxParallelPrefix] || infiniteValue;
+        // IE 6/7 Don't like Infinity - Infinity...
+        if (!isFinite(a) && !isFinite(b)) {
+            return false;
+        }
+        return a - b;
+    },
+
+    // Sort into *descending* order.
+    minSizeSortFn: function(a, b) {
+        return b.available - a.available;
+    },
+
+    constructor: function(config) {
+        var me = this;
+
+        me.callParent(arguments);
+
+        // The sort function needs access to properties in this, so must be bound.
+        me.flexSortFn = Ext.Function.bind(me.flexSortFn, me);
+
+        me.initOverflowHandler();
+    },
+
+    /**
+     * @private
+     * Returns the current size and positioning of the passed child item.
+     * @param {Component} child The child Component to calculate the box for
+     * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
+     */
+    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()
+        };
+    },
+
+    /**
+     * @private
+     * Calculates the size and positioning of the passed child item.
+     * @param {Component} child The child Component to calculate the box for
+     * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
+     */
+    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];
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Calculates the size and positioning of each item in the box. This iterates over all of the rendered,
+     * visible items and returns a height, width, top and left for each, as well as a reference to each. Also
+     * returns meta data such as maxSize which are useful when resizing layout wrappers such as this.innerCt.
+     * @param {Array} visibleItems The array of all rendered, visible items to be calculated for
+     * @param {Object} targetSize Object containing target size and height
+     * @return {Object} Object containing box measurements for each child, plus meta data
+     */
+    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;
+
+        //gather the total flex of all flexed items and the width taken up by fixed width items
+        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];
+
+            // Create the box description object for this child item.
+            tmpObj = {
+                component: child,
+                margins: childMargins
+            };
+
+            // flex and not 'auto' width
+            if (child.flex) {
+                totalFlex += child.flex;
+                childParallel = undefinedValue;
+            }
+            // Not flexed or 'auto' width or undefined width
+            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);
+
+            // Max height for align - force layout of non-laid out subcontainers without a numeric height
+            if (typeof childPerpendicular != 'number') {
+                // Clear any static sizing and revert to flow so we can get a proper measurement
+                // child['set' + perpendicularPrefixCap](null);
+                childPerpendicular = child['get' + perpendicularPrefixCap]();
+            }
+
+            // Track the maximum perpendicular size for use by the stretch and stretchmax align config values.
+            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;
+
+        //the space available to the flexed items
+        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 {
+            //all flexed items should be sized to their minimum size, other items should be shrunk down until
+            //the shortfall has been accounted for
+            if (shortfall > 0) {
+                /*
+                 * When we have a shortfall but are not tooNarrow, we need to shrink the width of each non-flexed item.
+                 * Flexed items are immediately reduced to their minWidth and anything already at minWidth is ignored.
+                 * The remaining items are collected into the minWidths array, which is later used to distribute the shortfall.
+                 */
+                for (i = 0; i < visibleCount; i++) {
+                    item = visibleItems[i];
+                    minSize = item[parallelMinString] || 0;
+
+                    //shrink each non-flex tab by an equal amount to make them all fit. Flexed items are all
+                    //shrunk to their minSize because they're flexible and should be the first to lose size
+                    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
+                        });
+                    }
+                }
+
+                //sort by descending amount of width remaining before minWidth is reached
+                Ext.Array.sort(minSizes, me.minSizeSortFn);
+
+                /*
+                 * Distribute the shortfall (difference between total desired size of all items and actual size available)
+                 * between the non-flexed items. We try to distribute the shortfall evenly, but apply it to items with the
+                 * smallest difference between their size and minSize first, so that if reducing the size by the average
+                 * amount would make that item less than its minSize, we carry the remainder over to the next item.
+                 */
+                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 = [];
+
+                // Create an array containing *just the flexed boxes* for allocation of remainingSpace
+                for (i = 0; i < visibleCount; i++) {
+                    child = visibleItems[i];
+                    if (isStart && child.flex) {
+                        flexedBoxes.push(boxes[Ext.Array.indexOf(visibleItems, child)]);
+                    }
+                }
+                // The flexed boxes need to be sorted in ascending order of maxSize to work properly
+                // so that unallocated space caused by maxWidth being less than flexed width
+                // can be reallocated to subsequent flexed boxes.
+                Ext.Array.sort(flexedBoxes, me.flexSortFn);
+
+                // Calculate the size of each flexed item, and attempt to set it.
+                for (i = 0; i < flexedBoxes.length; i++) {
+                    calcs = flexedBoxes[i];
+                    child = calcs.component;
+                    childMargins = calcs.margins;
+
+                    flexedSize = math.ceil((child.flex / remainingFlex) * remainingSpace);
+
+                    // Implement maxSize and minSize check
+                    flexedSize = Math.max(child['min' + parallelPrefixCap] || 0, math.min(child['max' + parallelPrefixCap] || infiniteValue, flexedSize));
+
+                    // Remaining space has already had all parallel margins subtracted from it, so just subtract consumed size
+                    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;
+        }
+
+        // Fix for left and right docked Components in a dock component layout. This is for docked Headers and docked Toolbars.
+        // Older Microsoft browsers do not size a position:absolute element's width to match its content.
+        // So in this case, in the updateInnerCtSize method we may need to adjust the size of the owning Container's element explicitly based upon
+        // the discovered max width. So here we put a calculatedWidth property in the metadata to facilitate this.
+        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;
+            }
+            // If the owning element is not sized, calculate the available width to center or stretch in based upon maxSize
+            availPerpendicularSize = Math.min(availPerpendicularSize, targetSize.width = maxSize + padding.left + padding.right);
+        }
+
+        //finally, calculate the left and top position of each item
+        for (i = 0; i < visibleCount; i++) {
+            child = visibleItems[i];
+            calcs = boxes[i];
+
+            childMargins = calcs.margins;
+
+            perpendicularMargins = childMargins[me.perpendicularLeftTop] + childMargins[me.perpendicularRightBottom];
+
+            // Advance past the "before" margin
+            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) {
+                // When calculating a centered position within the content box of the innerCt, the width of the borders must be subtracted from
+                // the size to yield the space available to center within.
+                // The updateInnerCtSize method explicitly adds the border widths to the set size of the innerCt.
+                diff = mmax(availPerpendicularSize, maxSize) - me.innerCt.getBorderWidth(me.perpendicularLT + me.perpendicularRB) - calcs[perpendicularPrefix];
+                if (diff > 0) {
+                    calcs[me.perpendicularLeftTop] = perpendicularOffset + Math.round(diff / 2);
+                }
+            }
+
+            // Advance past the box size and the "after" margin
+            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
+            }
+        };
+    },
+
+    /**
+     * @private
+     */
+    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);
+    },
+
+    /**
+     * @private
+     * Runs the child box calculations and caches them in childBoxCache. Subclasses can used these cached values
+     * when laying out
+     */
+    onLayout: function() {
+        this.callParent();
+        // Clear the innerCt size so it doesn't influence the child items.
+        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;
+        }
+
+        //invoke the overflow handler, if one is configured
+        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();
+        }
+
+        /**
+         * @private
+         * @property layoutTargetLastSize
+         * @type Object
+         * Private cache of the last measured size of the layout target. This should never be used except by
+         * BoxLayout subclasses during their onLayout run.
+         */
+        me.layoutTargetLastSize = targetSize;
+
+        /**
+         * @private
+         * @property childBoxCache
+         * @type Array
+         * Array of the last calculated height, width, top and left positions of each visible rendered component
+         * within the Box layout.
+         */
+        me.childBoxCache = calcs;
+
+        me.updateInnerCtSize(targetSize, calcs);
+        me.updateChildBoxes(boxes);
+        me.handleTargetOverflow(targetSize);
+    },
+
+    /**
+     * Resizes and repositions each child component
+     * @param {Array} boxes The box measurements
+     */
+    updateChildBoxes: function(boxes) {
+        var me = this,
+            i = 0,
+            length = boxes.length,
+            animQueue = [],
+            dd = Ext.dd.DDM.getDDById(me.innerCt.id), // Any DD active on this layout's element (The BoxReorderer plugin does this.)
+            oldBox, newBox, changed, comp, boxAnim, animCallback;
+
+        for (; i < length; i++) {
+            newBox = boxes[i];
+            comp = newBox.component;
+
+            // If a Component is being drag/dropped, skip positioning it.
+            // Accomodate the BoxReorderer plugin: Its current dragEl must not be positioned by the layout
+            if (dd && (dd.getDragEl() === comp.el.dom)) {
+                continue;
+            }
+
+            changed = false;
+
+            oldBox = me.getChildBox(comp);
+
+            // If we are animating, we build up an array of Anim config objects, one for each
+            // child Component which has any changed box properties. Those with unchanged
+            // properties are not animated.
+            if (me.animate) {
+                // Animate may be a config object containing callback.
+                animCallback = me.animate.callback || me.animate;
+                boxAnim = {
+                    layoutAnimation: true,  // Component Target handler must use set*Calculated*Size
+                    target: comp,
+                    from: {},
+                    to: {},
+                    listeners: {}
+                };
+                // Only set from and to properties when there's a change.
+                // Perform as few Component setter methods as possible.
+                // Temporarily set the property values that we are not animating
+                // so that doComponentLayout does not auto-size them.
+                if (!isNaN(newBox.width) && (newBox.width != oldBox.width)) {
+                    changed = true;
+                    // boxAnim.from.width = oldBox.width;
+                    boxAnim.to.width = newBox.width;
+                }
+                if (!isNaN(newBox.height) && (newBox.height != oldBox.height)) {
+                    changed = true;
+                    // boxAnim.from.height = oldBox.height;
+                    boxAnim.to.height = newBox.height;
+                }
+                if (!isNaN(newBox.left) && (newBox.left != oldBox.left)) {
+                    changed = true;
+                    // boxAnim.from.left = oldBox.left;
+                    boxAnim.to.left = newBox.left;
+                }
+                if (!isNaN(newBox.top) && (newBox.top != oldBox.top)) {
+                    changed = true;
+                    // boxAnim.from.top = oldBox.top;
+                    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);
+                    }
+                }
+                // Don't set positions to NaN
+                if (isNaN(newBox.left) || isNaN(newBox.top)) {
+                    continue;
+                }
+                comp.setPosition(newBox.left, newBox.top);
+            }
+        }
+
+        // Kick off any queued animations
+        length = animQueue.length;
+        if (length) {
+
+            // A function which cleans up when a Component's animation is done.
+            // The last one to finish calls the callback.
+            var afterAnimate = function(anim) {
+                // When we've animated all changed boxes into position, clear our busy flag and call the callback.
+                length -= 1;
+                if (!length) {
+                    me.layoutBusy = false;
+                    if (Ext.isFunction(animCallback)) {
+                        animCallback();
+                    }
+                }
+            };
+
+            var beforeAnimate = function() {
+                me.layoutBusy = true;
+            };
+
+            // Start each box animation off
+            for (i = 0, length = animQueue.length; i < length; i++) {
+                boxAnim = animQueue[i];
+
+                // Clean up the Component after. Clean up the *layout* after the last animation finishes
+                boxAnim.listeners.afteranimate = afterAnimate;
+
+                // The layout is busy during animation, and may not be called, so set the flag when the first animation begins
+                if (!i) {
+                    boxAnim.listeners.beforeanimate = beforeAnimate;
+                }
+                if (me.animate.duration) {
+                    boxAnim.duration = me.animate.duration;
+                }
+                comp = boxAnim.target;
+                delete boxAnim.target;
+                // Stop any currently running animation
+                comp.stopAnimation();
+                comp.animate(boxAnim);
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Called by onRender just before the child components are sized and positioned. This resizes the innerCt
+     * to make sure all child items fit within it. We call this before sizing the children because if our child
+     * items are larger than the previous innerCt size the browser will insert scrollbars and then remove them
+     * again immediately afterwards, giving a performance hit.
+     * Subclasses should provide an implementation.
+     * @param {Object} currentSize The current height and width of the innerCt
+     * @param {Array} calculations The new box calculations of all items to be laid out
+     */
+    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 a calculated width has been found (and this only happens for auto-width vertical docked Components in old Microsoft browsers)
+        // then, if the Component has not assumed the size of its content, set it to do so.
+        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;
+        }
+    },
+
+    /**
+     * @private
+     * This should be called after onLayout of any BoxLayout subclass. If the target's overflow is not set to 'hidden',
+     * we need to lay out a second time because the scrollbars may have modified the height and width of the layout
+     * target. Having a Box layout inside such a target is therefore not recommended.
+     * @param {Object} previousTargetSize The size and height of the layout target before we just laid out
+     * @param {Ext.container.Container} container The container
+     * @param {Ext.core.Element} target The target element
+     * @return True if the layout overflowed, and was reflowed in a secondary onLayout call.
+     */
+    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;
+    },
+
+    // private
+    isValidParent : function(item, target, position) {
+        // Note: Box layouts do not care about order within the innerCt element because it's an absolutely positioning layout
+        // We only care whether the item is a direct child of the innerCt element.
+        var itemEl = item.el ? item.el.dom : Ext.getDom(item);
+        return (itemEl && this.innerCt && itemEl.parentNode === this.innerCt.dom) || false;
+    },
+
+    // Overridden method from AbstractContainer.
+    // Used in the base AbstractLayout.beforeLayout method to render all items into.
+    getRenderTarget: function() {
+        if (!this.innerCt) {
+            // the innerCt prevents wrapping and shuffling while the container is resizing
+            this.innerCt = this.getTarget().createChild({
+                cls: this.innerCls,
+                role: 'presentation'
+            });
+            this.padding = Ext.util.Format.parseBox(this.padding);
+        }
+        return this.innerCt;
+    },
+
+    // private
+    renderItem: function(item, target) {
+        this.callParent(arguments);
+        var me = this,
+            itemEl = item.getEl(),
+            style = itemEl.dom.style,
+            margins = item.margins || item.margin;
+
+        // Parse the item's margin/margins specification
+        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);
+        }
+
+        // Add any before/after CSS margins to the configured margins, and zero the CSS margins
+        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 must reference calculated margins.
+        item.margins = margins;
+    },
+
+    /**
+     * @private
+     */
+    destroy: function() {
+        Ext.destroy(this.overflowHandler);
+        this.callParent(arguments);
+    }
+});
+/**
+ * @class Ext.layout.container.HBox
+ * @extends Ext.layout.container.Box
+ * <p>A layout that arranges items horizontally across a Container. This layout optionally divides available horizontal
+ * space between child items containing a numeric <code>flex</code> configuration.</p>
+ * This layout may also be used to set the heights of child items by configuring it with the {@link #align} option.
+ * {@img Ext.layout.container.HBox/Ext.layout.container.HBox.png Ext.layout.container.HBox container layout}
+ * Example usage:
+    Ext.create('Ext.Panel', {
+        width: 500,
+        height: 300,
+        title: "HBoxLayout Panel",
+        layout: {
+            type: 'hbox',
+            align: 'stretch'
+        },
+        renderTo: document.body,
+        items: [{
+            xtype: 'panel',
+            title: 'Inner Panel One',
+            flex: 2
+        },{
+            xtype: 'panel',
+            title: 'Inner Panel Two',
+            flex: 1
+        },{
+            xtype: 'panel',
+            title: 'Inner Panel Three',
+            flex: 1
+        }]
+    });
+ */
+Ext.define('Ext.layout.container.HBox', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.hbox'],
+    extend: 'Ext.layout.container.Box',
+    alternateClassName: 'Ext.layout.HBoxLayout',
+    
+    /* End Definitions */
+
+    /**
+     * @cfg {String} align
+     * Controls how the child items of the container are aligned. Acceptable configuration values for this
+     * property are:
+     * <div class="mdetail-params"><ul>
+     * <li><b><tt>top</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned vertically
+     * at the <b>top</b> of the container</div></li>
+     * <li><b><tt>middle</tt></b> : <div class="sub-desc">child items are aligned vertically in the
+     * <b>middle</b> of the container</div></li>
+     * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched vertically to fill
+     * the height of the container</div></li>
+     * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched vertically to
+     * the height of the largest item.</div></li>
+     * </ul></div>
+     */
+    align: 'top', // top, middle, stretch, strechmax
+
+    //@private
+    alignCenteringString: 'middle',
+
+    type : 'hbox',
+
+    direction: 'horizontal',
+
+    // When creating an argument list to setSize, use this order
+    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'
+});
+/**
+ * @class Ext.layout.container.VBox
+ * @extends Ext.layout.container.Box
+ * <p>A layout that arranges items vertically down a Container. This layout optionally divides available vertical
+ * space between child items containing a numeric <code>flex</code> configuration.</p>
+ * This layout may also be used to set the widths of child items by configuring it with the {@link #align} option.
+ * {@img Ext.layout.container.VBox/Ext.layout.container.VBox.png Ext.layout.container.VBox container layout}
+ * Example usage:
+       Ext.create('Ext.Panel', {
+               width: 500,
+               height: 400,
+               title: "VBoxLayout Panel",
+               layout: {                        
+                       type: 'vbox',
+                       align: 'center'
+               },
+               renderTo: document.body,
+               items: [{                        
+                       xtype: 'panel',
+                       title: 'Inner Panel One',
+                       width: 250,
+                       flex: 2                      
+               },{
+                       xtype: 'panel',
+                       title: 'Inner Panel Two',
+                       width: 250,                     
+                       flex: 4
+               },{
+                       xtype: 'panel',
+                       title: 'Inner Panel Three',
+                       width: '50%',                   
+                       flex: 4
+               }]
+       });
+ */
+Ext.define('Ext.layout.container.VBox', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.vbox'],
+    extend: 'Ext.layout.container.Box',
+    alternateClassName: 'Ext.layout.VBoxLayout',
+    
+    /* End Definitions */
+
+    /**
+     * @cfg {String} align
+     * Controls how the child items of the container are aligned. Acceptable configuration values for this
+     * property are:
+     * <div class="mdetail-params"><ul>
+     * <li><b><tt>left</tt></b> : <b>Default</b><div class="sub-desc">child items are aligned horizontally
+     * at the <b>left</b> side of the container</div></li>
+     * <li><b><tt>center</tt></b> : <div class="sub-desc">child items are aligned horizontally at the
+     * <b>mid-width</b> of the container</div></li>
+     * <li><b><tt>stretch</tt></b> : <div class="sub-desc">child items are stretched horizontally to fill
+     * the width of the container</div></li>
+     * <li><b><tt>stretchmax</tt></b> : <div class="sub-desc">child items are stretched horizontally to
+     * the size of the largest item.</div></li>
+     * </ul></div>
+     */
+    align : 'left', // left, center, stretch, strechmax
+
+    //@private
+    alignCenteringString: 'center',
+
+    type: 'vbox',
+
+    direction: 'vertical',
+
+    // When creating an argument list to setSize, use this order
+    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'
+});
+/**
+ * @class Ext.FocusManager
+
+The FocusManager is responsible for globally:
+
+1. Managing component focus
+2. Providing basic keyboard navigation
+3. (optional) Provide a visual cue for focused components, in the form of a focus ring/frame.
+
+To activate the FocusManager, simply call {@link #enable `Ext.FocusManager.enable();`}. In turn, you may
+deactivate the FocusManager by subsequently calling {@link #disable `Ext.FocusManager.disable();`}.  The
+FocusManager is disabled by default.
+
+To enable the optional focus frame, pass `true` or `{focusFrame: true}` to {@link #enable}.
+
+Another feature of the FocusManager is to provide basic keyboard focus navigation scoped to any {@link Ext.container.Container}
+that would like to have navigation between its child {@link Ext.Component}'s. The {@link Ext.container.Container} can simply
+call {@link #subscribe Ext.FocusManager.subscribe} to take advantage of this feature, and can at any time call
+{@link #unsubscribe Ext.FocusManager.unsubscribe} to turn the navigation off.
+
+ * @singleton
+ * @markdown
+ * @author Jarred Nicholls <jarred@sencha.com>
+ * @docauthor Jarred Nicholls <jarred@sencha.com>
+ */
+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'
+    ],
+
+    /**
+     * @property {Boolean} enabled
+     * Whether or not the FocusManager is currently enabled
+     */
+    enabled: false,
+
+    /**
+     * @property {Ext.Component} focusedCmp
+     * The currently focused component. Defaults to `undefined`.
+     * @markdown
+     */
+
+    focusElementCls: Ext.baseCSSPrefix + 'focus-element',
+
+    focusFrameCls: Ext.baseCSSPrefix + 'focus-frame',
+
+    /**
+     * @property {Array} whitelist
+     * A list of xtypes that should ignore certain navigation input keys and
+     * allow for the default browser event/behavior. These input keys include:
+     *
+     * 1. Backspace
+     * 2. Delete
+     * 3. Left
+     * 4. Right
+     * 5. Up
+     * 6. Down
+     *
+     * The FocusManager will not attempt to navigate when a component is an xtype (or descendents thereof)
+     * that belongs to this whitelist. E.g., an {@link Ext.form.field.Text} should allow
+     * the user to move the input cursor left and right, and to delete characters, etc.
+     *
+     * This whitelist currently defaults to `['textfield']`.
+     * @markdown
+     */
+    whitelist: [
+        'textfield'
+    ],
+
+    tabIndexWhitelist: [
+        'a',
+        'button',
+        'embed',
+        'frame',
+        'iframe',
+        'img',
+        'input',
+        'object',
+        'select',
+        'textarea'
+    ],
+
+    constructor: function() {
+        var me = this,
+            CQ = Ext.ComponentQuery;
+
+        me.addEvents(
+            /**
+             * @event beforecomponentfocus
+             * Fires before a component becomes focused. Return `false` to prevent
+             * the component from gaining focus.
+             * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
+             * @param {Ext.Component} cmp The component that is being focused
+             * @param {Ext.Component} previousCmp The component that was previously focused,
+             * or `undefined` if there was no previously focused component.
+             * @markdown
+             */
+            'beforecomponentfocus',
+
+            /**
+             * @event componentfocus
+             * Fires after a component becomes focused.
+             * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
+             * @param {Ext.Component} cmp The component that has been focused
+             * @param {Ext.Component} previousCmp The component that was previously focused,
+             * or `undefined` if there was no previously focused component.
+             * @markdown
+             */
+            'componentfocus',
+
+            /**
+             * @event disable
+             * Fires when the FocusManager is disabled
+             * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
+             */
+            'disable',
+
+            /**
+             * @event enable
+             * Fires when the FocusManager is enabled
+             * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
+             */
+            'enable'
+        );
+
+        // Setup KeyNav that's bound to document to catch all
+        // unhandled/bubbled key events for navigation
+        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
+
+            //space: me.navigateIn,
+            //del: me.focusLast,
+            //left: me.navigateSiblings,
+            //right: me.navigateSiblings,
+            //down: me.navigateSiblings,
+            //up: me.navigateSiblings
+        });
+
+        me.focusData = {};
+        me.subscribers = Ext.create('Ext.util.HashMap');
+        me.focusChain = {};
+
+        // Setup some ComponentQuery pseudos
+        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;
+            }
+        });
+    },
+
+    /**
+     * Adds the specified xtype to the {@link #whitelist}.
+     * @param {String/Array} xtype Adds the xtype(s) to the {@link #whitelist}.
+     */
+    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();
+        }
+    },
+
+    /**
+     * Disables the FocusManager by turning of all automatic focus management and keyboard navigation
+     */
+    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();
+
+        // Stop handling key navigation
+        me.keyNav.disable();
+
+        // disable focus for all components
+        me.setFocusAll(false);
+
+        me.fireEvent('disable', me);
+    },
+
+    /**
+     * Enables the FocusManager by turning on all automatic focus management and keyboard navigation
+     * @param {Boolean/Object} options Either `true`/`false` to turn on the focus frame, or an object of the following options:
+        - focusFrame : Boolean
+            `true` to show the focus frame around a component when it is focused. Defaults to `false`.
+     * @markdown
+     */
+    enable: function(options) {
+        var me = this;
+
+        if (options === true) {
+            options = { focusFrame: true };
+        }
+        me.options = options = options || {};
+
+        if (me.enabled) {
+            return;
+        }
+
+        // Handle components that are newly added after we are enabled
+        Ext.ComponentManager.all.on('add', me.onComponentCreated, me);
+
+        me.initDOM(options);
+
+        // Start handling key navigation
+        me.keyNav.enable();
+
+        // enable focus for all components
+        me.setFocusAll(true, options);
+
+        // Finally, let's focus our global focus el so we start fresh
+        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;
+        }
+
+        // Go back to last focused item
+        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 items should go to the top of our root stack, and be ordered
+        // by their z-index (highest first)
+        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;
+        }
+
+        // Create global focus element
+        if (!me.focusEl) {
+            me.focusEl = Ext.getBody().createChild({
+                tabIndex: '-1',
+                cls: me.focusElementCls,
+                html: sp
+            });
+        }
+
+        // Create global focus frame
+        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) {
+            // No focus yet, so focus the first root cmp on the page
+            rootCmps = me.getRootComponents();
+            if (rootCmps.length) {
+                rootCmps[0].focus();
+            }
+        } else {
+            // Drill into child ref items of the focused cmp, if applicable.
+            // This works for any Component with a getRefItems implementation.
+            firstChild = Ext.ComponentQuery.query('>:focusable', focusedCmp)[0];
+            if (firstChild) {
+                firstChild.focus();
+            } else {
+                // Let's try to fire a click event, as if it came from the mouse
+                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);
+
+            // Check our focus chain, so we don't run into a never ending recursion
+            // If we've attempted (unsuccessfully) to focus this component before,
+            // then we're caught in a loop of child->parent->...->child and we
+            // need to cut the loop off rather than feed into it.
+            if (chain[cmp.id]) {
+                return;
+            }
+
+            // Try to focus the parent instead
+            var parent = cmp.up();
+            if (parent) {
+                // Add component to our focus chain to detect infinite focus loop
+                // before we fire off an attempt to focus our parent.
+                // See the comments above.
+                chain[cmp.id] = true;
+                parent.focus();
+            }
+
+            return;
+        }
+
+        // Clear our focus chain when we have a focusable component
+        me.focusChain = {};
+
+        // Defer focusing for 90ms so components can do a layout/positioning
+        // and give us an ability to buffer focuses
+        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 we have a focus frame, show it around the focused component
+        if (me.shouldShowFocusFrame(cmp)) {
+            var cls = '.' + me.focusFrameCls + '-',
+                ff = me.focusFrame,
+                fw = me.focusFrameWidth,
+                box = cmp.el.getPageBox(),
+
+            // Size the focus frame's t/b/l/r according to the box
+            // This leaves a hole in the middle of the frame so user
+            // interaction w/ the mouse can continue
+                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 we are still enabled globally, or there are still subscribers
+        // then we will halt here, since our DOM stuff is still being used
+        if (me.enabled || me.subscribers.length) {
+            return;
+        }
+
+        Ext.destroy(
+            me.focusEl,
+            me.focusFrame
+        );
+        delete me.focusEl;
+        delete me.focusFrame;
+        delete me.focusFrameWidth;
+    },
+
+    /**
+     * Removes the specified xtype from the {@link #whitelist}.
+     * @param {String/Array} xtype Removes the xtype(s) from the {@link #whitelist}.
+     */
+    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 || {};
+
+        // Come back and do this after the component is rendered
+        if (!cmp.rendered) {
+            cmp.on('afterrender', Ext.pass(me.setFocus, arguments, me), me, { single: true });
+            return;
+        }
+
+        el = cmp.getFocusEl();
+        dom = el.dom;
+
+        // Decorate the component's focus el for focus-ability
+        if ((focusable && !me.focusData[cmp.id]) || (!focusable && me.focusData[cmp.id])) {
+            if (focusable) {
+                data = {
+                    focusFrame: options.focusFrame
+                };
+
+                // Only set -1 tabIndex if we need it
+                // inputs, buttons, and anchor tags do not need it,
+                // and neither does any DOM that has it set already
+                // programmatically or in markup.
+                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) {
+                    // Root the sibling navigation to this container, so that we
+                    // can automatically dive into the container, rather than forcing
+                    // the user to hit the enter key to dive in.
+                    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;
+        }
+
+        // Global trumps
+        if (opts.focusFrame) {
+            return true;
+        }
+
+        if (me.focusData[cmp.id].focusFrame) {
+            return true;
+        }
+
+        return false;
+    },
+
+    /**
+     * Subscribes an {@link Ext.container.Container} to provide basic keyboard focus navigation between its child {@link Ext.Component}'s.
+     * @param {Ext.container.Container} container A reference to the {@link Ext.container.Container} on which to enable keyboard functionality and focus management.
+     * @param {Boolean/Object} options An object of the following options:
+        - keys : Array/Object
+            An array containing the string names of navigation keys to be supported. The allowed values are:
+
+            - 'left'
+            - 'right'
+            - 'up'
+            - 'down'
+
+            Or, an object containing those key names as keys with `true` or a callback function as their value. A scope may also be passed. E.g.:
+
+                {
+                    left: this.onLeftKey,
+                    right: this.onRightKey,
+                    scope: this
+                }
+
+        - focusFrame : Boolean (optional)
+            `true` to show the focus frame around a component when it is focused. Defaults to `false`.
+     * @markdown
+     */
+    subscribe: function(container, options) {
+        var me = this,
+            EA = Ext.Array,
+            data = {},
+            subs = me.subscribers,
+
+            // Recursively add focus ability as long as a descendent container isn't
+            // itself subscribed to the FocusManager, or else we'd have unwanted side
+            // effects for subscribing a descendent container twice.
+            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);
+                }
+            };
+
+        // We only accept containers
+        if (!container || !container.isContainer) {
+            return;
+        }
+
+        if (!container.rendered) {
+            container.on('afterrender', Ext.pass(me.subscribe, arguments, me), me, { single: true });
+            return;
+        }
+
+        // Init the DOM, incase this is the first time it will be used
+        me.initDOM(options);
+
+        // Create key navigation for subscriber based on keys option
+        data.keyNav = me.setupSubscriberKeys(container, options.keys);
+
+        // We need to keep track of components being added to our subscriber
+        // and any containers nested deeply within it (omg), so let's do that.
+        // Components that are removed are globally handled.
+        // Also keep track of destruction of our container for auto-unsubscribe.
+        data.onAdd = function(ct, cmp, idx) {
+            safeSetFocus(cmp);
+        };
+        container.on('beforedestroy', me.unsubscribe, me);
+
+        // Now we setup focusing abilities for the container and all its components
+        safeSetFocus(container);
+
+        // Add to our subscribers list
+        subs.add(container.id, data);
+    },
+
+    /**
+     * Unsubscribes an {@link Ext.container.Container} from keyboard focus management.
+     * @param {Ext.container.Container} container A reference to the {@link Ext.container.Container} to unsubscribe from the FocusManager.
+     * @markdown
+     */
+    unsubscribe: function(container) {
+        var me = this,
+            EA = Ext.Array,
+            subs = me.subscribers,
+            data,
+
+            // Recursively remove focus ability as long as a descendent container isn't
+            // itself subscribed to the FocusManager, or else we'd have unwanted side
+            // effects for unsubscribing an ancestor container.
+            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();
+    }
+});
+/**
+ * @class Ext.toolbar.Toolbar
+ * @extends Ext.container.Container
+
+Basic Toolbar class. Although the {@link Ext.container.Container#defaultType defaultType} for Toolbar is {@link Ext.button.Button button}, Toolbar 
+elements (child items for the Toolbar container) may be virtually any type of Component. Toolbar elements can be created explicitly via their 
+constructors, or implicitly via their xtypes, and can be {@link #add}ed dynamically.
+
+__Some items have shortcut strings for creation:__
+
+| Shortcut | xtype         | Class                         | Description                                        |
+|:---------|:--------------|:------------------------------|:---------------------------------------------------|
+| `->`     | `tbspacer`    | {@link Ext.toolbar.Fill}      | begin using the right-justified button container   |
+| `-`      | `tbseparator` | {@link Ext.toolbar.Separator} | add a vertical separator bar between toolbar items |
+| ` `      | `tbspacer`    | {@link Ext.toolbar.Spacer}    | add horiztonal space between elements              |
+
+{@img Ext.toolbar.Toolbar/Ext.toolbar.Toolbar1.png Toolbar component}
+Example usage:
+
+    Ext.create('Ext.toolbar.Toolbar", {
+        renderTo: document.body,
+        width   : 500,
+        items: [
+            {
+                // xtype: 'button', // default for Toolbars
+                text: 'Button'
+            },
+            {
+                xtype: 'splitbutton',
+                text : 'Split Button'
+            },
+            // begin using the right-justified button container
+            '->', // same as {xtype: 'tbfill'}, // Ext.toolbar.Fill
+            {
+                xtype    : 'textfield',
+                name     : 'field1',
+                emptyText: 'enter search term'
+            },
+            // add a vertical separator bar between toolbar items
+            '-', // same as {xtype: 'tbseparator'} to create Ext.toolbar.Separator
+            'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.toolbar.TextItem
+            {xtype: 'tbspacer'},// same as ' ' to create Ext.toolbar.Spacer
+            'text 2',
+            {xtype: 'tbspacer', width: 50}, // add a 50px space
+            'text 3'
+        ]
+    });
+
+Toolbars have {@link #enable} and {@link #disable} methods which when called, will enable/disable all items within your toolbar.
+
+{@img Ext.toolbar.Toolbar/Ext.toolbar.Toolbar2.png Toolbar component}
+Example usage:
+
+    Ext.create('Ext.toolbar.Toolbar', {
+        renderTo: document.body,
+        width   : 400,
+        items: [
+            {
+                text: 'Button'
+            },
+            {
+                xtype: 'splitbutton',
+                text : 'Split Button'
+            },
+            '->',
+            {
+                xtype    : 'textfield',
+                name     : 'field1',
+                emptyText: 'enter search term'
+            }
+        ]
+    });
+
+{@img Ext.toolbar.Toolbar/Ext.toolbar.Toolbar3.png Toolbar component}
+Example usage:
+    
+    var enableBtn = Ext.create('Ext.button.Button', {
+        text    : 'Enable All Items',
+        disabled: true,
+        scope   : this,
+        handler : function() {
+            //disable the enable button and enable the disable button
+            enableBtn.disable();
+            disableBtn.enable();
+            
+            //enable the toolbar
+            toolbar.enable();
+        }
+    });
+    
+    var disableBtn = Ext.create('Ext.button.Button', {
+        text    : 'Disable All Items',
+        scope   : this,
+        handler : function() {
+            //enable the enable button and disable button
+            disableBtn.disable();
+            enableBtn.enable();
+            
+            //disable the toolbar
+            toolbar.disable();
+        }
+    });
+    
+    var toolbar = Ext.create('Ext.toolbar.Toolbar', {
+        renderTo: document.body,
+        width   : 400,
+        margin  : '5 0 0 0',
+        items   : [enableBtn, disableBtn]
+    });
+
+Adding items to and removing items from a toolbar is as simple as calling the {@link #add} and {@link #remove} methods. There is also a {@link #removeAll} method 
+which remove all items within the toolbar.
+
+{@img Ext.toolbar.Toolbar/Ext.toolbar.Toolbar4.png Toolbar component}
+Example usage:
+
+    var toolbar = Ext.create('Ext.toolbar.Toolbar', {
+        renderTo: document.body,
+        width   : 700,
+        items: [
+            {
+                text: 'Example Button'
+            }
+        ]
+    });
+    
+    var addedItems = [];
+    
+    Ext.create('Ext.toolbar.Toolbar', {
+        renderTo: document.body,
+        width   : 700,
+        margin  : '5 0 0 0',
+        items   : [
+            {
+                text   : 'Add a button',
+                scope  : this,
+                handler: function() {
+                    var text = prompt('Please enter the text for your button:');
+                    addedItems.push(toolbar.add({
+                        text: text
+                    }));
+                }
+            },
+            {
+                text   : 'Add a text item',
+                scope  : this,
+                handler: function() {
+                    var text = prompt('Please enter the text for your item:');
+                    addedItems.push(toolbar.add(text));
+                }
+            },
+            {
+                text   : 'Add a toolbar seperator',
+                scope  : this,
+                handler: function() {
+                    addedItems.push(toolbar.add('-'));
+                }
+            },
+            {
+                text   : 'Add a toolbar spacer',
+                scope  : this,
+                handler: function() {
+                    addedItems.push(toolbar.add('->'));
+                }
+            },
+            '->',
+            {
+                text   : 'Remove last inserted item',
+                scope  : this,
+                handler: function() {
+                    if (addedItems.length) {
+                        toolbar.remove(addedItems.pop());
+                    } else if (toolbar.items.length) {
+                        toolbar.remove(toolbar.items.last());
+                    } else {
+                        alert('No items in the toolbar');
+                    }
+                }
+            },
+            {
+                text   : 'Remove all items',
+                scope  : this,
+                handler: function() {
+                    toolbar.removeAll();
+                }
+            }
+        ]
+    });
+
+ * @constructor
+ * Creates a new Toolbar
+ * @param {Object/Array} config A config object or an array of buttons to <code>{@link #add}</code>
+ * @xtype toolbar
+ * @docauthor Robert Dougan <rob@sencha.com>
+ * @markdown
+ */
+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',
+    
+    /**
+     * @cfg {Boolean} vertical
+     * Set to `true` to make the toolbar vertical. The layout will become a `vbox`.
+     * (defaults to `false`)
+     */
+    vertical: false,
+
+    /**
+     * @cfg {String/Object} layout
+     * This class assigns a default layout (<code>layout:'<b>hbox</b>'</code>).
+     * Developers <i>may</i> override this configuration option if another layout
+     * is required (the constructor must be passed a configuration object in this
+     * case instead of an array).
+     * See {@link Ext.container.Container#layout} for additional information.
+     */
+
+    /**
+     * @cfg {Boolean} enableOverflow
+     * Defaults to false. Configure <code>true</code> to make the toolbar provide a button
+     * which activates a dropdown Menu to show items which overflow the Toolbar's width.
+     */
+    enableOverflow: false,
+    
+    // private
+    trackMenus: true,
+    
+    itemCls: Ext.baseCSSPrefix + 'toolbar-item',
+    
+    initComponent: function() {
+        var me = this,
+            keys;
+
+        // check for simplified (old-style) overflow config:
+        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');
+        }
+        
+        // @TODO: remove this hack and implement a more general solution
+        if (me.ui === 'footer') {
+            me.ignoreBorderManagement = true;
+        }
+        
+        me.callParent();
+
+        /**
+         * @event overflowchange
+         * Fires after the overflow state has changed.
+         * @param {Object} c The Container
+         * @param {Boolean} lastOverflow overflow state
+         */
+        me.addEvents('overflowchange');
+        
+        // Subscribe to Ext.FocusManager for key navigation
+        keys = me.vertical ? ['up', 'down'] : ['left', 'right'];
+        Ext.FocusManager.subscribe(me, {
+            keys: keys
+        });
+    },
+
+    /**
+     * <p>Adds element(s) to the toolbar -- this function takes a variable number of
+     * arguments of mixed type and adds them to the toolbar.</p>
+     * <br><p><b>Note</b>: See the notes within {@link Ext.container.Container#add}.</p>
+     * @param {Mixed} arg1 The following types of arguments are all valid:<br />
+     * <ul>
+     * <li>{@link Ext.button.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
+     * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
+     * <li>Field: Any form field (equivalent to {@link #addField})</li>
+     * <li>Item: Any subclass of {@link Ext.toolbar.Item} (equivalent to {@link #addItem})</li>
+     * <li>String: Any generic string (gets wrapped in a {@link Ext.toolbar.TextItem}, equivalent to {@link #addText}).
+     * Note that there are a few special strings that are treated differently as explained next.</li>
+     * <li>'-': Creates a separator element (equivalent to {@link #addSeparator})</li>
+     * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
+     * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
+     * </ul>
+     * @param {Mixed} arg2
+     * @param {Mixed} etc.
+     * @method add
+     */
+
+    // private
+    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);
+    },
+
+    // private
+    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;
+    },
+
+    // private
+    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);
+        }
+    },
+
+    // private
+    constructButton: function(item) {
+        return item.events ? item : this.createComponent(item, item.split ? 'splitbutton' : this.defaultType);
+    },
+
+    // private
+    onBeforeAdd: function(component) {
+        if (component.is('field') || (component.is('button') && this.ui != 'footer')) {
+            component.ui = component.ui + '-toolbar';
+        }
+        
+        // Any separators needs to know if is vertical or not
+        if (component instanceof Ext.toolbar.Separator) {
+            component.setUI((this.vertical) ? 'vertical' : 'horizontal');
+        }
+        
+        this.callParent(arguments);
+    },
+
+    // private
+    onAdd: function(component) {
+        this.callParent(arguments);
+
+        this.trackMenu(component);
+        if (this.disabled) {
+            component.disable();
+        }
+    },
+
+    // private
+    onRemove: function(c) {
+        this.callParent(arguments);
+        this.trackMenu(c, true);
+    },
+
+    // private
+    onButtonTriggerOver: function(btn){
+        if (this.activeMenuBtn && this.activeMenuBtn != btn) {
+            this.activeMenuBtn.hideMenu();
+            btn.showMenu();
+            this.activeMenuBtn = btn;
+        }
+    },
+
+    // private
+    onButtonMenuShow: function(btn) {
+        this.activeMenuBtn = btn;
+    },
+
+    // private
+    onButtonMenuHide: function(btn) {
+        delete this.activeMenuBtn;
+    }
+}, function() {
+    this.shortcuts = {
+        '-' : 'tbseparator',
+        ' ' : 'tbspacer',
+        '->': 'tbfill'
+    };
+});
+/**
+ * @class Ext.panel.AbstractPanel
+ * @extends Ext.container.Container
+ * <p>A base class which provides methods common to Panel classes across the Sencha product range.</p>
+ * <p>Please refer to sub class's documentation</p>
+ * @constructor
+ * @param {Object} config The config object
+ */
+Ext.define('Ext.panel.AbstractPanel', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.container.Container',
+
+    requires: ['Ext.util.MixedCollection', 'Ext.core.Element', 'Ext.toolbar.Toolbar'],
+
+    /* End Definitions */
+
+    /**
+     * @cfg {String} baseCls
+     * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
+     */
+    baseCls : Ext.baseCSSPrefix + 'panel',
+
+    /**
+     * @cfg {Number/String} bodyPadding
+     * A shortcut for setting a padding style on the body element. The value can either be
+     * a number to be applied to all sides, or a normal css string describing padding.
+     * Defaults to <code>undefined</code>.
+     */
+
+    /**
+     * @cfg {Boolean} bodyBorder
+     * A shortcut to add or remove the border on the body of a panel. This only applies to a panel which has the {@link #frame} configuration set to `true`.
+     * Defaults to <code>undefined</code>.
+     */
+    
+    /**
+     * @cfg {String/Object/Function} bodyStyle
+     * Custom CSS styles to be applied to the panel's body element, which can be supplied as a valid CSS style string,
+     * an object containing style property name/value pairs or a function that returns such a string or object.
+     * For example, these two formats are interpreted to be equivalent:<pre><code>
+bodyStyle: 'background:#ffc; padding:10px;'
+
+bodyStyle: {
+    background: '#ffc',
+    padding: '10px'
+}
+     * </code></pre>
+     */
+    
+    /**
+     * @cfg {String/Array} bodyCls
+     * A CSS class, space-delimited string of classes, or array of classes to be applied to the panel's body element.
+     * The following examples are all valid:<pre><code>
+bodyCls: 'foo'
+bodyCls: 'foo bar'
+bodyCls: ['foo', 'bar']
+     * </code></pre>
+     */
+
+    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>'],
+
+    // TODO: Move code examples into product-specific files. The code snippet below is Touch only.
+    /**
+     * @cfg {Object/Array} dockedItems
+     * A component or series of components to be added as docked items to this panel.
+     * The docked items can be docked to either the top, right, left or bottom of a panel.
+     * This is typically used for things like toolbars or tab bars:
+     * <pre><code>
+var panel = new Ext.panel.Panel({
+    fullscreen: true,
+    dockedItems: [{
+        xtype: 'toolbar',
+        dock: 'top',
+        items: [{
+            text: 'Docked to the top'
+        }]
+    }]
+});</code></pre>
+     */
+     
+    border: true,
+
+    initComponent : function() {
+        var me = this;
+        
+        me.addEvents(
+            /**
+             * @event bodyresize
+             * Fires after the Panel has been resized.
+             * @param {Ext.panel.Panel} p the Panel which has been resized.
+             * @param {Number} width The Panel body's new width.
+             * @param {Number} height The Panel body's new height.
+             */
+            'bodyresize'
+            // // inherited
+            // 'activate',
+            // // inherited
+            // 'deactivate'
+        );
+
+        Ext.applyIf(me.renderSelectors, {
+            body: '.' + me.baseCls + '-body'
+        });
+        
+        //!frame 
+        //!border
+        
+        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();
+    },
+
+    // @private
+    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);
+        }
+    },
+
+    /**
+     * Finds a docked component by id, itemId or position. Also see {@link #getDockedItems}
+     * @param {String/Number} comp The id, itemId or position of the docked component (see {@link #getComponent} for details)
+     * @return {Ext.Component} The docked component (if found)
+     */
+    getDockedComponent: function(comp) {
+        if (Ext.isObject(comp)) {
+            comp = comp.getItemId();
+        }
+        return this.dockedItems.get(comp);
+    },
+
+    /**
+     * Attempts a default component lookup (see {@link Ext.container.Container#getComponent}). If the component is not found in the normal
+     * items, the dockedItems are searched and the matched component (if any) returned (see {@loink #getDockedComponent}). Note that docked
+     * items will only be matched by component id or itemId -- if you pass a numeric index only non-docked child components will be searched.
+     * @param {String/Number} comp The component id, itemId or position to find
+     * @return {Ext.Component} The component (if found)
+     */
+    getComponent: function(comp) {
+        var component = this.callParent(arguments);
+        if (component === undefined && !Ext.isNumber(comp)) {
+            // If the arg is a numeric index skip docked items
+            component = this.getDockedComponent(comp);
+        }
+        return component;
+    },
+
+    /**
+     * Parses the {@link bodyStyle} config if available to create a style string that will be applied to the body element.
+     * This also includes {@link bodyPadding} and {@link bodyBorder} if available.
+     * @return {String} A CSS style string with body styles, padding and border.
+     * @private
+     */
+    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;
+    },
+    
+    /**
+     * Parse the {@link bodyCls} config if available to create a comma-delimited string of 
+     * CSS classes to be applied to the body element.
+     * @return {String} The CSS class(es)
+     * @private
+     */
+    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;
+    },
+    
+    /**
+     * Initialized the renderData to be used when rendering the renderTpl.
+     * @return {Object} Object with keys and values that are going to be applied to the renderTpl
+     * @private
+     */
+    initRenderData: function() {
+        return Ext.applyIf(this.callParent(), {
+            bodyStyle: this.initBodyStyles(),
+            bodyCls: this.initBodyCls()
+        });
+    },
+
+    /**
+     * Adds docked item(s) to the panel.
+     * @param {Object/Array} component The Component or array of components to add. The components
+     * must include a 'dock' parameter on each component to indicate where it should be docked ('top', 'right',
+     * 'bottom', 'left').
+     * @param {Number} pos (optional) The index at which the Component will be added
+     */
+    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';
+
+            // Allow older browsers to target docked items to style without borders
+            if (me.border === false) {
+                // item.cls = item.cls || '' + ' ' + me.baseCls + '-noborder-docked-' + item.dock;
+            }
+
+            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.doComponentLayout();
+        }
+        return items;
+    },
+
+    // Placeholder empty functions
+    onDockedAdd : Ext.emptyFn,
+    onDockedRemove : Ext.emptyFn,
+
+    /**
+     * Inserts docked item(s) to the panel at the indicated position.
+     * @param {Number} pos The index at which the Component will be inserted
+     * @param {Object/Array} component. The Component or array of components to add. The components
+     * must include a 'dock' paramater on each component to indicate where it should be docked ('top', 'right',
+     * 'bottom', 'left').
+     */
+    insertDocked : function(pos, items) {
+        this.addDocked(items, pos);
+    },
+
+    /**
+     * Removes the docked item from the panel.
+     * @param {Ext.Component} item. The Component to remove.
+     * @param {Boolean} autoDestroy (optional) Destroy the component after removal.
+     */
+    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;
+    },
+
+    /**
+     * Retrieve an array of all currently docked Components.
+     * @param {String} cqSelector A {@link Ext.ComponentQuery ComponentQuery} selector string to filter the returned items.
+     * @return {Array} An array of components.
+     */
+    getDockedItems : function(cqSelector) {
+        var me = this,
+            // Start with a weight of 1, so users can provide <= 0 to come before top items
+            // Odd numbers, so users can provide a weight to come in between if desired
+            defaultWeight = { top: 1, left: 3, right: 5, bottom: 7 },
+            dockedItems;
+
+        if (me.dockedItems && me.dockedItems.items.length) {
+            // Allow filtering of returned docked items by CQ selector.
+            if (cqSelector) {
+                dockedItems = Ext.ComponentQuery.query(cqSelector, me.dockedItems.items);
+            } else {
+                dockedItems = me.dockedItems.items.slice();
+            }
+
+            Ext.Array.sort(dockedItems, function(a, b) {
+                // Docked items are ordered by their visual representation by default (t,l,r,b)
+                // TODO: Enforce position ordering, and have weights be sub-ordering within positions?
+                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 [];
+    },
+    
+    // inherit docs
+    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);
+        }
+    },
+    
+    // inherit docs
+    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);
+        }
+    },
+    
+    // inherit docs
+    addUIToElement: function(force) {
+        var me = this;
+        
+        me.callParent(arguments);
+        
+        if (!force && me.rendered) {
+            me.body.addCls(me.baseCls + '-body-' + me.ui);
+        }
+    },
+    
+    // inherit docs
+    removeUIFromElement: function() {
+        var me = this;
+        
+        me.callParent(arguments);
+        
+        if (me.rendered) {
+            me.body.removeCls(me.baseCls + '-body-' + me.ui);
+        }
+    },
+
+    // @private
+    getTargetEl : function() {
+        return this.body;
+    },
+
+    getRefItems: function(deep) {
+        var items = this.callParent(arguments),
+            // deep fetches all docked items, and their descendants using '*' selector and then '* *'
+            dockedItems = this.getDockedItems(deep ? '*,* *' : undefined),
+            ln = dockedItems.length,
+            i = 0,
+            item;
+        
+        // Find the index where we go from top/left docked items to right/bottom docked items
+        for (; i < ln; i++) {
+            item = dockedItems[i];
+            if (item.dock === 'right' || item.dock === 'bottom') {
+                break;
+            }
+        }
+        
+        // Return docked items in the top/left position before our container items, and
+        // return right/bottom positioned items after our container items.
+        // See AbstractDock.renderItems() for more information.
+        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();
+        }
+    }
+});
+/**
+ * @class Ext.panel.Header
+ * @extends Ext.container.Container
+ * Simple header class which is used for on {@link Ext.panel.Panel} and {@link Ext.window.Window}
+ * @xtype header
+ */
+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';
+
+        //add the dock as a ui
+        //this is so we support top/right/left/bottom headers
+        me.addClsWithUI(me.orientation);
+        me.addClsWithUI(me.dock);
+
+        Ext.applyIf(me.renderSelectors, {
+            body: '.' + me.baseCls + '-body'
+        });
+
+        // Add Icon
+        if (!Ext.isEmpty(me.iconCls)) {
+            me.initIconCmp();
+            me.items.push(me.iconCmp);
+        }
+
+        // Add Title
+        if (me.orientation == 'vertical') {
+            // Hack for IE6/7's inability to display an inline-block
+            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);
+
+        // Spacer ->
+        me.items.push({
+            xtype: 'component',
+            html : '&nbsp;',
+            flex : 1,
+            focusable: false
+        });
+
+        // Add Tools
+        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);
+
+        // IE7 needs a forced repaint to make the top framing div expand to full width
+        if (Ext.isIE7) {
+            me.el.repaint();
+        }
+    },
+
+    // inherit docs
+    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);
+        }
+    },
+
+    // inherit docs
+    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);
+        }
+    },
+
+    // inherit docs
+    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);
+        }
+    },
+
+    // inherit docs
+    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;
+    },
+
+    /**
+     * Sets the title of the header.
+     * @param {String} title The title to be set
+     */
+    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
+            });
+        }
+    },
+
+    /**
+     * Sets the CSS class that provides the icon image for this panel.  This method will replace any existing
+     * icon class if one has already been set and fire the {@link #iconchange} event after completion.
+     * @param {String} cls The new CSS class name
+     */
+    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;
+            }
+        }
+    },
+
+    /**
+     * Add a tool to the header
+     * @param {Object} tool
+     */
+    addTool: function(tool) {
+        this.tools.push(this.add(tool));
+    },
+
+    /**
+     * @private
+     * Set up the tools.&lt;tool type> link in the owning Panel.
+     * Bind the tool to its owning Panel.
+     * @param component
+     * @param index
+     */
+    onAdd: function(component, index) {
+        this.callParent([arguments]);
+        if (component instanceof Ext.panel.Tool) {
+            component.bindTo(this.ownerCt);
+            this.tools[component.type] = component;
+        }
+    }
+});
+
+/**
+ * @class Ext.fx.target.Element
+ * @extends Ext.fx.target.Target
+ * 
+ * This class represents a animation target for an {@link Ext.core.Element}. In general this class will not be
+ * created directly, the {@link Ext.core.Element} will be passed to the animation and
+ * and the appropriate target will be created.
+ */
+Ext.define('Ext.fx.target.Element', {
+
+    /* Begin Definitions */
+    
+    extend: 'Ext.fx.target.Target',
+    
+    /* End Definitions */
+
+    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);
+                        }
+                    }
+                }
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.fx.target.CompositeElement
+ * @extends Ext.fx.target.Element
+ * 
+ * This class represents a animation target for a {@link Ext.CompositeElement}. It allows
+ * each {@link Ext.core.Element} in the group to be animated as a whole. In general this class will not be
+ * created directly, the {@link Ext.CompositeElement} will be passed to the animation and
+ * and the appropriate target will be created.
+ */
+Ext.define('Ext.fx.target.CompositeElement', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.fx.target.Element',
+
+    /* End Definitions */
+
+    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;
+    }
+});
+
+/**
+ * @class Ext.fx.Manager
+ * Animation Manager which keeps track of all current animations and manages them on a frame by frame basis.
+ * @private
+ * @singleton
+ */
+
+Ext.define('Ext.fx.Manager', {
+
+    /* Begin Definitions */
+
+    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'
+    },
+
+    /* End Definitions */
+
+    constructor: function() {
+        this.items = Ext.create('Ext.util.MixedCollection');
+        this.mixins.queue.constructor.call(this);
+
+        // this.requestAnimFrame = (function() {
+        //     var raf = window.requestAnimationFrame ||
+        //               window.webkitRequestAnimationFrame ||
+        //               window.mozRequestAnimationFrame ||
+        //               window.oRequestAnimationFrame ||
+        //               window.msRequestAnimationFrame;
+        //     if (raf) {
+        //         return function(callback, element) {
+        //             raf(callback);
+        //         };
+        //     }
+        //     else {
+        //         return function(callback, element) {
+        //             window.setTimeout(callback, Ext.fx.Manager.interval);
+        //         };
+        //     }
+        // })();
+    },
+
+    /**
+     * @cfg {Number} interval Default interval in miliseconds to calculate each frame.  Defaults to 16ms (~60fps)
+     */
+    interval: 16,
+
+    /**
+     * @cfg {Boolean} forceJS Turn off to not use CSS3 transitions when they are available
+     */
+    forceJS: true,
+
+    // @private Target factory
+    createTarget: function(target) {
+        var me = this,
+            useCSS3 = !me.forceJS && Ext.supports.Transitions,
+            targetObj;
+
+        me.useCSS3 = useCSS3;
+
+        // dom id
+        if (Ext.isString(target)) {
+            target = Ext.get(target);
+        }
+        // dom element
+        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)) {
+            // Element
+            if (target.dom) {
+                targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
+            }
+            // Element Composite
+            else if (target.isComposite) {
+                targetObj = Ext.create('Ext.fx.target.' + 'CompositeElement' + (useCSS3 ? 'CSS' : ''), target);
+            }
+            // Draw Sprite
+            else if (target.isSprite) {
+                targetObj = Ext.create('Ext.fx.target.Sprite', target);
+            }
+            // Draw Sprite Composite
+            else if (target.isCompositeSprite) {
+                targetObj = Ext.create('Ext.fx.target.CompositeSprite', target);
+            }
+            // Component
+            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;
+        }
+    },
+
+    /**
+     * Add an Anim to the manager. This is done automatically when an Anim instance is created.
+     * @param {Ext.fx.Anim} anim
+     */
+    addAnim: function(anim) {
+        var items = this.items,
+            task = this.task;
+        // var me = this,
+        //     items = me.items,
+        //     cb = function() {
+        //         if (items.length) {
+        //             me.task = true;
+        //             me.runner();
+        //             me.requestAnimFrame(cb);
+        //         }
+        //         else {
+        //             me.task = false;
+        //         }
+        //     };
+
+        items.add(anim);
+
+        // Start the timer if not already running
+        if (!task && items.length) {
+            task = this.task = {
+                run: this.runner,
+                interval: this.interval,
+                scope: this
+            };
+            Ext.TaskManager.start(task);
+        }
+
+        // //Start the timer if not already running
+        // if (!me.task && items.length) {
+        //     me.requestAnimFrame(cb);
+        // }
+    },
+
+    /**
+     * Remove an Anim from the manager. This is done automatically when an Anim ends.
+     * @param {Ext.fx.Anim} anim
+     */
+    removeAnim: function(anim) {
+        // this.items.remove(anim);
+        var items = this.items,
+            task = this.task;
+        items.remove(anim);
+        // Stop the timer if there are no more managed Anims
+        if (task && !items.length) {
+            Ext.TaskManager.stop(task);
+            delete this.task;
+        }
+    },
+
+    /**
+     * @private
+     * Filter function to determine which animations need to be started
+     */
+    startingFilter: function(o) {
+        return o.paused === false && o.running === false && o.iterations > 0;
+    },
+
+    /**
+     * @private
+     * Filter function to determine which animations are still running
+     */
+    runningFilter: function(o) {
+        return o.paused === false && o.running === true && o.isAnimator !== true;
+    },
+
+    /**
+     * @private
+     * Runner function being called each frame
+     */
+    runner: function() {
+        var me = this,
+            items = me.items;
+
+        me.targetData = {};
+        me.targetArr = {};
+
+        // Single timestamp for all animations this interval
+        me.timestamp = new Date();
+
+        // Start any items not current running
+        items.filterBy(me.startingFilter).each(me.startAnim, me);
+
+        // Build the new attributes to be applied for all targets in this frame
+        items.filterBy(me.runningFilter).each(me.runAnim, me);
+
+        // Apply all the pending changes to their targets
+        me.applyPendingAttrs();
+    },
+
+    /**
+     * @private
+     * Start the individual animation (initialization)
+     */
+    startAnim: function(anim) {
+        anim.start(this.timestamp);
+    },
+
+    /**
+     * @private
+     * Run the individual animation for this frame
+     */
+    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);
+
+        // For CSS3 animation, we need to immediately set the first frame's attributes without any transition
+        // to get a good initial state, then add the transition properties and set the final attributes.
+        if (useCSS3) {
+            // Flush the collected attributes, without transition
+            anim.target.setAttr(me.targetData[targetId], true);
+
+            // Add the end frame data
+            me.targetData[targetId] = [];
+            me.collectTargetData(anim, anim.duration, useCSS3);
+
+            // Pause the animation so runAnim doesn't keep getting called
+            anim.paused = true;
+
+            target = anim.target.target;
+            // We only want to attach an event on the last element in a composite
+            if (anim.target.isComposite) {
+                target = anim.target.target.last();
+            }
+
+            // Listen for the transitionend event
+            o = {};
+            o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
+            o.scope = anim;
+            o.single = true;
+            target.on(o);
+        }
+        // For JS animation, trigger the lastFrame handler if this is the final frame
+        else if (elapsedTime >= anim.duration) {
+            me.applyPendingAttrs(true);
+            delete me.targetData[targetId];
+            delete me.targetArr[targetId];
+            anim.lastFrame();
+        }
+    },
+
+    /**
+     * Collect target attributes for the given Anim object at the given timestamp
+     * @param {Ext.fx.Anim} anim The Anim instance
+     * @param {Number} timestamp Time after the anim's start time
+     */
+    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);
+    },
+
+    /**
+     * @private
+     * Apply all pending attribute changes to their targets
+     */
+    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);
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.fx.Animator
+ * Animation instance
+
+This class is used to run keyframe based animations, which follows the CSS3 based animation structure. 
+Keyframe animations differ from typical from/to animations in that they offer the ability to specify values 
+at various points throughout the animation.
+
+__Using Keyframes__
+The {@link #keyframes} option is the most important part of specifying an animation when using this 
+class. A key frame is a point in a particular animation. We represent this as a percentage of the
+total animation duration. At each key frame, we can specify the target values at that time. Note that
+you *must* specify the values at 0% and 100%, the start and ending values. There is also a {@link keyframe}
+event that fires after each key frame is reached.
+
+__Example Usage__
+In the example below, we modify the values of the element at each fifth throughout the animation.
+
+    Ext.create('Ext.fx.Animator', {
+        target: Ext.getBody().createChild({
+            style: {
+                width: '100px',
+                height: '100px',
+                'background-color': 'red'
+            }
+        }),
+        duration: 10000, // 10 seconds
+        keyframes: {
+            0: {
+                opacity: 1,
+                backgroundColor: 'FF0000'
+            },
+            20: {
+                x: 30,
+                opacity: 0.5    
+            },
+            40: {
+                x: 130,
+                backgroundColor: '0000FF'    
+            },
+            60: {
+                y: 80,
+                opacity: 0.3    
+            },
+            80: {
+                width: 200,
+                y: 200    
+            },
+            100: {
+                opacity: 1,
+                backgroundColor: '00FF00'
+            }
+        }
+    });
+
+ * @markdown
+ */
+Ext.define('Ext.fx.Animator', {
+
+    /* Begin Definitions */
+
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+
+    requires: ['Ext.fx.Manager'],
+
+    /* End Definitions */
+
+    isAnimator: true,
+
+    /**
+     * @cfg {Number} duration
+     * Time in milliseconds for the animation to last. Defaults to 250.
+     */
+    duration: 250,
+
+    /**
+     * @cfg {Number} delay
+     * Time to delay before starting the animation. Defaults to 0.
+     */
+    delay: 0,
+
+    /* private used to track a delayed starting time */
+    delayStart: 0,
+
+    /**
+     * @cfg {Boolean} dynamic
+     * Currently only for Component Animation: Only set a component's outer element size bypassing layouts.  Set to true to do full layouts for every frame of the animation.  Defaults to false.
+     */
+    dynamic: false,
+
+    /**
+     * @cfg {String} easing
+
+This describes how the intermediate values used during a transition will be calculated. It allows for a transition to change
+speed over its duration. 
+
+- backIn
+- backOut
+- bounceIn
+- bounceOut
+- ease
+- easeIn
+- easeOut
+- easeInOut
+- elasticIn
+- elasticOut
+- cubic-bezier(x1, y1, x2, y2)
+
+Note that cubic-bezier will create a custom easing curve following the CSS3 transition-timing-function specification `{@link http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag}`. The four values specify points P1 and P2 of the curve
+as (x1, y1, x2, y2). All values must be in the range [0, 1] or the definition is invalid.
+
+     * @markdown
+     */
+    easing: 'ease',
+
+    /**
+     * Flag to determine if the animation has started
+     * @property running
+     * @type boolean
+     */
+    running: false,
+
+    /**
+     * Flag to determine if the animation is paused. Only set this to true if you need to
+     * keep the Anim instance around to be unpaused later; otherwise call {@link #end}.
+     * @property paused
+     * @type boolean
+     */
+    paused: false,
+
+    /**
+     * @private
+     */
+    damper: 1,
+
+    /**
+     * @cfg {Number} iterations
+     * Number of times to execute the animation. Defaults to 1.
+     */
+    iterations: 1,
+
+    /**
+     * Current iteration the animation is running.
+     * @property currentIteration
+     * @type int
+     */
+    currentIteration: 0,
+
+    /**
+     * Current keyframe step of the animation.
+     * @property keyframeStep
+     * @type Number
+     */
+    keyframeStep: 0,
+
+    /**
+     * @private
+     */
+    animKeyFramesRE: /^(from|to|\d+%?)$/,
+
+    /**
+     * @cfg {Ext.fx.target} target
+     * The Ext.fx.target to apply the animation to.  If not specified during initialization, this can be passed to the applyAnimator
+     * method to apply the same animation to many targets.
+     */
+
+     /**
+      * @cfg {Object} keyframes
+      * Animation keyframes follow the CSS3 Animation configuration pattern. 'from' is always considered '0%' and 'to'
+      * is considered '100%'.<b>Every keyframe declaration must have a keyframe rule for 0% and 100%, possibly defined using
+      * "from" or "to"</b>.  A keyframe declaration without these keyframe selectors is invalid and will not be available for
+      * animation.  The keyframe declaration for a keyframe rule consists of properties and values. Properties that are unable to
+      * be animated are ignored in these rules, with the exception of 'easing' which can be changed at each keyframe. For example:
+ <pre><code>
+keyframes : {
+    '0%': {
+        left: 100
+    },
+    '40%': {
+        left: 150
+    },
+    '60%': {
+        left: 75
+    },
+    '100%': {
+        left: 100
+    }
+}
+ </code></pre>
+      */
+    constructor: function(config) {
+        var me = this;
+        config = Ext.apply(me, config || {});
+        me.config = config;
+        me.id = Ext.id(null, 'ext-animator-');
+        me.addEvents(
+            /**
+             * @event beforeanimate
+             * Fires before the animation starts. A handler can return false to cancel the animation.
+             * @param {Ext.fx.Animator} this
+             */
+            'beforeanimate',
+            /**
+              * @event keyframe
+              * Fires at each keyframe.
+              * @param {Ext.fx.Animator} this
+              * @param {Number} keyframe step number
+              */
+            'keyframe',
+            /**
+             * @event afteranimate
+             * Fires when the animation is complete.
+             * @param {Ext.fx.Animator} this
+             * @param {Date} startTime
+             */
+            '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);
+        }
+    },
+
+    /**
+     * @private
+     */
+    sorter: function (a, b) {
+        return a.pct - b.pct;
+    },
+
+    /**
+     * @private
+     * Takes the given keyframe configuration object and converts it into an ordered array with the passed attributes per keyframe
+     * or applying the 'to' configuration to all keyframes.  Also calculates the proper animation duration per keyframe.
+     */
+    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)};
+                // CSS3 spec allow for from/to to be specified.
+                if (pct == "from") {
+                    pct = 0;
+                }
+                else if (pct == "to") {
+                    pct = 100;
+                }
+                // convert % values into integers
+                attr.pct = parseInt(pct, 10);
+                attrs.push(attr);
+            }
+        }
+        // Sort by pct property
+        Ext.Array.sort(attrs, me.sorter);
+        // Only an end
+        //if (attrs[0].pct) {
+        //    attrs.unshift({pct: 0, attrs: element.attrs});
+        //}
+
+        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
+            });
+        }
+    },
+
+    /**
+     * Applies animation to the Ext.fx.target
+     * @private
+     * @param target
+     * @type string/object
+     */
+    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);
+        }
+    },
+
+    /*
+     * @private
+     * Fires beforeanimate and sets the running flag.
+     */
+    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 {
+                    // Compensate for frame delay;
+                    startTime = new Date(delayStart.getTime() + delay);
+                }
+            }
+        }
+        if (me.fireEvent('beforeanimate', me) !== false) {
+            me.startTime = startTime;
+            me.running = true;
+            me.animations[me.keyframeStep].paused = false;
+        }
+    },
+
+    /*
+     * @private
+     * Perform lastFrame cleanup and handle iterations
+     * @returns a hash of the new attributes.
+     */
+    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();
+        }
+    },
+
+    /*
+     * Fire afteranimate event and end the animation. Usually called automatically when the
+     * animation reaches its final frame, but can also be called manually to pre-emptively
+     * stop and destroy the running animation.
+     */
+    end: function() {
+        var me = this;
+        me.fireEvent('afteranimate', me, me.startTime, new Date() - me.startTime);
+    }
+});
+/**
+ * @class Ext.fx.Easing
+ * 
+This class contains a series of function definitions used to modify values during an animation.
+They describe how the intermediate values used during a transition will be calculated. It allows for a transition to change
+speed over its duration. The following options are available: 
+
+- linear The default easing type
+- backIn
+- backOut
+- bounceIn
+- bounceOut
+- ease
+- easeIn
+- easeOut
+- easeInOut
+- elasticIn
+- elasticOut
+- cubic-bezier(x1, y1, x2, y2)
+
+Note that cubic-bezier will create a custom easing curve following the CSS3 transition-timing-function specification `{@link http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag}`. The four values specify points P1 and P2 of the curve
+as (x1, y1, x2, y2). All values must be in the range [0, 1] or the definition is invalid.
+ * @markdown
+ * @singleton
+ */
+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 = {
+        // ease: Ext.fx.CubicBezier.cubicBezier(0.25, 0.1, 0.25, 1),
+        // linear: Ext.fx.CubicBezier.cubicBezier(0, 0, 1, 1),
+        // 'ease-in': Ext.fx.CubicBezier.cubicBezier(0.42, 0, 1, 1),
+        // 'ease-out': Ext.fx.CubicBezier.cubicBezier(0, 0.58, 1, 1),
+        // 'ease-in-out': Ext.fx.CubicBezier.cubicBezier(0.42, 0, 0.58, 1),
+        // 'easeIn': Ext.fx.CubicBezier.cubicBezier(0.42, 0, 1, 1),
+        // 'easeOut': Ext.fx.CubicBezier.cubicBezier(0, 0.58, 1, 1),
+        // 'easeInOut': Ext.fx.CubicBezier.cubicBezier(0.42, 0, 0.58, 1)
+    };
+
+    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
+    });
+});
+/*
+ * @class Ext.draw.Draw
+ * Base Drawing class.  Provides base drawing functions.
+ */
+
+Ext.define('Ext.draw.Draw', {
+    /* Begin Definitions */
+
+    singleton: true,
+
+    requires: ['Ext.draw.Color'],
+
+    /* End Definitions */
+
+    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");
+    },
+
+    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")) { // rough assumption
+            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")) { // rough assumption
+            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")) { // rough assumption
+            pathArray = this.parsePathString(pathArray);
+        }
+        var res = [],
+            x = 0,
+            y = 0,
+            mx = 0,
+            my = 0,
+            start = 0,
+            i,
+            ii,
+            r,
+            pa,
+            j,
+            jj,
+            k,
+            kk;
+        if (pathArray[0][0] == "M") {
+            x = +pathArray[0][1];
+            y = +pathArray[0][2];
+            mx = x;
+            my = y;
+            start++;
+            res[0] = ["M", x, y];
+        }
+        for (i = start, ii = pathArray.length; i < ii; i++) {
+            r = res[i] = [];
+            pa = pathArray[i];
+            if (pa[0] != pa[0].toUpperCase()) {
+                r[0] = pa[0].toUpperCase();
+                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);
+                        r[7] = +(pa[7] + y);
+                        break;
+                    case "V":
+                        r[1] = +pa[1] + y;
+                        break;
+                    case "H":
+                        r[1] = +pa[1] + x;
+                        break;
+                    case "M":
+                        mx = +pa[1] + x;
+                        my = +pa[2] + y;
+                    default:
+                        for (j = 1, jj = pa.length; j < jj; j++) {
+                            r[j] = +pa[j] + ((j % 2) ? x : y);
+                        }
+                }
+            } else {
+                for (k = 0, kk = pa.length; k < kk; k++) {
+                    res[i][k] = pa[k];
+                }
+            }
+            switch (r[0]) {
+                case "Z":
+                    x = mx;
+                    y = my;
+                    break;
+                case "H":
+                    x = r[1];
+                    break;
+                case "V":
+                    y = r[1];
+                    break;
+                case "M":
+                    mx = res[i][res[i].length - 2];
+                    my = res[i][res[i].length - 1];
+                default:
+                    x = res[i][res[i].length - 2];
+                    y = res[i][res[i].length - 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;
+    },
+
+    //Returns a path converted to a set of curveto commands
+    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];
+    },
+    
+    //Returns any path command as a curveto command based on the attrs passed
+    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) {
+        // for more information of where this Math came from visit:
+        // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+        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;
+        }
+    },
+    
+    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
+        };
+    },
+
+    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;
+    },
+    
+    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 = [],
+            p,
+            i,
+            ii,
+            xmin,
+            ymin,
+            dim;
+        for (i = 0, ii = path.length; i < ii; 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
+        };
+    },
+    
+    intersect: function(subjectPolygon, clipPolygon) {
+        var cp1, cp2, s, e, point;
+        var inside = function(p) {
+            return (cp2[0]-cp1[0]) * (p[1]-cp1[1]) > (cp2[1]-cp1[1]) * (p[0]-cp1[0]);
+        };
+        var intersection = function() {
+            var p = [];
+            var 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;
+        };
+        var outputList = subjectPolygon;
+        cp1 = clipPolygon[clipPolygon.length -1];
+        for (var i = 0, l = clipPolygon.length; i < l; ++i) {
+            cp2 = clipPolygon[i];
+            var inputList = outputList;
+            outputList = [];
+            s = inputList[inputList.length -1];
+            for (var j = 0, ln = inputList.length; j < ln; j++) {
+                e = inputList[j];
+                if (inside(e)) {
+                    if (!inside(s)) {
+                        outputList.push(intersection());
+                    }
+                    outputList.push(e);
+                } else if (inside(s)) {
+                    outputList.push(intersection());
+                }
+                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;
+    },
+
+    /* Smoothing function for a path.  Converts a path into cubic beziers.  Value defines the divider of the distance between points.
+     * Defaults to a value of 4.
+     */
+    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);
+            }
+        }
+        // Sort by pct property
+        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
+            };
+        }
+    }
+});
+
+/**
+ * @class Ext.fx.PropertyHandler
+ * @ignore
+ */
+Ext.define('Ext.fx.PropertyHandler', {
+
+    /* Begin Definitions */
+
+    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;
+                //Extra check for when the color string is not recognized.
+                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;
+                        //multiple checks to reformat the color if it can't recognized by computeDelta.
+                        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;
+            }
+        }
+        /* End Definitions */
+    }
+}, function() {
+    Ext.each([
+        'outlineColor',
+        'backgroundColor',
+        'borderColor',
+        'borderTopColor',
+        'borderRightColor', 
+        'borderBottomColor', 
+        'borderLeftColor',
+        'fill',
+        'stroke'
+    ], function(prop) {
+        this[prop] = this.color;
+    }, this);
+});
+/**
+ * @class Ext.fx.Anim
+ * 
+ * This class manages animation for a specific {@link #target}. The animation allows
+ * animation of various properties on the target, such as size, position, color and others.
+ * 
+ * ## Starting Conditions
+ * The starting conditions for the animation are provided by the {@link #from} configuration.
+ * Any/all of the properties in the {@link #from} configuration can be specified. If a particular
+ * property is not defined, the starting value for that property will be read directly from the target.
+ * 
+ * ## End Conditions
+ * The ending conditions for the animation are provided by the {@link #to} configuration. These mark
+ * the final values once the animations has finished. The values in the {@link #from} can mirror
+ * those in the {@link #to} configuration to provide a starting point.
+ * 
+ * ## Other Options
+ *  - {@link #duration}: Specifies the time period of the animation.
+ *  - {@link #easing}: Specifies the easing of the animation.
+ *  - {@link #iterations}: Allows the animation to repeat a number of times.
+ *  - {@link #alternate}: Used in conjunction with {@link #iterations}, reverses the direction every second iteration.
+ * 
+ * ## Example Code
+ * 
+ *     var myComponent = Ext.create('Ext.Component', {
+ *         renderTo: document.body,
+ *         width: 200,
+ *         height: 200,
+ *         style: 'border: 1px solid red;'
+ *     });
+ *     
+ *     new Ext.fx.Anim({
+ *         target: myComponent,
+ *         duration: 1000,
+ *         from: {
+ *             width: 400 //starting width 400
+ *         },
+ *         to: {
+ *             width: 300, //end width 300
+ *             height: 300 // end width 300
+ *         }
+ *     });
+ */
+Ext.define('Ext.fx.Anim', {
+
+    /* Begin Definitions */
+
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+
+    requires: ['Ext.fx.Manager', 'Ext.fx.Animator', 'Ext.fx.Easing', 'Ext.fx.CubicBezier', 'Ext.fx.PropertyHandler'],
+
+    /* End Definitions */
+
+    isAnimation: true,
+    /**
+     * @cfg {Number} duration
+     * Time in milliseconds for a single animation to last. Defaults to 250. If the {@link #iterations} property is
+     * specified, then each animate will take the same duration for each iteration.
+     */
+    duration: 250,
+
+    /**
+     * @cfg {Number} delay
+     * Time to delay before starting the animation. Defaults to 0.
+     */
+    delay: 0,
+
+    /* private used to track a delayed starting time */
+    delayStart: 0,
+
+    /**
+     * @cfg {Boolean} dynamic
+     * Currently only for Component Animation: Only set a component's outer element size bypassing layouts.  Set to true to do full layouts for every frame of the animation.  Defaults to false.
+     */
+    dynamic: false,
+
+    /**
+     * @cfg {String} easing
+This describes how the intermediate values used during a transition will be calculated. It allows for a transition to change
+speed over its duration. 
+
+         -backIn
+         -backOut
+         -bounceIn
+         -bounceOut
+         -ease
+         -easeIn
+         -easeOut
+         -easeInOut
+         -elasticIn
+         -elasticOut
+         -cubic-bezier(x1, y1, x2, y2)
+
+Note that cubic-bezier will create a custom easing curve following the CSS3 transition-timing-function specification `{@link http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag}`. The four values specify points P1 and P2 of the curve
+as (x1, y1, x2, y2). All values must be in the range [0, 1] or the definition is invalid.
+     * @markdown
+     */
+    easing: 'ease',
+
+     /**
+      * @cfg {Object} keyframes
+      * Animation keyframes follow the CSS3 Animation configuration pattern. 'from' is always considered '0%' and 'to'
+      * is considered '100%'.<b>Every keyframe declaration must have a keyframe rule for 0% and 100%, possibly defined using
+      * "from" or "to"</b>.  A keyframe declaration without these keyframe selectors is invalid and will not be available for
+      * animation.  The keyframe declaration for a keyframe rule consists of properties and values. Properties that are unable to
+      * be animated are ignored in these rules, with the exception of 'easing' which can be changed at each keyframe. For example:
+ <pre><code>
+keyframes : {
+    '0%': {
+        left: 100
+    },
+    '40%': {
+        left: 150
+    },
+    '60%': {
+        left: 75
+    },
+    '100%': {
+        left: 100
+    }
+}
+ </code></pre>
+      */
+
+    /**
+     * @private
+     */
+    damper: 1,
+
+    /**
+     * @private
+     */
+    bezierRE: /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
+
+    /**
+     * Run the animation from the end to the beginning
+     * Defaults to false.
+     * @cfg {Boolean} reverse
+     */
+    reverse: false,
+
+    /**
+     * Flag to determine if the animation has started
+     * @property running
+     * @type boolean
+     */
+    running: false,
+
+    /**
+     * Flag to determine if the animation is paused. Only set this to true if you need to
+     * keep the Anim instance around to be unpaused later; otherwise call {@link #end}.
+     * @property paused
+     * @type boolean
+     */
+    paused: false,
+
+    /**
+     * Number of times to execute the animation. Defaults to 1.
+     * @cfg {int} iterations
+     */
+    iterations: 1,
+
+    /**
+     * Used in conjunction with iterations to reverse the animation each time an iteration completes.
+     * @cfg {Boolean} alternate
+     * Defaults to false.
+     */
+    alternate: false,
+
+    /**
+     * Current iteration the animation is running.
+     * @property currentIteration
+     * @type int
+     */
+    currentIteration: 0,
+
+    /**
+     * Starting time of the animation.
+     * @property startTime
+     * @type Date
+     */
+    startTime: 0,
+
+    /**
+     * Contains a cache of the interpolators to be used.
+     * @private
+     * @property propHandlers
+     * @type Object
+     */
+
+    /**
+     * @cfg {String/Object} target
+     * The {@link Ext.fx.target.Target} to apply the animation to.  This should only be specified when creating an Ext.fx.Anim directly.
+     * The target does not need to be a {@link Ext.fx.target.Target} instance, it can be the underlying object. For example, you can
+     * pass a Component, Element or Sprite as the target and the Anim will create the appropriate {@link Ext.fx.target.Target} object
+     * automatically.
+     */
+
+    /**
+     * @cfg {Object} from
+     * An object containing property/value pairs for the beginning of the animation.  If not specified, the current state of the
+     * Ext.fx.target will be used. For example:
+<pre><code>
+from : {
+    opacity: 0,       // Transparent
+    color: '#ffffff', // White
+    left: 0
+}
+</code></pre>
+     */
+
+    /**
+     * @cfg {Object} to
+     * An object containing property/value pairs for the end of the animation. For example:
+ <pre><code>
+ to : {
+     opacity: 1,       // Opaque
+     color: '#00ff00', // Green
+     left: 500
+ }
+ </code></pre>
+     */
+
+    // @private
+    constructor: function(config) {
+        var me = this;
+        config = config || {};
+        // If keyframes are passed, they really want an Animator instead.
+        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 not a pre-defined curve, try a cubic-bezier
+        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(
+            /**
+             * @event beforeanimate
+             * Fires before the animation starts. A handler can return false to cancel the animation.
+             * @param {Ext.fx.Anim} this
+             */
+            'beforeanimate',
+             /**
+              * @event afteranimate
+              * Fires when the animation is complete.
+              * @param {Ext.fx.Anim} this
+              * @param {Date} startTime
+              */
+            'afteranimate',
+             /**
+              * @event lastframe
+              * Fires when the animation's last frame has been set.
+              * @param {Ext.fx.Anim} this
+              * @param {Date} startTime
+              */
+            'lastframe'
+        );
+        me.mixins.observable.constructor.call(me, config);
+        if (config.callback) {
+            me.on('afteranimate', config.callback, config.scope);
+        }
+        return me;
+    },
+
+    /**
+     * @private
+     * Helper to the target
+     */
+    setAttr: function(attr, value) {
+        return Ext.fx.Manager.items.get(this.id).setAttr(this.target, attr, value);
+    },
+
+    /*
+     * @private
+     * Set up the initial currentAttrs hash.
+     */
+    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];
+                // Use default (numeric) property handler
+                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;
+                    }
+                }
+                // Use custom handler
+                else {
+                    propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler[attr];
+                }
+                out[attr] = propHandler.get(start, end, me.damper, initialFrom[attr], attr);
+            }
+        }
+        me.currentAttrs = out;
+    },
+
+    /*
+     * @private
+     * Fires beforeanimate and sets the running flag.
+     */
+    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 {
+                    // Compensate for frame delay;
+                    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;
+        }
+    },
+
+    /*
+     * @private
+     * Calculate attribute value at the passed timestamp.
+     * @returns a hash of the new attributes.
+     */
+    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;
+    },
+
+    /*
+     * @private
+     * Perform lastFrame cleanup and handle iterations
+     * @returns a hash of the new attributes.
+     */
+    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;
+            // Turn off paused for CSS3 Transitions
+            me.paused = false;
+        }
+        else {
+            me.currentIteration = 0;
+            me.end();
+            me.fireEvent('lastframe', me, me.startTime);
+        }
+    },
+
+    /*
+     * Fire afteranimate event and end the animation. Usually called automatically when the
+     * animation reaches its final frame, but can also be called manually to pre-emptively
+     * stop and destroy the running animation.
+     */
+    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);
+    }
+});
+// Set flag to indicate that Fx is available. Class might not be available immediately.
+Ext.enableFx = true;
+
+/*
+ * This is a derivative of the similarly named class in the YUI Library.
+ * The original license:
+ * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+
+/**
+ * @class Ext.dd.DragDrop
+ * Defines the interface and base operation of items that that can be
+ * dragged or can be drop targets.  It was designed to be extended, overriding
+ * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
+ * Up to three html elements can be associated with a DragDrop instance:
+ * <ul>
+ * <li>linked element: the element that is passed into the constructor.
+ * This is the element which defines the boundaries for interaction with
+ * other DragDrop objects.</li>
+ * <li>handle element(s): The drag operation only occurs if the element that
+ * was clicked matches a handle element.  By default this is the linked
+ * element, but there are times that you will want only a portion of the
+ * linked element to initiate the drag operation, and the setHandleElId()
+ * method provides a way to define this.</li>
+ * <li>drag element: this represents the element that would be moved along
+ * with the cursor during a drag operation.  By default, this is the linked
+ * element itself as in {@link Ext.dd.DD}.  setDragElId() lets you define
+ * a separate element that would be moved, as in {@link Ext.dd.DDProxy}.
+ * </li>
+ * </ul>
+ * This class should not be instantiated until the onload event to ensure that
+ * the associated elements are available.
+ * The following would define a DragDrop obj that would interact with any
+ * other DragDrop obj in the "group1" group:
+ * <pre>
+ *  dd = new Ext.dd.DragDrop("div1", "group1");
+ * </pre>
+ * Since none of the event handlers have been implemented, nothing would
+ * actually happen if you were to run the code above.  Normally you would
+ * override this class or one of the default implementations, but you can
+ * also override the methods you want on an instance of the class...
+ * <pre>
+ *  dd.onDragDrop = function(e, id) {
+ *  &nbsp;&nbsp;alert("dd was dropped on " + id);
+ *  }
+ * </pre>
+ * @constructor
+ * @param {String} id of the element that is linked to this instance
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DragDrop:
+ *                    padding, isTarget, maintainOffset, primaryButtonOnly
+ */
+
+Ext.define('Ext.dd.DragDrop', {
+    requires: ['Ext.dd.DragDropManager'],
+    constructor: function(id, sGroup, config) {
+        if(id) {
+            this.init(id, sGroup, config);
+        }
+    },
+    
+    /**
+     * Set to false to enable a DragDrop object to fire drag events while dragging
+     * over its own Element. Defaults to true - DragDrop objects do not by default
+     * fire drag events to themselves.
+     * @property ignoreSelf
+     * @type Boolean
+     */
+
+    /**
+     * The id of the element associated with this object.  This is what we
+     * refer to as the "linked element" because the size and position of
+     * this element is used to determine when the drag and drop objects have
+     * interacted.
+     * @property id
+     * @type String
+     */
+    id: null,
+
+    /**
+     * Configuration attributes passed into the constructor
+     * @property config
+     * @type object
+     */
+    config: null,
+
+    /**
+     * The id of the element that will be dragged.  By default this is same
+     * as the linked element, but could be changed to another element. Ex:
+     * Ext.dd.DDProxy
+     * @property dragElId
+     * @type String
+     * @private
+     */
+    dragElId: null,
+
+    /**
+     * The ID of the element that initiates the drag operation.  By default
+     * this is the linked element, but could be changed to be a child of this
+     * element.  This lets us do things like only starting the drag when the
+     * header element within the linked html element is clicked.
+     * @property handleElId
+     * @type String
+     * @private
+     */
+    handleElId: null,
+
+    /**
+     * An object who's property names identify HTML tags to be considered invalid as drag handles.
+     * A non-null property value identifies the tag as invalid. Defaults to the 
+     * following value which prevents drag operations from being initiated by &lt;a> elements:<pre><code>
+{
+    A: "A"
+}</code></pre>
+     * @property invalidHandleTypes
+     * @type Object
+     */
+    invalidHandleTypes: null,
+
+    /**
+     * An object who's property names identify the IDs of elements to be considered invalid as drag handles.
+     * A non-null property value identifies the ID as invalid. For example, to prevent
+     * dragging from being initiated on element ID "foo", use:<pre><code>
+{
+    foo: true
+}</code></pre>
+     * @property invalidHandleIds
+     * @type Object
+     */
+    invalidHandleIds: null,
+
+    /**
+     * An Array of CSS class names for elements to be considered in valid as drag handles.
+     * @property invalidHandleClasses
+     * @type Array
+     */
+    invalidHandleClasses: null,
+
+    /**
+     * The linked element's absolute X position at the time the drag was
+     * started
+     * @property startPageX
+     * @type int
+     * @private
+     */
+    startPageX: 0,
+
+    /**
+     * The linked element's absolute X position at the time the drag was
+     * started
+     * @property startPageY
+     * @type int
+     * @private
+     */
+    startPageY: 0,
+
+    /**
+     * The group defines a logical collection of DragDrop objects that are
+     * related.  Instances only get events when interacting with other
+     * DragDrop object in the same group.  This lets us define multiple
+     * groups using a single DragDrop subclass if we want.
+     * @property groups
+     * @type object An object in the format {'group1':true, 'group2':true}
+     */
+    groups: null,
+
+    /**
+     * Individual drag/drop instances can be locked.  This will prevent
+     * onmousedown start drag.
+     * @property locked
+     * @type boolean
+     * @private
+     */
+    locked: false,
+
+    /**
+     * Lock this instance
+     * @method lock
+     */
+    lock: function() {
+        this.locked = true;
+    },
+
+    /**
+     * When set to true, other DD objects in cooperating DDGroups do not receive
+     * notification events when this DD object is dragged over them. Defaults to false.
+     * @property moveOnly
+     * @type boolean
+     */
+    moveOnly: false,
+
+    /**
+     * Unlock this instace
+     * @method unlock
+     */
+    unlock: function() {
+        this.locked = false;
+    },
+
+    /**
+     * By default, all instances can be a drop target.  This can be disabled by
+     * setting isTarget to false.
+     * @property isTarget
+     * @type boolean
+     */
+    isTarget: true,
+
+    /**
+     * The padding configured for this drag and drop object for calculating
+     * the drop zone intersection with this object.
+     * @property padding
+     * @type int[] An array containing the 4 padding values: [top, right, bottom, left]
+     */
+    padding: null,
+
+    /**
+     * Cached reference to the linked element
+     * @property _domRef
+     * @private
+     */
+    _domRef: null,
+
+    /**
+     * Internal typeof flag
+     * @property __ygDragDrop
+     * @private
+     */
+    __ygDragDrop: true,
+
+    /**
+     * Set to true when horizontal contraints are applied
+     * @property constrainX
+     * @type boolean
+     * @private
+     */
+    constrainX: false,
+
+    /**
+     * Set to true when vertical contraints are applied
+     * @property constrainY
+     * @type boolean
+     * @private
+     */
+    constrainY: false,
+
+    /**
+     * The left constraint
+     * @property minX
+     * @type int
+     * @private
+     */
+    minX: 0,
+
+    /**
+     * The right constraint
+     * @property maxX
+     * @type int
+     * @private
+     */
+    maxX: 0,
+
+    /**
+     * The up constraint
+     * @property minY
+     * @type int
+     * @private
+     */
+    minY: 0,
+
+    /**
+     * The down constraint
+     * @property maxY
+     * @type int
+     * @private
+     */
+    maxY: 0,
+
+    /**
+     * Maintain offsets when we resetconstraints.  Set to true when you want
+     * the position of the element relative to its parent to stay the same
+     * when the page changes
+     *
+     * @property maintainOffset
+     * @type boolean
+     */
+    maintainOffset: false,
+
+    /**
+     * Array of pixel locations the element will snap to if we specified a
+     * horizontal graduation/interval.  This array is generated automatically
+     * when you define a tick interval.
+     * @property xTicks
+     * @type int[]
+     */
+    xTicks: null,
+
+    /**
+     * Array of pixel locations the element will snap to if we specified a
+     * vertical graduation/interval.  This array is generated automatically
+     * when you define a tick interval.
+     * @property yTicks
+     * @type int[]
+     */
+    yTicks: null,
+
+    /**
+     * By default the drag and drop instance will only respond to the primary
+     * button click (left button for a right-handed mouse).  Set to true to
+     * allow drag and drop to start with any mouse click that is propogated
+     * by the browser
+     * @property primaryButtonOnly
+     * @type boolean
+     */
+    primaryButtonOnly: true,
+
+    /**
+     * The available property is false until the linked dom element is accessible.
+     * @property available
+     * @type boolean
+     */
+    available: false,
+
+    /**
+     * By default, drags can only be initiated if the mousedown occurs in the
+     * region the linked element is.  This is done in part to work around a
+     * bug in some browsers that mis-report the mousedown if the previous
+     * mouseup happened outside of the window.  This property is set to true
+     * if outer handles are defined.
+     *
+     * @property hasOuterHandles
+     * @type boolean
+     * @default false
+     */
+    hasOuterHandles: false,
+
+    /**
+     * Code that executes immediately before the startDrag event
+     * @method b4StartDrag
+     * @private
+     */
+    b4StartDrag: function(x, y) { },
+
+    /**
+     * Abstract method called after a drag/drop object is clicked
+     * and the drag or mousedown time thresholds have beeen met.
+     * @method startDrag
+     * @param {int} X click location
+     * @param {int} Y click location
+     */
+    startDrag: function(x, y) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDrag event
+     * @method b4Drag
+     * @private
+     */
+    b4Drag: function(e) { },
+
+    /**
+     * Abstract method called during the onMouseMove event while dragging an
+     * object.
+     * @method onDrag
+     * @param {Event} e the mousemove event
+     */
+    onDrag: function(e) { /* override this */ },
+
+    /**
+     * Abstract method called when this element fist begins hovering over
+     * another DragDrop obj
+     * @method onDragEnter
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this is hovering over.  In INTERSECT mode, an array of one or more
+     * dragdrop items being hovered over.
+     */
+    onDragEnter: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragOver event
+     * @method b4DragOver
+     * @private
+     */
+    b4DragOver: function(e) { },
+
+    /**
+     * Abstract method called when this element is hovering over another
+     * DragDrop obj
+     * @method onDragOver
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this is hovering over.  In INTERSECT mode, an array of dd items
+     * being hovered over.
+     */
+    onDragOver: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragOut event
+     * @method b4DragOut
+     * @private
+     */
+    b4DragOut: function(e) { },
+
+    /**
+     * Abstract method called when we are no longer hovering over an element
+     * @method onDragOut
+     * @param {Event} e the mousemove event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this was hovering over.  In INTERSECT mode, an array of dd items
+     * that the mouse is no longer over.
+     */
+    onDragOut: function(e, id) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the onDragDrop event
+     * @method b4DragDrop
+     * @private
+     */
+    b4DragDrop: function(e) { },
+
+    /**
+     * Abstract method called when this item is dropped on another DragDrop
+     * obj
+     * @method onDragDrop
+     * @param {Event} e the mouseup event
+     * @param {String|DragDrop[]} id In POINT mode, the element
+     * id this was dropped on.  In INTERSECT mode, an array of dd items this
+     * was dropped on.
+     */
+    onDragDrop: function(e, id) { /* override this */ },
+
+    /**
+     * Abstract method called when this item is dropped on an area with no
+     * drop target
+     * @method onInvalidDrop
+     * @param {Event} e the mouseup event
+     */
+    onInvalidDrop: function(e) { /* override this */ },
+
+    /**
+     * Code that executes immediately before the endDrag event
+     * @method b4EndDrag
+     * @private
+     */
+    b4EndDrag: function(e) { },
+
+    /**
+     * Fired when we are done dragging the object
+     * @method endDrag
+     * @param {Event} e the mouseup event
+     */
+    endDrag: function(e) { /* override this */ },
+
+    /**
+     * Code executed immediately before the onMouseDown event
+     * @method b4MouseDown
+     * @param {Event} e the mousedown event
+     * @private
+     */
+    b4MouseDown: function(e) {  },
+
+    /**
+     * Event handler that fires when a drag/drop obj gets a mousedown
+     * @method onMouseDown
+     * @param {Event} e the mousedown event
+     */
+    onMouseDown: function(e) { /* override this */ },
+
+    /**
+     * Event handler that fires when a drag/drop obj gets a mouseup
+     * @method onMouseUp
+     * @param {Event} e the mouseup event
+     */
+    onMouseUp: function(e) { /* override this */ },
+
+    /**
+     * Override the onAvailable method to do what is needed after the initial
+     * position was determined.
+     * @method onAvailable
+     */
+    onAvailable: function () {
+    },
+
+    /**
+     * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
+     * @type Object
+     */
+    defaultPadding: {
+        left: 0,
+        right: 0,
+        top: 0,
+        bottom: 0
+    },
+
+    /**
+     * Initializes the drag drop object's constraints to restrict movement to a certain element.
+ *
+ * Usage:
+ <pre><code>
+ var dd = new Ext.dd.DDProxy("dragDiv1", "proxytest",
+                { dragElId: "existingProxyDiv" });
+ dd.startDrag = function(){
+     this.constrainTo("parent-id");
+ };
+ </code></pre>
+ * Or you can initalize it using the {@link Ext.core.Element} object:
+ <pre><code>
+ Ext.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
+     startDrag : function(){
+         this.constrainTo("parent-id");
+     }
+ });
+ </code></pre>
+     * @param {Mixed} constrainTo The element to constrain to.
+     * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
+     * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
+     * an object containing the sides to pad. For example: {right:10, bottom:10}
+     * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
+     */
+    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), // left
+                c.width - leftSpace - b.width - (pad.right||0), //right
+                               this.xTickSize
+        );
+        this.setYConstraint(topSpace - (pad.top||0), //top
+                c.height - topSpace - b.height - (pad.bottom||0), //bottom
+                               this.yTickSize
+        );
+    },
+
+    /**
+     * Returns a reference to the linked element
+     * @method getEl
+     * @return {HTMLElement} the html element
+     */
+    getEl: function() {
+        if (!this._domRef) {
+            this._domRef = Ext.getDom(this.id);
+        }
+
+        return this._domRef;
+    },
+
+    /**
+     * Returns a reference to the actual element to drag.  By default this is
+     * the same as the html element, but it can be assigned to another
+     * element. An example of this can be found in Ext.dd.DDProxy
+     * @method getDragEl
+     * @return {HTMLElement} the html element
+     */
+    getDragEl: function() {
+        return Ext.getDom(this.dragElId);
+    },
+
+    /**
+     * Sets up the DragDrop object.  Must be called in the constructor of any
+     * Ext.dd.DragDrop subclass
+     * @method init
+     * @param id the id of the linked element
+     * @param {String} sGroup the group of related items
+     * @param {object} config configuration attributes
+     */
+    init: function(id, sGroup, config) {
+        this.initTarget(id, sGroup, config);
+        Ext.EventManager.on(this.id, "mousedown", this.handleMouseDown, this);
+        // Ext.EventManager.on(this.id, "selectstart", Event.preventDefault);
+    },
+
+    /**
+     * Initializes Targeting functionality only... the object does not
+     * get a mousedown handler.
+     * @method initTarget
+     * @param id the id of the linked element
+     * @param {String} sGroup the group of related items
+     * @param {object} config configuration attributes
+     */
+    initTarget: function(id, sGroup, config) {
+
+        // configuration attributes
+        this.config = config || {};
+
+        // create a local reference to the drag and drop manager
+        this.DDMInstance = Ext.dd.DragDropManager;
+        // initialize the groups array
+        this.groups = {};
+
+        // assume that we have an element reference instead of an id if the
+        // parameter is not a string
+        if (typeof id !== "string") {
+            id = Ext.id(id);
+        }
+
+        // set the id
+        this.id = id;
+
+        // add to an interaction group
+        this.addToGroup((sGroup) ? sGroup : "default");
+
+        // We don't want to register this as the handle with the manager
+        // so we just set the id rather than calling the setter.
+        this.handleElId = id;
+
+        // the linked element is the element that gets dragged by default
+        this.setDragElId(id);
+
+        // by default, clicked anchors will not start drag operations.
+        this.invalidHandleTypes = { A: "A" };
+        this.invalidHandleIds = {};
+        this.invalidHandleClasses = [];
+
+        this.applyConfig();
+
+        this.handleOnAvailable();
+    },
+
+    /**
+     * Applies the configuration parameters that were passed into the constructor.
+     * This is supposed to happen at each level through the inheritance chain.  So
+     * a DDProxy implentation will execute apply config on DDProxy, DD, and
+     * DragDrop in order to get all of the parameters that are available in
+     * each object.
+     * @method applyConfig
+     */
+    applyConfig: function() {
+
+        // configurable properties:
+        //    padding, isTarget, maintainOffset, primaryButtonOnly
+        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);
+
+    },
+
+    /**
+     * Executed when the linked element is available
+     * @method handleOnAvailable
+     * @private
+     */
+    handleOnAvailable: function() {
+        this.available = true;
+        this.resetConstraints();
+        this.onAvailable();
+    },
+
+     /**
+     * Configures the padding for the target zone in px.  Effectively expands
+     * (or reduces) the virtual object size for targeting calculations.
+     * Supports css-style shorthand; if only one parameter is passed, all sides
+     * will have that padding, and if only two are passed, the top and bottom
+     * will have the first param, the left and right the second.
+     * @method setPadding
+     * @param {int} iTop    Top pad
+     * @param {int} iRight  Right pad
+     * @param {int} iBot    Bot pad
+     * @param {int} iLeft   Left pad
+     */
+    setPadding: function(iTop, iRight, iBot, iLeft) {
+        // this.padding = [iLeft, iRight, iTop, iBot];
+        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];
+        }
+    },
+
+    /**
+     * Stores the initial placement of the linked element.
+     * @method setInitPosition
+     * @param {int} diffX   the X offset, default 0
+     * @param {int} diffY   the Y offset, default 0
+     */
+    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);
+    },
+
+    /**
+     * Sets the start position of the element.  This is set when the obj
+     * is initialized, the reset when a drag is started.
+     * @method setStartPosition
+     * @param pos current position (from previous lookup)
+     * @private
+     */
+    setStartPosition: function(pos) {
+        var p = pos || Ext.core.Element.getXY( this.getEl() );
+        this.deltaSetXY = null;
+
+        this.startPageX = p[0];
+        this.startPageY = p[1];
+    },
+
+    /**
+     * Add this instance to a group of related drag/drop objects.  All
+     * instances belong to at least one group, and can belong to as many
+     * groups as needed.
+     * @method addToGroup
+     * @param sGroup {string} the name of the group
+     */
+    addToGroup: function(sGroup) {
+        this.groups[sGroup] = true;
+        this.DDMInstance.regDragDrop(this, sGroup);
+    },
+
+    /**
+     * Remove's this instance from the supplied interaction group
+     * @method removeFromGroup
+     * @param {string}  sGroup  The group to drop
+     */
+    removeFromGroup: function(sGroup) {
+        if (this.groups[sGroup]) {
+            delete this.groups[sGroup];
+        }
+
+        this.DDMInstance.removeDDFromGroup(this, sGroup);
+    },
+
+    /**
+     * Allows you to specify that an element other than the linked element
+     * will be moved with the cursor during a drag
+     * @method setDragElId
+     * @param id {string} the id of the element that will be used to initiate the drag
+     */
+    setDragElId: function(id) {
+        this.dragElId = id;
+    },
+
+    /**
+     * Allows you to specify a child of the linked element that should be
+     * used to initiate the drag operation.  An example of this would be if
+     * you have a content div with text and links.  Clicking anywhere in the
+     * content area would normally start the drag operation.  Use this method
+     * to specify that an element inside of the content div is the element
+     * that starts the drag operation.
+     * @method setHandleElId
+     * @param id {string} the id of the element that will be used to
+     * initiate the drag.
+     */
+    setHandleElId: function(id) {
+        if (typeof id !== "string") {
+            id = Ext.id(id);
+        }
+        this.handleElId = id;
+        this.DDMInstance.regHandle(this.id, id);
+    },
+
+    /**
+     * Allows you to set an element outside of the linked element as a drag
+     * handle
+     * @method setOuterHandleElId
+     * @param id the id of the element that will be used to initiate the drag
+     */
+    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;
+    },
+
+    /**
+     * Remove all drag and drop hooks for this element
+     * @method unreg
+     */
+    unreg: function() {
+        Ext.EventManager.un(this.id, "mousedown", this.handleMouseDown, this);
+        this._domRef = null;
+        this.DDMInstance._remove(this);
+    },
+
+    destroy : function(){
+        this.unreg();
+    },
+
+    /**
+     * Returns true if this instance is locked, or the drag drop mgr is locked
+     * (meaning that all drag/drop is disabled on the page.)
+     * @method isLocked
+     * @return {boolean} true if this obj or all drag/drop is locked, else
+     * false
+     */
+    isLocked: function() {
+        return (this.DDMInstance.isLocked() || this.locked);
+    },
+
+    /**
+     * Fired when this object is clicked
+     * @method handleMouseDown
+     * @param {Event} e
+     * @param {Ext.dd.DragDrop} oDD the clicked dd object (this dd obj)
+     * @private
+     */
+    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)) {
+                // set the initial element position
+                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)) );
+    },
+
+    /**
+     * Allows you to specify a tag name that should not start a drag operation
+     * when clicked.  This is designed to facilitate embedding links within a
+     * drag handle that do something other than start the drag.
+     * @method addInvalidHandleType
+     * @param {string} tagName the type of element to exclude
+     */
+    addInvalidHandleType: function(tagName) {
+        var type = tagName.toUpperCase();
+        this.invalidHandleTypes[type] = type;
+    },
+
+    /**
+     * Lets you to specify an element id for a child of a drag handle
+     * that should not initiate a drag
+     * @method addInvalidHandleId
+     * @param {string} id the element id of the element you wish to ignore
+     */
+    addInvalidHandleId: function(id) {
+        if (typeof id !== "string") {
+            id = Ext.id(id);
+        }
+        this.invalidHandleIds[id] = id;
+    },
+
+    /**
+     * Lets you specify a css class of elements that will not initiate a drag
+     * @method addInvalidHandleClass
+     * @param {string} cssClass the class of the elements you wish to ignore
+     */
+    addInvalidHandleClass: function(cssClass) {
+        this.invalidHandleClasses.push(cssClass);
+    },
+
+    /**
+     * Unsets an excluded tag name set by addInvalidHandleType
+     * @method removeInvalidHandleType
+     * @param {string} tagName the type of element to unexclude
+     */
+    removeInvalidHandleType: function(tagName) {
+        var type = tagName.toUpperCase();
+        // this.invalidHandleTypes[type] = null;
+        delete this.invalidHandleTypes[type];
+    },
+
+    /**
+     * Unsets an invalid handle id
+     * @method removeInvalidHandleId
+     * @param {string} id the id of the element to re-enable
+     */
+    removeInvalidHandleId: function(id) {
+        if (typeof id !== "string") {
+            id = Ext.id(id);
+        }
+        delete this.invalidHandleIds[id];
+    },
+
+    /**
+     * Unsets an invalid css class
+     * @method removeInvalidHandleClass
+     * @param {string} cssClass the class of the element(s) you wish to
+     * re-enable
+     */
+    removeInvalidHandleClass: function(cssClass) {
+        for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
+            if (this.invalidHandleClasses[i] == cssClass) {
+                delete this.invalidHandleClasses[i];
+            }
+        }
+    },
+
+    /**
+     * Checks the tag exclusion list to see if this click should be ignored
+     * @method isValidHandleChild
+     * @param {HTMLElement} node the HTMLElement to evaluate
+     * @return {boolean} true if this is a valid tag type, false if not
+     */
+    isValidHandleChild: function(node) {
+
+        var valid = true;
+        // var n = (node.nodeName == "#text") ? node.parentNode : node;
+        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;
+
+    },
+
+    /**
+     * Create the array of horizontal tick marks if an interval was specified
+     * in setXConstraint().
+     * @method setXTicks
+     * @private
+     */
+    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);
+    },
+
+    /**
+     * Create the array of vertical tick marks if an interval was specified in
+     * setYConstraint().
+     * @method setYTicks
+     * @private
+     */
+    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);
+    },
+
+    /**
+     * By default, the element can be dragged any place on the screen.  Use
+     * this method to limit the horizontal travel of the element.  Pass in
+     * 0,0 for the parameters if you want to lock the drag to the y axis.
+     * @method setXConstraint
+     * @param {int} iLeft the number of pixels the element can move to the left
+     * @param {int} iRight the number of pixels the element can move to the
+     * right
+     * @param {int} iTickSize optional parameter for specifying that the
+     * element
+     * should move iTickSize pixels at a time.
+     */
+    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;
+    },
+
+    /**
+     * Clears any constraints applied to this instance.  Also clears ticks
+     * since they can't exist independent of a constraint at this time.
+     * @method clearConstraints
+     */
+    clearConstraints: function() {
+        this.constrainX = false;
+        this.constrainY = false;
+        this.clearTicks();
+    },
+
+    /**
+     * Clears any tick interval defined for this instance
+     * @method clearTicks
+     */
+    clearTicks: function() {
+        this.xTicks = null;
+        this.yTicks = null;
+        this.xTickSize = 0;
+        this.yTickSize = 0;
+    },
+
+    /**
+     * By default, the element can be dragged any place on the screen.  Set
+     * this to limit the vertical travel of the element.  Pass in 0,0 for the
+     * parameters if you want to lock the drag to the x axis.
+     * @method setYConstraint
+     * @param {int} iUp the number of pixels the element can move up
+     * @param {int} iDown the number of pixels the element can move down
+     * @param {int} iTickSize optional parameter for specifying that the
+     * element should move iTickSize pixels at a time.
+     */
+    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 must be called if you manually reposition a dd element.
+     * @method resetConstraints
+     * @param {boolean} maintainOffset
+     */
+    resetConstraints: function() {
+        // Maintain offsets if necessary
+        if (this.initPageX || this.initPageX === 0) {
+            // figure out how much this thing has moved
+            var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
+            var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
+
+            this.setInitPosition(dx, dy);
+
+        // This is the first time we have detected the element's position
+        } 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         );
+        }
+    },
+
+    /**
+     * Normally the drag element is moved pixel by pixel, but we can specify
+     * that it move a number of pixels at a time.  This method resolves the
+     * location when we have it set up like this.
+     * @method getTick
+     * @param {int} val where we want to place the object
+     * @param {int[]} tickArray sorted array of valid points
+     * @return {int} the closest tick
+     * @private
+     */
+    getTick: function(val, tickArray) {
+        if (!tickArray) {
+            // If tick interval is not defined, it is effectively 1 pixel,
+            // so we return the value passed to us.
+            return val;
+        } else if (tickArray[0] >= val) {
+            // The value is lower than the first tick, so we return the first
+            // tick.
+            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];
+                }
+            }
+
+            // The value is larger than the last tick, so we return the last
+            // tick.
+            return tickArray[tickArray.length - 1];
+        }
+    },
+
+    /**
+     * toString method
+     * @method toString
+     * @return {string} string representation of the dd obj
+     */
+    toString: function() {
+        return ("DragDrop " + this.id);
+    }
+
+});
+/*
+ * This is a derivative of the similarly named class in the YUI Library.
+ * The original license:
+ * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+
+/**
+ * @class Ext.dd.DD
+ * A DragDrop implementation where the linked element follows the
+ * mouse cursor during a drag.
+ * @extends Ext.dd.DragDrop
+ * @constructor
+ * @param {String} id the id of the linked element
+ * @param {String} sGroup the group of related DragDrop items
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DD:
+ *                    scroll
+ */
+
+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);
+        }
+    },
+
+    /**
+     * When set to true, the utility automatically tries to scroll the browser
+     * window when a drag and drop element is dragged near the viewport boundary.
+     * Defaults to true.
+     * @property scroll
+     * @type boolean
+     */
+    scroll: true,
+
+    /**
+     * Sets the pointer offset to the distance between the linked element's top
+     * left corner and the location the element was clicked
+     * @method autoOffset
+     * @param {int} iPageX the X coordinate of the click
+     * @param {int} iPageY the Y coordinate of the click
+     */
+    autoOffset: function(iPageX, iPageY) {
+        var x = iPageX - this.startPageX;
+        var y = iPageY - this.startPageY;
+        this.setDelta(x, y);
+    },
+
+    /**
+     * Sets the pointer offset.  You can call this directly to force the
+     * offset to be in a particular location (e.g., pass in 0,0 to set it
+     * to the center of the object)
+     * @method setDelta
+     * @param {int} iDeltaX the distance from the left
+     * @param {int} iDeltaY the distance from the top
+     */
+    setDelta: function(iDeltaX, iDeltaY) {
+        this.deltaX = iDeltaX;
+        this.deltaY = iDeltaY;
+    },
+
+    /**
+     * Sets the drag element to the location of the mousedown or click event,
+     * maintaining the cursor location relative to the location on the element
+     * that was clicked.  Override this if you want to place the element in a
+     * location other than where the cursor is.
+     * @method setDragElPos
+     * @param {int} iPageX the X coordinate of the mousedown or drag event
+     * @param {int} iPageY the Y coordinate of the mousedown or drag event
+     */
+    setDragElPos: function(iPageX, iPageY) {
+        // the first time we do this, we are going to check to make sure
+        // the element has css positioning
+
+        var el = this.getDragEl();
+        this.alignElWithMouse(el, iPageX, iPageY);
+    },
+
+    /**
+     * Sets the element to the location of the mousedown or click event,
+     * maintaining the cursor location relative to the location on the element
+     * that was clicked.  Override this if you want to place the element in a
+     * location other than where the cursor is.
+     * @method alignElWithMouse
+     * @param {HTMLElement} el the element to move
+     * @param {int} iPageX the X coordinate of the mousedown or drag event
+     * @param {int} iPageY the Y coordinate of the mousedown or drag event
+     */
+    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;
+    },
+
+    /**
+     * Saves the most recent position so that we can reset the constraints and
+     * tick marks on-demand.  We need to know this so that we can calculate the
+     * number of pixels the element is offset from its original position.
+     * @method cachePosition
+     * @param iPageX the current x position (optional, this just makes it so we
+     * don't have to look it up again)
+     * @param iPageY the current y position (optional, this just makes it so we
+     * don't have to look it up again)
+     */
+    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];
+        }
+    },
+
+    /**
+     * Auto-scroll the window if the dragged object has been moved beyond the
+     * visible window boundary.
+     * @method autoScroll
+     * @param {int} x the drag element's x position
+     * @param {int} y the drag element's y position
+     * @param {int} h the height of the drag element
+     * @param {int} w the width of the drag element
+     * @private
+     */
+    autoScroll: function(x, y, h, w) {
+
+        if (this.scroll) {
+            // The client height
+            var clientH = Ext.core.Element.getViewHeight();
+
+            // The client width
+            var clientW = Ext.core.Element.getViewWidth();
+
+            // The amt scrolled down
+            var st = this.DDMInstance.getScrollTop();
+
+            // The amt scrolled right
+            var sl = this.DDMInstance.getScrollLeft();
+
+            // Location of the bottom of the element
+            var bot = h + y;
+
+            // Location of the right of the element
+            var right = w + x;
+
+            // The distance from the cursor to the bottom of the visible area,
+            // adjusted so that we don't scroll if the cursor is beyond the
+            // element drag constraints
+            var toBot = (clientH + st - y - this.deltaY);
+
+            // The distance from the cursor to the right of the visible area
+            var toRight = (clientW + sl - x - this.deltaX);
+
+
+            // How close to the edge the cursor must be before we scroll
+            // var thresh = (document.all) ? 100 : 40;
+            var thresh = 40;
+
+            // How many pixels to scroll per autoscroll op.  This helps to reduce
+            // clunky scrolling. IE is more sensitive about this ... it needs this
+            // value to be higher.
+            var scrAmt = (document.all) ? 80 : 30;
+
+            // Scroll down if we are near the bottom of the visible page and the
+            // obj extends below the crease
+            if ( bot > clientH && toBot < thresh ) {
+                window.scrollTo(sl, st + scrAmt);
+            }
+
+            // Scroll up if the window is scrolled down and the top of the object
+            // goes above the top border
+            if ( y < st && st > 0 && y - st < thresh ) {
+                window.scrollTo(sl, st - scrAmt);
+            }
+
+            // Scroll right if the obj is beyond the right border and the cursor is
+            // near the border.
+            if ( right > clientW && toRight < thresh ) {
+                window.scrollTo(sl + scrAmt, st);
+            }
+
+            // Scroll left if the window has been scrolled to the right and the obj
+            // extends past the left border
+            if ( x < sl && sl > 0 && x - sl < thresh ) {
+                window.scrollTo(sl - scrAmt, st);
+            }
+        }
+    },
+
+    /**
+     * Finds the location the element should be placed if we want to move
+     * it to where the mouse location less the click offset would place us.
+     * @method getTargetCoord
+     * @param {int} iPageX the X coordinate of the click
+     * @param {int} iPageY the Y coordinate of the click
+     * @return an object that contains the coordinates (Object.x and Object.y)
+     * @private
+     */
+    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};
+    },
+
+    /**
+     * Sets up config options specific to this class. Overrides
+     * Ext.dd.DragDrop, but all versions of this method through the
+     * inheritance chain are called
+     */
+    applyConfig: function() {
+        this.callParent();
+        this.scroll = (this.config.scroll !== false);
+    },
+
+    /**
+     * Event that fires prior to the onMouseDown event.  Overrides
+     * Ext.dd.DragDrop.
+     */
+    b4MouseDown: function(e) {
+        // this.resetConstraints();
+        this.autoOffset(e.getPageX(), e.getPageY());
+    },
+
+    /**
+     * Event that fires prior to the onDrag event.  Overrides
+     * Ext.dd.DragDrop.
+     */
+    b4Drag: function(e) {
+        this.setDragElPos(e.getPageX(), e.getPageY());
+    },
+
+    toString: function() {
+        return ("DD " + this.id);
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+    // Debugging ygDragDrop events that can be overridden
+    //////////////////////////////////////////////////////////////////////////
+    /*
+    startDrag: function(x, y) {
+    },
+
+    onDrag: function(e) {
+    },
+
+    onDragEnter: function(e, id) {
+    },
+
+    onDragOver: function(e, id) {
+    },
+
+    onDragOut: function(e, id) {
+    },
+
+    onDragDrop: function(e, id) {
+    },
+
+    endDrag: function(e) {
+    }
+
+    */
+
+});
+
+/*
+ * This is a derivative of the similarly named class in the YUI Library.
+ * The original license:
+ * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+/**
+ * @class Ext.dd.DDProxy
+ * A DragDrop implementation that inserts an empty, bordered div into
+ * the document that follows the cursor during drag operations.  At the time of
+ * the click, the frame div is resized to the dimensions of the linked html
+ * element, and moved to the exact location of the linked element.
+ *
+ * References to the "frame" element refer to the single proxy element that
+ * was created to be dragged in place of all DDProxy elements on the
+ * page.
+ *
+ * @extends Ext.dd.DD
+ * @constructor
+ * @param {String} id the id of the linked html element
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                Valid properties for DDProxy in addition to those in DragDrop:
+ *                   resizeFrame, centerFrame, dragElId
+ */
+Ext.define('Ext.dd.DDProxy', {
+    extend: 'Ext.dd.DD',
+
+    statics: {
+        /**
+         * The default drag frame div id
+         * @property Ext.dd.DDProxy.dragElId
+         * @type String
+         * @static
+         */
+        dragElId: "ygddfdiv"
+    },
+
+    constructor: function(id, sGroup, config) {
+        if (id) {
+            this.init(id, sGroup, config);
+            this.initFrame();
+        }
+    },
+
+    /**
+     * By default we resize the drag frame to be the same size as the element
+     * we want to drag (this is to get the frame effect).  We can turn it off
+     * if we want a different behavior.
+     * @property resizeFrame
+     * @type boolean
+     */
+    resizeFrame: true,
+
+    /**
+     * By default the frame is positioned exactly where the drag element is, so
+     * we use the cursor offset provided by Ext.dd.DD.  Another option that works only if
+     * you do not have constraints on the obj is to have the drag frame centered
+     * around the cursor.  Set centerFrame to true for this effect.
+     * @property centerFrame
+     * @type boolean
+     */
+    centerFrame: false,
+
+    /**
+     * Creates the proxy element if it does not yet exist
+     * @method createFrame
+     */
+    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;
+
+            // appendChild can blow up IE if invoked prior to the window load event
+            // while rendering a table.  It is possible there are other scenarios
+            // that would cause this to happen as well.
+            body.insertBefore(div, body.firstChild);
+        }
+    },
+
+    /**
+     * Initialization for the drag frame element.  Must be called in the
+     * constructor of all subclasses
+     * @method initFrame
+     */
+    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);
+    },
+
+    /**
+     * Resizes the drag frame to the dimensions of the clicked object, positions
+     * it over the object, and finally displays it
+     * @method showFrame
+     * @param {int} iPageX X click position
+     * @param {int} iPageY Y click position
+     * @private
+     */
+    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();
+    },
+
+    /**
+     * The proxy is automatically resized to the dimensions of the linked
+     * element when a drag is initiated, unless resizeFrame is set to false
+     * @method _resizeProxy
+     * @private
+     */
+    _resizeProxy: function() {
+        if (this.resizeFrame) {
+            var el = this.getEl();
+            Ext.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
+        }
+    },
+
+    // overrides Ext.dd.DragDrop
+    b4MouseDown: function(e) {
+        var x = e.getPageX();
+        var y = e.getPageY();
+        this.autoOffset(x, y);
+        this.setDragElPos(x, y);
+    },
+
+    // overrides Ext.dd.DragDrop
+    b4StartDrag: function(x, y) {
+        // show the drag frame
+        this.showFrame(x, y);
+    },
+
+    // overrides Ext.dd.DragDrop
+    b4EndDrag: function(e) {
+        Ext.fly(this.getDragEl()).hide();
+    },
+
+    // overrides Ext.dd.DragDrop
+    // By default we try to move the element to the last location of the frame.
+    // This is so that the default behavior mirrors that of Ext.dd.DD.
+    endDrag: function(e) {
+
+        var lel = this.getEl();
+        var del = this.getDragEl();
+
+        // Show the drag frame briefly so we can get its position
+        del.style.visibility = "";
+
+        this.beforeMove();
+        // Hide the linked element before the move to get around a Safari
+        // rendering bug.
+        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);
+    }
+
+});
+
+/**
+ * @class Ext.dd.DragSource
+ * @extends Ext.dd.DDProxy
+ * A simple class that provides the basic implementation needed to make any element draggable.
+ * @constructor
+ * @param {Mixed} el The container element
+ * @param {Object} config
+ */
+Ext.define('Ext.dd.DragSource', {
+    extend: 'Ext.dd.DDProxy',
+    requires: [
+        'Ext.dd.StatusProxy',
+        'Ext.dd.DragDropManager'
+    ],
+
+    /**
+     * @cfg {String} ddGroup
+     * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
+     * interact with other drag drop objects in the same group (defaults to undefined).
+     */
+
+    /**
+     * @cfg {String} dropAllowed
+     * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
+     */
+
+    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
+    /**
+     * @cfg {String} dropNotAllowed
+     * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
+     */
+    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',
+
+    /**
+     * @cfg {Boolean} animRepair
+     * Defaults to true. If true, animates the proxy element back to the position of the handle element used to trigger the drag.
+     */
+    animRepair: true,
+
+    /**
+     * @cfg {String} repairHighlightColor The color to use when visually highlighting the drag source in the afterRepair
+     * method after a failed drop (defaults to 'c3daf9' - light blue). The color must be a 6 digit hex value, without
+     * a preceding '#'.
+     */
+    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;
+    },
+
+    /**
+     * Returns the data object associated with this drag source
+     * @return {Object} data An object containing arbitrary data
+     */
+    getDragData : function(e){
+        return this.dragData;
+    },
+
+    // private
+    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) {
+                /**
+                 * An empty function by default, but provided so that you can perform a custom action
+                 * when the dragged item enters the drop target by providing an implementation.
+                 * @param {Ext.dd.DragDrop} target The drop target
+                 * @param {Event} e The event object
+                 * @param {String} id The id of the dragged element
+                 * @method afterDragEnter
+                 */
+                this.afterDragEnter(target, e, id);
+            }
+        }
+    },
+
+    /**
+     * An empty function by default, but provided so that you can perform a custom action
+     * before the dragged item enters the drop target and optionally cancel the onDragEnter.
+     * @param {Ext.dd.DragDrop} target The drop target
+     * @param {Event} e The event object
+     * @param {String} id The id of the dragged element
+     * @return {Boolean} isValid True if the drag event is valid, else false to cancel
+     */
+    beforeDragEnter: function(target, e, id) {
+        return true;
+    },
+
+    // private
+    alignElWithMouse: function() {
+        this.callParent(arguments);
+        this.proxy.sync();
+    },
+
+    // private
+    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) {
+                /**
+                 * An empty function by default, but provided so that you can perform a custom action
+                 * while the dragged item is over the drop target by providing an implementation.
+                 * @param {Ext.dd.DragDrop} target The drop target
+                 * @param {Event} e The event object
+                 * @param {String} id The id of the dragged element
+                 * @method afterDragOver
+                 */
+                this.afterDragOver(target, e, id);
+            }
+        }
+    },
+
+    /**
+     * An empty function by default, but provided so that you can perform a custom action
+     * while the dragged item is over the drop target and optionally cancel the onDragOver.
+     * @param {Ext.dd.DragDrop} target The drop target
+     * @param {Event} e The event object
+     * @param {String} id The id of the dragged element
+     * @return {Boolean} isValid True if the drag event is valid, else false to cancel
+     */
+    beforeDragOver: function(target, e, id) {
+        return true;
+    },
+
+    // private
+    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) {
+                /**
+                 * An empty function by default, but provided so that you can perform a custom action
+                 * after the dragged item is dragged out of the target without dropping.
+                 * @param {Ext.dd.DragDrop} target The drop target
+                 * @param {Event} e The event object
+                 * @param {String} id The id of the dragged element
+                 * @method afterDragOut
+                 */
+                this.afterDragOut(target, e, id);
+            }
+        }
+        this.cachedTarget = null;
+    },
+
+    /**
+     * An empty function by default, but provided so that you can perform a custom action before the dragged
+     * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
+     * @param {Ext.dd.DragDrop} target The drop target
+     * @param {Event} e The event object
+     * @param {String} id The id of the dragged element
+     * @return {Boolean} isValid True if the drag event is valid, else false to cancel
+     */
+    beforeDragOut: function(target, e, id){
+        return true;
+    },
+
+    // private
+    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) { // valid drop?
+                    this.onValidDrop(target, e, id);
+                } else {
+                    this.onInvalidDrop(target, e, id);
+                }
+            } else {
+                this.onValidDrop(target, e, id);
+            }
+
+            if (this.afterDragDrop) {
+                /**
+                 * An empty function by default, but provided so that you can perform a custom action
+                 * after a valid drag drop has occurred by providing an implementation.
+                 * @param {Ext.dd.DragDrop} target The drop target
+                 * @param {Event} e The event object
+                 * @param {String} id The id of the dropped element
+                 * @method afterDragDrop
+                 */
+                this.afterDragDrop(target, e, id);
+            }
+        }
+        delete this.cachedTarget;
+    },
+
+    /**
+     * An empty function by default, but provided so that you can perform a custom action before the dragged
+     * item is dropped onto the target and optionally cancel the onDragDrop.
+     * @param {Ext.dd.DragDrop} target The drop target
+     * @param {Event} e The event object
+     * @param {String} id The id of the dragged element
+     * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
+     */
+    beforeDragDrop: function(target, e, id){
+        return true;
+    },
+
+    // private
+    onValidDrop: function(target, e, id){
+        this.hideProxy();
+        if(this.afterValidDrop){
+            /**
+             * An empty function by default, but provided so that you can perform a custom action
+             * after a valid drop has occurred by providing an implementation.
+             * @param {Object} target The target DD
+             * @param {Event} e The event object
+             * @param {String} id The id of the dropped element
+             * @method afterInvalidDrop
+             */
+            this.afterValidDrop(target, e, id);
+        }
+    },
+
+    // private
+    getRepairXY: function(e, data){
+        return this.el.getXY();
+    },
+
+    // private
+    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) {
+            /**
+             * An empty function by default, but provided so that you can perform a custom action
+             * after an invalid drop has occurred by providing an implementation.
+             * @param {Event} e The event object
+             * @param {String} id The id of the dropped element
+             * @method afterInvalidDrop
+             */
+            this.afterInvalidDrop(e, id);
+        }
+    },
+
+    // private
+    afterRepair: function() {
+        var me = this;
+        if (Ext.enableFx) {
+            me.el.highlight(me.repairHighlightColor);
+        }
+        me.dragging = false;
+    },
+
+    /**
+     * An empty function by default, but provided so that you can perform a custom action after an invalid
+     * drop has occurred.
+     * @param {Ext.dd.DragDrop} target The drop target
+     * @param {Event} e The event object
+     * @param {String} id The id of the dragged element
+     * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
+     */
+    beforeInvalidDrop: function(target, e, id) {
+        return true;
+    },
+
+    // private
+    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);
+        }
+    },
+
+    /**
+     * An empty function by default, but provided so that you can perform a custom action before the initial
+     * drag event begins and optionally cancel it.
+     * @param {Object} data An object containing arbitrary data to be shared with drop targets
+     * @param {Event} e The event object
+     * @return {Boolean} isValid True if the drag event is valid, else false to cancel
+     */
+    onBeforeDrag: function(data, e){
+        return true;
+    },
+
+    /**
+     * An empty function by default, but provided so that you can perform a custom action once the initial
+     * drag event has begun.  The drag cannot be canceled from this function.
+     * @param {Number} x The x position of the click on the dragged object
+     * @param {Number} y The y position of the click on the dragged object
+     */
+    onStartDrag: Ext.emptyFn,
+
+    // private override
+    startDrag: function(x, y) {
+        this.proxy.reset();
+        this.dragging = true;
+        this.proxy.update("");
+        this.onInitDrag(x, y);
+        this.proxy.show();
+    },
+
+    // private
+    onInitDrag: function(x, y) {
+        var clone = this.el.dom.cloneNode(true);
+        clone.id = Ext.id(); // prevent duplicate ids
+        this.proxy.update(clone);
+        this.onStartDrag(x, y);
+        return true;
+    },
+
+    /**
+     * Returns the drag source's underlying {@link Ext.dd.StatusProxy}
+     * @return {Ext.dd.StatusProxy} proxy The StatusProxy
+     */
+    getProxy: function() {
+        return this.proxy;
+    },
+
+    /**
+     * Hides the drag source's {@link Ext.dd.StatusProxy}
+     */
+    hideProxy: function() {
+        this.proxy.hide();
+        this.proxy.reset(true);
+        this.dragging = false;
+    },
+
+    // private
+    triggerCacheRefresh: function() {
+        Ext.dd.DDM.refreshCache(this.groups);
+    },
+
+    // private - override to prevent hiding
+    b4EndDrag: function(e) {
+    },
+
+    // private - override to prevent moving
+    endDrag : function(e){
+        this.onEndDrag(this.dragData, e);
+    },
+
+    // private
+    onEndDrag : function(data, e){
+    },
+
+    // private - pin to cursor
+    autoOffset : function(x, y) {
+        this.setDelta(-12, -20);
+    },
+
+    destroy: function(){
+        this.callParent();
+        Ext.destroy(this.proxy);
+    }
+});
+
+// private - DD implementation for Panels
+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);
+    }
+});
+
+/**
+ * @class Ext.layout.component.Dock
+ * @extends Ext.layout.component.AbstractDock
+ * @private
+ */
+Ext.define('Ext.layout.component.Dock', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.dock'],
+
+    extend: 'Ext.layout.component.AbstractDock'
+
+    /* End Definitions */
+
+});
+/**
+ * @class Ext.panel.Panel
+ * @extends Ext.panel.AbstractPanel
+ * <p>Panel is a container that has specific functionality and structural components that make
+ * it the perfect building block for application-oriented user interfaces.</p>
+ * <p>Panels are, by virtue of their inheritance from {@link Ext.container.Container}, capable
+ * of being configured with a {@link Ext.container.Container#layout layout}, and containing child Components.</p>
+ * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.container.Container#add adding} Components
+ * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
+ * those child elements need to be sized using one of Ext&#39;s built-in <code><b>{@link Ext.container.Container#layout layout}</b></code> schemes. By
+ * default, Panels use the {@link Ext.layout.container.Auto Auto} scheme. This simply renders
+ * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
+ * at all.</p>
+ * {@img Ext.panel.Panel/panel.png Panel components}
+ * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
+ * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
+ * information).</p>
+ * <p>Panel also provides built-in {@link #collapsible collapsible, expandable} and {@link #closable} behavior.
+ * Panels can be easily dropped into any {@link Ext.container.Container Container} or layout, and the
+ * layout and rendering pipeline is {@link Ext.container.Container#add completely managed by the framework}.</p>
+ * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Panel resulting in removal of the Panel
+ * and the destruction of any descendant Components. This makes the Panel object, and all its descendants <b>unusable</b>. To enable the close
+ * tool to simply <i>hide</i> a Panel for later re-use, configure the Panel with <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
+ * <p>Usually, Panels are used as constituents within an application, in which case, they would be used as child items of Containers,
+ * and would themselves use Ext.Components as child {@link #items}. However to illustrate simply rendering a Panel into the document,
+ * here&#39;s how to do it:<pre><code>
+Ext.create('Ext.panel.Panel', {
+    title: 'Hello',
+    width: 200,
+    html: '&lt;p&gt;World!&lt;/p&gt;',
+    renderTo: document.body
+});
+</code></pre></p>
+ * <p>A more realistic scenario is a Panel created to house input fields which will not be rendered, but used as a constituent part of a Container:<pre><code>
+var filterPanel = Ext.create('Ext.panel.Panel', {
+    bodyPadding: 5,  // Don&#39;t want content to crunch against the borders
+    title: 'Filters',
+    items: [{
+        xtype: 'datefield',
+        fieldLabel: 'Start date'
+    }, {
+        xtype: 'datefield',
+        fieldLabel: 'End date'
+    }]
+});
+</code></pre></p>
+ * <p>Note that the Panel above is not configured to render into the document, nor is it configured with a size or position. In a real world scenario,
+ * the Container into which the Panel is added will use a {@link #layout} to render, size and position its child Components.</p>
+ * <p>Panels will often use specific {@link #layout}s to provide an application with shape and structure by containing and arranging child
+ * Components: <pre><code>
+var resultsPanel = Ext.create('Ext.panel.Panel', {
+    title: 'Results',
+    width: 600,
+    height: 400,
+    renderTo: document.body,
+    layout: {
+        type: 'vbox',       // Arrange child items vertically
+        align: 'stretch',    // Each takes up full width
+        padding: 5
+    },
+    items: [{               // Results grid specified as a config object with an xtype of 'grid'
+        xtype: 'grid',
+        columns: [{header: 'Column One'}],            // One header just for show. There&#39;s no data,
+        store: Ext.create('Ext.data.ArrayStore', {}), // A dummy empty data store
+        flex: 1                                       // Use 1/3 of Container&#39;s height (hint to Box layout)
+    }, {
+        xtype: 'splitter'   // A splitter between the two child items
+    }, {                    // Details Panel specified as a config object (no xtype defaults to 'panel').
+        title: 'Details',
+        bodyPadding: 5,
+        items: [{
+            fieldLabel: 'Data item',
+            xtype: 'textfield'
+        }], // An array of form fields
+        flex: 2             // Use 2/3 of Container&#39;s height (hint to Box layout)
+    }]
+});
+</code></pre>
+ * The example illustrates one possible method of displaying search results. The Panel contains a grid with the resulting data arranged
+ * in rows. Each selected row may be displayed in detail in the Panel below. The {@link Ext.layout.container.VBox vbox} layout is used
+ * to arrange the two vertically. It is configured to stretch child items horizontally to full width. Child items may either be configured
+ * with a numeric height, or with a <code>flex</code> value to distribute available space proportionately.</p>
+ * <p>This Panel itself may be a child item of, for exaple, a {@link Ext.tab.Panel} which will size its child items to fit within its
+ * content area.</p>
+ * <p>Using these techniques, as long as the <b>layout</b> is chosen and configured correctly, an application may have any level of
+ * nested containment, all dynamically sized according to configuration, the user&#39;s preference and available browser size.</p>
+ * @constructor
+ * @param {Object} config The config object
+ * @xtype panel
+ */
+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',
+
+    /**
+     * @cfg {String} collapsedCls
+     * A CSS class to add to the panel&#39;s element after it has been collapsed (defaults to
+     * <code>'collapsed'</code>).
+     */
+    collapsedCls: 'collapsed',
+
+    /**
+     * @cfg {Boolean} animCollapse
+     * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
+     * animation (defaults to <code>true</code> if the {@link Ext.fx.Anim} class is available, otherwise <code>false</code>).
+     * May also be specified as the animation duration in milliseconds.
+     */
+    animCollapse: Ext.enableFx,
+
+    /**
+     * @cfg {Number} minButtonWidth
+     * Minimum width of all footer toolbar buttons in pixels (defaults to <tt>75</tt>). If set, this will
+     * be used as the default value for the <tt>{@link Ext.button.Button#minWidth}</tt> config of
+     * each Button added to the <b>footer toolbar</b> via the {@link #fbar} or {@link #buttons} configurations.
+     * It will be ignored for buttons that have a minWidth configured some other way, e.g. in their own config
+     * object or via the {@link Ext.container.Container#config-defaults defaults} of their parent container.
+     */
+    minButtonWidth: 75,
+
+    /**
+     * @cfg {Boolean} collapsed
+     * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
+     * <code>false</code>).
+     */
+    collapsed: false,
+
+    /**
+     * @cfg {Boolean} collapseFirst
+     * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
+     * any other tools in the panel&#39;s title bar, <code>false</code> to render it last (defaults to <code>true</code>).
+     */
+    collapseFirst: true,
+
+    /**
+     * @cfg {Boolean} hideCollapseTool
+     * <code>true</code> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
+     * <code>false</code> to display it (defaults to <code>false</code>).
+     */
+    hideCollapseTool: false,
+
+    /**
+     * @cfg {Boolean} titleCollapse
+     * <code>true</code> to allow expanding and collapsing the panel (when <code>{@link #collapsible} = true</code>)
+     * by clicking anywhere in the header bar, <code>false</code>) to allow it only by clicking to tool button
+     * (defaults to <code>false</code>)).
+     */
+    titleCollapse: false,
+
+    /**
+     * @cfg {String} collapseMode
+     * <p><b>Important: this config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
+     * <p>When <i>not</i> a direct child item of a {@link Ext.layout.container.Border border layout}, then the Panel&#39;s header remains visible, and the body is collapsed to zero dimensions.
+     * If the Panel has no header, then a new header (orientated correctly depending on the {@link #collapseDirection}) will be inserted to show a the title and a re-expand tool.</p>
+     * <p>When a child item of a {@link Ext.layout.container.Border border layout}, this config has two options:
+     * <div class="mdetail-params"><ul>
+     * <li><b><code>undefined/omitted</code></b><div class="sub-desc">When collapsed, a placeholder {@link Ext.panel.Header Header} is injected into the layout to represent the Panel
+     * and to provide a UI with a Tool to allow the user to re-expand the Panel.</div></li>
+     * <li><b><code>header</code></b> : <div class="sub-desc">The Panel collapses to leave its header visible as when not inside a {@link Ext.layout.container.Border border layout}.</div></li>
+     * </ul></div></p>
+     */
+
+    /**
+     * @cfg {Mixed} placeholder
+     * <p><b>Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}
+     * when not using the <code>'header'</code> {@link #collapseMode}.</b></p>
+     * <p><b>Optional.</b> A Component (or config object for a Component) to show in place of this Panel when this Panel is collapsed by a
+     * {@link Ext.layout.container.Border border layout}. Defaults to a generated {@link Ext.panel.Header Header}
+     * containing a {@link Ext.panel.Tool Tool} to re-expand the Panel.</p>
+     */
+
+    /**
+     * @cfg {Boolean} floatable
+     * <p><b>Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
+     * <tt>true</tt> to allow clicking a collapsed Panel&#39;s {@link #placeholder} to display the Panel floated
+     * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
+     * clicking the expand button to see it again (defaults to <tt>true</tt>).
+     */
+    floatable: true,
+
+    /**
+     * @cfg {Boolean} collapsible
+     * <p>True to make the panel collapsible and have an expand/collapse toggle Tool added into
+     * the header tool button area. False to keep the panel sized either statically, or by an owning layout manager, with no toggle Tool (defaults to false).</p>
+     * See {@link #collapseMode} and {@link #collapseDirection}
+     */
+    collapsible: false,
+
+    /**
+     * @cfg {Boolean} collapseDirection
+     * <p>The direction to collapse the Panel when the toggle button is clicked.</p>
+     * <p>Defaults to the {@link #headerPosition}</p>
+     * <p><b>Important: This config is <u>ignored</u> for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
+     * <p>Specify as <code>'top'</code>, <code>'bottom'</code>, <code>'left'</code> or <code>'right'</code>.</p>
+     */
+
+    /**
+     * @cfg {Boolean} closable
+     * <p>True to display the 'close' tool button and allow the user to close the window, false to
+     * hide the button and disallow closing the window (defaults to <code>false</code>).</p>
+     * <p>By default, when close is requested by clicking the close button in the header, the {@link #close}
+     * method will be called. This will <i>{@link Ext.Component#destroy destroy}</i> the Panel and its content
+     * meaning that it may not be reused.</p>
+     * <p>To make closing a Panel <i>hide</i> the Panel so that it may be reused, set
+     * {@link #closeAction} to 'hide'.</p>
+     */
+    closable: false,
+
+    /**
+     * @cfg {String} closeAction
+     * <p>The action to take when the close header tool is clicked:
+     * <div class="mdetail-params"><ul>
+     * <li><b><code>'{@link #destroy}'</code></b> : <b>Default</b><div class="sub-desc">
+     * {@link #destroy remove} the window from the DOM and {@link Ext.Component#destroy destroy}
+     * it and all descendant Components. The window will <b>not</b> be available to be
+     * redisplayed via the {@link #show} method.
+     * </div></li>
+     * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
+     * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
+     * The window will be available to be redisplayed via the {@link #show} method.
+     * </div></li>
+     * </ul></div>
+     * <p><b>Note:</b> This behavior has changed! setting *does* affect the {@link #close} method
+     * which will invoke the approriate closeAction.
+     */
+    closeAction: 'destroy',
+
+    /**
+     * @cfg {Object/Array} dockedItems
+     * A component or series of components to be added as docked items to this panel.
+     * The docked items can be docked to either the top, right, left or bottom of a panel.
+     * This is typically used for things like toolbars or tab bars:
+     * <pre><code>
+var panel = new Ext.panel.Panel({
+    dockedItems: [{
+        xtype: 'toolbar',
+        dock: 'top',
+        items: [{
+            text: 'Docked to the top'
+        }]
+    }]
+});</pre></code>
+     */
+
+    /**
+      * @cfg {Boolean} preventHeader Prevent a Header from being created and shown. Defaults to false.
+      */
+    preventHeader: false,
+
+     /**
+      * @cfg {String} headerPosition Specify as <code>'top'</code>, <code>'bottom'</code>, <code>'left'</code> or <code>'right'</code>. Defaults to <code>'top'</code>.
+      */
+    headerPosition: 'top',
+
+     /**
+     * @cfg {Boolean} frame
+     * True to apply a frame to the panel.
+     */
+    frame: false,
+
+    /**
+     * @cfg {Boolean} frameHeader
+     * True to apply a frame to the panel panels header (if 'frame' is true).
+     */
+    frameHeader: true,
+
+    /**
+     * @cfg {Array} tools
+     * An array of {@link Ext.panel.Tool} configs/instances to be added to the header tool area. The tools are stored as child
+     * components of the header container. They can be accessed using {@link #down} and {#query}, as well as the other
+     * component methods. The toggle tool is automatically created if {@link #collapsible} is set to true.
+     * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
+     * tools only provide the visual button. Any required functionality must be provided by adding
+     * handlers that implement the necessary behavior.</p>
+     * <p>Example usage:</p>
+     * <pre><code>
+tools:[{
+    type:'refresh',
+    qtip: 'Refresh form Data',
+    // hidden:true,
+    handler: function(event, toolEl, panel){
+        // refresh logic
+    }
+},
+{
+    type:'help',
+    qtip: 'Get Help',
+    handler: function(event, toolEl, panel){
+        // show help here
+    }
+}]
+</code></pre>
+     */
+
+
+    initComponent: function() {
+        var me = this,
+            cls;
+
+        me.addEvents(
+        /**
+         * @event titlechange
+         * Fires after the Panel title has been set or changed.
+         * @param {Ext.panel.Panel} p the Panel which has been resized.
+         * @param {String} newTitle The new title.
+         * @param {String} oldTitle The previous panel title.
+         */
+            'titlechange',
+        /**
+         * @event iconchange
+         * Fires after the Panel iconCls has been set or changed.
+         * @param {Ext.panel.Panel} p the Panel which has been resized.
+         * @param {String} newIconCls The new iconCls.
+         * @param {String} oldIconCls The previous panel iconCls.
+         */
+            '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;
+
+        // Backwards compatibility
+        me.bridgeToolbars();
+    },
+
+    setBorder: function(border) {
+        // var me     = this,
+        //     method = (border === false || border === 0) ? 'addClsWithUI' : 'removeClsWithUI';
+        // 
+        // me.callParent(arguments);
+        // 
+        // if (me.collapsed) {
+        //     me[method](me.collapsedCls + '-noborder');
+        // }
+        // 
+        // if (me.header) {
+        //     me.header.setBorder(border);
+        //     if (me.collapsed) {
+        //         me.header[method](me.collapsedCls + '-noborder');
+        //     }
+        // }
+        
+        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;
+    },
+
+    /**
+     * Set a title for the panel&#39;s header. See {@link Ext.panel.Header#title}.
+     * @param {String} newTitle
+     */
+    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);
+    },
+
+    /**
+     * Set the iconCls for the panel&#39;s header. See {@link Ext.panel.Header#iconCls}.
+     * @param {String} newIconCls
+     */
+    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;
+        }
+
+        // Backwards compatibility
+
+        /**
+         * @cfg {Object/Array} tbar
+
+Convenience method. Short for 'Top Bar'.
+
+    tbar: [
+      { xtype: 'button', text: 'Button 1' }
+    ]
+
+is equivalent to
+
+    dockedItems: [{
+        xtype: 'toolbar',
+        dock: 'top',
+        items: [
+            { xtype: 'button', text: 'Button 1' }
+        ]
+    }]
+
+         * @markdown
+         */
+        if (me.tbar) {
+            me.addDocked(initToolbar(me.tbar, 'top'));
+            me.tbar = null;
+        }
+
+        /**
+         * @cfg {Object/Array} bbar
+
+Convenience method. Short for 'Bottom Bar'.
+
+    bbar: [
+      { xtype: 'button', text: 'Button 1' }
+    ]
+
+is equivalent to
+
+    dockedItems: [{
+        xtype: 'toolbar',
+        dock: 'bottom',
+        items: [
+            { xtype: 'button', text: 'Button 1' }
+        ]
+    }]
+
+         * @markdown
+         */
+        if (me.bbar) {
+            me.addDocked(initToolbar(me.bbar, 'bottom'));
+            me.bbar = null;
+        }
+
+        /**
+         * @cfg {Object/Array} buttons
+
+Convenience method used for adding buttons docked to the bottom right of the panel. This is a
+synonym for the {@link #fbar} config.
+
+    buttons: [
+      { text: 'Button 1' }
+    ]
+
+is equivalent to
+
+    dockedItems: [{
+        xtype: 'toolbar',
+        dock: 'bottom',
+        defaults: {minWidth: {@link #minButtonWidth}},
+        items: [
+            { xtype: 'component', flex: 1 },
+            { xtype: 'button', text: 'Button 1' }
+        ]
+    }]
+
+The {@link #minButtonWidth} is used as the default {@link Ext.button.Button#minWidth minWidth} for
+each of the buttons in the buttons toolbar.
+
+         * @markdown
+         */
+        if (me.buttons) {
+            me.fbar = me.buttons;
+            me.buttons = null;
+        }
+
+        /**
+         * @cfg {Object/Array} fbar
+
+Convenience method used for adding items to the bottom right of the panel. Short for Footer Bar.
+
+    fbar: [
+      { type: 'button', text: 'Button 1' }
+    ]
+
+is equivalent to
+
+    dockedItems: [{
+        xtype: 'toolbar',
+        dock: 'bottom',
+        defaults: {minWidth: {@link #minButtonWidth}},
+        items: [
+            { xtype: 'component', flex: 1 },
+            { xtype: 'button', text: 'Button 1' }
+        ]
+    }]
+
+The {@link #minButtonWidth} is used as the default {@link Ext.button.Button#minWidth minWidth} for
+each of the buttons in the fbar.
+
+         * @markdown
+         */
+        if (me.fbar) {
+            fbar = initToolbar(me.fbar, 'bottom');
+            fbar.ui = 'footer';
+
+            // Apply the minButtonWidth config to buttons in the toolbar
+            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;
+        }
+
+        /**
+         * @cfg {Object/Array} lbar
+         *
+         * Convenience method. Short for 'Left Bar' (left-docked, vertical toolbar).
+         *
+         *    lbar: [
+         *      { xtype: 'button', text: 'Button 1' }
+         *    ]
+         *
+         * is equivalent to
+         *
+         *    dockedItems: [{
+         *        xtype: 'toolbar',
+         *        dock: 'left',
+         *        items: [
+         *            { xtype: 'button', text: 'Button 1' }
+         *        ]
+         *    }]
+         *
+         * @markdown
+         */
+        if (me.lbar) {
+            me.addDocked(initToolbar(me.lbar, 'left'));
+            me.lbar = null;
+        }
+
+        /**
+         * @cfg {Object/Array} rbar
+         *
+         * Convenience method. Short for 'Right Bar' (right-docked, vertical toolbar).
+         *
+         *    rbar: [
+         *      { xtype: 'button', text: 'Button 1' }
+         *    ]
+         *
+         * is equivalent to
+         *
+         *    dockedItems: [{
+         *        xtype: 'toolbar',
+         *        dock: 'right',
+         *        items: [
+         *            { xtype: 'button', text: 'Button 1' }
+         *        ]
+         *    }]
+         *
+         * @markdown
+         */
+        if (me.rbar) {
+            me.addDocked(initToolbar(me.rbar, 'right'));
+            me.rbar = null;
+        }
+    },
+
+    /**
+     * @private
+     * Tools are a Panel-specific capabilty.
+     * Panel uses initTools. Subclasses may contribute tools by implementing addTools.
+     */
+    initTools: function() {
+        var me = this;
+
+        me.tools = me.tools || [];
+
+        // Add a collapse tool unless configured to not show a collapse tool
+        // or to not even show a header.
+        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
+            });
+
+            // Prepend collapse tool is configured to do so.
+            if (me.collapseFirst) {
+                me.tools.unshift(me.collapseTool);
+            }
+        }
+
+        // Add subclass-specific tools.
+        me.addTools();
+
+        // Make Panel closable.
+        if (me.closable) {
+            me.addClsWithUI('closable');
+            me.addTool({
+                type: 'close',
+                handler: Ext.Function.bind(me.close, this, [])
+            });
+        }
+
+        // Append collapse tool if needed.
+        if (me.collapseTool && !me.collapseFirst) {
+            me.tools.push(me.collapseTool);
+        }
+    },
+
+    /**
+     * @private
+     * Template method to be implemented in subclasses to add their tools after the collapsible tool.
+     */
+    addTools: Ext.emptyFn,
+
+    /**
+     * <p>Closes the Panel. By default, this method, removes it from the DOM, {@link Ext.Component#destroy destroy}s
+     * the Panel object and all its descendant Components. The {@link #beforeclose beforeclose}
+     * event is fired before the close happens and will cancel the close action if it returns false.<p>
+     * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
+     * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
+     * To hide the Panel without destroying it, call {@link #hide}.</p>
+     */
+    close: function() {
+        if (this.fireEvent('beforeclose', this) !== false) {
+            this.doClose();
+        }
+    },
+
+    // private
+    doClose: function() {
+        this.fireEvent('close', this);
+        this[this.closeAction]();
+    },
+
+    onRender: function(ct, position) {
+        var me = this,
+            topContainer;
+
+        // Add class-specific header tools.
+        // Panel adds collapsible and closable.
+        me.initTools();
+
+        // Dock the header/title
+        me.updateHeader();
+
+        // If initially collapsed, collapsed flag must indicate true current state at this point.
+        // Do collapse after the first time the Panel's structure has been laid out.
+        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);
+                };
+            }
+        }
+
+        // Call to super after adding the header, to prevent an unnecessary re-layout
+        me.callParent(arguments);
+    },
+
+    /**
+     * Create, hide, or show the header component as appropriate based on the current config.
+     * @private
+     * @param {Boolean} force True to force the the header to be created
+     */
+    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);
+
+                // Reference the Header's tool array.
+                // Header injects named references.
+                me.tools = header.tools;
+            }
+            header.show();
+            me.initHeaderAria();
+        } else if (header) {
+            header.hide();
+        }
+    },
+
+    // inherit docs
+    setUI: function(ui) {
+        var me = this;
+
+        me.callParent(arguments);
+
+        if (me.header) {
+            me.header.setUI(ui);
+        }
+    },
+
+    // private
+    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;
+        }
+    },
+
+    /**
+     * Collapses the panel body so that the body becomes hidden. Docked Components parallel to the
+     * border towards which the collapse takes place will remain visible.  Fires the {@link #beforecollapse} event which will
+     * cancel the collapse action if it returns false.
+     * @param {Number} direction. The direction to collapse towards. Must be one of<ul>
+     * <li>Ext.Component.DIRECTION_TOP</li>
+     * <li>Ext.Component.DIRECTION_RIGHT</li>
+     * <li>Ext.Component.DIRECTION_BOTTOM</li>
+     * <li>Ext.Component.DIRECTION_LEFT</li></ul>
+     * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
+     * {@link #animCollapse} panel config)
+     * @return {Ext.panel.Panel} this
+     */
+    collapse: function(direction, animate, /* private - passed if called at render time */ 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 (Called because of initial collapsed state), then no animation, and no events.
+        if (internal) {
+            animate = false;
+        } else if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) {
+            return false;
+        }
+
+        reExpanderDock = direction;
+        me.expandDirection = me.getOppositeDirection(direction);
+
+        // Track docked items which we hide during collapsed state
+        me.hiddenDocked = [];
+
+        switch (direction) {
+            case c.DIRECTION_TOP:
+            case c.DIRECTION_BOTTOM:
+                me.expandedSize = me.getHeight();
+                reExpanderOrientation = 'horizontal';
+                collapseDimension = 'height';
+                getDimension = 'getHeight';
+                setDimension = 'setHeight';
+
+                // Collect the height of the visible header.
+                // Hide all docked items except the header.
+                // Hide *ALL* docked items if we're going to end up hiding the whole Panel anyway
+                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';
+
+                // Collect the height of the visible header.
+                // Hide all docked items except the header.
+                // Hide *ALL* docked items if we're going to end up hiding the whole Panel anyway
+                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');
+        }
+
+        // No scrollbars when we shrink this Panel
+        // And no laying out of any children... we're effectively *hiding* the body
+        me.setAutoScroll(false);
+        me.suspendLayout = true;
+        me.body.setVisibilityMode(Ext.core.Element.DISPLAY);
+
+        // Disable toggle tool during animated collapse
+        if (animate && me.collapseTool) {
+            me.collapseTool.disable();
+        }
+
+        // Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken.
+        me.addClsWithUI(me.collapsedCls);
+        // if (me.border === false) {
+        //     me.addClsWithUI(me.collapsedCls + '-noborder');
+        // }
+
+        // We found a header: Measure it to find the collapse-to size.
+        if (reExpander) {
+            //we must add the collapsed cls to the header and then remove to get the proper height
+            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();
+                        
+            //get the size
+            newSize = reExpander[getDimension]() + (frameInfo ? frameInfo[direction] : 0);
+
+            //and remove
+            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);
+            }
+        }
+        // No header: Render and insert a temporary one, and then measure it.
+        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
+            };
+            reExpander[(reExpander.orientation == 'horizontal') ? 'tools' : 'items'] = [{
+                xtype: 'tool',
+                type: 'expand-' + me.expandDirection,
+                handler: me.toggleCollapse,
+                scope: me
+            }];
+
+            // Capture the size of the re-expander.
+            // For vertical headers in IE6 and IE7, this will be sized by a CSS rule in _panel.scss
+            reExpander = me.reExpander = Ext.create('Ext.panel.Header', reExpander);
+            newSize = reExpander[getDimension]() + ((reExpander.frame) ? reExpander.frameSize[direction] : 0);
+            reExpander.hide();
+
+            // Insert the new docked item
+            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 collapsing right or down, we'll be also animating the left or top.
+        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);
+        }
+
+        // Animate to the new size
+        anim.to[collapseDimension] = newSize;
+
+        // Remove any flex config before we attempt to collapse.
+        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.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 Panel was configured with a collapse tool in its header, flip it's type
+        if (me.collapseTool) {
+            me.collapseTool.setType('expand-' + me.expandDirection);
+        }
+        if (!internal) {
+            me.fireEvent('collapse', me);
+        }
+
+        // Re-enable the toggle tool after an animated collapse
+        if (animated && me.collapseTool) {
+            me.collapseTool.enable();
+        }
+    },
+
+    /**
+     * Expands the panel body so that it becomes visible.  Fires the {@link #beforeexpand} event which will
+     * cancel the expand action if it returns false.
+     * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
+     * {@link #animCollapse} panel config)
+     * @return {Ext.panel.Panel} this
+     */
+    expand: function(animate) {
+        if (!this.collapsed || this.fireEvent('beforeexpand', this, animate) === false) {
+            return false;
+        }
+        var me = this,
+            i = 0,
+            l = me.hiddenDocked.length,
+            direction = me.expandDirection,
+            height = me.getHeight(),
+            width = me.getWidth(),
+            pos, anim, satisfyJSLint;
+
+        // Disable toggle tool during animated expand
+        if (animate && me.collapseTool) {
+            me.collapseTool.disable();
+        }
+
+        // Show any docked items that we hid on collapse
+        // And hide the injected reExpander Header
+        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 Panel was configured with a collapse tool in its header, flip it's type
+        if (me.collapseTool) {
+            me.collapseTool.setType('collapse-' + me.collapseDirection);
+        }
+
+        // Unset the flag before the potential call to calculateChildBox to calculate our newly flexed size
+        me.collapsed = false;
+
+        // Collapsed means body element was hidden
+        me.body.show();
+
+        // Remove any collapsed styling before any animation begins
+        me.removeClsWithUI(me.collapsedCls);
+        // if (me.border === false) {
+        //     me.removeClsWithUI(me.collapsedCls + '-noborder');
+        // }
+
+        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 autoHeight, measure the height now we have shown the body element.
+            if (me.autoHeight) {
+                me.setCalculatedSize(me.width, null);
+                anim.to.height = me.getHeight();
+
+                // Must size back down to collapsed for the animation.
+                me.setCalculatedSize(me.width, anim.from.height);
+            }
+            // If we were flexed, then we can't just restore to the saved size.
+            // We must restore to the currently correct, flexed size, so we much ask the Box layout what that is.
+            else if (me.savedFlex) {
+                me.flex = me.savedFlex;
+                anim.to.height = me.ownerCt.layout.calculateChildBox(me).height;
+                delete me.flex;
+            }
+            // Else, restore to saved height
+            else {
+                anim.to.height = me.expandedSize;
+            }
+
+            // top needs animating upwards
+            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 autoWidth, measure the width now we have shown the body element.
+            if (me.autoWidth) {
+                me.setCalculatedSize(null, me.height);
+                anim.to.width = me.getWidth();
+
+                // Must size back down to collapsed for the animation.
+                me.setCalculatedSize(anim.from.width, me.height);
+            }
+            // If we were flexed, then we can't just restore to the saved size.
+            // We must restore to the currently correct, flexed size, so we much ask the Box layout what that is.
+            else if (me.savedFlex) {
+                me.flex = me.savedFlex;
+                anim.to.width = me.ownerCt.layout.calculateChildBox(me).width;
+                delete me.flex;
+            }
+            // Else, restore to saved width
+            else {
+                anim.to.width = me.expandedSize;
+            }
+
+            // left needs animating leftwards
+            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);
+
+        // Restored to a calculated flex. Delete the set width and height properties so that flex works from now on.
+        if (me.savedFlex) {
+            me.flex = me.savedFlex;
+            delete me.savedFlex;
+            delete me.width;
+            delete me.height;
+        }
+
+        // Reinstate layout out after Panel has re-expanded
+        delete me.suspendLayout;
+        if (animated && me.ownerCt) {
+            me.ownerCt.doLayout();
+        }
+
+        if (me.resizer) {
+            me.resizer.enable();
+        }
+
+        me.fireEvent('expand', me);
+
+        // Re-enable the toggle tool after an animated expand
+        if (animated && me.collapseTool) {
+            me.collapseTool.enable();
+        }
+    },
+
+    /**
+     * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
+     * @return {Ext.panel.Panel} this
+     */
+    toggleCollapse: function() {
+        if (this.collapsed) {
+            this.expand(this.animCollapse);
+        } else {
+            this.collapse(this.collapseDirection, this.animCollapse);
+        }
+        return this;
+    },
+
+    // private
+    getKeyMap : function(){
+        if(!this.keyMap){
+            this.keyMap = Ext.create('Ext.util.KeyMap', this.el, this.keys);
+        }
+        return this.keyMap;
+    },
+
+    // private
+    initDraggable : function(){
+        /**
+         * <p>If this Panel is configured {@link #draggable}, this property will contain
+         * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
+         * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
+         * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
+         * @type Ext.dd.DragSource.
+         * @property dd
+         */
+        this.dd = Ext.create('Ext.panel.DD', this, Ext.isBoolean(this.draggable) ? null : this.draggable);
+    },
+
+    // private - helper function for ghost
+    ghostTools : function() {
+        var tools = [],
+            origTools = this.initialConfig.tools;
+
+        if (origTools) {
+            Ext.each(origTools, function(tool) {
+                // Some tools can be full components, and copying them into the ghost
+                // actually removes them from the owning panel. You could also potentially
+                // end up with duplicate DOM ids as well. To avoid any issues we just make
+                // a simple bare-minimum clone of each tool for ghosting purposes.
+                tools.push({
+                    type: tool.type
+                });
+            });
+        }
+        else {
+            tools = [{
+                type: 'placeholder'
+            }];
+        }
+        return tools;
+    },
+
+    // private - used for dragging
+    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;
+    },
+
+    // private
+    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]);
+    }
+});
+
+/**
+ * Component layout for Tip/ToolTip/etc. components
+ * @class Ext.layout.component.Tip
+ * @extends Ext.layout.component.Dock
+ * @private
+ */
+
+Ext.define('Ext.layout.component.Tip', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.tip'],
+
+    extend: 'Ext.layout.component.Dock',
+
+    /* End Definitions */
+
+    type: 'tip',
+    
+    onLayout: function(width, height) {
+        var me = this,
+            owner = me.owner,
+            el = owner.el,
+            minWidth,
+            maxWidth,
+            naturalWidth,
+            constrainedWidth,
+            xy = el.getXY();
+
+        // Position offscreen so the natural width is not affected by the viewport's right edge
+        el.setXY([-9999,-9999]);
+
+        // Calculate initial layout
+        this.callParent(arguments);
+
+        // Handle min/maxWidth for auto-width tips
+        if (!Ext.isNumber(width)) {
+            minWidth = owner.minWidth;
+            maxWidth = owner.maxWidth;
+            // IE6/7 in strict mode have a problem doing an autoWidth
+            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]);
+            }
+        }
+
+        // Restore position
+        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;
+    }
+});
+
+/**
+ * @class Ext.tip.Tip
+ * @extends Ext.panel.Panel
+ * This is the base class for {@link Ext.tip.QuickTip} and {@link Ext.tip.ToolTip} that provides the basic layout and
+ * positioning that all tip-based classes require. This class can be used directly for simple, statically-positioned
+ * tips that are displayed programmatically, or it can be extended to provide custom tip implementations.
+ * @constructor
+ * Create a new Tip
+ * @param {Object} config The configuration options
+ * @xtype tip
+ */
+Ext.define('Ext.tip.Tip', {
+    extend: 'Ext.panel.Panel',
+    requires: [ 'Ext.layout.component.Tip' ],
+    alternateClassName: 'Ext.Tip',
+    /**
+     * @cfg {Boolean} closable True to render a close tool button into the tooltip header (defaults to false).
+     */
+    /**
+     * @cfg {Number} width
+     * Width in pixels of the tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
+     * {@link #minWidth} or {@link #maxWidth}.  The maximum supported value is 500.
+     */
+    /**
+     * @cfg {Number} minWidth The minimum width of the tip in pixels (defaults to 40).
+     */
+    minWidth : 40,
+    /**
+     * @cfg {Number} maxWidth The maximum width of the tip in pixels (defaults to 300).  The maximum supported value is 500.
+     */
+    maxWidth : 300,
+    /**
+     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
+     * for bottom-right shadow (defaults to "sides").
+     */
+    shadow : "sides",
+
+    /**
+     * @cfg {String} defaultAlign <b>Experimental</b>. The default {@link Ext.core.Element#alignTo} anchor position value
+     * for this tip relative to its element of origin (defaults to "tl-bl?").
+     */
+    defaultAlign : "tl-bl?",
+    /**
+     * @cfg {Boolean} constrainPosition If true, then the tooltip will be automatically constrained to stay within
+     * the browser viewport. Defaults to false.
+     */
+    constrainPosition : true,
+
+    /**
+     * @inherited
+     */
+    frame: false,
+
+    // private panel overrides
+    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);
+
+        // Or in the deprecated config. Floating.doConstrain only constrains if the constrain property is truthy.
+        this.constrain = this.constrain || this.constrainPosition;
+    },
+
+    /**
+     * Shows this tip at the specified XY position.  Example usage:
+     * <pre><code>
+// Show the tip at x:50 and y:100
+tip.showAt([50,100]);
+</code></pre>
+     * @param {Array} xy An array containing the x and y coordinates
+     */
+    showAt : function(xy){
+        var me = this;
+        this.callParent();
+        // Show may have been vetoed.
+        if (me.isVisible()) {
+            me.setPagePosition(xy[0], xy[1]);
+            if (me.constrainPosition || me.constrain) {
+                me.doConstrain();
+            }
+            me.toFront(true);
+        }
+    },
+
+    /**
+     * <b>Experimental</b>. Shows this tip at a position relative to another element using a standard {@link Ext.core.Element#alignTo}
+     * anchor position value.  Example usage:
+     * <pre><code>
+// Show the tip at the default position ('tl-br?')
+tip.showBy('my-el');
+
+// Show the tip's top-left corner anchored to the element's top-right corner
+tip.showBy('my-el', 'tl-tr');
+</code></pre>
+     * @param {Mixed} el An HTMLElement, Ext.core.Element or string id of the target element to align to
+     * @param {String} position (optional) A valid {@link Ext.core.Element#alignTo} anchor position (defaults to 'tl-br?' or
+     * {@link #defaultAlign} if specified).
+     */
+    showBy : function(el, pos) {
+        this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign));
+    },
+
+    /**
+     * @private
+     * @override
+     * Set Tip draggable using base Component's draggability
+     */
+    initDraggable : function(){
+        var me = this;
+        me.draggable = {
+            el: me.getDragEl(),
+            delegate: me.header.el,
+            constrain: me,
+            constrainTo: me.el.dom.parentNode
+        };
+        // Important: Bypass Panel's initDraggable. Call direct to Component's implementation.
+        Ext.Component.prototype.initDraggable.call(me);
+    },
+
+    // Tip does not ghost. Drag is "live"
+    ghost: undefined,
+    unghost: undefined
+});
+
+/**
+ * @class Ext.tip.ToolTip
+ * @extends Ext.tip.Tip
+ * 
+ * ToolTip is a {@link Ext.tip.Tip} implementation that handles the common case of displaying a
+ * tooltip when hovering over a certain element or elements on the page. It allows fine-grained
+ * control over the tooltip's alignment relative to the target element or mouse, and the timing
+ * of when it is automatically shown and hidden.
+ * 
+ * This implementation does **not** have a built-in method of automatically populating the tooltip's
+ * text based on the target element; you must either configure a fixed {@link #html} value for each
+ * ToolTip instance, or implement custom logic (e.g. in a {@link #beforeshow} event listener) to
+ * generate the appropriate tooltip content on the fly. See {@link Ext.tip.QuickTip} for a more
+ * convenient way of automatically populating and configuring a tooltip based on specific DOM
+ * attributes of each target element.
+ * 
+ * ## Basic Example
+ * 
+ *     var tip = Ext.create('Ext.tip.ToolTip', {
+ *         target: 'clearButton',
+ *         html: 'Press this button to clear the form'
+ *     });
+ * 
+ * {@img Ext.tip.ToolTip/Ext.tip.ToolTip1.png Basic Ext.tip.ToolTip}
+ * 
+ * ## Delegation
+ * 
+ * In addition to attaching a ToolTip to a single element, you can also use delegation to attach
+ * one ToolTip to many elements under a common parent. This is more efficient than creating many
+ * ToolTip instances. To do this, point the {@link #target} config to a common ancestor of all the
+ * elements, and then set the {@link #delegate} config to a CSS selector that will select all the
+ * appropriate sub-elements.
+ * 
+ * When using delegation, it is likely that you will want to programmatically change the content
+ * of the ToolTip based on each delegate element; you can do this by implementing a custom
+ * listener for the {@link #beforeshow} event. Example:
+ * 
+ *     var myGrid = Ext.create('Ext.grid.GridPanel', gridConfig);
+ *     myGrid.on('render', function(grid) {
+ *         var view = grid.getView();    // Capture the grid's view.
+ *         grid.tip = Ext.create('Ext.tip.ToolTip', {
+ *             target: view.el,          // The overall target element.
+ *             delegate: view.itemSelector, // Each grid row causes its own seperate show and hide.
+ *             trackMouse: true,         // Moving within the row should not hide the tip.
+ *             renderTo: Ext.getBody(),  // Render immediately so that tip.body can be referenced prior to the first show.
+ *             listeners: {              // Change content dynamically depending on which element triggered the show.
+ *                 beforeshow: function updateTipBody(tip) {
+ *                     tip.update('Over company "' + view.getRecord(tip.triggerElement).get('company') + '"');
+ *                 }
+ *             }
+ *         });
+ *     });
+ * 
+ * {@img Ext.tip.ToolTip/Ext.tip.ToolTip2.png Ext.tip.ToolTip with delegation}
+ * 
+ * ## Alignment
+ * 
+ * The following configuration properties allow control over how the ToolTip is aligned relative to
+ * the target element and/or mouse pointer:
+ * 
+ *  - {@link #anchor}
+ *  - {@link #anchorToTarget}
+ *  - {@link #anchorOffset}
+ *  - {@link #trackMouse}
+ *  - {@link #mouseOffset}
+ * 
+ * ## Showing/Hiding
+ * 
+ * The following configuration properties allow control over how and when the ToolTip is automatically
+ * shown and hidden:
+ * 
+ *  - {@link #autoHide}
+ *  - {@link #showDelay}
+ *  - {@link #hideDelay}
+ *  - {@link #dismissDelay}
+ * 
+ * @constructor
+ * Create a new ToolTip instance
+ * @param {Object} config The configuration options
+ * @xtype tooltip
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+Ext.define('Ext.tip.ToolTip', {
+    extend: 'Ext.tip.Tip',
+    alias: 'widget.tooltip',
+    alternateClassName: 'Ext.ToolTip',
+    /**
+     * When a ToolTip is configured with the <code>{@link #delegate}</code>
+     * option to cause selected child elements of the <code>{@link #target}</code>
+     * Element to each trigger a seperate show event, this property is set to
+     * the DOM element which triggered the show.
+     * @type DOMElement
+     * @property triggerElement
+     */
+    /**
+     * @cfg {Mixed} target The target HTMLElement, Ext.core.Element or id to monitor
+     * for mouseover events to trigger showing this ToolTip.
+     */
+    /**
+     * @cfg {Boolean} autoHide True to automatically hide the tooltip after the
+     * mouse exits the target element or after the <code>{@link #dismissDelay}</code>
+     * has expired if set (defaults to true).  If <code>{@link #closable} = true</code>
+     * a close tool button will be rendered into the tooltip header.
+     */
+    /**
+     * @cfg {Number} showDelay Delay in milliseconds before the tooltip displays
+     * after the mouse enters the target element (defaults to 500)
+     */
+    showDelay: 500,
+    /**
+     * @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the
+     * target element but before the tooltip actually hides (defaults to 200).
+     * Set to 0 for the tooltip to hide immediately.
+     */
+    hideDelay: 200,
+    /**
+     * @cfg {Number} dismissDelay Delay in milliseconds before the tooltip
+     * automatically hides (defaults to 5000). To disable automatic hiding, set
+     * dismissDelay = 0.
+     */
+    dismissDelay: 5000,
+    /**
+     * @cfg {Array} mouseOffset An XY offset from the mouse position where the
+     * tooltip should be shown (defaults to [15,18]).
+     */
+    /**
+     * @cfg {Boolean} trackMouse True to have the tooltip follow the mouse as it
+     * moves over the target element (defaults to false).
+     */
+    trackMouse: false,
+    /**
+     * @cfg {String} anchor If specified, indicates that the tip should be anchored to a
+     * particular side of the target element or mouse pointer ("top", "right", "bottom",
+     * or "left"), with an arrow pointing back at the target or mouse pointer. If
+     * {@link #constrainPosition} is enabled, this will be used as a preferred value
+     * only and may be flipped as needed.
+     */
+    /**
+     * @cfg {Boolean} anchorToTarget True to anchor the tooltip to the target
+     * element, false to anchor it relative to the mouse coordinates (defaults
+     * to true).  When <code>anchorToTarget</code> is true, use
+     * <code>{@link #defaultAlign}</code> to control tooltip alignment to the
+     * target element.  When <code>anchorToTarget</code> is false, use
+     * <code>{@link #anchorPosition}</code> instead to control alignment.
+     */
+    anchorToTarget: true,
+    /**
+     * @cfg {Number} anchorOffset A numeric pixel value used to offset the
+     * default position of the anchor arrow (defaults to 0).  When the anchor
+     * position is on the top or bottom of the tooltip, <code>anchorOffset</code>
+     * will be used as a horizontal offset.  Likewise, when the anchor position
+     * is on the left or right side, <code>anchorOffset</code> will be used as
+     * a vertical offset.
+     */
+    anchorOffset: 0,
+    /**
+     * @cfg {String} delegate <p>Optional. A {@link Ext.DomQuery DomQuery}
+     * selector which allows selection of individual elements within the
+     * <code>{@link #target}</code> element to trigger showing and hiding the
+     * ToolTip as the mouse moves within the target.</p>
+     * <p>When specified, the child element of the target which caused a show
+     * event is placed into the <code>{@link #triggerElement}</code> property
+     * before the ToolTip is shown.</p>
+     * <p>This may be useful when a Component has regular, repeating elements
+     * in it, each of which need a ToolTip which contains information specific
+     * to that element. For example:</p><pre><code>
+var myGrid = Ext.create('Ext.grid.GridPanel', gridConfig);
+myGrid.on('render', function(grid) {
+    var view = grid.getView();    // Capture the grid's view.
+    grid.tip = Ext.create('Ext.tip.ToolTip', {
+        target: view.el,          // The overall target element.
+        delegate: view.itemSelector, // Each grid row causes its own seperate show and hide.
+        trackMouse: true,         // Moving within the row should not hide the tip.
+        renderTo: Ext.getBody(),  // Render immediately so that tip.body can be referenced prior to the first show.
+        listeners: {              // Change content dynamically depending on which element triggered the show.
+            beforeshow: function(tip) {
+                tip.update('Over Record ID ' + view.getRecord(tip.triggerElement).id);
+            }
+        }
+    });
+});
+     *</code></pre>
+     */
+
+    // private
+    targetCounter: 0,
+    quickShowInterval: 250,
+
+    // private
+    initComponent: function() {
+        var me = this;
+        me.callParent(arguments);
+        me.lastActive = new Date();
+        me.setTarget(me.target);
+        me.origAnchor = me.anchor;
+    },
+
+    // private
+    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
+        });
+    },
+
+    // private
+    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);
+    },
+
+    /**
+     * Binds this ToolTip to the specified element. The tooltip will be displayed when the mouse moves over the element.
+     * @param {Mixed} t The Element, HtmlElement, or ID of an element to bind to
+     */
+    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, {
+                // TODO - investigate why IE6/7 seem to fire recursive resize in e.getXY
+                // breaking QuickTip#onTargetOver (EXTJSIV-1608)
+                freezeEvent: true,
+
+                mouseover: me.onTargetOver,
+                mouseout: me.onTargetOut,
+                mousemove: me.onMouseMove,
+                scope: me
+            });
+        }
+        if (me.anchor) {
+            me.anchorTarget = me.target;
+        }
+    },
+
+    // private
+    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();
+        }
+    },
+
+    // private
+    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;
+    },
+
+    // private
+    getAnchorPosition: function() {
+        var me = this,
+            m;
+        if (me.anchor) {
+            me.tipAnchor = me.anchor.charAt(0);
+        } else {
+            m = me.defaultAlign.match(/^([a-z]+)-([a-z]+)(\?)?$/);
+            //<debug>
+            if (!m) {
+                Ext.Error.raise('The AnchorTip.defaultAlign value "' + me.defaultAlign + '" is invalid.');
+            }
+            //</debug>
+            me.tipAnchor = m[1].charAt(0);
+        }
+
+        switch (me.tipAnchor) {
+        case 't':
+            return 'top';
+        case 'b':
+            return 'bottom';
+        case 'r':
+            return 'right';
+        }
+        return 'left';
+    },
+
+    // private
+    getAnchorAlign: function() {
+        switch (this.anchor) {
+        case 'top':
+            return 'tl-bl';
+        case 'left':
+            return 'tl-tr';
+        case 'right':
+            return 'tr-tl';
+        default:
+            return 'bl-tl';
+        }
+    },
+
+    // private
+    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;
+    },
+
+    // private
+    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();
+        }
+    },
+
+    // private
+    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();
+        }
+    },
+
+    // private
+    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();
+        }
+    },
+
+    // private
+    delayHide: function() {
+        var me = this;
+        if (!me.hidden && !me.hideTimer) {
+            me.hideTimer = Ext.defer(me.hide, me.hideDelay, me);
+        }
+    },
+
+    /**
+     * Hides this tooltip if visible.
+     */
+    hide: function() {
+        var me = this;
+        me.clearTimer('dismiss');
+        me.lastActive = new Date();
+        if (me.anchorEl) {
+            me.anchorEl.hide();
+        }
+        me.callParent(arguments);
+        delete me.triggerElement;
+    },
+
+    /**
+     * Shows this tooltip at the current event target XY position.
+     */
+    show: function() {
+        var me = this;
+
+        // Show this Component first, so that sizing can be calculated
+        // pre-show it off screen so that the el will have dimensions
+        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();
+            }
+        }
+    },
+
+    // inherit docs
+    showAt: function(xy) {
+        var me = this;
+        me.lastActive = new Date();
+        me.clearTimers();
+
+        // Only call if this is hidden. May have been called from show above.
+        if (!me.isVisible()) {
+            this.callParent(arguments);
+        }
+
+        // Show may have been vetoed.
+        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();
+        }
+    },
+
+    // private
+    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);
+    },
+
+    // private
+    setPagePosition: function(x, y) {
+        var me = this;
+        me.callParent(arguments);
+        if (me.anchor) {
+            me.syncAnchor();
+        }
+    },
+
+    // private
+    clearTimer: function(name) {
+        name = name + 'Timer';
+        clearTimeout(this[name]);
+        delete this[name];
+    },
+
+    // private
+    clearTimers: function() {
+        var me = this;
+        me.clearTimer('show');
+        me.clearTimer('dismiss');
+        me.clearTimer('hide');
+    },
+
+    // private
+    onShow: function() {
+        var me = this;
+        me.callParent();
+        me.mon(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
+    },
+
+    // private
+    onHide: function() {
+        var me = this;
+        me.callParent();
+        me.mun(Ext.getDoc(), 'mousedown', me.onDocMouseDown, me);
+    },
+
+    // private
+    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);
+        }
+    },
+
+    // private
+    doEnable: function() {
+        if (!this.isDestroyed) {
+            this.enable();
+        }
+    },
+
+    // private
+    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();
+    },
+
+    // private
+    onDestroy: function() {
+        Ext.getDoc().un('mousedown', this.onDocMouseDown, this);
+        this.callParent();
+    }
+});
+
+/**
+ * @class Ext.tip.QuickTip
+ * @extends Ext.tip.ToolTip
+ * A specialized tooltip class for tooltips that can be specified in markup and automatically managed by the global
+ * {@link Ext.tip.QuickTipManager} instance.  See the QuickTipManager class header for additional usage details and examples.
+ * @constructor
+ * Create a new Tip
+ * @param {Object} config The configuration options
+ * @xtype quicktip
+ */
+Ext.define('Ext.tip.QuickTip', {
+    extend: 'Ext.tip.ToolTip',
+    alternateClassName: 'Ext.QuickTip',
+    /**
+     * @cfg {Mixed} target The target HTMLElement, Ext.core.Element or id to associate with this Quicktip (defaults to the document).
+     */
+    /**
+     * @cfg {Boolean} interceptTitles True to automatically use the element's DOM title value if available (defaults to false).
+     */
+    interceptTitles : false,
+
+    // Force creation of header Component
+    title: '&#160;',
+
+    // private
+    tagConfig : {
+        namespace : "data-",
+        attribute : "qtip",
+        width : "qwidth",
+        target : "target",
+        title : "qtitle",
+        hide : "hide",
+        cls : "qclass",
+        align : "qalign",
+        anchor : "anchor"
+    },
+
+    // private
+    initComponent : function(){
+        var me = this;
+        
+        me.target = me.target || Ext.getDoc();
+        me.targets = me.targets || {};
+        me.callParent();
+    },
+
+    /**
+     * Configures a new quick tip instance and assigns it to a target element.  The following config values are
+     * supported (for example usage, see the {@link Ext.tip.QuickTipManager} class header):
+     * <div class="mdetail-params"><ul>
+     * <li>autoHide</li>
+     * <li>cls</li>
+     * <li>dismissDelay (overrides the singleton value)</li>
+     * <li>target (required)</li>
+     * <li>text (required)</li>
+     * <li>title</li>
+     * <li>width</li></ul></div>
+     * @param {Object} config The config object
+     */
+    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;
+                }
+            }
+        }
+    },
+
+    /**
+     * Removes this quick tip from its element and destroys it.
+     * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
+     */
+    unregister : function(el){
+        delete this.targets[Ext.id(el)];
+    },
+    
+    /**
+     * Hides a visible tip or cancels an impending show for a particular element.
+     * @param {String/HTMLElement/Element} el The element that is the target of the tip.
+     */
+    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;
+    },
+
+    // private
+    onTargetOver : function(e){
+        var me = this,
+            target = e.getTarget(),
+            elTarget,
+            cfg,
+            ns,
+            ttp,
+            autoHide;
+        
+        if (me.disabled) {
+            return;
+        }
+
+        // TODO - this causes "e" to be recycled in IE6/7 (EXTJSIV-1608) so ToolTip#setTarget
+        // was changed to include freezeEvent. The issue seems to be a nested 'resize' event
+        // that smashed Ext.EventObject.
+        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();
+        }
+    },
+
+    // private
+    onTargetOut : function(e){
+        var me = this;
+        
+        // If moving within the current target, and it does not have a new tip, ignore the mouseout
+        if (me.activeTarget && e.within(me.activeTarget.el) && !me.getTipCfg(e)) {
+            return;
+        }
+
+        me.clearTimer('show');
+        if (me.autoHide !== false) {
+            me.delayHide();
+        }
+    },
+
+    // inherit docs
+    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) { // TODO: this doesn't seem to work consistently
+                xy = me.el.getAlignToXY(target.el, target.align);
+                me.constrainPosition = false;
+            }else{
+                me.constrainPosition = true;
+            }
+        }
+        me.callParent([xy]);
+    },
+
+    // inherit docs
+    hide: function(){
+        delete this.activeTarget;
+        this.callParent();
+    }
+});
+
+/**
+ * @class Ext.tip.QuickTipManager
+ * <p>Provides attractive and customizable tooltips for any element. The QuickTips
+ * singleton is used to configure and manage tooltips globally for multiple elements
+ * in a generic manner.  To create individual tooltips with maximum customizability,
+ * you should consider either {@link Ext.tip.Tip} or {@link Ext.tip.ToolTip}.</p>
+ * <p>Quicktips can be configured via tag attributes directly in markup, or by
+ * registering quick tips programmatically via the {@link #register} method.</p>
+ * <p>The singleton's instance of {@link Ext.tip.QuickTip} is available via
+ * {@link #getQuickTip}, and supports all the methods, and all the all the
+ * configuration properties of Ext.tip.QuickTip. These settings will apply to all
+ * tooltips shown by the singleton.</p>
+ * <p>Below is the summary of the configuration properties which can be used.
+ * For detailed descriptions see the config options for the {@link Ext.tip.QuickTip QuickTip} class</p>
+ * <p><b>QuickTips singleton configs (all are optional)</b></p>
+ * <div class="mdetail-params"><ul><li>dismissDelay</li>
+ * <li>hideDelay</li>
+ * <li>maxWidth</li>
+ * <li>minWidth</li>
+ * <li>showDelay</li>
+ * <li>trackMouse</li></ul></div>
+ * <p><b>Target element configs (optional unless otherwise noted)</b></p>
+ * <div class="mdetail-params"><ul><li>autoHide</li>
+ * <li>cls</li>
+ * <li>dismissDelay (overrides singleton value)</li>
+ * <li>target (required)</li>
+ * <li>text (required)</li>
+ * <li>title</li>
+ * <li>width</li></ul></div>
+ * <p>Here is an example showing how some of these config options could be used:</p>
+ *
+ * {@img Ext.tip.QuickTipManager/Ext.tip.QuickTipManager.png Ext.tip.QuickTipManager component}
+ *
+ * ## Code
+ *    // Init the singleton.  Any tag-based quick tips will start working.
+ *    Ext.tip.QuickTipManager.init();
+ *    
+ *    // Apply a set of config properties to the singleton
+ *    Ext.apply(Ext.tip.QuickTipManager.getQuickTip(), {
+ *        maxWidth: 200,
+ *        minWidth: 100,
+ *        showDelay: 50      // Show 50ms after entering target
+ *    });
+ *    
+ *    // Create a small panel to add a quick tip to
+ *    Ext.create('Ext.container.Container', {
+ *        id: 'quickTipContainer',
+ *        width: 200,
+ *        height: 150,
+ *        style: {
+ *            backgroundColor:'#000000'
+ *        },
+ *        renderTo: Ext.getBody()
+ *    });
+ *
+ *    
+ *    // Manually register a quick tip for a specific element
+ *    Ext.tip.QuickTipManager.register({
+ *        target: 'quickTipContainer',
+ *        title: 'My Tooltip',
+ *        text: 'This tooltip was added in code',
+ *        width: 100,
+ *        dismissDelay: 10000 // Hide after 10 seconds hover
+ *    });
+</code></pre>
+ * <p>To register a quick tip in markup, you simply add one or more of the valid QuickTip attributes prefixed with
+ * the <b>ext:</b> namespace.  The HTML element itself is automatically set as the quick tip target. Here is the summary
+ * of supported attributes (optional unless otherwise noted):</p>
+ * <ul><li><b>hide</b>: Specifying "user" is equivalent to setting autoHide = false.  Any other value will be the
+ * same as autoHide = true.</li>
+ * <li><b>qclass</b>: A CSS class to be applied to the quick tip (equivalent to the 'cls' target element config).</li>
+ * <li><b>qtip (required)</b>: The quick tip text (equivalent to the 'text' target element config).</li>
+ * <li><b>qtitle</b>: The quick tip title (equivalent to the 'title' target element config).</li>
+ * <li><b>qwidth</b>: The quick tip width (equivalent to the 'width' target element config).</li></ul>
+ * <p>Here is an example of configuring an HTML element to display a tooltip from markup:</p>
+ * <pre><code>
+// Add a quick tip to an HTML button
+&lt;input type="button" value="OK" ext:qtitle="OK Button" ext:qwidth="100"
+     data-qtip="This is a quick tip from markup!">&lt;/input>
+</code></pre>
+ * @singleton
+ */
+Ext.define('Ext.tip.QuickTipManager', function() {
+    var tip,
+        disabled = false;
+
+    return {
+        requires: ['Ext.tip.QuickTip'],
+        singleton: true,
+        alternateClassName: 'Ext.QuickTips',
+        /**
+         * Initialize the global QuickTips instance and prepare any quick tips.
+         * @param {Boolean} autoRender True to render the QuickTips container immediately to preload images. (Defaults to true) 
+         */
+        init : function(autoRender){
+            if (!tip) {
+                if (!Ext.isReady) {
+                    Ext.onReady(function(){
+                        Ext.tip.QuickTipManager.init(autoRender);
+                    });
+                    return;
+                }
+                tip = Ext.create('Ext.tip.QuickTip', {
+                    disabled: disabled,
+                    renderTo: autoRender !== false ? document.body : undefined
+                });
+            }
+        },
+
+        /**
+         * Destroy the QuickTips instance.
+         */
+        destroy: function() {
+            if (tip) {
+                var undef;
+                tip.destroy();
+                tip = undef;
+            }
+        },
+
+        // Protected method called by the dd classes
+        ddDisable : function(){
+            // don't disable it if we don't need to
+            if(tip && !disabled){
+                tip.disable();
+            }
+        },
+
+        // Protected method called by the dd classes
+        ddEnable : function(){
+            // only enable it if it hasn't been disabled
+            if(tip && !disabled){
+                tip.enable();
+            }
+        },
+
+        /**
+         * Enable quick tips globally.
+         */
+        enable : function(){
+            if(tip){
+                tip.enable();
+            }
+            disabled = false;
+        },
+
+        /**
+         * Disable quick tips globally.
+         */
+        disable : function(){
+            if(tip){
+                tip.disable();
+            }
+            disabled = true;
+        },
+
+        /**
+         * Returns true if quick tips are enabled, else false.
+         * @return {Boolean}
+         */
+        isEnabled : function(){
+            return tip !== undefined && !tip.disabled;
+        },
+
+        /**
+         * Gets the single {@link Ext.tip.QuickTip QuickTip} instance used to show tips from all registered elements.
+         * @return {Ext.tip.QuickTip}
+         */
+        getQuickTip : function(){
+            return tip;
+        },
+
+        /**
+         * Configures a new quick tip instance and assigns it to a target element.  See
+         * {@link Ext.tip.QuickTip#register} for details.
+         * @param {Object} config The config object
+         */
+        register : function(){
+            tip.register.apply(tip, arguments);
+        },
+
+        /**
+         * Removes any registered quick tip from the target element and destroys it.
+         * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
+         */
+        unregister : function(){
+            tip.unregister.apply(tip, arguments);
+        },
+
+        /**
+         * Alias of {@link #register}.
+         * @param {Object} config The config object
+         */
+        tips : function(){
+            tip.register.apply(tip, arguments);
+        }
+    };
+}());
+/**
+ * @class Ext.app.Application
+ * @constructor
+ * 
+ * Represents an Ext JS 4 application, which is typically a single page app using a {@link Ext.container.Viewport Viewport}.
+ * A typical Ext.app.Application might look like this:
+ * 
+ * Ext.application({
+     name: 'MyApp',
+     launch: function() {
+         Ext.create('Ext.container.Viewport', {
+             items: {
+                 html: 'My App'
+             }
+         });
+     }
+ });
+ * 
+ * This does several things. First it creates a global variable called 'MyApp' - all of your Application's classes (such
+ * as its Models, Views and Controllers) will reside under this single namespace, which drastically lowers the chances
+ * of colliding global variables.
+ * 
+ * When the page is ready and all of your JavaScript has loaded, your Application's {@link #launch} function is called,
+ * at which time you can run the code that starts your app. Usually this consists of creating a Viewport, as we do in
+ * the example above.
+ * 
+ * <u>Telling Application about the rest of the app</u>
+ * 
+ * Because an Ext.app.Application represents an entire app, we should tell it about the other parts of the app - namely
+ * the Models, Views and Controllers that are bundled with the application. Let's say we have a blog management app; we
+ * might have Models and Controllers for Posts and Comments, and Views for listing, adding and editing Posts and Comments.
+ * Here's how we'd tell our Application about all these things:
+ * 
+ * Ext.application({
+     name: 'Blog',
+     models: ['Post', 'Comment'],
+     controllers: ['Posts', 'Comments'],
+
+     launch: function() {
+         ...
+     }
+ });
+ * 
+ * Note that we didn't actually list the Views directly in the Application itself. This is because Views are managed by
+ * Controllers, so it makes sense to keep those dependencies there. The Application will load each of the specified 
+ * Controllers using the pathing conventions laid out in the <a href="../guide/application_architecture">application 
+ * architecture guide</a> - in this case expecting the controllers to reside in app/controller/Posts.js and
+ * app/controller/Comments.js. In turn, each Controller simply needs to list the Views it uses and they will be
+ * automatically loaded. Here's how our Posts controller like be defined:
+ * 
+ * Ext.define('MyApp.controller.Posts', {
+     extend: 'Ext.app.Controller',
+     views: ['posts.List', 'posts.Edit'],
+
+     //the rest of the Controller here
+ });
+ * 
+ * Because we told our Application about our Models and Controllers, and our Controllers about their Views, Ext JS will
+ * automatically load all of our app files for us. This means we don't have to manually add script tags into our html
+ * files whenever we add a new class, but more importantly it enables us to create a minimized build of our entire 
+ * application using the Ext JS 4 SDK Tools.
+ * 
+ * For more information about writing Ext JS 4 applications, please see the <a href="../guide/application_architecture">
+ * application architecture guide</a>.
+ * 
+ * @markdown
+ * @docauthor Ed Spencer
+ */
+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'
+    ],
+
+    /**
+     * @cfg {Object} name The name of your application. This will also be the namespace for your views, controllers
+     * models and stores. Don't use spaces or special characters in the name.
+     */
+
+    /**
+     * @cfg {Object} scope The scope to execute the {@link #launch} function in. Defaults to the Application
+     * instance.
+     */
+    scope: undefined,
+
+    /**
+     * @cfg {Boolean} enableQuickTips True to automatically set up Ext.tip.QuickTip support (defaults to true)
+     */
+    enableQuickTips: true,
+
+    /**
+     * @cfg {String} defaultUrl When the app is first loaded, this url will be redirected to. Defaults to undefined
+     */
+
+    /**
+     * @cfg {String} appFolder The path to the directory which contains all application's classes.
+     * This path will be registered via {@link Ext.Loader#setPath} for the namespace specified in the {@link #name name} config.
+     * Defaults to 'app'
+     */
+    appFolder: 'app',
+
+    /**
+     * @cfg {Boolean} autoCreateViewport Automatically loads and instantiates AppName.view.Viewport before firing the launch function.
+     */
+    autoCreateViewport: true,
+
+    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 = this.controllers,
+            ln = 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);
+    },
+
+    /**
+     * Called automatically when the page has completely loaded. This is an empty function that should be
+     * overridden by each application that needs to take action on page load
+     * @property launch
+     * @type Function
+     * @param {String} profile The detected {@link #profiles application profile}
+     * @return {Boolean} By default, the Application will dispatch to the configured startup controller and
+     * action immediately after running the launch function. Return false to prevent this behavior.
+     */
+    launch: Ext.emptyFn,
+
+    /**
+     * @private
+     */
+    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);
+    }
+});
+
+/**
+ * @class Ext.chart.Callout
+ * @ignore
+ */
+Ext.define('Ext.chart.Callout', {
+
+    /* Begin Definitions */
+
+    /* End Definitions */
+
+    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 = {
+                //label: false,
+                //box: false,
+                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);
+                }
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.draw.CompositeSprite
+ * @extends Ext.util.MixedCollection
+ *
+ * A composite Sprite handles a group of sprites with common methods to a sprite
+ * such as `hide`, `show`, `setAttributes`. These methods are applied to the set of sprites
+ * added to the group.
+ *
+ * CompositeSprite extends {@link Ext.util.MixedCollection} so you can use the same methods
+ * in `MixedCollection` to iterate through sprites, add and remove elements, etc.
+ *
+ * In order to create a CompositeSprite, one has to provide a handle to the surface where it is
+ * rendered:
+ *
+ *     var group = Ext.create('Ext.draw.CompositeSprite', {
+ *         surface: drawComponent.surface
+ *     });
+ *                  
+ * Then just by using `MixedCollection` methods it's possible to add {@link Ext.draw.Sprite}s:
+ *  
+ *     group.add(sprite1);
+ *     group.add(sprite2);
+ *     group.add(sprite3);
+ *                  
+ * And then apply common Sprite methods to them:
+ *  
+ *     group.setAttributes({
+ *         fill: '#f00'
+ *     }, true);
+ */
+Ext.define('Ext.draw.CompositeSprite', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.util.MixedCollection',
+    mixins: {
+        animate: 'Ext.util.Animate'
+    },
+
+    /* End Definitions */
+    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();
+    },
+
+    // @private
+    onClick: function(e) {
+        this.fireEvent('click', e);
+    },
+
+    // @private
+    onMouseUp: function(e) {
+        this.fireEvent('mouseup', e);
+    },
+
+    // @private
+    onMouseDown: function(e) {
+        this.fireEvent('mousedown', e);
+    },
+
+    // @private
+    onMouseOver: function(e) {
+        this.fireEvent('mouseover', e);
+    },
+
+    // @private
+    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 a Sprite to the Group */
+    add: function(key, o) {
+        var result = this.callParent(arguments);
+        this.attachEvents(result);
+        return result;
+    },
+
+    insert: function(index, key, o) {
+        return this.callParent(arguments);
+    },
+
+    /** Remove a Sprite from the Group */
+    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);
+    },
+    
+    /**
+     * Returns the group bounding box.
+     * Behaves like {@link Ext.draw.Sprite} getBBox method.
+    */
+    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
+        };
+    },
+
+    /**
+     *  Iterates through all sprites calling
+     *  `setAttributes` on each one. For more information
+     *  {@link Ext.draw.Sprite} provides a description of the
+     *  attributes that can be set with this method.
+     */
+    setAttributes: function(attrs, redraw) {
+        var i = 0,
+            items = this.items,
+            len = this.length;
+            
+        for (; i < len; i++) {
+            items[i].setAttributes(attrs, redraw);
+        }
+        return this;
+    },
+
+    /**
+     * Hides all sprites. If the first parameter of the method is true
+     * then a redraw will be forced for each sprite.
+     */
+    hide: function(attrs) {
+        var i = 0,
+            items = this.items,
+            len = this.length;
+            
+        for (; i < len; i++) {
+            items[i].hide();
+        }
+        return this;
+    },
+
+    /**
+     * Shows all sprites. If the first parameter of the method is true
+     * then a redraw will be forced for each sprite.
+     */
+    show: function(attrs) {
+        var i = 0,
+            items = this.items,
+            len = this.length;
+            
+        for (; i < len; i++) {
+            items[i].show();
+        }
+        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);
+            }
+        }
+    },
+    
+    /**
+     * Grab the surface from the items
+     * @private
+     * @return {Ext.draw.Surface} The surface, null if not found
+     */
+    getSurface: function(){
+        var first = this.first();
+        if (first) {
+            return first.surface;
+        }
+        return null;
+    },
+    
+    /**
+     * Destroys the SpriteGroup
+     */
+    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();
+    }
+});
+
+/**
+ * @class Ext.layout.component.Draw
+ * @extends Ext.layout.component.Component
+ * @private
+ *
+ */
+
+Ext.define('Ext.layout.component.Draw', {
+
+    /* Begin Definitions */
+
+    alias: 'layout.draw',
+
+    extend: 'Ext.layout.component.Auto',
+
+    /* End Definitions */
+
+    type: 'draw',
+
+    onLayout : function(width, height) {
+        this.owner.surface.setSize(width, height);
+        this.callParent(arguments);
+    }
+});
+/**
+ * @class Ext.chart.theme.Theme
+ * @ignore
+ */
+Ext.define('Ext.chart.theme.Theme', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.draw.Color'],
+
+    /* End Definitions */
+
+    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;
+                }
+            }
+            //<debug>
+            Ext.Error.raise('No theme found named "' + theme + '"');
+            //</debug>
+        }
+    }
+}, 
+// This callback is executed right after when the class is created. This scope refers to the newly created class itself
+function() {
+   /* Theme constructor: takes either a complex object with styles like:
+  
+   {
+        axis: {
+            fill: '#000',
+            'stroke-width': 1
+        },
+        axisLabelTop: {
+            fill: '#000',
+            font: '11px Arial'
+        },
+        axisLabelLeft: {
+            fill: '#000',
+            font: '11px Arial'
+        },
+        axisLabelRight: {
+            fill: '#000',
+            font: '11px Arial'
+        },
+        axisLabelBottom: {
+            fill: '#000',
+            font: '11px Arial'
+        },
+        axisTitleTop: {
+            fill: '#000',
+            font: '11px Arial'
+        },
+        axisTitleLeft: {
+            fill: '#000',
+            font: '11px Arial'
+        },
+        axisTitleRight: {
+            fill: '#000',
+            font: '11px Arial'
+        },
+        axisTitleBottom: {
+            fill: '#000',
+            font: '11px Arial'
+        },
+        series: {
+            'stroke-width': 1
+        },
+        seriesLabel: {
+            font: '12px Arial',
+            fill: '#333'
+        },
+        marker: {
+            stroke: '#555',
+            fill: '#000',
+            radius: 3,
+            size: 3
+        },
+        seriesThemes: [{
+            fill: '#C6DBEF'
+        }, {
+            fill: '#9ECAE1'
+        }, {
+            fill: '#6BAED6'
+        }, {
+            fill: '#4292C6'
+        }, {
+            fill: '#2171B5'
+        }, {
+            fill: '#084594'
+        }],
+        markerThemes: [{
+            fill: '#084594',
+            type: 'circle' 
+        }, {
+            fill: '#2171B5',
+            type: 'cross'
+        }, {
+            fill: '#4292C6',
+            type: 'plus'
+        }]
+    }
+  
+  ...or also takes just an array of colors and creates the complex object:
+  
+  {
+      colors: ['#aaa', '#bcd', '#eee']
+  }
+  
+  ...or takes just a base color and makes a theme from it
+  
+  {
+      baseColor: '#bce'
+  }
+  
+  To create a new theme you may add it to the Themes object:
+  
+  Ext.chart.theme.MyNewTheme = Ext.extend(Object, {
+      constructor: function(config) {
+          Ext.chart.theme.call(this, config, {
+              baseColor: '#mybasecolor'
+          });
+      }
+  });
+  
+  //Proposal:
+  Ext.chart.theme.MyNewTheme = Ext.chart.createTheme('#basecolor');
+  
+  ...and then to use it provide the name of the theme (as a lower case string) in the chart config.
+  
+  {
+      theme: 'mynewtheme'
+  }
+ */
+
+(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);
+        //the user is configuring something in particular (either markers, series or pie slices)
+        }
+        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;
+        }
+        /*
+        base.axis = Ext.apply(base.axis || {}, config.axis || {});
+        base.axisLabel = Ext.apply(base.axisLabel || {}, config.axisLabel || {});
+        base.axisTitle = Ext.apply(base.axisTitle || {}, config.axisTitle || {});
+        */
+        Ext.apply(this, base);
+    };
+})();
+});
+
+/**
+ * @class Ext.chart.Mask
+ *
+ * Defines a mask for a chart's series.
+ * The 'chart' member must be set prior to rendering.
+ *
+ * A Mask can be used to select a certain region in a chart.
+ * When enabled, the `select` event will be triggered when a
+ * region is selected by the mask, allowing the user to perform
+ * other tasks like zooming on that region, etc.
+ *
+ * In order to use the mask one has to set the Chart `mask` option to
+ * `true`, `vertical` or `horizontal`. Then a possible configuration for the
+ * listener could be:
+ *
+        items: {
+            xtype: 'chart',
+            animate: true,
+            store: store1,
+            mask: 'horizontal',
+            listeners: {
+                select: {
+                    fn: function(me, selection) {
+                        me.setZoom(selection);
+                        me.mask.hide();
+                    }
+                }
+            },
+
+ * In this example we zoom the chart to that particular region. You can also get
+ * a handle to a mask instance from the chart object. The `chart.mask` element is a
+ * `Ext.Panel`.
+ * 
+ * @constructor
+ */
+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() {
+                //create a mask layer component
+                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);
+                    }
+                });
+                //create a resize handler for the component
+                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);
+    }
+});
+    
+/**
+ * @class Ext.chart.Navigation
+ *
+ * Handles panning and zooming capabilities.
+ * 
+ * @ignore
+ */
+Ext.define('Ext.chart.Navigation', {
+
+    constructor: function() {
+        this.originalStore = this.store;
+    },
+    
+    //filters the store to the specified interval(s)
+    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 = {};
+            //get all record field names in a simple array
+            if (!recFields.length) {
+                rec.fields.each(function(f) {
+                    recFields.push(f.name);
+                });
+                recFieldsLen = recFields.length;
+            }
+            //append record values to an aggregation record
+            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);
+    }
+    
+});
+/**
+ * @class Ext.chart.Shape
+ * @ignore
+ */
+Ext.define('Ext.chart.Shape', {
+
+    /* Begin Definitions */
+
+    singleton: true,
+
+    /* End Definitions */
+
+    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, // Shift here, Not sure why.
+            y: y + size * Math.cos(angle) + 5,
+            text:  text,
+            'font-size': size * 12 / 40,
+            stroke: 'none',
+            fill: '#fff'
+        });
+    }
+});
+/**
+ * @class Ext.draw.Surface
+ * @extends Object
+ *
+ * A Surface is an interface to render methods inside a draw {@link Ext.draw.Component}.
+ * A Surface contains methods to render sprites, get bounding boxes of sprites, add
+ * sprites to the canvas, initialize other graphic components, etc. One of the most used
+ * methods for this class is the `add` method, to add Sprites to the surface.
+ *
+ * Most of the Surface methods are abstract and they have a concrete implementation
+ * in VML or SVG engines.
+ *
+ * A Surface instance can be accessed as a property of a draw component. For example:
+ *
+ *     drawComponent.surface.add({
+ *         type: 'circle',
+ *         fill: '#ffc',
+ *         radius: 100,
+ *         x: 100,
+ *         y: 100
+ *     });
+ *
+ * The configuration object passed in the `add` method is the same as described in the {@link Ext.draw.Sprite}
+ * class documentation.
+ *
+ * ### Listeners
+ *
+ * You can also add event listeners to the surface using the `Observable` listener syntax. Supported events are:
+ *
+ * - mousedown
+ * - mouseup
+ * - mouseover
+ * - mouseout
+ * - mousemove
+ * - mouseenter
+ * - mouseleave
+ * - click
+ *
+ * For example:
+ *
+        drawComponent.surface.on({
+           'mousemove': function() {
+                console.log('moving the mouse over the surface');   
+            }
+        });
+ */
+Ext.define('Ext.draw.Surface', {
+
+    /* Begin Definitions */
+
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+
+    requires: ['Ext.draw.CompositeSprite'],
+    uses: ['Ext.draw.engine.Svg', 'Ext.draw.engine.Vml'],
+
+    separatorRe: /[, ]+/,
+
+    statics: {
+        /**
+         * Create and return a new concrete Surface instance appropriate for the current environment.
+         * @param {Object} config Initial configuration for the Surface instance
+         * @param {Array} enginePriority Optional order of implementations to use; the first one that is
+         *                available in the current environment will be used. Defaults to
+         *                <code>['Svg', 'Vml']</code>.
+         */
+        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;
+        }
+    },
+
+    /* End Definitions */
+
+    // @private
+    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
+    },
+
+ /**
+  * @cfg {Number} height
+  * The height of this component in pixels (defaults to auto).
+  * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
+  */
+ /**
+  * @cfg {Number} width
+  * The width of this component in pixels (defaults to auto).
+  * <b>Note</b> to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
+  */
+    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);
+    },
+
+    // @private called to initialize components in the surface
+    // this is dependent on the underlying implementation.
+    initSurface: Ext.emptyFn,
+
+    // @private called to setup the surface to render an item
+    //this is dependent on the underlying implementation.
+    renderItem: Ext.emptyFn,
+
+    // @private
+    renderItems: Ext.emptyFn,
+
+    // @private
+    setViewBox: Ext.emptyFn,
+
+    /**
+     * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
+     *
+     * For example:
+     *
+     *          drawComponent.surface.addCls(sprite, 'x-visible');
+     *      
+     * @param {Object} sprite The sprite to add the class to.
+     * @param {String/Array} className The CSS class to add, or an array of classes
+     */
+    addCls: Ext.emptyFn,
+
+    /**
+     * Removes one or more CSS classes from the element.
+     *
+     * For example:
+     *
+     *      drawComponent.surface.removeCls(sprite, 'x-visible');
+     *      
+     * @param {Object} sprite The sprite to remove the class from.
+     * @param {String/Array} className The CSS class to remove, or an array of classes
+     */
+    removeCls: Ext.emptyFn,
+
+    /**
+     * Sets CSS style attributes to an element.
+     *
+     * For example:
+     *
+     *      drawComponent.surface.setStyle(sprite, {
+     *          'cursor': 'pointer'
+     *      });
+     *      
+     * @param {Object} sprite The sprite to add, or an array of classes to
+     * @param {Object} styles An Object with CSS styles.
+     */
+    setStyle: Ext.emptyFn,
+
+    // @private
+    initGradients: function() {
+        var gradients = this.gradients;
+        if (gradients) {
+            Ext.each(gradients, this.addGradient, this);
+        }
+    },
+
+    // @private
+    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);
+        }
+    },
+    
+    // @private
+    initBackground: function(config) {
+        var gradientId, 
+            gradient,
+            backgroundSprite,
+            width = this.width,
+            height = this.height;
+        if (config) {
+            if (config.gradient) {
+                gradient = config.gradient;
+                gradientId = gradient.id;
+                this.addGradient(gradient);
+                this.background = this.add({
+                    type: 'rect',
+                    x: 0,
+                    y: 0,
+                    width: width,
+                    height: height,
+                    fill: 'url(#' + gradientId + ')'
+                });
+            } else if (config.fill) {
+                this.background = this.add({
+                    type: 'rect',
+                    x: 0,
+                    y: 0,
+                    width: width,
+                    height: height,
+                    fill: config.fill
+                });
+            } else if (config.image) {
+                this.background = this.add({
+                    type: 'image',
+                    x: 0,
+                    y: 0,
+                    width: width,
+                    height: height,
+                    src: config.image
+                });
+            }
+        }
+    },
+    
+    /**
+     * Sets the size of the surface. Accomodates the background (if any) to fit the new size too.
+     *
+     * For example:
+     *
+     *      drawComponent.surface.setSize(500, 500);
+     *
+     * This method is generally called when also setting the size of the draw Component.
+     * 
+     * @param {Number} w The new width of the canvas.
+     * @param {Number} h The new height of the canvas.
+     */
+    setSize: function(w, h) {
+        if (this.background) {
+            this.background.setAttributes({
+                width: w,
+                height: h,
+                hidden: false
+            }, true);
+        }
+    },
+
+    // @private
+    scrubAttrs: function(sprite) {
+        var i,
+            attrs = {},
+            exclude = {},
+            sattr = sprite.attr;
+        for (i in sattr) {    
+            // Narrow down attributes to the main set
+            if (this.translateAttrs.hasOwnProperty(i)) {
+                // Translated attr
+                attrs[this.translateAttrs[i]] = sattr[i];
+                exclude[this.translateAttrs[i]] = true;
+            }
+            else if (this.availableAttrs.hasOwnProperty(i) && !exclude[i]) {
+                // Passtrhough attr
+                attrs[i] = sattr[i];
+            }
+        }
+        return attrs;
+    },
+
+    // @private
+    onClick: function(e) {
+        this.processEvent('click', e);
+    },
+
+    // @private
+    onMouseUp: function(e) {
+        this.processEvent('mouseup', e);
+    },
+
+    // @private
+    onMouseDown: function(e) {
+        this.processEvent('mousedown', e);
+    },
+
+    // @private
+    onMouseOver: function(e) {
+        this.processEvent('mouseover', e);
+    },
+
+    // @private
+    onMouseOut: function(e) {
+        this.processEvent('mouseout', e);
+    },
+
+    // @private
+    onMouseMove: function(e) {
+        this.fireEvent('mousemove', e);
+    },
+
+    // @private
+    onMouseEnter: Ext.emptyFn,
+
+    // @private
+    onMouseLeave: Ext.emptyFn,
+
+    /**
+     * Add a gradient definition to the Surface. Note that in some surface engines, adding
+     * a gradient via this method will not take effect if the surface has already been rendered.
+     * Therefore, it is preferred to pass the gradients as an item to the surface config, rather
+     * than calling this method, especially if the surface is rendered immediately (e.g. due to
+     * 'renderTo' in its config). For more information on how to create gradients in the Chart
+     * configuration object please refer to {@link Ext.chart.Chart}.
+     *
+     * The gradient object to be passed into this method is composed by:
+     * 
+     * 
+     *  - **id** - string - The unique name of the gradient.
+     *  - **angle** - number, optional - The angle of the gradient in degrees.
+     *  - **stops** - object - An object with numbers as keys (from 0 to 100) and style objects as values.
+     * 
+     *
+     For example:
+                drawComponent.surface.addGradient({
+                    id: 'gradientId',
+                    angle: 45,
+                    stops: {
+                        0: {
+                            color: '#555'
+                        },
+                        100: {
+                            color: '#ddd'
+                        }
+                    }
+                });
+     */
+    addGradient: Ext.emptyFn,
+
+    /**
+     * Add a Sprite to the surface. See {@link Ext.draw.Sprite} for the configuration object to be passed into this method.
+     *
+     * For example:
+     *
+     *     drawComponent.surface.add({
+     *         type: 'circle',
+     *         fill: '#ffc',
+     *         radius: 100,
+     *         x: 100,
+     *         y: 100
+     *     });
+     *
+    */
+    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;
+    },
+
+    /**
+     * @private
+     * Insert or move a given sprite into the correct position in the items
+     * MixedCollection, according to its zIndex. Will be inserted at the end of
+     * an existing series of sprites with the same or lower zIndex. If the sprite
+     * is already positioned within an appropriate zIndex group, it will not be moved.
+     * This ordering can be used by subclasses to assist in rendering the sprites in
+     * the correct order for proper z-index stacking.
+     * @param {Ext.draw.Sprite} sprite
+     * @return {Number} the sprite's new index in the list
+     */
+    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 a given sprite from the surface, optionally destroying the sprite in the process.
+     * You can also call the sprite own `remove` method.
+     *
+     * For example:
+     *
+     *      drawComponent.surface.remove(sprite);
+     *      //or...
+     *      sprite.remove();
+     *      
+     * @param {Ext.draw.Sprite} sprite
+     * @param {Boolean} destroySprite
+     * @return {Number} the sprite's new index in the list
+     */
+    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();
+            }
+        }
+    },
+
+    /**
+     * Remove all sprites from the surface, optionally destroying the sprites in the process.
+     *
+     * For example:
+     *
+     *      drawComponent.surface.removeAll();
+     *      
+     * @param {Boolean} destroySprites Whether to destroy all sprites when removing them.
+     * @return {Number} The sprite's new index in the list.
+     */
+    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,
+
+    // @private
+    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 = [];
+        }
+    },
+
+    // @private
+    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
+        });
+    },
+
+    // @private
+    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
+        });
+    },
+
+    // @private
+    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
+        });
+    },
+
+    // @private
+    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"]];
+    },
+
+    // @private
+    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"]];
+    },
+
+    // @private
+    getPathpath: function (el) {
+        return el.attr.path;
+    },
+
+    // @private
+    getPathcircle: function (el) {
+        var a = el.attr;
+        return this.ellipsePath(a.x, a.y, a.radius, a.radius);
+    },
+
+    // @private
+    getPathellipse: function (el) {
+        var a = el.attr;
+        return this.ellipsePath(a.x, a.y, a.radiusX, a.radiusY);
+    },
+
+    // @private
+    getPathrect: function (el) {
+        var a = el.attr;
+        return this.rectPath(a.x, a.y, a.width, a.height, a.r);
+    },
+
+    // @private
+    getPathimage: function (el) {
+        var a = el.attr;
+        return this.rectPath(a.x || 0, a.y || 0, a.width, a.height);
+    },
+
+    // @private
+    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;
+    },
+
+    /**
+     * Returns a new group or an existent group associated with the current surface.
+     * The group returned is a {@link Ext.draw.CompositeSprite} group.
+     *
+     * For example:
+     *
+     *      var spriteGroup = drawComponent.surface.getGroup('someGroupId');
+     *      
+     * @param {String} id The unique identifier of the group.
+     * @return {Object} The {@link Ext.draw.CompositeSprite}.
+     */
+    getGroup: function(id) {
+        if (typeof id == "string") {
+            var group = this.groups.get(id);
+            if (!group) {
+                group = this.createGroup(id);
+            }
+        } else {
+            group = id;
+        }
+        return group;
+    },
+
+    // @private
+    prepareItems: function(items, applyDefaults) {
+        items = [].concat(items);
+        // Make sure defaults are applied and item is initialized
+        var item, i, ln;
+        for (i = 0, ln = items.length; i < ln; i++) {
+            item = items[i];
+            if (!(item instanceof Ext.draw.Sprite)) {
+                // Temporary, just take in configs...
+                item.surface = this;
+                items[i] = this.createItem(item);
+            } else {
+                item.surface = this;
+            }
+        }
+        return items;
+    },
+    
+    /**
+     * Changes the text in the sprite element. The sprite must be a `text` sprite.
+     * This method can also be called from {@link Ext.draw.Sprite}.
+     *
+     * For example:
+     *
+     *      var spriteGroup = drawComponent.surface.setText(sprite, 'my new text');
+     *      
+     * @param {Object} sprite The Sprite to change the text.
+     * @param {String} text The new text to be set.
+     */
+    setText: Ext.emptyFn,
+    
+    //@private Creates an item and appends it to the surface. Called
+    //as an internal method when calling `add`.
+    createItem: Ext.emptyFn,
+
+    /**
+     * Retrieves the id of this component.
+     * Will autogenerate an id if one has not already been set.
+     */
+    getId: function() {
+        return this.id || (this.id = Ext.id(null, 'ext-surface-'));
+    },
+
+    /**
+     * Destroys the surface. This is done by removing all components from it and
+     * also removing its reference to a DOM element.
+     *
+     * For example:
+     *
+     *      drawComponent.surface.destroy();
+     */
+    destroy: function() {
+        delete this.domRef;
+        this.removeAll();
+    }
+});
+/**
+ * @class Ext.draw.Component
+ * @extends Ext.Component
+ *
+ * The Draw Component is a surface in which sprites can be rendered. The Draw Component
+ * manages and holds a `Surface` instance: an interface that has
+ * an SVG or VML implementation depending on the browser capabilities and where
+ * Sprites can be appended.
+ * {@img Ext.draw.Component/Ext.draw.Component.png Ext.draw.Component component}
+ * One way to create a draw component is:
+ * 
+ *     var drawComponent = Ext.create('Ext.draw.Component', {
+ *         viewBox: false,
+ *         items: [{
+ *             type: 'circle',
+ *             fill: '#79BB3F',
+ *             radius: 100,
+ *             x: 100,
+ *             y: 100
+ *         }]
+ *     });
+ *   
+ *     Ext.create('Ext.Window', {
+ *         width: 215,
+ *         height: 235,
+ *         layout: 'fit',
+ *         items: [drawComponent]
+ *     }).show();
+ * 
+ * In this case we created a draw component and added a sprite to it.
+ * The *type* of the sprite is *circle* so if you run this code you'll see a yellow-ish
+ * circle in a Window. When setting `viewBox` to `false` we are responsible for setting the object's position and
+ * dimensions accordingly. 
+ * 
+ * You can also add sprites by using the surface's add method:
+ *    
+ *     drawComponent.surface.add({
+ *         type: 'circle',
+ *         fill: '#79BB3F',
+ *         radius: 100,
+ *         x: 100,
+ *         y: 100
+ *     });
+ *  
+ * For more information on Sprites, the core elements added to a draw component's surface,
+ * refer to the Ext.draw.Sprite documentation.
+ */
+Ext.define('Ext.draw.Component', {
+
+    /* Begin Definitions */
+
+    alias: 'widget.draw',
+
+    extend: 'Ext.Component',
+
+    requires: [
+        'Ext.draw.Surface',
+        'Ext.layout.component.Draw'
+    ],
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Array} enginePriority
+     * Defines the priority order for which Surface implementation to use. The first
+     * one supported by the current environment will be used.
+     */
+    enginePriority: ['Svg', 'Vml'],
+
+    baseCls: Ext.baseCSSPrefix + 'surface',
+
+    componentLayout: 'draw',
+
+    /**
+     * @cfg {Boolean} viewBox
+     * Turn on view box support which will scale and position items in the draw component to fit to the component while
+     * maintaining aspect ratio. Note that this scaling can override other sizing settings on yor items. Defaults to true.
+     */
+    viewBox: true,
+
+    /**
+     * @cfg {Boolean} autoSize
+     * Turn on autoSize support which will set the bounding div's size to the natural size of the contents. Defaults to false.
+     */
+    autoSize: false,
+    
+    /**
+     * @cfg {Array} gradients (optional) Define a set of gradients that can be used as `fill` property in sprites.
+     * The gradients array is an array of objects with the following properties:
+     *
+     * <ul>
+     * <li><strong>id</strong> - string - The unique name of the gradient.</li>
+     * <li><strong>angle</strong> - number, optional - The angle of the gradient in degrees.</li>
+     * <li><strong>stops</strong> - object - An object with numbers as keys (from 0 to 100) and style objects
+     * as values</li>
+     * </ul>
+     * 
+     
+     For example:
+     
+     <pre><code>
+        gradients: [{
+            id: 'gradientId',
+            angle: 45,
+            stops: {
+                0: {
+                    color: '#555'
+                },
+                100: {
+                    color: '#ddd'
+                }
+            }
+        },  {
+            id: 'gradientId2',
+            angle: 0,
+            stops: {
+                0: {
+                    color: '#590'
+                },
+                20: {
+                    color: '#599'
+                },
+                100: {
+                    color: '#ddd'
+                }
+            }
+        }]
+     </code></pre>
+     
+     Then the sprites can use `gradientId` and `gradientId2` by setting the fill attributes to those ids, for example:
+     
+     <pre><code>
+        sprite.setAttributes({
+            fill: 'url(#gradientId)'
+        }, true);
+     </code></pre>
+     
+     */
+
+    initComponent: function() {
+        this.callParent(arguments);
+
+        this.addEvents(
+            'mousedown',
+            'mouseup',
+            'mousemove',
+            'mouseenter',
+            'mouseleave',
+            'click'
+        );
+    },
+
+    /**
+     * @private
+     *
+     * Create the Surface on initial render
+     */
+    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 {
+                // AutoSized
+                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,
+                //Opera has a slight offset in the y axis.
+                y: -bbox.y + (+Ext.isOpera)
+            }
+        }, true);
+        if (me.rendered) {
+            me.setSize(width, height);
+        }
+        else {
+            me.surface.setSize(width, height);
+        }
+        me.el.setSize(width, height);
+    },
+
+    /**
+     * Create the Surface instance. Resolves the correct Surface implementation to
+     * instantiate based on the 'enginePriority' config. Once the Surface instance is
+     * created you can use the handle to that instance to add sprites. For example:
+     *
+     <pre><code>
+        drawComponent.surface.add(sprite);
+     </code></pre>
+     */
+    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')
+        });
+    },
+
+
+    /**
+     * @private
+     * 
+     * Clean up the Surface instance on component destruction
+     */
+    onDestroy: function() {
+        var surface = this.surface;
+        if (surface) {
+            surface.destroy();
+        }
+        this.callParent(arguments);
+    }
+
+});
+
+/**
+ * @class Ext.chart.LegendItem
+ * @extends Ext.draw.CompositeSprite
+ * A single item of a legend (marker plus label)
+ * @constructor
+ */
+Ext.define('Ext.chart.LegendItem', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.draw.CompositeSprite',
+
+    requires: ['Ext.chart.Shape'],
+
+    /* End Definitions */
+
+    // Position of the item, relative to the upper-left corner of the legend box
+    x: 0,
+    y: 0,
+    zIndex: 500,
+
+    constructor: function(config) {
+        this.callParent(arguments);
+        this.createLegend(config);
+    },
+
+    /**
+     * Creates all the individual sprites for this legend item
+     */
+    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')
+        }));
+
+        // Line series - display as short line with optional marker in the middle
+        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'
+                    }
+                }));
+            }
+        }
+        // All other series types - display as filled box
+        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'
+            }
+        }));
+
+        //add toggle listener
+        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}); //Relative to 0,0 at first so that the bbox is calculated correctly
+    },
+
+    /**
+     * Update the positions of all this item's sprites to match the root position
+     * of the legend box.
+     * @param {Object} relativeTo (optional) If specified, this object's 'x' and 'y' values will be used
+     *                 as the reference point for the relative positioning. Defaults to the Legend.
+     */
+    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);
+            }
+        }
+    }
+});
+/**
+ * @class Ext.chart.Legend
+ *
+ * Defines a legend for a chart's series.
+ * The 'chart' member must be set prior to rendering.
+ * The legend class displays a list of legend items each of them related with a
+ * series being rendered. In order to render the legend item of the proper series
+ * the series configuration object must have `showInSeries` set to true.
+ *
+ * The legend configuration object accepts a `position` as parameter.
+ * The `position` parameter can be `left`, `right`
+ * `top` or `bottom`. For example:
+ *
+ *     legend: {
+ *         position: 'right'
+ *     },
+ * 
+ * Full example:
+    <pre><code>
+    var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        animate: true,
+        store: store,
+        shadow: true,
+        theme: 'Category1',
+        legend: {
+            position: 'top'
+        },
+         axes: [{
+                type: 'Numeric',
+                grid: true,
+                position: 'left',
+                fields: ['data1', 'data2', 'data3', 'data4', 'data5'],
+                title: 'Sample Values',
+                grid: {
+                    odd: {
+                        opacity: 1,
+                        fill: '#ddd',
+                        stroke: '#bbb',
+                        'stroke-width': 1
+                    }
+                },
+                minimum: 0,
+                adjustMinimumByMajorUnit: 0
+            }, {
+                type: 'Category',
+                position: 'bottom',
+                fields: ['name'],
+                title: 'Sample Metrics',
+                grid: true,
+                label: {
+                    rotate: {
+                        degrees: 315
+                    }
+                }
+        }],
+        series: [{
+            type: 'area',
+            highlight: false,
+            axis: 'left',
+            xField: 'name',
+            yField: ['data1', 'data2', 'data3', 'data4', 'data5'],
+            style: {
+                opacity: 0.93
+            }
+        }]
+    });    
+    </code></pre>    
+ *
+ * @constructor
+ */
+Ext.define('Ext.chart.Legend', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.chart.LegendItem'],
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Boolean} visible
+     * Whether or not the legend should be displayed.
+     */
+    visible: true,
+
+    /**
+     * @cfg {String} position
+     * The position of the legend in relation to the chart. One of: "top",
+     * "bottom", "left", "right", or "float". If set to "float", then the legend
+     * box will be positioned at the point denoted by the x and y parameters.
+     */
+    position: 'bottom',
+
+    /**
+     * @cfg {Number} x
+     * X-position of the legend box. Used directly if position is set to "float", otherwise 
+     * it will be calculated dynamically.
+     */
+    x: 0,
+
+    /**
+     * @cfg {Number} y
+     * Y-position of the legend box. Used directly if position is set to "float", otherwise
+     * it will be calculated dynamically.
+     */
+    y: 0,
+
+    /**
+     * @cfg {String} labelFont
+     * Font to be used for the legend labels, eg '12px Helvetica'
+     */
+    labelFont: '12px Helvetica, sans-serif',
+
+    /**
+     * @cfg {String} boxStroke
+     * Style of the stroke for the legend box
+     */
+    boxStroke: '#000',
+
+    /**
+     * @cfg {String} boxStrokeWidth
+     * Width of the stroke for the legend box
+     */
+    boxStrokeWidth: 1,
+
+    /**
+     * @cfg {String} boxFill
+     * Fill style for the legend box
+     */
+    boxFill: '#FFF',
+
+    /**
+     * @cfg {Number} itemSpacing
+     * Amount of space between legend items
+     */
+    itemSpacing: 10,
+
+    /**
+     * @cfg {Number} padding
+     * Amount of padding between the legend box's border and its items
+     */
+    padding: 5,
+
+    // @private
+    width: 0,
+    // @private
+    height: 0,
+
+    /**
+     * @cfg {Number} boxZIndex
+     * Sets the z-index for the legend. Defaults to 100.
+     */
+    boxZIndex: 100,
+
+    constructor: function(config) {
+        var me = this;
+        if (config) {
+            Ext.apply(me, config);
+        }
+        me.items = [];
+        /**
+         * Whether the legend box is oriented vertically, i.e. if it is on the left or right side or floating.
+         * @type {Boolean}
+         */
+        me.isVertical = ("left|right|float".indexOf(me.position) !== -1);
+        
+        // cache these here since they may get modified later on
+        me.origX = me.x;
+        me.origY = me.y;
+    },
+
+    /**
+     * @private Create all the sprites for the legend
+     */
+    create: function() {
+        var me = this;
+        me.createItems();
+        if (!me.created && me.isDisplayed()) {
+            me.createBox();
+            me.created = true;
+
+            // Listen for changes to series titles to trigger regeneration of the legend
+            me.chart.series.each(function(series) {
+                series.on('titlechange', function() {
+                    me.create();
+                    me.updatePosition();
+                });
+            });
+        }
+    },
+
+    /**
+     * @private Determine whether the legend should be displayed. Looks at the legend's 'visible' config,
+     * and also the 'showInLegend' config for each of the series.
+     */
+    isDisplayed: function() {
+        return this.visible && this.chart.series.findIndex('showInLegend', true) !== -1;
+    },
+
+    /**
+     * @private Create the series markers and labels
+     */
+    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;
+
+        //remove all legend items
+        if (len) {
+            for (; i < len; i++) {
+                items[i].destroy();
+            }
+        }
+        //empty array
+        items.length = [];
+        // Create all the item labels, collecting their dimensions and positioning each one
+        // properly in relation to the previous item
+        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();
+
+                    //always measure from x=0, since not all markers go all the way to the left
+                    width = bbox.width; 
+                    height = bbox.height;
+
+                    if (i + j === 0) {
+                        spacing = vertical ? padding + height / 2 : padding;
+                    }
+                    else {
+                        spacing = itemSpacing / (vertical ? 2 : 1);
+                    }
+                    // Set the item's position relative to the legend box
+                    item.x = mfloor(vertical ? padding : totalWidth + spacing);
+                    item.y = mfloor(vertical ? totalHeight + spacing : padding + height / 2);
+
+                    // Collect cumulative dimensions
+                    totalWidth += width + spacing;
+                    totalHeight += height + spacing;
+                    maxWidth = mmax(maxWidth, width);
+                    maxHeight = mmax(maxHeight, height);
+
+                    items.push(item);
+                }, this);
+            }
+        }, me);
+
+        // Store the collected dimensions for later
+        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;
+    },
+
+    /**
+     * @private Get the bounds for the legend's outer box
+     */
+    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
+        };
+    },
+
+    /**
+     * @private Create the box around the legend items
+     */
+    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();
+    },
+
+    /**
+     * @private Update the position of all the legend's sprites to match its current x/y values
+     */
+    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()) {
+            // Find the position based on the dimensions
+            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;
+
+            // Update the position of each item
+            Ext.each(me.items, function(item) {
+                item.updatePosition();
+            });
+            // Update the position of the outer box
+            me.boxSprite.setAttributes(me.getBBox(), true);
+        }
+    }
+});
+/**
+ * @class Ext.chart.Chart
+ * @extends Ext.draw.Component
+ *
+ * The Ext.chart package provides the capability to visualize data.
+ * Each chart binds directly to an Ext.data.Store enabling automatic updates of the chart.
+ * A chart configuration object has some overall styling options as well as an array of axes
+ * and series. A chart instance example could look like:
+ *
+  <pre><code>
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 800,
+        height: 600,
+        animate: true,
+        store: store1,
+        shadow: true,
+        theme: 'Category1',
+        legend: {
+            position: 'right'
+        },
+        axes: [ ...some axes options... ],
+        series: [ ...some series options... ]
+    });
+  </code></pre>
+ *
+ * In this example we set the `width` and `height` of the chart, we decide whether our series are
+ * animated or not and we select a store to be bound to the chart. We also turn on shadows for all series,
+ * select a color theme `Category1` for coloring the series, set the legend to the right part of the chart and
+ * then tell the chart to render itself in the body element of the document. For more information about the axes and
+ * series configurations please check the documentation of each series (Line, Bar, Pie, etc).
+ *
+ * @xtype chart
+ */
+
+Ext.define('Ext.chart.Chart', {
+
+    /* Begin Definitions */
+
+    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'
+    ],
+
+    /* End Definitions */
+
+    // @private
+    viewBox: false,
+
+    /**
+     * @cfg {String} theme (optional) The name of the theme to be used. A theme defines the colors and
+     * other visual displays of tick marks on axis, text, title text, line colors, marker colors and styles, etc.
+     * Possible theme values are 'Base', 'Green', 'Sky', 'Red', 'Purple', 'Blue', 'Yellow' and also six category themes
+     * 'Category1' to 'Category6'. Default value is 'Base'.
+     */
+
+    /**
+     * @cfg {Boolean/Object} animate (optional) true for the default animation (easing: 'ease' and duration: 500)
+     * or a standard animation config object to be used for default chart animations. Defaults to false.
+     */
+    animate: false,
+
+    /**
+     * @cfg {Boolean/Object} legend (optional) true for the default legend display or a legend config object. Defaults to false.
+     */
+    legend: false,
+
+    /**
+     * @cfg {integer} insetPadding (optional) Set the amount of inset padding in pixels for the chart. Defaults to 10.
+     */
+    insetPadding: 10,
+
+    /**
+     * @cfg {Array} enginePriority
+     * Defines the priority order for which Surface implementation to use. The first
+     * one supported by the current environment will be used.
+     */
+    enginePriority: ['Svg', 'Vml'],
+
+    /**
+     * @cfg {Object|Boolean} background (optional) Set the chart background. This can be a gradient object, image, or color.
+     * Defaults to false for no background.
+     *
+     * For example, if `background` were to be a color we could set the object as
+     *
+     <pre><code>
+        background: {
+            //color string
+            fill: '#ccc'
+        }
+     </code></pre>
+
+     You can specify an image by using:
+
+     <pre><code>
+        background: {
+            image: 'http://path.to.image/'
+        }
+     </code></pre>
+
+     Also you can specify a gradient by using the gradient object syntax:
+
+     <pre><code>
+        background: {
+            gradient: {
+                id: 'gradientId',
+                angle: 45,
+                stops: {
+                    0: {
+                        color: '#555'
+                    }
+                    100: {
+                        color: '#ddd'
+                    }
+                }
+            }
+        }
+     </code></pre>
+     */
+    background: false,
+
+    /**
+     * @cfg {Array} gradients (optional) Define a set of gradients that can be used as `fill` property in sprites.
+     * The gradients array is an array of objects with the following properties:
+     *
+     * <ul>
+     * <li><strong>id</strong> - string - The unique name of the gradient.</li>
+     * <li><strong>angle</strong> - number, optional - The angle of the gradient in degrees.</li>
+     * <li><strong>stops</strong> - object - An object with numbers as keys (from 0 to 100) and style objects
+     * as values</li>
+     * </ul>
+     *
+
+     For example:
+
+     <pre><code>
+        gradients: [{
+            id: 'gradientId',
+            angle: 45,
+            stops: {
+                0: {
+                    color: '#555'
+                },
+                100: {
+                    color: '#ddd'
+                }
+            }
+        },  {
+            id: 'gradientId2',
+            angle: 0,
+            stops: {
+                0: {
+                    color: '#590'
+                },
+                20: {
+                    color: '#599'
+                },
+                100: {
+                    color: '#ddd'
+                }
+            }
+        }]
+     </code></pre>
+
+     Then the sprites can use `gradientId` and `gradientId2` by setting the fill attributes to those ids, for example:
+
+     <pre><code>
+        sprite.setAttributes({
+            fill: 'url(#gradientId)'
+        }, true);
+     </code></pre>
+
+     */
+
+
+    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',
+            /**
+                 * @event beforerefresh
+                 * Fires before a refresh to the chart data is called.  If the beforerefresh handler returns
+                 * <tt>false</tt> the {@link #refresh} action will be cancelled.
+                 * @param {Chart} this
+                 */
+            'beforerefresh',
+            /**
+                 * @event refresh
+                 * Fires after the chart data has been refreshed.
+                 * @param {Chart} this
+                 */
+            '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
+        });
+    },
+
+    // @private overrides the component method to set the correct dimensions to the chart.
+    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 the chart. If animations are set this will animate the chart too.
+     * @cfg {boolean} resize Optional flag which changes the default origin points of the chart for animations.
+     */
+    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);
+        // Instantiate Series and Axes
+        me.series.each(me.initializeSeries, me);
+        me.axes.each(me.initializeAxis, me);
+        //process all views (aggregated data etc) on stores
+        //before rendering.
+        me.axes.each(function(axis) {
+            axis.processView();
+        });
+        me.axes.each(function(axis) {
+            axis.drawAxis(true);
+        });
+
+        // Create legend if not already created
+        if (legend !== false) {
+            legend.create();
+        }
+
+        // Place axes properly, including influence from each other
+        me.alignAxes();
+
+        // Reposition legend based on new axis alignment
+        if (me.legend !== false) {
+            legend.updatePosition();
+        }
+
+        // Find the max gutter
+        me.getMaxGutter();
+
+        // Draw axes and series
+        me.resizing = !!resize;
+
+        me.axes.each(me.drawAxis, me);
+        me.series.each(me.drawCharts, me);
+        me.resizing = false;
+    },
+
+    // @private set the store after rendering the chart.
+    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();
+    },
+
+    // @private get x and y position of the mouse cursor.
+    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];
+    },
+
+    // @private wrap the mouse down position to delegate the event to the series.
+    onClick: function(e) {
+        var me = this,
+            position = me.getEventXY(e),
+            item;
+
+        // Ask each series if it has an item corresponding to (not necessarily exactly
+        // on top of) the current mouse coords. Fire itemclick event.
+        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);
+    },
+
+    // @private wrap the mouse down position to delegate the event to the series.
+    onMouseDown: function(e) {
+        var me = this,
+            position = me.getEventXY(e),
+            item;
+
+        if (me.mask) {
+            me.mixins.mask.onMouseDown.call(me, e);
+        }
+        // Ask each series if it has an item corresponding to (not necessarily exactly
+        // on top of) the current mouse coords. Fire mousedown event.
+        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);
+    },
+
+    // @private wrap the mouse up event to delegate it to the series.
+    onMouseUp: function(e) {
+        var me = this,
+            position = me.getEventXY(e),
+            item;
+
+        if (me.mask) {
+            me.mixins.mask.onMouseUp.call(me, e);
+        }
+        // Ask each series if it has an item corresponding to (not necessarily exactly
+        // on top of) the current mouse coords. Fire mousedown event.
+        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);
+    },
+
+    // @private wrap the mouse move event so it can be delegated to the series.
+    onMouseMove: function(e) {
+        var me = this,
+            position = me.getEventXY(e),
+            item, last, storeItem, storeField;
+
+        if (me.mask) {
+            me.mixins.mask.onMouseMove.call(me, e);
+        }
+        // Ask each series if it has an item corresponding to (not necessarily exactly
+        // on top of) the current mouse coords. Fire itemmouseover/out events.
+        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);
+    },
+
+    // @private handle mouse leave event.
+    onMouseLeave: function(e) {
+        var me = this;
+        if (me.mask) {
+            me.mixins.mask.onMouseLeave.call(me, e);
+        }
+        me.series.each(function(series) {
+            delete series._lastItemForPoint;
+        });
+    },
+
+    // @private buffered refresh for when we update the store
+    delayRefresh: function() {
+        var me = this;
+        if (!me.refreshTask) {
+            me.refreshTask = Ext.create('Ext.util.DelayedTask', me.refresh, me);
+        }
+        me.refreshTask.delay(me.refreshBuffer);
+    },
+
+    // @private
+    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);
+            }
+        }
+    },
+
+    /**
+     * Changes the data store bound to this chart and refreshes it.
+     * @param {Store} store The store to bind to this chart
+     */
+    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();
+        }
+    },
+
+    // @private Create Axis
+    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);
+        }
+    },
+
+
+    /**
+     * @private Adjust the dimensions and positions of each axis and the chart body area after accounting
+     * for the space taken up on each side by the axes and legend.
+     */
+    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);
+        }
+
+        // Find the space needed by axes and legend as a positive inset from each edge
+        Ext.each(edges, function(edge) {
+            var isVertical = (edge === 'left' || edge === 'right'),
+                axis = getAxis(edge),
+                bbox;
+
+            // Add legend size if it's on this edge
+            if (legend !== false) {
+                if (legend.position === edge) {
+                    bbox = legend.getBBox();
+                    insets[edge] += (isVertical ? bbox.width : bbox.height) + insets[edge];
+                }
+            }
+
+            // Add axis size if there's one on this edge only if it has been
+            //drawn before.
+            if (axis && axis.bbox) {
+                bbox = axis.bbox;
+                insets[edge] += (isVertical ? bbox.width : bbox.height);
+            }
+        });
+        // Build the chart bbox based on the collected inset values
+        chartBBox = {
+            x: insets.left,
+            y: insets.top,
+            width: me.curWidth - insets.left - insets.right,
+            height: me.curHeight - insets.top - insets.bottom
+        };
+        me.chartBBox = chartBBox;
+
+        // Go back through each axis and set its length and position based on the
+        // corresponding edge of the 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);
+        });
+    },
+
+    // @private initialize the series.
+    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();
+        }
+    },
+
+    // @private
+    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;
+    },
+
+    // @private draw axis.
+    drawAxis: function(axis) {
+        axis.drawAxis();
+    },
+
+    // @private draw series.
+    drawCharts: function(series) {
+        series.triggerafterrender = false;
+        series.drawSeries();
+        if (!this.animate) {
+            series.fireEvent('afterrender');
+        }
+    },
+
+    // @private remove gently.
+    destroy: function() {
+        this.surface.destroy();
+        this.bindStore(null);
+        this.callParent(arguments);
+    }
+});
+
+/**
+ * @class Ext.chart.Highlight
+ * @ignore
+ */
+Ext.define('Ext.chart.Highlight', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.fx.Anim'],
+
+    /* End Definitions */
+
+    /**
+     * Highlight the given series item.
+     * @param {Boolean|Object} Default's false. Can also be an object width style properties (i.e fill, stroke, radius) 
+     * or just use default styles per series by setting highlight = true.
+     */
+    highlight: false,
+
+    highlightCfg : null,
+
+    constructor: function(config) {
+        if (config.highlight) {
+            if (config.highlight !== true) { //is an object
+                this.highlightCfg = Ext.apply({}, config.highlight);
+            }
+            else {
+                this.highlightCfg = {
+                    fill: '#fdd',
+                    radius: 20,
+                    lineWidth: 5,
+                    stroke: '#f55'
+                };
+            }
+        }
+    },
+
+    /**
+     * Highlight the given series item.
+     * @param {Object} item Info about the item; same format as returned by #getItemForPoint.
+     */
+    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);
+        }
+    },
+
+    /**
+     * Un-highlight any existing highlights
+     */
+    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;
+                //delete sprite._defaults;
+            }
+        }
+    },
+
+    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;
+            }
+        }
+    }
+});
+/**
+ * @class Ext.chart.Label
+ *
+ * Labels is a mixin whose methods are appended onto the Series class. Labels is an interface with methods implemented
+ * in each of the Series (Pie, Bar, etc) for label creation and label placement.
+ *
+ * The methods implemented by the Series are:
+ *  
+ * - **`onCreateLabel(storeItem, item, i, display)`** Called each time a new label is created.
+ *   The arguments of the method are:
+ *   - *`storeItem`* The element of the store that is related to the label sprite.
+ *   - *`item`* The item related to the label sprite. An item is an object containing the position of the shape
+ *     used to describe the visualization and also pointing to the actual shape (circle, rectangle, path, etc).
+ *   - *`i`* The index of the element created (i.e the first created label, second created label, etc)
+ *   - *`display`* The display type. May be <b>false</b> if the label is hidden
+ *
+ *  - **`onPlaceLabel(label, storeItem, item, i, display, animate)`** Called for updating the position of the label.
+ *    The arguments of the method are:
+ *    - *`label`* The sprite label.</li>
+ *    - *`storeItem`* The element of the store that is related to the label sprite</li>
+ *    - *`item`* The item related to the label sprite. An item is an object containing the position of the shape
+ *      used to describe the visualization and also pointing to the actual shape (circle, rectangle, path, etc).
+ *    - *`i`* The index of the element to be updated (i.e. whether it is the first, second, third from the labelGroup)
+ *    - *`display`* The display type. May be <b>false</b> if the label is hidden.
+ *    - *`animate`* A boolean value to set or unset animations for the labels.
+ */
+Ext.define('Ext.chart.Label', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.draw.Color'],
+    
+    /* End Definitions */
+
+    /**
+     * @cfg {String} display
+     * Specifies the presence and position of labels for each pie slice. Either "rotate", "middle", "insideStart",
+     * "insideEnd", "outside", "over", "under", or "none" to prevent label rendering.
+     * Default value: 'none'.
+     */
+
+    /**
+     * @cfg {String} color
+     * The color of the label text.
+     * Default value: '#000' (black).
+     */
+
+    /**
+     * @cfg {String} field
+     * The name of the field to be displayed in the label.
+     * Default value: 'name'.
+     */
+
+    /**
+     * @cfg {Number} minMargin
+     * Specifies the minimum distance from a label to the origin of the visualization.
+     * This parameter is useful when using PieSeries width variable pie slice lengths.
+     * Default value: 50.
+     */
+
+    /**
+     * @cfg {String} font
+     * The font used for the labels.
+     * Defautl value: "11px Helvetica, sans-serif".
+     */
+
+    /**
+     * @cfg {String} orientation
+     * Either "horizontal" or "vertical".
+     * Dafault value: "horizontal".
+     */
+
+    /**
+     * @cfg {Function} renderer
+     * Optional function for formatting the label into a displayable value.
+     * Default value: function(v) { return v; }
+     * @param v
+     */
+
+    //@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, 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++) {
+            for (j = 0; j < ratio; j++) {
+                item = items[count];
+                label = group.getAt(count);
+                storeItem = store.getAt(i);
+
+                if (!item && label) {
+                    label.hide(true);
+                }
+
+                if (item && field[j]) {
+                    if (!label) {
+                        label = me.onCreateLabel(storeItem, item, i, display, j, count);
+                    }
+                    me.onPlaceLabel(label, storeItem, item, i, display, animate, j, count);
+
+                    //set contrast
+                    if (config.contrast && item.sprite) {
+                        sprite = item.sprite;
+                        colorString = sprite._to && sprite._to.fill || sprite.attr.fill;
+                        spriteColor = Color.fromString(colorString);
+                        //color wasn't parsed property maybe because it's a gradient id
+                        if (colorString && !spriteColor) {
+                            colorString = colorString.match(me.colorStringRe)[1];
+                            for (k = 0; k < gradientsCount; k++) {
+                                gradient = gradients[k];
+                                if (gradient.id == colorString) {
+                                    //avg color stops
+                                    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++;
+            }
+        }
+        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;
+    
+            // Cache the start [X, Y] array
+            this.startPosition = comp.getPosition(true);
+    
+            // If client Component has a ghost method to show a lightweight version of itself
+            // then use that as a drag proxy unless configured to liveDrag.
+            if (comp.ghost && !comp.liveDrag) {
+                 me.proxy = comp.ghost();
+                 me.dragTarget = me.proxy.header.el;
+            }
+    
+            // Set the constrainTo Region before we start dragging.
+            if (me.constrain || me.constrainDelegate) {
+                me.constrainTo = me.calculateConstrainRegion();
+            }
+        };
+    }
+});
+/**
+ * @class Ext.chart.TipSurface
+ * @ignore
+ */
+Ext.define('Ext.chart.TipSurface', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.draw.Component',
+
+    /* End Definitions */
+
+    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);
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.chart.Tip
+ * @ignore
+ */
+Ext.define('Ext.chart.Tip', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.tip.ToolTip', 'Ext.chart.TipSurface'],
+
+    /* End Definitions */
+
+    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) {
+                //initialize a 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);
+    }
+});
+/**
+ * @class Ext.chart.axis.Abstract
+ * @ignore
+ */
+Ext.define('Ext.chart.axis.Abstract', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.chart.Chart'],
+
+    /* End Definitions */
+
+    constructor: function(config) {
+        config = config || {};
+
+        var me = this,
+            pos = config.position || 'left';
+
+        pos = pos.charAt(0).toUpperCase() + pos.substring(1);
+        //axisLabel(Top|Bottom|Right|Left)Style
+        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-'));
+    },
+
+    /*
+      Called to process a view i.e to make aggregation and filtering over
+      a store creating a substore to be used to render the axis. Since many axes
+      may do different things on the data and we want the final result of all these
+      operations to be rendered we need to call processView on all axes before drawing
+      them.
+    */
+    processView: Ext.emptyFn,
+
+    drawAxis: Ext.emptyFn,
+    addDisplayAndLabels: Ext.emptyFn
+});
+
+/**
+ * @class Ext.chart.axis.Axis
+ * @extends Ext.chart.axis.Abstract
+ * 
+ * Defines axis for charts. The axis position, type, style can be configured.
+ * The axes are defined in an axes array of configuration objects where the type, 
+ * field, grid and other configuration options can be set. To know more about how 
+ * to create a Chart please check the Chart class documentation. Here's an example for the axes part:
+ * An example of axis for a series (in this case for an area chart that has multiple layers of yFields) could be:
+ * 
+  <pre><code>
+    axes: [{
+        type: 'Numeric',
+        grid: true,
+        position: 'left',
+        fields: ['data1', 'data2', 'data3'],
+        title: 'Number of Hits',
+        grid: {
+            odd: {
+                opacity: 1,
+                fill: '#ddd',
+                stroke: '#bbb',
+                'stroke-width': 1
+            }
+        },
+        minimum: 0
+    }, {
+        type: 'Category',
+        position: 'bottom',
+        fields: ['name'],
+        title: 'Month of the Year',
+        grid: true,
+        label: {
+            rotate: {
+                degrees: 315
+            }
+        }
+    }]
+   </code></pre>
+ * 
+ * In this case we use a `Numeric` axis for displaying the values of the Area series and a `Category` axis for displaying the names of
+ * the store elements. The numeric axis is placed on the left of the screen, while the category axis is placed at the bottom of the chart. 
+ * Both the category and numeric axes have `grid` set, which means that horizontal and vertical lines will cover the chart background. In the 
+ * category axis the labels will be rotated so they can fit the space better.
+ */
+Ext.define('Ext.chart.axis.Axis', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.axis.Abstract',
+
+    alternateClassName: 'Ext.chart.Axis',
+
+    requires: ['Ext.draw.Draw'],
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Number} majorTickSteps 
+     * If `minimum` and `maximum` are specified it forces the number of major ticks to the specified value.
+     */
+
+    /**
+     * @cfg {Number} minorTickSteps 
+     * The number of small ticks between two major ticks. Default is zero.
+     */
+
+    /**
+     * @cfg {Number} dashSize 
+     * The size of the dash marker. Default's 3.
+     */
+    dashSize: 3,
+    
+    /**
+     * @cfg {String} position
+     * Where to set the axis. Available options are `left`, `bottom`, `right`, `top`. Default's `bottom`.
+     */
+    position: 'bottom',
+    
+    // @private
+    skipFirst: false,
+    
+    /**
+     * @cfg {Number} length
+     * Offset axis position. Default's 0.
+     */
+    length: 0,
+    
+    /**
+     * @cfg {Number} width
+     * Offset axis width. Default's 0.
+     */
+    width: 0,
+    
+    majorTickSteps: false,
+
+    // @private
+    applyData: Ext.emptyFn,
+
+    // @private creates a structure with start, end and step points.
+    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;
+
+        //if one series is stacked I have to aggregate the values
+        //for the scale.
+        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;
+        }
+        //normalize min max for snapEnds.
+        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 (!isNaN(me.maximum)) {
+            //TODO(nico) users are responsible for their own minimum/maximum values set.
+            //Clipping should be added to remove lines in the chart which are below the axis.
+            out.to = me.maximum;
+        }
+        if (!isNaN(me.minimum)) {
+            //TODO(nico) users are responsible for their own minimum/maximum values set.
+            //Clipping should be added to remove lines in the chart which are below the axis.
+            out.from = me.minimum;
+        }
+        
+        //Adjust after adjusting minimum and maximum
+        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;
+    },
+
+    /**
+     * Renders the axis into the screen and updates it's position.
+     */
+    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 no steps are specified
+        //then don't draw the axis. This generally happens
+        //when an empty store.
+        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();
+    },
+
+    /**
+     * Renders an horizontal and/or vertical grid into the Surface.
+     */
+    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();
+        }
+        //get untransformed bounding box
+        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;
+        //transform the points
+        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;
+        //get a reference to the first text label dimensions
+        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);
+
+            // Skip label if there isn't available minimum space
+            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);
+            // Skip label if there isn't available minimum space
+            if (i != 0 && me.intersect(textLabel, prevLabel)) {
+                textLabel.hide(true);
+                continue;
+            }
+            prevLabel = textLabel;
+        }
+        
+        return maxWidth;
+    },
+
+    /**
+     * Renders the labels in the axes.
+     */
+    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();
+        }
+
+        // Hide unused bars
+        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);
+        }
+    },
+
+    // @private creates the elipsis for the text.
+    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;
+    },
+
+    /**
+     * Updates the {@link #title} of this axis.
+     * @param {String} title
+     */
+    setTitle: function(title) {
+        this.title = title;
+        this.drawLabel();
+    },
+
+    // @private draws the title for the axis.
+    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);
+    }
+});
+/**
+ * @class Ext.chart.axis.Category
+ * @extends Ext.chart.axis.Axis
+ *
+ * A type of axis that displays items in categories. This axis is generally used to
+ * display categorical information like names of items, month names, quarters, etc.
+ * but no quantitative values. For that other type of information <em>Number</em>
+ * axis are more suitable.
+ *
+ * As with other axis you can set the position of the axis and its title. For example:
+ * {@img Ext.chart.axis.Category/Ext.chart.axis.Category.png Ext.chart.axis.Category chart axis}
+    <pre><code>
+   var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        store: store,
+        axes: [{
+            type: 'Numeric',
+            grid: true,
+            position: 'left',
+            fields: ['data1', 'data2', 'data3', 'data4', 'data5'],
+            title: 'Sample Values',
+            grid: {
+                odd: {
+                    opacity: 1,
+                    fill: '#ddd',
+                    stroke: '#bbb',
+                    'stroke-width': 1
+                }
+            },
+            minimum: 0,
+            adjustMinimumByMajorUnit: 0
+        }, {
+            type: 'Category',
+            position: 'bottom',
+            fields: ['name'],
+            title: 'Sample Metrics',
+            grid: true,
+            label: {
+                rotate: {
+                    degrees: 315
+                }
+            }
+        }],
+        series: [{
+            type: 'area',
+            highlight: false,
+            axis: 'left',
+            xField: 'name',
+            yField: ['data1', 'data2', 'data3', 'data4', 'data5'],
+            style: {
+                opacity: 0.93
+            }
+        }]
+    });
+    </code></pre>
+
+    In this example with set the category axis to the bottom of the surface, bound the axis to
+    the <em>name</em> property and set as title <em>Month of the Year</em>.
+ */
+
+Ext.define('Ext.chart.axis.Category', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.axis.Axis',
+
+    alternateClassName: 'Ext.chart.CategoryAxis',
+
+    alias: 'axis.category',
+
+    /* End Definitions */
+
+    /**
+     * A list of category names to display along this axis.
+     *
+     * @property categoryNames
+     * @type Array
+     */
+    categoryNames: null,
+
+    /**
+     * Indicates whether or not to calculate the number of categories (ticks and
+     * labels) when there is not enough room to display all labels on the axis.
+     * If set to true, the axis will determine the number of categories to plot.
+     * If not, all categories will be plotted.
+     *
+     * @property calculateCategoryCount
+     * @type Boolean
+     */
+    calculateCategoryCount: false,
+
+    // @private creates an array of labels to be used when rendering.
+    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);
+    },
+
+    // @private calculates labels positions and marker positions for rendering.
+    applyData: function() {
+        this.callParent();
+        this.setLabels();
+        var count = this.chart.store.getCount();
+        return {
+            from: 0,
+            to: count,
+            power: 1,
+            step: 1,
+            steps: count - 1
+        };
+    }
+});
+
+/**
+ * @class Ext.chart.axis.Gauge
+ * @extends Ext.chart.axis.Abstract
+ *
+ * Gauge Axis is the axis to be used with a Gauge series. The Gauge axis
+ * displays numeric data from an interval defined by the `minimum`, `maximum` and
+ * `step` configuration properties. The placement of the numeric data can be changed
+ * by altering the `margin` option that is set to `10` by default.
+ *
+ * A possible configuration for this axis would look like:
+ *
+            axes: [{
+                type: 'gauge',
+                position: 'gauge',
+                minimum: 0,
+                maximum: 100,
+                steps: 10,
+                margin: 7
+            }],
+ * 
+ */
+Ext.define('Ext.chart.axis.Gauge', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.axis.Abstract',
+
+    /* End Definitions */
+    
+    /**
+     * @cfg {Number} minimum (required) the minimum value of the interval to be displayed in the axis.
+     */
+
+    /**
+     * @cfg {Number} maximum (required) the maximum value of the interval to be displayed in the axis.
+     */
+
+    /**
+     * @cfg {Number} steps (required) the number of steps and tick marks to add to the interval.
+     */
+
+    /**
+     * @cfg {Number} margin (optional) the offset positioning of the tick marks and labels in pixels. Default's 10.
+     */
+
+    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) {
+                //draw circles
+                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;
+                //draw circles
+                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);
+    },
+
+    /**
+     * Updates the {@link #title} of this axis.
+     * @param {String} title
+     */
+    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) {
+            //draw scale
+            for (i = 0; i <= steps; i++) {
+                // TODO Adjust for height of text / 2 instead
+                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;
+            //draw values
+            for (i = 0; i <= steps; i++) {
+                // TODO Adjust for height of text / 2 instead
+                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;
+    }
+});
+/**
+ * @class Ext.chart.axis.Numeric
+ * @extends Ext.chart.axis.Axis
+ *
+ * An axis to handle numeric values. This axis is used for quantitative data as
+ * opposed to the category axis. You can set mininum and maximum values to the
+ * axis so that the values are bound to that. If no values are set, then the
+ * scale will auto-adjust to the values.
+ * {@img Ext.chart.axis.Numeric/Ext.chart.axis.Numeric.png Ext.chart.axis.Numeric chart axis}
+ * For example:
+
+    <pre><code>
+   var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        store: store,
+        axes: [{
+            type: 'Numeric',
+            grid: true,
+            position: 'left',
+            fields: ['data1', 'data2', 'data3', 'data4', 'data5'],
+            title: 'Sample Values',
+            grid: {
+                odd: {
+                    opacity: 1,
+                    fill: '#ddd',
+                    stroke: '#bbb',
+                    'stroke-width': 1
+                }
+            },
+            minimum: 0,
+            adjustMinimumByMajorUnit: 0
+        }, {
+            type: 'Category',
+            position: 'bottom',
+            fields: ['name'],
+            title: 'Sample Metrics',
+            grid: true,
+            label: {
+                rotate: {
+                    degrees: 315
+                }
+            }
+        }],
+        series: [{
+            type: 'area',
+            highlight: false,
+            axis: 'left',
+            xField: 'name',
+            yField: ['data1', 'data2', 'data3', 'data4', 'data5'],
+            style: {
+                opacity: 0.93
+            }
+        }]
+    });
+    </code></pre>
+
+ *
+ * In this example we create an axis of Numeric type. We set a minimum value so that
+ * even if all series have values greater than zero, the grid starts at zero. We bind
+ * the axis onto the left part of the surface by setting <em>position</em> to <em>left</em>.
+ * We bind three different store fields to this axis by setting <em>fields</em> to an array.
+ * We set the title of the axis to <em>Number of Hits</em> by using the <em>title</em> property.
+ * We use a <em>grid</em> configuration to set odd background rows to a certain style and even rows
+ * to be transparent/ignored.
+ *
+ *
+ * @constructor
+ */
+Ext.define('Ext.chart.axis.Numeric', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.axis.Axis',
+
+    alternateClassName: 'Ext.chart.NumericAxis',
+
+    /* End Definitions */
+
+    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;
+    },
+    
+    /**
+     * The minimum value drawn by the axis. If not set explicitly, the axis
+     * minimum will be calculated automatically.
+     *
+     * @property minimum
+     * @type Number
+     */
+    minimum: NaN,
+
+    /**
+     * The maximum value drawn by the axis. If not set explicitly, the axis
+     * maximum will be calculated automatically.
+     *
+     * @property maximum
+     * @type Number
+     */
+    maximum: NaN,
+
+    /**
+     * The number of decimals to round the value to.
+     * Default's 2.
+     *
+     * @property decimals
+     * @type Number
+     */
+    decimals: 2,
+
+    /**
+     * The scaling algorithm to use on this axis. May be "linear" or
+     * "logarithmic".
+     *
+     * @property scale
+     * @type String
+     */
+    scale: "linear",
+
+    /**
+     * Indicates the position of the axis relative to the chart
+     *
+     * @property position
+     * @type String
+     */
+    position: 'left',
+
+    /**
+     * Indicates whether to extend maximum beyond data's maximum to the nearest
+     * majorUnit.
+     *
+     * @property adjustMaximumByMajorUnit
+     * @type Boolean
+     */
+    adjustMaximumByMajorUnit: false,
+
+    /**
+     * Indicates whether to extend the minimum beyond data's minimum to the
+     * nearest majorUnit.
+     *
+     * @property adjustMinimumByMajorUnit
+     * @type Boolean
+     */
+    adjustMinimumByMajorUnit: false,
+
+    // @private apply data.
+    applyData: function() {
+        this.callParent();
+        return this.calcEnds();
+    }
+});
+
+/**
+ * @class Ext.chart.axis.Radial
+ * @extends Ext.chart.axis.Abstract
+ * @ignore
+ */
+Ext.define('Ext.chart.axis.Radial', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.axis.Abstract',
+
+    /* End Definitions */
+
+    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) {
+            //draw circles
+            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);
+            }
+            //draw lines
+            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;
+            //draw circles
+            for (i = 0; i < steps; i++) {
+                sprites[i].setAttributes({
+                    x: centerX,
+                    y: centerY,
+                    radius: Math.max(rho * (i + 1) / steps, 0),
+                    stroke: '#ccc'
+                }, true);
+            }
+            //draw lines
+            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;
+        }
+
+        //get all rendered fields
+        chart.series.each(function(series) {
+            fields.push(series.yField);
+            xField = series.xField;
+        });
+        
+        //get maxValue to interpolate
+        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') {
+                //draw scale
+                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') {
+                //draw text
+                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') {
+                //draw values
+                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') {
+                //draw text
+                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;
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.AbstractStore
+ *
+ * <p>AbstractStore is a superclass of {@link Ext.data.Store} and {@link Ext.data.TreeStore}. It's never used directly,
+ * but offers a set of methods used by both of those subclasses.</p>
+ * 
+ * <p>We've left it here in the docs for reference purposes, but unless you need to make a whole new type of Store, what
+ * you're probably looking for is {@link Ext.data.Store}. If you're still interested, here's a brief description of what 
+ * AbstractStore is and is not.</p>
+ * 
+ * <p>AbstractStore provides the basic configuration for anything that can be considered a Store. It expects to be 
+ * given a {@link Ext.data.Model Model} that represents the type of data in the Store. It also expects to be given a 
+ * {@link Ext.data.proxy.Proxy Proxy} that handles the loading of data into the Store.</p>
+ * 
+ * <p>AbstractStore provides a few helpful methods such as {@link #load} and {@link #sync}, which load and save data
+ * respectively, passing the requests through the configured {@link #proxy}. Both built-in Store subclasses add extra
+ * behavior to each of these functions. Note also that each AbstractStore subclass has its own way of storing data - 
+ * in {@link Ext.data.Store} the data is saved as a flat {@link Ext.util.MixedCollection MixedCollection}, whereas in
+ * {@link Ext.data.TreeStore TreeStore} we use a {@link Ext.data.Tree} to maintain the data's hierarchy.</p>
+ * 
+ * TODO: Update these docs to explain about the sortable and filterable mixins.
+ * <p>Finally, AbstractStore provides an API for sorting and filtering data via its {@link #sorters} and {@link #filters}
+ * {@link Ext.util.MixedCollection MixedCollections}. Although this functionality is provided by AbstractStore, there's a
+ * good description of how to use it in the introduction of {@link Ext.data.Store}.
+ * 
+ */
+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,
+
+    /**
+     * @cfg {String/Ext.data.proxy.Proxy/Object} proxy The Proxy to use for this Store. This can be either a string, a config
+     * object or a Proxy instance - see {@link #setProxy} for details.
+     */
+
+    /**
+     * @cfg {Boolean/Object} autoLoad If data is not specified, and if autoLoad is true or an Object, this store's load method
+     * is automatically called after creation. If the value of autoLoad is an Object, this Object will be passed to the store's
+     * load method. Defaults to false.
+     */
+    autoLoad: false,
+
+    /**
+     * @cfg {Boolean} autoSync True to automatically sync the Store with its Proxy after every edit to one of its Records.
+     * Defaults to false.
+     */
+    autoSync: false,
+
+    /**
+     * Sets the updating behavior based on batch synchronization. 'operation' (the default) will update the Store's
+     * internal representation of the data after each operation of the batch has completed, 'complete' will wait until
+     * the entire batch has been completed before updating the Store's data. 'complete' is a good choice for local
+     * storage proxies, 'operation' is better for remote proxies, where there is a comparatively high latency.
+     * @property batchUpdateMode
+     * @type String
+     */
+    batchUpdateMode: 'operation',
+
+    /**
+     * If true, any filters attached to this Store will be run after loading data, before the datachanged event is fired.
+     * Defaults to true, ignored if {@link #remoteFilter} is true
+     * @property filterOnLoad
+     * @type Boolean
+     */
+    filterOnLoad: true,
+
+    /**
+     * If true, any sorters attached to this Store will be run after loading data, before the datachanged event is fired.
+     * Defaults to true, igored if {@link #remoteSort} is true
+     * @property sortOnLoad
+     * @type Boolean
+     */
+    sortOnLoad: true,
+
+    /**
+     * True if a model was created implicitly for this Store. This happens if a fields array is passed to the Store's constructor
+     * instead of a model constructor or name.
+     * @property implicitModel
+     * @type Boolean
+     * @private
+     */
+    implicitModel: false,
+
+    /**
+     * The string type of the Proxy to create if none is specified. This defaults to creating a {@link Ext.data.proxy.Memory memory proxy}.
+     * @property defaultProxyType
+     * @type String
+     */
+    defaultProxyType: 'memory',
+
+    /**
+     * True if the Store has already been destroyed via {@link #destroyStore}. If this is true, the reference to Store should be deleted
+     * as it will not function correctly any more.
+     * @property isDestroyed
+     * @type Boolean
+     */
+    isDestroyed: false,
+
+    isStore: true,
+
+    /**
+     * @cfg {String} storeId Optional unique identifier for this store. If present, this Store will be registered with 
+     * the {@link Ext.data.StoreManager}, making it easy to reuse elsewhere. Defaults to undefined.
+     */
+    
+    /**
+     * @cfg {Array} fields
+     * This may be used in place of specifying a {@link #model} configuration. The fields should be a 
+     * set of {@link Ext.data.Field} configuration objects. The store will automatically create a {@link Ext.data.Model}
+     * with these fields. In general this configuration option should be avoided, it exists for the purposes of
+     * backwards compatibility. For anything more complicated, such as specifying a particular id property or
+     * assocations, a {@link Ext.data.Model} should be defined and specified for the {@link #model} config.
+     */
+
+    sortRoot: 'data',
+    
+    //documented above
+    constructor: function(config) {
+        var me = this;
+        
+        me.addEvents(
+            /**
+             * @event add
+             * Fired when a Model instance has been added to this Store
+             * @param {Ext.data.Store} store The store
+             * @param {Array} records The Model instances that were added
+             * @param {Number} index The index at which the instances were inserted
+             */
+            'add',
+
+            /**
+             * @event remove
+             * Fired when a Model instance has been removed from this Store
+             * @param {Ext.data.Store} store The Store object
+             * @param {Ext.data.Model} record The record that was removed
+             * @param {Number} index The index of the record that was removed
+             */
+            'remove',
+            
+            /**
+             * @event update
+             * Fires when a Record has been updated
+             * @param {Store} this
+             * @param {Ext.data.Model} record The Model instance that was updated
+             * @param {String} operation The update operation being performed. Value may be one of:
+             * <pre><code>
+               Ext.data.Model.EDIT
+               Ext.data.Model.REJECT
+               Ext.data.Model.COMMIT
+             * </code></pre>
+             */
+            'update',
+
+            /**
+             * @event datachanged
+             * Fires whenever the records in the Store have changed in some way - this could include adding or removing records,
+             * or updating the data in existing records
+             * @param {Ext.data.Store} this The data store
+             */
+            'datachanged',
+
+            /**
+             * @event beforeload
+             * Event description
+             * @param {Ext.data.Store} store This Store
+             * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to load the Store
+             */
+            'beforeload',
+
+            /**
+             * @event load
+             * Fires whenever the store reads data from a remote data source.
+             * @param {Ext.data.Store} this
+             * @param {Array} records An array of records
+             * @param {Boolean} successful True if the operation was successful.
+             */
+            'load',
+
+            /**
+             * @event beforesync
+             * Called before a call to {@link #sync} is executed. Return false from any listener to cancel the synv
+             * @param {Object} options Hash of all records to be synchronized, broken down into create, update and destroy
+             */
+            'beforesync',
+            /**
+             * @event clear
+             * Fired after the {@link #removeAll} method is called.
+             * @param {Ext.data.Store} this
+             */
+            'clear'
+        );
+        
+        Ext.apply(me, config);
+
+        /**
+         * Temporary cache in which removed model instances are kept until successfully synchronised with a Proxy,
+         * at which point this is cleared.
+         * @private
+         * @property removed
+         * @type Array
+         */
+        me.removed = [];
+
+        me.mixins.observable.constructor.apply(me, arguments);
+        me.model = Ext.ModelManager.getModel(config.model || me.model);
+
+        /**
+         * @property modelDefaults
+         * @type Object
+         * @private
+         * A set of default values to be applied to every model instance added via {@link #insert} or created via {@link #create}.
+         * This is used internally by associations to set foreign keys and other fields. See the Association classes source code
+         * for examples. This should not need to be used by application developers.
+         */
+        Ext.applyIf(me, {
+            modelDefaults: {}
+        });
+
+        //Supports the 3.x style of simply passing an array of fields to the store, implicitly creating a model
+        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;
+        }
+
+        //ensures that the Proxy is instantiated correctly
+        me.setProxy(config.proxy || 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);        
+        
+        /**
+         * The collection of {@link Ext.util.Filter Filters} currently applied to this Store
+         * @property filters
+         * @type Ext.util.MixedCollection
+         */
+        me.filters = Ext.create('Ext.util.MixedCollection');
+        me.filters.addAll(me.decodeFilters(config.filters));
+    },
+
+    /**
+     * Sets the Store's Proxy by string, config object or Proxy instance
+     * @param {String|Object|Ext.data.proxy.Proxy} proxy The new Proxy, which can be either a type string, a configuration object
+     * or an Ext.data.proxy.Proxy instance
+     * @return {Ext.data.proxy.Proxy} The attached Proxy object
+     */
+    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;
+    },
+
+    /**
+     * Returns the proxy currently attached to this proxy instance
+     * @return {Ext.data.proxy.Proxy} The Proxy instance
+     */
+    getProxy: function() {
+        return this.proxy;
+    },
+
+    //saves any phantom records
+    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);
+    },
+
+    /**
+     * @private
+     * Callback for any write Operation over the Proxy. Updates the Store's MixedCollection to reflect
+     * the updates provided by the Proxy
+     */
+    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);
+        }
+        //this is a callback that would have been passed to the 'create', 'update' or 'destroy' function and is optional
+        Ext.callback(operation.callback, operation.scope || me, [records, operation, success]);
+    },
+
+
+    //tells the attached proxy to destroy the given records
+    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);
+    },
+
+    /**
+     * @private
+     * Attached as the 'operationcomplete' event listener to a proxy's Batch object. By default just calls through
+     * to onProxyWrite.
+     */
+    onBatchOperationComplete: function(batch, operation) {
+        return this.onProxyWrite(operation);
+    },
+
+    /**
+     * @private
+     * Attached as the 'complete' event listener to a proxy's Batch object. Iterates over the batch operations
+     * and updates the Store's internal data MixedCollection.
+     */
+    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) {
+        // //decide what to do... could continue with the next operation
+        // batch.start();
+        //
+        // //or retry the last operation
+        // batch.retry();
+    },
+
+    /**
+     * @private
+     * Filter function for new records.
+     */
+    filterNew: function(item) {
+        // only want phantom records that are valid
+        return item.phantom === true && item.isValid();
+    },
+
+    /**
+     * Returns all Model instances that are either currently a phantom (e.g. have no id), or have an ID but have not
+     * yet been saved on this Store (this happens when adding a non-phantom record from another Store into this one)
+     * @return {Array} The Model instances
+     */
+    getNewRecords: function() {
+        return [];
+    },
+
+    /**
+     * Returns all Model instances that have been updated in the Store but not yet synchronized with the Proxy
+     * @return {Array} The updated Model instances
+     */
+    getUpdatedRecords: function() {
+        return [];
+    },
+
+    /**
+     * @private
+     * Filter function for updated records.
+     */
+    filterUpdated: function(item) {
+        // only want dirty records, not phantoms that are valid
+        return item.dirty === true && item.phantom !== true && item.isValid();
+    },
+
+    //returns any records that have been removed from the store but not yet destroyed on the proxy
+    getRemovedRecords: function() {
+        return this.removed;
+    },
+
+    filter: function(filters, value) {
+
+    },
+
+    /**
+     * @private
+     * Normalizes an array of filter objects, ensuring that they are all Ext.util.Filter instances
+     * @param {Array} filters The filters array
+     * @return {Array} Array of Ext.util.Filter objects
+     */
+    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'
+                });
+
+                //support for 3.x style filters where a function can be defined as 'fn'
+                if (config.fn) {
+                    config.filterFn = config.fn;
+                }
+
+                //support a function to be passed as a filter definition
+                if (typeof config == 'function') {
+                    config = {
+                        filterFn: config
+                    };
+                }
+
+                filters[i] = new Filter(config);
+            }
+        }
+
+        return filters;
+    },
+
+    clearFilter: function(supressEvent) {
+
+    },
+
+    isFiltered: function() {
+
+    },
+
+    filterBy: function(fn, scope) {
+
+    },
+    
+    /**
+     * Synchronizes the Store with its Proxy. This asks the Proxy to batch together any new, updated
+     * and deleted records in the store, updating the Store's internal representation of the records
+     * as each operation completes.
+     */
+    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());
+        }
+    },
+
+
+    /**
+     * @private
+     * Returns an object which is passed in as the listeners argument to proxy.batch inside this.sync.
+     * This is broken out into a separate function to allow for customisation of the listeners
+     * @return {Object} The listeners object
+     */
+    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;
+    },
+
+    //deprecated, will be removed in 5.0
+    save: function() {
+        return this.sync.apply(this, arguments);
+    },
+
+    /**
+     * Loads the Store using its configured {@link #proxy}.
+     * @param {Object} options Optional config object. This is passed into the {@link Ext.data.Operation Operation}
+     * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
+     * @param {Ext.data.Model} record The model instance that was edited
+     */
+    afterEdit : function(record) {
+        var me = this;
+        
+        if (me.autoSync) {
+            me.sync();
+        }
+        
+        me.fireEvent('update', me, record, Ext.data.Model.EDIT);
+    },
+
+    /**
+     * @private
+     * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to..
+     * @param {Ext.data.Model} record The model instance that was edited
+     */
+    afterReject : function(record) {
+        this.fireEvent('update', this, record, Ext.data.Model.REJECT);
+    },
+
+    /**
+     * @private
+     * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
+     * @param {Ext.data.Model} record The model instance that was edited
+     */
+    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;
+            // Ext.destroy(this.proxy);
+            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) {
+            //the load function will pick up the new sorters and request the sorted data from the proxy
+            me.load();
+        } else {
+            me.data.sortBy(sorterFn);
+            me.fireEvent('datachanged', me);
+        }
+    },
+
+    getCount: Ext.emptyFn,
+
+    getById: Ext.emptyFn,
+    
+    /**
+     * Removes all records from the store. This method does a "fast remove",
+     * individual remove events are not called. The {@link #clear} event is
+     * fired upon completion.
+     */
+    removeAll: Ext.emptyFn,
+    // individual substores should implement a "fast" remove
+    // and fire a clear event afterwards
+
+    /**
+     * Returns true if the Store is currently performing a load operation
+     * @return {Boolean} True if the Store is currently loading
+     */
+    isLoading: function() {
+        return this.loading;
+     }
+});
+
+/**
+ * @class Ext.util.Grouper
+ * @extends Ext.util.Sorter
+ */
+Ext.define('Ext.util.Grouper', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.util.Sorter',
+
+    /* End Definitions */
+
+    /**
+     * Function description
+     * @param {Ext.data.Model} instance The Model instance
+     * @return {String} The group string for this model
+     */
+    getGroupString: function(instance) {
+        return instance.get(this.property);
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.Store
+ * @extends Ext.data.AbstractStore
+ *
+ * <p>The Store class encapsulates a client side cache of {@link Ext.data.Model Model} objects. Stores load
+ * data via a {@link Ext.data.proxy.Proxy Proxy}, and also provide functions for {@link #sort sorting},
+ * {@link #filter filtering} and querying the {@link Ext.data.Model model} instances contained within it.</p>
+ *
+ * <p>Creating a Store is easy - we just tell it the Model and the Proxy to use to load and save its data:</p>
+ *
+<pre><code>
+// Set up a {@link Ext.data.Model model} to use in our Store
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'firstName', type: 'string'},
+        {name: 'lastName',  type: 'string'},
+        {name: 'age',       type: 'int'},
+        {name: 'eyeColor',  type: 'string'}
+    ]
+});
+
+var myStore = new Ext.data.Store({
+    model: 'User',
+    proxy: {
+        type: 'ajax',
+        url : '/users.json',
+        reader: {
+            type: 'json',
+            root: 'users'
+        }
+    },
+    autoLoad: true
+});
+</code></pre>
+
+ * <p>In the example above we configured an AJAX proxy to load data from the url '/users.json'. We told our Proxy
+ * to use a {@link Ext.data.reader.Json JsonReader} to parse the response from the server into Model object -
+ * {@link Ext.data.reader.Json see the docs on JsonReader} for details.</p>
+ *
+ * <p><u>Inline data</u></p>
+ *
+ * <p>Stores can also load data inline. Internally, Store converts each of the objects we pass in as {@link #data}
+ * into Model instances:</p>
+ *
+<pre><code>
+new Ext.data.Store({
+    model: 'User',
+    data : [
+        {firstName: 'Ed',    lastName: 'Spencer'},
+        {firstName: 'Tommy', lastName: 'Maintz'},
+        {firstName: 'Aaron', lastName: 'Conran'},
+        {firstName: 'Jamie', lastName: 'Avins'}
+    ]
+});
+</code></pre>
+ *
+ * <p>Loading inline data using the method above is great if the data is in the correct format already (e.g. it doesn't need
+ * to be processed by a {@link Ext.data.reader.Reader reader}). If your inline data requires processing to decode the data structure,
+ * use a {@link Ext.data.proxy.Memory MemoryProxy} instead (see the {@link Ext.data.proxy.Memory MemoryProxy} docs for an example).</p>
+ *
+ * <p>Additional data can also be loaded locally using {@link #add}.</p>
+ *
+ * <p><u>Loading Nested Data</u></p>
+ *
+ * <p>Applications often need to load sets of associated data - for example a CRM system might load a User and her Orders.
+ * Instead of issuing an AJAX request for the User and a series of additional AJAX requests for each Order, we can load a nested dataset
+ * and allow the Reader to automatically populate the associated models. Below is a brief example, see the {@link Ext.data.reader.Reader} intro
+ * docs for a full explanation:</p>
+ *
+<pre><code>
+var store = new Ext.data.Store({
+    autoLoad: true,
+    model: "User",
+    proxy: {
+        type: 'ajax',
+        url : 'users.json',
+        reader: {
+            type: 'json',
+            root: 'users'
+        }
+    }
+});
+</code></pre>
+ *
+ * <p>Which would consume a response like this:</p>
+ *
+<pre><code>
+{
+    "users": [
+        {
+            "id": 1,
+            "name": "Ed",
+            "orders": [
+                {
+                    "id": 10,
+                    "total": 10.76,
+                    "status": "invoiced"
+                },
+                {
+                    "id": 11,
+                    "total": 13.45,
+                    "status": "shipped"
+                }
+            ]
+        }
+    ]
+}
+</code></pre>
+ *
+ * <p>See the {@link Ext.data.reader.Reader} intro docs for a full explanation.</p>
+ *
+ * <p><u>Filtering and Sorting</u></p>
+ *
+ * <p>Stores can be sorted and filtered - in both cases either remotely or locally. The {@link #sorters} and {@link #filters} are
+ * held inside {@link Ext.util.MixedCollection MixedCollection} instances to make them easy to manage. Usually it is sufficient to
+ * either just specify sorters and filters in the Store configuration or call {@link #sort} or {@link #filter}:
+ *
+<pre><code>
+var store = new Ext.data.Store({
+    model: 'User',
+    sorters: [
+        {
+            property : 'age',
+            direction: 'DESC'
+        },
+        {
+            property : 'firstName',
+            direction: 'ASC'
+        }
+    ],
+
+    filters: [
+        {
+            property: 'firstName',
+            value   : /Ed/
+        }
+    ]
+});
+</code></pre>
+ *
+ * <p>The new Store will keep the configured sorters and filters in the MixedCollection instances mentioned above. By default, sorting
+ * and filtering are both performed locally by the Store - see {@link #remoteSort} and {@link #remoteFilter} to allow the server to
+ * perform these operations instead.</p>
+ *
+ * <p>Filtering and sorting after the Store has been instantiated is also easy. Calling {@link #filter} adds another filter to the Store
+ * and automatically filters the dataset (calling {@link #filter} with no arguments simply re-applies all existing filters). Note that by
+ * default {@link #sortOnFilter} is set to true, which means that your sorters are automatically reapplied if using local sorting.</p>
+ *
+<pre><code>
+store.filter('eyeColor', 'Brown');
+</code></pre>
+ *
+ * <p>Change the sorting at any time by calling {@link #sort}:</p>
+ *
+<pre><code>
+store.sort('height', 'ASC');
+</code></pre>
+ *
+ * <p>Note that all existing sorters will be removed in favor of the new sorter data (if {@link #sort} is called with no arguments,
+ * the existing sorters are just reapplied instead of being removed). To keep existing sorters and add new ones, just add them
+ * to the MixedCollection:</p>
+ *
+<pre><code>
+store.sorters.add(new Ext.util.Sorter({
+    property : 'shoeSize',
+    direction: 'ASC'
+}));
+
+store.sort();
+</code></pre>
+ *
+ * <p><u>Registering with StoreManager</u></p>
+ *
+ * <p>Any Store that is instantiated with a {@link #storeId} will automatically be registed with the {@link Ext.data.StoreManager StoreManager}.
+ * This makes it easy to reuse the same store in multiple views:</p>
+ *
+ <pre><code>
+//this store can be used several times
+new Ext.data.Store({
+    model: 'User',
+    storeId: 'usersStore'
+});
+
+new Ext.List({
+    store: 'usersStore',
+
+    //other config goes here
+});
+
+new Ext.view.View({
+    store: 'usersStore',
+
+    //other config goes here
+});
+</code></pre>
+ *
+ * <p><u>Further Reading</u></p>
+ *
+ * <p>Stores are backed up by an ecosystem of classes that enables their operation. To gain a full understanding of these
+ * pieces and how they fit together, see:</p>
+ *
+ * <ul style="list-style-type: disc; padding-left: 25px">
+ * <li>{@link Ext.data.proxy.Proxy Proxy} - overview of what Proxies are and how they are used</li>
+ * <li>{@link Ext.data.Model Model} - the core class in the data package</li>
+ * <li>{@link Ext.data.reader.Reader Reader} - used by any subclass of {@link Ext.data.proxy.Server ServerProxy} to read a response</li>
+ * </ul>
+ *
+ * @constructor
+ * @param {Object} config Optional config object
+ */
+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'],
+
+    /**
+     * @cfg {Boolean} remoteSort
+     * True to defer any sorting operation to the server. If false, sorting is done locally on the client. Defaults to <tt>false</tt>.
+     */
+    remoteSort: false,
+
+    /**
+     * @cfg {Boolean} remoteFilter
+     * True to defer any filtering operation to the server. If false, filtering is done locally on the client. Defaults to <tt>false</tt>.
+     */
+    remoteFilter: false,
+    
+    /**
+     * @cfg {Boolean} remoteGroup
+     * True if the grouping should apply on the server side, false if it is local only (defaults to false).  If the
+     * grouping is local, it can be applied immediately to the data.  If it is remote, then it will simply act as a
+     * helper, automatically sending the grouping information to the server.
+     */
+    remoteGroup : false,
+
+    /**
+     * @cfg {String/Ext.data.proxy.Proxy/Object} proxy The Proxy to use for this Store. This can be either a string, a config
+     * object or a Proxy instance - see {@link #setProxy} for details.
+     */
+
+    /**
+     * @cfg {Array} data Optional array of Model instances or data objects to load locally. See "Inline data" above for details.
+     */
+
+    /**
+     * @cfg {String} model The {@link Ext.data.Model} associated with this store
+     */
+
+    /**
+     * The (optional) field by which to group data in the store. Internally, grouping is very similar to sorting - the
+     * groupField and {@link #groupDir} are injected as the first sorter (see {@link #sort}). Stores support a single
+     * level of grouping, and groups can be fetched via the {@link #getGroups} method.
+     * @property groupField
+     * @type String
+     */
+    groupField: undefined,
+
+    /**
+     * The direction in which sorting should be applied when grouping. Defaults to "ASC" - the other supported value is "DESC"
+     * @property groupDir
+     * @type String
+     */
+    groupDir: "ASC",
+
+    /**
+     * The number of records considered to form a 'page'. This is used to power the built-in
+     * paging using the nextPage and previousPage functions. Defaults to 25.
+     * @property pageSize
+     * @type Number
+     */
+    pageSize: 25,
+
+    /**
+     * The page that the Store has most recently loaded (see {@link #loadPage})
+     * @property currentPage
+     * @type Number
+     */
+    currentPage: 1,
+
+    /**
+     * @cfg {Boolean} clearOnPageLoad True to empty the store when loading another page via {@link #loadPage},
+     * {@link #nextPage} or {@link #previousPage} (defaults to true). Setting to false keeps existing records, allowing
+     * large data sets to be loaded one page at a time but rendered all together.
+     */
+    clearOnPageLoad: true,
+
+    /**
+     * True if the Store is currently loading via its Proxy
+     * @property loading
+     * @type Boolean
+     * @private
+     */
+    loading: false,
+
+    /**
+     * @cfg {Boolean} sortOnFilter For local filtering only, causes {@link #sort} to be called whenever {@link #filter} is called,
+     * causing the sorters to be reapplied after filtering. Defaults to true
+     */
+    sortOnFilter: true,
+    
+    /**
+     * @cfg {Boolean} buffered
+     * Allow the store to buffer and pre-fetch pages of records. This is to be used in conjunction with a view will
+     * tell the store to pre-fetch records ahead of a time.
+     */
+    buffered: false,
+    
+    /**
+     * @cfg {Number} purgePageCount 
+     * The number of pages to keep in the cache before purging additional records. A value of 0 indicates to never purge the prefetched data.
+     * This option is only relevant when the {@link #buffered} option is set to true.
+     */
+    purgePageCount: 5,
+
+    isStore: true,
+
+    //documented above
+    constructor: function(config) {
+        config = config || {};
+
+        var me = this,
+            groupers = config.groupers,
+            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(
+            /**
+             * @event beforeprefetch
+             * Fires before a prefetch occurs. Return false to cancel.
+             * @param {Ext.data.store} this
+             * @param {Ext.data.Operation} operation The associated operation
+             */
+            'beforeprefetch',
+            /**
+             * @event groupchange
+             * Fired whenever the grouping in the grid changes
+             * @param {Ext.data.Store} store The store
+             * @param {Array} groupers The array of grouper objects
+             */
+            'groupchange',
+            /**
+             * @event load
+             * Fires whenever records have been prefetched
+             * @param {Ext.data.store} this
+             * @param {Array} records An array of records
+             * @param {Boolean} successful True if the operation was successful.
+             * @param {Ext.data.Operation} operation The associated operation
+             */
+            'prefetch'
+        );
+        data = config.data || me.data;
+
+        /**
+         * The MixedCollection that holds this store's local cache of records
+         * @property data
+         * @type Ext.util.MixedCollection
+         */
+        me.data = Ext.create('Ext.util.MixedCollection', false, function(record) {
+            return record.internalId;
+        });
+
+        if (data) {
+            me.inlineData = data;
+            delete config.data;
+        }
+        
+        if (!groupers && config.groupField) {
+            groupers = [{
+                property : config.groupField,
+                direction: config.groupDir
+            }];
+        }
+        delete config.groupers;
+        
+        /**
+         * The collection of {@link Ext.util.Grouper Groupers} currently applied to this Store
+         * @property groupers
+         * @type Ext.util.MixedCollection
+         */
+        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]);
+            // Remove the defer call, we may need reinstate this at some point, but currently it's not obvious why it's here.
+            // this.load(typeof this.autoLoad == 'object' ? this.autoLoad : undefined);
+        }
+    },
+    
+    onBeforeSort: function() {
+        this.sort(this.groupers.items, 'prepend', false);
+    },
+    
+    /**
+     * @private
+     * Normalizes an array of grouper objects, ensuring that they are all Ext.util.Grouper instances
+     * @param {Array} groupers The groupers array
+     * @return {Array} Array of Ext.util.Grouper objects
+     */
+    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"
+                });
+
+                //support for 3.x style sorters where a function can be defined as 'fn'
+                if (config.fn) {
+                    config.sorterFn = config.fn;
+                }
+
+                //support a function to be passed as a sorter definition
+                if (typeof config == 'function') {
+                    config = {
+                        sorterFn: config
+                    };
+                }
+
+                groupers[i] = new Grouper(config);
+            }
+        }
+
+        return groupers;
+    },
+    
+    /**
+     * Group data in the store
+     * @param {String|Array} groupers Either a string name of one of the fields in this Store's configured {@link Ext.data.Model Model},
+     * or an Array of grouper configurations.
+     * @param {String} direction The overall direction to group the data by. Defaults to "ASC".
+     */
+    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);
+        }
+    },
+    
+    /**
+     * Clear any groupers in the store
+     */
+    clearGrouping: function(){
+        var me = this;
+        // Clear any groupers we pushed on to the sorters
+        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);
+        }
+    },
+    
+    /**
+     * Checks if the store is currently grouped
+     * @return {Boolean} True if the store is grouped.
+     */
+    isGrouped: function() {
+        return this.groupers.getCount() > 0;    
+    },
+    
+    /**
+     * Fires the groupchange event. Abstracted out so we can use it
+     * as a callback
+     * @private
+     */
+    fireGroupChange: function(){
+        this.fireEvent('groupchange', this, this.groupers);    
+    },
+
+    /**
+     * Returns an object containing the result of applying grouping to the records in this store. See {@link #groupField},
+     * {@link #groupDir} and {@link #getGroupString}. Example for a store containing records with a color field:
+<pre><code>
+var myStore = new Ext.data.Store({
+    groupField: 'color',
+    groupDir  : 'DESC'
+});
+
+myStore.getGroups(); //returns:
+[
+    {
+        name: 'yellow',
+        children: [
+            //all records where the color field is 'yellow'
+        ]
+    },
+    {
+        name: 'red',
+        children: [
+            //all records where the color field is 'red'
+        ]
+    }
+]
+</code></pre>
+     * @param {String} groupName (Optional) Pass in an optional groupName argument to access a specific group as defined by {@link #getGroupString}
+     * @return {Array} The grouped data
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * For a given set of records and a Grouper, returns an array of arrays - each of which is the set of records
+     * matching a certain group.
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * This is used recursively to gather the records into the configured Groupers. The data MUST have been sorted for
+     * this to work properly (see {@link #getGroupData} and {@link #getGroupsForGrouper}) Most of the work is done by
+     * {@link #getGroupsForGrouper} - this function largely just handles the recursion.
+     * @param {Array} records The set or subset of records to group
+     * @param {Number} grouperIndex The grouper index to retrieve
+     * @return {Array} The grouped records
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * <p>Returns records grouped by the configured {@link #groupers grouper} configuration. Sample return value (in
+     * this case grouping by genre and then author in a fictional books dataset):</p>
+<pre><code>
+[
+    {
+        name: 'Fantasy',
+        depth: 0,
+        records: [
+            //book1, book2, book3, book4
+        ],
+        children: [
+            {
+                name: 'Rowling',
+                depth: 1,
+                records: [
+                    //book1, book2
+                ]
+            },
+            {
+                name: 'Tolkein',
+                depth: 1,
+                records: [
+                    //book3, book4
+                ]
+            }
+        ]
+    }
+]
+</code></pre>
+     * @param {Boolean} sort True to call {@link #sort} before finding groups. Sorting is required to make grouping
+     * function correctly so this should only be set to false if the Store is known to already be sorted correctly
+     * (defaults to true)
+     * @return {Array} The group data
+     */
+    getGroupData: function(sort) {
+        var me = this;
+        if (sort !== false) {
+            me.sort();
+        }
+
+        return me.getGroupsForGrouperIndex(me.data.items, 0);
+    },
+
+    /**
+     * <p>Returns the string to group on for a given model instance. The default implementation of this method returns
+     * the model's {@link #groupField}, but this can be overridden to group by an arbitrary string. For example, to
+     * group by the first letter of a model's 'name' field, use the following code:</p>
+<pre><code>
+new Ext.data.Store({
+    groupDir: 'ASC',
+    getGroupString: function(instance) {
+        return instance.get('name')[0];
+    }
+});
+</code></pre>
+     * @param {Ext.data.Model} instance The model instance
+     * @return {String} The string to compare when forming groups
+     */
+    getGroupString: function(instance) {
+        var group = this.groupers.first();
+        if (group) {
+            return instance.get(group.property);
+        }
+        return '';
+    },
+    /**
+     * Inserts Model instances into the Store at the given index and fires the {@link #add} event.
+     * See also <code>{@link #add}</code>.
+     * @param {Number} index The start index at which to insert the passed Records.
+     * @param {Ext.data.Model[]} records An Array of Ext.data.Model objects to add to the cache.
+     */
+    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);
+            // reassign the model in the array in case it wasn't created yet
+            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();
+        }
+    },
+
+    /**
+     * Adds Model instances to the Store by instantiating them based on a JavaScript object. When adding already-
+     * instantiated Models, use {@link #insert} instead. The instances will be added at the end of the existing collection.
+     * This method accepts either a single argument array of Model instances or any number of model instance arguments.
+     * Sample usage:
+     *
+<pre><code>
+myStore.add({some: 'data'}, {some: 'other data'});
+</code></pre>
+     *
+     * @param {Object} data The data for each model
+     * @return {Array} The array of newly created model instances
+     */
+    add: function(records) {
+        //accept both a single-argument array of records, or any number of record arguments
+        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]);
+            // reassign the model in the array in case it wasn't created yet
+            records[i] = record;
+        }
+
+        me.insert(me.data.length, records);
+
+        return records;
+    },
+
+    /**
+     * Converts a literal to a model, if it's not a model already
+     * @private
+     * @param record {Ext.data.Model/Object} The record to create
+     * @return {Ext.data.Model}
+     */
+    createModel: function(record) {
+        if (!record.isModel) {
+            record = Ext.ModelManager.create(record, this.model);
+        }
+
+        return record;
+    },
+
+    /**
+     * Calls the specified function for each of the {@link Ext.data.Model Records} in the cache.
+     * @param {Function} fn The function to call. The {@link Ext.data.Model Record} is passed as the first parameter.
+     * Returning <tt>false</tt> aborts and exits the iteration.
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed.
+     * Defaults to the current {@link Ext.data.Model Record} in the iteration.
+     */
+    each: function(fn, scope) {
+        this.data.each(fn, scope);
+    },
+
+    /**
+     * Removes the given record from the Store, firing the 'remove' event for each instance that is removed, plus a single
+     * 'datachanged' event after removal.
+     * @param {Ext.data.Model/Array} records The Ext.data.Model instance or array of instances to remove
+     */
+    remove: function(records, /* private */ isMove) {
+        if (!Ext.isArray(records)) {
+            records = [records];
+        }
+
+        /*
+         * Pass the isMove parameter if we know we're going to be re-inserting this record
+         */
+        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) {
+                    // don't push phantom records onto removed
+                    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();
+        }
+    },
+
+    /**
+     * Removes the model instance at the given index
+     * @param {Number} index The record index
+     */
+    removeAt: function(index) {
+        var record = this.getAt(index);
+
+        if (record) {
+            this.remove(record);
+        }
+    },
+
+    /**
+     * <p>Loads data into the Store via the configured {@link #proxy}. This uses the Proxy to make an
+     * asynchronous call to whatever storage backend the Proxy uses, automatically adding the retrieved
+     * instances into the Store and calling an optional callback if required. Example usage:</p>
+     *
+<pre><code>
+store.load({
+    scope   : this,
+    callback: function(records, operation, success) {
+        //the {@link Ext.data.Operation operation} object contains all of the details of the load operation
+        console.log(records);
+    }
+});
+</code></pre>
+     *
+     * <p>If the callback scope does not need to be set, a function can simply be passed:</p>
+     *
+<pre><code>
+store.load(function(records, operation, success) {
+    console.log('loaded records');
+});
+</code></pre>
+     *
+     * @param {Object/Function} options Optional config object, passed into the Ext.data.Operation object before loading.
+     */
+    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]);
+    },
+
+    /**
+     * @private
+     * Called internally when a Proxy has completed a load request
+     */
+    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);
+
+        //TODO: deprecate this event, it should always have been 'load' instead. 'load' is now documented, 'read' is not.
+        //People are definitely using this so can't deprecate safely until 2.x
+        me.fireEvent('read', me, records, operation.wasSuccessful());
+
+        //this is a callback that would have been passed to the 'read' function and is optional
+        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
+    },
+    
+    /**
+     * Create any new records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of new records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was 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;
+
+            /**
+             * Loop over each record returned from the server. Assume they are
+             * returned in order of how they were sent. If we find a matching
+             * record, replace it with the newly created one.
+             */
+            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);
+                }
+            }
+        }
+    },
+
+    /**
+     * Update any records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of updated records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was successful
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * Remove any records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of removed records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was successful
+     */
+    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 = [];
+        }
+    },
+
+    //inherit docs
+    getNewRecords: function() {
+        return this.data.filterBy(this.filterNew).items;
+    },
+
+    //inherit docs
+    getUpdatedRecords: function() {
+        return this.data.filterBy(this.filterUpdated).items;
+    },
+
+    /**
+     * Filters the loaded set of records by a given set of filters.
+     * @param {Mixed} filters The set of filters to apply to the data. These are stored internally on the store,
+     * but the filtering itself is done on the Store's {@link Ext.util.MixedCollection MixedCollection}. See
+     * MixedCollection's {@link Ext.util.MixedCollection#filter filter} method for filter syntax. Alternatively,
+     * pass in a property string
+     * @param {String} value Optional value to filter by (only if using a property string as the first argument)
+     */
+    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) {
+            //the load function will pick up the new filters and request the filtered data from the proxy
+            me.load();
+        } else {
+            /**
+             * A pristine (unfiltered) collection of the records in this store. This is used to reinstate
+             * records when a filter is removed or changed
+             * @property snapshot
+             * @type Ext.util.MixedCollection
+             */
+            if (me.filters.getCount()) {
+                me.snapshot = me.snapshot || me.data.clone();
+                me.data = me.data.filter(me.filters.items);
+
+                if (doLocalSort) {
+                    me.sort();
+                }
+                // fire datachanged event if it hasn't already been fired by doSort
+                if (!doLocalSort || me.sorters.length < 1) {
+                    me.fireEvent('datachanged', me);
+                }
+            }
+        }
+    },
+
+    /**
+     * Revert to a view of the Record cache with no filtering applied.
+     * @param {Boolean} suppressEvent If <tt>true</tt> the filter is cleared silently without firing the
+     * {@link #datachanged} event.
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * Returns true if this store is currently filtered
+     * @return {Boolean}
+     */
+    isFiltered: function() {
+        var snapshot = this.snapshot;
+        return !! snapshot && snapshot !== this.data;
+    },
+
+    /**
+     * Filter by a function. The specified function will be called for each
+     * Record in this Store. If the function returns <tt>true</tt> the Record is included,
+     * otherwise it is filtered out.
+     * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
+     * <li><b>record</b> : Ext.data.Model<p class="sub-desc">The {@link Ext.data.Model record}
+     * to test for filtering. Access field values using {@link Ext.data.Model#get}.</p></li>
+     * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
+     * </ul>
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
+     */
+    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);
+    },
+
+    /**
+     * Query the cached records in this Store using a filtering function. The specified function
+     * will be called with each record in this Store. If the function returns <tt>true</tt> the record is
+     * included in the results.
+     * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
+     * <li><b>record</b> : Ext.data.Model<p class="sub-desc">The {@link Ext.data.Model record}
+     * to test for filtering. Access field values using {@link Ext.data.Model#get}.</p></li>
+     * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
+     * </ul>
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
+     * @return {MixedCollection} Returns an Ext.util.MixedCollection of the matched records
+     **/
+    queryBy: function(fn, scope) {
+        var me = this,
+        data = me.snapshot || me.data;
+        return data.filterBy(fn, scope || me);
+    },
+
+    /**
+     * Loads an array of data straight into the Store
+     * @param {Array} data Array of data to load. Any non-model instances will be cast into model instances first
+     * @param {Boolean} append True to add the records to the existing records in the store, false to remove the old ones first
+     */
+    loadData: function(data, append) {
+        var model = this.model,
+            length = data.length,
+            i,
+            record;
+
+        //make sure each data element is an Ext.data.Model instance
+        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});
+    },
+
+    /**
+     * Loads an array of {@Ext.data.Model model} instances into the store, fires the datachanged event. This should only usually
+     * be called internally when loading from the {@link Ext.data.proxy.Proxy Proxy}, when adding records manually use {@link #add} instead
+     * @param {Array} records The array of records to load
+     * @param {Object} options {addRecords: true} to add these records to the existing records, false to remove the Store's existing records first
+     */
+    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);
+
+        //FIXME: this is not a good solution. Ed Spencer is totally responsible for this and should be forced to fix it immediately.
+        for (; i < length; i++) {
+            if (options.start !== undefined) {
+                records[i].index = options.start + i;
+
+            }
+            records[i].join(me);
+        }
+
+        /*
+         * this rather inelegant suspension and resumption of events is required because both the filter and sort functions
+         * fire an additional datachanged event, which is not wanted. Ideally we would do this a different way. The first
+         * datachanged event is fired by the call to this.add, above.
+         */
+        me.suspendEvents();
+
+        if (me.filterOnLoad && !me.remoteFilter) {
+            me.filter();
+        }
+
+        if (me.sortOnLoad && !me.remoteSort) {
+            me.sort();
+        }
+
+        me.resumeEvents();
+        me.fireEvent('datachanged', me, records);
+    },
+
+    // PAGING METHODS
+    /**
+     * Loads a given 'page' of data by setting the start and limit values appropriately. Internally this just causes a normal
+     * load operation, passing in calculated 'start' and 'limit' params
+     * @param {Number} page The number of the page to load
+     */
+    loadPage: function(page) {
+        var me = this;
+
+        me.currentPage = page;
+
+        me.read({
+            page: page,
+            start: (page - 1) * me.pageSize,
+            limit: me.pageSize,
+            addRecords: !me.clearOnPageLoad
+        });
+    },
+
+    /**
+     * Loads the next 'page' in the current data set
+     */
+    nextPage: function() {
+        this.loadPage(this.currentPage + 1);
+    },
+
+    /**
+     * Loads the previous 'page' in the current data set
+     */
+    previousPage: function() {
+        this.loadPage(this.currentPage - 1);
+    },
+
+    // private
+    clearData: function() {
+        this.data.each(function(record) {
+            record.unjoin();
+        });
+
+        this.data.clear();
+    },
+    
+    // Buffering
+    /**
+     * Prefetches data the Store using its configured {@link #proxy}.
+     * @param {Object} options Optional config object, passed into the Ext.data.Operation object before loading.
+     * See {@link #load}
+     */
+    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);
+
+        // HACK to implement loadMask support.
+        //if (operation.blocking) {
+        //    me.fireEvent('beforeload', me, operation);
+        //}
+        if (me.fireEvent('beforeprefetch', me, operation) !== false) {
+            me.loading = true;
+            me.proxy.read(operation, me.onProxyPrefetch, me);
+        }
+        
+        return me;
+    },
+    
+    /**
+     * Prefetches a page of data.
+     * @param {Number} page The page to prefetch
+     * @param {Object} options Optional config object, passed into the Ext.data.Operation object before loading.
+     * See {@link #load}
+     * @param
+     */
+    prefetchPage: function(page, options) {
+        var me = this,
+            pageSize = me.pageSize,
+            start = (page - 1) * me.pageSize,
+            end = start + pageSize;
+        
+        // Currently not requesting this page and range isn't already satisified 
+        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);
+        }
+        
+    },
+    
+    /**
+     * Returns a unique requestId to track requests.
+     * @private
+     */
+    getRequestId: function() {
+        this.requestSeed = this.requestSeed || 1;
+        return this.requestSeed++;
+    },
+    
+    /**
+     * Handles a success pre-fetch
+     * @private
+     * @param {Ext.data.Operation} operation The operation that completed
+     */
+    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);
+        
+        // HACK to support loadMask
+        if (operation.blocking) {
+            me.fireEvent('load', me, records, successful);
+        }
+
+        //this is a callback that would have been passed to the 'read' function and is optional
+        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
+    },
+    
+    /**
+     * Caches the records in the prefetch and stripes them with their server-side
+     * index.
+     * @private
+     * @param {Array} records The records to cache
+     * @param {Ext.data.Operation} The associated operation
+     */
+    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++) {
+            // this is the true index, not the viewIndex
+            records[i].index = start + i;
+        }
+        
+        me.prefetchData.addAll(records);
+        if (me.purgePageCount) {
+            me.purgeRecords();
+        }
+        
+    },
+    
+    
+    /**
+     * Purge the least recently used records in the prefetch if the purgeCount
+     * has been exceeded.
+     */
+    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);
+        }
+    },
+    
+    /**
+     * Determines if the range has already been satisfied in the prefetchData.
+     * @private
+     * @param {Number} start The start index
+     * @param {Number} end The end index in the range
+     */
+    rangeSatisfied: function(start, end) {
+        var me = this,
+            i = start,
+            satisfied = true;
+
+        for (; i < end; i++) {
+            if (!me.prefetchData.getByKey(i)) {
+                satisfied = false;
+                //<debug>
+                if (end - i > me.pageSize) {
+                    Ext.Error.raise("A single page prefetch could never satisfy this request.");
+                }
+                //</debug>
+                break;
+            }
+        }
+        return satisfied;
+    },
+    
+    /**
+     * Determines the page from a record index
+     * @param {Number} index The record index
+     * @return {Number} The page the record belongs to
+     */
+    getPageFromRecordIndex: function(index) {
+        return Math.floor(index / this.pageSize) + 1;
+    },
+    
+    /**
+     * Handles a guaranteed range being loaded
+     * @private
+     */
+    onGuaranteedRange: function() {
+        var me = this,
+            totalCount = me.getTotalCount(),
+            start = me.requestStart,
+            end = ((totalCount - 1) < me.requestEnd) ? totalCount - 1 : me.requestEnd,
+            range = [],
+            record,
+            i = start;
+            
+        //<debug>
+        if (start > end) {
+            Ext.Error.raise("Start (" + start + ") was greater than end (" + end + ")");
+        }
+        //</debug>
+        
+        if (start !== me.guaranteedStart && end !== me.guaranteedEnd) {
+            me.guaranteedStart = start;
+            me.guaranteedEnd = end;
+            
+            for (; i <= end; i++) {
+                record = me.prefetchData.getByKey(i);
+                //<debug>
+                if (!record) {
+                    Ext.Error.raise("Record was not found and store said it was guaranteed");
+                }
+                //</debug>
+                range.push(record);
+            }
+            me.fireEvent('guaranteedrange', range, start, end);
+            if (me.cb) {
+                me.cb.call(me.scope || me, range);
+            }
+        }
+        
+        me.unmask();
+    },
+    
+    // hack to support loadmask
+    mask: function() {
+        this.masked = true;
+        this.fireEvent('beforeload');
+    },
+    
+    // hack to support loadmask
+    unmask: function() {
+        if (this.masked) {
+            this.fireEvent('load');
+        }
+    },
+    
+    /**
+     * Returns the number of pending requests out.
+     */
+    hasPendingRequests: function() {
+        return this.pendingRequests.length;
+    },
+    
+    
+    // wait until all requests finish, until guaranteeing the range.
+    onWaitForGuarantee: function() {
+        if (!this.hasPendingRequests()) {
+            this.onGuaranteedRange();
+        }
+    },
+    
+    /**
+     * Guarantee a specific range, this will load the store with a range (that
+     * must be the pageSize or smaller) and take care of any loading that may
+     * be necessary.
+     */
+    guaranteeRange: function(start, end, cb, scope) {
+        //<debug>
+        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"
+                });
+            }
+        }
+        //</debug>
+        
+        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;
+        // neither beginning or end are loaded
+        if (!startLoaded || !endLoaded) {
+            // same page, lets load it
+            if (startPage === endPage) {
+                me.mask();
+                me.prefetchPage(startPage, {
+                    //blocking: true,
+                    callback: me.onWaitForGuarantee,
+                    scope: me
+                });
+            // need to load two pages
+            } else {
+                me.mask();
+                me.prefetchPage(startPage, {
+                    //blocking: true,
+                    callback: me.onWaitForGuarantee,
+                    scope: me
+                });
+                me.prefetchPage(endPage, {
+                    //blocking: true,
+                    callback: me.onWaitForGuarantee,
+                    scope: me
+                });
+            }
+        // Request was already satisfied via the prefetch
+        } else {
+            me.onGuaranteedRange();
+        }
+    },
+    
+    // because prefetchData is stored by index
+    // this invalidates all of the prefetchedData
+    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;
+                range;
+                
+                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);
+        }
+    },
+
+    // overriden to provide striping of the indexes as sorting occurs.
+    // this cannot be done inside of sort because datachanged has already
+    // fired and will trigger a repaint of the bound view.
+    doSort: function(sorterFn) {
+        var me = this;
+        if (me.remoteSort) {
+            //the load function will pick up the new sorters and request the sorted data from the proxy
+            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);
+        }
+    },
+    
+    /**
+     * Finds the index of the first matching Record in this store by a specific field value.
+     * @param {String} fieldName The name of the Record field to test.
+     * @param {String/RegExp} value Either a string that the field value
+     * should begin with, or a RegExp to test against the field.
+     * @param {Number} startIndex (optional) The index to start searching at
+     * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
+     * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
+     * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
+     * @return {Number} The matched index or -1
+     */
+    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;
+    },
+
+    /**
+     * Finds the first matching Record in this store by a specific field value.
+     * @param {String} fieldName The name of the Record field to test.
+     * @param {String/RegExp} value Either a string that the field value
+     * should begin with, or a RegExp to test against the field.
+     * @param {Number} startIndex (optional) The index to start searching at
+     * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
+     * @param {Boolean} caseSensitive (optional) True for case sensitive comparison
+     * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
+     * @return {Ext.data.Model} The matched record or null
+     */
+    findRecord: function() {
+        var me = this,
+            index = me.find.apply(me, arguments);
+        return index !== -1 ? me.getAt(index) : null;
+    },
+
+    /**
+     * @private
+     * Returns a filter function used to test a the given property's value. Defers most of the work to
+     * Ext.util.MixedCollection's createValueMatcher function
+     * @param {String} property The property to create the filter function for
+     * @param {String/RegExp} value The string/regex to compare the property value to
+     * @param {Boolean} anyMatch True if we don't care if the filter value is not the full value (defaults to false)
+     * @param {Boolean} caseSensitive True to create a case-sensitive regex (defaults to false)
+     * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false.
+     * Ignored if anyMatch is true.
+     */
+    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]);
+        };
+    },
+
+    /**
+     * Finds the index of the first matching Record in this store by a specific field value.
+     * @param {String} fieldName The name of the Record field to test.
+     * @param {Mixed} value The value to match the field against.
+     * @param {Number} startIndex (optional) The index to start searching at
+     * @return {Number} The matched index or -1
+     */
+    findExact: function(property, value, start) {
+        return this.data.findIndexBy(function(rec) {
+            return rec.get(property) === value;
+        },
+        this, start);
+    },
+
+    /**
+     * Find the index of the first matching Record in this Store by a function.
+     * If the function returns <tt>true</tt> it is considered a match.
+     * @param {Function} fn The function to be called. It will be passed the following parameters:<ul>
+     * <li><b>record</b> : Ext.data.Model<p class="sub-desc">The {@link Ext.data.Model record}
+     * to test for filtering. Access field values using {@link Ext.data.Model#get}.</p></li>
+     * <li><b>id</b> : Object<p class="sub-desc">The ID of the Record passed.</p></li>
+     * </ul>
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this Store.
+     * @param {Number} startIndex (optional) The index to start searching at
+     * @return {Number} The matched index or -1
+     */
+    findBy: function(fn, scope, start) {
+        return this.data.findIndexBy(fn, scope, start);
+    },
+
+    /**
+     * Collects unique values for a particular dataIndex from this store.
+     * @param {String} dataIndex The property to collect
+     * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
+     * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
+     * @return {Array} An array of the unique values
+     **/
+    collect: function(dataIndex, allowNull, bypassFilter) {
+        var me = this,
+            data = (bypassFilter === true && me.snapshot) ? me.snapshot: me.data;
+
+        return data.collect(dataIndex, 'data', allowNull);
+    },
+
+    /**
+     * Gets the number of cached records.
+     * <p>If using paging, this may not be the total size of the dataset. If the data object
+     * used by the Reader contains the dataset size, then the {@link #getTotalCount} function returns
+     * the dataset size.  <b>Note</b>: see the Important note in {@link #load}.</p>
+     * @return {Number} The number of Records in the Store's cache.
+     */
+    getCount: function() {
+        return this.data.length || 0;
+    },
+
+    /**
+     * Returns the total number of {@link Ext.data.Model Model} instances that the {@link Ext.data.proxy.Proxy Proxy}
+     * indicates exist. This will usually differ from {@link #getCount} when using paging - getCount returns the
+     * number of records loaded into the Store at the moment, getTotalCount returns the number of records that
+     * could be loaded into the Store if the Store contained all data
+     * @return {Number} The total number of Model instances available via the Proxy
+     */
+    getTotalCount: function() {
+        return this.totalCount;
+    },
+
+    /**
+     * Get the Record at the specified index.
+     * @param {Number} index The index of the Record to find.
+     * @return {Ext.data.Model} The Record at the passed index. Returns undefined if not found.
+     */
+    getAt: function(index) {
+        return this.data.getAt(index);
+    },
+
+    /**
+     * Returns a range of Records between specified indices.
+     * @param {Number} startIndex (optional) The starting index (defaults to 0)
+     * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
+     * @return {Ext.data.Model[]} An array of Records
+     */
+    getRange: function(start, end) {
+        return this.data.getRange(start, end);
+    },
+
+    /**
+     * Get the Record with the specified id.
+     * @param {String} id The id of the Record to find.
+     * @return {Ext.data.Model} The Record with the passed id. Returns undefined if not found.
+     */
+    getById: function(id) {
+        return (this.snapshot || this.data).findBy(function(record) {
+            return record.getId() === id;
+        });
+    },
+
+    /**
+     * Get the index within the cache of the passed Record.
+     * @param {Ext.data.Model} record The Ext.data.Model object to find.
+     * @return {Number} The index of the passed Record. Returns -1 if not found.
+     */
+    indexOf: function(record) {
+        return this.data.indexOf(record);
+    },
+
+
+    /**
+     * Get the index within the entire dataset. From 0 to the totalCount.
+     * @param {Ext.data.Model} record The Ext.data.Model object to find.
+     * @return {Number} The index of the passed Record. Returns -1 if not found.
+     */
+    indexOfTotal: function(record) {
+        return record.index || this.indexOf(record);
+    },
+
+    /**
+     * Get the index within the cache of the Record with the passed id.
+     * @param {String} id The id of the Record to find.
+     * @return {Number} The index of the Record. Returns -1 if not found.
+     */
+    indexOfId: function(id) {
+        return this.data.indexOfKey(id);
+    },
+        
+    /**
+     * Remove all items from the store.
+     * @param {Boolean} silent Prevent the `clear` event from being fired.
+     */
+    removeAll: function(silent) {
+        var me = this;
+
+        me.clearData();
+        if (me.snapshot) {
+            me.snapshot.clear();
+        }
+        if (silent !== true) {
+            me.fireEvent('clear', me);
+        }
+    },
+
+    /*
+     * Aggregation methods
+     */
+
+    /**
+     * Convenience function for getting the first model instance in the store
+     * @param {Boolean} grouped (Optional) True to perform the operation for each group
+     * in the store. The value returned will be an object literal with the key being the group
+     * name and the first record being the value. The grouped parameter is only honored if
+     * the store has a groupField.
+     * @return {Ext.data.Model/undefined} The first model instance in the store, or undefined
+     */
+    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();
+        }
+    },
+
+    /**
+     * Convenience function for getting the last model instance in the store
+     * @param {Boolean} grouped (Optional) True to perform the operation for each group
+     * in the store. The value returned will be an object literal with the key being the group
+     * name and the last record being the value. The grouped parameter is only honored if
+     * the store has a groupField.
+     * @return {Ext.data.Model/undefined} The last model instance in the store, or undefined
+     */
+    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();
+        }
+    },
+
+    /**
+     * Sums the value of <tt>property</tt> for each {@link Ext.data.Model record} between <tt>start</tt>
+     * and <tt>end</tt> and returns the result.
+     * @param {String} field A field in each record
+     * @param {Boolean} grouped (Optional) True to perform the operation for each group
+     * in the store. The value returned will be an object literal with the key being the group
+     * name and the sum for that group being the value. The grouped parameter is only honored if
+     * the store has a groupField.
+     * @return {Number} The sum
+     */
+    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);
+        }
+    },
+
+    // @private, see sum
+    getSum: function(records, field) {
+        var total = 0,
+            i = 0,
+            len = records.length;
+
+        for (; i < len; ++i) {
+            total += records[i].get(field);
+        }
+
+        return total;
+    },
+
+    /**
+     * Gets the count of items in the store.
+     * @param {Boolean} grouped (Optional) True to perform the operation for each group
+     * in the store. The value returned will be an object literal with the key being the group
+     * name and the count for each group being the value. The grouped parameter is only honored if
+     * the store has a groupField.
+     * @return {Number} the count
+     */
+    count: function(grouped) {
+        var me = this;
+
+        if (grouped && me.isGrouped()) {
+            return me.aggregate(function(records) {
+                return records.length;
+            }, me, true);
+        } else {
+            return me.getCount();
+        }
+    },
+
+    /**
+     * Gets the minimum value in the store.
+     * @param {String} field The field in each record
+     * @param {Boolean} grouped (Optional) True to perform the operation for each group
+     * in the store. The value returned will be an object literal with the key being the group
+     * name and the minimum in the group being the value. The grouped parameter is only honored if
+     * the store has a groupField.
+     * @return {Mixed/undefined} The minimum value, if no items exist, undefined.
+     */
+    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);
+        }
+    },
+
+    // @private, see min
+    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;
+    },
+
+    /**
+     * Gets the maximum value in the store.
+     * @param {String} field The field in each record
+     * @param {Boolean} grouped (Optional) True to perform the operation for each group
+     * in the store. The value returned will be an object literal with the key being the group
+     * name and the maximum in the group being the value. The grouped parameter is only honored if
+     * the store has a groupField.
+     * @return {Mixed/undefined} The maximum value, if no items exist, undefined.
+     */
+    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);
+        }
+    },
+
+    // @private, see max
+    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;
+    },
+
+    /**
+     * Gets the average value in the store.
+     * @param {String} field The field in each record
+     * @param {Boolean} grouped (Optional) True to perform the operation for each group
+     * in the store. The value returned will be an object literal with the key being the group
+     * name and the group average being the value. The grouped parameter is only honored if
+     * the store has a groupField.
+     * @return {Mixed/undefined} The average value, if no items exist, 0.
+     */
+    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);
+        }
+    },
+
+    // @private, see average
+    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;
+    },
+
+    /**
+     * Runs the aggregate function for all the records in the store.
+     * @param {Function} fn The function to execute. The function is called with a single parameter,
+     * an array of records for that group.
+     * @param {Object} scope (optional) The scope to execute the function in. Defaults to the store.
+     * @param {Boolean} grouped (Optional) True to perform the operation for each group
+     * in the store. The value returned will be an object literal with the key being the group
+     * name and the group average being the value. The grouped parameter is only honored if
+     * the store has a groupField.
+     * @param {Array} args (optional) Any arguments to append to the function call
+     * @return {Object} An object literal with the group names and their appropriate values.
+     */
+    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));
+        }
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.JsonStore
+ * @extends Ext.data.Store
+ * @ignore
+ *
+ * <p>Small helper class to make creating {@link Ext.data.Store}s from JSON data easier.
+ * A JsonStore will be automatically configured with a {@link Ext.data.reader.Json}.</p>
+ *
+ * <p>A store configuration would be something like:</p>
+ *
+<pre><code>
+var store = new Ext.data.JsonStore({
+    // store configs
+    autoDestroy: true,
+    storeId: 'myStore'
+
+    proxy: {
+        type: 'ajax',
+        url: 'get-images.php',
+        reader: {
+            type: 'json',
+            root: 'images',
+            idProperty: 'name'
+        }
+    },
+
+    //alternatively, a {@link Ext.data.Model} name can be given (see {@link Ext.data.Store} for an example)
+    fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
+});
+</code></pre>
+ *
+ * <p>This store is configured to consume a returned object of the form:<pre><code>
+{
+    images: [
+        {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
+        {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
+    ]
+}
+</code></pre>
+ *
+ * <p>An object literal of this form could also be used as the {@link #data} config option.</p>
+ *
+ * @constructor
+ * @param {Object} config
+ * @xtype jsonstore
+ */
+Ext.define('Ext.data.JsonStore',  {
+    extend: 'Ext.data.Store',
+    alias: 'store.json',
+
+    /**
+     * @cfg {Ext.data.DataReader} reader @hide
+     */
+    constructor: function(config) {
+        config = config || {};
+
+        Ext.applyIf(config, {
+            proxy: {
+                type  : 'ajax',
+                reader: 'json',
+                writer: 'json'
+            }
+        });
+
+        this.callParent([config]);
+    }
+});
+
+/**
+ * @class Ext.chart.axis.Time
+ * @extends Ext.chart.axis.Axis
+ *
+ * A type of axis whose units are measured in time values. Use this axis
+ * for listing dates that you will want to group or dynamically change.
+ * If you just want to display dates as categories then use the
+ * Category class for axis instead.
+ *
+ * For example:
+ *
+  <pre><code>
+    axes: [{
+        type: 'Time',
+        position: 'bottom',
+        fields: 'date',
+        title: 'Day',
+        dateFormat: 'M d',
+        groupBy: 'year,month,day',
+        aggregateOp: 'sum',
+
+        constrain: true,
+        fromDate: new Date('1/1/11'),
+        toDate: new Date('1/7/11')
+    }]
+  </code></pre>
+ *
+ * In this example we're creating a time axis that has as title <em>Day</em>.
+ * The field the axis is bound to is <em>date</em>.
+ * The date format to use to display the text for the axis labels is <em>M d</em>
+ * which is a three letter month abbreviation followed by the day number.
+ * The time axis will show values for dates betwee <em>fromDate</em> and <em>toDate</em>.
+ * Since <em>constrain</em> is set to true all other values for other dates not between
+ * the fromDate and toDate will not be displayed.
+ * 
+ * @constructor
+ */
+Ext.define('Ext.chart.axis.Time', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.axis.Category',
+
+    alternateClassName: 'Ext.chart.TimeAxis',
+
+    alias: 'axis.time',
+
+    requires: ['Ext.data.Store', 'Ext.data.JsonStore'],
+
+    /* End Definitions */
+
+     /**
+      * The minimum value drawn by the axis. If not set explicitly, the axis
+      * minimum will be calculated automatically.
+      * @property calculateByLabelSize
+      * @type Boolean
+      */
+    calculateByLabelSize: true,
+    
+     /**
+     * Indicates the format the date will be rendered on. 
+     * For example: 'M d' will render the dates as 'Jan 30', etc.
+      *
+     * @property dateFormat
+     * @type {String|Boolean}
+      */
+    dateFormat: false,
+    
+     /**
+     * Indicates the time unit to use for each step. Can be 'day', 'month', 'year' or a comma-separated combination of all of them.
+     * Default's 'year,month,day'.
+     *
+     * @property timeUnit
+     * @type {String}
+     */
+    groupBy: 'year,month,day',
+    
+    /**
+     * Aggregation operation when grouping. Possible options are 'sum', 'avg', 'max', 'min'. Default's 'sum'.
+     * 
+     * @property aggregateOp
+     * @type {String}
+      */
+    aggregateOp: 'sum',
+    
+    /**
+     * The starting date for the time axis.
+     * @property fromDate
+     * @type Date
+     */
+    fromDate: false,
+    
+    /**
+     * The ending date for the time axis.
+     * @property toDate
+     * @type Date
+     */
+    toDate: false,
+    
+    /**
+     * An array with two components: The first is the unit of the step (day, month, year, etc). The second one is the number of units for the step (1, 2, etc.).
+     * Default's [Ext.Date.DAY, 1].
+     * 
+     * @property step 
+     * @type Array
+     */
+    step: [Ext.Date.DAY, 1],
+    
+    /**
+     * If true, the values of the chart will be rendered only if they belong between the fromDate and toDate. 
+     * If false, the time axis will adapt to the new values by adding/removing steps.
+     * Default's [Ext.Date.DAY, 1].
+     * 
+     * @property constrain 
+     * @type Boolean
+     */
+    constrain: false,
+    
+    // @private a wrapper for date methods.
+    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();
+        }
+    },
+    
+    // @private holds aggregate functions.
+    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;
+            }
+        };
+    })(),
+    
+    // @private normalized the store to fill date gaps in the time interval.
+    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;
+    },
+    
+    // @private aggregates values if multiple store elements belong to the same time step.
+    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) {
+            //get all record field names in a simple array
+            if (!recFields.length) {
+                rec.fields.each(function(f) {
+                    recFields.push(f.name);
+                });
+                recFieldsLen = recFields.length;
+            }
+            //get record date value
+            value = rec.get(field);
+            //generate key for grouping records
+            for (i = 0; i < l; i++) {
+                if (i == 0) {
+                    key = String(dateMethods[fields[i]](value));
+                } else {
+                    key += '||' + dateMethods[fields[i]](value);
+                }
+            }
+            //get aggregation record from hash
+            if (key in aggStore) {
+                obj = aggStore[key];
+            } else {
+                obj = aggStore[key] = {};
+                aggKeys.push(key);
+                dates.push(value);
+            }
+            //append record values to an aggregation record
+            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));
+                }
+            }
+        });
+        //perform aggregation operations on fields
+        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;
+    },
+    
+    // @private creates a label array to be used as the axis labels.
+     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() {
+         //TODO(nico): fix this eventually...
+         if (this.constrain) {
+             this.constrainDates();
+             this.aggregate();
+             this.chart.substore = this.chart.filteredStore;
+         } else {
+             this.aggregate();
+         }
+    },
+
+     // @private modifies the store and creates the labels for the axes.
+     applyData: function() {
+        this.setLabels();
+        var count = this.chart.substore.getCount();
+         return {
+             from: 0,
+             to: count,
+             steps: count - 1,
+             step: 1
+         };
+     }
+ });
+
+
+/**
+ * @class Ext.chart.series.Series
+ * 
+ * Series is the abstract class containing the common logic to all chart series. Series includes 
+ * methods from Labels, Highlights, Tips and Callouts mixins. This class implements the logic of handling 
+ * mouse events, animating, hiding, showing all elements and returning the color of the series to be used as a legend item.
+ *
+ * ## Listeners
+ *
+ * The series class supports listeners via the Observable syntax. Some of these listeners are:
+ *
+ *  - `itemmouseup` When the user interacts with a marker.
+ *  - `itemmousedown` When the user interacts with a marker.
+ *  - `itemmousemove` When the user iteracts with a marker.
+ *  - `afterrender` Will be triggered when the animation ends or when the series has been rendered completely.
+ *
+ * For example:
+ *
+ *     series: [{
+ *             type: 'column',
+ *             axis: 'left',
+ *             listeners: {
+ *                     'afterrender': function() {
+ *                             console('afterrender');
+ *                     }
+ *             },
+ *             xField: 'category',
+ *             yField: 'data1'
+ *     }]
+ *     
+ */
+Ext.define('Ext.chart.series.Series', {
+
+    /* Begin Definitions */
+
+    mixins: {
+        observable: 'Ext.util.Observable',
+        labels: 'Ext.chart.Label',
+        highlights: 'Ext.chart.Highlight',
+        tips: 'Ext.chart.Tip',
+        callouts: 'Ext.chart.Callout'
+    },
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Boolean|Object} highlight
+     * If set to `true` it will highlight the markers or the series when hovering
+     * with the mouse. This parameter can also be an object with the same style
+     * properties you would apply to a {@link Ext.draw.Sprite} to apply custom
+     * styles to markers and series.
+     */
+
+    /**
+     * @cfg {Object} tips
+     * Add tooltips to the visualization's markers. The options for the tips are the
+     * same configuration used with {@link Ext.tip.ToolTip}. For example:
+     *
+     *     tips: {
+     *       trackMouse: true,
+     *       width: 140,
+     *       height: 28,
+     *       renderer: function(storeItem, item) {
+     *         this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data1') + ' views');
+     *       }
+     *     },
+     */
+
+    /**
+     * @cfg {String} type
+     * The type of series. Set in subclasses.
+     */
+    type: null,
+
+    /**
+     * @cfg {String} title
+     * The human-readable name of the series.
+     */
+    title: null,
+
+    /**
+     * @cfg {Boolean} showInLegend
+     * Whether to show this series in the legend.
+     */
+    showInLegend: true,
+
+    /**
+     * @cfg {Function} renderer
+     * A function that can be overridden to set custom styling properties to each rendered element.
+     * Passes in (sprite, record, attributes, index, store) to the function.
+     */
+    renderer: function(sprite, record, attributes, index, store) {
+        return attributes;
+    },
+
+    /**
+     * @cfg {Array} shadowAttributes
+     * An array with shadow attributes
+     */
+    shadowAttributes: null,
+    
+    //@private triggerdrawlistener flag
+    triggerAfterDraw: false,
+
+    /**
+     * @cfg {Object} listeners  
+     * An (optional) object with event callbacks. All event callbacks get the target *item* as first parameter. The callback functions are:
+     *  
+     *  <ul>
+     *      <li>itemmouseover</li>
+     *      <li>itemmouseout</li>
+     *      <li>itemmousedown</li>
+     *      <li>itemmouseup</li>
+     *  </ul>
+     */
+    
+    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,
+
+            /**
+             * @event titlechange
+             * Fires when the series title is changed via {@link #setTitle}.
+             * @param {String} title The new title value
+             * @param {Number} index The index in the collection of titles
+             */
+            titlechange: true
+        });
+
+        me.mixins.observable.constructor.call(me, config);
+
+        me.on({
+            scope: me,
+            itemmouseover: me.onItemMouseOver,
+            itemmouseout: me.onItemMouseOut,
+            mouseleave: me.onMouseLeave
+        });
+    },
+
+    // @private set the bbox and clipBox for the series
+    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;
+    },
+
+    // @private set the animation for the sprite
+    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');
+                    }    
+                }    
+            }));
+        }
+    },
+    
+    // @private return the gutter.
+    getGutters: function() {
+        return [0, 0];
+    },
+
+    // @private wrapper for the itemmouseover event.
+    onItemMouseOver: function(item) { 
+        var me = this;
+        if (item.series === me) {
+            if (me.highlight) {
+                me.highlightItem(item);
+            }
+            if (me.tooltip) {
+                me.showTip(item);
+            }
+        }
+    },
+
+    // @private wrapper for the itemmouseout event.
+    onItemMouseOut: function(item) {
+        var me = this;
+        if (item.series === me) {
+            me.unHighlightItem();
+            if (me.tooltip) {
+                me.hideTip(item);
+            }
+        }
+    },
+
+    // @private wrapper for the mouseleave event.
+    onMouseLeave: function() {
+        var me = this;
+        me.unHighlightItem();
+        if (me.tooltip) {
+            me.hideTip();
+        }
+    },
+
+    /**
+     * For a given x/y point relative to the Surface, find a corresponding item from this
+     * series, if any.
+     * @param {Number} x
+     * @param {Number} y
+     * @return {Object} An object describing the item, or null if there is no matching item. The exact contents of
+     *                  this object will vary by series type, but should always contain at least the following:
+     *                  <ul>
+     *                    <li>{Ext.chart.series.Series} series - the Series object to which the item belongs</li>
+     *                    <li>{Object} value - the value(s) of the item's data point</li>
+     *                    <li>{Array} point - the x/y coordinates relative to the chart box of a single point
+     *                        for this data item, which can be used as e.g. a tooltip anchor point.</li>
+     *                    <li>{Ext.draw.Sprite} sprite - the item's rendering Sprite.
+     *                  </ul>
+     */
+    getItemForPoint: function(x, y) {
+        //if there are no items to query just return null.
+        if (!this.items || !this.items.length || this.seriesIsHidden) {
+            return null;
+        }
+        var me = this,
+            items = me.items,
+            bbox = me.bbox,
+            item, i, ln;
+        // Check bounds
+        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;
+    },
+
+    /**
+     * Hides all the elements in the series.
+     */
+    hideAll: function() {
+        var me = this,
+            items = me.items,
+            item, len, i, sprite;
+
+        me.seriesIsHidden = true;
+        me._prevShowMarkers = me.showMarkers;
+
+        me.showMarkers = false;
+        //hide all labels
+        me.hideLabels(0);
+        //hide all sprites
+        for (i = 0, len = items.length; i < len; i++) {
+            item = items[i];
+            sprite = item.sprite;
+            if (sprite) {
+                sprite.setAttributes({
+                    hidden: true
+                }, true);
+            }
+        }
+    },
+
+    /**
+     * Shows all the elements in the series.
+     */
+    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;
+    },
+    
+    /**
+     * Returns a string with the color to be used for the series legend item. 
+     */
+    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';
+    },
+    
+    /**
+     * Checks whether the data field should be visible in the legend
+     * @private
+     * @param {Number} index The index of the current item
+     */
+    visibleInLegend: function(index){
+        var excludes = this.__excludes;
+        if (excludes) {
+            return !excludes[index];
+        }
+        return !this.seriesIsHidden;
+    },
+
+    /**
+     * Changes the value of the {@link #title} for the series.
+     * Arguments can take two forms:
+     * <ul>
+     * <li>A single String value: this will be used as the new single title for the series (applies
+     * to series with only one yField)</li>
+     * <li>A numeric index and a String value: this will set the title for a single indexed yField.</li>
+     * </ul>
+     * @param {Number} index
+     * @param {String} title
+     */
+    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);
+    }
+});
+
+/**
+ * @class Ext.chart.series.Cartesian
+ * @extends Ext.chart.series.Series
+ *
+ * Common base class for series implementations which plot values using x/y coordinates.
+ *
+ * @constructor
+ */
+Ext.define('Ext.chart.series.Cartesian', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.series.Series',
+
+    alternateClassName: ['Ext.chart.CartesianSeries', 'Ext.chart.CartesianChart'],
+
+    /* End Definitions */
+
+    /**
+     * The field used to access the x axis value from the items from the data
+     * source.
+     *
+     * @cfg xField
+     * @type String
+     */
+    xField: null,
+
+    /**
+     * The field used to access the y-axis value from the items from the data
+     * source.
+     *
+     * @cfg yField
+     * @type String
+     */
+    yField: null,
+
+    /**
+     * Indicates which axis the series will bind to
+     *
+     * @property axis
+     * @type String
+     */
+    axis: 'left'
+});
+
+/**
+ * @class Ext.chart.series.Area
+ * @extends Ext.chart.series.Cartesian
+ * 
+ <p>
+    Creates a Stacked Area Chart. The stacked area chart is useful when displaying multiple aggregated layers of information.
+    As with all other series, the Area Series must be appended in the *series* Chart array configuration. See the Chart 
+    documentation for more information. A typical configuration object for the area series could be:
+ </p>
+{@img Ext.chart.series.Area/Ext.chart.series.Area.png Ext.chart.series.Area chart series} 
+  <pre><code>
+   var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        store: store,
+        axes: [{
+            type: 'Numeric',
+            grid: true,
+            position: 'left',
+            fields: ['data1', 'data2', 'data3', 'data4', 'data5'],
+            title: 'Sample Values',
+            grid: {
+                odd: {
+                    opacity: 1,
+                    fill: '#ddd',
+                    stroke: '#bbb',
+                    'stroke-width': 1
+                }
+            },
+            minimum: 0,
+            adjustMinimumByMajorUnit: 0
+        }, {
+            type: 'Category',
+            position: 'bottom',
+            fields: ['name'],
+            title: 'Sample Metrics',
+            grid: true,
+            label: {
+                rotate: {
+                    degrees: 315
+                }
+            }
+        }],
+        series: [{
+            type: 'area',
+            highlight: false,
+            axis: 'left',
+            xField: 'name',
+            yField: ['data1', 'data2', 'data3', 'data4', 'data5'],
+            style: {
+                opacity: 0.93
+            }
+        }]
+    });
+   </code></pre>
+  
+ <p>
+  In this configuration we set `area` as the type for the series, set highlighting options to true for highlighting elements on hover, 
+  take the left axis to measure the data in the area series, set as xField (x values) the name field of each element in the store, 
+  and as yFields (aggregated layers) seven data fields from the same store. Then we override some theming styles by adding some opacity 
+  to the style object.
+ </p>
+  
+ * @xtype area
+ * 
+ */
+Ext.define('Ext.chart.series.Area', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.series.Cartesian',
+    
+    alias: 'series.area',
+
+    requires: ['Ext.chart.axis.Axis', 'Ext.draw.Color', 'Ext.fx.Anim'],
+
+    /* End Definitions */
+
+    type: 'area',
+
+    // @private Area charts are alyways stacked
+    stacked: true,
+
+    /**
+     * @cfg {Object} style 
+     * Append styling properties to this object for it to override theme properties.
+     */
+    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);
+    },
+
+    // @private Shrinks dataSets down to a smaller size
+    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 = [];
+        //initialize array
+        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) {
+                //push averages
+                xRes.push(xSum/ratio);
+                for (j = 0; j < yCompLen; ++j) {
+                    ySum[j] /= ratio;
+                }
+                yRes.push(ySum);
+                //reset sum accumulators
+                xSum = 0;
+                for (j = 0, ySum = []; j < yCompLen; ++j) {
+                    ySum[j] = 0;
+                }
+            }
+        }
+        return {
+            x: xRes,
+            y: yRes
+        };
+    },
+
+    // @private Get chart and data boundaries
+    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;
+
+        // Run through the axis
+        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
+        };
+    },
+
+    // @private Build an array of paths for the chart
+    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;
+        // Start the path
+        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++) {
+                // Excluded series
+                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]);
+            }
+        }
+        
+        // Close the paths
+        for (areaIndex = 0; areaIndex < bounds.areasLen; areaIndex++) {
+            // Excluded series
+            if (me.__excludes[areaIndex]) {
+                continue;
+            }
+            path = paths[areaIndex];
+            // Close bottom path to the axis
+            if (areaIndex == 0 || first) {
+                first = false;
+                path.push('L', x, bbox.y + bbox.height,
+                          'L', bbox.x, bbox.y + bbox.height,
+                          'Z');
+            }
+            // Close other paths to the one before them
+            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
+        };
+    },
+
+    /**
+     * Draws the series for the current chart.
+     */
+    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++) {
+            // Excluded series
+            if (me.__excludes[areaIndex]) {
+                continue;
+            }
+            if (!me.areas[areaIndex]) {
+                me.items[areaIndex].sprite = me.areas[areaIndex] = surface.add(Ext.apply({}, {
+                    type: 'path',
+                    group: group,
+                    // 'clip-rect': me.clipBox,
+                    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) {
+                //Add renderer to line. There is not a unique record associated with this.
+                rendererAttributes = me.renderer(areaElem, false, { 
+                    path: path,
+                    // 'clip-rect': me.clipBox,
+                    fill: colorArrayStyle[areaIndex % colorArrayLength],
+                    stroke: endLineStyle.stroke || colorArrayStyle[areaIndex % colorArrayLength]
+                }, areaIndex, store);
+                //fill should not be used here but when drawing the special fill path object
+                me.animation = me.onAnimate(areaElem, {
+                    to: rendererAttributes
+                });
+            } else {
+                rendererAttributes = me.renderer(areaElem, false, { 
+                    path: path,
+                    // 'clip-rect': me.clipBox,
+                    hidden: false,
+                    fill: colorArrayStyle[areaIndex % colorArrayLength],
+                    stroke: endLineStyle.stroke || colorArrayStyle[areaIndex % colorArrayLength]
+                }, areaIndex, store);
+                me.areas[areaIndex].setAttributes(rendererAttributes, true);
+            }
+        }
+        me.renderLabels();
+        me.renderCallouts();
+    },
+
+    // @private
+    onAnimate: function(sprite, attr) {
+        sprite.show();
+        return this.callParent(arguments);
+    },
+
+    // @private
+    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 || {}));
+    },
+
+    // @private
+    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);
+            }
+        }
+    },
+
+    // @private
+    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;
+
+        //get the right two points
+        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]];
+        
+        //keep the label always on the outer part of the "elbow"
+        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;
+        }
+
+        //position
+        x = cur[0] + normal[0] * offsetFromViz;
+        y = cur[1] + normal[1] * offsetFromViz;
+        
+        //box position and dimensions
+        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;
+        
+        //now check if we're out of bounds and invert the normal vector correspondingly
+        //this may add new overlaps between labels (but labels won't be out of bounds).
+        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;
+        }
+
+        //update positions
+        x = cur[0] + normal[0] * offsetFromViz;
+        y = cur[1] + normal[1] * offsetFromViz;
+        
+        //update box position and dimensions
+        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;
+        
+        //set the line from the middle of the pie to the box.
+        callout.lines.setAttributes({
+            path: ["M", cur[0], cur[1], "L", x, y, "Z"]
+        }, true);
+        //set box position
+        callout.box.setAttributes({
+            x: boxx,
+            y: boxy,
+            width: boxw,
+            height: boxh
+        }, true);
+        //set text position
+        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;
+    },
+
+    /**
+     * Highlight this entire series.
+     * @param {Object} item Info about the item; same format as returned by #getItemForPoint.
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * UnHighlight this entire series.
+     * @param {Object} item Info about the item; same format as returned by #getItemForPoint.
+     */
+    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
+                    }
+                });
+            }
+        }
+    },
+
+    /**
+     * Highlight the specified item. If no item is provided the whole series will be highlighted.
+     * @param item {Object} Info about the item; same format as returned by #getItemForPoint
+     */
+    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);
+    },
+
+    /**
+     * un-highlights the specified item. If no item is provided it will un-highlight the entire series.
+     * @param item {Object} Info about the item; same format as returned by #getItemForPoint
+     */
+    unHighlightItem: function(item) {
+        if (!item) {
+            this.unHighlightSeries();
+        }
+
+        if (this.highlightSprite) {
+            this.highlightSprite.hide(true);
+        }
+    },
+
+    // @private
+    hideAll: function() {
+        if (!isNaN(this._index)) {
+            this.__excludes[this._index] = true;
+            this.areas[this._index].hide(true);
+            this.drawSeries();
+        }
+    },
+
+    // @private
+    showAll: function() {
+        if (!isNaN(this._index)) {
+            this.__excludes[this._index] = false;
+            this.areas[this._index].show(true);
+            this.drawSeries();
+        }
+    },
+
+    /**
+     * Returns the color of the series (to be displayed as color for the series legend item).
+     * @param item {Object} Info about the item; same format as returned by #getItemForPoint
+     */
+    getLegendColor: function(index) {
+        var me = this;
+        return me.colorArrayStyle[index % me.colorArrayStyle.length];
+    }
+});
+
+/**
+ * Creates a Bar Chart. A Bar Chart is a useful visualization technique to display quantitative information for
+ * different categories that can show some progression (or regression) in the dataset. As with all other series, the Bar
+ * Series must be appended in the *series* Chart array configuration. See the Chart documentation for more information.
+ * A typical configuration object for the bar series could be:
+ *
+ * {@img Ext.chart.series.Bar/Ext.chart.series.Bar.png Ext.chart.series.Bar chart series}
+ *
+ *     var store = Ext.create('Ext.data.JsonStore', {
+ *         fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+ *         data: [
+ *             {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+ *             {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+ *             {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+ *             {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+ *             {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
+ *         ]
+ *     });
+ *     
+ *     Ext.create('Ext.chart.Chart', {
+ *         renderTo: Ext.getBody(),
+ *         width: 500,
+ *         height: 300,
+ *         animate: true,
+ *         store: store,
+ *         axes: [{
+ *             type: 'Numeric',
+ *             position: 'bottom',
+ *             fields: ['data1'],
+ *             label: {
+ *                 renderer: Ext.util.Format.numberRenderer('0,0')
+ *             },
+ *             title: 'Sample Values',
+ *             grid: true,
+ *             minimum: 0
+ *         }, {
+ *             type: 'Category',
+ *             position: 'left',
+ *             fields: ['name'],
+ *             title: 'Sample Metrics'
+ *         }],
+ *         series: [{
+ *             type: 'bar',
+ *             axis: 'bottom',
+ *             highlight: true,
+ *             tips: {
+ *               trackMouse: true,
+ *               width: 140,
+ *               height: 28,
+ *               renderer: function(storeItem, item) {
+ *                 this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data1') + ' views');
+ *               }
+ *             },
+ *             label: {
+ *               display: 'insideEnd',
+ *                 field: 'data1',
+ *                 renderer: Ext.util.Format.numberRenderer('0'),
+ *                 orientation: 'horizontal',
+ *                 color: '#333',
+ *                 'text-anchor': 'middle'
+ *             },
+ *             xField: 'name',
+ *             yField: ['data1']
+ *         }]
+ *     });
+ *
+ * In this configuration we set `bar` as the series type, bind the values of the bar to the bottom axis and set the
+ * xField or category field to the `name` parameter of the store. We also set `highlight` to true which enables smooth
+ * animations when bars are hovered. We also set some configuration for the bar labels to be displayed inside the bar,
+ * to display the information found in the `data1` property of each element store, to render a formated text with the
+ * `Ext.util.Format` we pass in, to have an `horizontal` orientation (as opposed to a vertical one) and we also set
+ * other styles like `color`, `text-anchor`, etc.
+ */
+Ext.define('Ext.chart.series.Bar', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.series.Cartesian',
+
+    alternateClassName: ['Ext.chart.BarSeries', 'Ext.chart.BarChart', 'Ext.chart.StackedBarChart'],
+
+    requires: ['Ext.chart.axis.Axis', 'Ext.fx.Anim'],
+
+    /* End Definitions */
+
+    type: 'bar',
+
+    alias: 'series.bar',
+    /**
+     * @cfg {Boolean} column Whether to set the visualization as column chart or horizontal bar chart.
+     */
+    column: false,
+    
+    /**
+     * @cfg style Style properties that will override the theming series styles.
+     */
+    style: {},
+    
+    /**
+     * @cfg {Number} gutter The gutter space between single bars, as a percentage of the bar width
+     */
+    gutter: 38.2,
+
+    /**
+     * @cfg {Number} groupGutter The gutter space between groups of bars, as a percentage of the bar width
+     */
+    groupGutter: 38.2,
+
+    /**
+     * @cfg {Number} xPadding Padding between the left/right axes and the bars
+     */
+    xPadding: 0,
+
+    /**
+     * @cfg {Number} yPadding Padding between the top/bottom axes and the bars
+     */
+    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));
+            }
+        }
+    },
+
+    // @private sets the bar girth.
+    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);
+    },
+
+    // @private returns the gutters.
+    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];
+    },
+
+    // @private Get chart and data boundaries
+    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;
+
+        //Skip excluded series
+        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
+        };
+    },
+
+    // @private Build an array of paths for the chart
+    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++) {
+                // Excluded series
+                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 {
+                    // draw in reverse order
+                    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]
+                });
+                // When resizing, reset before animating
+                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;
+                        //update shadows
+                        for (shadowIndex = 0; shadowIndex < shadowGroupsLn; shadowIndex++) {
+                            shadow = shadowGroups[shadowIndex].getAt(stacked ? i : (i * barsLen + j));
+                            if (shadow) {
+                                shadow.setAttributes(attrs, true);
+                            }
+                        }
+                    }
+                    //update sprite position and width/height
+                    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);
+    },
+
+    // @private render/setAttributes on the shadows
+    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;
+            //create shadows
+            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;
+    },
+
+    /**
+     * Draws the series for the current chart.
+     */
+    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;
+        }
+        
+        //fill colors are taken from the colors array.
+        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;
+        // Create new or reuse sprites and animate/display
+        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);
+            }
+
+            // Create a new sprite if needed (no height)
+            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;
+        }
+
+        // Hide unused sprites
+        ln = group.getCount();
+        for (j = i; j < ln; j++) {
+            group.getAt(j).hide(true);
+        }
+        // Hide unused shadows
+        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();
+    },
+    
+    // @private handled when creating a label.
+    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 || {}));
+    },
+    
+    // @private callback used when placing a label.
+    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
+        // Determine the label's final position. Starts with the configured preferred value but
+        // may get flipped from inside to outside or vice-versa depending on space.
+        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;
+        }
+        //set position
+        finalAttr = {
+            x: x,
+            y: y
+        };
+        //rotate
+        if (rotate) {
+            finalAttr.rotate = {
+                x: x,
+                y: y,
+                degrees: 270
+            };
+        }
+        //check for resizing
+        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);
+            }
+        }
+        //handle animation
+        if (animate) {
+            me.onAnimate(label, { to: finalAttr });
+        }
+        else {
+            label.setAttributes(Ext.apply(finalAttr, {
+                hidden: false
+            }), true);
+        }
+    },
+
+    /* @private
+     * Gets the dimensions of a given bar label. Uses a single hidden sprite to avoid
+     * changing visible sprites.
+     * @param value
+     */
+    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);
+
+        // Flip the width/height if rotated, as getBBox returns the pre-rotated dimensions
+        bbox = tester.getBBox();
+        w = bbox.width;
+        h = bbox.height;
+        return {
+            width: rotated ? h : w,
+            height: rotated ? w : h
+        };
+    },
+
+    // @private used to animate label, markers and other sprites.
+    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;
+    },
+    
+    // @private hide all markers
+    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();
+            });
+        }
+    },
+
+    // @private show all markers
+    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();
+            });
+        }
+    },
+    
+    /**
+     * Returns a string with the color to be used for the series legend item.
+     * @param index
+     */
+    getLegendColor: function(index) {
+        var me = this;
+        return me.colorArrayStyle[index % me.colorArrayStyle.length];
+    }
+});
+/**
+ * @class Ext.chart.series.Column
+ * @extends Ext.chart.series.Bar
+ * 
+  <p>
+  Creates a Column Chart. Much of the methods are inherited from Bar. A Column Chart is a useful visualization technique to display quantitative information for different 
+  categories that can show some progression (or regression) in the data set.
+  As with all other series, the Column Series must be appended in the *series* Chart array configuration. See the Chart 
+  documentation for more information. A typical configuration object for the column series could be:
+  </p>
+{@img Ext.chart.series.Column/Ext.chart.series.Column.png Ext.chart.series.Column chart series  
+  <pre><code>
+    var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        animate: true,
+        store: store,
+        axes: [{
+            type: 'Numeric',
+            position: 'bottom',
+            fields: ['data1'],
+            label: {
+                renderer: Ext.util.Format.numberRenderer('0,0')
+            },
+            title: 'Sample Values',
+            grid: true,
+            minimum: 0
+        }, {
+            type: 'Category',
+            position: 'left',
+            fields: ['name'],
+            title: 'Sample Metrics'
+        }],
+            axes: [{
+                type: 'Numeric',
+                position: 'left',
+                fields: ['data1'],
+                label: {
+                    renderer: Ext.util.Format.numberRenderer('0,0')
+                },
+                title: 'Sample Values',
+                grid: true,
+                minimum: 0
+            }, {
+                type: 'Category',
+                position: 'bottom',
+                fields: ['name'],
+                title: 'Sample Metrics'
+            }],
+            series: [{
+                type: 'column',
+                axis: 'left',
+                highlight: true,
+                tips: {
+                  trackMouse: true,
+                  width: 140,
+                  height: 28,
+                  renderer: function(storeItem, item) {
+                    this.setTitle(storeItem.get('name') + ': ' + storeItem.get('data1') + ' $');
+                  }
+                },
+                label: {
+                  display: 'insideEnd',
+                  'text-anchor': 'middle',
+                    field: 'data1',
+                    renderer: Ext.util.Format.numberRenderer('0'),
+                    orientation: 'vertical',
+                    color: '#333'
+                },
+                xField: 'name',
+                yField: 'data1'
+            }]
+    });
+   </code></pre>
+  <p>
+  In this configuration we set `column` as the series type, bind the values of the bars to the bottom axis, set `highlight` to true so that bars are smoothly highlighted
+  when hovered and bind the `xField` or category field to the data store `name` property and the `yField` as the data1 property of a store element. 
+  </p>
+ */
+
+Ext.define('Ext.chart.series.Column', {
+
+    /* Begin Definitions */
+
+    alternateClassName: ['Ext.chart.ColumnSeries', 'Ext.chart.ColumnChart', 'Ext.chart.StackedColumnChart'],
+
+    extend: 'Ext.chart.series.Bar',
+
+    /* End Definitions */
+
+    type: 'column',
+    alias: 'series.column',
+
+    column: true,
+
+    /**
+     * @cfg {Number} xPadding
+     * Padding between the left/right axes and the bars
+     */
+    xPadding: 10,
+
+    /**
+     * @cfg {Number} yPadding
+     * Padding between the top/bottom axes and the bars
+     */
+    yPadding: 0
+});
+/**
+ * @class Ext.chart.series.Gauge
+ * @extends Ext.chart.series.Series
+ * 
+ * Creates a Gauge Chart. Gauge Charts are used to show progress in a certain variable. There are two ways of using the Gauge chart.
+ * One is setting a store element into the Gauge and selecting the field to be used from that store. Another one is instanciating the
+ * visualization and using the `setValue` method to adjust the value you want.
+ *
+ * A chart/series configuration for the Gauge visualization could look like this:
+ * 
+ *     {
+ *         xtype: 'chart',
+ *         store: store,
+ *         axes: [{
+ *             type: 'gauge',
+ *             position: 'gauge',
+ *             minimum: 0,
+ *             maximum: 100,
+ *             steps: 10,
+ *             margin: -10
+ *         }],
+ *         series: [{
+ *             type: 'gauge',
+ *             field: 'data1',
+ *             donut: false,
+ *             colorSet: ['#F49D10', '#ddd']
+ *         }]
+ *     }
+ * 
+ * In this configuration we create a special Gauge axis to be used with the gauge visualization (describing half-circle markers), and also we're
+ * setting a maximum, minimum and steps configuration options into the axis. The Gauge series configuration contains the store field to be bound to
+ * the visual display and the color set to be used with the visualization.
+ * 
+ * @xtype gauge
+ */
+Ext.define('Ext.chart.series.Gauge', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.series.Series',
+
+    /* End Definitions */
+
+    type: "gauge",
+    alias: 'series.gauge',
+
+    rad: Math.PI / 180,
+
+    /**
+     * @cfg {Number} highlightDuration
+     * The duration for the pie slice highlight effect.
+     */
+    highlightDuration: 150,
+
+    /**
+     * @cfg {String} angleField
+     * The store record field name to be used for the pie angles.
+     * The values bound to this field name must be positive real numbers.
+     * This parameter is required.
+     */
+    angleField: false,
+
+    /**
+     * @cfg {Boolean} needle
+     * Use the Gauge Series as an area series or add a needle to it. Default's false.
+     */
+    needle: false,
+    
+    /**
+     * @cfg {Boolean|Number} donut
+     * Use the entire disk or just a fraction of it for the gauge. Default's false.
+     */
+    donut: false,
+
+    /**
+     * @cfg {Boolean} showInLegend
+     * Whether to add the pie chart elements as legend items. Default's false.
+     */
+    showInLegend: false,
+
+    /**
+     * @cfg {Object} style
+     * An object containing styles for overriding series styles from Theming.
+     */
+    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;
+        //Add yFields to be used in Legend.js
+        me.yField = [];
+        if (me.label.field) {
+            store.each(function(rec) {
+                me.yField.push(rec.get(me.label.field));
+            });
+        }
+    },
+
+    // @private returns an object with properties for a Slice
+    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;
+        }
+        //Solves mysterious clipping bug with IE
+        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"]]
+            };
+        }
+    },
+
+    // @private utility function to calculate the middle point of a pie slice.
+    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
+        };
+    },
+
+    /**
+     * Draws the series for the current chart.
+     */
+    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;
+
+        //override theme colors
+        if (me.colorSet) {
+            colorArrayStyle = me.colorSet;
+            colorArrayLength = colorArrayStyle.length;
+        }
+        
+        //if not store or store is empty then there's nothing to draw
+        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);
+        }
+        
+        //do pie slices after.
+        for (i = 0, ln = slices.length; i < ln; i++) {
+            slice = slices[i];
+            sprite = group.getAt(i);
+            //set pie slice properties
+            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;
+            // Create a new sprite if needed (no height)
+            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;
+    },
+    
+    /**
+     * Sets the Gauge chart to the current specified value.
+    */
+    setValue: function (value) {
+        this.value = value;
+        this.drawSeries();
+    },
+
+    // @private callback for when creating a label sprite.
+    onCreateLabel: function(storeItem, item, i, display) {},
+
+    // @private callback for when placing a label sprite.
+    onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},
+
+    // @private callback for when placing a callout.
+    onPlaceCallout: function() {},
+
+    // @private handles sprite animation for the series.
+    onAnimate: function(sprite, attr) {
+        sprite.show();
+        return this.callParent(arguments);
+    },
+
+    isItemInPoint: function(x, y, item, i) {
+        return false;
+    },
+    
+    // @private shows all elements in the series.
+    showAll: function() {
+        if (!isNaN(this._index)) {
+            this.__excludes[this._index] = false;
+            this.drawSeries();
+        }
+    },
+    
+    /**
+     * Returns the color of the series (to be displayed as color for the series legend item).
+     * @param item {Object} Info about the item; same format as returned by #getItemForPoint
+     */
+    getLegendColor: function(index) {
+        var me = this;
+        return me.colorArrayStyle[index % me.colorArrayStyle.length];
+    }
+});
+
+
+/**
+ * @class Ext.chart.series.Line
+ * @extends Ext.chart.series.Cartesian
+ * 
+ <p> 
+  Creates a Line Chart. A Line Chart is a useful visualization technique to display quantitative information for different 
+  categories or other real values (as opposed to the bar chart), that can show some progression (or regression) in the dataset.
+  As with all other series, the Line Series must be appended in the *series* Chart array configuration. See the Chart 
+  documentation for more information. A typical configuration object for the line series could be:
+ </p>
+{@img Ext.chart.series.Line/Ext.chart.series.Line.png Ext.chart.series.Line chart series}
+  <pre><code>
+    var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        animate: true,
+        store: store,
+        axes: [{
+            type: 'Numeric',
+            position: 'bottom',
+            fields: ['data1'],
+            label: {
+                renderer: Ext.util.Format.numberRenderer('0,0')
+            },
+            title: 'Sample Values',
+            grid: true,
+            minimum: 0
+        }, {
+            type: 'Category',
+            position: 'left',
+            fields: ['name'],
+            title: 'Sample Metrics'
+        }],
+        series: [{
+            type: 'line',
+            highlight: {
+                size: 7,
+                radius: 7
+            },
+            axis: 'left',
+            xField: 'name',
+            yField: 'data1',
+            markerCfg: {
+                type: 'cross',
+                size: 4,
+                radius: 4,
+                'stroke-width': 0
+            }
+        }, {
+            type: 'line',
+            highlight: {
+                size: 7,
+                radius: 7
+            },
+            axis: 'left',
+            fill: true,
+            xField: 'name',
+            yField: 'data3',
+            markerCfg: {
+                type: 'circle',
+                size: 4,
+                radius: 4,
+                'stroke-width': 0
+            }
+        }]
+    });
+   </code></pre>
+ <p> 
+  In this configuration we're adding two series (or lines), one bound to the `data1` property of the store and the other to `data3`. The type for both configurations is 
+  `line`. The `xField` for both series is the same, the name propert of the store. Both line series share the same axis, the left axis. You can set particular marker 
+  configuration by adding properties onto the markerConfig object. Both series have an object as highlight so that markers animate smoothly to the properties in highlight 
+  when hovered. The second series has `fill=true` which means that the line will also have an area below it of the same color.
+ </p>
+ */
+
+Ext.define('Ext.chart.series.Line', {
+
+    /* Begin Definitions */
+
+    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'],
+
+    /* End Definitions */
+
+    type: 'line',
+    
+    alias: 'series.line',
+
+    /**
+     * @cfg {Number} selectionTolerance
+     * The offset distance from the cursor position to the line series to trigger events (then used for highlighting series, etc).
+     */
+    selectionTolerance: 20,
+    
+    /**
+     * @cfg {Boolean} showMarkers
+     * Whether markers should be displayed at the data points along the line. If true,
+     * then the {@link #markerConfig} config item will determine the markers' styling.
+     */
+    showMarkers: true,
+
+    /**
+     * @cfg {Object} markerConfig
+     * The display style for the markers. Only used if {@link #showMarkers} is true.
+     * The markerConfig is a configuration object containing the same set of properties defined in
+     * the Sprite class. For example, if we were to set red circles as markers to the line series we could
+     * pass the object:
+     *
+     <pre><code>
+        markerConfig: {
+            type: 'circle',
+            radius: 4,
+            'fill': '#f00'
+        }
+     </code></pre>
+     
+     */
+    markerConfig: {},
+
+    /**
+     * @cfg {Object} style
+     * An object containing styles for the visualization lines. These styles will override the theme styles. 
+     * Some options contained within the style object will are described next.
+     */
+    style: {},
+    
+    /**
+     * @cfg {Boolean} smooth
+     * If true, the line will be smoothed/rounded around its points, otherwise straight line
+     * segments will be drawn. Defaults to false.
+     */
+    smooth: false,
+
+    /**
+     * @cfg {Boolean} fill
+     * If true, the area below the line will be filled in using the {@link #style.eefill} and
+     * {@link #style.opacity} config properties. Defaults to 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));
+            }
+        }
+    },
+    
+    // @private makes an average of points when there are more data points than pixels to be rendered.
+    shrink: function(xValues, yValues, size) {
+        // Start at the 2nd point...
+        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
+        };
+    },
+
+    /**
+     * Draws the series for the current chart.
+     */
+    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 = [],
+            onbreak = false,
+            markerStyle = me.markerStyle,
+            seriesStyle = me.seriesStyle,
+            seriesLabelStyle = me.seriesLabelStyle,
+            colorArrayStyle = me.colorArrayStyle,
+            colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
+            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;
+        
+        //if store is empty then there's nothing to draw.
+        if (!store || !store.getCount()) {
+            return;
+        }
+        
+        //prepare style objects for line and markers
+        endMarkerStyle = Ext.apply(markerStyle, me.markerConfig);
+        type = endMarkerStyle.type;
+        delete endMarkerStyle.type;
+        endLineStyle = Ext.apply(seriesStyle, me.style);
+        //if no stroke with is specified force it to 0.5 because this is
+        //about making *lines*
+        if (!endLineStyle['stroke-width']) {
+            endLineStyle['stroke-width'] = 0.5;
+        }
+        //If we're using a time axis and we need to translate the points,
+        //then reuse the first markers as the last markers.
+        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];
+
+        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 a field was specified without a corresponding axis, create one to get bounds
+        //only do this for the axis where real values are bound (that's why we check for
+        //me.axis)
+        if (me.xField && !Ext.isNumber(minX)
+            && (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;
+        }
+        if (me.yField && !Ext.isNumber(minY)
+            && (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;
+        }
+
+        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);
+            //skip undefined values
+            if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)) {
+                //<debug warn>
+                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);
+                }
+                //</debug>
+                return;
+            }
+            // Ensure a value
+            if (typeof xValue == 'string' || typeof xValue == 'object'
+                //set as uniform distribution if the axis is a category axis.
+                || (me.axis != 'top' && me.axis != 'bottom')) {
+                xValue = i;
+            }
+            if (typeof yValue == 'string' || typeof yValue == 'object'
+                //set as uniform distribution if the axis is a category axis.
+                || (me.axis != 'left' && me.axis != 'right')) {
+                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 = [];
+
+        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 this is the first line, create a dummypath to animate in from.
+            if (!me.line || chart.resizing) {
+                dummyPath = dummyPath.concat([x, bbox.y + bbox.height / 2]);
+            }
+
+            // When resizing, reset before animating
+            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(i);
+                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) {
+            //nothing to be rendered
+            return;    
+        }
+        
+        if (me.smooth) {
+            path = Ext.draw.Draw.smooth(path, 6);
+        }
+        
+        //Correct path if we're animating timeAxis intervals
+        if (chart.markerIndex && me.previousPath) {
+            fromPath = me.previousPath;
+            fromPath.splice(1, 2);
+        } else {
+            fromPath = path;
+        }
+
+        // Only create a line if one doesn't exist.
+        if (!me.line) {
+            me.line = surface.add(Ext.apply({
+                type: 'path',
+                group: group,
+                path: dummyPath,
+                stroke: endLineStyle.stroke || endLineStyle.fill
+            }, endLineStyle || {}));
+            //unset fill here (there's always a default fill withing the themes).
+            me.line.setAttributes({
+                fill: 'none'
+            });
+            if (!endLineStyle.stroke && colorArrayLength) {
+                me.line.setAttributes({
+                    stroke: colorArrayStyle[seriesIdx % colorArrayLength]
+                }, true);
+            }
+            if (enableShadows) {
+                //create shadows
+                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;
+            //Add renderer to line. There is not unique record associated with this.
+            rendererAttributes = me.renderer(line, false, { path: path }, i, store);
+            Ext.apply(rendererAttributes, endLineStyle || {}, {
+                stroke: endLineStyle.stroke || endLineStyle.fill
+            });
+            //fill should not be used here but when drawing the special fill path object
+            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
+                });
+            }
+            //animate shadows
+            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 }
+                        });
+                    }
+                }
+            }
+            //animate fill path
+            if (fill) {
+                me.onAnimate(me.fillPath, {
+                    to: Ext.apply({}, {
+                        path: fillPath,
+                        fill: colorArrayStyle[seriesIdx % colorArrayLength] || endLineStyle.fill
+                    }, endLineStyle || {})
+                });
+            }
+            //animate markers
+            if (showMarkers) {
+                for(i = 0; i < ln; i++) {
+                    item = markerGroup.getAt(i);
+                    if (item) {
+                        if (me.items[i]) {
+                            rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
+                            me.onAnimate(item, {
+                                to: Ext.apply(rendererAttributes, endMarkerStyle || {})
+                            });
+                        } else {
+                            item.setAttributes(Ext.apply({
+                                hidden: true 
+                            }, item._to), true);
+                        }
+                    }
+                }
+                for(; i < markerCount; i++) {
+                    item = markerGroup.getAt(i);
+                    item.hide(true);
+                }
+//                for(i = 0; i < (chart.markerIndex || 0)-1; i++) {
+//                    item = markerGroup.getAt(i);
+//                    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
+            });
+            //fill should not be used here but when drawing the special fill path object
+            delete rendererAttributes.fill;
+            me.line.setAttributes(rendererAttributes, true);
+            //set path for shadows
+            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) {
+                for(i = 0; i < ln; i++) {
+                    item = markerGroup.getAt(i);
+                    if (item) {
+                        if (me.items[i]) {
+                            rendererAttributes = me.renderer(item, store.getAt(i), item._to, i, store);
+                            item.setAttributes(Ext.apply(endMarkerStyle || {}, rendererAttributes || {}), true);
+                        } else {
+                            item.hide(true);
+                        }
+                    }
+                }
+                for(; i < markerCount; i++) {
+                    item = markerGroup.getAt(i);
+                    item.hide(true);
+                }
+            }
+        }
+
+        if (chart.markerIndex) {
+            path.splice(1, 0, path[1], path[2]);
+            me.previousPath = path;
+        }
+        me.renderLabels();
+        me.renderCallouts();
+    },
+    
+    // @private called when a label is to be created.
+    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 || {}));
+    },
+    
+    // @private called when a label is to be created.
+    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);
+            //correct label position to fit into the box
+            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') {
+            //TODO(nicolas): find out why width/height values in circle bounding boxes are undefined.
+            bb = item.sprite.getBBox();
+            bb.width = bb.width || (radius * 2);
+            bb.height = bb.height || (radius * 2);
+            y = y + (display == 'over'? -bb.height : bb.height);
+            //correct label position to fit into the box
+            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;
+
+        //get the right two points
+        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]];
+        
+        //keep the label always on the outer part of the "elbow"
+        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;
+        }
+        //position
+        x = cur[0] + normal[0] * offsetFromViz;
+        y = cur[1] + normal[1] * offsetFromViz;
+
+        //box position and dimensions
+        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;
+        
+        //now check if we're out of bounds and invert the normal vector correspondingly
+        //this may add new overlaps between labels (but labels won't be out of bounds).
+        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;
+        }
+
+        //update positions
+        x = cur[0] + normal[0] * offsetFromViz;
+        y = cur[1] + normal[1] * offsetFromViz;
+        
+        //update box position and dimensions
+        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) {
+            //set the line from the middle of the pie to the box.
+            me.onAnimate(callout.lines, {
+                to: {
+                    path: ["M", cur[0], cur[1], "L", x, y, "Z"]
+                }
+            });
+            //set component position
+            if (callout.panel) {
+                callout.panel.setPosition(boxx, boxy, true);
+            }
+        }
+        else {
+            //set the line from the middle of the pie to the box.
+            callout.lines.setAttributes({
+                path: ["M", cur[0], cur[1], "L", x, y, "Z"]
+            }, true);
+            //set component position
+            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;
+    },
+    
+    // @private toggle visibility of all series elements (markers, sprites).
+    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);
+            //hide shadows too
+            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);
+        }
+    },
+    
+    // @private hide all series elements (markers, sprites).
+    hideAll: function() {
+        this.toggleAll(false);
+    },
+    
+    // @private hide all series elements (markers, sprites).
+    showAll: function() {
+        this.toggleAll(true);
+    }
+});
+/**
+ * @class Ext.chart.series.Pie
+ * @extends Ext.chart.series.Series
+ * 
+ * Creates a Pie Chart. A Pie Chart is a useful visualization technique to display quantitative information for different 
+ * categories that also have a meaning as a whole.
+ * As with all other series, the Pie Series must be appended in the *series* Chart array configuration. See the Chart 
+ * documentation for more information. A typical configuration object for the pie series could be:
+ * 
+{@img Ext.chart.series.Pie/Ext.chart.series.Pie.png Ext.chart.series.Pie chart series}
+  <pre><code>
+    var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        animate: true,
+        store: store,
+        theme: 'Base:gradients',
+        series: [{
+            type: 'pie',
+            field: 'data1',
+            showInLegend: true,
+            tips: {
+              trackMouse: true,
+              width: 140,
+              height: 28,
+              renderer: function(storeItem, item) {
+                //calculate and display percentage on hover
+                var total = 0;
+                store.each(function(rec) {
+                    total += rec.get('data1');
+                });
+                this.setTitle(storeItem.get('name') + ': ' + Math.round(storeItem.get('data1') / total * 100) + '%');
+              }
+            },
+            highlight: {
+              segment: {
+                margin: 20
+              }
+            },
+            label: {
+                field: 'name',
+                display: 'rotate',
+                contrast: true,
+                font: '18px Arial'
+            }
+        }]    
+    });
+   </code></pre>
+ * 
+ * In this configuration we set `pie` as the type for the series, set an object with specific style properties for highlighting options 
+ * (triggered when hovering elements). We also set true to `showInLegend` so all the pie slices can be represented by a legend item. 
+ * We set `data1` as the value of the field to determine the angle span for each pie slice. We also set a label configuration object 
+ * where we set the field name of the store field to be renderer as text for the label. The labels will also be displayed rotated. 
+ * We set `contrast` to `true` to flip the color of the label if it is to similar to the background color. Finally, we set the font family 
+ * and size through the `font` parameter. 
+ * 
+ * @xtype pie
+ *
+ */
+Ext.define('Ext.chart.series.Pie', {
+
+    /* Begin Definitions */
+
+    alternateClassName: ['Ext.chart.PieSeries', 'Ext.chart.PieChart'],
+
+    extend: 'Ext.chart.series.Series',
+
+    /* End Definitions */
+
+    type: "pie",
+    
+    alias: 'series.pie',
+
+    rad: Math.PI / 180,
+
+    /**
+     * @cfg {Number} highlightDuration
+     * The duration for the pie slice highlight effect.
+     */
+    highlightDuration: 150,
+
+    /**
+     * @cfg {String} angleField
+     * The store record field name to be used for the pie angles.
+     * The values bound to this field name must be positive real numbers.
+     * This parameter is required.
+     */
+    angleField: false,
+
+    /**
+     * @cfg {String} lengthField
+     * The store record field name to be used for the pie slice lengths.
+     * The values bound to this field name must be positive real numbers.
+     * This parameter is optional.
+     */
+    lengthField: false,
+
+    /**
+     * @cfg {Boolean|Number} donut
+     * Whether to set the pie chart as donut chart.
+     * Default's false. Can be set to a particular percentage to set the radius
+     * of the donut chart.
+     */
+    donut: false,
+
+    /**
+     * @cfg {Boolean} showInLegend
+     * Whether to add the pie chart elements as legend items. Default's false.
+     */
+    showInLegend: false,
+
+    /**
+     * @cfg {Array} colorSet
+     * An array of color values which will be used, in order, as the pie slice fill colors.
+     */
+    
+    /**
+     * @cfg {Object} style
+     * An object containing styles for overriding series styles from Theming.
+     */
+    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;
+        //Add yFields to be used in Legend.js
+        me.yField = [];
+        if (me.label.field) {
+            store.each(function(rec) {
+                me.yField.push(rec.get(me.label.field));
+            });
+        }
+    },
+
+    // @private returns an object with properties for a PieSlice.
+    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;
+        }
+        //Solves mysterious clipping bug with IE
+        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"]]
+            };
+        }
+    },
+
+    // @private utility function to calculate the middle point of a pie slice.
+    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
+        };
+    },
+
+    /**
+     * Draws the series for the current chart.
+     */
+    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;
+
+        //override theme colors
+        if (me.colorSet) {
+            colorArrayStyle = me.colorSet;
+            colorArrayLength = colorArrayStyle.length;
+        }
+        
+        //if not store or store is empty then there's nothing to draw
+        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]) {
+                //hidden series
+                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]) {
+                //hidden series
+                return;
+            } 
+            value = record.get(field);
+            middleAngle = angle - 360 * value / totalField / 2;
+            // TODO - Put up an empty circle
+            if (isNaN(middleAngle)) {
+                middleAngle = 360;
+                value = 1;
+                totalField = 1;
+            }
+            // First slice
+            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);
+        
+        //do all shadows first.
+        if (enableShadows) {
+            for (i = 0, ln = slices.length; i < ln; i++) {
+                if (this.__excludes && this.__excludes[i]) {
+                    //hidden series
+                    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;
+                    //set pie slice properties
+                    rendererAttributes = {
+                        segment: {
+                            startAngle: slice.startAngle,
+                            endAngle: slice.endAngle,
+                            margin: 0,
+                            rho: slice.rho,
+                            startRho: rhoAcum + (deltaRho * donut / 100),
+                            endRho: rhoAcum + deltaRho
+                        }
+                    };
+                    //create shadows
+                    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) {
+                            rendererAttributes = me.renderer(shadow, store.getAt(i), Ext.apply({},
+                            rendererAttributes, shadowAttr), i, store);
+                            me.onAnimate(shadow, {
+                                to: rendererAttributes
+                            });
+                        } else {
+                            rendererAttributes = me.renderer(shadow, store.getAt(i), Ext.apply(shadowAttr, {
+                                hidden: false
+                            }), i, store);
+                            shadow.setAttributes(rendererAttributes, true);
+                        }
+                        shadows.push(shadow);
+                    }
+                    slice.shadowAttrs[j] = shadows;
+                }
+            }
+        }
+        //do pie slices after.
+        for (i = 0, ln = slices.length; i < ln; i++) {
+            if (this.__excludes && this.__excludes[i]) {
+                //hidden series
+                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;
+                //set pie slice properties
+                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;
+                // Create a new sprite if needed (no height)
+                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;
+            }
+        }
+        
+        // Hide unused bars
+        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();
+    },
+
+    // @private callback for when creating a label sprite.
+    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));
+    },
+
+    // @private callback for when placing a label sprite.
+    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;
+            //update positions
+            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);
+            }
+            //update rotation angle
+            opt.rotate = {
+                degrees: dg,
+                x: opt.x,
+                y: opt.y
+            };
+            break;
+
+        default:
+            break;
+        }
+        //ensure the object has zero translation
+        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;
+    },
+
+    // @private callback for when placing a callout sprite.
+    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;
+
+        //should be able to config this.
+        rho = item.endRho + offsetFromViz;
+        rhoCenter = (item.endRho + item.startRho) / 2 + (item.endRho - item.startRho) / 3;
+        //update positions
+        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) {
+            //set the line from the middle of the pie to the box.
+            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"]
+                }
+            });
+            //set box position
+            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
+                }
+            });
+            //set text position
+            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 {
+            //set the line from the middle of the pie to the box.
+            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);
+            //set box position
+            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);
+            //set text position
+            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);
+        }
+    },
+
+    // @private handles sprite animation for the series.
+    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;
+        
+        // normalize to the same range of angles created by drawSeries
+        if (angle > me.firstAngle) {
+            angle -= 360;
+        }
+        return (angle <= startAngle && angle > endAngle
+                && rho >= item.startRho && rho <= item.endRho);
+    },
+    
+    // @private hides all elements in the series.
+    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();
+        }
+    },
+    
+    // @private shows all elements in the series.
+    showAll: function() {
+        if (!isNaN(this._index)) {
+            this.__excludes[this._index] = false;
+            this.drawSeries();
+        }
+    },
+
+    /**
+     * Highlight the specified item. If no item is provided the whole series will be highlighted.
+     * @param item {Object} Info about the item; same format as returned by #getItemForPoint
+     */
+    highlightItem: function(item) {
+        var me = this,
+            rad = me.rad;
+        item = item || this.items[this._index];
+        
+        //TODO(nico): sometimes in IE itemmouseover is triggered
+        //twice without triggering itemmouseout in between. This
+        //fixes the highlighting bug. Eventually, events should be
+        //changed to trigger one itemmouseout between two itemmouseovers.
+        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;
+            //animate labels
+            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);
+
+                //TODO(nico): rounding to 1e-10
+                //gives the right translation. Translation
+                //was buggy for very small numbers. In this
+                //case we're not looking to translate to very small
+                //numbers but not to translate at all.
+                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);
+                }
+            }
+            //animate shadows
+            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);
+                    }
+                }
+            }
+        }
+    },
+
+    /**
+     * un-highlights the specified item. If no item is provided it will un-highlight the entire series.
+     * @param item {Object} Info about the item; same format as returned by #getItemForPoint
+     */
+    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) {
+                    //animate labels
+                    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);
+    },
+    
+    /**
+     * Returns the color of the series (to be displayed as color for the series legend item).
+     * @param item {Object} Info about the item; same format as returned by #getItemForPoint
+     */
+    getLegendColor: function(index) {
+        var me = this;
+        return me.colorArrayStyle[index % me.colorArrayStyle.length];
+    }
+});
+
+
+/**
+ * @class Ext.chart.series.Radar
+ * @extends Ext.chart.series.Series
+ * 
+ * Creates a Radar Chart. A Radar Chart is a useful visualization technique for comparing different quantitative values for 
+ * a constrained number of categories.
+ * As with all other series, the Radar series must be appended in the *series* Chart array configuration. See the Chart 
+ * documentation for more information. A typical configuration object for the radar series could be:
+ * 
+ {@img Ext.chart.series.Radar/Ext.chart.series.Radar.png Ext.chart.series.Radar chart series}  
+  <pre><code>
+    var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        animate: true,
+        theme:'Category2',
+        store: store,
+        axes: [{
+            type: 'Radial',
+            position: 'radial',
+            label: {
+                display: true
+            }
+        }],
+        series: [{
+            type: 'radar',
+            xField: 'name',
+            yField: 'data3',
+            showInLegend: true,
+            showMarkers: true,
+            markerConfig: {
+                radius: 5,
+                size: 5           
+            },
+            style: {
+                'stroke-width': 2,
+                fill: 'none'
+            }
+        },{
+            type: 'radar',
+            xField: 'name',
+            yField: 'data2',
+            showMarkers: true,
+            showInLegend: true,
+            markerConfig: {
+                radius: 5,
+                size: 5
+            },
+            style: {
+                'stroke-width': 2,
+                fill: 'none'
+            }
+        },{
+            type: 'radar',
+            xField: 'name',
+            yField: 'data5',
+            showMarkers: true,
+            showInLegend: true,
+            markerConfig: {
+                radius: 5,
+                size: 5
+            },
+            style: {
+                'stroke-width': 2,
+                fill: 'none'
+            }
+        }]    
+    });
+   </code></pre>
+ * 
+ * In this configuration we add three series to the chart. Each of these series is bound to the same categories field, `name` but bound to different properties for each category,
+ * `data1`, `data2` and `data3` respectively. All series display markers by having `showMarkers` enabled. The configuration for the markers of each series can be set by adding properties onto 
+ * the markerConfig object. Finally we override some theme styling properties by adding properties to the `style` object.
+ * 
+ * @xtype radar
+ * 
+ */
+Ext.define('Ext.chart.series.Radar', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.series.Series',
+
+    requires: ['Ext.chart.Shape', 'Ext.fx.Anim'],
+
+    /* End Definitions */
+
+    type: "radar",
+    alias: 'series.radar',
+
+    
+    rad: Math.PI / 180,
+
+    showInLegend: false,
+
+    /**
+     * @cfg {Object} style
+     * An object containing styles for overriding series styles from Theming.
+     */
+    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');
+        }
+    },
+
+    /**
+     * Draws the series for the current chart.
+     */
+    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 the store is empty then there's nothing to draw
+        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) {
+            //get all renderer fields
+            chart.series.each(function(series) {
+                fields.push(series.yField);
+            });
+            //get maxValue to interpolate
+            store.each(function(record, i) {
+                for (i = 0, nfields = fields.length; i < nfields; i++) {
+                    maxValue = max(+record.get(fields[i]), maxValue);
+                }
+            });
+        }
+        //ensure non-zero value.
+        maxValue = maxValue || 1;
+        //create path and items
+        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, //TODO(nico): add markers
+                point: [centerX + x, centerY + y],
+                series: me
+            });
+        });
+        path.push('Z');
+        //create path sprite
+        if (!me.radar) {
+            me.radar = surface.add(Ext.apply({
+                type: 'path',
+                group: group,
+                path: startPath
+            }, seriesStyle || {}));
+        }
+        //reset on resizing
+        if (chart.resizing) {
+            me.radar.setAttributes({
+                path: startPath
+            }, true);
+        }
+        //render/animate
+        if (chart.animate) {
+            me.onAnimate(me.radar, {
+                to: Ext.apply({
+                    path: path
+                }, seriesStyle || {})
+            });
+        } else {
+            me.radar.setAttributes(Ext.apply({
+                path: path
+            }, seriesStyle || {}), true);
+        }
+        //render markers, labels and callouts
+        if (me.showMarkers) {
+            me.drawMarkers();
+        }
+        me.renderLabels();
+        me.renderCallouts();
+    },
+    
+    // @private draws the markers for the lines (if any).
+    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]
+                }
+            };
+            //render/animate
+            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);
+    },
+
+    // @private callback for when creating a label sprite.
+    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 || {}));
+    },
+
+    // @private callback for when placing a label sprite.
+    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);
+        }
+    },
+
+    // @private for toggling (show/hide) series. 
+    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);
+            //hide shadows too
+            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);
+                }
+            }
+        }
+    },
+    
+    // @private hide all elements in the series.
+    hideAll: function() {
+        this.toggleAll(false);
+        this.hideMarkers(0);
+    },
+    
+    // @private show all elements in the series.
+    showAll: function() {
+        this.toggleAll(true);
+    },
+    
+    // @private hide all markers that belong to `markerGroup`
+    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);
+        }
+    }
+});
+
+
+/**
+ * @class Ext.chart.series.Scatter
+ * @extends Ext.chart.series.Cartesian
+ * 
+ * Creates a Scatter Chart. The scatter plot is useful when trying to display more than two variables in the same visualization. 
+ * These variables can be mapped into x, y coordinates and also to an element's radius/size, color, etc.
+ * As with all other series, the Scatter Series must be appended in the *series* Chart array configuration. See the Chart 
+ * documentation for more information on creating charts. A typical configuration object for the scatter could be:
+ *
+{@img Ext.chart.series.Scatter/Ext.chart.series.Scatter.png Ext.chart.series.Scatter chart series}  
+  <pre><code>
+    var store = Ext.create('Ext.data.JsonStore', {
+        fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+        data: [
+            {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+            {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+            {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+            {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+            {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}                                                
+        ]
+    });
+    
+    Ext.create('Ext.chart.Chart', {
+        renderTo: Ext.getBody(),
+        width: 500,
+        height: 300,
+        animate: true,
+        theme:'Category2',
+        store: store,
+        axes: [{
+            type: 'Numeric',
+            position: 'bottom',
+            fields: ['data1', 'data2', 'data3'],
+            title: 'Sample Values',
+            grid: true,
+            minimum: 0
+        }, {
+            type: 'Category',
+            position: 'left',
+            fields: ['name'],
+            title: 'Sample Metrics'
+        }],
+        series: [{
+            type: 'scatter',
+            markerConfig: {
+                radius: 5,
+                size: 5
+            },
+            axis: 'left',
+            xField: 'name',
+            yField: 'data2'
+        }, {
+            type: 'scatter',
+            markerConfig: {
+                radius: 5,
+                size: 5
+            },
+            axis: 'left',
+            xField: 'name',
+            yField: 'data3'
+        }]   
+    });
+   </code></pre>
+ * 
+ * In this configuration we add three different categories of scatter series. Each of them is bound to a different field of the same data store, 
+ * `data1`, `data2` and `data3` respectively. All x-fields for the series must be the same field, in this case `name`. 
+ * Each scatter series has a different styling configuration for markers, specified by the `markerConfig` object. Finally we set the left axis as 
+ * axis to show the current values of the elements.
+ * 
+ * @xtype scatter
+ * 
+ */
+Ext.define('Ext.chart.series.Scatter', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.chart.series.Cartesian',
+
+    requires: ['Ext.chart.axis.Axis', 'Ext.chart.Shape', 'Ext.fx.Anim'],
+
+    /* End Definitions */
+
+    type: 'scatter',
+    alias: 'series.scatter',
+
+    /**
+     * @cfg {Object} markerConfig
+     * The display style for the scatter series markers.
+     */
+    
+    /**
+     * @cfg {Object} style 
+     * Append styling properties to this object for it to override theme properties.
+     */
+
+    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));
+            }
+        }
+    },
+
+    // @private Get chart and data boundaries
+    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 a field was specified without a corresponding axis, create one to get bounds
+        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
+        };
+    },
+
+    // @private Build an array of paths for the chart
+    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);
+            //skip undefined values
+            if (typeof yValue == 'undefined' || (typeof yValue == 'string' && !yValue)) {
+                //<debug warn>
+                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);
+                }
+                //</debug>
+                return;
+            }
+            // Ensure a value
+            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
+            });
+
+            // When resizing, reset before animating
+            if (chart.animate && chart.resizing) {
+                sprite = group.getAt(i);
+                if (sprite) {
+                    me.resetPoint(sprite);
+                    if (enableShadows) {
+                        me.resetShadow(sprite);
+                    }
+                }
+            }
+        });
+        return attrs;
+    },
+
+    // @private translate point to the center
+    resetPoint: function(sprite) {
+        var bbox = this.bbox;
+        sprite.setAttributes({
+            translate: {
+                x: (bbox.x + bbox.width) / 2,
+                y: (bbox.y + bbox.height) / 2
+            }
+        }, true);
+    },
+
+    // @private translate shadows of a sprite to the center
+    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);
+        }
+    },
+
+    // @private create a new point
+    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));
+    },
+
+    // @private create a new set of shadows for a sprite
+    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);
+        }
+    },
+
+    /**
+     * Draws the series for the current chart.
+     */
+    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 the store is empty then there's nothing to be rendered
+        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);
+
+            // Create a new sprite if needed (no height)
+            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
+                });
+                //animate shadows
+                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);
+                //update shadows
+                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;
+        }
+
+        // Hide unused sprites
+        ln = group.getCount();
+        for (i = attrs.length; i < ln; i++) {
+            group.getAt(i).hide(true);
+        }
+        me.renderLabels();
+        me.renderCallouts();
+    },
+    
+    // @private callback for when creating a label sprite.
+    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));
+    },
+    
+    // @private callback for when placing a label sprite.
+    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);
+            //correct label position to fit into the box
+            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') {
+            //TODO(nicolas): find out why width/height values in circle bounding boxes are undefined.
+            bb = item.sprite.getBBox();
+            bb.width = bb.width || (radius * 2);
+            bb.height = bb.height || (radius * 2);
+            y = y + (display == 'over'? -bb.height : bb.height);
+            //correct label position to fit into the box
+            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
+                    }
+                });
+            }
+        }
+    },
+    
+    // @private callback for when placing a callout sprite.    
+    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;
+    
+        //position
+        normal = [Math.cos(Math.PI /4), -Math.sin(Math.PI /4)];
+        x = cur[0] + normal[0] * offsetFromViz;
+        y = cur[1] + normal[1] * offsetFromViz;
+        
+        //box position and dimensions
+        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;
+        
+        //now check if we're out of bounds and invert the normal vector correspondingly
+        //this may add new overlaps between labels (but labels won't be out of bounds).
+        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;
+        }
+    
+        //update positions
+        x = cur[0] + normal[0] * offsetFromViz;
+        y = cur[1] + normal[1] * offsetFromViz;
+        
+        //update box position and dimensions
+        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) {
+            //set the line from the middle of the pie to the box.
+            me.onAnimate(callout.lines, {
+                to: {
+                    path: ["M", cur[0], cur[1], "L", x, y, "Z"]
+                }
+            }, true);
+            //set box position
+            me.onAnimate(callout.box, {
+                to: {
+                    x: boxx,
+                    y: boxy,
+                    width: boxw,
+                    height: boxh
+                }
+            }, true);
+            //set text position
+            me.onAnimate(callout.label, {
+                to: {
+                    x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
+                    y: y
+                }
+            }, true);
+        } else {
+            //set the line from the middle of the pie to the box.
+            callout.lines.setAttributes({
+                path: ["M", cur[0], cur[1], "L", x, y, "Z"]
+            }, true);
+            //set box position
+            callout.box.setAttributes({
+                x: boxx,
+                y: boxy,
+                width: boxw,
+                height: boxh
+            }, true);
+            //set text position
+            callout.label.setAttributes({
+                x: x + (normal[0] > 0? offsetBox : -(bbox.width + offsetBox)),
+                y: y
+            }, true);
+        }
+        for (p in callout) {
+            callout[p].show(true);
+        }
+    },
+
+    // @private handles sprite animation for the series.
+    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);
+    }
+});
+
+
+/**
+ * @class Ext.chart.theme.Base
+ * Provides default colors for non-specified things. Should be sub-classed when creating new themes.
+ * @ignore
+ */
+Ext.define('Ext.chart.theme.Base', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.chart.theme.Theme'],
+
+    /* End Definitions */
+
+    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;
+    
+    //Create themes from base colors
+    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]);
+    }
+    
+    //Create theme from color array
+    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]);
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.ArrayStore
+ * @extends Ext.data.Store
+ * @ignore
+ *
+ * <p>Small helper class to make creating {@link Ext.data.Store}s from Array data easier.
+ * An ArrayStore will be automatically configured with a {@link Ext.data.reader.Array}.</p>
+ *
+ * <p>A store configuration would be something like:</p>
+<pre><code>
+var store = new Ext.data.ArrayStore({
+    // store configs
+    autoDestroy: true,
+    storeId: 'myStore',
+    // reader configs
+    idIndex: 0,
+    fields: [
+       'company',
+       {name: 'price', type: 'float'},
+       {name: 'change', type: 'float'},
+       {name: 'pctChange', type: 'float'},
+       {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
+    ]
+});
+</code></pre>
+ * <p>This store is configured to consume a returned object of the form:
+<pre><code>
+var myData = [
+    ['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
+    ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
+    ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
+    ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
+    ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']
+];
+</code></pre>
+*
+ * <p>An object literal of this form could also be used as the {@link #data} config option.</p>
+ *
+ * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
+ * <b>{@link Ext.data.reader.Array ArrayReader}</b>.</p>
+ *
+ * @constructor
+ * @param {Object} config
+ * @xtype arraystore
+ */
+Ext.define('Ext.data.ArrayStore', {
+    extend: 'Ext.data.Store',
+    alias: 'store.array',
+    uses: ['Ext.data.reader.Array'],
+
+    /**
+     * @cfg {Ext.data.DataReader} reader @hide
+     */
+    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() {
+    // backwards compat
+    Ext.data.SimpleStore = Ext.data.ArrayStore;
+    // Ext.reg('simplestore', Ext.data.SimpleStore);
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.Batch
+ * 
+ * <p>Provides a mechanism to run one or more {@link Ext.data.Operation operations} in a given order. Fires the 'operationcomplete' event
+ * after the completion of each Operation, and the 'complete' event when all Operations have been successfully executed. Fires an 'exception'
+ * event if any of the Operations encounter an exception.</p>
+ * 
+ * <p>Usually these are only used internally by {@link Ext.data.proxy.Proxy} classes</p>
+ * 
+ * @constructor
+ * @param {Object} config Optional config object
+ */
+Ext.define('Ext.data.Batch', {
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+    
+    /**
+     * True to immediately start processing the batch as soon as it is constructed (defaults to false)
+     * @property autoStart
+     * @type Boolean
+     */
+    autoStart: false,
+    
+    /**
+     * The index of the current operation being executed
+     * @property current
+     * @type Number
+     */
+    current: -1,
+    
+    /**
+     * The total number of operations in this batch. Read only
+     * @property total
+     * @type Number
+     */
+    total: 0,
+    
+    /**
+     * True if the batch is currently running
+     * @property isRunning
+     * @type Boolean
+     */
+    isRunning: false,
+    
+    /**
+     * True if this batch has been executed completely
+     * @property isComplete
+     * @type Boolean
+     */
+    isComplete: false,
+    
+    /**
+     * True if this batch has encountered an exception. This is cleared at the start of each operation
+     * @property hasException
+     * @type Boolean
+     */
+    hasException: false,
+    
+    /**
+     * True to automatically pause the execution of the batch if any operation encounters an exception (defaults to true)
+     * @property pauseOnException
+     * @type Boolean
+     */
+    pauseOnException: true,
+    
+    constructor: function(config) {   
+        var me = this;
+                     
+        me.addEvents(
+          /**
+           * @event complete
+           * Fired when all operations of this batch have been completed
+           * @param {Ext.data.Batch} batch The batch object
+           * @param {Object} operation The last operation that was executed
+           */
+          'complete',
+          
+          /**
+           * @event exception
+           * Fired when a operation encountered an exception
+           * @param {Ext.data.Batch} batch The batch object
+           * @param {Object} operation The operation that encountered the exception
+           */
+          'exception',
+          
+          /**
+           * @event operationcomplete
+           * Fired when each operation of the batch completes
+           * @param {Ext.data.Batch} batch The batch object
+           * @param {Object} operation The operation that just completed
+           */
+          'operationcomplete'
+        );
+        
+        me.mixins.observable.constructor.call(me, config);
+        
+        /**
+         * Ordered array of operations that will be executed by this batch
+         * @property operations
+         * @type Array
+         */
+        me.operations = [];
+    },
+    
+    /**
+     * Adds a new operation to this batch
+     * @param {Object} operation The {@link Ext.data.Operation Operation} object
+     */
+    add: function(operation) {
+        this.total++;
+        
+        operation.setBatch(this);
+        
+        this.operations.push(operation);
+    },
+    
+    /**
+     * Kicks off the execution of the batch, continuing from the next operation if the previous
+     * operation encountered an exception, or if execution was paused
+     */
+    start: function() {
+        this.hasException = false;
+        this.isRunning = true;
+        
+        this.runNextOperation();
+    },
+    
+    /**
+     * @private
+     * Runs the next operation, relative to this.current.
+     */
+    runNextOperation: function() {
+        this.runOperation(this.current + 1);
+    },
+    
+    /**
+     * Pauses execution of the batch, but does not cancel the current operation
+     */
+    pause: function() {
+        this.isRunning = false;
+    },
+    
+    /**
+     * Executes a operation by its numeric index
+     * @param {Number} index The operation index to run
+     */
+    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);
+        }
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.BelongsToAssociation
+ * @extends Ext.data.Association
+ *
+ * <p>Represents a many to one association with another model. The owner model is expected to have
+ * a foreign key which references the primary key of the associated model:</p>
+ *
+<pre><code>
+Ext.define('Category', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'id',   type: 'int'},
+        {name: 'name', type: 'string'}
+    ]
+});
+
+Ext.define('Product', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'id',          type: 'int'},
+        {name: 'category_id', type: 'int'},
+        {name: 'name',        type: 'string'}
+    ],
+    // we can use the belongsTo shortcut on the model to create a belongsTo association
+    belongsTo: {type: 'belongsTo', model: 'Category'}
+});
+</code></pre>
+ * <p>In the example above we have created models for Products and Categories, and linked them together
+ * by saying that each Product belongs to a Category. This automatically links each Product to a Category
+ * based on the Product's category_id, and provides new functions on the Product model:</p>
+ *
+ * <p><u>Generated getter function</u></p>
+ *
+ * <p>The first function that is added to the owner model is a getter function:</p>
+ *
+<pre><code>
+var product = new Product({
+    id: 100,
+    category_id: 20,
+    name: 'Sneakers'
+});
+
+product.getCategory(function(category, operation) {
+    //do something with the category object
+    alert(category.get('id')); //alerts 20
+}, this);
+</code></pre>
+*
+ * <p>The getCategory function was created on the Product model when we defined the association. This uses the
+ * Category's configured {@link Ext.data.proxy.Proxy proxy} to load the Category asynchronously, calling the provided
+ * callback when it has loaded.</p>
+ *
+ * <p>The new getCategory function will also accept an object containing success, failure and callback properties
+ * - callback will always be called, success will only be called if the associated model was loaded successfully
+ * and failure will only be called if the associatied model could not be loaded:</p>
+ *
+<pre><code>
+product.getCategory({
+    callback: function(category, operation) {}, //a function that will always be called
+    success : function(category, operation) {}, //a function that will only be called if the load succeeded
+    failure : function(category, operation) {}, //a function that will only be called if the load did not succeed
+    scope   : this //optionally pass in a scope object to execute the callbacks in
+});
+</code></pre>
+ *
+ * <p>In each case above the callbacks are called with two arguments - the associated model instance and the
+ * {@link Ext.data.Operation operation} object that was executed to load that instance. The Operation object is
+ * useful when the instance could not be loaded.</p>
+ *
+ * <p><u>Generated setter function</u></p>
+ *
+ * <p>The second generated function sets the associated model instance - if only a single argument is passed to
+ * the setter then the following two calls are identical:</p>
+ *
+<pre><code>
+//this call
+product.setCategory(10);
+
+//is equivalent to this call:
+product.set('category_id', 10);
+</code></pre>
+ * <p>If we pass in a second argument, the model will be automatically saved and the second argument passed to
+ * the owner model's {@link Ext.data.Model#save save} method:</p>
+<pre><code>
+product.setCategory(10, function(product, operation) {
+    //the product has been saved
+    alert(product.get('category_id')); //now alerts 10
+});
+
+//alternative syntax:
+product.setCategory(10, {
+    callback: function(product, operation), //a function that will always be called
+    success : function(product, operation), //a function that will only be called if the load succeeded
+    failure : function(product, operation), //a function that will only be called if the load did not succeed
+    scope   : this //optionally pass in a scope object to execute the callbacks in
+})
+</code></pre>
+*
+ * <p><u>Customisation</u></p>
+ *
+ * <p>Associations reflect on the models they are linking to automatically set up properties such as the
+ * {@link #primaryKey} and {@link #foreignKey}. These can alternatively be specified:</p>
+ *
+<pre><code>
+Ext.define('Product', {
+    fields: [...],
+
+    associations: [
+        {type: 'belongsTo', model: 'Category', primaryKey: 'unique_id', foreignKey: 'cat_id'}
+    ]
+});
+ </code></pre>
+ *
+ * <p>Here we replaced the default primary key (defaults to 'id') and foreign key (calculated as 'category_id')
+ * with our own settings. Usually this will not be needed.</p>
+ */
+Ext.define('Ext.data.BelongsToAssociation', {
+    extend: 'Ext.data.Association',
+
+    alias: 'association.belongsto',
+
+    /**
+     * @cfg {String} foreignKey The name of the foreign key on the owner model that links it to the associated
+     * model. Defaults to the lowercased name of the associated model plus "_id", e.g. an association with a
+     * model called Product would set up a product_id foreign key.
+     * <pre><code>
+Ext.define('Order', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'date'],
+    hasMany: 'Product'
+});
+
+Ext.define('Product', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'order_id'], // refers to the id of the order that this product belongs to
+    belongsTo: 'Group'
+});
+var product = new Product({
+    id: 1,
+    name: 'Product 1',
+    order_id: 22
+}, 1);
+product.getOrder(); // Will make a call to the server asking for order_id 22
+
+     * </code></pre>
+     */
+
+    /**
+     * @cfg {String} getterName The name of the getter function that will be added to the local model's prototype.
+     * Defaults to 'get' + the name of the foreign model, e.g. getCategory
+     */
+
+    /**
+     * @cfg {String} setterName The name of the setter function that will be added to the local model's prototype.
+     * Defaults to 'set' + the name of the foreign model, e.g. setCategory
+     */
+    
+    /**
+     * @cfg {String} type The type configuration can be used when creating associations using a configuration object.
+     * Use 'belongsTo' to create a HasManyAssocation
+     * <pre><code>
+associations: [{
+    type: 'belongsTo',
+    model: 'User'
+}]
+     * </code></pre>
+     */
+
+    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();
+    },
+
+    /**
+     * @private
+     * Returns a setter function to be placed on the owner model's prototype
+     * @return {Function} The setter function
+     */
+    createSetter: function() {
+        var me              = this,
+            ownerModel      = me.ownerModel,
+            associatedModel = me.associatedModel,
+            foreignKey      = me.foreignKey,
+            primaryKey      = me.primaryKey;
+
+        //'this' refers to the Model instance inside this function
+        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);
+            }
+        };
+    },
+
+    /**
+     * @private
+     * Returns a getter function to be placed on the owner model's prototype. We cache the loaded instance
+     * the first time it is loaded so that subsequent calls to the getter always receive the same reference.
+     * @return {Function} The getter function
+     */
+    createGetter: function() {
+        var me              = this,
+            ownerModel      = me.ownerModel,
+            associatedName  = me.associatedName,
+            associatedModel = me.associatedModel,
+            foreignKey      = me.foreignKey,
+            primaryKey      = me.primaryKey,
+            instanceName    = me.instanceName;
+
+        //'this' refers to the Model instance inside this function
+        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];
+
+                //TODO: We're duplicating the callback invokation code that the instance.load() call above
+                //makes here - ought to be able to normalize this - perhaps by caching at the Model.load layer
+                //instead of the association layer.
+                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 associated data
+     * @private
+     * @param {Ext.data.Model} record The record we're writing to
+     * @param {Ext.data.reader.Reader} reader The reader for the associated model
+     * @param {Object} associationData The raw associated data
+     */
+    read: function(record, reader, associationData){
+        record[this.instanceName] = reader.read([associationData]).records[0];
+    }
+});
+
+/**
+ * @class Ext.data.BufferStore
+ * @extends Ext.data.Store
+ * @ignore
+ */
+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');
+    }
+});
+/**
+ * @class Ext.direct.Manager
+ * <p><b><u>Overview</u></b></p>
+ *
+ * <p>Ext.Direct aims to streamline communication between the client and server
+ * by providing a single interface that reduces the amount of common code
+ * typically required to validate data and handle returned data packets
+ * (reading data, error conditions, etc).</p>
+ *
+ * <p>The Ext.direct namespace includes several classes for a closer integration
+ * with the server-side. The Ext.data namespace also includes classes for working
+ * with Ext.data.Stores which are backed by data from an Ext.Direct method.</p>
+ *
+ * <p><b><u>Specification</u></b></p>
+ *
+ * <p>For additional information consult the
+ * <a href="http://sencha.com/products/extjs/extdirect">Ext.Direct Specification</a>.</p>
+ *
+ * <p><b><u>Providers</u></b></p>
+ *
+ * <p>Ext.Direct uses a provider architecture, where one or more providers are
+ * used to transport data to and from the server. There are several providers
+ * that exist in the core at the moment:</p><div class="mdetail-params"><ul>
+ *
+ * <li>{@link Ext.direct.JsonProvider JsonProvider} for simple JSON operations</li>
+ * <li>{@link Ext.direct.PollingProvider PollingProvider} for repeated requests</li>
+ * <li>{@link Ext.direct.RemotingProvider RemotingProvider} exposes server side
+ * on the client.</li>
+ * </ul></div>
+ *
+ * <p>A provider does not need to be invoked directly, providers are added via
+ * {@link Ext.direct.Manager}.{@link Ext.direct.Manager#add add}.</p>
+ *
+ * <p><b><u>Router</u></b></p>
+ *
+ * <p>Ext.Direct utilizes a "router" on the server to direct requests from the client
+ * to the appropriate server-side method. Because the Ext.Direct API is completely
+ * platform-agnostic, you could completely swap out a Java based server solution
+ * and replace it with one that uses C# without changing the client side JavaScript
+ * at all.</p>
+ *
+ * <p><b><u>Server side events</u></b></p>
+ *
+ * <p>Custom events from the server may be handled by the client by adding
+ * listeners, for example:</p>
+ * <pre><code>
+{"type":"event","name":"message","data":"Successfully polled at: 11:19:30 am"}
+
+// add a handler for a 'message' event sent by the server
+Ext.direct.Manager.on('message', function(e){
+    out.append(String.format('&lt;p>&lt;i>{0}&lt;/i>&lt;/p>', e.data));
+            out.el.scrollTo('t', 100000, true);
+});
+ * </code></pre>
+ * @singleton
+ */
+
+Ext.define('Ext.direct.Manager', {
+    
+    /* Begin Definitions */
+    singleton: true,
+   
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+    
+    requires: ['Ext.util.MixedCollection'],
+    
+    statics: {
+        exceptions: {
+            TRANSPORT: 'xhr',
+            PARSE: 'parse',
+            LOGIN: 'login',
+            SERVER: 'exception'
+        }
+    },
+    
+    /* End Definitions */
+   
+    constructor: function(){
+        var me = this;
+       
+        me.addEvents(
+            /**
+             * @event event
+             * Fires after an event.
+             * @param {event} e The Ext.direct.Event type that occurred.
+             * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
+             */
+            'event',
+            /**
+             * @event exception
+             * Fires after an event exception.
+             * @param {event} e The Ext.direct.Event type that occurred.
+             */
+            'exception'
+        );
+        me.transactions = Ext.create('Ext.util.MixedCollection');
+        me.providers = Ext.create('Ext.util.MixedCollection');
+        
+        me.mixins.observable.constructor.call(me);
+    },
+    
+    /**
+     * Adds an Ext.Direct Provider and creates the proxy or stub methods to execute server-side methods.
+     * If the provider is not already connected, it will auto-connect.
+     * <pre><code>
+var pollProv = new Ext.direct.PollingProvider({
+    url: 'php/poll2.php'
+});
+
+Ext.direct.Manager.addProvider({
+    "type":"remoting",       // create a {@link Ext.direct.RemotingProvider}
+    "url":"php\/router.php", // url to connect to the Ext.Direct server-side router.
+    "actions":{              // each property within the actions object represents a Class
+        "TestAction":[       // array of methods within each server side Class
+        {
+            "name":"doEcho", // name of method
+            "len":1
+        },{
+            "name":"multiply",
+            "len":1
+        },{
+            "name":"doForm",
+            "formHandler":true, // handle form on server with Ext.Direct.Transaction
+            "len":1
+        }]
+    },
+    "namespace":"myApplication",// namespace to create the Remoting Provider in
+},{
+    type: 'polling', // create a {@link Ext.direct.PollingProvider}
+    url:  'php/poll.php'
+}, pollProv); // reference to previously created instance
+     * </code></pre>
+     * @param {Object/Array} provider Accepts either an Array of Provider descriptions (an instance
+     * or config object for a Provider) or any number of Provider descriptions as arguments.  Each
+     * Provider description instructs Ext.Direct how to create client-side stub methods.
+     */
+    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 has not already been instantiated
+        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;
+    },
+    
+    /**
+     * Retrieve a {@link Ext.direct.Provider provider} by the
+     * <b><tt>{@link Ext.direct.Provider#id id}</tt></b> specified when the provider is
+     * {@link #addProvider added}.
+     * @param {String/Ext.data.Provider} id The id of the provider, or the provider instance.
+     */
+    getProvider : function(id){
+        return id.isProvider ? id : this.providers.get(id);
+    },
+    
+    /**
+     * Removes the provider.
+     * @param {String/Ext.direct.Provider} provider The provider instance or the id of the provider.
+     * @return {Ext.direct.Provider} The provider, null if not found.
+     */
+    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;
+    },
+    
+    /**
+     * Add a transaction to the manager.
+     * @private
+     * @param {Ext.direct.Transaction} transaction The transaction to add
+     * @return {Ext.direct.Transaction} transaction
+     */
+    addTransaction: function(transaction){
+        this.transactions.add(transaction);
+        return transaction;
+    },
+
+    /**
+     * Remove a transaction from the manager.
+     * @private
+     * @param {String/Ext.direct.Transaction} transaction The transaction/id of transaction to remove
+     * @return {Ext.direct.Transaction} transaction
+     */
+    removeTransaction: function(transaction){
+        transaction = this.getTransaction(transaction);
+        this.transactions.remove(transaction);
+        return transaction;
+    },
+
+    /**
+     * Gets a transaction
+     * @private
+     * @param {String/Ext.direct.Transaction} transaction The transaction/id of transaction to get
+     * @return {Ext.direct.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(){
+    // Backwards compatibility
+    Ext.Direct = Ext.direct.Manager;
+});
+
+/**
+ * @class Ext.data.proxy.Direct
+ * @extends Ext.data.proxy.Server
+ * 
+ * This class is used to send requests to the server using {@link Ext.direct}. When a request is made,
+ * the transport mechanism is handed off to the appropriate {@link Ext.direct.RemotingProvider Provider}
+ * to complete the call.
+ * 
+ * ## Specifying the function
+ * This proxy expects a Direct remoting method to be passed in order to be able to complete requests.
+ * This can be done by specifying the {@link #directFn} configuration. This will use the same direct
+ * method for all requests. Alternatively, you can provide an {@link #api} configuration. This
+ * allows you to specify a different remoting method for each CRUD action.
+ * 
+ * ## Paramaters
+ * This proxy provides options to help configure which parameters will be sent to the server.
+ * By specifying the {@link #paramsAsHash} option, it will send an object literal containing each
+ * of the passed parameters. The {@link #paramOrder} option can be used to specify the order in which
+ * the remoting method parameters are passed.
+ * 
+ * ## Example Usage
+ * 
+ *     Ext.define('User', {
+ *         extend: 'Ext.data.Model',
+ *         fields: ['firstName', 'lastName'],
+ *         proxy: {
+ *             type: 'direct',
+ *             directFn: MyApp.getUsers,
+ *             paramOrder: 'id' // Tells the proxy to pass the id as the first parameter to the remoting method.
+ *         }
+ *     });
+ *     User.load(1);
+ */
+Ext.define('Ext.data.proxy.Direct', {
+    /* Begin Definitions */
+    
+    extend: 'Ext.data.proxy.Server',
+    alternateClassName: 'Ext.data.DirectProxy',
+    
+    alias: 'proxy.direct',
+    
+    requires: ['Ext.direct.Manager'],
+    
+    /* End Definitions */
+   
+   /**
+     * @cfg {Array/String} paramOrder Defaults to <tt>undefined</tt>. A list of params to be executed
+     * server side.  Specify the params in the order in which they must be executed on the server-side
+     * as either (1) an Array of String values, or (2) a String of params delimited by either whitespace,
+     * comma, or pipe. For example,
+     * any of the following would be acceptable:<pre><code>
+paramOrder: ['param1','param2','param3']
+paramOrder: 'param1 param2 param3'
+paramOrder: 'param1,param2,param3'
+paramOrder: 'param1|param2|param'
+     </code></pre>
+     */
+    paramOrder: undefined,
+
+    /**
+     * @cfg {Boolean} paramsAsHash
+     * Send parameters as a collection of named arguments (defaults to <tt>true</tt>). Providing a
+     * <tt>{@link #paramOrder}</tt> nullifies this configuration.
+     */
+    paramsAsHash: true,
+
+    /**
+     * @cfg {Function} directFn
+     * Function to call when executing a request.  directFn is a simple alternative to defining the api configuration-parameter
+     * for Store's which will not implement a full CRUD api.
+     */
+    directFn : undefined,
+    
+    /**
+     * @cfg {Object} api The same as {@link Ext.data.proxy.Server#api}, however instead of providing urls, you should provide a direct
+     * function call.
+     */
+    
+    /**
+     * @cfg {Object} extraParams Extra parameters that will be included on every read request. Individual requests with params
+     * of the same name will override these params when they are in conflict.
+     */
+    
+    // private
+    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;
+            
+        //<debug>
+        if (!fn) {
+            Ext.Error.raise('No direct function specified for this proxy');
+        }
+        //</debug>
+            
+        if (operation.allowWrite()) {
+            request = writer.write(request);
+        }
+        
+        if (operation.action == 'read') {
+            // We need to pass params
+            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);
+    },
+    
+    /*
+     * Inherit docs. We don't apply any encoding here because
+     * all of the direct requests go out as jsonData
+     */
+    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);
+        };
+    },
+    
+    // inherit docs
+    extractResponseData: function(response){
+        return Ext.isDefined(response.result) ? response.result : response.data;
+    },
+    
+    // inherit docs
+    setException: function(operation, response) {
+        operation.setException(response.message);
+    },
+    
+    // inherit docs
+    buildUrl: function(){
+        return '';
+    }
+});
+
+/**
+ * @class Ext.data.DirectStore
+ * @extends Ext.data.Store
+ * <p>Small helper class to create an {@link Ext.data.Store} configured with an
+ * {@link Ext.data.proxy.Direct} and {@link Ext.data.reader.Json} to make interacting
+ * with an {@link Ext.Direct} Server-side {@link Ext.direct.Provider Provider} easier.
+ * To create a different proxy/reader combination create a basic {@link Ext.data.Store}
+ * configured as needed.</p>
+ *
+ * <p><b>*Note:</b> Although they are not listed, this class inherits all of the config options of:</p>
+ * <div><ul class="mdetail-params">
+ * <li><b>{@link Ext.data.Store Store}</b></li>
+ * <div class="sub-desc"><ul class="mdetail-params">
+ *
+ * </ul></div>
+ * <li><b>{@link Ext.data.reader.Json JsonReader}</b></li>
+ * <div class="sub-desc"><ul class="mdetail-params">
+ * <li><tt><b>{@link Ext.data.reader.Json#root root}</b></tt></li>
+ * <li><tt><b>{@link Ext.data.reader.Json#idProperty idProperty}</b></tt></li>
+ * <li><tt><b>{@link Ext.data.reader.Json#totalProperty totalProperty}</b></tt></li>
+ * </ul></div>
+ *
+ * <li><b>{@link Ext.data.proxy.Direct DirectProxy}</b></li>
+ * <div class="sub-desc"><ul class="mdetail-params">
+ * <li><tt><b>{@link Ext.data.proxy.Direct#directFn directFn}</b></tt></li>
+ * <li><tt><b>{@link Ext.data.proxy.Direct#paramOrder paramOrder}</b></tt></li>
+ * <li><tt><b>{@link Ext.data.proxy.Direct#paramsAsHash paramsAsHash}</b></tt></li>
+ * </ul></div>
+ * </ul></div>
+ *
+ * @constructor
+ * @param {Object} config
+ */
+
+Ext.define('Ext.data.DirectStore', {
+    /* Begin Definitions */
+    
+    extend: 'Ext.data.Store',
+    
+    alias: 'store.direct',
+    
+    requires: ['Ext.data.proxy.Direct'],
+   
+    /* End Definitions */
+   
+   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]);
+    }    
+});
+
+/**
+ * @class Ext.util.Inflector
+ * @extends Object
+ * <p>General purpose inflector class that {@link #pluralize pluralizes}, {@link #singularize singularizes} and 
+ * {@link #ordinalize ordinalizes} words. Sample usage:</p>
+ * 
+<pre><code>
+//turning singular words into plurals
+Ext.util.Inflector.pluralize('word'); //'words'
+Ext.util.Inflector.pluralize('person'); //'people'
+Ext.util.Inflector.pluralize('sheep'); //'sheep'
+
+//turning plurals into singulars
+Ext.util.Inflector.singularize('words'); //'word'
+Ext.util.Inflector.singularize('people'); //'person'
+Ext.util.Inflector.singularize('sheep'); //'sheep'
+
+//ordinalizing numbers
+Ext.util.Inflector.ordinalize(11); //"11th"
+Ext.util.Inflector.ordinalize(21); //"21th"
+Ext.util.Inflector.ordinalize(1043); //"1043rd"
+</code></pre>
+ * 
+ * <p><u>Customization</u></p>
+ * 
+ * <p>The Inflector comes with a default set of US English pluralization rules. These can be augmented with additional
+ * rules if the default rules do not meet your application's requirements, or swapped out entirely for other languages.
+ * Here is how we might add a rule that pluralizes "ox" to "oxen":</p>
+ * 
+<pre><code>
+Ext.util.Inflector.plural(/^(ox)$/i, "$1en");
+</code></pre>
+ * 
+ * <p>Each rule consists of two items - a regular expression that matches one or more rules, and a replacement string.
+ * In this case, the regular expression will only match the string "ox", and will replace that match with "oxen". 
+ * Here's how we could add the inverse rule:</p>
+ * 
+<pre><code>
+Ext.util.Inflector.singular(/^(ox)en$/i, "$1");
+</code></pre>
+ * 
+ * <p>Note that the ox/oxen rules are present by default.</p>
+ * 
+ * @singleton
+ */
+
+Ext.define('Ext.util.Inflector', {
+
+    /* Begin Definitions */
+
+    singleton: true,
+
+    /* End Definitions */
+
+    /**
+     * @private
+     * The registered plural tuples. Each item in the array should contain two items - the first must be a regular
+     * expression that matchers the singular form of a word, the second must be a String that replaces the matched
+     * part of the regular expression. This is managed by the {@link #plural} method.
+     * @property plurals
+     * @type Array
+     */
+    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"      ]
+    ],
+    
+    /**
+     * @private
+     * The set of registered singular matchers. Each item in the array should contain two items - the first must be a 
+     * regular expression that matches the plural form of a word, the second must be a String that replaces the 
+     * matched part of the regular expression. This is managed by the {@link #singular} method.
+     * @property singulars
+     * @type Array
+     */
+    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),                                                            ""       ]
+    ],
+    
+    /**
+     * @private
+     * The registered uncountable words
+     * @property uncountable
+     * @type Array
+     */
+     uncountable: [
+        "sheep",
+        "fish",
+        "series",
+        "species",
+        "money",
+        "rice",
+        "information",
+        "equipment",
+        "grass",
+        "mud",
+        "offspring",
+        "deer",
+        "means"
+    ],
+    
+    /**
+     * Adds a new singularization rule to the Inflector. See the intro docs for more information
+     * @param {RegExp} matcher The matcher regex
+     * @param {String} replacer The replacement string, which can reference matches from the matcher argument
+     */
+    singular: function(matcher, replacer) {
+        this.singulars.unshift([matcher, replacer]);
+    },
+    
+    /**
+     * Adds a new pluralization rule to the Inflector. See the intro docs for more information
+     * @param {RegExp} matcher The matcher regex
+     * @param {String} replacer The replacement string, which can reference matches from the matcher argument
+     */
+    plural: function(matcher, replacer) {
+        this.plurals.unshift([matcher, replacer]);
+    },
+    
+    /**
+     * Removes all registered singularization rules
+     */
+    clearSingulars: function() {
+        this.singulars = [];
+    },
+    
+    /**
+     * Removes all registered pluralization rules
+     */
+    clearPlurals: function() {
+        this.plurals = [];
+    },
+    
+    /**
+     * Returns true if the given word is transnumeral (the word is its own singular and plural form - e.g. sheep, fish)
+     * @param {String} word The word to test
+     * @return {Boolean} True if the word is transnumeral
+     */
+    isTransnumeral: function(word) {
+        return Ext.Array.indexOf(this.uncountable, word) != -1;
+    },
+
+    /**
+     * Returns the pluralized form of a word (e.g. Ext.util.Inflector.pluralize('word') returns 'words')
+     * @param {String} word The word to pluralize
+     * @return {String} The pluralized form of the word
+     */
+    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;
+    },
+    
+    /**
+     * Returns the singularized form of a word (e.g. Ext.util.Inflector.singularize('words') returns 'word')
+     * @param {String} word The word to singularize
+     * @return {String} The singularized form of the 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;
+    },
+    
+    /**
+     * Returns the correct {@link Ext.data.Model Model} name for a given string. Mostly used internally by the data 
+     * package
+     * @param {String} word The word to classify
+     * @return {String} The classified version of the word
+     */
+    classify: function(word) {
+        return Ext.String.capitalize(this.singularize(word));
+    },
+    
+    /**
+     * Ordinalizes a given number by adding a prefix such as 'st', 'nd', 'rd' or 'th' based on the last digit of the 
+     * number. 21 -> 21st, 22 -> 22nd, 23 -> 23rd, 24 -> 24th etc
+     * @param {Number} number The number to ordinalize
+     * @return {String} The ordinalized number
+     */
+    ordinalize: function(number) {
+        var parsed = parseInt(number, 10),
+            mod10  = parsed % 10,
+            mod100 = parsed % 100;
+        
+        //11 through 13 are a special case
+        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() {
+    //aside from the rules above, there are a number of words that have irregular pluralization so we add them here
+    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);
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.HasManyAssociation
+ * @extends Ext.data.Association
+ * 
+ * <p>Represents a one-to-many relationship between two models. Usually created indirectly via a model definition:</p>
+ * 
+<pre><code>
+Ext.define('Product', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'id',      type: 'int'},
+        {name: 'user_id', type: 'int'},
+        {name: 'name',    type: 'string'}
+    ]
+});
+
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'id',   type: 'int'},
+        {name: 'name', type: 'string'}
+    ],
+    // we can use the hasMany shortcut on the model to create a hasMany association
+    hasMany: {model: 'Product', name: 'products'}
+});
+</pre></code>
+* 
+ * <p>Above we created Product and User models, and linked them by saying that a User hasMany Products. This gives
+ * us a new function on every User instance, in this case the function is called 'products' because that is the name
+ * we specified in the association configuration above.</p>
+ * 
+ * <p>This new function returns a specialized {@link Ext.data.Store Store} which is automatically filtered to load
+ * only Products for the given model instance:</p>
+ * 
+<pre><code>
+//first, we load up a User with id of 1
+var user = Ext.ModelManager.create({id: 1, name: 'Ed'}, 'User');
+
+//the user.products function was created automatically by the association and returns a {@link Ext.data.Store Store}
+//the created store is automatically scoped to the set of Products for the User with id of 1
+var products = user.products();
+
+//we still have all of the usual Store functions, for example it's easy to add a Product for this User
+products.add({
+    name: 'Another Product'
+});
+
+//saves the changes to the store - this automatically sets the new Product's user_id to 1 before saving
+products.sync();
+</code></pre>
+ * 
+ * <p>The new Store is only instantiated the first time you call products() to conserve memory and processing time,
+ * though calling products() a second time returns the same store instance.</p>
+ * 
+ * <p><u>Custom filtering</u></p>
+ * 
+ * <p>The Store is automatically furnished with a filter - by default this filter tells the store to only return
+ * records where the associated model's foreign key matches the owner model's primary key. For example, if a User
+ * with ID = 100 hasMany Products, the filter loads only Products with user_id == 100.</p>
+ * 
+ * <p>Sometimes we want to filter by another field - for example in the case of a Twitter search application we may
+ * have models for Search and Tweet:</p>
+ * 
+<pre><code>
+Ext.define('Search', {
+    extend: 'Ext.data.Model',
+    fields: [
+        'id', 'query'
+    ],
+
+    hasMany: {
+        model: 'Tweet',
+        name : 'tweets',
+        filterProperty: 'query'
+    }
+});
+
+Ext.define('Tweet', {
+    extend: 'Ext.data.Model',
+    fields: [
+        'id', 'text', 'from_user'
+    ]
+});
+
+//returns a Store filtered by the filterProperty
+var store = new Search({query: 'Sencha Touch'}).tweets();
+</code></pre>
+ * 
+ * <p>The tweets association above is filtered by the query property by setting the {@link #filterProperty}, and is
+ * equivalent to this:</p>
+ * 
+<pre><code>
+var store = new Ext.data.Store({
+    model: 'Tweet',
+    filters: [
+        {
+            property: 'query',
+            value   : 'Sencha Touch'
+        }
+    ]
+});
+</code></pre>
+ */
+Ext.define('Ext.data.HasManyAssociation', {
+    extend: 'Ext.data.Association',
+    requires: ['Ext.util.Inflector'],
+
+    alias: 'association.hasmany',
+
+    /**
+     * @cfg {String} foreignKey The name of the foreign key on the associated model that links it to the owner
+     * model. Defaults to the lowercased name of the owner model plus "_id", e.g. an association with a where a
+     * model called Group hasMany Users would create 'group_id' as the foreign key. When the remote store is loaded,
+     * the store is automatically filtered so that only records with a matching foreign key are included in the 
+     * resulting child store. This can be overridden by specifying the {@link #filterProperty}.
+     * <pre><code>
+Ext.define('Group', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name'],
+    hasMany: 'User'
+});
+
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'group_id'], // refers to the id of the group that this user belongs to
+    belongsTo: 'Group'
+});
+     * </code></pre>
+     */
+    
+    /**
+     * @cfg {String} name The name of the function to create on the owner model to retrieve the child store.
+     * If not specified, the pluralized name of the child model is used.
+     * <pre><code>
+// This will create a users() method on any Group model instance
+Ext.define('Group', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name'],
+    hasMany: 'User'
+});
+var group = new Group();
+console.log(group.users());
+
+// The method to retrieve the users will now be getUserList
+Ext.define('Group', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name'],
+    hasMany: {model: 'User', name: 'getUserList'}
+});
+var group = new Group();
+console.log(group.getUserList());
+     * </code></pre>
+     */
+    
+    /**
+     * @cfg {Object} storeConfig Optional configuration object that will be passed to the generated Store. Defaults to 
+     * undefined.
+     */
+    
+    /**
+     * @cfg {String} filterProperty Optionally overrides the default filter that is set up on the associated Store. If
+     * this is not set, a filter is automatically created which filters the association based on the configured 
+     * {@link #foreignKey}. See intro docs for more details. Defaults to undefined
+     */
+    
+    /**
+     * @cfg {Boolean} autoLoad True to automatically load the related store from a remote source when instantiated.
+     * Defaults to <tt>false</tt>.
+     */
+    
+    /**
+     * @cfg {String} type The type configuration can be used when creating associations using a configuration object.
+     * Use 'hasMany' to create a HasManyAssocation
+     * <pre><code>
+associations: [{
+    type: 'hasMany',
+    model: 'User'
+}]
+     * </code></pre>
+     */
+    
+    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();
+    },
+    
+    /**
+     * @private
+     * Creates a function that returns an Ext.data.Store which is configured to load a set of data filtered
+     * by the owner model's primary key - e.g. in a hasMany association where Group hasMany Users, this function
+     * returns a Store configured to return the filtered set of a single Group's Users.
+     * @return {Function} The store-generating function
+     */
+    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 associated data
+     * @private
+     * @param {Ext.data.Model} record The record we're writing to
+     * @param {Ext.data.reader.Reader} reader The reader for the associated model
+     * @param {Object} associationData The raw associated data
+     */
+    read: function(record, reader, associationData){
+        var store = record[this.name](),
+            inverse;
+    
+        store.add(reader.read(associationData).records);
+    
+        //now that we've added the related records to the hasMany association, set the inverse belongsTo
+        //association on each of them if it exists
+        inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
+            return assoc.type === 'belongsTo' && assoc.associatedName === record.$className;
+        });
+    
+        //if the inverse association was found, set it now on each record we've just created
+        if (inverse) {
+            store.data.each(function(associatedRecord){
+                associatedRecord[inverse.instanceName] = record;
+            });
+        }
+    }
+});
+/**
+ * @class Ext.data.JsonP
+ * @singleton
+ * This class is used to create JSONP requests. JSONP is a mechanism that allows for making
+ * requests for data cross domain. More information is available here:
+ * http://en.wikipedia.org/wiki/JSONP
+ */
+Ext.define('Ext.data.JsonP', {
+    
+    /* Begin Definitions */
+    
+    singleton: true,
+    
+    statics: {
+        requestCount: 0,
+        requests: {}
+    },
+    
+    /* End Definitions */
+    
+    /**
+     * @property timeout
+     * @type Number
+     * A default timeout for any JsonP requests. If the request has not completed in this time the
+     * failure callback will be fired. The timeout is in ms. Defaults to <tt>30000</tt>.
+     */
+    timeout: 30000,
+    
+    /**
+     * @property disableCaching
+     * @type Boolean
+     * True to add a unique cache-buster param to requests. Defaults to <tt>true</tt>.
+     */
+    disableCaching: true,
+   
+    /**
+     * @property disableCachingParam 
+     * @type String
+     * Change the parameter which is sent went disabling caching through a cache buster. Defaults to <tt>'_dc'</tt>.
+     */
+    disableCachingParam: '_dc',
+   
+    /**
+     * @property callbackKey
+     * @type String
+     * Specifies the GET parameter that will be sent to the server containing the function name to be executed when
+     * the request completes. Defaults to <tt>callback</tt>. Thus, a common request will be in the form of
+     * url?callback=Ext.data.JsonP.callback1
+     */
+    callbackKey: 'callback',
+   
+    /**
+     * Makes a JSONP request.
+     * @param {Object} options An object which may contain the following properties. Note that options will
+     * take priority over any defaults that are specified in the class.
+     * <ul>
+     * <li><b>url</b> : String <div class="sub-desc">The URL to request.</div></li>
+     * <li><b>params</b> : Object (Optional)<div class="sub-desc">An object containing a series of
+     * key value pairs that will be sent along with the request.</div></li>
+     * <li><b>timeout</b> : Number (Optional) <div class="sub-desc">See {@link #timeout}</div></li>
+     * <li><b>callbackKey</b> : String (Optional) <div class="sub-desc">See {@link #callbackKey}</div></li>
+     * <li><b>disableCaching</b> : Boolean (Optional) <div class="sub-desc">See {@link #disableCaching}</div></li>
+     * <li><b>disableCachingParam</b> : String (Optional) <div class="sub-desc">See {@link #disableCachingParam}</div></li>
+     * <li><b>success</b> : Function (Optional) <div class="sub-desc">A function to execute if the request succeeds.</div></li>
+     * <li><b>failure</b> : Function (Optional) <div class="sub-desc">A function to execute if the request fails.</div></li>
+     * <li><b>callback</b> : Function (Optional) <div class="sub-desc">A function to execute when the request 
+     * completes, whether it is a success or failure.</div></li>
+     * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
+     * which to execute the callbacks: The "this" object for the callback function. Defaults to the browser window.</div></li>
+     * </ul>
+     * @return {Object} request An object containing the request details.
+     */
+    request: function(options){
+        options = Ext.apply({}, options);
+       
+        //<debug>
+        if (!options.url) {
+            Ext.Error.raise('A url must be specified for a JSONP request.');
+        }
+        //</debug>
+        
+        var me = this, 
+            disableCaching = Ext.isDefined(options.disableCaching) ? options.disableCaching : me.disableCaching, 
+            cacheParam = options.disableCachingParam || me.disableCachingParam, 
+            id = ++me.statics().requestCount, 
+            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 a request. If the request parameter is not specified all open requests will
+     * be aborted.
+     * @param {Object/String} request (Optional) The request to abort
+     */
+    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]);
+                }
+            }
+        }
+    },
+    
+    /**
+     * Sets up error handling for the script
+     * @private
+     * @param {Object} request The request
+     */
+    setupErrorHandling: function(request){
+        request.script.onerror = Ext.bind(this.handleError, this, [request]);
+    },
+    
+    /**
+     * Handles any aborts when loading the script
+     * @private
+     * @param {Object} request The request
+     */
+    handleAbort: function(request){
+        request.errorType = 'abort';
+        this.handleResponse(null, request);
+    },
+    
+    /**
+     * Handles any script errors when loading the script
+     * @private
+     * @param {Object} request The request
+     */
+    handleError: function(request){
+        request.errorType = 'error';
+        this.handleResponse(null, request);
+    },
+    /**
+     * Cleans up anu script handling errors
+     * @private
+     * @param {Object} request The request
+     */
+    cleanupErrorHandling: function(request){
+        request.script.onerror = null;
+    },
+    /**
+     * Handle any script timeouts
+     * @private
+     * @param {Object} request The request
+     */
+    handleTimeout: function(request){
+        request.errorType = 'timeout';
+        this.handleResponse(null, request);
+    },
+    /**
+     * Handle a successful response
+     * @private
+     * @param {Object} result The result from the request
+     * @param {Object} request The 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]);
+    },
+    
+    /**
+     * Create the script tag
+     * @private
+     * @param {String} url The url of the request
+     * @param {Object} params Any extra params to be sent
+     */
+    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;
+    }
+});
+
+/**
+ * @class Ext.data.JsonPStore
+ * @extends Ext.data.Store
+ * @ignore
+ * @private
+ * <p><b>NOTE:</b> This class is in need of migration to the new API.</p>
+ * <p>Small helper class to make creating {@link Ext.data.Store}s from different domain JSON data easier.
+ * A JsonPStore will be automatically configured with a {@link Ext.data.reader.Json} and a {@link Ext.data.proxy.JsonP JsonPProxy}.</p>
+ * <p>A store configuration would be something like:<pre><code>
+var store = new Ext.data.JsonPStore({
+    // store configs
+    autoDestroy: true,
+    storeId: 'myStore',
+
+    // proxy configs
+    url: 'get-images.php',
+
+    // reader configs
+    root: 'images',
+    idProperty: 'name',
+    fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
+});
+ * </code></pre></p>
+ * <p>This store is configured to consume a returned object of the form:<pre><code>
+stcCallback({
+    images: [
+        {name: 'Image one', url:'/GetImage.php?id=1', size:46.5, lastmod: new Date(2007, 10, 29)},
+        {name: 'Image Two', url:'/GetImage.php?id=2', size:43.2, lastmod: new Date(2007, 10, 30)}
+    ]
+})
+ * </code></pre>
+ * <p>Where stcCallback is the callback name passed in the request to the remote domain. See {@link Ext.data.proxy.JsonP JsonPProxy}
+ * for details of how this works.</p>
+ * An object literal of this form could also be used as the {@link #data} config option.</p>
+ * <p><b>*Note:</b> Although not listed here, this class accepts all of the configuration options of
+ * <b>{@link Ext.data.reader.Json JsonReader}</b> and <b>{@link Ext.data.proxy.JsonP JsonPProxy}</b>.</p>
+ * @constructor
+ * @param {Object} config
+ * @xtype jsonpstore
+ */
+Ext.define('Ext.data.JsonPStore', {
+    extend: 'Ext.data.Store',
+    alias : 'store.jsonp',
+
+    /**
+     * @cfg {Ext.data.DataReader} reader @hide
+     */
+    constructor: function(config) {
+        this.callParent(Ext.apply(config, {
+            reader: Ext.create('Ext.data.reader.Json', config),
+            proxy : Ext.create('Ext.data.proxy.JsonP', config)
+        }));
+    }
+});
+
+/**
+ * @class Ext.data.NodeInterface
+ * This class is meant to be used as a set of methods that are applied to the prototype of a
+ * Record to decorate it with a Node API. This means that models used in conjunction with a tree
+ * will have all of the tree related methods available on the model. In general this class will
+ * not be used directly by the developer.
+ */
+Ext.define('Ext.data.NodeInterface', {
+    requires: ['Ext.data.Field'],
+    
+    statics: {
+        /**
+         * This method allows you to decorate a Record's prototype to implement the NodeInterface.
+         * This adds a set of methods, new events, new properties and new fields on every Record
+         * with the same Model as the passed Record.
+         * @param {Ext.data.Record} record The Record you want to decorate the prototype of.
+         * @static
+         */
+        decorate: function(record) {
+            if (!record.isNode) {
+                // Apply the methods and fields to the prototype
+                // @TODO: clean this up to use proper class system stuff
+                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;
+
+                // Start by adding the NodeInterface methods to the Model's prototype
+                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;
+                // Set default values to all instances already out there
+                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: []
+            });
+            // Commit any fields so the record doesn't show as dirty initially
+            record.commit(true);
+            
+            record.enableBubble([
+                /**
+                 * @event append
+                 * Fires when a new child node is appended
+                 * @param {Node} this This node
+                 * @param {Node} node The newly appended node
+                 * @param {Number} index The index of the newly appended node
+                 */
+                "append",
+
+                /**
+                 * @event remove
+                 * Fires when a child node is removed
+                 * @param {Node} this This node
+                 * @param {Node} node The removed node
+                 */
+                "remove",
+
+                /**
+                 * @event move
+                 * Fires when this node is moved to a new location in the tree
+                 * @param {Node} this This node
+                 * @param {Node} oldParent The old parent of this node
+                 * @param {Node} newParent The new parent of this node
+                 * @param {Number} index The index it was moved to
+                 */
+                "move",
+
+                /**
+                 * @event insert
+                 * Fires when a new child node is inserted.
+                 * @param {Node} this This node
+                 * @param {Node} node The child node inserted
+                 * @param {Node} refNode The child node the node was inserted before
+                 */
+                "insert",
+
+                /**
+                 * @event beforeappend
+                 * Fires before a new child is appended, return false to cancel the append.
+                 * @param {Node} this This node
+                 * @param {Node} node The child node to be appended
+                 */
+                "beforeappend",
+
+                /**
+                 * @event beforeremove
+                 * Fires before a child is removed, return false to cancel the remove.
+                 * @param {Node} this This node
+                 * @param {Node} node The child node to be removed
+                 */
+                "beforeremove",
+
+                /**
+                 * @event beforemove
+                 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
+                 * @param {Node} this This node
+                 * @param {Node} oldParent The parent of this node
+                 * @param {Node} newParent The new parent this node is moving to
+                 * @param {Number} index The index it is being moved to
+                 */
+                "beforemove",
+
+                 /**
+                  * @event beforeinsert
+                  * Fires before a new child is inserted, return false to cancel the insert.
+                  * @param {Node} this This node
+                  * @param {Node} node The child node to be inserted
+                  * @param {Node} refNode The child node the node is being inserted before
+                  */
+                "beforeinsert",
+                
+                /**
+                 * @event expand
+                 * Fires when this node is expanded.
+                 * @param {Node} this The expanding node
+                 */
+                "expand",
+                
+                /**
+                 * @event collapse
+                 * Fires when this node is collapsed.
+                 * @param {Node} this The collapsing node
+                 */
+                "collapse",
+                
+                /**
+                 * @event beforeexpand
+                 * Fires before this node is expanded.
+                 * @param {Node} this The expanding node
+                 */
+                "beforeexpand",
+                
+                /**
+                 * @event beforecollapse
+                 * Fires before this node is collapsed.
+                 * @param {Node} this The collapsing node
+                 */
+                "beforecollapse",
+                
+                /**
+                 * @event beforecollapse
+                 * Fires before this node is collapsed.
+                 * @param {Node} this The collapsing node
+                 */
+                "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,
+
+                /**
+                 * Ensures that the passed object is an instance of a Record with the NodeInterface applied
+                 * @return {Boolean}
+                 */
+                createNode: function(node) {
+                    if (Ext.isObject(node) && !node.isModel) {
+                        node = Ext.ModelManager.create(node, this.modelName);
+                    }
+                    // Make sure the node implements the node interface
+                    return Ext.data.NodeInterface.decorate(node);
+                },
+                
+                /**
+                 * Returns true if this node is a leaf
+                 * @return {Boolean}
+                 */
+                isLeaf : function() {
+                    return this.get('leaf') === true;
+                },
+
+                /**
+                 * Sets the first child of this node
+                 * @private
+                 * @param {Ext.data.NodeInterface} node
+                 */
+                setFirstChild : function(node) {
+                    this.firstChild = node;
+                },
+
+                /**
+                 * Sets the last child of this node
+                 * @private
+                 * @param {Ext.data.NodeInterface} node
+                 */
+                setLastChild : function(node) {
+                    this.lastChild = node;
+                },
+
+                /**
+                 * Updates general data of this node like isFirst, isLast, depth. This
+                 * method is internally called after a node is moved. This shouldn't
+                 * have to be called by the developer unless they are creating custom
+                 * Tree plugins.
+                 * @return {Boolean}
+                 */
+                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);
+                    }
+                },
+
+                /**
+                 * Returns true if this node is the last child of its parent
+                 * @return {Boolean}
+                 */
+                isLast : function() {
+                   return this.get('isLast');
+                },
+
+                /**
+                 * Returns true if this node is the first child of its parent
+                 * @return {Boolean}
+                 */
+                isFirst : function() {
+                   return this.get('isFirst');
+                },
+
+                /**
+                 * Returns true if this node has one or more child nodes, else false.
+                 * @return {Boolean}
+                 */
+                hasChildNodes : function() {
+                    return !this.isLeaf() && this.childNodes.length > 0;
+                },
+
+                /**
+                 * Returns true if this node has one or more child nodes, or if the <tt>expandable</tt>
+                 * node attribute is explicitly specified as true (see {@link #attributes}), otherwise returns false.
+                 * @return {Boolean}
+                 */
+                isExpandable : function() {
+                    return this.get('expandable') || this.hasChildNodes();
+                },
+
+                /**
+                 * <p>Insert node(s) as the last child node of this node.</p>
+                 * <p>If the node was previously a child node of another parent node, it will be removed from that node first.</p>
+                 * @param {Node/Array} node The node or Array of nodes to append
+                 * @return {Node} The appended node if single append, or null if an array was passed
+                 */
+                appendChild : function(node, suppressEvents, suppressNodeUpdate) {
+                    var me = this,
+                        i, ln,
+                        index,
+                        oldParent,
+                        ps;
+
+                    // if passed an array or multiple args do them one by one
+                    if (Ext.isArray(node)) {
+                        for (i = 0, ln = node.length; i < ln; i++) {
+                            me.appendChild(node[i]);
+                        }
+                    } else {
+                        // Make sure it is a record
+                        node = me.createNode(node);
+                        
+                        if (suppressEvents !== true && me.fireEvent("beforeappend", me, node) === false) {
+                            return false;                         
+                        }
+
+                        index = me.childNodes.length;
+                        oldParent = node.parentNode;
+
+                        // it's a move, make sure we move it cleanly
+                        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);
+                        
+                        // As soon as we append a child to this node, we are loaded
+                        if (!me.isLoaded()) {
+                            me.set('loaded', true);                            
+                        }
+                        // If this node didnt have any childnodes before, update myself
+                        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;
+                    }
+                },
+                
+                /**
+                 * Returns the bubble target for this node
+                 * @private
+                 * @return {Object} The bubble target
+                 */
+                getBubbleTarget: function() {
+                    return this.parentNode;
+                },
+
+                /**
+                 * Removes a child node from this node.
+                 * @param {Node} node The node to remove
+                 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
+                 * @return {Node} The removed node
+                 */
+                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;
+                    }
+
+                    // remove it from childNodes collection
+                    me.childNodes.splice(index, 1);
+
+                    // update child refs
+                    if (me.firstChild == node) {
+                        me.setFirstChild(node.nextSibling);
+                    }
+                    if (me.lastChild == node) {
+                        me.setLastChild(node.previousSibling);
+                    }
+                    
+                    // update siblings
+                    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 this node suddenly doesnt have childnodes anymore, update myself
+                    if (!me.childNodes.length) {
+                        me.set('loaded', me.isLoaded());
+                    }
+                    
+                    if (destroy) {
+                        node.destroy(true);
+                    } else {
+                        node.clear();
+                    }
+
+                    return node;
+                },
+
+                /**
+                 * Creates a copy (clone) of this Node.
+                 * @param {String} id (optional) A new id, defaults to this Node's id. See <code>{@link #id}</code>.
+                 * @param {Boolean} deep (optional) <p>If passed as <code>true</code>, all child Nodes are recursively copied into the new Node.</p>
+                 * <p>If omitted or false, the copy will have no child Nodes.</p>
+                 * @return {Node} A copy of this Node.
+                 */
+                copy: function(newId, deep) {
+                    var me = this,
+                        result = me.callOverridden(arguments),
+                        len = me.childNodes ? me.childNodes.length : 0,
+                        i;
+
+                    // Move child nodes across to the copy if required
+                    if (deep) {
+                        for (i = 0; i < len; i++) {
+                            result.appendChild(me.childNodes[i].copy(true));
+                        }
+                    }
+                    return result;
+                },
+
+                /**
+                 * Clear the node.
+                 * @private
+                 * @param {Boolean} destroy True to destroy the node.
+                 */
+                clear : function(destroy) {
+                    var me = this;
+                    
+                    // clear any references from the node
+                    me.parentNode = me.previousSibling = me.nextSibling = null;
+                    if (destroy) {
+                        me.firstChild = me.lastChild = null;
+                    }
+                },
+
+                /**
+                 * Destroys the node.
+                 */
+                destroy : function(silent) {
+                    /*
+                     * Silent is to be used in a number of cases
+                     * 1) When setRoot is called.
+                     * 2) When destroy on the tree is called
+                     * 3) For destroying child nodes on a node
+                     */
+                    var me = this;
+                    
+                    if (silent === true) {
+                        me.clear(true);
+                        Ext.each(me.childNodes, function(n) {
+                            n.destroy(true);
+                        });
+                        me.childNodes = null;
+                    } else {
+                        me.remove(true);
+                    }
+
+                    me.callOverridden();
+                },
+
+                /**
+                 * Inserts the first node before the second node in this nodes childNodes collection.
+                 * @param {Node} node The node to insert
+                 * @param {Node} refNode The node to insert before (if null the node is appended)
+                 * @return {Node} The inserted node
+                 */
+                insertBefore : function(node, refNode, suppressEvents) {
+                    var me = this,
+                        index     = me.indexOf(refNode),
+                        oldParent = node.parentNode,
+                        refIndex  = index,
+                        ps;
+                    
+                    if (!refNode) { // like standard Dom, refNode can be null for append
+                        return me.appendChild(node);
+                    }
+                    
+                    // nothing to do
+                    if (node == refNode) {
+                        return false;
+                    }
+
+                    // Make sure it is a record with the NodeInterface
+                    node = me.createNode(node);
+                    
+                    if (suppressEvents !== true && me.fireEvent("beforeinsert", me, node, refNode) === false) {
+                        return false;
+                    }
+                    
+                    // when moving internally, indexes will change after remove
+                    if (oldParent == me && me.indexOf(node) < index) {
+                        refIndex--;
+                    }
+
+                    // it's a move, make sure we move it cleanly
+                    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);                            
+                    }    
+                    // If this node didnt have any childnodes before, update myself
+                    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;
+                },
+                
+                /**
+                 * Insert a node into this node
+                 * @param {Number} index The zero-based index to insert the node at
+                 * @param {Ext.data.Model} node The node to insert
+                 * @return {Ext.data.Record} The record you just inserted
+                 */    
+                insertChild: function(index, node) {
+                    var sibling = this.childNodes[index];
+                    if (sibling) {
+                        return this.insertBefore(node, sibling);
+                    }
+                    else {
+                        return this.appendChild(node);
+                    }
+                },
+
+                /**
+                 * Removes this node from its parent
+                 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
+                 * @return {Node} this
+                 */
+                remove : function(destroy, suppressEvents) {
+                    var parentNode = this.parentNode;
+
+                    if (parentNode) {
+                        parentNode.removeChild(this, destroy, suppressEvents, true);
+                    }
+                    return this;
+                },
+
+                /**
+                 * Removes all child nodes from this node.
+                 * @param {Boolean} destroy <tt>true</tt> to destroy the node upon removal. Defaults to <tt>false</tt>.
+                 * @return {Node} this
+                 */
+                removeAll : function(destroy, suppressEvents) {
+                    var cn = this.childNodes,
+                        n;
+
+                    while ((n = cn[0])) {
+                        this.removeChild(n, destroy, suppressEvents);
+                    }
+                    return this;
+                },
+
+                /**
+                 * Returns the child node at the specified index.
+                 * @param {Number} index
+                 * @return {Node}
+                 */
+                getChildAt : function(index) {
+                    return this.childNodes[index];
+                },
+
+                /**
+                 * Replaces one child node in this node with another.
+                 * @param {Node} newChild The replacement node
+                 * @param {Node} oldChild The node to replace
+                 * @return {Node} The replaced node
+                 */
+                replaceChild : function(newChild, oldChild, suppressEvents) {
+                    var s = oldChild ? oldChild.nextSibling : null;
+                    
+                    this.removeChild(oldChild, suppressEvents);
+                    this.insertBefore(newChild, s, suppressEvents);
+                    return oldChild;
+                },
+
+                /**
+                 * Returns the index of a child node
+                 * @param {Node} node
+                 * @return {Number} The index of the node or -1 if it was not found
+                 */
+                indexOf : function(child) {
+                    return Ext.Array.indexOf(this.childNodes, child);
+                },
+
+                /**
+                 * Returns depth of this node (the root node has a depth of 0)
+                 * @return {Number}
+                 */
+                getDepth : function() {
+                    return this.get('depth');
+                },
+
+                /**
+                 * Bubbles up the tree from this node, calling the specified function with each node. The arguments to the function
+                 * will be the args provided or the current node. If the function returns false at any point,
+                 * the bubble is stopped.
+                 * @param {Function} fn The function to call
+                 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
+                 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
+                 */
+                bubble : function(fn, scope, args) {
+                    var p = this;
+                    while (p) {
+                        if (fn.apply(scope || p, args || [p]) === false) {
+                            break;
+                        }
+                        p = p.parentNode;
+                    }
+                },
+
+                //<deprecated since=0.99>
+                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);
+                },
+                //</deprecated>
+
+                /**
+                 * Cascades down the tree from this node, calling the specified function with each node. The arguments to the function
+                 * will be the args provided or the current node. If the function returns false at any point,
+                 * the cascade is stopped on that branch.
+                 * @param {Function} fn The function to call
+                 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node.
+                 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
+                 */
+                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);
+                        }
+                    }
+                },
+
+                /**
+                 * Interates the child nodes of this node, calling the specified function with each node. The arguments to the function
+                 * will be the args provided or the current node. If the function returns false at any point,
+                 * the iteration stops.
+                 * @param {Function} fn The function to call
+                 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Node in the iteration.
+                 * @param {Array} args (optional) The args to call the function with (default to passing the current Node)
+                 */
+                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;
+                        }
+                    }
+                },
+
+                /**
+                 * Finds the first child that has the attribute with the specified value.
+                 * @param {String} attribute The attribute name
+                 * @param {Mixed} value The value to search for
+                 * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
+                 * @return {Node} The found child or null if none was found
+                 */
+                findChild : function(attribute, value, deep) {
+                    return this.findChildBy(function() {
+                        return this.get(attribute) == value;
+                    }, null, deep);
+                },
+
+                /**
+                 * Finds the first child by a custom function. The child matches if the function passed returns <code>true</code>.
+                 * @param {Function} fn A function which must return <code>true</code> if the passed Node is the required Node.
+                 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Node being tested.
+                 * @param {Boolean} deep (Optional) True to search through nodes deeper than the immediate children
+                 * @return {Node} The found child or null if none was found
+                 */
+                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;
+                },
+
+                /**
+                 * Returns true if this node is an ancestor (at any point) of the passed node.
+                 * @param {Node} node
+                 * @return {Boolean}
+                 */
+                contains : function(node) {
+                    return node.isAncestor(this);
+                },
+
+                /**
+                 * Returns true if the passed node is an ancestor (at any point) of this node.
+                 * @param {Node} node
+                 * @return {Boolean}
+                 */
+                isAncestor : function(node) {
+                    var p = this.parentNode;
+                    while (p) {
+                        if (p == node) {
+                            return true;
+                        }
+                        p = p.parentNode;
+                    }
+                    return false;
+                },
+
+                /**
+                 * Sorts this nodes children using the supplied sort function.
+                 * @param {Function} fn A function which, when passed two Nodes, returns -1, 0 or 1 depending upon required sort order.
+                 * @param {Boolean} recursive Whether or not to apply this sort recursively
+                 * @param {Boolean} suppressEvent Set to true to not fire a sort event.
+                 */
+                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);
+                        }
+                    }
+                },
+                        
+                /**
+                 * Returns true if this node is expaned
+                 * @return {Boolean}
+                 */        
+                isExpanded: function() {
+                    return this.get('expanded');
+                },
+                
+                /**
+                 * Returns true if this node is loaded
+                 * @return {Boolean}
+                 */ 
+                isLoaded: function() {
+                    return this.get('loaded');
+                },
+
+                /**
+                 * Returns true if this node is loading
+                 * @return {Boolean}
+                 */ 
+                isLoading: function() {
+                    return this.get('loading');
+                },
+                                
+                /**
+                 * Returns true if this node is the root node
+                 * @return {Boolean}
+                 */ 
+                isRoot: function() {
+                    return !this.parentNode;
+                },
+                
+                /**
+                 * Returns true if this node is visible
+                 * @return {Boolean}
+                 */ 
+                isVisible: function() {
+                    var parent = this.parentNode;
+                    while (parent) {
+                        if (!parent.isExpanded()) {
+                            return false;
+                        }
+                        parent = parent.parentNode;
+                    }
+                    return true;
+                },
+                
+                /**
+                 * Expand this node.
+                 * @param {Function} recursive (Optional) True to recursively expand all the children
+                 * @param {Function} callback (Optional) The function to execute once the expand completes
+                 * @param {Object} scope (Optional) The scope to run the callback in
+                 */
+                expand: function(recursive, callback, scope) {
+                    var me = this;
+
+                    // all paths must call the callback (eventually) or things like
+                    // selectPath fail
+
+                    // First we start by checking if this node is a parent
+                    if (!me.isLeaf()) {
+                        // Now we check if this record is already expanding or expanded
+                        if (!me.isLoading() && !me.isExpanded()) {
+                            // The TreeStore actually listens for the beforeexpand method and checks
+                            // whether we have to asynchronously load the children from the server
+                            // first. Thats why we pass a callback function to the event that the
+                            // store can call once it has loaded and parsed all the children.
+                            me.fireEvent('beforeexpand', me, function(records) {
+                                me.set('expanded', true); 
+                                me.fireEvent('expand', me, me.childNodes, false);
+                                
+                                // Call the expandChildren method if recursive was set to true 
+                                if (recursive) {
+                                    me.expandChildren(true, callback, scope);
+                                }
+                                else {
+                                    Ext.callback(callback, scope || me, [me.childNodes]);                                
+                                }
+                            }, me);                            
+                        }
+                        // If it is is already expanded but we want to recursively expand then call expandChildren
+                        else if (recursive) {
+                            me.expandChildren(true, callback, scope);
+                        }
+                        else {
+                            Ext.callback(callback, scope || me, [me.childNodes]);
+                        }
+
+                        // TODO - if the node isLoading, we probably need to defer the
+                        // callback until it is loaded (e.g., selectPath would need us
+                        // to not make the callback until the childNodes exist).
+                    }
+                    // If it's not then we fire the callback right away
+                    else {
+                        Ext.callback(callback, scope || me); // leaf = no childNodes
+                    }
+                },
+                
+                /**
+                 * Expand all the children of this node.
+                 * @param {Function} recursive (Optional) True to recursively expand all the children
+                 * @param {Function} callback (Optional) The function to execute once all the children are expanded
+                 * @param {Object} scope (Optional) The scope to run the callback in
+                 */
+                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 this node.
+                 * @param {Function} recursive (Optional) True to recursively collapse all the children
+                 * @param {Function} callback (Optional) The function to execute once the collapse completes
+                 * @param {Object} scope (Optional) The scope to run the callback in
+                 */
+                collapse: function(recursive, callback, scope) {
+                    var me = this;
+
+                    // First we start by checking if this node is a parent
+                    if (!me.isLeaf()) {
+                        // Now we check if this record is already collapsing or collapsed
+                        if (!me.collapsing && me.isExpanded()) {
+                            me.fireEvent('beforecollapse', me, function(records) {
+                                me.set('expanded', false); 
+                                me.fireEvent('collapse', me, me.childNodes, false);
+                                
+                                // Call the collapseChildren method if recursive was set to true 
+                                if (recursive) {
+                                    me.collapseChildren(true, callback, scope);
+                                }
+                                else {
+                                    Ext.callback(callback, scope || me, [me.childNodes]);                                
+                                }
+                            }, me);                            
+                        }
+                        // If it is is already collapsed but we want to recursively collapse then call collapseChildren
+                        else if (recursive) {
+                            me.collapseChildren(true, callback, scope);
+                        }
+                    }
+                    // If it's not then we fire the callback right away
+                    else {
+                        Ext.callback(callback, scope || me, me.childNodes); 
+                    }
+                },
+                
+                /**
+                 * Collapse all the children of this node.
+                 * @param {Function} recursive (Optional) True to recursively collapse all the children
+                 * @param {Function} callback (Optional) The function to execute once all the children are collapsed
+                 * @param {Object} scope (Optional) The scope to run the callback in
+                 */
+                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);
+                    }
+                }
+            };
+        }
+    }
+});
+/**
+ * @class Ext.data.NodeStore
+ * @extends Ext.data.AbstractStore
+ * Node Store
+ * @ignore
+ */
+Ext.define('Ext.data.NodeStore', {
+    extend: 'Ext.data.Store',
+    alias: 'store.node',
+    requires: ['Ext.data.NodeInterface'],
+    
+    /**
+     * @cfg {Ext.data.Record} node The Record you want to bind this Store to. Note that
+     * this record will be decorated with the Ext.data.NodeInterface if this is not the
+     * case yet.
+     */
+    node: null,
+    
+    /**
+     * @cfg {Boolean} recursive Set this to true if you want this NodeStore to represent
+     * all the descendents of the node in its flat data collection. This is useful for
+     * rendering a tree structure to a DataView and is being used internally by
+     * the TreeView. Any records that are moved, removed, inserted or appended to the
+     * node at any depth below the node this store is bound to will be automatically
+     * updated in this Store's internal flat data structure.
+     */
+    recursive: false,
+    
+    /** 
+     * @cfg {Boolean} rootVisible <tt>false</tt> to not include the root node in this Stores collection (defaults to <tt>true</tt>)
+     */    
+    rootVisible: false,
+    
+    constructor: function(config) {
+        var me = this,
+            node;
+            
+        config = config || {};
+        Ext.apply(me, config);
+        
+        //<debug>
+        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.");
+        }
+        //</debug>
+
+        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) {
+            // We want to unbind our listeners on the old 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()) {
+                        // Take a shortcut                        
+                        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()) {
+                    // Take a shortcut                        
+                    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()) {
+                    // Take a shortcut                        
+                    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;
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.Request
+ * @extends Object
+ * 
+ * <p>Simple class that represents a Request that will be made by any {@link Ext.data.proxy.Server} subclass.
+ * All this class does is standardize the representation of a Request as used by any ServerProxy subclass,
+ * it does not contain any actual logic or perform the request itself.</p>
+ * 
+ * @constructor
+ * @param {Object} config Optional config object
+ */
+Ext.define('Ext.data.Request', {
+    /**
+     * @cfg {String} action The name of the action this Request represents. Usually one of 'create', 'read', 'update' or 'destroy'
+     */
+    action: undefined,
+    
+    /**
+     * @cfg {Object} params HTTP request params. The Proxy and its Writer have access to and can modify this object.
+     */
+    params: undefined,
+    
+    /**
+     * @cfg {String} method The HTTP method to use on this Request (defaults to 'GET'). Should be one of 'GET', 'POST', 'PUT' or 'DELETE'
+     */
+    method: 'GET',
+    
+    /**
+     * @cfg {String} url The url to access on this Request
+     */
+    url: undefined,
+
+    constructor: function(config) {
+        Ext.apply(this, config);
+    }
+});
+/**
+ * @class Ext.data.Tree
+ * 
+ * This class is used as a container for a series of nodes. The nodes themselves maintain
+ * the relationship between parent/child. The tree itself acts as a manager. It gives functionality
+ * to retrieve a node by its identifier: {@link #getNodeById}. 
+ *
+ * The tree also relays events from any of it's child nodes, allowing them to be handled in a 
+ * centralized fashion. In general this class is not used directly, rather used internally 
+ * by other parts of the framework.
+ *
+ * @constructor
+ * @param {Node} root (optional) The root node
+ */
+Ext.define('Ext.data.Tree', {
+    alias: 'data.tree',
+    
+    mixins: {
+        observable: "Ext.util.Observable"
+    },
+
+    /**
+     * The root node for this tree
+     * @type Node
+     */
+    root: null,
+        
+    constructor: function(root) {
+        var me = this;
+        
+        me.nodeHash = {};
+
+        me.mixins.observable.constructor.call(me);
+                        
+        if (root) {
+            me.setRootNode(root);
+        }
+    },
+
+    /**
+     * Returns the root node for this tree.
+     * @return {Ext.data.NodeInterface}
+     */
+    getRootNode : function() {
+        return this.root;
+    },
+
+    /**
+     * Sets the root node for this tree.
+     * @param {Ext.data.NodeInterface} node
+     * @return {Ext.data.NodeInterface} The root node
+     */
+    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, [
+                /**
+                 * @event append
+                 * Fires when a new child node is appended to a node in this tree.
+                 * @param {Tree} tree The owner tree
+                 * @param {Node} parent The parent node
+                 * @param {Node} node The newly appended node
+                 * @param {Number} index The index of the newly appended node
+                 */
+                "append",
+
+                /**
+                 * @event remove
+                 * Fires when a child node is removed from a node in this tree.
+                 * @param {Tree} tree The owner tree
+                 * @param {Node} parent The parent node
+                 * @param {Node} node The child node removed
+                 */
+                "remove",
+
+                /**
+                 * @event move
+                 * Fires when a node is moved to a new location in the tree
+                 * @param {Tree} tree The owner tree
+                 * @param {Node} node The node moved
+                 * @param {Node} oldParent The old parent of this node
+                 * @param {Node} newParent The new parent of this node
+                 * @param {Number} index The index it was moved to
+                 */
+                "move",
+
+                /**
+                 * @event insert
+                 * Fires when a new child node is inserted in a node in this tree.
+                 * @param {Tree} tree The owner tree
+                 * @param {Node} parent The parent node
+                 * @param {Node} node The child node inserted
+                 * @param {Node} refNode The child node the node was inserted before
+                 */
+                "insert",
+
+                /**
+                 * @event beforeappend
+                 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
+                 * @param {Tree} tree The owner tree
+                 * @param {Node} parent The parent node
+                 * @param {Node} node The child node to be appended
+                 */
+                "beforeappend",
+
+                /**
+                 * @event beforeremove
+                 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
+                 * @param {Tree} tree The owner tree
+                 * @param {Node} parent The parent node
+                 * @param {Node} node The child node to be removed
+                 */
+                "beforeremove",
+
+                /**
+                 * @event beforemove
+                 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
+                 * @param {Tree} tree The owner tree
+                 * @param {Node} node The node being moved
+                 * @param {Node} oldParent The parent of the node
+                 * @param {Node} newParent The new parent the node is moving to
+                 * @param {Number} index The index it is being moved to
+                 */
+                "beforemove",
+
+                /**
+                 * @event beforeinsert
+                 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
+                 * @param {Tree} tree The owner tree
+                 * @param {Node} parent The parent node
+                 * @param {Node} node The child node to be inserted
+                 * @param {Node} refNode The child node the node is being inserted before
+                 */
+                "beforeinsert",
+
+                 /**
+                  * @event expand
+                  * Fires when this node is expanded.
+                  * @param {Node} this The expanding node
+                  */
+                 "expand",
+
+                 /**
+                  * @event collapse
+                  * Fires when this node is collapsed.
+                  * @param {Node} this The collapsing node
+                  */
+                 "collapse",
+
+                 /**
+                  * @event beforeexpand
+                  * Fires before this node is expanded.
+                  * @param {Node} this The expanding node
+                  */
+                 "beforeexpand",
+
+                 /**
+                  * @event beforecollapse
+                  * Fires before this node is collapsed.
+                  * @param {Node} this The collapsing node
+                  */
+                 "beforecollapse" ,
+
+                 /**
+                  * @event rootchange
+                  * Fires whenever the root node is changed in the tree.
+                  * @param {Ext.data.Model} root The new root
+                  */
+                 "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;
+    },
+    
+    /**
+     * Flattens all the nodes in the tree into an array.
+     * @private
+     * @return {Array} The flattened nodes.
+     */
+    flatten: function(){
+        var nodes = [],
+            hash = this.nodeHash,
+            key;
+            
+        for (key in hash) {
+            if (hash.hasOwnProperty(key)) {
+                nodes.push(hash[key]);
+            }
+        }
+        return nodes;
+    },
+    
+    /**
+     * Fired when a node is inserted into the root or one of it's children
+     * @private
+     * @param {Ext.data.NodeInterface} parent The parent node
+     * @param {Ext.data.NodeInterface} node The inserted node
+     */
+    onNodeInsert: function(parent, node) {
+        this.registerNode(node);
+    },
+    
+    /**
+     * Fired when a node is appended into the root or one of it's children
+     * @private
+     * @param {Ext.data.NodeInterface} parent The parent node
+     * @param {Ext.data.NodeInterface} node The appended node
+     */
+    onNodeAppend: function(parent, node) {
+        this.registerNode(node);
+    },
+    
+    /**
+     * Fired when a node is removed from the root or one of it's children
+     * @private
+     * @param {Ext.data.NodeInterface} parent The parent node
+     * @param {Ext.data.NodeInterface} node The removed node
+     */
+    onNodeRemove: function(parent, node) {
+        this.unregisterNode(node);
+    },
+
+    /**
+     * Gets a node in this tree by its id.
+     * @param {String} id
+     * @return {Ext.data.NodeInterface} The match node.
+     */
+    getNodeById : function(id) {
+        return this.nodeHash[id];
+    },
+
+    /**
+     * Registers a node with the tree
+     * @private
+     * @param {Ext.data.NodeInterface} The node to register
+     */
+    registerNode : function(node) {
+        this.nodeHash[node.getId() || node.internalId] = node;
+    },
+
+    /**
+     * Unregisters a node with the tree
+     * @private
+     * @param {Ext.data.NodeInterface} The node to unregister
+     */
+    unregisterNode : function(node) {
+        delete this.nodeHash[node.getId() || node.internalId];
+    },
+    
+    /**
+     * Sorts this tree
+     * @private
+     * @param {Function} sorterFn The function to use for sorting
+     * @param {Boolean} recursive True to perform recursive sorting
+     */
+    sort: function(sorterFn, recursive) {
+        this.getRootNode().sort(sorterFn, recursive);
+    },
+    
+     /**
+     * Filters this tree
+     * @private
+     * @param {Function} sorterFn The function to use for filtering
+     * @param {Boolean} recursive True to perform recursive filtering
+     */
+    filter: function(filters, recursive) {
+        this.getRootNode().filter(filters, recursive);
+    }
+});
+/**
+ * @class Ext.data.TreeStore
+ * @extends Ext.data.AbstractStore
+ * 
+ * The TreeStore is a store implementation that is backed by by an {@link Ext.data.Tree}.
+ * It provides convenience methods for loading nodes, as well as the ability to use
+ * the hierarchical tree structure combined with a store. This class is generally used
+ * in conjunction with {@link Ext.tree.Panel}. This class also relays many events from
+ * the Tree for convenience.
+ * 
+ * ## Using Models
+ * If no Model is specified, an implicit model will be created that implements {@link Ext.data.NodeInterface}.
+ * The standard Tree fields will also be copied onto the Model for maintaining their state.
+ * 
+ * ## Reading Nested Data
+ * For the tree to read nested data, the {@link Ext.data.Reader} must be configured with a root property,
+ * so the reader can find nested data for each node. If a root is not specified, it will default to
+ * 'children'.
+ */
+Ext.define('Ext.data.TreeStore', {
+    extend: 'Ext.data.AbstractStore',
+    alias: 'store.tree',
+    requires: ['Ext.data.Tree', 'Ext.data.NodeInterface', 'Ext.data.NodeStore'],
+
+    /**
+     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
+     * child nodes before loading.
+     */
+    clearOnLoad : true,
+
+    /**
+     * @cfg {String} nodeParam The name of the parameter sent to the server which contains
+     * the identifier of the node. Defaults to <tt>'node'</tt>.
+     */
+    nodeParam: 'node',
+
+    /**
+     * @cfg {String} defaultRootId
+     * The default root id. Defaults to 'root'
+     */
+    defaultRootId: 'root',
+    
+    /**
+     * @cfg {String} defaultRootProperty
+     * The root property to specify on the reader if one is not explicitly defined.
+     */
+    defaultRootProperty: 'children',
+
+    /**
+     * @cfg {Boolean} folderSort Set to true to automatically prepend a leaf sorter (defaults to <tt>undefined</tt>)
+     */
+    folderSort: false,
+    
+    constructor: function(config) {
+        var me = this, 
+            root,
+            fields;
+            
+        
+        config = Ext.apply({}, config);
+        
+        /**
+         * If we have no fields declare for the store, add some defaults.
+         * These will be ignored if a model is explicitly specified.
+         */
+        fields = config.fields || me.fields;
+        if (!fields) {
+            config.fields = [{name: 'text', type: 'string'}];
+        }
+
+        me.callParent([config]);
+        
+        // We create our data tree.
+        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, [
+            /**
+             * @event append
+             * Fires when a new child node is appended to a node in this store's tree.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The newly appended node
+             * @param {Number} index The index of the newly appended node
+             */
+            "append",
+            
+            /**
+             * @event remove
+             * Fires when a child node is removed from a node in this store's tree.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node removed
+             */
+            "remove",
+            
+            /**
+             * @event move
+             * Fires when a node is moved to a new location in the store's tree
+             * @param {Tree} tree The owner tree
+             * @param {Node} node The node moved
+             * @param {Node} oldParent The old parent of this node
+             * @param {Node} newParent The new parent of this node
+             * @param {Number} index The index it was moved to
+             */
+            "move",
+            
+            /**
+             * @event insert
+             * Fires when a new child node is inserted in a node in this store's tree.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node inserted
+             * @param {Node} refNode The child node the node was inserted before
+             */
+            "insert",
+            
+            /**
+             * @event beforeappend
+             * Fires before a new child is appended to a node in this store's tree, return false to cancel the append.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be appended
+             */
+            "beforeappend",
+            
+            /**
+             * @event beforeremove
+             * Fires before a child is removed from a node in this store's tree, return false to cancel the remove.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be removed
+             */
+            "beforeremove",
+            
+            /**
+             * @event beforemove
+             * Fires before a node is moved to a new location in the store's tree. Return false to cancel the move.
+             * @param {Tree} tree The owner tree
+             * @param {Node} node The node being moved
+             * @param {Node} oldParent The parent of the node
+             * @param {Node} newParent The new parent the node is moving to
+             * @param {Number} index The index it is being moved to
+             */
+            "beforemove",
+            
+            /**
+             * @event beforeinsert
+             * Fires before a new child is inserted in a node in this store's tree, return false to cancel the insert.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be inserted
+             * @param {Node} refNode The child node the node is being inserted before
+             */
+            "beforeinsert",
+             
+             /**
+              * @event expand
+              * Fires when this node is expanded.
+              * @param {Node} this The expanding node
+              */
+             "expand",
+             
+             /**
+              * @event collapse
+              * Fires when this node is collapsed.
+              * @param {Node} this The collapsing node
+              */
+             "collapse",
+             
+             /**
+              * @event beforeexpand
+              * Fires before this node is expanded.
+              * @param {Node} this The expanding node
+              */
+             "beforeexpand",
+             
+             /**
+              * @event beforecollapse
+              * Fires before this node is collapsed.
+              * @param {Node} this The collapsing node
+              */
+             "beforecollapse",
+
+             /**
+              * @event sort
+              * Fires when this TreeStore is sorted.
+              * @param {Node} node The node that is sorted.
+              */             
+             "sort",
+             
+             /**
+              * @event rootchange
+              * Fires whenever the root node is changed in the tree.
+              * @param {Ext.data.Model} root The new root
+              */
+             "rootchange"
+        ]);
+        
+        me.addEvents(
+            /**
+             * @event rootchange
+             * Fires when the root node on this TreeStore is changed.
+             * @param {Ext.data.TreeStore} store This TreeStore
+             * @param {Node} The new root node.
+             */
+            'rootchange'
+        );
+        
+        //<deprecated since=0.99>
+        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;
+        }
+        //</deprecated>
+    },
+    
+    // inherit docs
+    setProxy: function(proxy) {
+        var reader,
+            needsRoot;
+        
+        if (proxy instanceof Ext.data.proxy.Proxy) {
+            // proxy instance, check if a root was set
+            needsRoot = Ext.isEmpty(proxy.getReader().root);
+        } else if (Ext.isString(proxy)) {
+            // string type, means a reader can't be set
+            needsRoot = true;
+        } else {
+            // object, check if a reader and a root were specified.
+            reader = proxy.reader;
+            needsRoot = !(reader && !Ext.isEmpty(reader.root));
+        }
+        proxy = this.callParent(arguments);
+        if (needsRoot) {
+            reader = proxy.getReader();
+            reader.root = this.defaultRootProperty;
+            // force rebuild
+            reader.buildExtractors(true);
+        }
+    },
+    
+    // inherit docs
+    onBeforeSort: function() {
+        if (this.folderSort) {
+            this.sort({
+                property: 'leaf',
+                direction: 'ASC'
+            }, 'prepend', false);    
+        }
+    },
+    
+    /**
+     * Called before a node is expanded.
+     * @private
+     * @param {Ext.data.NodeInterface} node The node being expanded.
+     * @param {Function} callback The function to run after the expand finishes
+     * @param {Object} scope The scope in which to run the callback function
+     */
+    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]);
+                }
+            });            
+        }
+    },
+    
+    //inherit docs
+    getNewRecords: function() {
+        return Ext.Array.filter(this.tree.flatten(), this.filterNew);
+    },
+
+    //inherit docs
+    getUpdatedRecords: function() {
+        return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
+    },
+    
+    /**
+     * Called before a node is collapsed.
+     * @private
+     * @param {Ext.data.NodeInterface} node The node being collapsed.
+     * @param {Function} callback The function to run after the collapse finishes
+     * @param {Object} scope The scope in which to run the callback function
+     */
+    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];
+            }
+        }
+    },
+        
+    /**
+     * Sets the root node for this store
+     * @param {Ext.data.Model/Ext.data.NodeInterface} root
+     * @return {Ext.data.NodeInterface} The new root
+     */
+    setRootNode: function(root) {
+        var me = this;
+
+        root = root || {};        
+        if (!root.isNode) {
+            // create a default rootNode and create internal data struct.        
+            Ext.applyIf(root, {
+                id: me.defaultRootId,
+                text: 'Root',
+                allowDrag: false
+            });
+            root = Ext.ModelManager.create(root, me.model);
+        }
+        Ext.data.NodeInterface.decorate(root);
+
+        // Because we have decorated the model with new fields,
+        // we need to build new extactor functions on the reader.
+        me.getProxy().getReader().buildExtractors(true);
+        
+        // When we add the root to the tree, it will automaticaly get the NodeInterface
+        me.tree.setRootNode(root);
+        
+        // If the user has set expanded: true on the root, we want to call the expand function
+        if (!root.isLoaded() && root.isExpanded()) {
+            me.load({
+                node: root
+            });
+        }
+        
+        return root;
+    },
+        
+    /**
+     * Returns the root node for this tree.
+     * @return {Ext.data.NodeInterface}
+     */
+    getRootNode: function() {
+        return this.tree.getRootNode();
+    },
+
+    /**
+     * Returns the record node by id
+     * @return {Ext.data.NodeInterface}
+     */
+    getNodeById: function(id) {
+        return this.tree.getNodeById(id);
+    },
+
+    /**
+     * Loads the Store using its configured {@link #proxy}.
+     * @param {Object} options Optional config object. This is passed into the {@link Ext.data.Operation Operation}
+     * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function.
+     * The options can also contain a node, which indicates which node is to be loaded. If not specified, it will
+     * default to the root node.
+     */
+    load: function(options) {
+        options = options || {};
+        options.params = options.params || {};
+        
+        var me = this,
+            node = options.node || me.tree.getRootNode(),
+            root;
+            
+        // If there is not a node it means the user hasnt defined a rootnode yet. In this case lets just
+        // create one for them.
+        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]);
+    },
+        
+
+    /**
+     * Fills a node with a series of child records.
+     * @private
+     * @param {Ext.data.NodeInterface} node The node to fill
+     * @param {Array} records The records to add
+     */
+    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;
+    },
+
+    // inherit docs
+    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);
+        }
+        // deprecate read?
+        me.fireEvent('read', me, operation.node, records, successful);
+        me.fireEvent('load', me, operation.node, records, successful);
+        //this is a callback that would have been passed to the 'read' function and is optional
+        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
+    },
+    
+    /**
+     * Create any new records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of new records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was successful
+     */
+    onCreateRecords: function(records, operation, success) {
+        if (success) {
+            var i = 0,
+                length = records.length,
+                originalRecords = operation.records,
+                parentNode,
+                record,
+                original,
+                index;
+
+            /**
+             * Loop over each record returned from the server. Assume they are
+             * returned in order of how they were sent. If we find a matching
+             * record, replace it with the newly created one.
+             */
+            for (; i < length; ++i) {
+                record = records[i];
+                original = originalRecords[i];
+                if (original) {
+                    parentNode = original.parentNode;
+                    if (parentNode) {
+                        // prevent being added to the removed cache
+                        original.isReplace = true;
+                        parentNode.replaceChild(record, original);
+                        delete original.isReplace;
+                    }
+                    record.phantom = false;
+                }
+            }
+        }
+    },
+
+    /**
+     * Update any records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of updated records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was successful
+     */
+    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) {
+                    // prevent being added to the removed cache
+                    original.isReplace = true;
+                    parentNode.replaceChild(record, original);
+                    original.isReplace = false;
+                }
+            }
+        }
+    },
+
+    /**
+     * Remove any records when a write is returned from the server.
+     * @private
+     * @param {Array} records The array of removed records
+     * @param {Ext.data.Operation} operation The operation that just completed
+     * @param {Boolean} success True if the operation was successful
+     */
+    onDestroyRecords: function(records, operation, success){
+        if (success) {
+            this.removed = [];
+        }
+    },
+
+    // inherit docs
+    removeAll: function() {
+        this.getRootNode().destroy();
+        this.fireEvent('clear', this);
+    },
+
+    // inherit docs
+    doSort: function(sorterFn) {
+        var me = this;
+        if (me.remoteSort) {
+            //the load function will pick up the new sorters and request the sorted data from the proxy
+            me.load();
+        } else {
+            me.tree.sort(sorterFn, true);
+            me.fireEvent('datachanged', me);
+        }   
+        me.fireEvent('sort', me);
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.XmlStore
+ * @extends Ext.data.Store
+ * @private
+ * @ignore
+ * <p>Small helper class to make creating {@link Ext.data.Store}s from XML data easier.
+ * A XmlStore will be automatically configured with a {@link Ext.data.reader.Xml}.</p>
+ * <p>A store configuration would be something like:<pre><code>
+var store = new Ext.data.XmlStore({
+    // store configs
+    autoDestroy: true,
+    storeId: 'myStore',
+    url: 'sheldon.xml', // automatically configures a HttpProxy
+    // reader configs
+    record: 'Item', // records will have an "Item" tag
+    idPath: 'ASIN',
+    totalRecords: '@TotalResults'
+    fields: [
+        // set up the fields mapping into the xml doc
+        // The first needs mapping, the others are very basic
+        {name: 'Author', mapping: 'ItemAttributes > Author'},
+        'Title', 'Manufacturer', 'ProductGroup'
+    ]
+});
+ * </code></pre></p>
+ * <p>This store is configured to consume a returned object of the form:<pre><code>
+&#60?xml version="1.0" encoding="UTF-8"?>
+&#60ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2009-05-15">
+    &#60Items>
+        &#60Request>
+            &#60IsValid>True&#60/IsValid>
+            &#60ItemSearchRequest>
+                &#60Author>Sidney Sheldon&#60/Author>
+                &#60SearchIndex>Books&#60/SearchIndex>
+            &#60/ItemSearchRequest>
+        &#60/Request>
+        &#60TotalResults>203&#60/TotalResults>
+        &#60TotalPages>21&#60/TotalPages>
+        &#60Item>
+            &#60ASIN>0446355453&#60/ASIN>
+            &#60DetailPageURL>
+                http://www.amazon.com/
+            &#60/DetailPageURL>
+            &#60ItemAttributes>
+                &#60Author>Sidney Sheldon&#60/Author>
+                &#60Manufacturer>Warner Books&#60/Manufacturer>
+                &#60ProductGroup>Book&#60/ProductGroup>
+                &#60Title>Master of the Game&#60/Title>
+            &#60/ItemAttributes>
+        &#60/Item>
+    &#60/Items>
+&#60/ItemSearchResponse>
+ * </code></pre>
+ * An object literal of this form could also be used as the {@link #data} config option.</p>
+ * <p><b>Note:</b> Although not listed here, this class accepts all of the configuration options of
+ * <b>{@link Ext.data.reader.Xml XmlReader}</b>.</p>
+ * @constructor
+ * @param {Object} config
+ * @xtype xmlstore
+ */
+Ext.define('Ext.data.XmlStore', {
+    extend: 'Ext.data.Store',
+    alternateClassName: 'Ext.data.XmlStore',
+    alias: 'store.xml',
+
+    /**
+     * @cfg {Ext.data.DataReader} reader @hide
+     */
+    constructor: function(config){
+        config = config || {};
+        config = config || {};
+
+        Ext.applyIf(config, {
+            proxy: {
+                type: 'ajax',
+                reader: 'xml',
+                writer: 'xml'
+            }
+        });
+
+        this.callParent([config]);
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.Client
+ * @extends Ext.data.proxy.Proxy
+ * 
+ * <p>Base class for any client-side storage. Used as a superclass for {@link Ext.data.proxy.Memory Memory} and 
+ * {@link Ext.data.proxy.WebStorage Web Storage} proxies. Do not use directly, use one of the subclasses instead.</p>
+ */
+Ext.define('Ext.data.proxy.Client', {
+    extend: 'Ext.data.proxy.Proxy',
+    alternateClassName: 'Ext.data.ClientProxy',
+    
+    /**
+     * Abstract function that must be implemented by each ClientProxy subclass. This should purge all record data
+     * from the client side storage, as well as removing any supporting data (such as lists of record IDs)
+     */
+    clear: function() {
+        //<debug>
+        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.");
+        //</debug>
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.JsonP
+ * @extends Ext.data.proxy.Server
+ *
+ * <p>JsonPProxy is useful when you need to load data from a domain other than the one your application is running
+ * on. If your application is running on http://domainA.com it cannot use {@link Ext.data.proxy.Ajax Ajax} to load its
+ * data from http://domainB.com because cross-domain ajax requests are prohibited by the browser.</p>
+ *
+ * <p>We can get around this using a JsonPProxy. JsonPProxy injects a &lt;script&gt; tag into the DOM whenever
+ * an AJAX request would usually be made. Let's say we want to load data from http://domainB.com/users - the script tag
+ * that would be injected might look like this:</p>
+ *
+<pre><code>
+&lt;script src="http://domainB.com/users?callback=someCallback"&gt;&lt;/script&gt;
+</code></pre>
+ *
+ * <p>When we inject the tag above, the browser makes a request to that url and includes the response as if it was any
+ * other type of JavaScript include. By passing a callback in the url above, we're telling domainB's server that we
+ * want to be notified when the result comes in and that it should call our callback function with the data it sends
+ * back. So long as the server formats the response to look like this, everything will work:</p>
+ *
+<pre><code>
+someCallback({
+    users: [
+        {
+            id: 1,
+            name: "Ed Spencer",
+            email: "ed@sencha.com"
+        }
+    ]
+});
+</code></pre>
+ *
+ * <p>As soon as the script finishes loading, the 'someCallback' function that we passed in the url is called with the
+ * JSON object that the server returned.</p>
+ *
+ * <p>JsonPProxy takes care of all of this automatically. It formats the url you pass, adding the callback
+ * parameter automatically. It even creates a temporary callback function, waits for it to be called and then puts
+ * the data into the Proxy making it look just like you loaded it through a normal {@link Ext.data.proxy.Ajax AjaxProxy}.
+ * Here's how we might set that up:</p>
+ *
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'email']
+});
+
+var store = new Ext.data.Store({
+    model: 'User',
+    proxy: {
+        type: 'jsonp',
+        url : 'http://domainB.com/users'
+    }
+});
+
+store.load();
+</code></pre>
+ *
+ * <p>That's all we need to do - JsonPProxy takes care of the rest. In this case the Proxy will have injected a
+ * script tag like this:
+ *
+<pre><code>
+&lt;script src="http://domainB.com/users?callback=stcCallback001" id="stcScript001"&gt;&lt;/script&gt;
+</code></pre>
+ *
+ * <p><u>Customization</u></p>
+ *
+ * <p>Most parts of this script tag can be customized using the {@link #callbackParam}, {@link #callbackPrefix} and
+ * {@link #scriptIdPrefix} configurations. For example:
+ *
+<pre><code>
+var store = new Ext.data.Store({
+    model: 'User',
+    proxy: {
+        type: 'jsonp',
+        url : 'http://domainB.com/users',
+        callbackParam: 'theCallbackFunction',
+        callbackPrefix: 'ABC',
+        scriptIdPrefix: 'injectedScript'
+    }
+});
+
+store.load();
+</code></pre>
+ *
+ * <p>Would inject a script tag like this:</p>
+ *
+<pre><code>
+&lt;script src="http://domainB.com/users?theCallbackFunction=ABC001" id="injectedScript001"&gt;&lt;/script&gt;
+</code></pre>
+ *
+ * <p><u>Implementing on the server side</u></p>
+ *
+ * <p>The remote server side needs to be configured to return data in this format. Here are suggestions for how you
+ * might achieve this using Java, PHP and ASP.net:</p>
+ *
+ * <p>Java:</p>
+ *
+<pre><code>
+boolean jsonP = false;
+String cb = request.getParameter("callback");
+if (cb != null) {
+    jsonP = true;
+    response.setContentType("text/javascript");
+} else {
+    response.setContentType("application/x-json");
+}
+Writer out = response.getWriter();
+if (jsonP) {
+    out.write(cb + "(");
+}
+out.print(dataBlock.toJsonString());
+if (jsonP) {
+    out.write(");");
+}
+</code></pre>
+ *
+ * <p>PHP:</p>
+ *
+<pre><code>
+$callback = $_REQUEST['callback'];
+
+// Create the output object.
+$output = array('a' => 'Apple', 'b' => 'Banana');
+
+//start output
+if ($callback) {
+    header('Content-Type: text/javascript');
+    echo $callback . '(' . json_encode($output) . ');';
+} else {
+    header('Content-Type: application/x-json');
+    echo json_encode($output);
+}
+</code></pre>
+ *
+ * <p>ASP.net:</p>
+ *
+<pre><code>
+String jsonString = "{success: true}";
+String cb = Request.Params.Get("callback");
+String responseString = "";
+if (!String.IsNullOrEmpty(cb)) {
+    responseString = cb + "(" + jsonString + ")";
+} else {
+    responseString = jsonString;
+}
+Response.Write(responseString);
+</code></pre>
+ *
+ */
+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',
+
+    /**
+     * @cfg {String} callbackKey (Optional) See {@link Ext.data.JsonP#callbackKey}.
+     */
+    callbackKey : 'callback',
+
+    /**
+     * @cfg {String} recordParam
+     * The param name to use when passing records to the server (e.g. 'records=someEncodedRecordString').
+     * Defaults to 'records'
+     */
+    recordParam: 'records',
+
+    /**
+     * @cfg {Boolean} autoAppendParams True to automatically append the request's params to the generated url. Defaults to true
+     */
+    autoAppendParams: true,
+
+    constructor: function(){
+        this.addEvents(
+            /**
+             * @event exception
+             * Fires when the server returns an exception
+             * @param {Ext.data.proxy.Proxy} this
+             * @param {Ext.data.Request} request The request that was sent
+             * @param {Ext.data.Operation} operation The operation that triggered the request
+             */
+            'exception'
+        );
+        this.callParent(arguments);
+    },
+
+    /**
+     * @private
+     * Performs the read request to the remote domain. JsonPProxy does not actually create an Ajax request,
+     * instead we write out a <script> tag based on the configuration of the internal Ext.data.Request object
+     * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object to execute
+     * @param {Function} callback A callback function to execute when the Operation has been completed
+     * @param {Object} scope The scope to execute the callback in
+     */
+    doRequest: function(operation, callback, scope) {
+        //generate the unique IDs for this request
+        var me      = this,
+            writer  = me.getWriter(),
+            request = me.buildRequest(operation),
+            params = request.params;
+
+        if (operation.allowWrite()) {
+            request = writer.write(request);
+        }
+
+        //apply JsonPProxy-specific attributes to the Request
+        Ext.apply(request, {
+            callbackKey: me.callbackKey,
+            timeout: me.timeout,
+            scope: me,
+            disableCaching: false, // handled by the proxy
+            callback: me.createRequestCallback(request, operation, callback, scope)
+        });
+        
+        // prevent doubling up
+        if (me.autoAppendParams) {
+            request.params = {};
+        }
+        
+        request.jsonp = Ext.data.JsonP.request(request);
+        // restore on the request
+        request.params = params;
+        operation.setStarted();
+        me.lastRequest = request;
+
+        return request;
+    },
+
+    /**
+     * @private
+     * Creates and returns the function that is called when the request has completed. The returned function
+     * should accept a Response object, which contains the response to be read by the configured Reader.
+     * The third argument is the callback that should be called after the request has been completed and the Reader has decoded
+     * the response. This callback will typically be the callback passed by a store, e.g. in proxy.read(operation, theCallback, scope)
+     * theCallback refers to the callback argument received by this function.
+     * See {@link #doRequest} for details.
+     * @param {Ext.data.Request} request The Request object
+     * @param {Ext.data.Operation} operation The Operation being executed
+     * @param {Function} callback The callback function to be called when the request completes. This is usually the callback
+     * passed to doRequest
+     * @param {Object} scope The scope in which to execute the callback function
+     * @return {Function} The callback function
+     */
+    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);
+        };
+    },
+    
+    // inherit docs
+    setException: function(operation, response) {
+        operation.setException(operation.request.jsonp.errorType);
+    },
+
+
+    /**
+     * Generates a url based on a given Ext.data.Request object. Adds the params and callback function name to the url
+     * @param {Ext.data.Request} request The request object
+     * @return {String} The url
+     */
+    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);
+                }
+            }
+        }
+
+        //if there are any records present, append them to the url also
+        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;
+    },
+
+    //inherit docs
+    destroy: function() {
+        this.abort();
+        this.callParent();
+    },
+
+    /**
+     * Aborts the current server request if one is currently running
+     */
+    abort: function() {
+        var lastRequest = this.lastRequest;
+        if (lastRequest) {
+            Ext.data.JsonP.abort(lastRequest.jsonp);
+        }
+    },
+
+    /**
+     * Encodes an array of records into a string suitable to be appended to the script src url. This is broken
+     * out into its own function so that it can be easily overridden.
+     * @param {Array} records The records array
+     * @return {String} The encoded records string
+     */
+    encodeRecords: function(records) {
+        var encoded = "",
+            i = 0,
+            len = records.length;
+
+        for (; i < len; i++) {
+            encoded += Ext.Object.toQueryString(records[i].data);
+        }
+
+        return encoded;
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.WebStorage
+ * @extends Ext.data.proxy.Client
+ * 
+ * <p>WebStorageProxy is simply a superclass for the {@link Ext.data.proxy.LocalStorage localStorage} and 
+ * {@link Ext.data.proxy.SessionStorage sessionStorage} proxies. It uses the new HTML5 key/value client-side storage 
+ * objects to save {@link Ext.data.Model model instances} for offline use.</p>
+ * 
+ * @constructor
+ * Creates the proxy, throws an error if local storage is not supported in the current browser
+ * @param {Object} config Optional config object
+ */
+Ext.define('Ext.data.proxy.WebStorage', {
+    extend: 'Ext.data.proxy.Client',
+    alternateClassName: 'Ext.data.WebStorageProxy',
+    
+    /**
+     * @cfg {String} id The unique ID used as the key in which all record data are stored in the local storage object
+     */
+    id: undefined,
+
+    /**
+     * @ignore
+     */
+    constructor: function(config) {
+        this.callParent(arguments);
+        
+        /**
+         * Cached map of records already retrieved by this Proxy - ensures that the same instance is always retrieved
+         * @property cache
+         * @type Object
+         */
+        this.cache = {};
+
+        //<debug>
+        if (this.getStorageObject() === undefined) {
+            Ext.Error.raise("Local Storage is not supported in this browser, please use another type of data proxy");
+        }
+        //</debug>
+
+        //if an id is not given, try to use the store's id instead
+        this.id = this.id || (this.store ? this.store.storeId : undefined);
+
+        //<debug>
+        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");
+        }
+        //</debug>
+
+        this.initialize();
+    },
+
+    //inherit docs
+    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);
+        }
+    },
+
+    //inherit docs
+    read: function(operation, callback, scope) {
+        //TODO: respect sorters, filters, start and limit options on the Operation
+
+        var records = [],
+            ids     = this.getIds(),
+            length  = ids.length,
+            i, recordData, record;
+        
+        //read a single 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);
+        }
+    },
+
+    //inherit docs
+    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);
+            
+            //we need to update the set of ids here because it's possible that a non-phantom record was added
+            //to this proxy - in which case the record's id would never have been added via the normal 'create' call
+            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);
+        }
+    },
+
+    //inherit
+    destroy: function(operation, callback, scope) {
+        var records = operation.records,
+            length  = records.length,
+            ids     = this.getIds(),
+
+            //newIds is a copy of ids, from which we remove the destroyed records
+            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);
+        }
+    },
+
+    /**
+     * @private
+     * Fetches a model instance from the Proxy by ID. Runs each field's decode function (if present) to decode the data
+     * @param {String} id The record's unique ID
+     * @return {Ext.data.Model} The model instance
+     */
+    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];
+    },
+
+    /**
+     * Saves the given record in the Proxy. Runs each field's encode function (if present) to encode the data
+     * @param {Ext.data.Model} record The model instance
+     * @param {String} id The id to save the record under (defaults to the value of the record's getId() function)
+     */
+    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);
+        
+        //keep the cache up to date
+        me.cache[id] = record;
+        
+        //iPad bug requires that we remove the item before setting it
+        obj.removeItem(key);
+        obj.setItem(key, Ext.encode(data));
+    },
+
+    /**
+     * @private
+     * Physically removes a given record from the local storage. Used internally by {@link #destroy}, which you should
+     * use instead because it updates the list of currently-stored record ids
+     * @param {String|Number|Ext.data.Model} id The id of the record to remove, or an Ext.data.Model instance
+     */
+    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));
+    },
+
+    /**
+     * @private
+     * Given the id of a record, returns a unique string based on that id and the id of this proxy. This is used when
+     * storing data in the local storage object and should prevent naming collisions.
+     * @param {String|Number|Ext.data.Model} id The record id, or a Model instance
+     * @return {String} The unique key for this record
+     */
+    getRecordKey: function(id) {
+        if (id.isModel) {
+            id = id.getId();
+        }
+
+        return Ext.String.format("{0}-{1}", this.id, id);
+    },
+
+    /**
+     * @private
+     * Returns the unique key used to store the current record counter for this proxy. This is used internally when
+     * realizing models (creating them when they used to be phantoms), in order to give each model instance a unique id.
+     * @return {String} The counter key
+     */
+    getRecordCounterKey: function() {
+        return Ext.String.format("{0}-counter", this.id);
+    },
+
+    /**
+     * @private
+     * Returns the array of record IDs stored in this Proxy
+     * @return {Array} The record IDs. Each is cast as a Number
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * Saves the array of ids representing the set of all records in the Proxy
+     * @param {Array} ids The ids to set
+     */
+    setIds: function(ids) {
+        var obj = this.getStorageObject(),
+            str = ids.join(",");
+        
+        obj.removeItem(this.id);
+        
+        if (!Ext.isEmpty(str)) {
+            obj.setItem(this.id, str);
+        }
+    },
+
+    /**
+     * @private
+     * Returns the next numerical ID that can be used when realizing a model instance (see getRecordCounterKey). Increments
+     * the counter.
+     * @return {Number} The id
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * Sets up the Proxy by claiming the key in the storage object that corresponds to the unique id of this Proxy. Called
+     * automatically by the constructor, this should not need to be called again unless {@link #clear} has been called.
+     */
+    initialize: function() {
+        var storageObject = this.getStorageObject();
+        storageObject.setItem(this.id, storageObject.getItem(this.id) || "");
+    },
+
+    /**
+     * Destroys all records stored in the proxy and removes all keys and values used to support the proxy from the storage object
+     */
+    clear: function() {
+        var obj = this.getStorageObject(),
+            ids = this.getIds(),
+            len = ids.length,
+            i;
+
+        //remove all the records
+        for (i = 0; i < len; i++) {
+            this.removeRecord(ids[i]);
+        }
+
+        //remove the supporting objects
+        obj.removeItem(this.getRecordCounterKey());
+        obj.removeItem(this.id);
+    },
+
+    /**
+     * @private
+     * Abstract function which should return the storage object that data will be saved to. This must be implemented
+     * in each subclass.
+     * @return {Object} The storage object
+     */
+    getStorageObject: function() {
+        //<debug>
+        Ext.Error.raise("The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass");
+        //</debug>
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.LocalStorage
+ * @extends Ext.data.proxy.WebStorage
+ * 
+ * <p>The LocalStorageProxy uses the new HTML5 localStorage API to save {@link Ext.data.Model Model} data locally on
+ * the client browser. HTML5 localStorage is a key-value store (e.g. cannot save complex objects like JSON), so
+ * LocalStorageProxy automatically serializes and deserializes data when saving and retrieving it.</p>
+ * 
+ * <p>localStorage is extremely useful for saving user-specific information without needing to build server-side 
+ * infrastructure to support it. Let's imagine we're writing a Twitter search application and want to save the user's
+ * searches locally so they can easily perform a saved search again later. We'd start by creating a Search model:</p>
+ * 
+<pre><code>
+Ext.define('Search', {
+    fields: ['id', 'query'],
+    extend: 'Ext.data.Model',
+    proxy: {
+        type: 'localstorage',
+        id  : 'twitter-Searches'
+    }
+});
+</code></pre>
+ * 
+ * <p>Our Search model contains just two fields - id and query - plus a Proxy definition. The only configuration we
+ * need to pass to the LocalStorage proxy is an {@link #id}. This is important as it separates the Model data in this
+ * Proxy from all others. The localStorage API puts all data into a single shared namespace, so by setting an id we
+ * enable LocalStorageProxy to manage the saved Search data.</p>
+ * 
+ * <p>Saving our data into localStorage is easy and would usually be done with a {@link Ext.data.Store Store}:</p>
+ * 
+<pre><code>
+//our Store automatically picks up the LocalStorageProxy defined on the Search model
+var store = new Ext.data.Store({
+    model: "Search"
+});
+
+//loads any existing Search data from localStorage
+store.load();
+
+//now add some Searches
+store.add({query: 'Sencha Touch'});
+store.add({query: 'Ext JS'});
+
+//finally, save our Search data to localStorage
+store.sync();
+</code></pre>
+ * 
+ * <p>The LocalStorageProxy automatically gives our new Searches an id when we call store.sync(). It encodes the Model
+ * data and places it into localStorage. We can also save directly to localStorage, bypassing the Store altogether:</p>
+ * 
+<pre><code>
+var search = Ext.ModelManager.create({query: 'Sencha Animator'}, 'Search');
+
+//uses the configured LocalStorageProxy to save the new Search to localStorage
+search.save();
+</code></pre>
+ * 
+ * <p><u>Limitations</u></p>
+ * 
+ * <p>If this proxy is used in a browser where local storage is not supported, the constructor will throw an error.
+ * A local storage proxy requires a unique ID which is used as a key in which all record data are stored in the
+ * local storage object.</p>
+ * 
+ * <p>It's important to supply this unique ID as it cannot be reliably determined otherwise. If no id is provided
+ * but the attached store has a storeId, the storeId will be used. If neither option is presented the proxy will
+ * throw an error.</p>
+ */
+Ext.define('Ext.data.proxy.LocalStorage', {
+    extend: 'Ext.data.proxy.WebStorage',
+    alias: 'proxy.localstorage',
+    alternateClassName: 'Ext.data.LocalStorageProxy',
+    
+    //inherit docs
+    getStorageObject: function() {
+        return window.localStorage;
+    }
+});
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.Memory
+ * @extends Ext.data.proxy.Client
+ *
+ * <p>In-memory proxy. This proxy simply uses a local variable for data storage/retrieval, so its contents are lost on
+ * every page refresh.</p>
+ *
+ * <p>Usually this Proxy isn't used directly, serving instead as a helper to a {@link Ext.data.Store Store} where a
+ * reader is required to load data. For example, say we have a Store for a User model and have some inline data we want
+ * to load, but this data isn't in quite the right format: we can use a MemoryProxy with a JsonReader to read it into
+ * our Store:</p>
+ *
+<pre><code>
+//this is the model we will be using in the store
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: [
+        {name: 'id',    type: 'int'},
+        {name: 'name',  type: 'string'},
+        {name: 'phone', type: 'string', mapping: 'phoneNumber'}
+    ]
+});
+
+//this data does not line up to our model fields - the phone field is called phoneNumber
+var data = {
+    users: [
+        {
+            id: 1,
+            name: 'Ed Spencer',
+            phoneNumber: '555 1234'
+        },
+        {
+            id: 2,
+            name: 'Abe Elias',
+            phoneNumber: '666 1234'
+        }
+    ]
+};
+
+//note how we set the 'root' in the reader to match the data structure above
+var store = new Ext.data.Store({
+    autoLoad: true,
+    model: 'User',
+    data : data,
+    proxy: {
+        type: 'memory',
+        reader: {
+            type: 'json',
+            root: 'users'
+        }
+    }
+});
+</code></pre>
+ */
+Ext.define('Ext.data.proxy.Memory', {
+    extend: 'Ext.data.proxy.Client',
+    alias: 'proxy.memory',
+    alternateClassName: 'Ext.data.MemoryProxy',
+
+    /**
+     * @cfg {Array} data Optional array of Records to load into the Proxy
+     */
+
+    constructor: function(config) {
+        this.callParent([config]);
+
+        //ensures that the reader has been instantiated properly
+        this.setReader(this.reader);
+    },
+
+    /**
+     * Reads data from the configured {@link #data} object. Uses the Proxy's {@link #reader}, if present
+     * @param {Ext.data.Operation} operation The read Operation
+     * @param {Function} callback The callback to call when reading has completed
+     * @param {Object} scope The scope to call the callback function in
+     */
+    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
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.Rest
+ * @extends Ext.data.proxy.Ajax
+ * 
+ * <p>RestProxy is a specialization of the {@link Ext.data.proxy.Ajax AjaxProxy} which simply maps the four actions 
+ * (create, read, update and destroy) to RESTful HTTP verbs. For example, let's set up a {@link Ext.data.Model Model}
+ * with an inline RestProxy</p>
+ * 
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'email'],
+
+    proxy: {
+        type: 'rest',
+        url : '/users'
+    }
+});
+</code></pre>
+ * 
+ * <p>Now we can create a new User instance and save it via the RestProxy. Doing this will cause the Proxy to send a
+ * POST request to '/users':
+ * 
+<pre><code>
+var user = Ext.ModelManager.create({name: 'Ed Spencer', email: 'ed@sencha.com'}, 'User');
+
+user.save(); //POST /users
+</code></pre>
+ * 
+ * <p>Let's expand this a little and provide a callback for the {@link Ext.data.Model#save} call to update the Model
+ * once it has been created. We'll assume the creation went successfully and that the server gave this user an ID of 
+ * 123:</p>
+ * 
+<pre><code>
+user.save({
+    success: function(user) {
+        user.set('name', 'Khan Noonien Singh');
+
+        user.save(); //PUT /users/123
+    }
+});
+</code></pre>
+ * 
+ * <p>Now that we're no longer creating a new Model instance, the request method is changed to an HTTP PUT, targeting
+ * the relevant url for that user. Now let's delete this user, which will use the DELETE method:</p>
+ * 
+<pre><code>
+    user.destroy(); //DELETE /users/123
+</code></pre>
+ * 
+ * <p>Finally, when we perform a load of a Model or Store, RestProxy will use the GET method:</p>
+ * 
+<pre><code>
+//1. Load via Store
+
+//the Store automatically picks up the Proxy from the User model
+var store = new Ext.data.Store({
+    model: 'User'
+});
+
+store.load(); //GET /users
+
+//2. Load directly from the Model
+
+//GET /users/123
+Ext.ModelManager.getModel('User').load(123, {
+    success: function(user) {
+        console.log(user.getId()); //outputs 123
+    }
+});
+</code></pre>
+ * 
+ * <p><u>Url generation</u></p>
+ * 
+ * <p>RestProxy is able to automatically generate the urls above based on two configuration options - {@link #appendId}
+ * and {@link #format}. If appendId is true (it is by default) then RestProxy will automatically append the ID of the 
+ * Model instance in question to the configured url, resulting in the '/users/123' that we saw above.</p>
+ * 
+ * <p>If the request is not for a specific Model instance (e.g. loading a Store), the url is not appended with an id. 
+ * RestProxy will automatically insert a '/' before the ID if one is not already present.</p>
+ * 
+<pre><code>
+new Ext.data.proxy.Rest({
+    url: '/users',
+    appendId: true //default
+});
+
+// Collection url: /users
+// Instance url  : /users/123
+</code></pre>
+ * 
+ * <p>RestProxy can also optionally append a format string to the end of any generated url:</p>
+ * 
+<pre><code>
+new Ext.data.proxy.Rest({
+    url: '/users',
+    format: 'json'
+});
+
+// Collection url: /users.json
+// Instance url  : /users/123.json
+</code></pre>
+ * 
+ * <p>If further customization is needed, simply implement the {@link #buildUrl} method and add your custom generated
+ * url onto the {@link Ext.data.Request Request} object that is passed to buildUrl. See 
+ * <a href="source/RestProxy.html#method-Ext.data.proxy.Rest-buildUrl">RestProxy's implementation</a> for an example of
+ * how to achieve this.</p>
+ * 
+ * <p>Note that RestProxy inherits from {@link Ext.data.proxy.Ajax AjaxProxy}, which already injects all of the sorter,
+ * filter, group and paging options into the generated url. See the {@link Ext.data.proxy.Ajax AjaxProxy docs} for more
+ * details.</p>
+ */
+Ext.define('Ext.data.proxy.Rest', {
+    extend: 'Ext.data.proxy.Ajax',
+    alternateClassName: 'Ext.data.RestProxy',
+    alias : 'proxy.rest',
+    
+    /**
+     * @cfg {Boolean} appendId True to automatically append the ID of a Model instance when performing a request based
+     * on that single instance. See RestProxy intro docs for more details. Defaults to true.
+     */
+    appendId: true,
+    
+    /**
+     * @cfg {String} format Optional data format to send to the server when making any request (e.g. 'json'). See the
+     * RestProxy intro docs for full details. Defaults to undefined.
+     */
+    
+    /**
+     * @cfg {Boolean} batchActions True to batch actions of a particular type when synchronizing the store.
+     * Defaults to <tt>false</tt>.
+     */
+    batchActions: false,
+    
+    /**
+     * Specialized version of buildUrl that incorporates the {@link #appendId} and {@link #format} options into the
+     * generated url. Override this to provide further customizations, but remember to call the superclass buildUrl
+     * so that additional parameters like the cache buster string are appended
+     */
+    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, {
+        /**
+         * Mapping of action name to HTTP request method. These default to RESTful conventions for the 'create', 'read',
+         * 'update' and 'destroy' actions (which map to 'POST', 'GET', 'PUT' and 'DELETE' respectively). This object should
+         * not be changed except globally via {@link Ext#override Ext.override} - the {@link #getMethod} function can be overridden instead.
+         * @property actionMethods
+         * @type Object
+         */
+        actionMethods: {
+            create : 'POST',
+            read   : 'GET',
+            update : 'PUT',
+            destroy: 'DELETE'
+        }
+    });
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.proxy.SessionStorage
+ * @extends Ext.data.proxy.WebStorage
+ * 
+ * <p>Proxy which uses HTML5 session storage as its data storage/retrieval mechanism.
+ * If this proxy is used in a browser where session storage is not supported, the constructor will throw an error.
+ * A session storage proxy requires a unique ID which is used as a key in which all record data are stored in the
+ * session storage object.</p>
+ * 
+ * <p>It's important to supply this unique ID as it cannot be reliably determined otherwise. If no id is provided
+ * but the attached store has a storeId, the storeId will be used. If neither option is presented the proxy will
+ * throw an error.</p>
+ * 
+ * <p>Proxies are almost always used with a {@link Ext.data.Store store}:<p>
+ * 
+<pre><code>
+new Ext.data.Store({
+    proxy: {
+        type: 'sessionstorage',
+        id  : 'myProxyKey'
+    }
+});
+</code></pre>
+ * 
+ * <p>Alternatively you can instantiate the Proxy directly:</p>
+ * 
+<pre><code>
+new Ext.data.proxy.SessionStorage({
+    id  : 'myOtherProxyKey'
+});
+ </code></pre>
+ * 
+ * <p>Note that session storage is different to local storage (see {@link Ext.data.proxy.LocalStorage}) - if a browser
+ * session is ended (e.g. by closing the browser) then all data in a SessionStorageProxy are lost. Browser restarts
+ * don't affect the {@link Ext.data.proxy.LocalStorage} - the data are preserved.</p>
+ */
+Ext.define('Ext.data.proxy.SessionStorage', {
+    extend: 'Ext.data.proxy.WebStorage',
+    alias: 'proxy.sessionstorage',
+    alternateClassName: 'Ext.data.SessionStorageProxy',
+    
+    //inherit docs
+    getStorageObject: function() {
+        return window.sessionStorage;
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.reader.Array
+ * @extends Ext.data.reader.Json
+ * 
+ * <p>Data reader class to create an Array of {@link Ext.data.Model} objects from an Array.
+ * Each element of that Array represents a row of data fields. The
+ * fields are pulled into a Record object using as a subscript, the <code>mapping</code> property
+ * of the field definition if it exists, or the field's ordinal position in the definition.</p>
+ * 
+ * <p><u>Example code:</u></p>
+ * 
+<pre><code>
+Employee = Ext.define('Employee', {
+    extend: 'Ext.data.Model',
+    fields: [
+        'id',
+        {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
+        {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.        
+    ]
+});
+
+var myReader = new Ext.data.reader.Array({
+    model: 'Employee'
+}, Employee);
+</code></pre>
+ * 
+ * <p>This would consume an Array like this:</p>
+ * 
+<pre><code>
+[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
+</code></pre>
+ * 
+ * @constructor
+ * Create a new ArrayReader
+ * @param {Object} meta Metadata configuration options.
+ */
+Ext.define('Ext.data.reader.Array', {
+    extend: 'Ext.data.reader.Json',
+    alternateClassName: 'Ext.data.ArrayReader',
+    alias : 'reader.array',
+
+    /**
+     * @private
+     * Most of the work is done for us by JsonReader, but we need to overwrite the field accessors to just
+     * reference the correct position in the 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;
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.reader.Xml
+ * @extends Ext.data.reader.Reader
+ * 
+ * <p>The XML Reader is used by a Proxy to read a server response that is sent back in XML format. This usually
+ * happens as a result of loading a Store - for example we might create something like this:</p>
+ * 
+<pre><code>
+Ext.define('User', {
+    extend: 'Ext.data.Model',
+    fields: ['id', 'name', 'email']
+});
+
+var store = new Ext.data.Store({
+    model: 'User',
+    proxy: {
+        type: 'ajax',
+        url : 'users.xml',
+        reader: {
+            type: 'xml',
+            record: 'user'
+        }
+    }
+});
+</code></pre>
+ * 
+ * <p>The example above creates a 'User' model. Models are explained in the {@link Ext.data.Model Model} docs if you're
+ * not already familiar with them.</p>
+ * 
+ * <p>We created the simplest type of XML Reader possible by simply telling our {@link Ext.data.Store Store}'s 
+ * {@link Ext.data.proxy.Proxy Proxy} that we want a XML Reader. The Store automatically passes the configured model to the
+ * Store, so it is as if we passed this instead:
+ * 
+<pre><code>
+reader: {
+    type : 'xml',
+    model: 'User',
+    record: 'user'
+}
+</code></pre>
+ * 
+ * <p>The reader we set up is ready to read data from our server - at the moment it will accept a response like this:</p>
+ *
+<pre><code>
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;user&gt;
+    &lt;id&gt;1&lt;/id&gt;
+    &lt;name&gt;Ed Spencer&lt;/name&gt;
+    &lt;email&gt;ed@sencha.com&lt;/email&gt;
+&lt;/user&gt;
+&lt;user&gt;
+    &lt;id&gt;2&lt;/id&gt;
+    &lt;name&gt;Abe Elias&lt;/name&gt;
+    &lt;email&gt;abe@sencha.com&lt;/email&gt;
+&lt;/user&gt;
+</code></pre>
+ * 
+ * <p>The XML Reader uses the configured {@link #record} option to pull out the data for each record - in this case we
+ * set record to 'user', so each &lt;user&gt; above will be converted into a User model.</p>
+ * 
+ * <p><u>Reading other XML formats</u></p>
+ * 
+ * <p>If you already have your XML format defined and it doesn't look quite like what we have above, you can usually
+ * pass XmlReader a couple of configuration options to make it parse your format. For example, we can use the 
+ * {@link #root} configuration to parse data that comes back like this:</p>
+ * 
+<pre><code>
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;users&gt;
+    &lt;user&gt;
+        &lt;id&gt;1&lt;/id&gt;
+        &lt;name&gt;Ed Spencer&lt;/name&gt;
+        &lt;email&gt;ed@sencha.com&lt;/email&gt;
+    &lt;/user&gt;
+    &lt;user&gt;
+        &lt;id&gt;2&lt;/id&gt;
+        &lt;name&gt;Abe Elias&lt;/name&gt;
+        &lt;email&gt;abe@sencha.com&lt;/email&gt;
+    &lt;/user&gt;
+&lt;/users&gt;
+</code></pre>
+ * 
+ * <p>To parse this we just pass in a {@link #root} configuration that matches the 'users' above:</p>
+ * 
+<pre><code>
+reader: {
+    type  : 'xml',
+    root  : 'users',
+    record: 'user'
+}
+</code></pre>
+ * 
+ * <p>Note that XmlReader doesn't care whether your {@link #root} and {@link #record} elements are nested deep inside
+ * a larger structure, so a response like this will still work:
+ * 
+<pre><code>
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;deeply&gt;
+    &lt;nested&gt;
+        &lt;xml&gt;
+            &lt;users&gt;
+                &lt;user&gt;
+                    &lt;id&gt;1&lt;/id&gt;
+                    &lt;name&gt;Ed Spencer&lt;/name&gt;
+                    &lt;email&gt;ed@sencha.com&lt;/email&gt;
+                &lt;/user&gt;
+                &lt;user&gt;
+                    &lt;id&gt;2&lt;/id&gt;
+                    &lt;name&gt;Abe Elias&lt;/name&gt;
+                    &lt;email&gt;abe@sencha.com&lt;/email&gt;
+                &lt;/user&gt;
+            &lt;/users&gt;
+        &lt;/xml&gt;
+    &lt;/nested&gt;
+&lt;/deeply&gt;
+</code></pre>
+ * 
+ * <p><u>Response metadata</u></p>
+ * 
+ * <p>The server can return additional data in its response, such as the {@link #totalProperty total number of records} 
+ * and the {@link #successProperty success status of the response}. These are typically included in the XML response
+ * like this:</p>
+ * 
+<pre><code>
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;total&gt;100&lt;/total&gt;
+&lt;success&gt;true&lt;/success&gt;
+&lt;users&gt;
+    &lt;user&gt;
+        &lt;id&gt;1&lt;/id&gt;
+        &lt;name&gt;Ed Spencer&lt;/name&gt;
+        &lt;email&gt;ed@sencha.com&lt;/email&gt;
+    &lt;/user&gt;
+    &lt;user&gt;
+        &lt;id&gt;2&lt;/id&gt;
+        &lt;name&gt;Abe Elias&lt;/name&gt;
+        &lt;email&gt;abe@sencha.com&lt;/email&gt;
+    &lt;/user&gt;
+&lt;/users&gt;
+</code></pre>
+ * 
+ * <p>If these properties are present in the XML response they can be parsed out by the XmlReader and used by the
+ * Store that loaded it. We can set up the names of these properties by specifying a final pair of configuration 
+ * options:</p>
+ * 
+<pre><code>
+reader: {
+    type: 'xml',
+    root: 'users',
+    totalProperty  : 'total',
+    successProperty: 'success'
+}
+</code></pre>
+ * 
+ * <p>These final options are not necessary to make the Reader work, but can be useful when the server needs to report
+ * an error or if it needs to indicate that there is a lot of data available of which only a subset is currently being
+ * returned.</p>
+ * 
+ * <p><u>Response format</u></p>
+ * 
+ * <p><b>Note:</b> in order for the browser to parse a returned XML document, the Content-Type header in the HTTP 
+ * response must be set to "text/xml" or "application/xml". This is very important - the XmlReader will not
+ * work correctly otherwise.</p>
+ */
+Ext.define('Ext.data.reader.Xml', {
+    extend: 'Ext.data.reader.Reader',
+    alternateClassName: 'Ext.data.XmlReader',
+    alias : 'reader.xml',
+    
+    /**
+     * @private
+     * Creates a function to return some particular key of data from a response. The totalProperty and
+     * successProperty are treated as special cases for type casting, everything else is just a simple selector.
+     * @param {String} key
+     * @return {Function}
+     */
+
+    /**
+     * @cfg {String} record The DomQuery path to the repeated element which contains record information.
+     */
+
+    createAccessor: function() {
+        var selectValue = function(expr, root){
+            var node = Ext.DomQuery.selectNode(expr, root),
+                val;
+                
+            
+            
+        };
+
+        return 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;
+    },
+
+    //inherit docs
+    getResponseData: function(response) {
+        var xml = response.responseXML;
+
+        //<debug>
+        if (!xml) {
+            Ext.Error.raise({
+                response: response,
+                msg: 'XML data not found in the response'
+            });
+        }
+        //</debug>
+
+        return xml;
+    },
+
+    /**
+     * Normalizes the data object
+     * @param {Object} data The raw data object
+     * @return {Object} Returns the documentElement property of the data object if present, or the same object if not
+     */
+    getData: function(data) {
+        return data.documentElement || data;
+    },
+
+    /**
+     * @private
+     * Given an XML object, returns the Element that represents the root as configured by the Reader's meta data
+     * @param {Object} data The XML data object
+     * @return {Element} The root node element
+     */
+    getRoot: function(data) {
+        var nodeName = data.nodeName,
+            root     = this.root;
+        
+        if (!root || (nodeName && nodeName == root)) {
+            return data;
+        } else if (Ext.DomQuery.isXml(data)) {
+            // This fix ensures we have XML data
+            // Related to TreeStore calling getRoot with the root node, which isn't XML
+            // Probably should be resolved in TreeStore at some point
+            return Ext.DomQuery.selectNode(root, data);
+        }
+    },
+
+    /**
+     * @private
+     * We're just preparing the data for the superclass by pulling out the record nodes we want
+     * @param {Element} root The XML root node
+     * @return {Array} The records
+     */
+    extractData: function(root) {
+        var recordName = this.record;
+        
+        //<debug>
+        if (!recordName) {
+            Ext.Error.raise('Record is a required parameter');
+        }
+        //</debug>
+        
+        if (recordName != root.nodeName) {
+            root = Ext.DomQuery.select(recordName, root);
+        } else {
+            root = [root];
+        }
+        return this.callParent([root]);
+    },
+    
+    /**
+     * @private
+     * See Ext.data.reader.Reader's getAssociatedDataRoot docs
+     * @param {Mixed} data The raw data object
+     * @param {String} associationName The name of the association to get data for (uses associationKey if present)
+     * @return {Mixed} The root
+     */
+    getAssociatedDataRoot: function(data, associationName) {
+        return Ext.DomQuery.select(associationName, data)[0];
+    },
+
+    /**
+     * Parses an XML document and returns a ResultSet containing the model instances
+     * @param {Object} doc Parsed XML document
+     * @return {Ext.data.ResultSet} The parsed result set
+     */
+    readRecords: function(doc) {
+        //it's possible that we get passed an array here by associations. Make sure we strip that out (see Ext.data.reader.Reader#readAssociated)
+        if (Ext.isArray(doc)) {
+            doc = doc[0];
+        }
+        
+        /**
+         * DEPRECATED - will be removed in Ext JS 5.0. This is just a copy of this.rawData - use that instead
+         * @property xmlData
+         * @type Object
+         */
+        this.xmlData = doc;
+        return this.callParent([doc]);
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.data.writer.Xml
+ * @extends Ext.data.writer.Writer
+ * 
+ * <p>Writer that outputs model data in XML format</p>
+ */
+Ext.define('Ext.data.writer.Xml', {
+    
+    /* Begin Definitions */
+    
+    extend: 'Ext.data.writer.Writer',
+    alternateClassName: 'Ext.data.XmlWriter',
+    
+    alias: 'writer.xml',
+    
+    /* End Definitions */
+    
+    /**
+     * @cfg {String} documentRoot The name of the root element of the document. Defaults to <tt>'xmlData'</tt>.
+     * If there is more than 1 record and the root is not specified, the default document root will still be used
+     * to ensure a valid XML document is created.
+     */
+    documentRoot: 'xmlData',
+    
+    /**
+     * @cfg {String} defaultDocumentRoot The root to be used if {@link #documentRoot} is empty and a root is required
+     * to form a valid XML document.
+     */
+    defaultDocumentRoot: 'xmlData',
+
+    /**
+     * @cfg {String} header A header to use in the XML document (such as setting the encoding or version).
+     * Defaults to <tt>''</tt>.
+     */
+    header: '',
+
+    /**
+     * @cfg {String} record The name of the node to use for each record. Defaults to <tt>'record'</tt>.
+     */
+    record: 'record',
+
+    //inherit docs
+    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;
+            
+        // may not exist
+        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;
+    }
+});
+
+/**
+ * @class Ext.direct.Event
+ * A base class for all Ext.direct events. An event is
+ * created after some kind of interaction with the server.
+ * The event class is essentially just a data structure
+ * to hold a direct response.
+ * 
+ * @constructor
+ * @param {Object} config The config object
+ */
+Ext.define('Ext.direct.Event', {
+    
+    /* Begin Definitions */
+   
+    alias: 'direct.event',
+    
+    requires: ['Ext.direct.Manager'],
+    
+    /* End Definitions */
+   
+    status: true,
+    
+    constructor: function(config) {
+        Ext.apply(this, config);
+    },
+    
+    /**
+     * Return the raw data for this event.
+     * @return {Object} The data from the event
+     */
+    getData: function(){
+        return this.data;
+    }
+});
+
+/**
+ * @class Ext.direct.RemotingEvent
+ * @extends Ext.direct.Event
+ * An event that is fired when data is received from a 
+ * {@link Ext.direct.RemotingProvider}. Contains a method to the
+ * related transaction for the direct request, see {@link #getTransaction}
+ */
+Ext.define('Ext.direct.RemotingEvent', {
+    
+    /* Begin Definitions */
+   
+    extend: 'Ext.direct.Event',
+    
+    alias: 'direct.rpc',
+    
+    /* End Definitions */
+    
+    /**
+     * Get the transaction associated with this event.
+     * @return {Ext.direct.Transaction} The transaction
+     */
+    getTransaction: function(){
+        return this.transaction || Ext.direct.Manager.getTransaction(this.tid);
+    }
+});
+
+/**
+ * @class Ext.direct.ExceptionEvent
+ * @extends Ext.direct.RemotingEvent
+ * An event that is fired when an exception is received from a {@link Ext.direct.RemotingProvider}
+ */
+Ext.define('Ext.direct.ExceptionEvent', {
+    
+    /* Begin Definitions */
+   
+    extend: 'Ext.direct.RemotingEvent',
+    
+    alias: 'direct.exception',
+    
+    /* End Definitions */
+   
+   status: false
+});
+
+/**
+ * @class Ext.direct.Provider
+ * <p>Ext.direct.Provider is an abstract class meant to be extended.</p>
+ * 
+ * <p>For example ExtJs implements the following subclasses:</p>
+ * <pre><code>
+Provider
+|
++---{@link Ext.direct.JsonProvider JsonProvider} 
+    |
+    +---{@link Ext.direct.PollingProvider PollingProvider}   
+    |
+    +---{@link Ext.direct.RemotingProvider RemotingProvider}   
+ * </code></pre>
+ * @abstract
+ */
+Ext.define('Ext.direct.Provider', {
+    
+    /* Begin Definitions */
+   
+   alias: 'direct.provider',
+   
+    mixins: {
+        observable: 'Ext.util.Observable'   
+    },
+   
+    /* End Definitions */
+   
+   /**
+     * @cfg {String} id
+     * The unique id of the provider (defaults to an {@link Ext#id auto-assigned id}).
+     * You should assign an id if you need to be able to access the provider later and you do
+     * not have an object reference available, for example:
+     * <pre><code>
+Ext.direct.Manager.addProvider({
+    type: 'polling',
+    url:  'php/poll.php',
+    id:   'poll-provider'
+});     
+var p = {@link Ext.direct.Manager}.{@link Ext.direct.Manager#getProvider getProvider}('poll-provider');
+p.disconnect();
+     * </code></pre>
+     */
+    
+    constructor : function(config){
+        var me = this;
+        
+        Ext.apply(me, config);
+        me.addEvents(
+            /**
+             * @event connect
+             * Fires when the Provider connects to the server-side
+             * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
+             */            
+            'connect',
+            /**
+             * @event disconnect
+             * Fires when the Provider disconnects from the server-side
+             * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
+             */            
+            'disconnect',
+            /**
+             * @event data
+             * Fires when the Provider receives data from the server-side
+             * @param {Ext.direct.Provider} provider The {@link Ext.direct.Provider Provider}.
+             * @param {event} e The Ext.Direct.Event type that occurred.
+             */            
+            'data',
+            /**
+             * @event exception
+             * Fires when the Provider receives an exception from the server-side
+             */                        
+            'exception'
+        );
+        me.mixins.observable.constructor.call(me, config);
+    },
+    
+    /**
+     * Returns whether or not the server-side is currently connected.
+     * Abstract method for subclasses to implement.
+     */
+    isConnected: function(){
+        return false;
+    },
+
+    /**
+     * Abstract methods for subclasses to implement.
+     */
+    connect: Ext.emptyFn,
+    
+    /**
+     * Abstract methods for subclasses to implement.
+     */
+    disconnect: Ext.emptyFn
+});
+
+/**
+ * @class Ext.direct.JsonProvider
+ * @extends Ext.direct.Provider
+
+A base provider for communicating using JSON. This is an abstract class
+and should not be instanced directly.
+
+ * @markdown
+ * @abstract
+ */
+
+Ext.define('Ext.direct.JsonProvider', {
+    
+    /* Begin Definitions */
+    
+    extend: 'Ext.direct.Provider',
+    
+    alias: 'direct.jsonprovider',
+    
+    uses: ['Ext.direct.ExceptionEvent'],
+    
+    /* End Definitions */
+   
+   /**
+    * Parse the JSON response
+    * @private
+    * @param {Object} response The XHR response object
+    * @return {Object} The data in the response.
+    */
+   parseResponse: function(response){
+        if (!Ext.isEmpty(response.responseText)) {
+            if (Ext.isObject(response.responseText)) {
+                return response.responseText;
+            }
+            return Ext.decode(response.responseText);
+        }
+        return null;
+    },
+
+    /**
+     * Creates a set of events based on the XHR response
+     * @private
+     * @param {Object} response The XHR response
+     * @return {Array} An array of Ext.direct.Event
+     */
+    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;
+    },
+    
+    /**
+     * Create an event from a response object
+     * @param {Object} response The XHR response object
+     * @return {Ext.direct.Event} The event
+     */
+    createEvent: function(response){
+        return Ext.create('direct.' + response.type, response);
+    }
+});
+/**
+ * @class Ext.direct.PollingProvider
+ * @extends Ext.direct.JsonProvider
+ *
+ * <p>Provides for repetitive polling of the server at distinct {@link #interval intervals}.
+ * The initial request for data originates from the client, and then is responded to by the
+ * server.</p>
+ * 
+ * <p>All configurations for the PollingProvider should be generated by the server-side
+ * API portion of the Ext.Direct stack.</p>
+ *
+ * <p>An instance of PollingProvider may be created directly via the new keyword or by simply
+ * specifying <tt>type = 'polling'</tt>.  For example:</p>
+ * <pre><code>
+var pollA = new Ext.direct.PollingProvider({
+    type:'polling',
+    url: 'php/pollA.php',
+});
+Ext.direct.Manager.addProvider(pollA);
+pollA.disconnect();
+
+Ext.direct.Manager.addProvider(
+    {
+        type:'polling',
+        url: 'php/pollB.php',
+        id: 'pollB-provider'
+    }
+);
+var pollB = Ext.direct.Manager.getProvider('pollB-provider');
+ * </code></pre>
+ */
+Ext.define('Ext.direct.PollingProvider', {
+    
+    /* Begin Definitions */
+    
+    extend: 'Ext.direct.JsonProvider',
+    
+    alias: 'direct.pollingprovider',
+    
+    uses: ['Ext.direct.ExceptionEvent'],
+    
+    requires: ['Ext.Ajax', 'Ext.util.DelayedTask'],
+    
+    /* End Definitions */
+    
+    /**
+     * @cfg {Number} interval
+     * How often to poll the server-side in milliseconds (defaults to <tt>3000</tt> - every
+     * 3 seconds).
+     */
+    interval: 3000,
+
+    /**
+     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
+     * on every polling request
+     */
+    
+    /**
+     * @cfg {String/Function} url
+     * The url which the PollingProvider should contact with each request. This can also be
+     * an imported Ext.Direct method which will accept the baseParams as its only argument.
+     */
+
+    // private
+    constructor : function(config){
+        this.callParent(arguments);
+        this.addEvents(
+            /**
+             * @event beforepoll
+             * Fired immediately before a poll takes place, an event handler can return false
+             * in order to cancel the poll.
+             * @param {Ext.direct.PollingProvider}
+             */
+            'beforepoll',            
+            /**
+             * @event poll
+             * This event has not yet been implemented.
+             * @param {Ext.direct.PollingProvider}
+             */
+            'poll'
+        );
+    },
+
+    // inherited
+    isConnected: function(){
+        return !!this.pollTask;
+    },
+
+    /**
+     * Connect to the server-side and begin the polling process. To handle each
+     * response subscribe to the data event.
+     */
+    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) {
+            //<debug>
+            Ext.Error.raise('Error initializing PollingProvider, no url configured.');
+            //</debug>
+        }
+    },
+
+    /**
+     * Disconnect from the server-side and stop the polling process. The disconnect
+     * event will be fired on a successful disconnect.
+     */
+    disconnect: function(){
+        var me = this;
+        
+        if (me.pollTask) {
+            Ext.TaskManager.stop(me.pollTask);
+            delete me.pollTask;
+            me.fireEvent('disconnect', me);
+        }
+    },
+
+    // private
+    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
+            }));
+        }
+    }
+});
+/**
+ * Small utility class used internally to represent a Direct method.
+ * Thi class is used internally.
+ * @class Ext.direct.RemotingMethod
+ * @ignore
+ */
+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)) {
+            // given only the number of parameters
+            me.len = params;
+            me.ordered = true;
+        } else {
+            /*
+             * Given an array of either
+             * a) String
+             * b) Objects with a name property. We may want to encode extra info in here later
+             */
+            me.params = [];
+            Ext.each(params, function(param){
+                name = Ext.isObject(param) ? param.name : param;
+                me.params.push(name);
+            });
+        }
+    },
+    
+    /**
+     * Takes the arguments for the Direct function and splits the arguments
+     * from the scope and the callback.
+     * @param {Array} args The arguments passed to the direct call
+     * @return {Object} An object with 3 properties, args, callback & scope.
+     */
+    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];
+            
+            // filter out any non-existent properties
+            for (name in data) {
+                if (data.hasOwnProperty(name)) {
+                    if (!Ext.Array.contains(params, name)) {
+                        delete data[name];
+                    }
+                }
+            }
+        }
+        
+        return {
+            data: data,
+            callback: callback,
+            scope: scope    
+        };
+    }
+});
+
+/**
+ * @class Ext.direct.Transaction
+ * @extends Object
+ * <p>Supporting Class for Ext.Direct (not intended to be used directly).</p>
+ * @constructor
+ * @param {Object} config
+ */
+Ext.define('Ext.direct.Transaction', {
+    
+    /* Begin Definitions */
+   
+    alias: 'direct.transaction',
+    alternateClassName: 'Ext.Direct.Transaction',
+   
+    statics: {
+        TRANSACTION_ID: 0
+    },
+   
+    /* End Definitions */
+   
+    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;
+    }
+});
+
+/**
+ * @class Ext.direct.RemotingProvider
+ * @extends Ext.direct.JsonProvider
+ * 
+ * <p>The {@link Ext.direct.RemotingProvider RemotingProvider} exposes access to
+ * server side methods on the client (a remote procedure call (RPC) type of
+ * connection where the client can initiate a procedure on the server).</p>
+ * 
+ * <p>This allows for code to be organized in a fashion that is maintainable,
+ * while providing a clear path between client and server, something that is
+ * not always apparent when using URLs.</p>
+ * 
+ * <p>To accomplish this the server-side needs to describe what classes and methods
+ * are available on the client-side. This configuration will typically be
+ * outputted by the server-side Ext.Direct stack when the API description is built.</p>
+ */
+Ext.define('Ext.direct.RemotingProvider', {
+    
+    /* Begin Definitions */
+   
+    alias: 'direct.remotingprovider',
+    
+    extend: 'Ext.direct.JsonProvider', 
+    
+    requires: [
+        'Ext.util.MixedCollection', 
+        'Ext.util.DelayedTask', 
+        'Ext.direct.Transaction',
+        'Ext.direct.RemotingMethod'
+    ],
+   
+    /* End Definitions */
+   
+   /**
+     * @cfg {Object} actions
+     * Object literal defining the server side actions and methods. For example, if
+     * the Provider is configured with:
+     * <pre><code>
+"actions":{ // each property within the 'actions' object represents a server side Class 
+    "TestAction":[ // array of methods within each server side Class to be   
+    {              // stubbed out on client
+        "name":"doEcho", 
+        "len":1            
+    },{
+        "name":"multiply",// name of method
+        "len":2           // The number of parameters that will be used to create an
+                          // array of data to send to the server side function.
+                          // Ensure the server sends back a Number, not a String. 
+    },{
+        "name":"doForm",
+        "formHandler":true, // direct the client to use specialized form handling method 
+        "len":1
+    }]
+}
+     * </code></pre>
+     * <p>Note that a Store is not required, a server method can be called at any time.
+     * In the following example a <b>client side</b> handler is used to call the
+     * server side method "multiply" in the server-side "TestAction" Class:</p>
+     * <pre><code>
+TestAction.multiply(
+    2, 4, // pass two arguments to server, so specify len=2
+    // callback function after the server is called
+    // result: the result returned by the server
+    //      e: Ext.direct.RemotingEvent object
+    function(result, e){
+        var t = e.getTransaction();
+        var action = t.action; // server side Class called
+        var method = t.method; // server side method called
+        if(e.status){
+            var answer = Ext.encode(result); // 8
+    
+        }else{
+            var msg = e.message; // failure message
+        }
+    }
+);
+     * </code></pre>
+     * In the example above, the server side "multiply" function will be passed two
+     * arguments (2 and 4).  The "multiply" method should return the value 8 which will be
+     * available as the <tt>result</tt> in the example above. 
+     */
+    
+    /**
+     * @cfg {String/Object} namespace
+     * Namespace for the Remoting Provider (defaults to the browser global scope of <i>window</i>).
+     * Explicitly specify the namespace Object, or specify a String to have a
+     * {@link Ext#namespace namespace created} implicitly.
+     */
+    
+    /**
+     * @cfg {String} url
+     * <b>Required<b>. The url to connect to the {@link Ext.direct.Manager} server-side router. 
+     */
+    
+    /**
+     * @cfg {String} enableUrlEncode
+     * Specify which param will hold the arguments for the method.
+     * Defaults to <tt>'data'</tt>.
+     */
+    
+    /**
+     * @cfg {Number/Boolean} enableBuffer
+     * <p><tt>true</tt> or <tt>false</tt> to enable or disable combining of method
+     * calls. If a number is specified this is the amount of time in milliseconds
+     * to wait before sending a batched request (defaults to <tt>10</tt>).</p>
+     * <br><p>Calls which are received within the specified timeframe will be
+     * concatenated together and sent in a single request, optimizing the
+     * application by reducing the amount of round trips that have to be made
+     * to the server.</p>
+     */
+    enableBuffer: 10,
+    
+    /**
+     * @cfg {Number} maxRetries
+     * Number of times to re-attempt delivery on failure of a call. Defaults to <tt>1</tt>.
+     */
+    maxRetries: 1,
+    
+    /**
+     * @cfg {Number} timeout
+     * The timeout to use for each request. Defaults to <tt>undefined</tt>.
+     */
+    timeout: undefined,
+    
+    constructor : function(config){
+        var me = this;
+        me.callParent(arguments);
+        me.addEvents(
+            /**
+             * @event beforecall
+             * Fires immediately before the client-side sends off the RPC call.
+             * By returning false from an event handler you can prevent the call from
+             * executing.
+             * @param {Ext.direct.RemotingProvider} provider
+             * @param {Ext.direct.Transaction} transaction
+             * @param {Object} meta The meta data
+             */            
+            'beforecall',            
+            /**
+             * @event call
+             * Fires immediately after the request to the server-side is sent. This does
+             * NOT fire after the response has come back from the call.
+             * @param {Ext.direct.RemotingProvider} provider
+             * @param {Ext.direct.Transaction} transaction
+             * @param {Object} meta The meta data
+             */            
+            'call'
+        );
+        me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || window;
+        me.transactions = Ext.create('Ext.util.MixedCollection');
+        me.callBuffer = [];
+    },
+    
+    /**
+     * Initialize the API
+     * @private
+     */
+    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);
+            }
+        }
+    },
+    
+    /**
+     * Create a handler function for a direct call.
+     * @private
+     * @param {String} action The action the call is for
+     * @param {Object} method The details of the method
+     * @return {Function} A JS function that will kick off the call
+     */
+    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;
+    },
+    
+    // inherit docs
+    isConnected: function(){
+        return !!this.connected;
+    },
+
+    // inherit docs
+    connect: function(){
+        var me = this;
+        
+        if (me.url) {
+            me.initAPI();
+            me.connected = true;
+            me.fireEvent('connect', me);
+        } else if(!me.url) {
+            //<debug>
+            Ext.Error.raise('Error initializing RemotingProvider, no url configured.');
+            //</debug>
+        }
+    },
+
+    // inherit docs
+    disconnect: function(){
+        var me = this;
+        
+        if (me.connected) {
+            me.connected = false;
+            me.fireEvent('disconnect', me);
+        }
+    },
+    
+    /**
+     * Run any callbacks related to the transaction.
+     * @private
+     * @param {Ext.direct.Transaction} transaction The transaction
+     * @param {Ext.direct.Event} event The event
+     */
+    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]);
+            }
+        }
+    },
+    
+    /**
+     * React to the ajax request being completed
+     * @private
+     */
+    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);
+                    }
+                }
+            }
+        }
+    },
+    
+    /**
+     * Get transaction from XHR options
+     * @private
+     * @param {Object} options The options sent to the Ajax request
+     * @return {Ext.direct.Transaction} The transaction, null if not found
+     */
+    getTransaction: function(options){
+        return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
+    },
+    
+    /**
+     * Configure a direct request
+     * @private
+     * @param {String} action The action being executed
+     * @param {Object} method The being executed
+     */
+    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);
+        }
+    },
+    
+    /**
+     * Gets the Ajax call info for a transaction
+     * @private
+     * @param {Ext.direct.Transaction} transaction The transaction
+     * @return {Object} The call params
+     */
+    getCallData: function(transaction){
+        return {
+            action: transaction.action,
+            method: transaction.method,
+            data: transaction.data,
+            type: 'rpc',
+            tid: transaction.id
+        };
+    },
+    
+    /**
+     * Sends a request to the server
+     * @private
+     * @param {Object/Array} data The data to send
+     */
+    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);
+    },
+    
+    /**
+     * Add a new transaction to the queue
+     * @private
+     * @param {Ext.direct.Transaction} transaction The transaction
+     */
+    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();
+        }
+    },
+    
+    /**
+     * Combine any buffered requests and send them off
+     * @private
+     */
+    combineAndSend : function(){
+        var buffer = this.callBuffer,
+            len = buffer.length;
+            
+        if (len > 0) {
+            this.sendRequest(len == 1 ? buffer[0] : buffer);
+            this.callBuffer = [];
+        }
+    },
+    
+    /**
+     * Configure a form submission request
+     * @private
+     * @param {String} action The action being executed
+     * @param {Object} method The method being executed
+     * @param {HTMLElement} form The form being submitted
+     * @param {Function} callback (optional) A callback to run after the form submits
+     * @param {Object} scope A scope to execute the callback in
+     */
+    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)
+            };
+            
+            // change made from typeof callback check to callback.params
+            // to support addl param passing in DirectSubmit EAC 6/2
+            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);
+        }
+    },
+    
+    /**
+     * Sends a form request
+     * @private
+     * @param {Ext.direct.Transaction} transaction The transaction to send
+     */
+    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
+        });
+    }
+    
+});
+
+/*
+ * @class Ext.draw.Matrix
+ * @private
+ */
+Ext.define('Ext.draw.Matrix', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.draw.Draw'],
+
+    /* End Definitions */
+
+    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 matrix into Translate Scale, Shear, and Rotate
+    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;
+
+        // scale and shear
+        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;
+
+        // rotation
+        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;
+    }
+});
+// private - DD implementation for Panels
+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);
+    },
+
+    /*
+      TODO(nico): Cumulative translations in VML are handled
+      differently than in SVG. While in SVG we specify the translation
+      relative to the original x, y position attributes, in VML the translation
+      is a delta between the last position of the object (modified by the last
+      translation) and the new one.
+      
+      In VML the translation alters the position
+      of the object, we should change that or alter the SVG impl.
+    */
+     
+    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;
+        }
+    }
+});
+/**
+ * @class Ext.draw.Sprite
+ * @extends Object
+ *
+ * A Sprite is an object rendered in a Drawing surface. There are different options and types of sprites.
+ * The configuration of a Sprite is an object with the following properties:
+ *
+ * - **type** - (String) The type of the sprite. Possible options are 'circle', 'path', 'rect', 'text', 'square'. 
+ * - **width** - (Number) Used in rectangle sprites, the width of the rectangle.
+ * - **height** - (Number) Used in rectangle sprites, the height of the rectangle.
+ * - **size** - (Number) Used in square sprites, the dimension of the square.
+ * - **radius** - (Number) Used in circle sprites, the radius of the circle.
+ * - **x** - (Number) The position along the x-axis.
+ * - **y** - (Number) The position along the y-axis.
+ * - **path** - (Array) Used in path sprites, the path of the sprite written in SVG-like path syntax.
+ * - **opacity** - (Number) The opacity of the sprite.
+ * - **fill** - (String) The fill color.
+ * - **stroke** - (String) The stroke color.
+ * - **stroke-width** - (Number) The width of the stroke.
+ * - **font** - (String) Used with text type sprites. The full font description. Uses the same syntax as the CSS `font` parameter.
+ * - **text** - (String) Used with text type sprites. The text itself.
+ * 
+ * Additionally there are three transform objects that can be set with `setAttributes` which are `translate`, `rotate` and
+ * `scale`.
+ * 
+ * For translate, the configuration object contains x and y attributes that indicate where to
+ * translate the object. For example:
+ * 
+ *     sprite.setAttributes({
+ *       translate: {
+ *        x: 10,
+ *        y: 10
+ *       }
+ *     }, true);
+ * 
+ * For rotation, the configuration object contains x and y attributes for the center of the rotation (which are optional),
+ * and a `degrees` attribute that specifies the rotation in degrees. For example:
+ * 
+ *     sprite.setAttributes({
+ *       rotate: {
+ *        degrees: 90
+ *       }
+ *     }, true);
+ * 
+ * For scaling, the configuration object contains x and y attributes for the x-axis and y-axis scaling. For example:
+ * 
+ *     sprite.setAttributes({
+ *       scale: {
+ *        x: 10,
+ *        y: 3
+ *       }
+ *     }, true);
+ *
+ * Sprites can be created with a reference to a {@link Ext.draw.Surface}
+ *
+ *      var drawComponent = Ext.create('Ext.draw.Component', options here...);
+ *
+ *      var sprite = Ext.create('Ext.draw.Sprite', {
+ *          type: 'circle',
+ *          fill: '#ff0',
+ *          surface: drawComponent.surface,
+ *          radius: 5
+ *      });
+ *
+ * Sprites can also be added to the surface as a configuration object:
+ *
+ *      var sprite = drawComponent.surface.add({
+ *          type: 'circle',
+ *          fill: '#ff0',
+ *          radius: 5
+ *      });
+ *
+ * In order to properly apply properties and render the sprite we have to
+ * `show` the sprite setting the option `redraw` to `true`:
+ *
+ *      sprite.show(true);
+ *
+ * The constructor configuration object of the Sprite can also be used and passed into the {@link Ext.draw.Surface}
+ * add method to append a new sprite to the canvas. For example:
+ *
+ *     drawComponent.surface.add({
+ *         type: 'circle',
+ *         fill: '#ffc',
+ *         radius: 100,
+ *         x: 100,
+ *         y: 100
+ *     });
+ */
+Ext.define('Ext.draw.Sprite', {
+    /* Begin Definitions */
+
+    mixins: {
+        observable: 'Ext.util.Observable',
+        animate: 'Ext.util.Animate'
+    },
+
+    requires: ['Ext.draw.SpriteDD'],
+
+    /* End Definitions */
+
+    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');
+        //attribute bucket
+        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 not bucket attributes
+        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);
+    },
+
+    /**
+     * <p>If this Sprite is configured {@link #draggable}, this property will contain
+     * an instance of {@link Ext.dd.DragSource} which handles dragging the Sprite.</p>
+     * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
+     * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
+     * @type Ext.dd.DragSource.
+     * @property dd
+     */
+    initDraggable: function() {
+        var me = this;
+        me.draggable = true;
+        //create element if it doesn't exist.
+        if (!me.el) {
+            me.surface.createSprite(me);
+        }
+        me.dd = Ext.create('Ext.draw.SpriteDD', me, Ext.isBoolean(me.draggable) ? null : me.draggable);
+        me.on('beforedestroy', me.dd.destroy, me.dd);
+    },
+
+    /**
+     * Change the attributes of the sprite.
+     * @param {Object} attrs attributes to be changed on the sprite.
+     * @param {Boolean} redraw Flag to immediatly draw the change.
+     * @return {Ext.draw.Sprite} this
+     */
+    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;
+
+        for (attr in custom) {
+            if (attrs.hasOwnProperty(attr) && typeof custom[attr] == "function") {
+                Ext.apply(attrs, custom[attr].apply(me, [].concat(attrs[attr])));
+            }
+        }
+
+        // Flag a change in hidden
+        if (!!attrs.hidden !== !!spriteAttrs.hidden) {
+            me.dirtyHidden = true;
+        }
+
+        // Flag path change
+        for (i = 0; i < pathPropsLength; i++) {
+            attr = pathProps[i];
+            if (attr in attrs && attrs[attr] !== spriteAttrs[attr]) {
+                me.dirtyPath = true;
+                break;
+            }
+        }
+
+        // Flag zIndex change
+        if ('zIndex' in attrs) {
+            me.zIndexDirty = true;
+        }
+
+        // Flag font/text change
+        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;
+    },
+
+    /**
+     * Retrieve the bounding box of the sprite. This will be returned as an object with x, y, width, and height properties.
+     * @return {Object} bbox
+     */
+    getBBox: function() {
+        return this.surface.getBBox(this);
+    },
+    
+    setText: function(text) {
+        return this.surface.setText(this, text);
+    },
+
+    /**
+     * Hide the sprite.
+     * @param {Boolean} redraw Flag to immediatly draw the change.
+     * @return {Ext.draw.Sprite} this
+     */
+    hide: function(redraw) {
+        this.setAttributes({
+            hidden: true
+        }, redraw);
+        return this;
+    },
+
+    /**
+     * Show the sprite.
+     * @param {Boolean} redraw Flag to immediatly draw the change.
+     * @return {Ext.draw.Sprite} this
+     */
+    show: function(redraw) {
+        this.setAttributes({
+            hidden: false
+        }, redraw);
+        return this;
+    },
+
+    /**
+     * Remove the sprite.
+     */
+    remove: function() {
+        if (this.surface) {
+            this.surface.remove(this);
+            return true;
+        }
+        return false;
+    },
+
+    onRemove: function() {
+        this.surface.onRemove(this);
+    },
+
+    /**
+     * Removes the sprite and clears all listeners.
+     */
+    destroy: function() {
+        var me = this;
+        if (me.fireEvent('beforedestroy', me) !== false) {
+            me.remove();
+            me.surface.onDestroy(me);
+            me.clearListeners();
+            me.fireEvent('destroy');
+        }
+    },
+
+    /**
+     * Redraw the sprite.
+     * @return {Ext.draw.Sprite} this
+     */
+    redraw: function() {
+        this.surface.renderItem(this);
+        return this;
+    },
+
+    /**
+     * Wrapper for setting style properties, also takes single object parameter of multiple styles.
+     * @param {String/Object} property The style property to be set, or an object of multiple styles.
+     * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
+     * @return {Ext.draw.Sprite} this
+     */
+    setStyle: function() {
+        this.el.setStyle.apply(this.el, arguments);
+        return this;
+    },
+
+    /**
+     * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.  Note this method
+     * is severly limited in VML.
+     * @param {String/Array} className The CSS class to add, or an array of classes
+     * @return {Ext.draw.Sprite} this
+     */
+    addCls: function(obj) {
+        this.surface.addCls(this, obj);
+        return this;
+    },
+
+    /**
+     * Removes one or more CSS classes from the element.
+     * @param {String/Array} className The CSS class to remove, or an array of classes.  Note this method
+     * is severly limited in VML.
+     * @return {Ext.draw.Sprite} this
+     */
+    removeCls: function(obj) {
+        this.surface.removeCls(this, obj);
+        return this;
+    }
+});
+
+/**
+ * @class Ext.draw.engine.Svg
+ * @extends Ext.draw.Surface
+ * Provides specific methods to draw with SVG.
+ */
+Ext.define('Ext.draw.engine.Svg', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.draw.Surface',
+
+    requires: ['Ext.draw.Draw', 'Ext.draw.Sprite', 'Ext.draw.Matrix', 'Ext.core.Element'],
+
+    /* End Definitions */
+
+    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) {
+        // Create svg element and append to the DOM.
+        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); //performs the insertion
+        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) {
+                // Firefox 3.0.x plays badly here
+            }
+            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]);
+    },
+
+    /**
+     * Get the region for the surface's canvas area
+     * @returns {Ext.util.Region}
+     */
+    getRegion: function() {
+        // Mozilla requires using the background rect because the svg element returns an
+        // incorrect region. Webkit gives no region for the rect and must use the svg element.
+        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(),
+
+                // Create a rect that is always the same size as the svg root; this serves 2 purposes:
+                // (1) It allows mouse events to be fired over empty areas in Webkit, and (2) we can
+                // use it rather than the svg element for retrieving the correct client rect of the
+                // surface in Mozilla (see https://bugzilla.mozilla.org/show_bug.cgi?id=530985)
+                bgRect = me.createSvgElement("rect", {
+                    width: "100%",
+                    height: "100%",
+                    fill: "#000",
+                    stroke: "none",
+                    opacity: 0
+                }),
+                webkitRect;
+            
+                if (Ext.isSafari3) {
+                    // Rect that we will show/hide to fix old WebKit bug with rendering issues.
+                    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();
+    },
+
+    // private
+    onMouseEnter: function(e) {
+        if (this.el.parent().getRegion().contains(e.getPoint())) {
+            this.fireEvent('mouseenter', e);
+        }
+    },
+
+    // private
+    onMouseLeave: function(e) {
+        if (!this.el.parent().getRegion().contains(e.getPoint())) {
+            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);
+        // We wrap text types in a tspan, sprite is the parent.
+        if (target.nodeName == "tspan" && target.parentNode) {
+            target = target.parentNode;
+        }
+        sprite = this.items.get(target.id);
+        if (sprite) {
+            sprite.fireEvent(name, sprite, e);
+        }
+    },
+
+    /* @private - Wrap SVG text inside a tspan to allow for line wrapping.  In addition this normallizes
+     * the baseline for text the vertical middle of the text to be the same as VML.
+     */
+    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);
+        }
+        // Normalize baseline via a DY shift of first tspan. Shift other rows by height * line height (1.2)
+        if (tspans.length) {
+            height = this.getBBoxText(sprite).height;
+            for (i = 0, ln = tspans.length; i < ln; i++) {
+                // The text baseline for FireFox 3.0 and 3.5 is different than other SVG implementations
+                // so we are going to normalize that here
+                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);
+        }
+        // Wrap each row into tspan to emulate rows
+        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) || {};
+
+        // if (sprite.dirtyPath) {
+        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.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, String(attrs[key]));
+            }
+        }
+        if (sprite.type == 'text') {
+            me.tuneText(sprite, attrs);
+        }
+
+        //set styles
+        style = sattr.style;
+        if (style) {
+            el.setStyle(style);
+        }
+
+        sprite.dirty = false;
+
+        if (Ext.isSafari3) {
+            // Refreshing the view to fix bug EXTJSIV-1: rendering issue in old Safari 3
+            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;
+        }
+        // if (!attrs[key]) {
+        //     var clip = Ext.getDoc().dom.getElementById(sprite.el.getAttribute("clip-path").replace(/(^url\(#|\)$)/g, ""));
+        //     clip && clip.parentNode.removeChild(clip);
+        //     sprite.el.setAttribute("clip-path", "");
+        //     delete attrss.clip;
+        // }
+    },
+
+    /**
+     * Insert or move a given sprite's element to the correct place in the DOM list for its zIndex
+     * @param {Ext.draw.Sprite} sprite
+     */
+    applyZIndex: function(sprite) {
+        var idx = this.normalizeSpriteCollection(sprite),
+            el = sprite.el,
+            prevEl;
+        if (this.el.dom.childNodes[idx + 2] !== el.dom) { //shift by 2 to account for defs and bg rect 
+            if (idx > 0) {
+                // Find the first previous sprite which has its DOM element created already
+                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);
+        }
+    },
+
+    /**
+     * Checks if the specified CSS class exists on this element's DOM node.
+     * @param {String} className The CSS class to check for
+     * @return {Boolean} True if the class exists, else false
+     */
+    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') || '';
+        // Separate case is for speed
+        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;
+    }
+});
+/**
+ * @class Ext.draw.engine.Vml
+ * @extends Ext.draw.Surface
+ * Provides specific methods to draw with VML.
+ */
+
+Ext.define('Ext.draw.engine.Vml', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.draw.Surface',
+
+    requires: ['Ext.draw.Draw', 'Ext.draw.Color', 'Ext.draw.Sprite', 'Ext.draw.Matrix', 'Ext.core.Element'],
+
+    /* End Definitions */
+
+    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; // TODO handle baseline differences and offset in VML Textpath
+
+            // text-anchor emulation
+            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);
+        // Clear bbox cache
+        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;
+        }
+
+        // Size outer div
+        if (width != undefined) {
+            me.el.setWidth(width);
+        }
+        if (height != undefined) {
+            me.el.setHeight(height);
+        }
+
+        // Handle viewBox sizing
+        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);
+            // Scale and translate group
+            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;
+        // VML Node factory method (createNode)
+        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);
+
+            // Measuring span (offscrren)
+            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) {
+        // Does the surface element exist?
+        if (!this.el) {
+            return;
+        }
+
+        // Create sprite element if necessary
+        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;
+
+
+        // Hide element while we transform
+
+        if (sprite.type != "image" && skew) {
+            // matrix transform via VML skew
+            skew.matrix = matrix.toString();
+            skew.offset = matrix.offset();
+        }
+        else {
+            deltaX = matrix.matrix[0][2];
+            deltaY = matrix.matrix[1][2];
+            // Scale via coordsize property
+            zoomScaleX = zoom / deltaScaleX;
+            zoomScaleY = zoom / deltaScaleY;
+
+            dom.coordsize = Math.abs(zoomScaleX) + " " + Math.abs(zoomScaleY);
+
+            // Rotate via rotation property
+            newAngle = deltaDegrees * (deltaScaleX * ((deltaScaleY < 0) ? -1 : 1));
+            if (newAngle != domStyle.rotation && !(newAngle === 0 && !domStyle.rotation)) {
+                domStyle.rotation = newAngle;
+            }
+            if (deltaDegrees) {
+                // Compensate x/y position due to rotation
+                compensate = me.rotationCompensation(deltaDegrees, deltaX, deltaY);
+                deltaX = compensate.x;
+                deltaY = compensate.y;
+            }
+
+            // Handle negative scaling via flipping
+            if (deltaScaleX < 0) {
+                flip += "x";
+            }
+            if (deltaScaleY < 0) {
+                flip += " y";
+                y = -1;
+            }
+            if (flip != "" && !dom.style.flip) {
+                domStyle.flip = flip;
+            }
+
+            // Translate via coordorigin property
+            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);
+        }
+    },
+
+    /**
+     * Adds a definition to this Surface for a linear gradient. We convert the gradient definition
+     * to its corresponding VML attributes and store it for later use by individual sprites.
+     * @param {Object} gradient
+     */
+    addGradient: function(gradient) {
+        var gradients = this.gradientsColl || (this.gradientsColl = Ext.create('Ext.util.MixedCollection')),
+            colors = [],
+            stops = Ext.create('Ext.util.MixedCollection');
+
+        // Build colors string
+        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;
+    }
+});
+
+/**
+ * @class Ext.fx.target.ElementCSS
+ * @extends Ext.fx.target.Element
+ * 
+ * This class represents a animation target for an {@link Ext.core.Element} that supports CSS
+ * based animation. In general this class will not be created directly, the {@link Ext.core.Element} 
+ * will be passed to the animation and the appropriate target will be created.
+ */
+Ext.define('Ext.fx.target.ElementCSS', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.fx.target.Element',
+
+    /* End Definitions */
+
+    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]);
+
+                    // Must trigger reflow to make this get used as the start point for the transition that follows
+                    if (isFirstFrame) {
+                        o = o[0].dom.offsetWidth;
+                    }
+                    else {
+                        // Remove transition properties when completed.
+                        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 });
+                    }
+                }
+            }
+        }
+    }
+});
+/**
+ * @class Ext.fx.target.CompositeElementCSS
+ * @extends Ext.fx.target.CompositeElement
+ * 
+ * This class represents a animation target for a {@link Ext.CompositeElement}, where the
+ * constituent elements support CSS based animation. It allows each {@link Ext.core.Element} in 
+ * the group to be animated as a whole. In general this class will not be created directly, 
+ * the {@link Ext.CompositeElement} will be passed to the animation and the appropriate target 
+ * will be created.
+ */
+Ext.define('Ext.fx.target.CompositeElementCSS', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.fx.target.CompositeElement',
+
+    requires: ['Ext.fx.target.ElementCSS'],
+
+    /* End Definitions */
+    setAttr: function() {
+        return Ext.fx.target.ElementCSS.prototype.setAttr.apply(this, arguments);
+    }
+});
+/**
+ * @class Ext.layout.container.AbstractFit
+ * @extends Ext.layout.container.Container
+ * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's
+ * container.  This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.container.Container#layout}
+ * config, and should generally not need to be created directly via the new keyword.</p>
+ * <p>FitLayout does not have any direct config options (other than inherited ones).  To fit a panel to a container
+ * using FitLayout, simply set layout:'fit' on the container and add a single panel to it.  If the container has
+ * multiple panels, only the first one will be displayed.  Example usage:</p>
+ * <pre><code>
+var p = new Ext.panel.Panel({
+    title: 'Fit Layout',
+    layout:'fit',
+    items: {
+        title: 'Inner Panel',
+        html: '&lt;p&gt;This is the inner panel content&lt;/p&gt;',
+        border: false
+    }
+});
+</code></pre>
+ */
+Ext.define('Ext.layout.container.AbstractFit', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.layout.container.Container',
+
+    /* End Definitions */
+
+    itemCls: Ext.baseCSSPrefix + 'fit-item',
+    targetCls: Ext.baseCSSPrefix + 'layout-fit',
+    type: 'fit'
+});
+/**
+ * @class Ext.layout.container.Fit
+ * @extends Ext.layout.container.AbstractFit
+ * <p>This is a base class for layouts that contain <b>a single item</b> that automatically expands to fill the layout's
+ * container.  This class is intended to be extended or created via the <tt>layout:'fit'</tt> {@link Ext.container.Container#layout}
+ * config, and should generally not need to be created directly via the new keyword.</p>
+ * <p>FitLayout does not have any direct config options (other than inherited ones).  To fit a panel to a container
+ * using FitLayout, simply set layout:'fit' on the container and add a single panel to it.  If the container has
+ * multiple panels, only the first one will be displayed.  
+ * {@img Ext.layout.container.Fit/Ext.layout.container.Fit.png Ext.layout.container.Fit container layout}
+ * Example usage:</p>
+ * <pre><code>
+    Ext.create('Ext.panel.Panel', {
+        title: 'Fit Layout',
+        width: 300,
+        height: 150,
+        layout:'fit',
+        items: {
+            title: 'Inner Panel',
+            html: '<p>This is the inner panel content</p>',
+            bodyPadding: 20,
+            border: false
+        },
+        renderTo: Ext.getBody()
+    });  
+</code></pre>
+ */
+Ext.define('Ext.layout.container.Fit', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.layout.container.AbstractFit',
+    alias: 'layout.fit',
+    alternateClassName: 'Ext.layout.FitLayout',
+
+    /* End Definitions */
+   
+    // @private
+    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);
+        }
+    }
+});
+/**
+ * @class Ext.layout.container.AbstractCard
+ * @extends Ext.layout.container.Fit
+ * <p>This layout manages multiple child Components, each is fit to the Container, where only a single child Component
+ * can be visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.
+ * This class is intended to be extended or created via the layout:'card' {@link Ext.container.Container#layout} config,
+ * and should generally not need to be created directly via the new keyword.</p>
+ * <p>The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,
+ * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of
+ * the next panel to display.  The layout itself does not provide a user interface for handling this navigation,
+ * so that functionality must be provided by the developer.</p>
+ * <p>Containers that are configured with a card layout will have a method setActiveItem dynamically added to it.
+ * <pre><code>
+      var p = new Ext.panel.Panel({
+          fullscreen: true,
+          layout: 'card',
+          items: [{
+              html: 'Card 1'
+          },{
+              html: 'Card 2'
+          }]
+      });
+      p.setActiveItem(1);
+   </code></pre>
+ * </p>
+ */
+
+Ext.define('Ext.layout.container.AbstractCard', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.layout.container.Fit',
+
+    /* End Definitions */
+
+    type: 'card',
+
+    sizeAllCards: false,
+
+    hideInactive: true,
+
+    /**
+     * @cfg {Boolean} deferredRender
+     * True to render each contained item at the time it becomes active, false to render all contained items
+     * as soon as the layout is rendered (defaults to false).  If there is a significant amount of content or
+     * a lot of heavy controls being rendered into panels that are not displayed by default, setting this to
+     * true might improve performance.
+     */
+    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) {
+        // Note: Card layout does not care about order within the target because only one is ever visible.
+        // We only care whether the item is a direct child of the target.
+        var itemEl = item.el ? item.el.dom : Ext.getDom(item);
+        return (itemEl && itemEl.parentNode === (target.dom || target)) || false;
+    },
+
+    /**
+     * Return the active (visible) component in the layout.
+     * @returns {Ext.Component}
+     */
+    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;
+    },
+
+    // @private
+    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);
+        }
+    },
+
+    // @private
+    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;
+            }
+        }
+    },
+
+    // @private
+    getAnimation: function(newCard, owner) {
+        var newAnim = (newCard || {}).cardSwitchAnimation;
+        if (newAnim === false) {
+            return false;
+        }
+        return newAnim || owner.cardSwitchAnimation;
+    },
+
+    /**
+     * Return the active (visible) component in the layout to the next card
+     * @returns {Ext.Component}
+     */
+    getNext: function(wrap) {
+        //NOTE: Removed the JSDoc for this function's arguments because it is not actually supported in 4.0. This 
+        //should come back in 4.1
+        
+        var items = this.getLayoutItems(),
+            index = Ext.Array.indexOf(items, this.activeItem);
+        return items[index + 1] || (wrap ? items[0] : false);
+    },
+
+    /**
+     * Sets the active (visible) component in the layout to the next card
+     */
+    next: function(anim, wrap) {
+        //NOTE: Removed the JSDoc for this function's arguments because it is not actually supported in 4.0. This 
+        //should come back in 4.1
+        
+        return this.setActiveItem(this.getNext(wrap), anim);
+    },
+
+    /**
+     * Return the active (visible) component in the layout to the previous card
+     * @returns {Ext.Component}
+     */
+    getPrev: function(wrap) {
+        //NOTE: Removed the JSDoc for this function's arguments because it is not actually supported in 4.0. This 
+        //should come back in 4.1
+        
+        var items = this.getLayoutItems(),
+            index = Ext.Array.indexOf(items, this.activeItem);
+        return items[index - 1] || (wrap ? items[items.length - 1] : false);
+    },
+
+    /**
+     * Sets the active (visible) component in the layout to the previous card
+     */
+    prev: function(anim, wrap) {
+        //NOTE: Removed the JSDoc for this function's arguments because it is not actually supported in 4.0. This 
+        //should come back in 4.1
+        
+        return this.setActiveItem(this.getPrev(wrap), anim);
+    }
+});
+
+/**
+ * @class Ext.selection.Model
+ * @extends Ext.util.Observable
+ *
+ * Tracks what records are currently selected in a databound widget.
+ *
+ * This is an abstract class and is not meant to be directly used.
+ *
+ * DataBound UI widgets such as GridPanel, TreePanel, and ListView
+ * should subclass AbstractStoreSelectionModel and provide a way
+ * to binding to the component.
+ *
+ * The abstract methods onSelectChange and onLastFocusChanged should
+ * be implemented in these subclasses to update the UI widget.
+ */
+Ext.define('Ext.selection.Model', {
+    extend: 'Ext.util.Observable',
+    alternateClassName: 'Ext.AbstractStoreSelectionModel',
+    requires: ['Ext.data.StoreManager'],
+    // lastSelected
+
+    /**
+     * @cfg {String} mode
+     * Modes of selection.
+     * Valid values are SINGLE, SIMPLE, and MULTI. Defaults to 'SINGLE'
+     */
+    
+    /**
+     * @cfg {Boolean} allowDeselect
+     * Allow users to deselect a record in a DataView, List or Grid. Only applicable when the SelectionModel's mode is 'SINGLE'. Defaults to false.
+     */
+    allowDeselect: false,
+
+    /**
+     * @property selected
+     * READ-ONLY A MixedCollection that maintains all of the currently selected
+     * records.
+     */
+    selected: null,
+    
+    
+    /**
+     * Prune records when they are removed from the store from the selection.
+     * This is a private flag. For an example of its usage, take a look at
+     * Ext.selection.TreeModel.
+     * @private
+     */
+    pruneRemoved: true,
+
+    constructor: function(cfg) {
+        var me = this;
+        
+        cfg = cfg || {};
+        Ext.apply(me, cfg);
+        
+        me.addEvents(
+            /**
+             * @event selectionchange
+             * Fired after a selection change has occurred
+             * @param {Ext.selection.Model} this
+             * @param  {Array} selected The selected records
+             */
+             'selectionchange'
+        );
+
+        me.modes = {
+            SINGLE: true,
+            SIMPLE: true,
+            MULTI: true
+        };
+
+        // sets this.selectionMode
+        me.setSelectionMode(cfg.mode || me.mode);
+
+        // maintains the currently selected records.
+        me.selected = Ext.create('Ext.util.MixedCollection');
+        
+        me.callParent(arguments);
+    },
+
+    // binds the store to the selModel.
+    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(silent) {
+        var selections = this.store.getRange(),
+            i = 0,
+            len = selections.length;
+            
+        for (; i < len; i++) {
+            this.doSelect(selections[i], true, silent);
+        }
+    },
+
+    deselectAll: function() {
+        var selections = this.getSelection(),
+            i = 0,
+            len = selections.length;
+            
+        for (; i < len; i++) {
+            this.doDeselect(selections[i]);
+        }
+    },
+
+    // Provides differentiation of logic between MULTI, SIMPLE and SINGLE
+    // selection modes. Requires that an event be passed so that we can know
+    // if user held ctrl or shift.
+    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 allowDeselect is on and this record isSelected, deselect it
+                if (me.allowDeselect && me.isSelected(record)) {
+                    me.doDeselect(record);
+                // select the record and do NOT maintain existing selections
+                } else {
+                    me.doSelect(record, false);
+                }
+                break;
+        }
+    },
+
+    /**
+     * Selects a range of rows if the selection model {@link #isLocked is not locked}.
+     * All rows in between startRow and endRow are also selected.
+     * @param {Ext.data.Model/Number} startRow The record or index of the first row in the range
+     * @param {Ext.data.Model/Number} endRow The record or index of the last row in the range
+     * @param {Boolean} keepExisting (optional) True to retain existing selections
+     */
+    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);
+        }
+        
+        // swap values
+        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);
+    },
+    
+    /**
+     * Selects a record instance by record instance or index.
+     * @param {Ext.data.Model/Index} records An array of records or an index
+     * @param {Boolean} keepExisting
+     * @param {Boolean} suppressEvent Set to false to not fire a select event
+     */
+    select: function(records, keepExisting, suppressEvent) {
+        this.doSelect(records, keepExisting, suppressEvent);
+    },
+
+    /**
+     * Deselects a record instance by record instance or index.
+     * @param {Ext.data.Model/Index} records An array of records or an index
+     * @param {Boolean} suppressEvent Set to false to not fire a deselect event
+     */
+    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(), true);
+        }
+
+        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);
+        // fire selchange if there was a change and there is no suppressEvent flag
+        me.maybeFireSelectionChange(change && !suppressEvent);
+    },
+
+    // records can be an index, a record or an array of records
+    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;
+            }
+        }
+        // fire selchange if there was a change and there is no suppressEvent flag
+        me.maybeFireSelectionChange(change && !suppressEvent);
+    },
+
+    doSingleSelect: function(record, suppressEvent) {
+        var me = this,
+            selected = me.selected;
+            
+        if (me.locked) {
+            return;
+        }
+        // already selected.
+        // should we also check beforeselect?
+        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);
+    },
+
+    /**
+     * @param {Ext.data.Model} record
+     * Set a record as the last focused record. This does NOT mean
+     * that the record has been selected.
+     */
+    setLastFocused: function(record, supressFocus) {
+        var me = this,
+            recordBeforeLast = me.lastFocused;
+        me.lastFocused = record;
+        me.onLastFocusChanged(recordBeforeLast, record, supressFocus);
+    },
+    
+    /**
+     * Determines if this record is currently focused.
+     * @param Ext.data.Record record
+     */
+    isFocused: function(record) {
+        return record === this.getLastFocused();
+    },
+
+
+    // fire selection change as long as true is not passed
+    // into maybeFireSelectionChange
+    maybeFireSelectionChange: function(fireEvent) {
+        if (fireEvent) {
+            var me = this;
+            me.fireEvent('selectionchange', me, me.getSelection());
+        }
+    },
+
+    /**
+     * Returns the last selected record.
+     */
+    getLastSelected: function() {
+        return this.lastSelected;
+    },
+    
+    getLastFocused: function() {
+        return this.lastFocused;
+    },
+
+    /**
+     * Returns an array of the currently selected records.
+     */
+    getSelection: function() {
+        return this.selected.getRange();
+    },
+
+    /**
+     * Returns the current selectionMode. SINGLE, MULTI or SIMPLE.
+     */
+    getSelectionMode: function() {
+        return this.selectionMode;
+    },
+
+    /**
+     * Sets the current selectionMode. SINGLE, MULTI or SIMPLE.
+     */
+    setSelectionMode: function(selMode) {
+        selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
+        // set to mode specified unless it doesnt exist, in that case
+        // use single.
+        this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
+    },
+
+    /**
+     * Returns true if the selections are locked.
+     * @return {Boolean}
+     */
+    isLocked: function() {
+        return this.locked;
+    },
+
+    /**
+     * Locks the current selection and disables any changes from
+     * happening to the selection.
+     * @param {Boolean} locked
+     */
+    setLocked: function(locked) {
+        this.locked = !!locked;
+    },
+
+    /**
+     * Returns <tt>true</tt> if the specified row is selected.
+     * @param {Record/Number} record The record or index of the record to check
+     * @return {Boolean}
+     */
+    isSelected: function(record) {
+        record = Ext.isNumber(record) ? this.store.getAt(record) : record;
+        return this.selected.indexOf(record) !== -1;
+    },
+    
+    /**
+     * Returns true if there is a selected record.
+     * @return {Boolean}
+     */
+    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();
+
+        // check to make sure that there are no records
+        // missing after the refresh was triggered, prune
+        // them from what is to be selected if so
+        for (; i < len; i++) {
+            selection = oldSelections[i];
+            if (!this.pruneRemoved || me.store.indexOf(selection) !== -1) {
+                toBeSelected.push(selection);
+            }
+        }
+
+        // there was a change from the old selected and
+        // the new selection
+        if (me.selected.getCount() != toBeSelected.length) {
+            change = true;
+        }
+
+        me.clearSelections();
+        
+        if (me.store.indexOf(lastFocused) !== -1) {
+            // restore the last focus but supress restoring focus
+            this.setLastFocused(lastFocused, true);
+        }
+
+        if (toBeSelected.length) {
+            // perform the selection again
+            me.doSelect(toBeSelected, false, true);
+        }
+        
+        me.maybeFireSelectionChange(change);
+    },
+
+    clearSelections: function() {
+        // reset the entire selection to nothing
+        var me = this;
+        me.selected.clear();
+        me.lastSelected = null;
+        me.setLastFocused(null);
+    },
+
+    // when a record is added to a store
+    onStoreAdd: function() {
+
+    },
+
+    // when a store is cleared remove all selections
+    // (if there were any)
+    onStoreClear: function() {
+        var me = this,
+            selected = this.selected;
+            
+        if (selected.getCount > 0) {
+            selected.clear();
+            me.lastSelected = null;
+            me.setLastFocused(null);
+            me.maybeFireSelectionChange(true);
+        }
+    },
+
+    // prune records from the SelectionModel if
+    // they were selected at the time they were
+    // removed.
+    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();
+    },
+
+    // cleanup.
+    destroy: function() {
+
+    },
+
+    // if records are updated
+    onStoreUpdate: function() {
+
+    },
+
+    // @abstract
+    onSelectChange: function(record, isSelected, suppressEvent) {
+
+    },
+
+    // @abstract
+    onLastFocusChanged: function(oldFocused, newFocused) {
+
+    },
+
+    // @abstract
+    onEditorKey: function(field, e) {
+
+    },
+
+    // @abstract
+    bindComponent: function(cmp) {
+
+    }
+});
+/**
+ * @class Ext.selection.DataViewModel
+ * @ignore
+ */
+Ext.define('Ext.selection.DataViewModel', {
+    extend: 'Ext.selection.Model',
+    
+    requires: ['Ext.util.KeyNav'],
+
+    deselectOnContainerClick: true,
+    
+    /**
+     * @cfg {Boolean} enableKeyNav
+     * 
+     * Turns on/off keyboard navigation within the DataView. Defaults to true.
+     */
+    enableKeyNav: true,
+    
+    constructor: function(cfg){
+        this.addEvents(
+            /**
+             * @event deselect
+             * Fired after a record is deselected
+             * @param {Ext.selection.DataViewModel} this
+             * @param  {Ext.data.Model} record The deselected record
+             */
+            'deselect',
+            
+            /**
+             * @event select
+             * Fired after a record is selected
+             * @param {Ext.selection.DataViewModel} this
+             * @param  {Ext.data.Model} record The selected record
+             */
+            '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);
+    },
+
+    // Allow the DataView to update the ui
+    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);
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.state.CookieProvider
+ * @extends Ext.state.Provider
+ * A Provider implementation which saves and retrieves state via cookies.
+ * The CookieProvider supports the usual cookie options, such as:
+ * <ul>
+ * <li>{@link #path}</li>
+ * <li>{@link #expires}</li>
+ * <li>{@link #domain}</li>
+ * <li>{@link #secure}</li>
+ * </ul>
+ <pre><code>
+   var cp = new Ext.state.CookieProvider({
+       path: "/cgi-bin/",
+       expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days
+       domain: "sencha.com"
+   });
+   Ext.state.Manager.setProvider(cp);
+ </code></pre>
+ * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
+ * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
+ * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
+ * your page is on, but you can specify a sub-domain, or simply the domain itself like 'sencha.com' to include
+ * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
+ * domain the page is running on including the 'www' like 'www.sencha.com')
+ * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
+ * @constructor
+ * Create a new CookieProvider
+ * @param {Object} config The configuration object
+ */
+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)); //7 days
+        me.domain = null;
+        me.secure = false;
+        me.callParent(arguments);
+        me.state = me.readCookies();
+    },
+    
+    // private
+    set : function(name, value){
+        var me = this;
+        
+        if(typeof value == "undefined" || value === null){
+            me.clear(name);
+            return;
+        }
+        me.setCookie(name, value);
+        me.callParent(arguments);
+    },
+
+    // private
+    clear : function(name){
+        this.clearCookie(name);
+        this.callParent(arguments);
+    },
+
+    // private
+    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;
+    },
+
+    // private
+    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" : "");
+    },
+
+    // private
+    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', {
+    /* Begin Definitions */
+    
+    extend: 'Ext.state.Provider',
+    
+    alias: 'state.localstorage',
+    
+    /* End Definitions */
+   
+    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);
+    },
+
+    // private
+    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;
+        }
+        //<debug>
+        Ext.Error.raise('LocalStorage is not supported by the current browser');
+        //</debug>
+    }    
+});
+
+/**
+ * @class Ext.util.Point
+ * @extends Ext.util.Region
+ *
+ * Represents a 2D point with x and y properties, useful for comparison and instantiation
+ * from an event:
+ * <pre><code>
+ * var point = Ext.util.Point.fromEvent(e);
+ * </code></pre>
+ */
+
+Ext.define('Ext.util.Point', {
+
+    /* Begin Definitions */
+    extend: 'Ext.util.Region',
+
+    statics: {
+
+        /**
+         * Returns a new instance of Ext.util.Point base on the pageX / pageY values of the given event
+         * @static
+         * @param {Event} e The event
+         * @returns Ext.util.Point
+         */
+        fromEvent: function(e) {
+            e = (e.changedTouches && e.changedTouches.length > 0) ? e.changedTouches[0] : e;
+            return new this(e.pageX, e.pageY);
+        }
+    },
+
+    /* End Definitions */
+
+    constructor: function(x, y) {
+        this.callParent([y, x, y, x]);
+    },
+
+    /**
+     * Returns a human-eye-friendly string that represents this point,
+     * useful for debugging
+     * @return {String}
+     */
+    toString: function() {
+        return "Point[" + this.x + "," + this.y + "]";
+    },
+
+    /**
+     * Compare this point and another point
+     * @param {Ext.util.Point/Object} The point to compare with, either an instance
+     * of Ext.util.Point or an object with left and top properties
+     * @return {Boolean} Returns whether they are equivalent
+     */
+    equals: function(p) {
+        return (this.x == p.x && this.y == p.y);
+    },
+
+    /**
+     * Whether the given point is not away from this point within the given threshold amount.
+     * TODO: Rename this isNear.
+     * @param {Ext.util.Point/Object} The point to check with, either an instance
+     * of Ext.util.Point or an object with left and top properties
+     * @param {Object/Number} threshold Can be either an object with x and y properties or a number
+     * @return {Boolean}
+     */
+    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);
+    },
+
+    /**
+     * Compare this point with another point when the x and y values of both points are rounded. E.g:
+     * [100.3,199.8] will equals to [100, 200]
+     * @param {Ext.util.Point/Object} The point to compare with, either an instance
+     * of Ext.util.Point or an object with x and y properties
+     * @return {Boolean}
+     */
+    roundedEquals: function(p) {
+        return (Math.round(this.x) == Math.round(p.x) && Math.round(this.y) == Math.round(p.y));
+    }
+}, function() {
+    /**
+     * Translate this region by the given offset amount. TODO: Either use translate or translateBy!
+     * @param {Ext.util.Offset/Object} offset Object containing the <code>x</code> and <code>y</code> properties.
+     * Or the x value is using the two argument form.
+     * @param {Number} The y value unless using an Offset object.
+     * @return {Ext.util.Region} this This Region
+     */
+    this.prototype.translate = Ext.util.Region.prototype.translateBy;
+});
+
+/**
+ * @class Ext.view.AbstractView
+ * @extends Ext.Component
+ * This is an abstract superclass and should not be used directly. Please see {@link Ext.view.View}.
+ */
+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);
+        }
+    },
+    
+    /**
+     * @cfg {String/Array/Ext.XTemplate} tpl
+     * @required
+     * The HTML fragment or an array of fragments that will make up the template used by this DataView.  This should
+     * be specified in the same format expected by the constructor of {@link Ext.XTemplate}.
+     */
+    /**
+     * @cfg {Ext.data.Store} store
+     * @required
+     * The {@link Ext.data.Store} to bind this DataView to.
+     */
+
+    /**
+     * @cfg {String} itemSelector
+     * @required
+     * <b>This is a required setting</b>. A simple CSS selector (e.g. <tt>div.some-class</tt> or
+     * <tt>span:first-child</tt>) that will be used to determine what nodes this DataView will be
+     * working with. The itemSelector is used to map DOM nodes to records. As such, there should
+     * only be one root level element that matches the selector for each record.
+     */
+    
+    /**
+     * @cfg {String} itemCls
+     * Specifies the class to be assigned to each element in the view when used in conjunction with the
+     * {@link #itemTpl} configuration.
+     */
+    itemCls: Ext.baseCSSPrefix + 'dataview-item',
+    
+    /**
+     * @cfg {String/Array/Ext.XTemplate} itemTpl
+     * The inner portion of the item template to be rendered. Follows an XTemplate
+     * structure and will be placed inside of a tpl.
+     */
+
+    /**
+     * @cfg {String} overItemCls
+     * A CSS class to apply to each item in the view on mouseover (defaults to undefined). 
+     * Ensure {@link #trackOver} is set to `true` to make use of this.
+     */
+
+    /**
+     * @cfg {String} loadingText
+     * A string to display during data load operations (defaults to undefined).  If specified, this text will be
+     * displayed in a loading div and the view's contents will be cleared while loading, otherwise the view's
+     * contents will continue to display normally until the new data is loaded and the contents are replaced.
+     */
+    loadingText: 'Loading...',
+    
+    /**
+     * @cfg {String} loadingCls
+     * The CSS class to apply to the loading message element (defaults to Ext.LoadMask.prototype.msgCls "x-mask-loading")
+     */
+    
+    /**
+     * @cfg {Boolean} loadingUseMsg
+     * Whether or not to use the loading message.
+     * @private
+     */
+    loadingUseMsg: true,
+    
+
+    /**
+     * @cfg {Number} loadingHeight
+     * If specified, gives an explicit height for the data view when it is showing the {@link #loadingText},
+     * if that is specified. This is useful to prevent the view's height from collapsing to zero when the
+     * loading mask is applied and there are no other contents in the data view. Defaults to undefined.
+     */
+
+    /**
+     * @cfg {String} selectedItemCls
+     * A CSS class to apply to each selected item in the view (defaults to 'x-view-selected').
+     */
+    selectedItemCls: Ext.baseCSSPrefix + 'item-selected',
+
+    /**
+     * @cfg {String} emptyText
+     * The text to display in the view when there is no data to display (defaults to '').
+     * Note that when using local data the emptyText will not be displayed unless you set
+     * the {@link #deferEmptyText} option to false.
+     */
+    emptyText: "",
+
+    /**
+     * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
+     */
+    deferEmptyText: true,
+
+    /**
+     * @cfg {Boolean} trackOver True to enable mouseenter and mouseleave events
+     */
+    trackOver: false,
+
+    /**
+     * @cfg {Boolean} blockRefresh Set this to true to ignore datachanged events on the bound store. This is useful if
+     * you wish to provide custom transition animations via a plugin (defaults to false)
+     */
+    blockRefresh: false,
+
+    /**
+     * @cfg {Boolean} disableSelection <p><tt>true</tt> to disable selection within the DataView. Defaults to <tt>false</tt>.
+     * This configuration will lock the selection model that the DataView uses.</p>
+     */
+
+
+    //private
+    last: false,
+    
+    triggerEvent: 'itemclick',
+    triggerCtEvent: 'containerclick',
+    
+    addCmpEvents: function() {
+        
+    },
+
+    // private
+    initComponent : function(){
+        var me = this,
+            isDef = Ext.isDefined,
+            itemTpl = me.itemTpl,
+            memberFn = {};
+            
+        if (itemTpl) {
+            if (Ext.isArray(itemTpl)) {
+                // string array
+                itemTpl = itemTpl.join('');
+            } else if (Ext.isObject(itemTpl)) {
+                // tpl instance
+                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);
+        }
+
+        //<debug>
+        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."
+            });
+        }
+        //</debug>
+
+        me.callParent();
+        if(Ext.isString(me.tpl) || Ext.isArray(me.tpl)){
+            me.tpl = Ext.create('Ext.XTemplate', me.tpl);
+        }
+
+        //<debug>
+        // backwards compat alias for overClass/selectedClass
+        // TODO: Consider support for overCls generation Ext.Component config
+        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;
+        }
+        //</debug>
+        
+        me.addEvents(
+            /**
+             * @event beforerefresh
+             * Fires before the view is refreshed
+             * @param {Ext.view.View} this The DataView object
+             */
+            'beforerefresh',
+            /**
+             * @event refresh
+             * Fires when the view is refreshed
+             * @param {Ext.view.View} this The DataView object
+             */
+            'refresh',
+            /**
+             * @event itemupdate
+             * Fires when the node associated with an individual record is updated
+             * @param {Ext.data.Model} record The model instance
+             * @param {Number} index The index of the record/node
+             * @param {HTMLElement} node The node that has just been updated
+             */
+            'itemupdate',
+            /**
+             * @event itemadd
+             * Fires when the nodes associated with an recordset have been added to the underlying store
+             * @param {Array[Ext.data.Model]} records The model instance
+             * @param {Number} index The index at which the set of record/nodes starts
+             * @param {Array[HTMLElement]} node The node that has just been updated
+             */
+            'itemadd',
+            /**
+             * @event itemremove
+             * Fires when the node associated with an individual record is removed
+             * @param {Ext.data.Model} record The model instance
+             * @param {Number} index The index of the record/node
+             */
+            '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) {
+            
+            // Attach the LoadMask to a *Component* so that it can be sensitive to resizing during long loads.
+            // If this DataView is floating, then mask this DataView.
+            // Otherwise, mask its owning Container (or this, if there *is* no owning Container).
+            // LoadMask captures the element upon render.
+            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;
+        }
+
+        // lock the selection model if user
+        // has disabled selection
+        if (me.disableSelection) {
+            me.selModel.locked = true;
+        }
+
+        return me.selModel;
+    },
+
+    /**
+     * Refreshes the view by reloading the data from the store and re-rendering the template.
+     */
+    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);
+    },
+
+    /**
+     * Function which can be overridden to provide custom formatting for each Record that is used by this
+     * DataView's {@link #tpl template} to render each node.
+     * @param {Array/Object} data The raw data object that was used to create the Record.
+     * @param {Number} recordIndex the index number of the Record being prepared for rendering.
+     * @param {Record} record The Record being prepared for rendering.
+     * @return {Array/Object} The formatted data in a format expected by the internal {@link #tpl template}'s overwrite() method.
+     * (either an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'}))
+     */
+    prepareData: function(data, index, record) {
+        if (record) {    
+            Ext.apply(data, record.getAssociatedData());            
+        }
+        return data;
+    },
+    
+    /**
+     * <p>Function which can be overridden which returns the data object passed to this
+     * DataView's {@link #tpl template} to render the whole DataView.</p>
+     * <p>This is usually an Array of data objects, each element of which is processed by an
+     * {@link Ext.XTemplate XTemplate} which uses <tt>'&lt;tpl for="."&gt;'</tt> to iterate over its supplied
+     * data object as an Array. However, <i>named</i> properties may be placed into the data object to
+     * provide non-repeating data such as headings, totals etc.</p>
+     * @param {Array} records An Array of {@link Ext.data.Model}s to be rendered into the DataView.
+     * @param {Number} startIndex the index number of the Record being prepared for rendering.
+     * @return {Array} An Array of data objects to be processed by a repeating XTemplate. May also
+     * contain <i>named</i> properties.
+     */
+    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;
+    },
+
+    // private
+    bufferRender : function(records, index){
+        var div = document.createElement('div');
+        this.tpl.overwrite(div, this.collectData(records, index));
+        return Ext.query(this.getItemSelector(), div);
+    },
+
+    // private
+    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);
+
+            // Maintain selection after update
+            // TODO: Move to approriate event handler.
+            me.selModel.refresh();
+            me.fireEvent('itemupdate', record, index, node);
+        }
+
+    },
+
+    // private
+    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);
+        }    
+    },
+    
+    // private
+    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);
+    },
+
+    /**
+     * Refreshes an individual node's data from the store.
+     * @param {Number} index The item's data index in the store
+     */
+    refreshNode : function(index){
+        this.onUpdate(this.store, this.store.getAt(index));
+    },
+
+    // private
+    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;
+            }
+        }
+    },
+
+    /**
+     * Returns the store associated with this DataView.
+     * @return {Ext.data.Store} The store
+     */
+    getStore : function(){
+        return this.store;
+    },
+
+    /**
+     * Changes the data store bound to this view and refreshes it.
+     * @param {Store} store The store to bind to this view
+     */
+    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;
+        // Bind the store to our selection model
+        me.getSelectionModel().bind(store);
+        
+        if (store) {
+            me.refresh(true);
+        }
+    },
+
+    /**
+     * @private
+     * Calls this.refresh if this.blockRefresh is not true
+     */
+    onDataChanged: function() {
+        if (this.blockRefresh !== true) {
+            this.refresh.apply(this, arguments);
+        }
+    },
+
+    /**
+     * Returns the template node the passed child belongs to, or null if it doesn't belong to one.
+     * @param {HTMLElement} node
+     * @return {HTMLElement} The template node
+     */
+    findItemByChild: function(node){
+        return Ext.fly(node).findParent(this.getItemSelector(), this.getTargetEl());
+    },
+    
+    /**
+     * Returns the template node by the Ext.EventObject or null if it is not found.
+     * @param {Ext.EventObject} e
+     */
+    findTargetByEvent: function(e) {
+        return e.getTarget(this.getItemSelector(), this.getTargetEl());
+    },
+
+
+    /**
+     * Gets the currently selected nodes.
+     * @return {Array} An array of HTMLElements
+     */
+    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;
+    },
+
+    /**
+     * Gets an array of the records from an array of nodes
+     * @param {Array} nodes The nodes to evaluate
+     * @return {Array} records The {@link Ext.data.Model} objects
+     */
+    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;
+    },
+
+    /**
+     * Gets a record from a node
+     * @param {Element/HTMLElement} node The node to evaluate
+     * 
+     * @return {Record} record The {@link Ext.data.Model} object
+     */
+    getRecord: function(node){
+        return this.store.data.getByKey(Ext.getDom(node).viewRecordId);
+    },
+    
+
+    /**
+     * Returns true if the passed node is selected, else false.
+     * @param {HTMLElement/Number/Ext.data.Model} node The node, node index or record to check
+     * @return {Boolean} True if selected, else false
+     */
+    isSelected : function(node) {
+        // TODO: El/Idx/Record
+        var r = this.getRecord(node);
+        return this.selModel.isSelected(r);
+    },
+    
+    /**
+     * Selects a record instance by record instance or index.
+     * @param {Ext.data.Model/Index} records An array of records or an index
+     * @param {Boolean} keepExisting
+     * @param {Boolean} suppressEvent Set to false to not fire a select event
+     */
+    select: function(records, keepExisting, suppressEvent) {
+        this.selModel.select(records, keepExisting, suppressEvent);
+    },
+
+    /**
+     * Deselects a record instance by record instance or index.
+     * @param {Ext.data.Model/Index} records An array of records or an index
+     * @param {Boolean} suppressEvent Set to false to not fire a deselect event
+     */
+    deselect: function(records, suppressEvent) {
+        this.selModel.deselect(records, suppressEvent);
+    },
+
+    /**
+     * Gets a template node.
+     * @param {HTMLElement/String/Number/Ext.data.Model} nodeInfo An HTMLElement template node, index of a template node,
+     * the id of a template node or the record associated with the node.
+     * @return {HTMLElement} The node or null if it wasn't found
+     */
+    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;
+    },
+    
+    /**
+     * @private
+     */
+    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;
+    },
+    
+    /**
+     * Gets a range nodes.
+     * @param {Number} start (optional) The index of the first node in the range
+     * @param {Number} end (optional) The index of the last node in the range
+     * @return {Array} An array of nodes
+     */
+    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;
+    },
+
+    /**
+     * Finds the index of the passed node.
+     * @param {HTMLElement/String/Number/Record} nodeInfo An HTMLElement template node, index of a template node, the id of a template node
+     * or a record associated with a node.
+     * @return {Number} The index of the node or -1
+     */
+    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();
+    },
+
+    // invoked by the selection model to maintain visual UI cues
+    onItemSelect: function(record) {
+        var node = this.getNode(record);
+        Ext.fly(node).addCls(this.selectedItemCls);
+    },
+
+    // invoked by the selection model to maintain visual UI cues
+    onItemDeselect: function(record) {
+        var node = this.getNode(record);
+        Ext.fly(node).removeCls(this.selectedItemCls);
+    },
+    
+    getItemSelector: function() {
+        return this.itemSelector;
+    }
+}, function() {
+    // all of this information is available directly
+    // from the SelectionModel itself, the only added methods
+    // to DataView regarding selection will perform some transformation/lookup
+    // between HTMLElement/Nodes to records and vice versa.
+    Ext.deprecate('extjs', '4.0', function() {
+        Ext.view.AbstractView.override({
+            /**
+             * @cfg {Boolean} multiSelect
+             * True to allow selection of more than one item at a time, false to allow selection of only a single item
+             * at a time or no selection at all, depending on the value of {@link #singleSelect} (defaults to false).
+             */
+            /**
+             * @cfg {Boolean} singleSelect
+             * True to allow selection of exactly one item at a time, false to allow no selection at all (defaults to false).
+             * Note that if {@link #multiSelect} = true, this value will be ignored.
+             */
+            /**
+             * @cfg {Boolean} simpleSelect
+             * True to enable multiselection by clicking on multiple items without requiring the user to hold Shift or Ctrl,
+             * false to force the user to hold Ctrl or Shift to select more than on item (defaults to false).
+             */
+            
+            /**
+             * Gets the number of selected nodes.
+             * @return {Number} The node count
+             */
+            getSelectionCount : function(){
+                console.warn("DataView: getSelectionCount will be removed, please interact with the Ext.selection.DataViewModel");
+                return this.selModel.getSelection().length;
+            },
+        
+            /**
+             * Gets an array of the selected records
+             * @return {Array} An array of {@link Ext.data.Model} objects
+             */
+            getSelectedRecords : function(){
+                console.warn("DataView: getSelectedRecords will be removed, please interact with the Ext.selection.DataViewModel");
+                return this.selModel.getSelection();
+            },
+    
+            select: function(records, keepExisting, supressEvents) {
+                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() {
+                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();
+            }
+        });    
+    });
+});
+
+/**
+ * @class Ext.Action
+ * <p>An Action is a piece of reusable functionality that can be abstracted out of any particular component so that it
+ * can be usefully shared among multiple components.  Actions let you share handlers, configuration options and UI
+ * updates across any components that support the Action interface (primarily {@link Ext.toolbar.Toolbar}, {@link Ext.button.Button}
+ * and {@link Ext.menu.Menu} components).</p>
+ * <p>Use a single Action instance as the config object for any number of UI Components which share the same configuration. The
+ * Action not only supplies the configuration, but allows all Components based upon it to have a common set of methods
+ * called at once through a single call to the Action.</p>
+ * <p>Any Component that is to be configured with an Action must also support
+ * the following methods:<ul>
+ * <li><code>setText(string)</code></li>
+ * <li><code>setIconCls(string)</code></li>
+ * <li><code>setDisabled(boolean)</code></li>
+ * <li><code>setVisible(boolean)</code></li>
+ * <li><code>setHandler(function)</code></li></ul>.</p>
+ * <p>This allows the Action to control its associated Components.</p>
+ * Example usage:<br>
+ * <pre><code>
+// Define the shared Action.  Each Component below will have the same
+// display text and icon, and will display the same message on click.
+var action = new Ext.Action({
+    {@link #text}: 'Do something',
+    {@link #handler}: function(){
+        Ext.Msg.alert('Click', 'You did something.');
+    },
+    {@link #iconCls}: 'do-something',
+    {@link #itemId}: 'myAction'
+});
+
+var panel = new Ext.panel.Panel({
+    title: 'Actions',
+    width: 500,
+    height: 300,
+    tbar: [
+        // Add the Action directly to a toolbar as a menu button
+        action,
+        {
+            text: 'Action Menu',
+            // Add the Action to a menu as a text item
+            menu: [action]
+        }
+    ],
+    items: [
+        // Add the Action to the panel body as a standard button
+        new Ext.button.Button(action)
+    ],
+    renderTo: Ext.getBody()
+});
+
+// Change the text for all components using the Action
+action.setText('Something else');
+
+// Reference an Action through a container using the itemId
+var btn = panel.getComponent('myAction');
+var aRef = btn.baseAction;
+aRef.setText('New text');
+</code></pre>
+ * @constructor
+ * @param {Object} config The configuration options
+ */
+Ext.define('Ext.Action', {
+
+    /* Begin Definitions */
+
+    /* End Definitions */
+
+    /**
+     * @cfg {String} text The text to set for all components configured by this Action (defaults to '').
+     */
+    /**
+     * @cfg {String} iconCls
+     * The CSS class selector that specifies a background image to be used as the header icon for
+     * all components configured by this Action (defaults to '').
+     * <p>An example of specifying a custom icon class would be something like:
+     * </p><pre><code>
+// specify the property in the config for the class:
+     ...
+     iconCls: 'do-something'
+
+// css class that specifies background image to be used as the icon image:
+.do-something { background-image: url(../images/my-icon.gif) 0 6px no-repeat !important; }
+</code></pre>
+     */
+    /**
+     * @cfg {Boolean} disabled True to disable all components configured by this Action, false to enable them (defaults to false).
+     */
+    /**
+     * @cfg {Boolean} hidden True to hide all components configured by this Action, false to show them (defaults to false).
+     */
+    /**
+     * @cfg {Function} handler The function that will be invoked by each component tied to this Action
+     * when the component's primary event is triggered (defaults to undefined).
+     */
+    /**
+     * @cfg {String} itemId
+     * See {@link Ext.Component}.{@link Ext.Component#itemId itemId}.
+     */
+    /**
+     * @cfg {Object} scope The scope (<code><b>this</b></code> reference) in which the
+     * <code>{@link #handler}</code> is executed. Defaults to the browser window.
+     */
+
+    constructor : function(config){
+        this.initialConfig = config;
+        this.itemId = config.itemId = (config.itemId || config.id || Ext.id());
+        this.items = [];
+    },
+
+    // private
+    isAction : true,
+
+    /**
+     * Sets the text to be displayed by all components configured by this Action.
+     * @param {String} text The text to display
+     */
+    setText : function(text){
+        this.initialConfig.text = text;
+        this.callEach('setText', [text]);
+    },
+
+    /**
+     * Gets the text currently displayed by all components configured by this Action.
+     */
+    getText : function(){
+        return this.initialConfig.text;
+    },
+
+    /**
+     * Sets the icon CSS class for all components configured by this Action.  The class should supply
+     * a background image that will be used as the icon image.
+     * @param {String} cls The CSS class supplying the icon image
+     */
+    setIconCls : function(cls){
+        this.initialConfig.iconCls = cls;
+        this.callEach('setIconCls', [cls]);
+    },
+
+    /**
+     * Gets the icon CSS class currently used by all components configured by this Action.
+     */
+    getIconCls : function(){
+        return this.initialConfig.iconCls;
+    },
+
+    /**
+     * Sets the disabled state of all components configured by this Action.  Shortcut method
+     * for {@link #enable} and {@link #disable}.
+     * @param {Boolean} disabled True to disable the component, false to enable it
+     */
+    setDisabled : function(v){
+        this.initialConfig.disabled = v;
+        this.callEach('setDisabled', [v]);
+    },
+
+    /**
+     * Enables all components configured by this Action.
+     */
+    enable : function(){
+        this.setDisabled(false);
+    },
+
+    /**
+     * Disables all components configured by this Action.
+     */
+    disable : function(){
+        this.setDisabled(true);
+    },
+
+    /**
+     * Returns true if the components using this Action are currently disabled, else returns false.  
+     */
+    isDisabled : function(){
+        return this.initialConfig.disabled;
+    },
+
+    /**
+     * Sets the hidden state of all components configured by this Action.  Shortcut method
+     * for <code>{@link #hide}</code> and <code>{@link #show}</code>.
+     * @param {Boolean} hidden True to hide the component, false to show it
+     */
+    setHidden : function(v){
+        this.initialConfig.hidden = v;
+        this.callEach('setVisible', [!v]);
+    },
+
+    /**
+     * Shows all components configured by this Action.
+     */
+    show : function(){
+        this.setHidden(false);
+    },
+
+    /**
+     * Hides all components configured by this Action.
+     */
+    hide : function(){
+        this.setHidden(true);
+    },
+
+    /**
+     * Returns true if the components configured by this Action are currently hidden, else returns false.
+     */
+    isHidden : function(){
+        return this.initialConfig.hidden;
+    },
+
+    /**
+     * Sets the function that will be called by each Component using this action when its primary event is triggered.
+     * @param {Function} fn The function that will be invoked by the action's components.  The function
+     * will be called with no arguments.
+     * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component firing the event.
+     */
+    setHandler : function(fn, scope){
+        this.initialConfig.handler = fn;
+        this.initialConfig.scope = scope;
+        this.callEach('setHandler', [fn, scope]);
+    },
+
+    /**
+     * Executes the specified function once for each Component currently tied to this Action.  The function passed
+     * in should accept a single argument that will be an object that supports the basic Action config/method interface.
+     * @param {Function} fn The function to execute for each component
+     * @param {Object} scope The scope (<code>this</code> reference) in which the function is executed.  Defaults to the Component.
+     */
+    each : function(fn, scope){
+        Ext.each(this.items, fn, scope);
+    },
+
+    // private
+    callEach : function(fnName, args){
+        var cs = this.items;
+        for(var i = 0, len = cs.length; i < len; i++){
+            cs[i][fnName].apply(cs[i], args);
+        }
+    },
+
+    // private
+    addComponent : function(comp){
+        this.items.push(comp);
+        comp.on('destroy', this.removeComponent, this);
+    },
+
+    // private
+    removeComponent : function(comp){
+        this.items.remove(comp);
+    },
+
+    /**
+     * Executes this Action manually using the handler function specified in the original config object
+     * or the handler function set with <code>{@link #setHandler}</code>.  Any arguments passed to this
+     * function will be passed on to the handler function.
+     * @param {Mixed} arg1 (optional) Variable number of arguments passed to the handler function
+     * @param {Mixed} arg2 (optional)
+     * @param {Mixed} etc... (optional)
+     */
+    execute : function(){
+        this.initialConfig.handler.apply(this.initialConfig.scope || Ext.global, arguments);
+    }
+});
+
+/**
+ * Component layout for editors
+ * @class Ext.layout.component.Editor
+ * @extends Ext.layout.component.Component
+ * @private
+ */
+Ext.define('Ext.layout.component.Editor', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.editor'],
+
+    extend: 'Ext.layout.component.Component',
+
+    /* End Definitions */
+
+    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;
+        }
+    }
+});
+/**
+ * @class Ext.Editor
+ * @extends Ext.Component
+ *
+ * <p>
+ * The Editor class is used to provide inline editing for elements on the page. The editor
+ * is backed by a {@link Ext.form.field.Field} that will be displayed to edit the underlying content.
+ * The editor is a floating Component, when the editor is shown it is automatically aligned to
+ * display over the top of the bound element it is editing. The Editor contains several options
+ * for how to handle key presses:
+ * <ul>
+ * <li>{@link #completeOnEnter}</li>
+ * <li>{@link #cancelOnEsc}</li>
+ * <li>{@link #swallowKeys}</li>
+ * </ul>
+ * It also has options for how to use the value once the editor has been activated:
+ * <ul>
+ * <li>{@link #revertInvalid}</li>
+ * <li>{@link #ignoreNoChange}</li>
+ * <li>{@link #updateEl}</li>
+ * </ul>
+ * Sample usage:
+ * </p>
+ * <pre><code>
+var editor = new Ext.Editor({
+    updateEl: true, // update the innerHTML of the bound element when editing completes
+    field: {
+        xtype: 'textfield'
+    }
+});
+var el = Ext.get('my-text'); // The element to 'edit'
+editor.startEdit(el); // The value of the field will be taken as the innerHTML of the element.
+ * </code></pre>
+ * {@img Ext.Editor/Ext.Editor.png Ext.Editor component}
+ *
+ * @constructor
+ * Create a new Editor
+ * @param {Object} config The config object
+ * @xtype editor
+ */
+Ext.define('Ext.Editor', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.Component',
+
+    alias: 'widget.editor',
+
+    requires: ['Ext.layout.component.Editor'],
+
+    /* End Definitions */
+
+   componentLayout: 'editor',
+
+    /**
+    * @cfg {Ext.form.field.Field} field
+    * The Field object (or descendant) or config object for field
+    */
+
+    /**
+     * @cfg {Boolean} allowBlur
+     * True to {@link #completeEdit complete the editing process} if in edit mode when the
+     * field is blurred. Defaults to <tt>true</tt>.
+     */
+    allowBlur: true,
+
+    /**
+     * @cfg {Boolean/Object} autoSize
+     * True for the editor to automatically adopt the size of the underlying field. Otherwise, an object
+     * can be passed to indicate where to get each dimension. The available properties are 'boundEl' and
+     * 'field'. If a dimension is not specified, it will use the underlying height/width specified on
+     * the editor object.
+     * Examples:
+     * <pre><code>
+autoSize: true // The editor will be sized to the height/width of the field
+
+height: 21,
+autoSize: {
+    width: 'boundEl' // The width will be determined by the width of the boundEl, the height from the editor (21)
+}
+
+autoSize: {
+    width: 'field', // Width from the field
+    height: 'boundEl' // Height from the boundEl
+}
+     * </pre></code>
+     */
+
+    /**
+     * @cfg {Boolean} revertInvalid
+     * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
+     * validation fails (defaults to true)
+     */
+    revertInvalid: true,
+
+    /**
+     * @cfg {Boolean} ignoreNoChange
+     * True to skip the edit completion process (no save, no events fired) if the user completes an edit and
+     * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
+     * will never be ignored.
+     */
+
+    /**
+     * @cfg {Boolean} hideEl
+     * False to keep the bound element visible while the editor is displayed (defaults to true)
+     */
+
+    /**
+     * @cfg {Mixed} value
+     * The data value of the underlying field (defaults to "")
+     */
+    value : '',
+
+    /**
+     * @cfg {String} alignment
+     * The position to align to (see {@link Ext.core.Element#alignTo} for more details, defaults to "c-c?").
+     */
+    alignment: 'c-c?',
+
+    /**
+     * @cfg {Array} offsets
+     * The offsets to use when aligning (see {@link Ext.core.Element#alignTo} for more details. Defaults to <tt>[0, 0]</tt>.
+     */
+    offsets: [0, 0],
+
+    /**
+     * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
+     * for bottom-right shadow (defaults to "frame")
+     */
+    shadow : 'frame',
+
+    /**
+     * @cfg {Boolean} constrain True to constrain the editor to the viewport
+     */
+    constrain : false,
+
+    /**
+     * @cfg {Boolean} swallowKeys Handle the keydown/keypress events so they don't propagate (defaults to true)
+     */
+    swallowKeys : true,
+
+    /**
+     * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed. Defaults to <tt>true</tt>.
+     */
+    completeOnEnter : true,
+
+    /**
+     * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed. Defaults to <tt>true</tt>.
+     */
+    cancelOnEsc : true,
+
+    /**
+     * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
+     */
+    updateEl : false,
+
+    /**
+     * @cfg {Mixed} parentEl An element to render to. Defaults to the <tt>document.body</tt>.
+     */
+
+    // private overrides
+    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,
+                // slight delay to avoid race condition with startEdits (e.g. grid view refresh)
+                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(
+            /**
+             * @event beforestartedit
+             * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
+             * false from the handler of this event.
+             * @param {Ext.Editor} this
+             * @param {Ext.core.Element} boundEl The underlying element bound to this editor
+             * @param {Mixed} value The field value being set
+             */
+            'beforestartedit',
+            /**
+             * @event startedit
+             * Fires when this editor is displayed
+             * @param {Ext.Editor} this
+             * @param {Ext.core.Element} boundEl The underlying element bound to this editor
+             * @param {Mixed} value The starting field value
+             */
+            'startedit',
+            /**
+             * @event beforecomplete
+             * Fires after a change has been made to the field, but before the change is reflected in the underlying
+             * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
+             * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
+             * event will not fire since no edit actually occurred.
+             * @param {Editor} this
+             * @param {Mixed} value The current field value
+             * @param {Mixed} startValue The original field value
+             */
+            'beforecomplete',
+            /**
+             * @event complete
+             * Fires after editing is complete and any changed value has been written to the underlying field.
+             * @param {Ext.Editor} this
+             * @param {Mixed} value The current field value
+             * @param {Mixed} startValue The original field value
+             */
+            'complete',
+            /**
+             * @event canceledit
+             * Fires after editing has been canceled and the editor's value has been reset.
+             * @param {Ext.Editor} this
+             * @param {Mixed} value The user-entered field value that was discarded
+             * @param {Mixed} startValue The original field value that was set back into the editor after cancel
+             */
+            'canceledit',
+            /**
+             * @event specialkey
+             * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
+             * {@link Ext.EventObject#getKey} to determine which key was pressed.
+             * @param {Ext.Editor} this
+             * @param {Ext.form.field.Field} The field attached to this editor
+             * @param {Ext.EventObject} event The event object
+             */
+            'specialkey'
+        );
+    },
+
+    // private
+    onAutoSize: function(){
+        this.doComponentLayout();
+    },
+
+    // private
+    onRender : function(ct, position) {
+        var me = this,
+            field = me.field;
+
+        me.callParent(arguments);
+
+        field.render(me.el);
+        //field.hide();
+        // Ensure the field doesn't get submitted as part of any form
+        field.inputEl.dom.name = '';
+        if (me.swallowKeys) {
+            field.inputEl.swallowEvent([
+                'keypress', // *** Opera
+                'keydown'   // *** all other browsers
+            ]);
+        }
+    },
+
+    // private
+    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();
+            // Must defer this slightly to prevent exiting edit mode before the field's own
+            // key nav can handle the enter key, e.g. selecting an item in a combobox list
+            Ext.defer(function() {
+                if (complete) {
+                    me.completeEdit();
+                } else {
+                    me.cancelEdit();
+                }
+                if (field.triggerBlur) {
+                    field.triggerBlur();
+                }
+            }, 10);
+        }
+
+        this.fireEvent('specialkey', this, field, event);
+    },
+
+    /**
+     * Starts the editing process and shows the editor.
+     * @param {Mixed} el The element to edit
+     * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
+      * to the innerHTML of el.
+     */
+    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;
+        }
+    },
+
+    /**
+     * Realigns the editor to the bound field based on the current alignment config value.
+     * @param {Boolean} autoSize (optional) True to size the field to the dimensions of the bound element.
+     */
+    realign : function(autoSize) {
+        var me = this;
+        if (autoSize === true) {
+            me.doComponentLayout();
+        }
+        me.alignTo(me.boundEl, me.alignment, me.offsets);
+    },
+
+    /**
+     * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
+     * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
+     */
+    completeEdit : function(remainVisible) {
+        var me = this,
+            field = me.field,
+            value;
+
+        if (!me.editing) {
+            return;
+        }
+
+        // Assert combo values first
+        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) {
+            // Grab the value again, may have changed in beforecomplete
+            value = me.getValue();
+            if (me.updateEl && me.boundEl) {
+                me.boundEl.update(value);
+            }
+            me.hideEdit(remainVisible);
+            me.fireEvent('complete', me, value, me.startValue);
+        }
+    },
+
+    // private
+    onShow : function() {
+        var me = this;
+
+        me.callParent(arguments);
+        if (me.hideEl !== false) {
+            me.boundEl.hide();
+        }
+        me.fireEvent("startedit", me.boundEl, me.startValue);
+    },
+
+    /**
+     * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
+     * reverted to the original starting value.
+     * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
+     * cancel (defaults to false)
+     */
+    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);
+        }
+    },
+
+    // private
+    hideEdit: function(remainVisible) {
+        if (remainVisible !== true) {
+            this.editing = false;
+            this.hide();
+        }
+    },
+
+    // private
+    onBlur : function() {
+        var me = this;
+
+        // selectSameEditor flag allows the same editor to be started without onBlur firing on itself
+        if(me.allowBlur === true && me.editing && me.selectSameEditor !== true) {
+            me.completeEdit();
+        }
+    },
+
+    // private
+    onHide : function() {
+        var me = this,
+            field = me.field;
+
+        if (me.editing) {
+            me.completeEdit();
+            return;
+        }
+        field.blur();
+        if (field.collapse) {
+            field.collapse();
+        }
+
+        //field.hide();
+        if (me.hideEl !== false) {
+            me.boundEl.show();
+        }
+        me.callParent(arguments);
+    },
+
+    /**
+     * Sets the data value of the editor
+     * @param {Mixed} value Any valid value supported by the underlying field
+     */
+    setValue : function(value) {
+        this.field.setValue(value);
+    },
+
+    /**
+     * Gets the data value of the editor
+     * @return {Mixed} The data 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);
+    }
+});
+/**
+ * @class Ext.Img
+ * @extends Ext.Component
+ *
+ * Simple helper class for easily creating image components. This simply renders an image tag to the DOM
+ * with the configured src.
+ *
+ * {@img Ext.Img/Ext.Img.png Ext.Img component}
+ *
+ * ## Example usage: 
+ *
+ *     var changingImage = Ext.create('Ext.Img', {
+ *         src: 'http://www.sencha.com/img/20110215-feat-html5.png',
+ *         renderTo: Ext.getBody()
+ *     });
+ *      
+ *     // change the src of the image programmatically
+ *     changingImage.setSrc('http://www.sencha.com/img/20110215-feat-perf.png');
+*/
+Ext.define('Ext.Img', {
+    extend: 'Ext.Component',
+    alias: ['widget.image', 'widget.imagecomponent'],
+    /** @cfg {String} src The image src */
+    src: '',
+
+    getElConfig: function() {
+        return {
+            tag: 'img',
+            src: this.src
+        };
+    },
+    
+    /**
+     * Updates the {@link #src} of the image
+     */
+    setSrc: function(src) {
+        var me = this,
+            img = me.el;
+        me.src = src;
+        if (img) {
+            img.dom.src = src;
+        }
+    }
+});
+
+/**
+ * @class Ext.Layer
+ * @extends Ext.core.Element
+ * An extended {@link Ext.core.Element} object that supports a shadow and shim, constrain to viewport and
+ * automatic maintaining of shadow/shim positions.
+ * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
+ * @cfg {String/Boolean} shadow True to automatically create an {@link Ext.Shadow}, or a string indicating the
+ * shadow's display {@link Ext.Shadow#mode}. False to disable the shadow. (defaults to false)
+ * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: 'div', cls: 'x-layer'}).
+ * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
+ * @cfg {String} cls CSS class to add to the element
+ * @cfg {Number} zindex Starting z-index (defaults to 11000)
+ * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 4)
+ * @cfg {Boolean} useDisplay
+ * Defaults to use css offsets to hide the Layer. Specify <tt>true</tt>
+ * to use css style <tt>'display:none;'</tt> to hide the Layer.
+ * @cfg {String} visibilityCls The CSS class name to add in order to hide this Layer if this layer
+ * is configured with <code>{@link #hideMode}: 'asclass'</code>
+ * @cfg {String} hideMode
+ * A String which specifies how this Layer will be hidden.
+ * Values may be<div class="mdetail-params"><ul>
+ * <li><code>'display'</code> : The Component will be hidden using the <code>display: none</code> style.</li>
+ * <li><code>'visibility'</code> : The Component will be hidden using the <code>visibility: hidden</code> style.</li>
+ * <li><code>'offsets'</code> : The Component will be hidden by absolutely positioning it out of the visible area of the document. This
+ * is useful when a hidden Component must maintain measurable dimensions. Hiding using <code>display</code> results
+ * in a Component having zero dimensions.</li></ul></div>
+ * @constructor
+ * @param {Object} config An object with config options.
+ * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
+ */
+Ext.define('Ext.Layer', {
+    uses: ['Ext.Shadow'],
+
+    // shims are shared among layer to keep from having 100 iframes
+    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;
+
+        // Allow Components to pass their hide mode down to the Layer if they are floating.
+        // Otherwise, allow useDisplay to override the default hiding method which is visibility.
+        // TODO: Have ExtJS's Element implement visibilityMode by using classes as in Mobile.
+        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);
+            }
+        }
+    },
+
+    /**
+     * @private
+     * <p>Synchronize this Layer's associated elements, the shadow, and possibly the shim.</p>
+     * <p>This code can execute repeatedly in milliseconds,
+     * eg: dragging a Component configured liveDrag: true, or which has no ghost method
+     * so code size was sacrificed for efficiency (e.g. no getBox/setBox, no XY calls)</p>
+     * @param {Boolean} doShow Pass true to ensure that the shadow is shown.
+     */
+    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) {
+                    // TODO: Determine how the shims zIndex is above the layer zIndex at this point
+                    shimIndex = shim.getStyle('z-index');
+                    if (shimIndex > me.zindex) {
+                        me.shim.setStyle('z-index', me.zindex - 2);
+                    }
+                    shim.show();
+                    // fit the shim behind the shadow, so it is shimmed too
+                    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) {
+                // TODO: Determine how the shims zIndex is above the layer zIndex at this point
+                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();
+    },
+
+    // private
+    beginUpdate: function() {
+        this.updating = true;
+    },
+
+    // private
+    endUpdate: function() {
+        this.updating = false;
+        this.sync(true);
+    },
+
+    // private
+    hideUnders: function() {
+        if (this.shadow) {
+            this.shadow.hide();
+        }
+        this.hideShim();
+    },
+
+    // private
+    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; // only move it if it needs it
+            // first validate right/bottom
+            if ((x + w) > vw + s.left) {
+                x = vw - w - so;
+                moved = true;
+            }
+            if ((y + h) > vh + s.top) {
+                y = vh - h - so;
+                moved = true;
+            }
+            // then make sure top/left isn't negative
+            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;
+    },
+
+    // overridden Element method
+    setVisible: function(visible, animate, duration, callback, easing) {
+        var me = this,
+            cb;
+
+        // post operation processing
+        cb = function() {
+            if (visible) {
+                me.sync(true);
+            }
+            if (callback) {
+                callback();
+            }
+        };
+
+        // Hide shadow and shim if hiding
+        if (!visible) {
+            this.hideUnders(true);
+        }
+        this.callParent([visible, animate, duration, callback, easing]);
+        if (!animate) {
+            cb();
+        }
+        return this;
+    },
+
+    // private
+    beforeFx: function() {
+        this.beforeAction();
+        return this.callParent(arguments);
+    },
+
+    // private
+    afterFx: function() {
+        this.callParent(arguments);
+        this.sync(this.isVisible());
+    },
+
+    // private
+    beforeAction: function() {
+        if (!this.updating && this.shadow) {
+            this.shadow.hide();
+        }
+    },
+
+    // overridden Element method
+    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 will restore shadow state and call the passed callback
+        callback = this.createCB(callback);
+
+        this.fixDisplay();
+        this.beforeAction();
+        this.callParent([xy, animate, duration, callback, easing]);
+        if (!animate) {
+            callback();
+        }
+        return this;
+    },
+
+    // private
+    createCB: function(callback) {
+        var me = this,
+            showShadow = me.shadow && me.shadow.isVisible();
+
+        return function() {
+            me.constrainXY();
+            me.sync(showShadow);
+            if (callback) {
+                callback();
+            }
+        };
+    },
+
+    // overridden Element method
+    setX: function(x, animate, duration, callback, easing) {
+        this.setXY([x, this.getY()], animate, duration, callback, easing);
+        return this;
+    },
+
+    // overridden Element method
+    setY: function(y, animate, duration, callback, easing) {
+        this.setXY([this.getX(), y], animate, duration, callback, easing);
+        return this;
+    },
+
+    // overridden Element method
+    setSize: function(w, h, animate, duration, callback, easing) {
+        // Callback will restore shadow state and call the passed callback
+        callback = this.createCB(callback);
+
+        this.beforeAction();
+        this.callParent([w, h, animate, duration, callback, easing]);
+        if (!animate) {
+            callback();
+        }
+        return this;
+    },
+
+    // overridden Element method
+    setWidth: function(w, animate, duration, callback, easing) {
+        // Callback will restore shadow state and call the passed callback
+        callback = this.createCB(callback);
+
+        this.beforeAction();
+        this.callParent([w, animate, duration, callback, easing]);
+        if (!animate) {
+            callback();
+        }
+        return this;
+    },
+
+    // overridden Element method
+    setHeight: function(h, animate, duration, callback, easing) {
+        // Callback will restore shadow state and call the passed callback
+        callback = this.createCB(callback);
+
+        this.beforeAction();
+        this.callParent([h, animate, duration, callback, easing]);
+        if (!animate) {
+            callback();
+        }
+        return this;
+    },
+
+    // overridden Element method
+    setBounds: function(x, y, width, height, animate, duration, callback, easing) {
+        // Callback will restore shadow state and call the passed callback
+        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;
+    },
+
+    /**
+     * <p>Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
+     * incremented depending upon the presence of a shim or a shadow in so that it always shows above those two associated elements.</p>
+     * <p>Any shim, will be assigned the passed z-index. A shadow will be assigned the next highet z-index, and the Layer's
+     * element will receive the highest  z-index.
+     * @param {Number} zindex The new z-index to set
+     * @return {this} The Layer
+     */
+    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;
+    }
+});
+
+/**
+ * @class Ext.layout.component.ProgressBar
+ * @extends Ext.layout.component.Component
+ * @private
+ */
+
+Ext.define('Ext.layout.component.ProgressBar', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.progressbar'],
+
+    extend: 'Ext.layout.component.Component',
+
+    /* End Definitions */
+
+    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);
+    }
+});
+/**
+ * @class Ext.ProgressBar
+ * @extends Ext.Component
+ * <p>An updateable progress bar component.  The progress bar supports two different modes: manual and automatic.</p>
+ * <p>In manual mode, you are responsible for showing, updating (via {@link #updateProgress}) and clearing the
+ * progress bar as needed from your own code.  This method is most appropriate when you want to show progress
+ * throughout an operation that has predictable points of interest at which you can update the control.</p>
+ * <p>In automatic mode, you simply call {@link #wait} and let the progress bar run indefinitely, only clearing it
+ * once the operation is complete.  You can optionally have the progress bar wait for a specific amount of time
+ * and then clear itself.  Automatic mode is most appropriate for timed operations or asynchronous operations in
+ * which you have no need for indicating intermediate progress.</p>
+ * {@img Ext.ProgressBar/Ext.ProgressBar.png Ext.ProgressBar component}
+ * Example Usage:
+     var p = Ext.create('Ext.ProgressBar', {
+       renderTo: Ext.getBody(),
+       width: 300
+    });
+
+    //Wait for 5 seconds, then update the status el (progress bar will auto-reset)
+    p.wait({
+       interval: 500, //bar will move fast!
+       duration: 50000,
+       increment: 15,
+       text: 'Updating...',
+       scope: this,
+       fn: function(){
+          p.updateText('Done!');
+       }
+    });
+ * @cfg {Float} value A floating point value between 0 and 1 (e.g., .5, defaults to 0)
+ * @cfg {String} text The progress bar text (defaults to '')
+ * @cfg {Mixed} textEl The element to render the progress text to (defaults to the progress
+ * bar's internal text element)
+ * @cfg {String} id The progress bar element's id (defaults to an auto-generated id)
+ * @xtype progressbar
+ */
+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'],
+   /**
+    * @cfg {String} baseCls
+    * The base CSS class to apply to the progress bar's wrapper element (defaults to 'x-progress')
+    */
+    baseCls: Ext.baseCSSPrefix + 'progress',
+
+    config: {
+        /**
+        * @cfg {Boolean} animate
+        * True to animate the progress bar during transitions (defaults to false)
+        */
+        animate: false,
+
+        /**
+         * @cfg {String} text The text shown in the progress bar (defaults to '')
+         */
+        text: ''
+    },
+
+    // private
+    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',
+
+    // private
+    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(
+            /**
+             * @event update
+             * Fires after each update interval
+             * @param {Ext.ProgressBar} this
+             * @param {Number} The current progress value
+             * @param {String} The current progress text
+             */
+            "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);
+        }
+    },
+
+    /**
+     * Updates the progress bar value, and optionally its text.  If the text argument is not specified,
+     * any existing text value will be unchanged.  To blank out existing text, pass ''.  Note that even
+     * if the progress bar value exceeds 1, it will never automatically reset -- you are responsible for
+     * determining when the progress is complete and calling {@link #reset} to clear and/or hide the control.
+     * @param {Float} value (optional) A floating point value between 0 and 1 (e.g., .5, defaults to 0)
+     * @param {String} text (optional) The string to display in the progress text element (defaults to '')
+     * @param {Boolean} animate (optional) Whether to animate the transition of the progress bar. If this value is
+     * not specified, the default for the class is used (default to false)
+     * @return {Ext.ProgressBar} this
+     */
+    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;
+    },
+
+    /**
+     * Updates the progress bar text.  If specified, textEl will be updated, otherwise the progress
+     * bar itself will display the updated text.
+     * @param {String} text (optional) The string to display in the progress text element (defaults to '')
+     * @return {Ext.ProgressBar} this
+     */
+    updateText: function(text) {
+        this.text = text;
+        if (this.rendered) {
+            this.textEl.update(this.text);
+        }
+        return this;
+    },
+
+    applyText : function(text) {
+        this.updateText(text);
+    },
+
+    /**
+         * Initiates an auto-updating progress bar.  A duration can be specified, in which case the progress
+         * bar will automatically reset after a fixed amount of time and optionally call a callback function
+         * if specified.  If no duration is passed in, then the progress bar will run indefinitely and must
+         * be manually cleared by calling {@link #reset}.  The wait method accepts a config object with
+         * the following properties:
+         * <pre>
+    Property   Type          Description
+    ---------- ------------  ----------------------------------------------------------------------
+    duration   Number        The length of time in milliseconds that the progress bar should
+                             run before resetting itself (defaults to undefined, in which case it
+                             will run indefinitely until reset is called)
+    interval   Number        The length of time in milliseconds between each progress update
+                             (defaults to 1000 ms)
+    animate    Boolean       Whether to animate the transition of the progress bar. If this value is
+                             not specified, the default for the class is used.
+    increment  Number        The number of progress update segments to display within the progress
+                             bar (defaults to 10).  If the bar reaches the end and is still
+                             updating, it will automatically wrap back to the beginning.
+    text       String        Optional text to display in the progress bar element (defaults to '').
+    fn         Function      A callback function to execute after the progress bar finishes auto-
+                             updating.  The function will be called with no arguments.  This function
+                             will be ignored if duration is not specified since in that case the
+                             progress bar can only be stopped programmatically, so any required function
+                             should be called by the same code after it resets the progress bar.
+    scope      Object        The scope that is passed to the callback function (only applies when
+                             duration and fn are both passed).
+    </pre>
+             *
+             * Example usage:
+             * <pre><code>
+    var p = new Ext.ProgressBar({
+       renderTo: 'my-el'
+    });
+
+    //Wait for 5 seconds, then update the status el (progress bar will auto-reset)
+    var p = Ext.create('Ext.ProgressBar', {
+       renderTo: Ext.getBody(),
+       width: 300
+    });
+
+    //Wait for 5 seconds, then update the status el (progress bar will auto-reset)
+    p.wait({
+       interval: 500, //bar will move fast!
+       duration: 50000,
+       increment: 15,
+       text: 'Updating...',
+       scope: this,
+       fn: function(){
+          p.updateText('Done!');
+       }
+    });
+
+    //Or update indefinitely until some async action completes, then reset manually
+    p.wait();
+    myAction.on('complete', function(){
+        p.reset();
+        p.updateText('Done!');
+    });
+    </code></pre>
+         * @param {Object} config (optional) Configuration options
+         * @return {Ext.ProgressBar} this
+         */
+    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;
+    },
+
+    /**
+     * Returns true if the progress bar is currently in a {@link #wait} operation
+     * @return {Boolean} True if waiting, else false
+     */
+    isWaiting: function(){
+        return this.waitTimer !== null;
+    },
+
+    /**
+     * Resets the progress bar value to 0 and text to empty string.  If hide = true, the progress
+     * bar will also be hidden (using the {@link #hideMode} property internally).
+     * @param {Boolean} hide (optional) True to hide the progress bar (defaults to false)
+     * @return {Ext.ProgressBar} this
+     */
+    reset: function(hide){
+        this.updateProgress(0);
+        this.clearTimer();
+        if (hide === true) {
+            this.hide();
+        }
+        return this;
+    },
+
+    // private
+    clearTimer: function(){
+        if (this.waitTimer) {
+            this.waitTimer.onStop = null; //prevent recursion
+            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();
+    }
+});
+
+/**
+ * @class Ext.ShadowPool
+ * @extends Object
+ * Private utility class that manages the internal Shadow cache
+ * @private
+ */
+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 = [];
+    }
+});
+/**
+ * @class Ext.Shadow
+ * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
+ * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
+ * functionality that can also provide the same shadow effect, see the {@link Ext.Layer} class.
+ * @constructor
+ * Create a new Shadow
+ * @param {Object} config The config object
+ */
+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()) {
+            // all this hideous nonsense calculates the various offsets for shadows
+            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;
+    },
+
+    /**
+     * @cfg {String} mode
+     * The shadow display mode.  Supports the following options:<div class="mdetail-params"><ul>
+     * <li><b><tt>sides</tt></b> : Shadow displays on both sides and bottom only</li>
+     * <li><b><tt>frame</tt></b> : Shadow displays equally on all four sides</li>
+     * <li><b><tt>drop</tt></b> : Traditional bottom-right drop shadow</li>
+     * </ul></div>
+     */
+    /**
+     * @cfg {String} offset
+     * The number of pixels to offset the shadow from the element (defaults to <tt>4</tt>)
+     */
+    offset: 4,
+
+    // private
+    defaultMode: "drop",
+
+    /**
+     * Displays the shadow under the target element
+     * @param {Mixed} targetEl The id or element under which the shadow should display
+     */
+    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";
+    },
+
+    /**
+     * Returns true if the shadow is visible, else false
+     */
+    isVisible: function() {
+        return this.el ? true: false;
+    },
+
+    /**
+     * Direct alignment when values are already available. Show must be called at least once before
+     * calling this method to ensure it is initialized.
+     * @param {Number} left The target element left position
+     * @param {Number} top The target element top position
+     * @param {Number} width The target element width
+     * @param {Number} height The target element height
+     */
+    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 {
+
+                // Adjust the 9 point framed element to poke out on the required sides
+                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";
+                }
+            }
+        }
+    },
+
+    /**
+     * Hides this shadow
+     */
+    hide: function() {
+        if (this.el) {
+            this.el.dom.style.display = "none";
+            Ext.ShadowPool.push(this.el);
+            delete this.el;
+        }
+    },
+
+    /**
+     * Adjust the z-index of this shadow
+     * @param {Number} zindex The new z-index
+     */
+    setZIndex: function(z) {
+        this.zIndex = z;
+        if (this.el) {
+            this.el.setStyle("z-index", z);
+        }
+    }
+});
+/**
+ * @class Ext.button.Split
+ * @extends Ext.button.Button
+ * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
+ * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
+ * options to the primary button action, but any custom handler can provide the arrowclick implementation.  
+ * {@img Ext.button.Split/Ext.button.Split.png Ext.button.Split component}
+ * Example usage:
+ * <pre><code>
+// display a dropdown menu:
+    Ext.create('Ext.button.Split', {
+        renderTo: 'button-ct', // the container id
+        text: 'Options',
+        handler: optionsHandler, // handle a click on the button itself
+        menu: new Ext.menu.Menu({
+        items: [
+                // these items will render as dropdown menu items when the arrow is clicked:
+                {text: 'Item 1', handler: item1Handler},
+                {text: 'Item 2', handler: item2Handler}
+        ]
+        })
+    });
+
+// Instead of showing a menu, you provide any type of custom
+// functionality you want when the dropdown arrow is clicked:
+    Ext.create('Ext.button.Split', {
+        renderTo: 'button-ct',
+        text: 'Options',
+        handler: optionsHandler,
+        arrowHandler: myCustomHandler
+    });
+</code></pre>
+ * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
+ * @cfg {String} arrowTooltip The title attribute of the arrow
+ * @constructor
+ * Create a new menu button
+ * @param {Object} config The config object
+ * @xtype splitbutton
+ */
+
+Ext.define('Ext.button.Split', {
+
+    /* Begin Definitions */
+
+    alias: 'widget.splitbutton',
+
+    extend: 'Ext.button.Button',
+    alternateClassName: 'Ext.SplitButton',
+
+    // private
+    arrowCls      : 'split',
+    split         : true,
+
+    // private
+    initComponent : function(){
+        this.callParent();
+        /**
+         * @event arrowclick
+         * Fires when this button's arrow is clicked
+         * @param {MenuButton} this
+         * @param {EventObject} e The click event
+         */
+        this.addEvents("arrowclick");
+    },
+
+     /**
+     * Sets this button's arrow click handler.
+     * @param {Function} handler The function to call when the arrow is clicked
+     * @param {Object} scope (optional) Scope for the function passed above
+     */
+    setArrowHandler : function(handler, scope){
+        this.arrowHandler = handler;
+        this.scope = scope;
+    },
+
+    // private
+    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();
+            }
+        }
+    }
+});
+/**
+ * @class Ext.button.Cycle
+ * @extends Ext.button.Split
+ * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically
+ * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's
+ * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the
+ * button displays the dropdown menu just like a normal SplitButton.  
+ * {@img Ext.button.Cycle/Ext.button.Cycle.png Ext.button.Cycle component}
+ * Example usage:
+ * <pre><code>
+    Ext.create('Ext.button.Cycle', {
+        showText: true,
+        prependText: 'View as ',
+        renderTo: Ext.getBody(),
+        menu: {
+            id: 'view-type-menu',
+            items: [{
+                text:'text only',
+                iconCls:'view-text',
+                checked:true
+            },{
+                text:'HTML',
+                iconCls:'view-html'
+            }]
+        },
+        changeHandler:function(cycleBtn, activeItem){
+            Ext.Msg.alert('Change View', activeItem.text);
+        }
+    });
+</code></pre>
+ * @constructor
+ * Create a new split button
+ * @param {Object} config The config object
+ * @xtype cycle
+ */
+
+Ext.define('Ext.button.Cycle', {
+
+    /* Begin Definitions */
+
+    alias: 'widget.cycle',
+
+    extend: 'Ext.button.Split',
+    alternateClassName: 'Ext.CycleButton',
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Array} items <p>Deprecated as of 4.0. Use the {@link #menu} config instead. All menu items will be created
+     * as {@link Ext.menu.CheckItem CheckItem}s.</p>
+     * <p>An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the
+     * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})
+     */
+    /**
+     * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false).
+     * The Button will show its configured {@link #text} if this. config is omitted.
+     */
+    /**
+     * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the
+     * button's text (only applies when showText = true, defaults to '')
+     */
+    /**
+     * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu
+     * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead
+     * fire the {@link #change} event on active item change.  The changeHandler function will be called with the
+     * following argument list: (SplitButton this, Ext.menu.CheckItem item)
+     */
+    /**
+     * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This
+     * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the 
+     * default behavior of changing the button's icon to match the selected item's icon on change.
+     */
+    /**
+     * @property menu
+     * @type Menu
+     * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.
+     */
+
+    // private
+    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;
+    },
+
+    /**
+     * Sets the button's active menu item.
+     * @param {Ext.menu.CheckItem} item The item to activate
+     * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * Gets the currently active menu item.
+     * @return {Ext.menu.CheckItem} The active item
+     */
+    getActiveItem: function() {
+        return this.activeItem;
+    },
+
+    // private
+    initComponent: function() {
+        var me = this,
+            checked = 0,
+            items;
+
+        me.addEvents(
+            /**
+             * @event change
+             * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function
+             * is set on this CycleButton, it will be called instead on active item change and this change event will
+             * not be fired.
+             * @param {Ext.button.Cycle} this
+             * @param {Ext.menu.CheckItem} item The menu item that was selected
+             */
+            "change"
+        );
+
+        if (me.changeHandler) {
+            me.on('change', me.changeHandler, me.scope || me);
+            delete me.changeHandler;
+        }
+
+        // Allow them to specify a menu config which is a standard Button config.
+        // Remove direct use of "items" in 5.0.
+        items = (me.menu.items||[]).concat(me.items||[]);
+        me.menu = Ext.applyIf({
+            cls: Ext.baseCSSPrefix + 'cycle-menu',
+            items: []
+        }, me.menu);
+
+        // Convert all items to CheckItems
+        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 configured with a fixed width, the cycling will center a different child item's text each click. Prevent this.
+        if (me.width && me.showText) {
+            me.addCls(Ext.baseCSSPrefix + 'cycle-fixed-width');
+        }
+    },
+
+    // private
+    checkHandler: function(item, pressed) {
+        if (pressed) {
+            this.setActiveItem(item);
+        }
+    },
+
+    /**
+     * This is normally called internally on button click, but can be called externally to advance the button's
+     * active item programmatically to the next one in the menu.  If the current item is the last one in the menu
+     * the active item will be set to the first item in the menu.
+     */
+    toggleSelected: function() {
+        var me = this,
+            m = me.menu,
+            checkItem;
+
+        checkItem = me.activeItem.next(':not([disabled])') || m.items.getAt(0);
+        checkItem.setChecked(true);
+    }
+});
+/**
+ * @class Ext.container.ButtonGroup
+ * @extends Ext.panel.Panel
+ * <p>Provides a container for arranging a group of related Buttons in a tabular manner.</p>
+ * Example usage:
+ * {@img Ext.container.ButtonGroup/Ext.container.ButtonGroup.png Ext.container.ButtonGroup component}
+ * <pre><code>
+    Ext.create('Ext.panel.Panel', {
+        title: 'Panel with ButtonGroup',
+        width: 300,
+        height:200,
+        renderTo: document.body,
+        html: 'HTML Panel Content',
+        tbar: [{
+            xtype: 'buttongroup',
+            columns: 3,
+            title: 'Clipboard',
+            items: [{
+                text: 'Paste',
+                scale: 'large',
+                rowspan: 3,
+                iconCls: 'add',
+                iconAlign: 'top',
+                cls: 'x-btn-as-arrow'
+            },{
+                xtype:'splitbutton',
+                text: 'Menu Button',
+                scale: 'large',
+                rowspan: 3,
+                iconCls: 'add',
+                iconAlign: 'top',
+                arrowAlign:'bottom',
+                menu: [{text: 'Menu Item 1'}]
+            },{
+                xtype:'splitbutton', text: 'Cut', iconCls: 'add16', menu: [{text: 'Cut Menu Item'}]
+            },{
+                text: 'Copy', iconCls: 'add16'
+            },{
+                text: 'Format', iconCls: 'add16'
+            }]
+        }]
+    });
+ * </code></pre>
+ * @constructor
+ * Create a new ButtonGroup.
+ * @param {Object} config The config object
+ * @xtype buttongroup
+ */
+Ext.define('Ext.container.ButtonGroup', {
+    extend: 'Ext.panel.Panel',
+    alias: 'widget.buttongroup',
+    alternateClassName: 'Ext.ButtonGroup',
+
+    /**
+     * @cfg {Number} columns The <tt>columns</tt> configuration property passed to the
+     * {@link #layout configured layout manager}. See {@link Ext.layout.container.Table#columns}.
+     */
+
+    /**
+     * @cfg {String} baseCls  Defaults to <tt>'x-btn-group'</tt>.  See {@link Ext.panel.Panel#baseCls}.
+     */
+    baseCls: Ext.baseCSSPrefix + 'btn-group',
+
+    /**
+     * @cfg {Object} layout  Defaults to <tt>'table'</tt>.  See {@link Ext.container.Container#layout}.
+     */
+    layout: {
+        type: 'table'
+    },
+
+    defaultType: 'button',
+
+    /**
+     * @cfg {Boolean} frame  Defaults to <tt>true</tt>.  See {@link Ext.panel.Panel#frame}.
+     */
+    frame: true,
+    
+    frameHeader: false,
+    
+    internalDefaults: {removeMode: 'container', hideParent: true},
+
+    initComponent : function(){
+        // Copy the component's columns config to the layout if specified
+        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);
+
+        // Pugly hack for a pugly browser:
+        // If not an explicitly set width, then size the width to match the inner table
+        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;
+        
+        //we need to add an addition item in here so the ButtonGroup title is centered
+        if (me.header) {
+            me.header.insert(0, {
+                xtype: 'component',
+                ui   : me.ui,
+                html : '&nbsp;',
+                flex : 1
+            });
+        }
+        
+        me.callParent(arguments);
+    },
+    
+    // private
+    onBeforeAdd: function(component) {
+        if (component.is('button')) {
+            component.ui = component.ui + '-toolbar';
+        }
+        this.callParent(arguments);
+    },
+
+    //private
+    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;
+    }
+
+    /**
+     * @cfg {Array} tools  @hide
+     */
+    /**
+     * @cfg {Boolean} collapsible  @hide
+     */
+    /**
+     * @cfg {Boolean} collapseMode  @hide
+     */
+    /**
+     * @cfg {Boolean} animCollapse  @hide
+     */
+    /**
+     * @cfg {Boolean} closable  @hide
+     */
+});
+
+/**
+ * @class Ext.container.Viewport
+ * @extends Ext.container.Container
+
+A specialized container representing the viewable application area (the browser viewport).
+
+The Viewport renders itself to the document body, and automatically sizes itself to the size of
+the browser viewport and manages window resizing. There may only be one Viewport created
+in a page.
+
+Like any {@link Ext.container.Container Container}, a Viewport will only perform sizing and positioning
+on its child Components if you configure it with a {@link #layout}.
+
+A Common layout used with Viewports is {@link Ext.layout.container.Border border layout}, but if the
+required layout is simpler, a different layout should be chosen.
+
+For example, to simply make a single child item occupy all available space, use {@link Ext.layout.container.Fit fit layout}.
+
+To display one "active" item at full size from a choice of several child items, use {@link Ext.layout.container.Card card layout}.
+
+Inner layouts are available by virtue of the fact that all {@link Ext.panel.Panel Panel}s
+added to the Viewport, either through its {@link #items}, or through the items, or the {@link #add}
+method of any of its child Panels may themselves have a layout.
+
+The Viewport does not provide scrolling, so child Panels within the Viewport should provide
+for scrolling if needed using the {@link #autoScroll} config.
+{@img Ext.container.Viewport/Ext.container.Viewport.png Ext.container.Viewport component}
+An example showing a classic application border layout:
+
+    Ext.create('Ext.container.Viewport', {
+        layout: 'border',
+        renderTo: Ext.getBody(),
+        items: [{
+            region: 'north',
+            html: '<h1 class="x-panel-header">Page Title</h1>',
+            autoHeight: true,
+            border: false,
+            margins: '0 0 5 0'
+        }, {
+            region: 'west',
+            collapsible: true,
+            title: 'Navigation',
+            width: 150
+            // could use a TreePanel or AccordionLayout for navigational items
+        }, {
+            region: 'south',
+            title: 'South Panel',
+            collapsible: true,
+            html: 'Information goes here',
+            split: true,
+            height: 100,
+            minHeight: 100
+        }, {
+            region: 'east',
+            title: 'East Panel',
+            collapsible: true,
+            split: true,
+            width: 150
+        }, {
+            region: 'center',
+            xtype: 'tabpanel', // TabPanel itself has no title
+            activeTab: 0,      // First tab active by default
+            items: {
+                title: 'Default Tab',
+                html: 'The first tab\'s content. Others may be added dynamically'
+            }
+        }]
+    });
+
+ * @constructor
+ * Create a new Viewport
+ * @param {Object} config The config object
+ * @markdown
+ * @xtype viewport
+ */
+Ext.define('Ext.container.Viewport', {
+    extend: 'Ext.container.Container',
+    alias: 'widget.viewport',
+    requires: ['Ext.EventManager'],
+    alternateClassName: 'Ext.Viewport',
+
+    /*
+     * Privatize config options which, if used, would interfere with the
+     * correct operation of the Viewport as the sole manager of the
+     * layout of the document body.
+     */
+    /**
+     * @cfg {Mixed} applyTo @hide
+     */
+    /**
+     * @cfg {Boolean} allowDomMove @hide
+     */
+    /**
+     * @cfg {Boolean} hideParent @hide
+     */
+    /**
+     * @cfg {Mixed} renderTo @hide
+     */
+    /**
+     * @cfg {Boolean} hideParent @hide
+     */
+    /**
+     * @cfg {Number} height @hide
+     */
+    /**
+     * @cfg {Number} width @hide
+     */
+    /**
+     * @cfg {Boolean} autoHeight @hide
+     */
+    /**
+     * @cfg {Boolean} autoWidth @hide
+     */
+    /**
+     * @cfg {Boolean} deferHeight @hide
+     */
+    /**
+     * @cfg {Boolean} monitorResize @hide
+     */
+
+    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;
+        //this.autoWidth = true;
+        //this.autoHeight = true;
+        Ext.EventManager.onWindowResize(me.fireResize, me);
+        me.renderTo = me.el;
+    },
+
+    fireResize : function(w, h){
+        // setSize is the single entry point to layouts
+        this.setSize(w, h);
+        //this.fireEvent('resize', this, w, h, w, h);
+    }
+});
+
+/*
+ * This is a derivative of the similarly named class in the YUI Library.
+ * The original license:
+ * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
+ * Code licensed under the BSD License:
+ * http://developer.yahoo.net/yui/license.txt
+ */
+
+
+/**
+ * @class Ext.dd.DDTarget
+ * A DragDrop implementation that does not move, but can be a drop
+ * target.  You would get the same result by simply omitting implementation
+ * for the event callbacks, but this way we reduce the processing cost of the
+ * event listener and the callbacks.
+ * @extends Ext.dd.DragDrop
+ * @constructor
+ * @param {String} id the id of the element that is a drop target
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ *                 Valid properties for DDTarget in addition to those in
+ *                 DragDrop:
+ *                    none
+ */
+Ext.define('Ext.dd.DDTarget', {
+    extend: 'Ext.dd.DragDrop',
+    constructor: function(id, sGroup, config) {
+        if (id) {
+            this.initTarget(id, sGroup, config);
+        }
+    },
+
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    getDragEl: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    isValidHandleChild: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    startDrag: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    endDrag: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    onDrag: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    onDragDrop: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    onDragEnter: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    onDragOut: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    onDragOver: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    onInvalidDrop: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    onMouseDown: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    onMouseUp: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    setXConstraint: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    setYConstraint: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    resetConstraints: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    clearConstraints: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    clearTicks: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    setInitPosition: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    setDragElId: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    setHandleElId: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    setOuterHandleElId: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    addInvalidHandleClass: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    addInvalidHandleId: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    addInvalidHandleType: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    removeInvalidHandleClass: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    removeInvalidHandleId: Ext.emptyFn,
+    /**
+     * @hide
+     * Overridden and disabled. A DDTarget does not support being dragged.
+     * @method
+     */
+    removeInvalidHandleType: Ext.emptyFn,
+
+    toString: function() {
+        return ("DDTarget " + this.id);
+    }
+});
+/**
+ * @class Ext.dd.DragTracker
+ * A DragTracker listens for drag events on an Element and fires events at the start and end of the drag,
+ * as well as during the drag. This is useful for components such as {@link Ext.slider.Multi}, where there is
+ * an element that can be dragged around to change the Slider's value.
+ * DragTracker provides a series of template methods that should be overridden to provide functionality
+ * in response to detected drag operations. These are onBeforeStart, onStart, onDrag and onEnd.
+ * See {@link Ext.slider.Multi}'s initEvents function for an example implementation.
+ */
+Ext.define('Ext.dd.DragTracker', {
+
+    uses: ['Ext.util.Region'],
+
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+
+    /**
+     * @property active
+     * @type Boolean
+     * Read-only property indicated whether the user is currently dragging this
+     * tracker.
+     */
+    active: false,
+
+    /**
+     * @property dragTarget
+     * @type HtmlElement
+     * <p><b>Only valid during drag operations. Read-only.</b></p>
+     * <p>The element being dragged.</p>
+     * <p>If the {@link #delegate} option is used, this will be the delegate element which was mousedowned.</p>
+     */
+
+    /**
+     * @cfg {Boolean} trackOver
+     * <p>Defaults to <code>false</code>. Set to true to fire mouseover and mouseout events when the mouse enters or leaves the target element.</p>
+     * <p>This is implicitly set when an {@link #overCls} is specified.</p>
+     * <b>If the {@link #delegate} option is used, these events fire only when a delegate element is entered of left.</b>.
+     */
+    trackOver: false,
+
+    /**
+     * @cfg {String} overCls
+     * <p>A CSS class to add to the DragTracker's target element when the element (or, if the {@link #delegate} option is used,
+     * when a delegate element) is mouseovered.</p>
+     * <b>If the {@link #delegate} option is used, these events fire only when a delegate element is entered of left.</b>.
+     */
+
+    /**
+     * @cfg {Ext.util.Region/Element} constrainTo
+     * <p>A {@link Ext.util.Region Region} (Or an element from which a Region measurement will be read) which is used to constrain
+     * the result of the {@link #getOffset} call.</p>
+     * <p>This may be set any time during the DragTracker's lifecycle to set a dynamic constraining region.</p>
+     */
+
+    /**
+     * @cfg {Number} tolerance
+     * Number of pixels the drag target must be moved before dragging is
+     * considered to have started. Defaults to <code>5</code>.
+     */
+    tolerance: 5,
+
+    /**
+     * @cfg {Boolean/Number} autoStart
+     * Defaults to <code>false</code>. Specify <code>true</code> to defer trigger start by 1000 ms.
+     * Specify a Number for the number of milliseconds to defer trigger start.
+     */
+    autoStart: false,
+
+    /**
+     * @cfg {String} delegate
+     * Optional. <p>A {@link Ext.DomQuery DomQuery} selector which identifies child elements within the DragTracker's encapsulating
+     * Element which are the tracked elements. This limits tracking to only begin when the matching elements are mousedowned.</p>
+     * <p>This may also be a specific child element within the DragTracker's encapsulating element to use as the tracked element.</p>
+     */
+
+    /**
+     * @cfg {Boolean} preventDefault
+     * Specify <code>false</code> to enable default actions on onMouseDown events. Defaults to <code>true</code>.
+     */
+
+    /**
+     * @cfg {Boolean} stopEvent
+     * Specify <code>true</code> to stop the <code>mousedown</code> event from bubbling to outer listeners from the target element (or its delegates). Defaults to <code>false</code>.
+     */
+
+    constructor : function(config){
+        Ext.apply(this, config);
+        this.addEvents(
+            /**
+             * @event mouseover <p><b>Only available when {@link #trackOver} is <code>true</code></b></p>
+             * <p>Fires when the mouse enters the DragTracker's target element (or if {@link #delegate} is
+             * used, when the mouse enters a delegate element).</p>
+             * @param {Object} this
+             * @param {Object} e event object
+             * @param {HtmlElement} target The element mouseovered.
+             */
+            'mouseover',
+
+            /**
+             * @event mouseout <p><b>Only available when {@link #trackOver} is <code>true</code></b></p>
+             * <p>Fires when the mouse exits the DragTracker's target element (or if {@link #delegate} is
+             * used, when the mouse exits a delegate element).</p>
+             * @param {Object} this
+             * @param {Object} e event object
+             */
+            'mouseout',
+
+            /**
+             * @event mousedown <p>Fires when the mouse button is pressed down, but before a drag operation begins. The
+             * drag operation begins after either the mouse has been moved by {@link #tolerance} pixels, or after
+             * the {@link #autoStart} timer fires.</p>
+             * <p>Return false to veto the drag operation.</p>
+             * @param {Object} this
+             * @param {Object} e event object
+             */
+            'mousedown',
+
+            /**
+             * @event mouseup
+             * @param {Object} this
+             * @param {Object} e event object
+             */
+            'mouseup',
+
+            /**
+             * @event mousemove Fired when the mouse is moved. Returning false cancels the drag operation.
+             * @param {Object} this
+             * @param {Object} e event object
+             */
+            'mousemove',
+
+            /**
+             * @event dragstart
+             * @param {Object} this
+             * @param {Object} e event object
+             */
+            'dragstart',
+
+            /**
+             * @event dragend
+             * @param {Object} this
+             * @param {Object} e event object
+             */
+            'dragend',
+
+            /**
+             * @event drag
+             * @param {Object} this
+             * @param {Object} e event object
+             */
+            'drag'
+        );
+
+        this.dragRegion = Ext.create('Ext.util.Region', 0,0,0,0);
+
+        if (this.el) {
+            this.initEl(this.el);
+        }
+
+        // Dont pass the config so that it is not applied to 'this' again
+        this.mixins.observable.constructor.call(this);
+        if (this.disabled) {
+            this.disable();
+        }
+
+    },
+
+    /**
+     * Initializes the DragTracker on a given element.
+     * @param {Ext.core.Element/HTMLElement} el The element
+     */
+    initEl: function(el) {
+        this.el = Ext.get(el);
+
+        // The delegate option may also be an element on which to listen
+        this.handle = Ext.get(this.delegate);
+
+        // If delegate specified an actual element to listen on, we do not use the delegate listener option
+        this.delegate = this.handle ? undefined : this.delegate;
+
+        if (!this.handle) {
+            this.handle = this.el;
+        }
+
+        // Add a mousedown listener which reacts only on the elements targeted by the delegate config.
+        // We process mousedown to begin tracking.
+        this.mon(this.handle, {
+            mousedown: this.onMouseDown,
+            delegate: this.delegate,
+            scope: this
+        });
+
+        // If configured to do so, track mouse entry and exit into the target (or delegate).
+        // The mouseover and mouseout CANNOT be replaced with mouseenter and mouseleave
+        // because delegate cannot work with those pseudoevents. Entry/exit checking is done in the handler.
+        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;
+    },
+
+    // When the pointer enters a tracking element, fire a mouseover if the mouse entered from outside.
+    // This is mouseenter functionality, but we cannot use mouseenter because we are using "delegate" to filter mouse targets
+    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);
+            }
+        }
+    },
+
+    // When the pointer exits a tracking element, fire a mouseout.
+    // This is mouseleave functionality, but we cannot use mouseleave because we are using "delegate" to filter mouse targets
+    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 is disabled, or the mousedown has been processed by an upstream DragTracker, return
+        if (this.disabled ||e.dragTracked) {
+            return;
+        }
+
+        // This information should be available in mousedown listener and onBeforeStart implementations
+        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.onBeforeStart(e) !== false) {
+
+            // Track when the mouse is down so that mouseouts while the mouse is down are not processed.
+            // The onMouseOut method will only ever be called after mouseup.
+            this.mouseIsDown = true;
+
+            // Flag for downstream DragTracker instances that the mouse is being tracked.
+            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){
+        // BrowserBug: IE hack to see if button was released outside of window.
+        // Needed in IE6-9 in quirks and strictmode
+        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;
+            }
+        }
+
+        // Returning false from a mousemove listener deactivates 
+        if (this.fireEvent('mousemove', this, e) === false) {
+            this.onMouseUp(e);
+        } else {
+            this.onDrag(e);
+            this.fireEvent('drag', this, e);
+        }
+    },
+
+    onMouseUp: function(e) {
+        // Clear the flag which ensures onMouseOut fires only after the mouse button
+        // is lifted if the mouseout happens *during* a drag.
+        this.mouseIsDown = false;
+
+        // Remove flag from event singleton
+        delete e.dragTracked;
+
+        // If we mouseouted the el *during* the drag, the onMouseOut method will not have fired. Ensure that it gets processed.
+        if (this.mouseIsOut) {
+            this.mouseIsOut = false;
+            this.onMouseOut(e);
+        }
+        e.preventDefault();
+        this.fireEvent('mouseup', this, e);
+        this.endDrag(e);
+    },
+
+    /**
+     * @private
+     * Stop the drag operation, and remove active mouse listeners.
+     */
+    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);
+        }
+        // Private property calculated when first required and only cached during a drag
+        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;
+    },
+
+    /**
+     * Template method which should be overridden by each DragTracker instance. Called when the user first clicks and
+     * holds the mouse button down. Return false to disallow the drag
+     * @param {Ext.EventObject} e The event object
+     */
+    onBeforeStart : function(e) {
+
+    },
+
+    /**
+     * Template method which should be overridden by each DragTracker instance. Called when a drag operation starts
+     * (e.g. the user has moved the tracked element beyond the specified tolerance)
+     * @param {Ext.EventObject} e The event object
+     */
+    onStart : function(xy) {
+
+    },
+
+    /**
+     * Template method which should be overridden by each DragTracker instance. Called whenever a drag has been detected.
+     * @param {Ext.EventObject} e The event object
+     */
+    onDrag : function(e) {
+
+    },
+
+    /**
+     * Template method which should be overridden by each DragTracker instance. Called when a drag operation has been completed
+     * (e.g. the user clicked and held the mouse down, dragged the element and then released the mouse button)
+     * @param {Ext.EventObject} e The event object
+     */
+    onEnd : function(e) {
+
+    },
+
+    /**
+     * </p>Returns the drag target. This is usually the DragTracker's encapsulating element.</p>
+     * <p>If the {@link #delegate} option is being used, this may be a child element which matches the
+     * {@link #delegate} selector.</p>
+     * @return {Ext.core.Element} The element currently being tracked.
+     */
+    getDragTarget : function(){
+        return this.dragTarget;
+    },
+
+    /**
+     * @private
+     * @returns {Element} The DragTracker's encapsulating element.
+     */
+    getDragCt : function(){
+        return this.el;
+    },
+
+    /**
+     * @private
+     * Return the Region into which the drag operation is constrained.
+     * Either the XY pointer itself can be constrained, or the dragTarget element
+     * The private property _constrainRegion is cached until onMouseUp
+     */
+    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].call(this, this.lastXY) : this.lastXY;
+    },
+
+    /**
+     * <p>Returns the X, Y offset of the current mouse position from the mousedown point.</p>
+     * <p>This method may optionally constrain the real offset values, and returns a point coerced in one
+     * of two modes:</p><ul>
+     * <li><code>point</code><div class="sub-desc">The current mouse position is coerced into the
+     * {@link #constrainRegion}, and the resulting position is returned.</div></li>
+     * <li><code>dragTarget</code><div class="sub-desc">The new {@link Ext.util.Region Region} of the
+     * {@link #getDragTarget dragTarget} is calculated based upon the current mouse position, and then
+     * coerced into the {@link #constrainRegion}. The returned mouse position is then adjusted by the
+     * same delta as was used to coerce the region.</div></li>
+     * </ul>
+     * @param constrainMode {String} Optional. If omitted the true mouse position is returned. May be passed
+     * as <code>'point'</code> or <code>'dragTarget'. See above.</code>.
+     * @returns {Array} The <code>X, Y</code> offset from the mousedown point, optionally constrained.
+     */
+    getOffset : function(constrain){
+        var xy = this.getXY(constrain),
+            s = this.startXY;
+
+        return [xy[0]-s[0], xy[1]-s[1]];
+    },
+
+    constrainModes: {
+        // Constrain the passed point to within the constrain region
+        point: function(xy) {
+            var dr = this.dragRegion,
+                constrainTo = this.getConstrainRegion();
+
+            // No constraint
+            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];
+        },
+
+        // Constrain the dragTarget to within the constrain region. Return the passed xy adjusted by the same delta.
+        dragTarget: function(xy) {
+            var s = this.startXY,
+                dr = this.startRegion.copy(),
+                constrainTo = this.getConstrainRegion(),
+                adjust;
+
+            // No constraint
+            if (!constrainTo) {
+                return xy;
+            }
+
+            // See where the passed XY would put the dragTarget if translated by the unconstrained offset.
+            // If it overflows, we constrain the passed XY to bring the potential
+            // region back within the boundary.
+            dr.translateBy.apply(dr, [xy[0]-s[0], xy[1]-s[1]]);
+
+            // Constrain the X coordinate by however much the dragTarget overflows
+            if (dr.right > constrainTo.right) {
+                xy[0] += adjust = (constrainTo.right - dr.right);    // overflowed the right
+                dr.left += adjust;
+            }
+            if (dr.left < constrainTo.left) {
+                xy[0] += (constrainTo.left - dr.left);      // overflowed the left
+            }
+
+            // Constrain the X coordinate by however much the dragTarget overflows
+            if (dr.bottom > constrainTo.bottom) {
+                xy[1] += adjust = (constrainTo.bottom - dr.bottom);  // overflowed the bottom
+                dr.top += adjust;
+            }
+            if (dr.top < constrainTo.top) {
+                xy[1] += (constrainTo.top - dr.top);        // overflowed the top
+            }
+            return xy;
+        }
+    }
+});
+/**
+ * @class Ext.dd.DragZone
+ * @extends Ext.dd.DragSource
+ * <p>This class provides a container DD instance that allows dragging of multiple child source nodes.</p>
+ * <p>This class does not move the drag target nodes, but a proxy element which may contain
+ * any DOM structure you wish. The DOM element to show in the proxy is provided by either a
+ * provided implementation of {@link #getDragData}, or by registered draggables registered with {@link Ext.dd.Registry}</p>
+ * <p>If you wish to provide draggability for an arbitrary number of DOM nodes, each of which represent some
+ * application object (For example nodes in a {@link Ext.view.View DataView}) then use of this class
+ * is the most efficient way to "activate" those nodes.</p>
+ * <p>By default, this class requires that draggable child nodes are registered with {@link Ext.dd.Registry}.
+ * However a simpler way to allow a DragZone to manage any number of draggable elements is to configure
+ * the DragZone with  an implementation of the {@link #getDragData} method which interrogates the passed
+ * mouse event to see if it has taken place within an element, or class of elements. This is easily done
+ * by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a
+ * {@link Ext.DomQuery} selector. For example, to make the nodes of a DataView draggable, use the following
+ * technique. Knowledge of the use of the DataView is required:</p><pre><code>
+myDataView.on('render', function(v) {
+    myDataView.dragZone = new Ext.dd.DragZone(v.getEl(), {
+
+//      On receipt of a mousedown event, see if it is within a DataView node.
+//      Return a drag data object if so.
+        getDragData: function(e) {
+
+//          Use the DataView's own itemSelector (a mandatory property) to
+//          test if the mousedown is within one of the DataView's nodes.
+            var sourceEl = e.getTarget(v.itemSelector, 10);
+
+//          If the mousedown is within a DataView node, clone the node to produce
+//          a ddel element for use by the drag proxy. Also add application data
+//          to the returned data object.
+            if (sourceEl) {
+                d = sourceEl.cloneNode(true);
+                d.id = Ext.id();
+                return {
+                    ddel: d,
+                    sourceEl: sourceEl,
+                    repairXY: Ext.fly(sourceEl).getXY(),
+                    sourceStore: v.store,
+                    draggedRecord: v.{@link Ext.view.View#getRecord getRecord}(sourceEl)
+                }
+            }
+        },
+
+//      Provide coordinates for the proxy to slide back to on failed drag.
+//      This is the original XY coordinates of the draggable element captured
+//      in the getDragData method.
+        getRepairXY: function() {
+            return this.dragData.repairXY;
+        }
+    });
+});</code></pre>
+ * See the {@link Ext.dd.DropZone DropZone} documentation for details about building a DropZone which
+ * cooperates with this DragZone.
+ * @constructor
+ * @param {Mixed} el The container element
+ * @param {Object} config
+ */
+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);
+        }
+    },
+
+    /**
+     * This property contains the data representing the dragged object. This data is set up by the implementation
+     * of the {@link #getDragData} method. It must contain a <tt>ddel</tt> property, but can contain
+     * any other data according to the application's needs.
+     * @type Object
+     * @property dragData
+     */
+
+    /**
+     * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
+     * for auto scrolling during drag operations.
+     */
+
+    /**
+     * Called when a mousedown occurs in this container. Looks in {@link Ext.dd.Registry}
+     * for a valid target to drag based on the mouse down. Override this method
+     * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
+     * object has a "ddel" attribute (with an HTML Element) for other functions to work.
+     * @param {EventObject} e The mouse down event
+     * @return {Object} The dragData
+     */
+    getDragData : function(e){
+        return Ext.dd.Registry.getHandleFromEvent(e);
+    },
+
+    /**
+     * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
+     * this.dragData.ddel
+     * @param {Number} x The x position of the click on the dragged object
+     * @param {Number} y The y position of the click on the dragged object
+     * @return {Boolean} true to continue the drag, false to cancel
+     */
+    onInitDrag : function(x, y){
+        this.proxy.update(this.dragData.ddel.cloneNode(true));
+        this.onStartDrag(x, y);
+        return true;
+    },
+
+    /**
+     * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel
+     */
+    afterRepair : function(){
+        var me = this;
+        if (Ext.enableFx) {
+            Ext.fly(me.dragData.ddel).highlight(me.repairHighlightColor);
+        }
+        me.dragging = false;
+    },
+
+    /**
+     * Called before a repair of an invalid drop to get the XY to animate to. By default returns
+     * the XY of this.dragData.ddel
+     * @param {EventObject} e The mouse up event
+     * @return {Array} The xy location (e.g. [100, 200])
+     */
+    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);
+        }
+    }
+});
+
+/**
+ * @class Ext.dd.ScrollManager
+ * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>
+ * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,
+ * but you can also override most of the configs per scroll container by adding a
+ * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},
+ * {@link #vthresh}, {@link #increment} and {@link #frequency}.  Example usage:
+ * <pre><code>
+var el = Ext.get('scroll-ct');
+el.ddScrollConfig = {
+    vthresh: 50,
+    hthresh: -1,
+    frequency: 100,
+    increment: 200
+};
+Ext.dd.ScrollManager.register(el);
+</code></pre>
+ * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
+ * @singleton
+ */
+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;
+            // refresh regions on drag start
+            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();
+    },
+
+    /**
+     * Registers new overflow element(s) to auto scroll
+     * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either
+     */
+    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;
+        }
+    },
+
+    /**
+     * Unregisters overflow element(s) so they are no longer scrolled
+     * @param {Mixed/Array} el The id of or the element to be removed or an array of either
+     */
+    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];
+        }
+    },
+
+    /**
+     * The number of pixels from the top or bottom edge of a container the pointer needs to be to
+     * trigger scrolling (defaults to 25)
+     * @type Number
+     */
+    vthresh : 25,
+    /**
+     * The number of pixels from the right or left edge of a container the pointer needs to be to
+     * trigger scrolling (defaults to 25)
+     * @type Number
+     */
+    hthresh : 25,
+
+    /**
+     * The number of pixels to scroll in each scroll increment (defaults to 100)
+     * @type Number
+     */
+    increment : 100,
+
+    /**
+     * The frequency of scrolls in milliseconds (defaults to 500)
+     * @type Number
+     */
+    frequency : 500,
+
+    /**
+     * True to animate the scroll (defaults to true)
+     * @type Boolean
+     */
+    animate: true,
+
+    /**
+     * The animation duration in seconds -
+     * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)
+     * @type Number
+     */
+    animDuration: 0.4,
+
+    /**
+     * The named drag drop {@link Ext.dd.DragSource#ddGroup group} to which this container belongs (defaults to undefined).
+     * If a ddGroup is specified, then container scrolling will only occur when a dragged object is in the same ddGroup.
+     * @type String
+     */
+    ddGroup: undefined,
+
+    /**
+     * Manually trigger a cache refresh.
+     */
+    refreshCache : function(){
+        var els = this.els,
+            id;
+        for (id in els) {
+            if(typeof els[id] == 'object'){ // for people extending the object prototype
+                els[id]._region = els[id].getRegion();
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.dd.DropTarget
+ * @extends Ext.dd.DDTarget
+ * A simple class that provides the basic implementation needed to make any element a drop target that can have
+ * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
+ * @constructor
+ * @param {Mixed} el The container element
+ * @param {Object} config
+ */
+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}]);
+    },
+
+    /**
+     * @cfg {String} ddGroup
+     * A named drag drop group to which this object belongs.  If a group is specified, then this object will only
+     * interact with other drag drop objects in the same group (defaults to undefined).
+     */
+    /**
+     * @cfg {String} overClass
+     * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
+     */
+    /**
+     * @cfg {String} dropAllowed
+     * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
+     */
+    dropAllowed : Ext.baseCSSPrefix + 'dd-drop-ok',
+    /**
+     * @cfg {String} dropNotAllowed
+     * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
+     */
+    dropNotAllowed : Ext.baseCSSPrefix + 'dd-drop-nodrop',
+
+    // private
+    isTarget : true,
+
+    // private
+    isNotifyTarget : true,
+
+    /**
+     * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source is now over the
+     * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
+     * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {String} status The CSS class that communicates the drop status back to the source so that the
+     * underlying {@link Ext.dd.StatusProxy} can be updated
+     */
+    notifyEnter : function(dd, e, data){
+        if(this.overClass){
+            this.el.addCls(this.overClass);
+        }
+        return this.dropAllowed;
+    },
+
+    /**
+     * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the target.
+     * This method will be called on every mouse movement while the drag source is over the drop target.
+     * This default implementation simply returns the dropAllowed config value.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {String} status The CSS class that communicates the drop status back to the source so that the
+     * underlying {@link Ext.dd.StatusProxy} can be updated
+     */
+    notifyOver : function(dd, e, data){
+        return this.dropAllowed;
+    },
+
+    /**
+     * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the source has been dragged
+     * out of the target without dropping.  This default implementation simply removes the CSS class specified by
+     * overClass (if any) from the drop element.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     */
+    notifyOut : function(dd, e, data){
+        if(this.overClass){
+            this.el.removeCls(this.overClass);
+        }
+    },
+
+    /**
+     * The function a {@link Ext.dd.DragSource} calls once to notify this drop target that the dragged item has
+     * been dropped on it.  This method has no default implementation and returns false, so you must provide an
+     * implementation that does something to process the drop event and returns true so that the drag source's
+     * repair action does not run.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {Boolean} False if the drop was invalid.
+     */
+    notifyDrop : function(dd, e, data){
+        return false;
+    },
+
+    destroy : function(){
+        this.callParent();
+        if(this.containerScroll){
+            Ext.dd.ScrollManager.unregister(this.el);
+        }
+    }
+});
+
+/**
+ * @class Ext.dd.Registry
+ * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
+ * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
+ * @singleton
+ */
+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;
+    },
+    
+    /**
+     * Resgister a drag drop element
+     * @param {String/HTMLElement} element The id or DOM node to register
+     * @param {Object} data (optional) An custom data object that will be passed between the elements that are involved
+     * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
+     * knows how to interpret, plus there are some specific properties known to the Registry that should be
+     * populated in the data object (if applicable):
+     * <pre>
+Value      Description<br />
+---------  ------------------------------------------<br />
+handles    Array of DOM nodes that trigger dragging<br />
+           for the element being registered<br />
+isHandle   True if the element passed in triggers<br />
+           dragging itself, else false
+</pre>
+     */
+    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 a drag drop element
+     * @param {String/HTMLElement} element The id or DOM node to unregister
+     */
+    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)];
+                }
+            }
+        }
+    },
+
+    /**
+     * Returns the handle registered for a DOM Node by id
+     * @param {String/HTMLElement} id The DOM node or id to look up
+     * @return {Object} handle The custom handle data
+     */
+    getHandle : function(id){
+        if(typeof id != "string"){ // must be element?
+            id = id.id;
+        }
+        return this.handles[id];
+    },
+
+    /**
+     * Returns the handle that is registered for the DOM node that is the target of the event
+     * @param {Event} e The event
+     * @return {Object} handle The custom handle data
+     */
+    getHandleFromEvent : function(e){
+        var t = e.getTarget();
+        return t ? this.handles[t.id] : null;
+    },
+
+    /**
+     * Returns a custom data object that is registered for a DOM node by id
+     * @param {String/HTMLElement} id The DOM node or id to look up
+     * @return {Object} data The custom data
+     */
+    getTarget : function(id){
+        if(typeof id != "string"){ // must be element?
+            id = id.id;
+        }
+        return this.elements[id];
+    },
+
+    /**
+     * Returns a custom data object that is registered for the DOM node that is the target of the event
+     * @param {Event} e The event
+     * @return {Object} data The custom data
+     */
+    getTargetFromEvent : function(e){
+        var t = e.getTarget();
+        return t ? this.elements[t.id] || this.handles[t.id] : null;
+    }
+});
+/**
+ * @class Ext.dd.DropZone
+ * @extends Ext.dd.DropTarget
+
+This class provides a container DD instance that allows dropping on multiple child target nodes.
+
+By default, this class requires that child nodes accepting drop are registered with {@link Ext.dd.Registry}.
+However a simpler way to allow a DropZone to manage any number of target elements is to configure the
+DropZone with an implementation of {@link #getTargetFromEvent} which interrogates the passed
+mouse event to see if it has taken place within an element, or class of elements. This is easily done
+by using the event's {@link Ext.EventObject#getTarget getTarget} method to identify a node based on a
+{@link Ext.DomQuery} selector.
+
+Once the DropZone has detected through calling getTargetFromEvent, that the mouse is over
+a drop target, that target is passed as the first parameter to {@link #onNodeEnter}, {@link #onNodeOver},
+{@link #onNodeOut}, {@link #onNodeDrop}. You may configure the instance of DropZone with implementations
+of these methods to provide application-specific behaviour for these events to update both
+application state, and UI state.
+
+For example to make a GridPanel a cooperating target with the example illustrated in
+{@link Ext.dd.DragZone DragZone}, the following technique might be used:
+
+    myGridPanel.on('render', function() {
+        myGridPanel.dropZone = new Ext.dd.DropZone(myGridPanel.getView().scroller, {
+
+            // If the mouse is over a grid row, return that node. This is
+            // provided as the "target" parameter in all "onNodeXXXX" node event handling functions
+            getTargetFromEvent: function(e) {
+                return e.getTarget(myGridPanel.getView().rowSelector);
+            },
+
+            // On entry into a target node, highlight that node.
+            onNodeEnter : function(target, dd, e, data){ 
+                Ext.fly(target).addCls('my-row-highlight-class');
+            },
+
+            // On exit from a target node, unhighlight that node.
+            onNodeOut : function(target, dd, e, data){ 
+                Ext.fly(target).removeCls('my-row-highlight-class');
+            },
+
+            // While over a target node, return the default drop allowed class which
+            // places a "tick" icon into the drag proxy.
+            onNodeOver : function(target, dd, e, data){ 
+                return Ext.dd.DropZone.prototype.dropAllowed;
+            },
+
+            // On node drop we can interrogate the target to find the underlying
+            // application object that is the real target of the dragged data.
+            // In this case, it is a Record in the GridPanel's Store.
+            // We can use the data set up by the DragZone's getDragData method to read
+            // any data we decided to attach in the DragZone's getDragData method.
+            onNodeDrop : function(target, dd, e, data){
+                var rowIndex = myGridPanel.getView().findRowIndex(target);
+                var r = myGridPanel.getStore().getAt(rowIndex);
+                Ext.Msg.alert('Drop gesture', 'Dropped Record id ' + data.draggedRecord.id +
+                    ' on Record id ' + r.id);
+                return true;
+            }
+        });
+    }
+
+See the {@link Ext.dd.DragZone DragZone} documentation for details about building a DragZone which
+cooperates with this DropZone.
+
+ * @constructor
+ * @param {Mixed} el The container element
+ * @param {Object} config
+ * @markdown
+ */
+Ext.define('Ext.dd.DropZone', {
+    extend: 'Ext.dd.DropTarget',
+    requires: ['Ext.dd.Registry'],
+
+    /**
+     * Returns a custom data object associated with the DOM node that is the target of the event.  By default
+     * this looks up the event target in the {@link Ext.dd.Registry}, although you can override this method to
+     * provide your own custom lookup.
+     * @param {Event} e The event
+     * @return {Object} data The custom data
+     */
+    getTargetFromEvent : function(e){
+        return Ext.dd.Registry.getTargetFromEvent(e);
+    },
+
+    /**
+     * Called when the DropZone determines that a {@link Ext.dd.DragSource} has entered a drop node
+     * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.
+     * This method has no default implementation and should be overridden to provide
+     * node-specific processing if necessary.
+     * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
+     * {@link #getTargetFromEvent} for this node)
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     */
+    onNodeEnter : function(n, dd, e, data){
+        
+    },
+
+    /**
+     * Called while the DropZone determines that a {@link Ext.dd.DragSource} is over a drop node
+     * that has either been registered or detected by a configured implementation of {@link #getTargetFromEvent}.
+     * The default implementation returns this.dropNotAllowed, so it should be
+     * overridden to provide the proper feedback.
+     * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
+     * {@link #getTargetFromEvent} for this node)
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {String} status The CSS class that communicates the drop status back to the source so that the
+     * underlying {@link Ext.dd.StatusProxy} can be updated
+     */
+    onNodeOver : function(n, dd, e, data){
+        return this.dropAllowed;
+    },
+
+    /**
+     * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dragged out of
+     * the drop node without dropping.  This method has no default implementation and should be overridden to provide
+     * node-specific processing if necessary.
+     * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
+     * {@link #getTargetFromEvent} for this node)
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     */
+    onNodeOut : function(n, dd, e, data){
+        
+    },
+
+    /**
+     * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped onto
+     * the drop node.  The default implementation returns false, so it should be overridden to provide the
+     * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
+     * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
+     * {@link #getTargetFromEvent} for this node)
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {Boolean} True if the drop was valid, else false
+     */
+    onNodeDrop : function(n, dd, e, data){
+        return false;
+    },
+
+    /**
+     * Called while the DropZone determines that a {@link Ext.dd.DragSource} is being dragged over it,
+     * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
+     * it should be overridden to provide the proper feedback if necessary.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {String} status The CSS class that communicates the drop status back to the source so that the
+     * underlying {@link Ext.dd.StatusProxy} can be updated
+     */
+    onContainerOver : function(dd, e, data){
+        return this.dropNotAllowed;
+    },
+
+    /**
+     * Called when the DropZone determines that a {@link Ext.dd.DragSource} has been dropped on it,
+     * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
+     * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
+     * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {Boolean} True if the drop was valid, else false
+     */
+    onContainerDrop : function(dd, e, data){
+        return false;
+    },
+
+    /**
+     * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source is now over
+     * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
+     * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
+     * you should override this method and provide a custom implementation.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {String} status The CSS class that communicates the drop status back to the source so that the
+     * underlying {@link Ext.dd.StatusProxy} can be updated
+     */
+    notifyEnter : function(dd, e, data){
+        return this.dropNotAllowed;
+    },
+
+    /**
+     * The function a {@link Ext.dd.DragSource} calls continuously while it is being dragged over the drop zone.
+     * This method will be called on every mouse movement while the drag source is over the drop zone.
+     * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
+     * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
+     * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
+     * registered node, it will call {@link #onContainerOver}.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {String} status The CSS class that communicates the drop status back to the source so that the
+     * underlying {@link Ext.dd.StatusProxy} can be updated
+     */
+    notifyOver : function(dd, e, data){
+        var n = this.getTargetFromEvent(e);
+        if(!n) { // not over valid drop target
+            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);
+    },
+
+    /**
+     * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the source has been dragged
+     * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
+     * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop target
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag zone
+     */
+    notifyOut : function(dd, e, data){
+        if(this.lastOverNode){
+            this.onNodeOut(this.lastOverNode, dd, e, data);
+            this.lastOverNode = null;
+        }
+    },
+
+    /**
+     * The function a {@link Ext.dd.DragSource} calls once to notify this drop zone that the dragged item has
+     * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
+     * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
+     * otherwise it will call {@link #onContainerDrop}.
+     * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone
+     * @param {Event} e The event
+     * @param {Object} data An object containing arbitrary data supplied by the drag source
+     * @return {Boolean} False if the drop was invalid.
+     */
+    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);
+    },
+
+    // private
+    triggerCacheRefresh : function() {
+        Ext.dd.DDM.refreshCache(this.groups);
+    }
+});
+/**
+ * @class Ext.flash.Component
+ * @extends Ext.Component
+ *
+ * A simple Component for displaying an Adobe Flash SWF movie. The movie will be sized and can participate
+ * in layout like any other Component.
+ *
+ * This component requires the third-party SWFObject library version 2.2 or above. It is not included within
+ * the ExtJS distribution, so you will have to include it into your page manually in order to use this component.
+ * The SWFObject library can be downloaded from the [SWFObject project page](http://code.google.com/p/swfobject)
+ * and then simply import it into the head of your HTML document:
+ *
+ *     <script type="text/javascript" src="path/to/local/swfobject.js"></script>
+ *
+ * ## Configuration
+ *
+ * This component allows several options for configuring how the target Flash movie is embedded. The most
+ * important is the required {@link #url} which points to the location of the Flash movie to load. Other
+ * configurations include:
+ *
+ * - {@link #backgroundColor}
+ * - {@link #wmode}
+ * - {@link #flashVars}
+ * - {@link #flashParams}
+ * - {@link #flashAttributes}
+ *
+ * ## Example usage:
+ *
+ *     var win = Ext.widget('window', {
+ *         title: "It's a tiger!",
+ *         layout: 'fit',
+ *         width: 300,
+ *         height: 300,
+ *         x: 20,
+ *         y: 20,
+ *         resizable: true,
+ *         items: {
+ *             xtype: 'flash',
+ *             url: 'tiger.swf'
+ *         }
+ *     });
+ *     win.show();
+ *
+ * ## Express Install
+ *
+ * Adobe provides a tool called [Express Install](http://www.adobe.com/devnet/flashplayer/articles/express_install.html)
+ * that offers users an easy way to upgrade their Flash player. If you wish to make use of this, you should set
+ * the static EXPRESS\_INSTALL\_URL property to the location of your Express Install SWF file:
+ *
+ *     Ext.flash.Component.EXPRESS_INSTALL_URL = 'path/to/local/expressInstall.swf';
+ *
+ * @constructor
+ * Creates a new Ext.flash.Component instance.
+ * @param {Object} config The component configuration.
+ *
+ * @xtype flash
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+Ext.define('Ext.flash.Component', {
+    extend: 'Ext.Component',
+    alternateClassName: 'Ext.FlashComponent',
+    alias: 'widget.flash',
+
+    /**
+     * @cfg {String} flashVersion
+     * Indicates the version the flash content was published for. Defaults to <tt>'9.0.115'</tt>.
+     */
+    flashVersion : '9.0.115',
+
+    /**
+     * @cfg {String} backgroundColor
+     * The background color of the SWF movie. Defaults to <tt>'#ffffff'</tt>.
+     */
+    backgroundColor: '#ffffff',
+
+    /**
+     * @cfg {String} wmode
+     * The wmode of the flash object. This can be used to control layering. Defaults to <tt>'opaque'</tt>.
+     * Set to 'transparent' to ignore the {@link #backgroundColor} and make the background of the Flash
+     * movie transparent.
+     */
+    wmode: 'opaque',
+
+    /**
+     * @cfg {Object} flashVars
+     * A set of key value pairs to be passed to the flash object as flash variables. Defaults to <tt>undefined</tt>.
+     */
+
+    /**
+     * @cfg {Object} flashParams
+     * A set of key value pairs to be passed to the flash object as parameters. Possible parameters can be found here:
+     * http://kb2.adobe.com/cps/127/tn_12701.html Defaults to <tt>undefined</tt>.
+     */
+
+    /**
+     * @cfg {Object} flashAttributes
+     * A set of key value pairs to be passed to the flash object as attributes. Defaults to <tt>undefined</tt>.
+     */
+
+    /**
+     * @cfg {String} url
+     * The URL of the SWF file to include. Required.
+     */
+
+    /**
+     * @cfg {String/Number} swfWidth The width of the embedded SWF movie inside the component. Defaults to "100%"
+     * so that the movie matches the width of the component.
+     */
+    swfWidth: '100%',
+
+    /**
+     * @cfg {String/Number} swfHeight The height of the embedded SWF movie inside the component. Defaults to "100%"
+     * so that the movie matches the height of the component.
+     */
+    swfHeight: '100%',
+
+    /**
+     * @cfg {Boolean} expressInstall
+     * True to prompt the user to install flash if not installed. Note that this uses
+     * Ext.FlashComponent.EXPRESS_INSTALL_URL, which should be set to the local resource. Defaults to <tt>false</tt>.
+     */
+    expressInstall: false,
+
+    /**
+     * @property swf
+     * @type {Ext.core.Element}
+     * A reference to the object or embed element into which the SWF file is loaded. Only
+     * populated after the component is rendered and the SWF has been successfully embedded.
+     */
+
+    // Have to create a placeholder div with the swfId, which SWFObject will replace with the object/embed element.
+    renderTpl: ['<div id="{swfId}"></div>'],
+
+    initComponent: function() {
+        // <debug>
+        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');
+        }
+        // </debug>
+
+        this.callParent();
+        this.addEvents(
+            /**
+             * @event success
+             * Fired when the Flash movie has been successfully embedded
+             * @param {Ext.flash.Component} this
+             */
+            'success',
+
+            /**
+             * @event failure
+             * Fired when the Flash movie embedding fails
+             * @param {Ext.flash.Component} this
+             */
+            '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)
+        );
+    },
+
+    /**
+     * @private
+     * The callback method for handling an embedding success or failure by SWFObject
+     * @param {Object} e The event object passed by SWFObject - see http://code.google.com/p/swfobject/wiki/api
+     */
+    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);
+        }
+    },
+
+    /**
+     * Retrieve the id of the SWF object/embed element
+     */
+    getSwfId: function() {
+        return this.swfId || (this.swfId = "extswf" + this.getAutoId());
+    },
+
+    onSuccess: function() {
+        // swfobject forces visiblity:visible on the swf element, which prevents it 
+        // from getting hidden when an ancestor is given visibility:hidden.
+        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: {
+        /**
+         * Sets the url for installing flash if it doesn't exist. This should be set to a local resource.
+         * See http://www.adobe.com/devnet/flashplayer/articles/express_install.html for details.
+         * @static
+         * @type String
+         */
+        EXPRESS_INSTALL_URL: 'http:/' + '/swfobject.googlecode.com/svn/trunk/swfobject/expressInstall.swf'
+    }
+});
+
+/**
+ * @class Ext.form.action.Action
+ * @extends Ext.Base
+ * <p>The subclasses of this class provide actions to perform upon {@link Ext.form.Basic Form}s.</p>
+ * <p>Instances of this class are only created by a {@link Ext.form.Basic Form} when
+ * the Form needs to perform an action such as submit or load. The Configuration options
+ * listed for this class are set through the Form's action methods: {@link Ext.form.Basic#submit submit},
+ * {@link Ext.form.Basic#load load} and {@link Ext.form.Basic#doAction doAction}</p>
+ * <p>The instance of Action which performed the action is passed to the success
+ * and failure callbacks of the Form's action methods ({@link Ext.form.Basic#submit submit},
+ * {@link Ext.form.Basic#load load} and {@link Ext.form.Basic#doAction doAction}),
+ * and to the {@link Ext.form.Basic#actioncomplete actioncomplete} and
+ * {@link Ext.form.Basic#actionfailed actionfailed} event handlers.</p>
+ * @constructor
+ * @param {Object} config The configuration for this instance.
+ */
+Ext.define('Ext.form.action.Action', {
+    alternateClassName: 'Ext.form.Action',
+
+    /**
+     * @cfg {Ext.form.Basic} form The {@link Ext.form.Basic BasicForm} instance that
+     * is invoking this Action. Required.
+     */
+
+    /**
+     * @cfg {String} url The URL that the Action is to invoke. Will default to the {@link Ext.form.Basic#url url}
+     * configured on the {@link #form}.
+     */
+
+    /**
+     * @cfg {Boolean} reset When set to <tt><b>true</b></tt>, causes the Form to be
+     * {@link Ext.form.Basic#reset reset} on Action success. If specified, this happens
+     * before the {@link #success} callback is called and before the Form's
+     * {@link Ext.form.Basic#actioncomplete actioncomplete} event fires.
+     */
+
+    /**
+     * @cfg {String} method The HTTP method to use to access the requested URL. Defaults to the
+     * {@link Ext.form.Basic#method BasicForm's method}, or 'POST' if not specified.
+     */
+
+    /**
+     * @cfg {Object/String} params <p>Extra parameter values to pass. These are added to the Form's
+     * {@link Ext.form.Basic#baseParams} and passed to the specified URL along with the Form's
+     * input fields.</p>
+     * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode Ext.Object.toQueryString}.</p>
+     */
+
+    /**
+     * @cfg {Object} headers <p>Extra headers to be sent in the AJAX request for submit and load actions. See
+     * {@link Ext.data.Connection#headers}.</p>
+     */
+
+    /**
+     * @cfg {Number} timeout The number of seconds to wait for a server response before
+     * failing with the {@link #failureType} as {@link Ext.form.action.Action#CONNECT_FAILURE}. If not specified,
+     * defaults to the configured <tt>{@link Ext.form.Basic#timeout timeout}</tt> of the
+     * {@link #form}.
+     */
+
+    /**
+     * @cfg {Function} success The function to call when a valid success return packet is received.
+     * The function is passed the following parameters:<ul class="mdetail-params">
+     * <li><b>form</b> : Ext.form.Basic<div class="sub-desc">The form that requested the action</div></li>
+     * <li><b>action</b> : Ext.form.action.Action<div class="sub-desc">The Action class. The {@link #result}
+     * property of this object may be examined to perform custom postprocessing.</div></li>
+     * </ul>
+     */
+
+    /**
+     * @cfg {Function} failure The function to call when a failure packet was received, or when an
+     * error ocurred in the Ajax communication.
+     * The function is passed the following parameters:<ul class="mdetail-params">
+     * <li><b>form</b> : Ext.form.Basic<div class="sub-desc">The form that requested the action</div></li>
+     * <li><b>action</b> : Ext.form.action.Action<div class="sub-desc">The Action class. If an Ajax
+     * error ocurred, the failure type will be in {@link #failureType}. The {@link #result}
+     * property of this object may be examined to perform custom postprocessing.</div></li>
+     * </ul>
+     */
+
+    /**
+     * @cfg {Object} scope The scope in which to call the configured <tt>success</tt> and <tt>failure</tt>
+     * callback functions (the <tt>this</tt> reference for the callback functions).
+     */
+
+    /**
+     * @cfg {String} waitMsg The message to be displayed by a call to {@link Ext.window.MessageBox#wait}
+     * during the time the action is being processed.
+     */
+
+    /**
+     * @cfg {String} waitTitle The title to be displayed by a call to {@link Ext.window.MessageBox#wait}
+     * during the time the action is being processed.
+     */
+
+    /**
+     * @cfg {Boolean} submitEmptyText If set to <tt>true</tt>, the emptyText value will be sent with the form
+     * when it is submitted. Defaults to <tt>true</tt>.
+     */
+
+    /**
+     * @property type
+     * The type of action this Action instance performs.
+     * Currently only "submit" and "load" are supported.
+     * @type {String}
+     */
+
+    /**
+     * The type of failure detected will be one of these: {@link Ext.form.action.Action#CLIENT_INVALID},
+     * {@link Ext.form.action.Action#SERVER_INVALID}, {@link Ext.form.action.Action#CONNECT_FAILURE}, or
+     * {@link Ext.form.action.Action#LOAD_FAILURE}.  Usage:
+     * <pre><code>
+var fp = new Ext.form.Panel({
+...
+buttons: [{
+    text: 'Save',
+    formBind: true,
+    handler: function(){
+        if(fp.getForm().isValid()){
+            fp.getForm().submit({
+                url: 'form-submit.php',
+                waitMsg: 'Submitting your data...',
+                success: function(form, action){
+                    // server responded with success = true
+                    var result = action.{@link #result};
+                },
+                failure: function(form, action){
+                    if (action.{@link #failureType} === {@link Ext.form.action.Action#CONNECT_FAILURE}) {
+                        Ext.Msg.alert('Error',
+                            'Status:'+action.{@link #response}.status+': '+
+                            action.{@link #response}.statusText);
+                    }
+                    if (action.failureType === {@link Ext.form.action.Action#SERVER_INVALID}){
+                        // server responded with success = false
+                        Ext.Msg.alert('Invalid', action.{@link #result}.errormsg);
+                    }
+                }
+            });
+        }
+    }
+},{
+    text: 'Reset',
+    handler: function(){
+        fp.getForm().reset();
+    }
+}]
+     * </code></pre>
+     * @property failureType
+     * @type {String}
+     */
+
+    /**
+     * The raw XMLHttpRequest object used to perform the action.
+     * @property response
+     * @type {Object}
+     */
+
+    /**
+     * The decoded response object containing a boolean <tt>success</tt> property and
+     * other, action-specific properties.
+     * @property result
+     * @type {Object}
+     */
+
+
+
+    constructor: function(config) {
+        if (config) {
+            Ext.apply(this, config);
+        }
+
+        // Normalize the params option to an Object
+        var params = config.params;
+        if (Ext.isString(params)) {
+            this.params = Ext.Object.fromQueryString(params);
+        }
+    },
+
+    /**
+     * Invokes this action using the current configuration.
+     */
+    run: Ext.emptyFn,
+
+    /**
+     * @private
+     * @method onSuccess
+     * Callback method that gets invoked when the action completes successfully. Must be implemented by subclasses.
+     * @param {Object} response
+     */
+
+    /**
+     * @private
+     * @method handleResponse
+     * Handles the raw response and builds a result object from it. Must be implemented by subclasses.
+     * @param {Object} response
+     */
+
+    /**
+     * @private
+     * Handles a failure response.
+     * @param {Object} response
+     */
+    onFailure : function(response){
+        this.response = response;
+        this.failureType = Ext.form.action.Action.CONNECT_FAILURE;
+        this.form.afterAction(this, false);
+    },
+
+    /**
+     * @private
+     * Validates that a response contains either responseText or responseXML and invokes
+     * {@link #handleResponse} to build the result object.
+     * @param {Object} response The raw response object.
+     * @return {Object/Boolean} result The result object as built by handleResponse, or <tt>true</tt> if
+     *                         the response had empty responseText and responseXML.
+     */
+    processResponse : function(response){
+        this.response = response;
+        if (!response.responseText && !response.responseXML) {
+            return true;
+        }
+        return (this.result = this.handleResponse(response));
+    },
+
+    /**
+     * @private
+     * Build the URL for the AJAX request. Used by the standard AJAX submit and load actions.
+     * @return {String} The URL.
+     */
+    getUrl: function() {
+        return this.url || this.form.url;
+    },
+
+    /**
+     * @private
+     * Determine the HTTP method to be used for the request.
+     * @return {String} The HTTP method
+     */
+    getMethod: function() {
+        return (this.method || this.form.method || 'POST').toUpperCase();
+    },
+
+    /**
+     * @private
+     * Get the set of parameters specified in the BasicForm's baseParams and/or the params option.
+     * Items in params override items of the same name in baseParams.
+     * @return {Object} the full set of parameters
+     */
+    getParams: function() {
+        return Ext.apply({}, this.params, this.form.baseParams);
+    },
+
+    /**
+     * @private
+     * Creates a callback object.
+     */
+    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: {
+        /**
+         * @property CLIENT_INVALID
+         * Failure type returned when client side validation of the Form fails
+         * thus aborting a submit action. Client side validation is performed unless
+         * {@link Ext.form.action.Submit#clientValidation} is explicitly set to <tt>false</tt>.
+         * @type {String}
+         * @static
+         */
+        CLIENT_INVALID: 'client',
+
+        /**
+         * @property SERVER_INVALID
+         * <p>Failure type returned when server side processing fails and the {@link #result}'s
+         * <tt>success</tt> property is set to <tt>false</tt>.</p>
+         * <p>In the case of a form submission, field-specific error messages may be returned in the
+         * {@link #result}'s <tt>errors</tt> property.</p>
+         * @type {String}
+         * @static
+         */
+        SERVER_INVALID: 'server',
+
+        /**
+         * @property CONNECT_FAILURE
+         * Failure type returned when a communication error happens when attempting
+         * to send a request to the remote server. The {@link #response} may be examined to
+         * provide further information.
+         * @type {String}
+         * @static
+         */
+        CONNECT_FAILURE: 'connect',
+
+        /**
+         * @property LOAD_FAILURE
+         * Failure type returned when the response's <tt>success</tt>
+         * property is set to <tt>false</tt>, or no field values are returned in the response's
+         * <tt>data</tt> property.
+         * @type {String}
+         * @static
+         */
+        LOAD_FAILURE: 'load'
+
+
+    }
+});
+
+/**
+ * @class Ext.form.action.Submit
+ * @extends Ext.form.action.Action
+ * <p>A class which handles submission of data from {@link Ext.form.Basic Form}s
+ * and processes the returned response.</p>
+ * <p>Instances of this class are only created by a {@link Ext.form.Basic Form} when
+ * {@link Ext.form.Basic#submit submit}ting.</p>
+ * <p><u><b>Response Packet Criteria</b></u></p>
+ * <p>A response packet may contain:
+ * <div class="mdetail-params"><ul>
+ * <li><b><code>success</code></b> property : Boolean
+ * <div class="sub-desc">The <code>success</code> property is required.</div></li>
+ * <li><b><code>errors</code></b> property : Object
+ * <div class="sub-desc"><div class="sub-desc">The <code>errors</code> property,
+ * which is optional, contains error messages for invalid fields.</div></li>
+ * </ul></div>
+ * <p><u><b>JSON Packets</b></u></p>
+ * <p>By default, response packets are assumed to be JSON, so a typical response
+ * packet may look like this:</p><pre><code>
+{
+    success: false,
+    errors: {
+        clientCode: "Client not found",
+        portOfLoading: "This field must not be null"
+    }
+}</code></pre>
+ * <p>Other data may be placed into the response for processing by the {@link Ext.form.Basic}'s callback
+ * or event handler methods. The object decoded from this JSON is available in the
+ * {@link Ext.form.action.Action#result result} property.</p>
+ * <p>Alternatively, if an {@link #errorReader} is specified as an {@link Ext.data.reader.Xml XmlReader}:</p><pre><code>
+    errorReader: new Ext.data.reader.Xml({
+            record : 'field',
+            success: '@success'
+        }, [
+            'id', 'msg'
+        ]
+    )
+</code></pre>
+ * <p>then the results may be sent back in XML format:</p><pre><code>
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;message success="false"&gt;
+&lt;errors&gt;
+    &lt;field&gt;
+        &lt;id&gt;clientCode&lt;/id&gt;
+        &lt;msg&gt;&lt;![CDATA[Code not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
+    &lt;/field&gt;
+    &lt;field&gt;
+        &lt;id&gt;portOfLoading&lt;/id&gt;
+        &lt;msg&gt;&lt;![CDATA[Port not found. &lt;br /&gt;&lt;i&gt;This is a test validation message from the server &lt;/i&gt;]]&gt;&lt;/msg&gt;
+    &lt;/field&gt;
+&lt;/errors&gt;
+&lt;/message&gt;
+</code></pre>
+ * <p>Other elements may be placed into the response XML for processing by the {@link Ext.form.Basic}'s callback
+ * or event handler methods. The XML document is available in the {@link #errorReader}'s {@link Ext.data.reader.Xml#xmlData xmlData} property.</p>
+ */
+Ext.define('Ext.form.action.Submit', {
+    extend:'Ext.form.action.Action',
+    alternateClassName: 'Ext.form.Action.Submit',
+    alias: 'formaction.submit',
+
+    type: 'submit',
+
+    /**
+     * @cfg {boolean} clientValidation Determines whether a Form's fields are validated
+     * in a final call to {@link Ext.form.Basic#isValid isValid} prior to submission.
+     * Pass <tt>false</tt> in the Form's submit options to prevent this. Defaults to true.
+     */
+
+    // inherit docs
+    run : function(){
+        var form = this.form;
+        if (this.clientValidation === false || form.isValid()) {
+            this.doSubmit();
+        } else {
+            // client validation failed
+            this.failureType = Ext.form.action.Action.CLIENT_INVALID;
+            form.afterAction(this, false);
+        }
+    },
+
+    /**
+     * @private
+     * Perform the submit of the form data.
+     */
+    doSubmit: function() {
+        var formEl,
+            ajaxOptions = Ext.apply(this.createCallback(), {
+                url: this.getUrl(),
+                method: this.getMethod(),
+                headers: this.headers
+            });
+
+        // For uploads we need to create an actual form that contains the file upload fields,
+        // and pass that to the ajax call so it can do its iframe-based submit method.
+        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);
+        }
+    },
+
+    /**
+     * @private
+     * Build the full set of parameters from the field values plus any additional configured params.
+     */
+    getParams: function() {
+        var nope = false,
+            configParams = this.callParent(),
+            fieldParams = this.form.getValues(nope, nope, this.submitEmptyText !== nope);
+        return Ext.apply({}, fieldParams, configParams);
+    },
+
+    /**
+     * @private
+     * Build a form element containing fields corresponding to all the parameters to be
+     * submitted (everything returned by {@link #getParams}.
+     * NOTE: the form element is automatically added to the DOM, so any code that uses
+     * it must remove it from the DOM after finishing with it.
+     * @return HTMLFormElement
+     */
+    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: val
+            });
+        }
+
+        // Add the form field values
+        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
+        };
+
+        // Set the proper encoding for file uploads
+        if (uploadFields.length) {
+            formSpec.encoding = formSpec.enctype = 'multipart/form-data';
+        }
+
+        // Create the form
+        formEl = Ext.core.DomHelper.append(Ext.getBody(), formSpec);
+
+        // Special handling for file upload fields: since browser security measures prevent setting
+        // their values programatically, and prevent carrying their selected values over when cloning,
+        // we have to move the actual field instances out of their components and into the form.
+        Ext.Array.each(uploadFields, function(field) {
+            if (field.rendered) { // can only have a selected file value after being rendered
+                formEl.appendChild(field.extractFileInput());
+            }
+        });
+
+        return formEl;
+    },
+
+
+
+    /**
+     * @private
+     */
+    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);
+    },
+
+    /**
+     * @private
+     */
+    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);
+    }
+});
+
+/**
+ * @class Ext.util.ComponentDragger
+ * @extends Ext.dd.DragTracker
+ * <p>A subclass of Ext.dd.DragTracker which handles dragging any Component.</p>
+ * <p>This is configured with a Component to be made draggable, and a config object for the
+ * {@link Ext.dd.DragTracker} class.</p>
+ * <p>A {@link #} delegate may be provided which may be either the element to use as the mousedown target
+ * or a {@link Ext.DomQuery} selector to activate multiple mousedown targets.</p>
+ * @constructor Create a new ComponentTracker
+ * @param {object} comp The Component to provide dragging for.
+ * @param {object} config The config object
+ */
+Ext.define('Ext.util.ComponentDragger', {
+
+    /**
+     * @cfg {Boolean} constrain
+     * Specify as <code>true</code> to constrain the Component to within the bounds of the {@link #constrainTo} region.
+     */
+
+    /**
+     * @cfg {String/Element} delegate
+     * Optional. <p>A {@link Ext.DomQuery DomQuery} selector which identifies child elements within the Component's encapsulating
+     * Element which are the drag handles. This limits dragging to only begin when the matching elements are mousedowned.</p>
+     * <p>This may also be a specific child element within the Component's encapsulating element to use as the drag handle.</p>
+     */
+
+    /**
+     * @cfg {Boolean} constrainDelegate
+     * Specify as <code>true</code> to constrain the drag handles within the {@link constrainTo} region.
+     */
+
+    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;
+
+        // Cache the start [X, Y] array
+        this.startPosition = comp.getPosition();
+
+        // If client Component has a ghost method to show a lightweight version of itself
+        // then use that as a drag proxy unless configured to liveDrag.
+        if (comp.ghost && !comp.liveDrag) {
+             me.proxy = comp.ghost();
+             me.dragTarget = me.proxy.header.el;
+        }
+
+        // Set the constrainTo Region before we start dragging.
+        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;
+
+        // The configured constrainTo might be a Region or an element
+        if (!(c instanceof Ext.util.Region)) {
+            c =  Ext.fly(c).getViewRegion();
+        }
+
+        // Reduce the constrain region to allow for shadow
+        if (shadowSize) {
+            c.adjust(0, -shadowSize, -shadowSize, shadowSize);
+        }
+
+        // If they only want to constrain the *delegate* to within the constrain region,
+        // adjust the region to be larger based on the insets of the delegate from the outer
+        // edges of the Component.
+        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;
+    },
+
+    // Move either the ghost Component or the target Component to its new position on drag
+    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.apply(comp, [me.startPosition[0] + offset[0], me.startPosition[1] + offset[1]]);
+    },
+
+    onEnd: function(e) {
+        if (this.proxy && !this.comp.liveDrag) {
+            this.comp.unghost();
+        }
+    }
+});
+/**
+ * @class Ext.form.Labelable
+
+A mixin which allows a component to be configured and decorated with a label and/or error message as is
+common for form fields. This is used by e.g. {@link Ext.form.field.Base} and {@link Ext.form.FieldContainer}
+to let them be managed by the Field layout.
+
+**NOTE**: This mixin is mainly for internal library use and most users should not need to use it directly. It
+is more likely you will want to use one of the component classes that import this mixin, such as
+{@link Ext.form.field.Base} or {@link Ext.form.FieldContainer}.
+
+Use of this mixin does not make a component a field in the logical sense, meaning it does not provide any
+logic or state related to values or validation; that is handled by the related {@link Ext.form.field.Field}
+mixin. These two mixins may be used separately (for example {@link Ext.form.FieldContainer} is Labelable but not a
+Field), or in combination (for example {@link Ext.form.field.Base} implements both and has logic for connecting the
+two.)
+
+Component classes which use this mixin should use the Field layout
+or a derivation thereof to properly size and position the label and message according to the component config.
+They must also call the {@link #initLabelable} method during component initialization to ensure the mixin gets
+set up correctly.
+
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+Ext.define("Ext.form.Labelable", {
+    requires: ['Ext.XTemplate'],
+
+    /**
+     * @cfg {Array/String/Ext.XTemplate} labelableRenderTpl
+     * The rendering template for the field decorations. Component classes using this mixin should include
+     * logic to use this as their {@link Ext.AbstractComponent#renderTpl renderTpl}, and implement the
+     * {@link #getSubTplMarkup} method to generate the field body content.
+     */
+    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
+        }
+    ],
+
+    /**
+     * @cfg {Ext.XTemplate} activeErrorsTpl
+     * The template used to format the Array of error messages passed to {@link #setActiveErrors}
+     * into a single HTML string. By default this renders each message as an item in an unordered list.
+     */
+    activeErrorsTpl: [
+        '<tpl if="errors && errors.length">',
+            '<ul><tpl for="errors"><li<tpl if="xindex == xcount"> class="last"</tpl>>{.}</li></tpl></ul>',
+        '</tpl>'
+    ],
+
+    /**
+     * @property isFieldLabelable
+     * @type Boolean
+     * Flag denoting that this object is labelable as a field. Always true.
+     */
+    isFieldLabelable: true,
+
+    /**
+     * @cfg {String} formItemCls
+     * A CSS class to be applied to the outermost element to denote that it is participating in the form
+     * field layout. Defaults to 'x-form-item'.
+     */
+    formItemCls: Ext.baseCSSPrefix + 'form-item',
+
+    /**
+     * @cfg {String} labelCls
+     * The CSS class to be applied to the label element. Defaults to 'x-form-item-label'.
+     */
+    labelCls: Ext.baseCSSPrefix + 'form-item-label',
+
+    /**
+     * @cfg {String} errorMsgCls
+     * The CSS class to be applied to the error message element. Defaults to 'x-form-error-msg'.
+     */
+    errorMsgCls: Ext.baseCSSPrefix + 'form-error-msg',
+
+    /**
+     * @cfg {String} baseBodyCls
+     * The CSS class to be applied to the body content element. Defaults to 'x-form-item-body'.
+     */
+    baseBodyCls: Ext.baseCSSPrefix + 'form-item-body',
+
+    /**
+     * @cfg {String} fieldBodyCls
+     * An extra CSS class to be applied to the body content element in addition to {@link #fieldBodyCls}.
+     * Defaults to empty.
+     */
+    fieldBodyCls: '',
+
+    /**
+     * @cfg {String} clearCls
+     * The CSS class to be applied to the special clearing div rendered directly after the field
+     * contents wrapper to provide field clearing (defaults to <tt>'x-clear'</tt>).
+     */
+    clearCls: Ext.baseCSSPrefix + 'clear',
+
+    /**
+     * @cfg {String} invalidCls
+     * The CSS class to use when marking the component invalid (defaults to 'x-form-invalid')
+     */
+    invalidCls : Ext.baseCSSPrefix + 'form-invalid',
+
+    /**
+     * @cfg {String} fieldLabel
+     * The label for the field. It gets appended with the {@link #labelSeparator}, and its position
+     * and sizing is determined by the {@link #labelAlign}, {@link #labelWidth}, and {@link #labelPad}
+     * configs. Defaults to undefined.
+     */
+    fieldLabel: undefined,
+
+    /**
+     * @cfg {String} labelAlign
+     * <p>Controls the position and alignment of the {@link #fieldLabel}. Valid values are:</p>
+     * <ul>
+     * <li><tt>"left"</tt> (the default) - The label is positioned to the left of the field, with its text
+     * aligned to the left. Its width is determined by the {@link #labelWidth} config.</li>
+     * <li><tt>"top"</tt> - The label is positioned above the field.</li>
+     * <li><tt>"right"</tt> - The label is positioned to the left of the field, with its text aligned
+     * to the right. Its width is determined by the {@link #labelWidth} config.</li>
+     * </ul>
+     */
+    labelAlign : 'left',
+
+    /**
+     * @cfg {Number} labelWidth
+     * The width of the {@link #fieldLabel} in pixels. Only applicable if the {@link #labelAlign} is set
+     * to "left" or "right". Defaults to <tt>100</tt>.
+     */
+    labelWidth: 100,
+
+    /**
+     * @cfg {Number} labelPad
+     * The amount of space in pixels between the {@link #fieldLabel} and the input field. Defaults to <tt>5</tt>.
+     */
+    labelPad : 5,
+
+    /**
+     * @cfg {String} labelSeparator
+     * Character(s) to be inserted at the end of the {@link #fieldLabel label text}.
+     */
+    labelSeparator : ':',
+
+    /**
+     * @cfg {String} labelStyle
+     * <p>A CSS style specification string to apply directly to this field's label. Defaults to undefined.</p>
+     */
+
+    /**
+     * @cfg {Boolean} hideLabel
+     * <p>Set to <tt>true</tt> to completely hide the label element ({@link #fieldLabel} and {@link #labelSeparator}).
+     * Defaults to <tt>false</tt>.</p>
+     * <p>Also see {@link #hideEmptyLabel}, which controls whether space will be reserved for an empty fieldLabel.</p>
+     */
+    hideLabel: false,
+
+    /**
+     * @cfg {Boolean} hideEmptyLabel
+     * <p>When set to <tt>true</tt>, the label element ({@link #fieldLabel} and {@link #labelSeparator}) will be
+     * automatically hidden if the {@link #fieldLabel} is empty. Setting this to <tt>false</tt> will cause the empty
+     * label element to be rendered and space to be reserved for it; this is useful if you want a field without a label
+     * to line up with other labeled fields in the same form. Defaults to <tt>true</tt>.</p>
+     * <p>If you wish to unconditionall hide the label even if a non-empty fieldLabel is configured, then set
+     * the {@link #hideLabel} config to <tt>true</tt>.</p>
+     */
+    hideEmptyLabel: true,
+
+    /**
+     * @cfg {Boolean} preventMark
+     * <tt>true</tt> to disable displaying any {@link #setActiveError error message} set on this object.
+     * Defaults to <tt>false</tt>.
+     */
+    preventMark: false,
+
+    /**
+     * @cfg {Boolean} autoFitErrors
+     * Whether to adjust the component's body area to make room for 'side' or 'under'
+     * {@link #msgTarget error messages}. Defaults to <tt>true</tt>.
+     */
+    autoFitErrors: true,
+
+    /**
+     * @cfg {String} msgTarget <p>The location where the error message text should display.
+     * Must be one of the following values:</p>
+     * <div class="mdetail-params"><ul>
+     * <li><code>qtip</code> Display a quick tip containing the message when the user hovers over the field. This is the default.
+     * <div class="subdesc"><b>{@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager.init} must have been called for this setting to work.</b></div></li>
+     * <li><code>title</code> Display the message in a default browser title attribute popup.</li>
+     * <li><code>under</code> Add a block div beneath the field containing the error message.</li>
+     * <li><code>side</code> Add an error icon to the right of the field, displaying the message in a popup on hover.</li>
+     * <li><code>none</code> Don't display any error message. This might be useful if you are implementing custom error display.</li>
+     * <li><code>[element id]</code> Add the error message directly to the innerHTML of the specified element.</li>
+     * </ul></div>
+     */
+    msgTarget: 'qtip',
+
+    /**
+     * @cfg {String} activeError
+     * If specified, then the component will be displayed with this value as its active error when
+     * first rendered. Defaults to undefined. Use {@link #setActiveError} or {@link #unsetActiveError} to
+     * change it after component creation.
+     */
+
+
+    /**
+     * Performs initialization of this mixin. Component classes using this mixin should call this method
+     * during their own initialization.
+     */
+    initLabelable: function() {
+        this.addCls(this.formItemCls);
+
+        this.addEvents(
+            /**
+             * @event errorchange
+             * Fires when the active error message is changed via {@link #setActiveError}.
+             * @param {Ext.form.Labelable} this
+             * @param {String} error The active error message
+             */
+            'errorchange'
+        );
+    },
+
+    /**
+     * Returns the label for the field. Defaults to simply returning the {@link #fieldLabel} config. Can be
+     * overridden to provide
+     * @return {String} The configured field label, or empty string if not defined
+     */
+    getFieldLabel: function() {
+        return this.fieldLabel || '';
+    },
+
+    /**
+     * @protected
+     * Generates the arguments for the field decorations {@link #labelableRenderTpl rendering template}.
+     * @return {Object} The template arguments
+     */
+    getLabelableRenderData: function() {
+        var me = this,
+            labelAlign = me.labelAlign,
+            labelPad = me.labelPad,
+            labelStyle;
+
+        // Calculate label styles up front rather than in the Field layout for speed; this
+        // is safe because label alignment/width/pad are not expected to change.
+        if (labelAlign === 'top') {
+            labelStyle = 'margin-bottom:' + labelPad + 'px;';
+        } else {
+            labelStyle = 'margin-right:' + labelPad + 'px;';
+            // Add the width for border-box browsers; will be set by the Field layout for content-box
+            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
+        );
+    },
+
+    /**
+     * @protected
+     * Returns the additional {@link Ext.AbstractComponent#renderSelectors} for selecting the field
+     * decoration elements from the rendered {@link #labelableRenderTpl}. Component classes using this mixin should
+     * be sure and merge this method's result into the component's {@link Ext.AbstractComponent#renderSelectors}
+     * before rendering.
+     */
+    getLabelableSelectors: function() {
+        return {
+            /**
+             * @property labelEl
+             * @type Ext.core.Element
+             * The label Element for this component. Only available after the component has been rendered.
+             */
+            labelEl: 'label.' + this.labelCls,
+
+            /**
+             * @property bodyEl
+             * @type Ext.core.Element
+             * The div Element wrapping the component's contents. Only available after the component has been rendered.
+             */
+            bodyEl: '.' + this.baseBodyCls,
+
+            /**
+             * @property errorEl
+             * @type Ext.core.Element
+             * The div Element that will contain the component's error message(s). Note that depending on the
+             * configured {@link #msgTarget}, this element may be hidden in favor of some other form of
+             * presentation, but will always be present in the DOM for use by assistive technologies.
+             */
+            errorEl: '.' + this.errorMsgCls
+        };
+    },
+
+    /**
+     * @protected
+     * Gets the markup to be inserted into the outer template's bodyEl. Defaults to empty string, should
+     * be implemented by classes including this mixin as needed.
+     * @return {String} The markup to be inserted
+     */
+    getSubTplMarkup: function() {
+        return '';
+    },
+
+    /**
+     * Get the input id, if any, for this component. This is used as the "for" attribute on the label element.
+     * Implementing subclasses may also use this as e.g. the id for their own <tt>input</tt> element.
+     * @return {String} The input id
+     */
+    getInputId: function() {
+        return '';
+    },
+
+    /**
+     * Gets the active error message for this component, if any. This does not trigger
+     * validation on its own, it merely returns any message that the component may already hold.
+     * @return {String} The active error message on the component; if there is no error, an empty string is returned.
+     */
+    getActiveError : function() {
+        return this.activeError || '';
+    },
+
+    /**
+     * Tells whether the field currently has an active error message. This does not trigger
+     * validation on its own, it merely looks for any message that the component may already hold.
+     * @return {Boolean}
+     */
+    hasActiveError: function() {
+        return !!this.getActiveError();
+    },
+
+    /**
+     * Sets the active error message to the given string. This replaces the entire error message
+     * contents with the given string. Also see {@link #setActiveErrors} which accepts an Array of
+     * messages and formats them according to the {@link #activeErrorsTpl}.
+     * @param {String} msg The error message
+     */
+    setActiveError: function(msg) {
+        this.activeError = msg;
+        this.activeErrors = [msg];
+        this.renderActiveError();
+    },
+
+    /**
+     * Gets an Array of any active error messages currently applied to the field. This does not trigger
+     * validation on its own, it merely returns any messages that the component may already hold.
+     * @return {Array} The active error messages on the component; if there are no errors, an empty Array is returned.
+     */
+    getActiveErrors: function() {
+        return this.activeErrors || [];
+    },
+
+    /**
+     * Set the active error message to an Array of error messages. The messages are formatted into
+     * a single message string using the {@link #activeErrorsTpl}. Also see {@link #setActiveError}
+     * which allows setting the entire error contents with a single string.
+     * @param {Array} errors The error messages
+     */
+    setActiveErrors: function(errors) {
+        this.activeErrors = errors;
+        this.activeError = this.getTpl('activeErrorsTpl').apply({errors: errors});
+        this.renderActiveError();
+    },
+
+    /**
+     * Clears the active error.
+     */
+    unsetActiveError: function() {
+        delete this.activeError;
+        delete this.activeErrors;
+        this.renderActiveError();
+    },
+
+    /**
+     * @private
+     * Updates the rendered DOM to match the current activeError. This only updates the content and
+     * attributes, you'll have to call doComponentLayout to actually update the display.
+     */
+    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) {
+            // Add/remove invalid class
+            me.el[hasError ? 'addCls' : 'removeCls'](me.invalidCls);
+
+            // Update the aria-invalid attribute
+            me.getActionEl().dom.setAttribute('aria-invalid', hasError);
+
+            // Update the errorEl with the error message text
+            me.errorEl.dom.innerHTML = activeError;
+        }
+    },
+
+    /**
+     * Applies a set of default configuration values to this Labelable instance. For each of the
+     * properties in the given object, check if this component hasOwnProperty that config; if not
+     * then it's inheriting a default value from its prototype and we should apply the default value.
+     * @param {Object} defaults The defaults to apply to the object.
+     */
+    setFieldDefaults: function(defaults) {
+        var me = this;
+        Ext.iterate(defaults, function(key, val) {
+            if (!me.hasOwnProperty(key)) {
+                me[key] = val;
+            }
+        });
+    },
+
+    /**
+     * @protected Calculate and return the natural width of the bodyEl. Override to provide custom logic.
+     * Note for implementors: if at all possible this method should be overridden with a custom implementation
+     * that can avoid anything that would cause the browser to reflow, e.g. querying offsetWidth.
+     */
+    getBodyNaturalWidth: function() {
+        return this.bodyEl.getWidth();
+    }
+
+});
+
+/**
+ * @class Ext.form.field.Field
+
+This mixin provides a common interface for the logical behavior and state of form fields, including:
+
+- Getter and setter methods for field values
+- Events and methods for tracking value and validity changes
+- Methods for triggering validation
+
+**NOTE**: When implementing custom fields, it is most likely that you will want to extend the {@link Ext.form.field.Base}
+component class rather than using this mixin directly, as BaseField contains additional logic for generating an
+actual DOM complete with {@link Ext.form.Labelable label and error message} display and a form input field,
+plus methods that bind the Field value getters and setters to the input field's value.
+
+If you do want to implement this mixin directly and don't want to extend {@link Ext.form.field.Base}, then
+you will most likely want to override the following methods with custom implementations: {@link #getValue},
+{@link #setValue}, and {@link #getErrors}. Other methods may be overridden as needed but their base
+implementations should be sufficient for common cases. You will also need to make sure that {@link #initField}
+is called during the component's initialization.
+
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+Ext.define('Ext.form.field.Field', {
+
+    /**
+     * @property isFormField
+     * @type {Boolean}
+     * Flag denoting that this component is a Field. Always true.
+     */
+    isFormField : true,
+
+    /**
+     * @cfg {Mixed} value A value to initialize this field with (defaults to undefined).
+     */
+    
+    /**
+     * @cfg {String} name The name of the field (defaults to undefined). By default this is used as the parameter
+     * name when including the {@link #getSubmitData field value} in a {@link Ext.form.Basic#submit form submit()}.
+     * To prevent the field from being included in the form submit, set {@link #submitValue} to <tt>false</tt>.
+     */
+
+    /**
+     * @cfg {Boolean} disabled True to disable the field (defaults to false). Disabled Fields will not be
+     * {@link Ext.form.Basic#submit submitted}.</p>
+     */
+    disabled : false,
+
+    /**
+     * @cfg {Boolean} submitValue Setting this to <tt>false</tt> will prevent the field from being
+     * {@link Ext.form.Basic#submit submitted} even when it is not disabled. Defaults to <tt>true</tt>.
+     */
+    submitValue: true,
+
+    /**
+     * @cfg {Boolean} validateOnChange
+     * <p>Specifies whether this field should be validated immediately whenever a change in its value is detected.
+     * Defaults to <tt>true</tt>. If the validation results in a change in the field's validity, a
+     * {@link #validitychange} event will be fired. This allows the field to show feedback about the
+     * validity of its contents immediately as the user is typing.</p>
+     * <p>When set to <tt>false</tt>, feedback will not be immediate. However the form will still be validated
+     * before submitting if the <tt>clientValidation</tt> option to {@link Ext.form.Basic#doAction} is
+     * enabled, or if the field or form are validated manually.</p>
+     * <p>See also {@link Ext.form.field.Base#checkChangeEvents}for controlling how changes to the field's value are detected.</p>
+     */
+    validateOnChange: true,
+
+    /**
+     * @private
+     */
+    suspendCheckChange: 0,
+
+    /**
+     * Initializes this Field mixin on the current instance. Components using this mixin should call
+     * this method during their own initialization process.
+     */
+    initField: function() {
+        this.addEvents(
+            /**
+             * @event change
+             * Fires when a user-initiated change is detected in the value of the field.
+             * @param {Ext.form.field.Field} this
+             * @param {Mixed} newValue The new value
+             * @param {Mixed} oldValue The original value
+             */
+            'change',
+            /**
+             * @event validitychange
+             * Fires when a change in the field's validity is detected.
+             * @param {Ext.form.field.Field} this
+             * @param {Boolean} isValid Whether or not the field is now valid
+             */
+            'validitychange',
+            /**
+             * @event dirtychange
+             * Fires when a change in the field's {@link #isDirty} state is detected.
+             * @param {Ext.form.field.Field} this
+             * @param {Boolean} isDirty Whether or not the field is now dirty
+             */
+            'dirtychange'
+        );
+
+        this.initValue();
+    },
+
+    /**
+     * @protected
+     * Initializes the field's value based on the initial config.
+     */
+    initValue: function() {
+        var me = this;
+
+        /**
+         * @property originalValue
+         * @type Mixed
+         * The original value of the field as configured in the {@link #value} configuration, or as loaded by
+         * the last form load operation if the form's {@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad}
+         * setting is <code>true</code>.
+         */
+        me.originalValue = me.lastValue = me.value;
+
+        // Set the initial value - prevent validation on initial set
+        me.suspendCheckChange++;
+        me.setValue(me.value);
+        me.suspendCheckChange--;
+    },
+
+    /**
+     * Returns the {@link Ext.form.field.Field#name name} attribute of the field. This is used as the parameter
+     * name when including the field value in a {@link Ext.form.Basic#submit form submit()}.
+     * @return {String} name The field {@link Ext.form.field.Field#name name}
+     */
+    getName: function() {
+        return this.name;
+    },
+
+    /**
+     * Returns the current data value of the field. The type of value returned is particular to the type of the
+     * particular field (e.g. a Date object for {@link Ext.form.field.Date}).
+     * @return {Mixed} value The field value
+     */
+    getValue: function() {
+        return this.value;
+    },
+    
+    /**
+     * Sets a data value into the field and runs the change detection and validation.
+     * @param {Mixed} value The value to set
+     * @return {Ext.form.field.Field} this
+     */
+    setValue: function(value) {
+        var me = this;
+        me.value = value;
+        me.checkChange();
+        return me;
+    },
+
+    /**
+     * Returns whether two field {@link #getValue values} are logically equal. Field implementations may override
+     * this to provide custom comparison logic appropriate for the particular field's data type.
+     * @param {Mixed} value1 The first value to compare
+     * @param {Mixed} value2 The second value to compare
+     * @return {Boolean} True if the values are equal, false if inequal.
+     */
+    isEqual: function(value1, value2) {
+        return String(value1) === String(value2);
+    },
+
+    /**
+     * <p>Returns the parameter(s) that would be included in a standard form submit for this field. Typically this
+     * will be an object with a single name-value pair, the name being this field's {@link #getName name} and the
+     * value being its current stringified value. More advanced field implementations may return more than one
+     * name-value pair.</p>
+     * <p>Note that the values returned from this method are not guaranteed to have been successfully
+     * {@link #validate validated}.</p>
+     * @return {Object} A mapping of submit parameter names to values; each value should be a string, or an array
+     * of strings if that particular name has multiple values. It can also return <tt>null</tt> if there are no
+     * parameters to be submitted.
+     */
+    getSubmitData: function() {
+        var me = this,
+            data = null;
+        if (!me.disabled && me.submitValue && !me.isFileUpload()) {
+            data = {};
+            data[me.getName()] = '' + me.getValue();
+        }
+        return data;
+    },
+
+    /**
+     * <p>Returns the value(s) that should be saved to the {@link Ext.data.Model} instance for this field, when
+     * {@link Ext.form.Basic#updateRecord} is called. Typically this will be an object with a single name-value
+     * pair, the name being this field's {@link #getName name} and the value being its current data value. More
+     * advanced field implementations may return more than one name-value pair. The returned values will be
+     * saved to the corresponding field names in the Model.</p>
+     * <p>Note that the values returned from this method are not guaranteed to have been successfully
+     * {@link #validate validated}.</p>
+     * @return {Object} A mapping of submit parameter names to values; each value should be a string, or an array
+     * of strings if that particular name has multiple values. It can also return <tt>null</tt> if there are no
+     * parameters to be submitted.
+     */
+    getModelData: function() {
+        var me = this,
+            data = null;
+        if (!me.disabled && !me.isFileUpload()) {
+            data = {};
+            data[me.getName()] = me.getValue();
+        }
+        return data;
+    },
+
+    /**
+     * Resets the current field value to the originally loaded value and clears any validation messages.
+     * See {@link Ext.form.Basic}.{@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad}
+     */
+    reset : function(){
+        var me = this;
+        
+        me.setValue(me.originalValue);
+        me.clearInvalid();
+        // delete here so we reset back to the original state
+        delete me.wasValid;
+    },
+
+    /**
+     * Resets the field's {@link #originalValue} property so it matches the current {@link #getValue value}.
+     * This is called by {@link Ext.form.Basic}.{@link Ext.form.Basic#setValues setValues} if the form's
+     * {@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad} property is set to true.
+     */
+    resetOriginalValue: function() {
+        this.originalValue = this.getValue();
+        this.checkDirty();
+    },
+
+    /**
+     * <p>Checks whether the value of the field has changed since the last time it was checked. If the value
+     * has changed, it:</p>
+     * <ol>
+     * <li>Fires the {@link #change change event},</li>
+     * <li>Performs validation if the {@link #validateOnChange} config is enabled, firing the
+     * {@link #validationchange validationchange event} if the validity has changed, and</li>
+     * <li>Checks the {@link #isDirty dirty state} of the field and fires the {@link #dirtychange dirtychange event}
+     * if it has changed.</li>
+     * </ol>
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Called when the field's value changes. Performs validation if the {@link #validateOnChange}
+     * config is enabled, and invokes the dirty check.
+     */
+    onChange: function(newVal, oldVal) {
+        if (this.validateOnChange) {
+            this.validate();
+        }
+        this.checkDirty();
+    },
+
+    /**
+     * <p>Returns true if the value of this Field has been changed from its {@link #originalValue}.
+     * Will always return false if the field is disabled.</p>
+     * <p>Note that if the owning {@link Ext.form.Basic form} was configured with
+     * {@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad}
+     * then the {@link #originalValue} is updated when the values are loaded by
+     * {@link Ext.form.Basic}.{@link Ext.form.Basic#setValues setValues}.</p>
+     * @return {Boolean} True if this field has been changed from its original value (and
+     * is not disabled), false otherwise.
+     */
+    isDirty : function() {
+        var me = this;
+        return !me.disabled && !me.isEqual(me.getValue(), me.originalValue);
+    },
+
+    /**
+     * Checks the {@link #isDirty} state of the field and if it has changed since the last time
+     * it was checked, fires the {@link #dirtychange} event.
+     */
+    checkDirty: function() {
+        var me = this,
+            isDirty = me.isDirty();
+        if (isDirty !== me.wasDirty) {
+            me.fireEvent('dirtychange', me, isDirty);
+            me.onDirtyChange(isDirty);
+            me.wasDirty = isDirty;
+        }
+    },
+
+    /**
+     * @private Called when the field's dirty state changes.
+     * @param {Boolean} isDirty
+     */
+    onDirtyChange: Ext.emptyFn,
+
+    /**
+     * <p>Runs this field's validators and returns an array of error messages for any validation failures.
+     * This is called internally during validation and would not usually need to be used manually.</p>
+     * <p>Each subclass should override or augment the return value to provide their own errors.</p>
+     * @param {Mixed} value The value to get errors for (defaults to the current field value)
+     * @return {Array} All error messages for this field; an empty Array if none.
+     */
+    getErrors: function(value) {
+        return [];
+    },
+
+    /**
+     * <p>Returns whether or not the field value is currently valid by {@link #getErrors validating} the
+     * field's current value. The {@link #validitychange} event will not be fired; use {@link #validate}
+     * instead if you want the event to fire. <b>Note</b>: {@link #disabled} fields are always treated as valid.</p>
+     * <p>Implementations are encouraged to ensure that this method does not have side-effects such as
+     * triggering error message display.</p>
+     * @return {Boolean} True if the value is valid, else false
+     */
+    isValid : function() {
+        var me = this;
+        return me.disabled || Ext.isEmpty(me.getErrors());
+    },
+
+    /**
+     * <p>Returns whether or not the field value is currently valid by {@link #getErrors validating} the
+     * field's current value, and fires the {@link #validitychange} event if the field's validity has
+     * changed since the last validation. <b>Note</b>: {@link #disabled} fields are always treated as valid.</p>
+     * <p>Custom implementations of this method are allowed to have side-effects such as triggering error
+     * message display. To validate without side-effects, use {@link #isValid}.</p>
+     * @return {Boolean} True if the value is valid, else false
+     */
+    validate : function() {
+        var me = this,
+            isValid = me.isValid();
+        if (isValid !== me.wasValid) {
+            me.wasValid = isValid;
+            me.fireEvent('validitychange', me, isValid);
+        }
+        return isValid;
+    },
+
+    /**
+     * A utility for grouping a set of modifications which may trigger value changes into a single
+     * transaction, to prevent excessive firing of {@link #change} events. This is useful for instance
+     * if the field has sub-fields which are being updated as a group; you don't want the container
+     * field to check its own changed state for each subfield change.
+     * @param fn A function containing the transaction code
+     */
+    batchChanges: function(fn) {
+        this.suspendCheckChange++;
+        fn();
+        this.suspendCheckChange--;
+        this.checkChange();
+    },
+
+    /**
+     * Returns whether this Field is a file upload field; if it returns true, forms will use
+     * special techniques for {@link Ext.form.Basic#submit submitting the form} via AJAX. See
+     * {@link Ext.form.Basic#hasUpload} for details. If this returns true, the {@link #extractFileInput}
+     * method must also be implemented to return the corresponding file input element.
+     * @return {Boolean}
+     */
+    isFileUpload: function() {
+        return false;
+    },
+
+    /**
+     * Only relevant if the instance's {@link #isFileUpload} method returns true. Returns a reference
+     * to the file input DOM element holding the user's selected file. The input will be appended into
+     * the submission form and will not be returned, so this method should also create a replacement.
+     * @return {HTMLInputElement}
+     */
+    extractFileInput: function() {
+        return null;
+    },
+
+    /**
+     * <p>Associate one or more error messages with this field. Components using this mixin should implement
+     * this method to update the component's rendering to display the messages.</p>
+     * <p><b>Note</b>: this method does not cause the Field's {@link #validate} or {@link #isValid} methods to
+     * return <code>false</code> if the value does <i>pass</i> validation. So simply marking a Field as invalid
+     * will not prevent submission of forms submitted with the {@link Ext.form.action.Submit#clientValidation}
+     * option set.</p>
+     * @param {String/Array} errors The error message(s) for the field.
+     */
+    markInvalid: Ext.emptyFn,
+
+    /**
+     * <p>Clear any invalid styles/messages for this field. Components using this mixin should implement
+     * this method to update the components rendering to clear any existing messages.</p>
+     * <p><b>Note</b>: this method does not cause the Field's {@link #validate} or {@link #isValid} methods to
+     * return <code>true</code> if the value does not <i>pass</i> validation. So simply clearing a field's errors
+     * will not necessarily allow submission of forms submitted with the {@link Ext.form.action.Submit#clientValidation}
+     * option set.</p>
+     */
+    clearInvalid: Ext.emptyFn
+
+});
+
+/**
+ * @class Ext.layout.component.field.Field
+ * @extends Ext.layout.component.Component
+ * Layout class for components with {@link Ext.form.Labelable field labeling}, handling the sizing and alignment of
+ * the form control, label, and error message treatment.
+ * @private
+ */
+Ext.define('Ext.layout.component.field.Field', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.field'],
+
+    extend: 'Ext.layout.component.Component',
+
+    uses: ['Ext.tip.QuickTip', 'Ext.util.TextMetrics'],
+
+    /* End Definitions */
+
+    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) { //first pass lastComponentSize.width is -Infinity
+                width = undef;
+            }
+        }
+        if (!isDefined(height)) {
+            height = lastSize.height;
+            if (height < 0) { //first pass lastComponentSize.height is -Infinity
+                height = undef;
+            }
+        }
+        autoWidth = !isNumber(width);
+        autoHeight = !isNumber(height);
+
+        info = {
+            autoWidth: autoWidth,
+            autoHeight: autoHeight,
+            width: autoWidth ? owner.getBodyNaturalWidth() : width, //always give a pixel width
+            height: height,
+
+            // insets for the bodyEl from each side of the component layout area
+            insets: {
+                top: 0,
+                right: 0,
+                bottom: 0,
+                left: 0
+            }
+        };
+
+        // NOTE the order of calculating insets and setting styles here is very important; we must first
+        // calculate and set horizontal layout alone, as the horizontal sizing of elements can have an impact
+        // on the vertical sizes due to wrapping, then calculate and set the vertical layout.
+
+        // perform preparation on the label and error (setting css classes, qtips, etc.)
+        labelStrategy.prepare(owner, info);
+        errorStrategy.prepare(owner, info);
+
+        // calculate the horizontal insets for the label and error
+        labelStrategy.adjustHorizInsets(owner, info);
+        errorStrategy.adjustHorizInsets(owner, info);
+
+        // set horizontal styles for label and error based on the current insets
+        labelStrategy.layoutHoriz(owner, info);
+        errorStrategy.layoutHoriz(owner, info);
+
+        // calculate the vertical insets for the label and error
+        labelStrategy.adjustVertInsets(owner, info);
+        errorStrategy.adjustVertInsets(owner, info);
+
+        // set vertical styles for label and error based on the current insets
+        labelStrategy.layoutVert(owner, info);
+        errorStrategy.layoutVert(owner, info);
+
+        // perform sizing of the elements based on the final dimensions and insets
+        if (autoWidth && autoHeight) {
+            // Don't use setTargetSize if auto-sized, so the calculated size is not reused next time
+            me.setElementSize(owner.el, info.width, info.height);
+        } else {
+            me.setTargetSize(info.width, info.height);
+        }
+        me.sizeBody(info);
+
+        me.activeError = owner.getActiveError();
+    },
+
+
+    /**
+     * Perform sizing and alignment of the bodyEl (and children) to match the calculated insets.
+     */
+    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;
+
+        // size the bodyEl
+        me.setElementSize(owner.bodyEl, width, height);
+
+        // size the bodyEl's inner contents if necessary
+        me.sizeBodyContents(width, height);
+    },
+
+    /**
+     * Size the contents of the field body, given the full dimensions of the bodyEl. Does nothing by
+     * default, subclasses can override to handle their specific contents.
+     * @param {Number} width The bodyEl width
+     * @param {Number} height The bodyEl height
+     */
+    sizeBodyContents: Ext.emptyFn,
+
+
+    /**
+     * Return the set of strategy functions from the {@link #labelStrategies labelStrategies collection}
+     * that is appropriate for the field's {@link Ext.form.field.Field#labelAlign labelAlign} config.
+     */
+    getLabelStrategy: function() {
+        var me = this,
+            strategies = me.labelStrategies,
+            labelAlign = me.owner.labelAlign;
+        return strategies[labelAlign] || strategies.base;
+    },
+
+    /**
+     * Return the set of strategy functions from the {@link #errorStrategies errorStrategies collection}
+     * that is appropriate for the field's {@link Ext.form.field.Field#msgTarget msgTarget} config.
+     */
+    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;
+    },
+
+
+
+    /**
+     * Collection of named strategies for laying out and adjusting labels to accommodate error messages.
+     * An appropriate one will be chosen based on the owner field's {@link Ext.form.field.Field#labelAlign} config.
+     */
+    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 auto width, add the label width to the body's natural width.
+                    if (info.autoWidth) {
+                        info.width += (!owner.labelEl ? 0 : owner.labelWidth + owner.labelPad);
+                    }
+                },
+                adjustHorizInsets: function(owner, info) {
+                    if (owner.labelEl) {
+                        info.insets.left += owner.labelWidth + owner.labelPad;
+                    }
+                },
+                layoutHoriz: function(owner, info) {
+                    // For content-box browsers we can't rely on Labelable.js#getLabelableRenderData
+                    // setting the width style because it needs to account for the final calculated
+                    // padding/border styles for the label. So we set the width programmatically here to
+                    // normalize content-box sizing, while letting border-box browsers use the original
+                    // width style.
+                    var labelEl = owner.labelEl;
+                    if (labelEl && !owner.isLabelSized && !Ext.isBorderBox) {
+                        labelEl.setWidth(owner.labelWidth);
+                        owner.isLabelSized = true;
+                    }
+                }
+            }, base);
+
+
+        return {
+            base: base,
+
+            /**
+             * Label displayed above the bodyEl
+             */
+            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),
+
+            /**
+             * Label displayed to the left of the bodyEl
+             */
+            left: left,
+
+            /**
+             * Same as left, only difference is text-align in CSS
+             */
+            right: left
+        };
+    })(),
+
+
+
+    /**
+     * Collection of named strategies for laying out and adjusting insets to accommodate error messages.
+     * An appropriate one will be chosen based on the owner field's {@link Ext.form.field.Field#msgTarget} config.
+     */
+    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,
+
+            /**
+             * Error displayed as icon (with QuickTip on hover) to right of the bodyEl
+             */
+            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),
+
+            /**
+             * Error message displayed underneath the bodyEl
+             */
+            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),
+
+            /**
+             * Error displayed as QuickTip on hover of the field container
+             */
+            qtip: applyIf({
+                prepare: function(owner) {
+                    setDisplayed(owner.errorEl, false);
+                    Ext.layout.component.field.Field.initTip();
+                    owner.getActionEl().dom.setAttribute('data-errorqtip', owner.getActiveError() || '');
+                }
+            }, base),
+
+            /**
+             * Error displayed as title tip on hover of the field container
+             */
+            title: applyIf({
+                prepare: function(owner) {
+                    setDisplayed(owner.errorEl, false);
+                    owner.el.dom.title = owner.getActiveError() || '';
+                }
+            }, base),
+
+            /**
+             * Error message displayed as content of an element with a given id elsewhere in the app
+             */
+            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: {
+        /**
+         * Use a custom QuickTip instance separate from the main QuickTips singleton, so that we
+         * can give it a custom frame style. Responds to errorqtip rather than the qtip property.
+         */
+        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);
+            }
+        },
+
+        /**
+         * Destroy the error tip instance.
+         */
+        destroyTip: function() {
+            var tip = this.tip;
+            if (tip) {
+                tip.destroy();
+                delete this.tip;
+            }
+        }
+    }
+
+});
+
+/**
+ * @class Ext.form.field.VTypes
+ * <p>This is a singleton object which contains a set of commonly used field validation functions.
+ * The validations provided are basic and intended to be easily customizable and extended.</p>
+ * <p>To add custom VTypes specify the <code>{@link Ext.form.field.Text#vtype vtype}</code> validation
+ * test function, and optionally specify any corresponding error text to display and any keystroke
+ * filtering mask to apply. For example:</p>
+ * <pre><code>
+// custom Vtype for vtype:'time'
+var timeTest = /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i;
+Ext.apply(Ext.form.field.VTypes, {
+    //  vtype validation function
+    time: function(val, field) {
+        return timeTest.test(val);
+    },
+    // vtype Text property: The error text to display when the validation function returns false
+    timeText: 'Not a valid time.  Must be in the format "12:34 PM".',
+    // vtype Mask property: The keystroke filter mask
+    timeMask: /[\d\s:amp]/i
+});
+ * </code></pre>
+ * Another example:
+ * <pre><code>
+// custom Vtype for vtype:'IPAddress'
+Ext.apply(Ext.form.field.VTypes, {
+    IPAddress:  function(v) {
+        return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(v);
+    },
+    IPAddressText: 'Must be a numeric IP address',
+    IPAddressMask: /[\d\.]/i
+});
+ * </code></pre>
+ * @singleton
+ */
+Ext.define('Ext.form.field.VTypes', (function(){
+    // closure these in so they are only created once.
+    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;
+
+    // All these messages and functions are configurable
+    return {
+        singleton: true,
+        alternateClassName: 'Ext.form.VTypes',
+
+        /**
+         * The function used to validate email addresses.  Note that this is a very basic validation -- complete
+         * validation per the email RFC specifications is very complex and beyond the scope of this class, although
+         * this function can be overridden if a more comprehensive validation scheme is desired.  See the validation
+         * section of the <a href="http://en.wikipedia.org/wiki/E-mail_address">Wikipedia article on email addresses</a>
+         * for additional information.  This implementation is intended to validate the following emails:<tt>
+         * 'barney@example.de', 'barney.rubble@example.com', 'barney-rubble@example.coop', 'barney+rubble@example.com'
+         * </tt>.
+         * @param {String} value The email address
+         * @return {Boolean} true if the RegExp test passed, and false if not.
+         */
+        'email' : function(v){
+            return email.test(v);
+        },
+        /**
+         * The error text to display when the email validation function returns false.  Defaults to:
+         * <tt>'This field should be an e-mail address in the format "user@example.com"'</tt>
+         * @type String
+         */
+        'emailText' : 'This field should be an e-mail address in the format "user@example.com"',
+        /**
+         * The keystroke filter mask to be applied on email input.  See the {@link #email} method for
+         * information about more complex email validation. Defaults to:
+         * <tt>/[a-z0-9_\.\-@]/i</tt>
+         * @type RegExp
+         */
+        'emailMask' : /[a-z0-9_\.\-@\+]/i,
+
+        /**
+         * The function used to validate URLs
+         * @param {String} value The URL
+         * @return {Boolean} true if the RegExp test passed, and false if not.
+         */
+        'url' : function(v){
+            return url.test(v);
+        },
+        /**
+         * The error text to display when the url validation function returns false.  Defaults to:
+         * <tt>'This field should be a URL in the format "http:/'+'/www.example.com"'</tt>
+         * @type String
+         */
+        'urlText' : 'This field should be a URL in the format "http:/'+'/www.example.com"',
+
+        /**
+         * The function used to validate alpha values
+         * @param {String} value The value
+         * @return {Boolean} true if the RegExp test passed, and false if not.
+         */
+        'alpha' : function(v){
+            return alpha.test(v);
+        },
+        /**
+         * The error text to display when the alpha validation function returns false.  Defaults to:
+         * <tt>'This field should only contain letters and _'</tt>
+         * @type String
+         */
+        'alphaText' : 'This field should only contain letters and _',
+        /**
+         * The keystroke filter mask to be applied on alpha input.  Defaults to:
+         * <tt>/[a-z_]/i</tt>
+         * @type RegExp
+         */
+        'alphaMask' : /[a-z_]/i,
+
+        /**
+         * The function used to validate alphanumeric values
+         * @param {String} value The value
+         * @return {Boolean} true if the RegExp test passed, and false if not.
+         */
+        'alphanum' : function(v){
+            return alphanum.test(v);
+        },
+        /**
+         * The error text to display when the alphanumeric validation function returns false.  Defaults to:
+         * <tt>'This field should only contain letters, numbers and _'</tt>
+         * @type String
+         */
+        'alphanumText' : 'This field should only contain letters, numbers and _',
+        /**
+         * The keystroke filter mask to be applied on alphanumeric input.  Defaults to:
+         * <tt>/[a-z0-9_]/i</tt>
+         * @type RegExp
+         */
+        'alphanumMask' : /[a-z0-9_]/i
+    };
+})());
+
+/**
+ * @private
+ * @class Ext.layout.component.field.Text
+ * @extends Ext.layout.component.field.Field
+ * Layout class for {@link Ext.form.field.Text} fields. Handles sizing the input field.
+ */
+Ext.define('Ext.layout.component.field.Text', {
+    extend: 'Ext.layout.component.field.Field',
+    alias: 'layout.textfield',
+    requires: ['Ext.util.TextMetrics'],
+
+    type: 'textfield',
+
+
+    /**
+     * Allow layout to proceed if the {@link Ext.form.field.Text#grow} config is enabled and the value has
+     * changed since the last layout.
+     */
+    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);
+    },
+
+
+    /**
+     * Size the field body contents given the total dimensions of the bodyEl, taking into account the optional
+     * {@link Ext.form.field.Text#grow} configurations.
+     * @param {Number} width The bodyEl width
+     * @param {Number} height The bodyEl height
+     */
+    sizeBodyContents: function(width, height) {
+        var size = this.adjustForGrow(width, height);
+        this.setElementSize(this.owner.inputEl, size[0], size[1]);
+    },
+
+
+    /**
+     * Given the target bodyEl dimensions, adjust them if necessary to return the correct final
+     * size based on the text field's {@link Ext.form.field.Text#grow grow config}.
+     * @param {Number} width The bodyEl width
+     * @param {Number} height The bodyEl height
+     * @return {Array} [inputElWidth, inputElHeight]
+     */
+    adjustForGrow: function(width, height) {
+        var me = this,
+            owner = me.owner,
+            inputEl, value, calcWidth,
+            result = [width, height];
+
+        if (owner.grow) {
+            inputEl = owner.inputEl;
+
+            // Find the width that contains the whole text value
+            value = (inputEl.dom.value || (owner.hasFocus ? '' : owner.emptyText) || '') + owner.growAppend;
+            calcWidth = inputEl.getTextWidth(value) + inputEl.getBorderWidth("lr") + inputEl.getPadding("lr");
+
+            // Constrain
+            result[0] = Ext.Number.constrain(calcWidth, owner.growMin,
+                    Math.max(owner.growMin, Math.min(owner.growMax, Ext.isNumber(width) ? width : Infinity)));
+        }
+
+        return result;
+    }
+
+});
+
+/**
+ * @private
+ * @class Ext.layout.component.field.TextArea
+ * @extends Ext.layout.component.field.Field
+ * Layout class for {@link Ext.form.field.TextArea} fields. Handles sizing the textarea field.
+ */
+Ext.define('Ext.layout.component.field.TextArea', {
+    extend: 'Ext.layout.component.field.Text',
+    alias: 'layout.textareafield',
+
+    type: 'textareafield',
+
+
+    /**
+     * Given the target bodyEl dimensions, adjust them if necessary to return the correct final
+     * size based on the text field's {@link Ext.form.field.Text#grow grow config}. Overrides the
+     * textfield layout's implementation to handle height rather than width.
+     * @param {Number} width The bodyEl width
+     * @param {Number} height The bodyEl height
+     * @return {Array} [inputElWidth, inputElHeight]
+     */
+    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); //subtract border/padding to get the available width for the text
+            curHeight = inputEl.getHeight();
+
+            // Get and normalize the field value for measurement
+            value = inputEl.dom.value || '&#160;';
+            value += owner.growAppend;
+
+            // Translate newlines to <br> tags
+            value = value.replace(/\n/g, '<br>');
+
+            // Find the height that contains the whole text value
+            calcHeight = Ext.util.TextMetrics.measure(inputEl, value, curWidth).height +
+                         inputEl.getBorderWidth("tb") + inputEl.getPadding("tb");
+
+            // Constrain
+            max = owner.growMax;
+            if (Ext.isNumber(height)) {
+                max = Math.min(max, height);
+            }
+            result[1] = Ext.Number.constrain(calcHeight, owner.growMin, max);
+        }
+
+        return result;
+    }
+
+});
+/**
+ * @class Ext.layout.container.Anchor
+ * @extends Ext.layout.container.Container
+ * <p>This is a layout that enables anchoring of contained elements relative to the container's dimensions.
+ * If the container is resized, all anchored items are automatically rerendered according to their
+ * <b><tt>{@link #anchor}</tt></b> rules.</p>
+ * <p>This class is intended to be extended or created via the layout: 'anchor' {@link Ext.layout.container.AbstractContainer#layout}
+ * config, and should generally not need to be created directly via the new keyword.</p>
+ * <p>AnchorLayout does not have any direct config options (other than inherited ones). By default,
+ * AnchorLayout will calculate anchor measurements based on the size of the container itself. However, the
+ * container using the AnchorLayout can supply an anchoring-specific config property of <b>anchorSize</b>.
+ * If anchorSize is specifed, the layout will use it as a virtual container for the purposes of calculating
+ * anchor measurements based on it instead, allowing the container to be sized independently of the anchoring
+ * logic if necessary.  
+ * {@img Ext.layout.container.Anchor/Ext.layout.container.Anchor.png Ext.layout.container.Anchor container layout}
+ * For example:
+       Ext.create('Ext.Panel', {
+               width: 500,
+               height: 400,
+               title: "AnchorLayout Panel",
+               layout: 'anchor',
+               renderTo: Ext.getBody(),
+               items: [{
+                       xtype: 'panel',
+                       title: '75% Width and 20% Height',
+                       anchor: '75% 20%'
+               },{
+                       xtype: 'panel',
+                       title: 'Offset -300 Width & -200 Height',
+                       anchor: '-300 -200'             
+               },{
+                       xtype: 'panel',
+                       title: 'Mixed Offset and Percent',
+                       anchor: '-250 20%'
+               }]
+       });
+ */
+
+Ext.define('Ext.layout.container.Anchor', {
+
+    /* Begin Definitions */
+
+    alias: 'layout.anchor',
+    extend: 'Ext.layout.container.Container',
+    alternateClassName: 'Ext.layout.AnchorLayout',
+
+    /* End Definitions */
+
+    /**
+     * @cfg {String} anchor
+     * <p>This configuation option is to be applied to <b>child <tt>items</tt></b> of a container managed by
+     * this layout (ie. configured with <tt>layout:'anchor'</tt>).</p><br/>
+     *
+     * <p>This value is what tells the layout how an item should be anchored to the container. <tt>items</tt>
+     * added to an AnchorLayout accept an anchoring-specific config property of <b>anchor</b> which is a string
+     * containing two values: the horizontal anchor value and the vertical anchor value (for example, '100% 50%').
+     * The following types of anchor values are supported:<div class="mdetail-params"><ul>
+     *
+     * <li><b>Percentage</b> : Any value between 1 and 100, expressed as a percentage.<div class="sub-desc">
+     * The first anchor is the percentage width that the item should take up within the container, and the
+     * second is the percentage height.  For example:<pre><code>
+// two values specified
+anchor: '100% 50%' // render item complete width of the container and
+                   // 1/2 height of the container
+// one value specified
+anchor: '100%'     // the width value; the height will default to auto
+     * </code></pre></div></li>
+     *
+     * <li><b>Offsets</b> : Any positive or negative integer value.<div class="sub-desc">
+     * This is a raw adjustment where the first anchor is the offset from the right edge of the container,
+     * and the second is the offset from the bottom edge. For example:<pre><code>
+// two values specified
+anchor: '-50 -100' // render item the complete width of the container
+                   // minus 50 pixels and
+                   // the complete height minus 100 pixels.
+// one value specified
+anchor: '-50'      // anchor value is assumed to be the right offset value
+                   // bottom offset will default to 0
+     * </code></pre></div></li>
+     *
+     * <li><b>Sides</b> : Valid values are <tt>'right'</tt> (or <tt>'r'</tt>) and <tt>'bottom'</tt>
+     * (or <tt>'b'</tt>).<div class="sub-desc">
+     * Either the container must have a fixed size or an anchorSize config value defined at render time in
+     * order for these to have any effect.</div></li>
+     *
+     * <li><b>Mixed</b> : <div class="sub-desc">
+     * Anchor values can also be mixed as needed.  For example, to render the width offset from the container
+     * right edge by 50 pixels and 75% of the container's height use:
+     * <pre><code>
+anchor: '-50 75%'
+     * </code></pre></div></li>
+     *
+     *
+     * </ul></div>
+     */
+
+    type: 'anchor',
+
+    /**
+     * @cfg {String} defaultAnchor
+     *
+     * default anchor for all child container items applied if no anchor or specific width is set on the child item.  Defaults to '100%'.
+     *
+     */
+    defaultAnchor: '100%',
+
+    parseAnchorRE: /^(r|right|b|bottom)$/i,
+
+    // private
+    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;
+
+        if (ownerWidth < 20 && ownerHeight < 20) {
+            return;
+        }
+
+        // Anchor layout uses natural HTML flow to arrange the child items.
+        // To ensure that all browsers (I'm looking at you IE!) add the bottom margin of the last child to the
+        // containing element height, we create a zero-sized element with style clear:both to force a "new line"
+        if (!me.clearEl) {
+            me.clearEl = target.createChild({
+                cls: Ext.baseCSSPrefix + 'clear',
+                role: 'presentation'
+            });
+        }
+
+        // find the container anchoring size
+        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;
+        }
+
+        // Work around WebKit RightMargin bug. We're going to inline-block all the children only ONCE and remove it when we're done
+        if (!Ext.supports.RightMargin) {
+            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;
+                // cache all anchor values
+                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
+                });
+            }
+        }
+
+        // Work around WebKit RightMargin bug. We're going to inline-block all the children only ONCE and remove it when we're done
+        if (!Ext.supports.RightMargin) {
+            target.removeCls(Ext.baseCSSPrefix + 'inline-children');
+        }
+
+        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;
+    },
+
+    // private
+    parseAnchor: function(a, start, cstart) {
+        if (a && a != 'none') {
+            var ratio;
+            // standard anchor
+            if (this.parseAnchorRE.test(a)) {
+                var diff = cstart - start;
+                return function(v) {
+                    return v - diff;
+                };
+            }    
+            // percentage
+            else if (a.indexOf('%') != -1) {
+                ratio = parseFloat(a.replace('%', '')) * 0.01;
+                return function(v) {
+                    return Math.floor(v * ratio);
+                };
+            }    
+            // simple offset adjustment
+            else {
+                a = parseInt(a, 10);
+                if (!isNaN(a)) {
+                    return function(v) {
+                        return v + a;
+                    };
+                }
+            }
+        }
+        return null;
+    },
+
+    // private
+    adjustWidthAnchor: function(value, comp) {
+        return value;
+    },
+
+    // private
+    adjustHeightAnchor: function(value, comp) {
+        return value;
+    }
+
+});
+/**
+ * @class Ext.form.action.Load
+ * @extends Ext.form.action.Action
+ * <p>A class which handles loading of data from a server into the Fields of an {@link Ext.form.Basic}.</p>
+ * <p>Instances of this class are only created by a {@link Ext.form.Basic Form} when
+ * {@link Ext.form.Basic#load load}ing.</p>
+ * <p><u><b>Response Packet Criteria</b></u></p>
+ * <p>A response packet <b>must</b> contain:
+ * <div class="mdetail-params"><ul>
+ * <li><b><code>success</code></b> property : Boolean</li>
+ * <li><b><code>data</code></b> property : Object</li>
+ * <div class="sub-desc">The <code>data</code> property contains the values of Fields to load.
+ * The individual value object for each Field is passed to the Field's
+ * {@link Ext.form.field.Field#setValue setValue} method.</div></li>
+ * </ul></div>
+ * <p><u><b>JSON Packets</b></u></p>
+ * <p>By default, response packets are assumed to be JSON, so for the following form load call:<pre><code>
+var myFormPanel = new Ext.form.Panel({
+    title: 'Client and routing info',
+    items: [{
+        fieldLabel: 'Client',
+        name: 'clientName'
+    }, {
+        fieldLabel: 'Port of loading',
+        name: 'portOfLoading'
+    }, {
+        fieldLabel: 'Port of discharge',
+        name: 'portOfDischarge'
+    }]
+});
+myFormPanel.{@link Ext.form.Panel#getForm getForm}().{@link Ext.form.Basic#load load}({
+    url: '/getRoutingInfo.php',
+    params: {
+        consignmentRef: myConsignmentRef
+    },
+    failure: function(form, action) {
+        Ext.Msg.alert("Load failed", action.result.errorMessage);
+    }
+});
+</code></pre>
+ * a <b>success response</b> packet may look like this:</p><pre><code>
+{
+    success: true,
+    data: {
+        clientName: "Fred. Olsen Lines",
+        portOfLoading: "FXT",
+        portOfDischarge: "OSL"
+    }
+}</code></pre>
+ * while a <b>failure response</b> packet may look like this:</p><pre><code>
+{
+    success: false,
+    errorMessage: "Consignment reference not found"
+}</code></pre>
+ * <p>Other data may be placed into the response for processing the {@link Ext.form.Basic Form}'s
+ * callback or event handler methods. The object decoded from this JSON is available in the
+ * {@link Ext.form.action.Action#result result} property.</p>
+ */
+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',
+
+    /**
+     * @private
+     */
+    run: function() {
+        Ext.Ajax.request(Ext.apply(
+            this.createCallback(),
+            {
+                method: this.getMethod(),
+                url: this.getUrl(),
+                headers: this.headers,
+                params: this.getParams()
+            }
+        ));
+    },
+
+    /**
+     * @private
+     */
+    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);
+    },
+
+    /**
+     * @private
+     */
+    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);
+    }
+});
+
+
+/**
+ * @class Ext.window.Window
+ * @extends Ext.panel.Panel
+ * <p>A specialized panel intended for use as an application window.  Windows are floated, {@link #resizable}, and
+ * {@link #draggable} by default.  Windows can be {@link #maximizable maximized} to fill the viewport,
+ * restored to their prior size, and can be {@link #minimize}d.</p>
+ * <p>Windows can also be linked to a {@link Ext.ZIndexManager} or managed by the {@link Ext.WindowManager} to provide
+ * grouping, activation, to front, to back and other application-specific behavior.</p>
+ * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element
+ * specify {@link Ext.Component#renderTo renderTo}.</p>
+ * <p><b>As with all {@link Ext.container.Container Container}s, it is important to consider how you want the Window
+ * to size and arrange any child Components. Choose an appropriate {@link #layout} configuration which lays out
+ * child Components in the required manner.</b></p>
+ * {@img Ext.window.Window/Ext.window.Window.png Window component}
+ * Example:<code><pre>
+Ext.create('Ext.window.Window', {
+    title: 'Hello',
+    height: 200,
+    width: 400,
+    layout: 'fit',
+    items: {  // Let's put an empty grid in just to illustrate fit layout
+        xtype: 'grid',
+        border: false,
+        columns: [{header: 'World'}],                 // One header just for show. There's no data,
+        store: Ext.create('Ext.data.ArrayStore', {}) // A dummy empty data store
+    }
+}).show();
+</pre></code>
+ * @constructor
+ * @param {Object} config The config object
+ * @xtype window
+ */
+Ext.define('Ext.window.Window', {
+    extend: 'Ext.panel.Panel',
+
+    alternateClassName: 'Ext.Window',
+
+    requires: ['Ext.util.ComponentDragger', 'Ext.util.Region', 'Ext.EventManager'],
+
+    alias: 'widget.window',
+
+    /**
+     * @cfg {Number} x
+     * The X position of the left edge of the window on initial showing. Defaults to centering the Window within
+     * the width of the Window's container {@link Ext.core.Element Element) (The Element that the Window is rendered to).
+     */
+    /**
+     * @cfg {Number} y
+     * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within
+     * the height of the Window's container {@link Ext.core.Element Element) (The Element that the Window is rendered to).
+     */
+    /**
+     * @cfg {Boolean} modal
+     * True to make the window modal and mask everything behind it when displayed, false to display it without
+     * restricting access to other UI elements (defaults to false).
+     */
+    /**
+     * @cfg {String/Element} animateTarget
+     * Id or element from which the window should animate while opening (defaults to null with no animation).
+     */
+    /**
+     * @cfg {String/Number/Component} defaultFocus
+     * <p>Specifies a Component to receive focus when this Window is focused.</p>
+     * <p>This may be one of:</p><div class="mdetail-params"><ul>
+     * <li>The index of a footer Button.</li>
+     * <li>The id or {@link Ext.AbstractComponent#itemId} of a descendant Component.</li>
+     * <li>A Component.</li>
+     * </ul></div>
+     */
+    /**
+     * @cfg {Function} onEsc
+     * Allows override of the built-in processing for the escape key. Default action
+     * is to close the Window (performing whatever action is specified in {@link #closeAction}.
+     * To prevent the Window closing when the escape key is pressed, specify this as
+     * Ext.emptyFn (See {@link Ext#emptyFn Ext.emptyFn}).
+     */
+    /**
+     * @cfg {Boolean} collapsed
+     * True to render the window collapsed, false to render it expanded (defaults to false). Note that if
+     * {@link #expandOnShow} is true (the default) it will override the <code>collapsed</code> config and the window
+     * will always be expanded when shown.
+     */
+    /**
+     * @cfg {Boolean} maximized
+     * True to initially display the window in a maximized state. (Defaults to false).
+     */
+
+    /**
+    * @cfg {String} baseCls
+    * The base CSS class to apply to this panel's element (defaults to 'x-window').
+    */
+    baseCls: Ext.baseCSSPrefix + 'window',
+
+    /**
+     * @cfg {Mixed} resizable
+     * <p>Specify as <code>true</code> to allow user resizing at each edge and corner of the window, false to disable
+     * resizing (defaults to true).</p>
+     * <p>This may also be specified as a config object to </p>
+     */
+    resizable: true,
+
+    /**
+     * @cfg {Boolean} draggable
+     * <p>True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  Note
+     * that by default the window will be centered in the viewport, so if dragging is disabled the window may need
+     * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).<p>
+     */
+    draggable: true,
+
+    /**
+     * @cfg {Boolean} constrain
+     * True to constrain the window within its containing element, false to allow it to fall outside of its
+     * containing element. By default the window will be rendered to document.body.  To render and constrain the
+     * window within another element specify {@link #renderTo}.
+     * (defaults to false).  Optionally the header only can be constrained using {@link #constrainHeader}.
+     */
+    constrain: false,
+
+    /**
+     * @cfg {Boolean} constrainHeader
+     * True to constrain the window header within its containing element (allowing the window body to fall outside
+     * of its containing element) or false to allow the header to fall outside its containing element (defaults to
+     * false). Optionally the entire window can be constrained using {@link #constrain}.
+     */
+    constrainHeader: false,
+
+    /**
+     * @cfg {Boolean} plain
+     * True to render the window body with a transparent background so that it will blend into the framing
+     * elements, false to add a lighter background color to visually highlight the body element and separate it
+     * more distinctly from the surrounding frame (defaults to false).
+     */
+    plain: false,
+
+    /**
+     * @cfg {Boolean} minimizable
+     * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
+     * and disallow minimizing the window (defaults to false).  Note that this button provides no implementation --
+     * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a
+     * custom minimize behavior implemented for this option to be useful.
+     */
+    minimizable: false,
+
+    /**
+     * @cfg {Boolean} maximizable
+     * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
+     * and disallow maximizing the window (defaults to false).  Note that when a window is maximized, the tool button
+     * will automatically change to a 'restore' button with the appropriate behavior already built-in that will
+     * restore the window to its previous size.
+     */
+    maximizable: false,
+
+    // inherit docs
+    minHeight: 100,
+
+    // inherit docs
+    minWidth: 200,
+
+    /**
+     * @cfg {Boolean} expandOnShow
+     * True to always expand the window when it is displayed, false to keep it in its current state (which may be
+     * {@link #collapsed}) when displayed (defaults to true).
+     */
+    expandOnShow: true,
+
+    // inherited docs, same default
+    collapsible: false,
+
+    /**
+     * @cfg {Boolean} closable
+     * <p>True to display the 'close' tool button and allow the user to close the window, false to
+     * hide the button and disallow closing the window (defaults to <code>true</code>).</p>
+     * <p>By default, when close is requested by either clicking the close button in the header
+     * or pressing ESC when the Window has focus, the {@link #close} method will be called. This
+     * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that
+     * it may not be reused.</p>
+     * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set
+     * {@link #closeAction} to 'hide'.</p>
+     */
+    closable: true,
+
+    /**
+     * @cfg {Boolean} hidden
+     * Render this Window hidden (default is <code>true</code>). If <code>true</code>, the
+     * {@link #hide} method will be called internally.
+     */
+    hidden: true,
+
+    // Inherit docs from Component. Windows render to the body on first show.
+    autoRender: true,
+
+    // Inherit docs from Component. Windows hide using visibility.
+    hideMode: 'visibility',
+
+    /** @cfg {Boolean} floating @hide Windows are always floating*/
+    floating: true,
+
+    ariaRole: 'alertdialog',
+    
+    itemCls: 'x-window-item',
+
+    overlapHeader: true,
+    
+    ignoreHeaderBorderManagement: true,
+
+    // private
+    initComponent: function() {
+        var me = this;
+        me.callParent();
+        me.addEvents(
+            /**
+             * @event activate
+             * Fires after the window has been visually activated via {@link #setActive}.
+             * @param {Ext.window.Window} this
+             */
+            /**
+             * @event deactivate
+             * Fires after the window has been visually deactivated via {@link #setActive}.
+             * @param {Ext.window.Window} this
+             */
+            /**
+             * @event resize
+             * Fires after the window has been resized.
+             * @param {Ext.window.Window} this
+             * @param {Number} width The window's new width
+             * @param {Number} height The window's new height
+             */
+            'resize',
+            /**
+             * @event maximize
+             * Fires after the window has been maximized.
+             * @param {Ext.window.Window} this
+             */
+            'maximize',
+            /**
+             * @event minimize
+             * Fires after the window has been minimized.
+             * @param {Ext.window.Window} this
+             */
+            'minimize',
+            /**
+             * @event restore
+             * Fires after the window has been restored to its original size after being maximized.
+             * @param {Ext.window.Window} this
+             */
+            'restore'
+        );
+
+        if (me.plain) {
+            me.addClsWithUI('plain');
+        }
+
+        if (me.modal) {
+            me.ariaRole = 'dialog';
+        }
+    },
+
+    // State Management
+    // private
+
+    initStateEvents: function(){
+        var events = this.stateEvents;
+        // push on stateEvents if they don't exist
+        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]
+                });
+            }
+        }
+    },
+
+    // private
+    onMouseDown: function () {
+        if (this.floating) {
+            this.toFront();
+        }
+    },
+
+    // private
+    onRender: function(ct, position) {
+        var me = this;
+        me.callParent(arguments);
+        me.focusEl = me.el;
+
+        // Double clicking a header will toggleMaximize
+        if (me.maximizable) {
+            me.header.on({
+                dblclick: {
+                    fn: me.toggleMaximize,
+                    element: 'el',
+                    scope: me
+                }
+            });
+        }
+    },
+
+    // private
+    afterRender: function() {
+        var me = this,
+            hidden = me.hidden,
+            keyMap;
+
+        me.hidden = false;
+        // Component's afterRender sizes and positions the Component
+        me.callParent();
+        me.hidden = hidden;
+
+        // Create the proxy after the size has been applied in Component.afterRender
+        me.proxy = me.getProxy();
+
+        // clickToRaise
+        me.mon(me.el, 'mousedown', me.onMouseDown, me);
+
+        // Initialize
+        if (me.maximized) {
+            me.maximized = false;
+            me.maximize();
+        }
+
+        if (me.closable) {
+            keyMap = me.getKeyMap();
+            keyMap.on(27, me.onEsc, me);
+            keyMap.disable();
+        }
+    },
+
+    /**
+     * @private
+     * @override
+     * Override Component.initDraggable.
+     * Window uses the header element as the delegate.
+     */
+    initDraggable: function() {
+        var me = this,
+            ddConfig;
+
+        if (!me.header) {
+            me.updateHeader(true);
+        }
+
+        ddConfig = Ext.applyIf({
+            el: me.el,
+            delegate: '#' + me.header.id
+        }, me.draggable);
+
+        // Add extra configs if Window is specified to be constrained
+        if (me.constrain || me.constrainHeader) {
+            ddConfig.constrain = me.constrain;
+            ddConfig.constrainDelegate = me.constrainHeader;
+            ddConfig.constrainTo = me.constrainTo || me.container;
+        }
+
+        /**
+         * <p>If this Window is configured {@link #draggable}, this property will contain
+         * an instance of {@link Ext.util.ComponentDragger} (A subclass of {@link Ext.dd.DragTracker DragTracker})
+         * which handles dragging the Window's DOM Element, and constraining according to the {@link #constrain}
+         * and {@link #constrainHeader} .</p>
+         * <p>This has implementations of <code>onBeforeStart</code>, <code>onDrag</code> and <code>onEnd</code>
+         * which perform the dragging action. If extra logic is needed at these points, use
+         * {@link Ext.Function#createInterceptor createInterceptor} or {@link Ext.Function#createSequence createSequence} to
+         * augment the existing implementations.</p>
+         * @type Ext.util.ComponentDragger
+         * @property dd
+         */
+        me.dd = Ext.create('Ext.util.ComponentDragger', this, ddConfig);
+        me.relayEvents(me.dd, ['dragstart', 'drag', 'dragend']);
+    },
+
+    // private
+    onEsc: function(k, e) {
+        e.stopEvent();
+        this[this.closeAction]();
+    },
+
+    // private
+    beforeDestroy: function() {
+        var me = this;
+        if (me.rendered) {
+            delete this.animateTarget;
+            me.hide();
+            Ext.destroy(
+                me.keyMap
+            );
+        }
+        me.callParent();
+    },
+
+    /**
+     * @private
+     * @override
+     * Contribute class-specific tools to the header.
+     * Called by Panel's initTools.
+     */
+    addTools: function() {
+        var me = this;
+
+        // Call Panel's initTools
+        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
+            });
+        }
+    },
+
+    /**
+     * Gets the configured default focus item.  If a {@link #defaultFocus} is set, it will receive focus, otherwise the
+     * Container itself will receive focus.
+     */
+    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;
+    },
+
+    // private
+    beforeShow: function() {
+        this.callParent();
+
+        if (this.expandOnShow) {
+            this.expand(false);
+        }
+    },
+
+    // private
+    afterShow: function(animateTarget) {
+        var me = this,
+            size;
+
+        // Perform superclass's afterShow tasks
+        // Which might include animating a proxy from an animTarget
+        me.callParent(arguments);
+
+        if (me.maximized) {
+            me.fitContainer();
+        }
+
+        if (me.monitorResize || me.constrain || me.constrainHeader) {
+            Ext.EventManager.onWindowResize(me.onWindowResize, me);
+        }
+        me.doConstrain();
+        if (me.keyMap) {
+            me.keyMap.enable();
+        }
+    },
+
+    // private
+    doClose: function() {
+        var me = this;
+
+        // immediate close
+        if (me.hidden) {
+            me.fireEvent('close', me);
+            me[me.closeAction]();
+        } else {
+            // close after hiding
+            me.hide(me.animTarget, me.doClose, me);
+        }
+    },
+
+    // private
+    afterHide: function() {
+        var me = this;
+
+        // No longer subscribe to resizing now that we're hidden
+        if (me.monitorResize || me.constrain || me.constrainHeader) {
+            Ext.EventManager.removeResizeListener(me.onWindowResize, me);
+        }
+
+        // Turn off keyboard handling once window is hidden
+        if (me.keyMap) {
+            me.keyMap.disable();
+        }
+
+        // Perform superclass's afterHide tasks.
+        me.callParent(arguments);
+    },
+
+    // private
+    onWindowResize: function() {
+        if (this.maximized) {
+            this.fitContainer();
+        }
+        this.doConstrain();
+    },
+
+    /**
+     * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #minimize} event
+     * since the behavior of minimizing a window is application-specific.  To implement custom minimize behavior,
+     * either the minimize event can be handled or this method can be overridden.
+     * @return {Ext.window.Window} this
+     */
+    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);
+    },
+
+    /**
+     * Fits the window within its current container and automatically replaces
+     * the {@link #maximizable 'maximize' tool button} with the 'restore' tool button.
+     * Also see {@link #toggleMaximize}.
+     * @return {Ext.window.Window} this
+     */
+    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.setPosition(0, 0);
+            me.fitContainer();
+            me.fireEvent('maximize', me);
+        }
+        return me;
+    },
+
+    /**
+     * Restores a {@link #maximizable maximized}  window back to its original
+     * size and position prior to being maximized and also replaces
+     * the 'restore' tool button with the 'maximize' tool button.
+     * Also see {@link #toggleMaximize}.
+     * @return {Ext.window.Window} this
+     */
+    restore: function() {
+        var me = this,
+            tools = me.tools;
+
+        if (me.maximized) {
+            delete me.hasSavedRestore;
+            me.removeCls(Ext.baseCSSPrefix + 'window-maximized');
+
+            // Toggle tool visibility
+            if (tools.restore) {
+                tools.restore.hide();
+            }
+            if (tools.maximize) {
+                tools.maximize.show();
+            }
+            if (me.collapseTool) {
+                me.collapseTool.show();
+            }
+
+            // Restore the position/sizing
+            me.setPosition(me.restorePos);
+            me.setSize(me.restoreSize);
+
+            // Unset old position/sizing
+            delete me.restorePos;
+            delete me.restoreSize;
+
+            me.maximized = false;
+
+            me.el.enableShadow(true);
+
+            // Allow users to drag and drop again
+            if (me.dd) {
+                me.dd.enable();
+            }
+
+            me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct');
+
+            me.doConstrain();
+            me.fireEvent('restore', me);
+        }
+        return me;
+    },
+
+    /**
+     * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized
+     * state of the window.
+     * @return {Ext.window.Window} this
+     */
+    toggleMaximize: function() {
+        return this[this.maximized ? 'restore': 'maximize']();
+    }
+
+    /**
+     * @cfg {Boolean} autoWidth @hide
+     * Absolute positioned element and therefore cannot support autoWidth.
+     * A width is a required configuration.
+     **/
+});
+/**
+ * @class Ext.form.field.Base
+ * @extends Ext.Component
+
+Base class for form fields that provides default event handling, rendering, and other common functionality
+needed by all form field types. Utilizes the {@link Ext.form.field.Field} mixin for value handling and validation,
+and the {@link Ext.form.Labelable} mixin to provide label and error message display.
+
+In most cases you will want to use a subclass, such as {@link Ext.form.field.Text} or {@link Ext.form.field.Checkbox},
+rather than creating instances of this class directly. However if you are implementing a custom form field,
+using this as the parent class is recommended.
+
+__Values and Conversions__
+
+Because BaseField implements the Field mixin, it has a main value that can be initialized with the
+{@link #value} config and manipulated via the {@link #getValue} and {@link #setValue} methods. This main
+value can be one of many data types appropriate to the current field, for instance a {@link Ext.form.field.Date Date}
+field would use a JavaScript Date object as its value type. However, because the field is rendered as a HTML
+input, this value data type can not always be directly used in the rendered field.
+
+Therefore BaseField introduces the concept of a "raw value". This is the value of the rendered HTML input field,
+and is normally a String. The {@link #getRawValue} and {@link #setRawValue} methods can be used to directly
+work with the raw value, though it is recommended to use getValue and setValue in most cases.
+
+Conversion back and forth between the main value and the raw value is handled by the {@link #valueToRaw} and
+{@link #rawToValue} methods. If you are implementing a subclass that uses a non-String value data type, you
+should override these methods to handle the conversion.
+
+__Rendering__
+
+The content of the field body is defined by the {@link #fieldSubTpl} XTemplate, with its argument data
+created by the {@link #getSubTplData} method. Override this template and/or method to create custom
+field renderings.
+{@img Ext.form.BaseField/Ext.form.BaseField.png Ext.form.BaseField BaseField component}
+__Example usage:__
+
+    // A simple subclass of BaseField that creates a HTML5 search field. Redirects to the
+    // searchUrl when the Enter key is pressed.
+    Ext.define('Ext.form.SearchField', {
+        extend: 'Ext.form.field.Base',
+        alias: 'widget.searchfield',
+    
+        inputType: 'search',
+    
+        // Config defining the search URL
+        searchUrl: 'http://www.google.com/search?q={0}',
+    
+        // Add specialkey listener
+        initComponent: function() {
+            this.callParent();
+            this.on('specialkey', this.checkEnterKey, this);
+        },
+    
+        // Handle enter key presses, execute the search if the field has a value
+        checkEnterKey: function(field, e) {
+            var value = this.getValue();
+            if (e.getKey() === e.ENTER && !Ext.isEmpty(value)) {
+                location.href = Ext.String.format(this.searchUrl, value);
+            }
+        }
+    });
+
+    Ext.create('Ext.form.Panel', {
+        title: 'BaseField Example',
+        bodyPadding: 5,
+        width: 250,
+                
+        // Fields will be arranged vertically, stretched to full width
+        layout: 'anchor',
+        defaults: {
+            anchor: '100%'
+        },
+        items: [{
+            xtype: 'searchfield',
+            fieldLabel: 'Search',
+            name: 'query'
+        }]
+        renderTo: Ext.getBody()
+    });
+
+ * @constructor
+ * Creates a new Field
+ * @param {Object} config Configuration options
+ *
+ * @xtype field
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+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
+        }
+    ],
+
+    /**
+     * @cfg {String} name The name of the field (defaults to undefined). This is used as the parameter
+     * name when including the field value in a {@link Ext.form.Basic#submit form submit()}. If no name is
+     * configured, it falls back to the {@link #inputId}. To prevent the field from being included in the
+     * form submit, set {@link #submitValue} to <tt>false</tt>.
+     */
+
+    /**
+     * @cfg {String} inputType
+     * <p>The type attribute for input fields -- e.g. radio, text, password, file (defaults to <tt>'text'</tt>).
+     * The extended types supported by HTML5 inputs (url, email, etc.) may also be used, though using them
+     * will cause older browsers to fall back to 'text'.</p>
+     * <p>The type 'password' must be used to render that field type currently -- there is no separate Ext
+     * component for that. You can use {@link Ext.form.field.File} which creates a custom-rendered file upload
+     * field, but if you want a plain unstyled file input you can use a BaseField with inputType:'file'.</p>
+     */
+    inputType: 'text',
+
+    /**
+     * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered,
+     * not those which are built via applyTo (defaults to undefined).
+     */
+
+    /**
+     * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided
+     * (defaults to 'The value in this field is invalid')
+     */
+    invalidText : 'The value in this field is invalid',
+
+    /**
+     * @cfg {String} fieldCls The default CSS class for the field input (defaults to 'x-form-field')
+     */
+    fieldCls : Ext.baseCSSPrefix + 'form-field',
+
+    /**
+     * @cfg {String} fieldStyle Optional CSS style(s) to be applied to the {@link #inputEl field input element}.
+     * Should be a valid argument to {@link Ext.core.Element#applyStyles}. Defaults to undefined. See also the
+     * {@link #setFieldStyle} method for changing the style after initialization.
+     */
+
+    /**
+     * @cfg {String} focusCls The CSS class to use when the field receives focus (defaults to 'x-form-focus')
+     */
+    focusCls : Ext.baseCSSPrefix + 'form-focus',
+
+    /**
+     * @cfg {String} dirtyCls The CSS class to use when the field value {@link #isDirty is dirty}.
+     */
+    dirtyCls : Ext.baseCSSPrefix + 'form-dirty',
+
+    /**
+     * @cfg {Array} checkChangeEvents
+     * <p>A list of event names that will be listened for on the field's {@link #inputEl input element}, which
+     * will cause the field's value to be checked for changes. If a change is detected, the
+     * {@link #change change event} will be fired, followed by validation if the {@link #validateOnChange}
+     * option is enabled.</p>
+     * <p>Defaults to <tt>['change', 'propertychange']</tt> in Internet Explorer, and <tt>['change', 'input',
+     * 'textInput', 'keyup', 'dragdrop']</tt> in other browsers. This catches all the ways that field values
+     * can be changed in most supported browsers; the only known exceptions at the time of writing are:</p>
+     * <ul>
+     * <li>Safari 3.2 and older: cut/paste in textareas via the context menu, and dragging text into textareas</li>
+     * <li>Opera 10 and 11: dragging text into text fields and textareas, and cut via the context menu in text
+     * fields and textareas</li>
+     * <li>Opera 9: Same as Opera 10 and 11, plus paste from context menu in text fields and textareas</li>
+     * </ul>
+     * <p>If you need to guarantee on-the-fly change notifications including these edge cases, you can call the
+     * {@link #checkChange} method on a repeating interval, e.g. using {@link Ext.TaskManager}, or if the field is
+     * within a {@link Ext.form.Panel}, you can use the FormPanel's {@link Ext.form.Panel#pollForChanges}
+     * configuration to set up such a task automatically.</p>
+     */
+    checkChangeEvents: Ext.isIE && (!document.documentMode || document.documentMode < 9) ?
+                        ['change', 'propertychange'] :
+                        ['change', 'input', 'textInput', 'keyup', 'dragdrop'],
+
+    /**
+     * @cfg {Number} checkChangeBuffer
+     * Defines a timeout in milliseconds for buffering {@link #checkChangeEvents} that fire in rapid succession.
+     * Defaults to 50 milliseconds.
+     */
+    checkChangeBuffer: 50,
+
+    componentLayout: 'field',
+
+    /**
+     * @cfg {Boolean} readOnly <tt>true</tt> to mark the field as readOnly in HTML
+     * (defaults to <tt>false</tt>).
+     * <br><p><b>Note</b>: this only sets the element's readOnly DOM attribute.
+     * Setting <code>readOnly=true</code>, for example, will not disable triggering a
+     * ComboBox or Date; it gives you the option of forcing the user to choose
+     * via the trigger without typing in the text box. To hide the trigger use
+     * <code>{@link Ext.form.field.Trigger#hideTrigger hideTrigger}</code>.</p>
+     */
+    readOnly : false,
+
+    /**
+     * @cfg {String} readOnlyCls The CSS class applied to the component's main element when it is {@link #readOnly}.
+     */
+    readOnlyCls: Ext.baseCSSPrefix + 'form-readonly',
+
+    /**
+     * @cfg {String} inputId
+     * The id that will be given to the generated input DOM element. Defaults to an automatically generated id.
+     * If you configure this manually, you must make sure it is unique in the document.
+     */
+
+    /**
+     * @cfg {Boolean} validateOnBlur
+     * Whether the field should validate when it loses focus (defaults to <tt>true</tt>). This will cause fields
+     * to be validated as the user steps through the fields in the form regardless of whether they are making
+     * changes to those fields along the way. See also {@link #validateOnChange}.
+     */
+    validateOnBlur: true,
+
+    // private
+    hasFocus : false,
+    
+    baseCls: Ext.baseCSSPrefix + 'field',
+    
+    maskOnDisable: false,
+
+    // private
+    initComponent : function() {
+        var me = this;
+
+        me.callParent();
+
+        me.subTplData = me.subTplData || {};
+
+        me.addEvents(
+            /**
+             * @event focus
+             * Fires when this field receives input focus.
+             * @param {Ext.form.field.Base} this
+             */
+            'focus',
+            /**
+             * @event blur
+             * Fires when this field loses input focus.
+             * @param {Ext.form.field.Base} this
+             */
+            'blur',
+            /**
+             * @event specialkey
+             * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.
+             * To handle other keys see {@link Ext.panel.Panel#keys} or {@link Ext.util.KeyMap}.
+             * You can check {@link Ext.EventObject#getKey} to determine which key was pressed.
+             * For example: <pre><code>
+var form = new Ext.form.Panel({
+    ...
+    items: [{
+            fieldLabel: 'Field 1',
+            name: 'field1',
+            allowBlank: false
+        },{
+            fieldLabel: 'Field 2',
+            name: 'field2',
+            listeners: {
+                specialkey: function(field, e){
+                    // e.HOME, e.END, e.PAGE_UP, e.PAGE_DOWN,
+                    // e.TAB, e.ESC, arrow keys: e.LEFT, e.RIGHT, e.UP, e.DOWN
+                    if (e.{@link Ext.EventObject#getKey getKey()} == e.ENTER) {
+                        var form = field.up('form').getForm();
+                        form.submit();
+                    }
+                }
+            }
+        }
+    ],
+    ...
+});
+             * </code></pre>
+             * @param {Ext.form.field.Base} this
+             * @param {Ext.EventObject} e The event object
+             */
+            'specialkey'
+        );
+
+        // Init mixins
+        me.initLabelable();
+        me.initField();
+
+        // Default name to inputId
+        if (!me.name) {
+            me.name = me.getInputId();
+        }
+    },
+
+    /**
+     * Returns the input id for this field. If none was specified via the {@link #inputId} config,
+     * then an id will be automatically generated.
+     */
+    getInputId: function() {
+        return this.inputId || (this.inputId = Ext.id());
+    },
+
+    /**
+     * @protected Creates and returns the data object to be used when rendering the {@link #fieldSubTpl}.
+     * @return {Object} The template data
+     */
+    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)
+        });
+    },
+
+    /**
+     * @protected
+     * Gets the markup to be inserted into the outer template's bodyEl. For fields this is the
+     * actual input element.
+     */
+    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());
+    },
+
+    /**
+     * Set the {@link #fieldStyle CSS style} of the {@link #inputEl field input element}.
+     * @param {String/Object/Function} style The style(s) to apply. Should be a valid argument to
+     * {@link Ext.core.Element#applyStyles}.
+     */
+    setFieldStyle: function(style) {
+        var me = this,
+            inputEl = me.inputEl;
+        if (inputEl) {
+            inputEl.applyStyles(style);
+        }
+        me.fieldStyle = style;
+    },
+
+    // private
+    onRender : function() {
+        var me = this,
+            fieldStyle = me.fieldStyle,
+            renderSelectors = me.renderSelectors;
+
+        Ext.applyIf(renderSelectors, me.getLabelableSelectors());
+
+        Ext.applyIf(renderSelectors, {
+            /**
+             * @property inputEl
+             * @type Ext.core.Element
+             * The input Element for this Field. Only available after the field has been rendered.
+             */
+            inputEl: '.' + me.fieldCls
+        });
+
+        me.callParent(arguments);
+
+        // Make the stored rawValue get set as the input element's value
+        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();
+
+        // Associate the field to the error message element
+        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;
+    },
+
+    // private override to use getSubmitValue() as a convenience
+    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;
+    },
+
+    /**
+     * <p>Returns the value that would be included in a standard form submit for this field. This will be combined
+     * with the field's name to form a <tt>name=value</tt> pair in the {@link #getSubmitData submitted parameters}.
+     * If an empty string is returned then just the <tt>name=</tt> will be submitted; if <tt>null</tt> is returned
+     * then nothing will be submitted.</p>
+     * <p>Note that the value returned will have been {@link #processRawValue processed} but may or may not have
+     * been successfully {@link #validate validated}.</p>
+     * @return {String} The value to be submitted, or <tt>null</tt>.
+     */
+    getSubmitValue: function() {
+        return this.processRawValue(this.getRawValue());
+    },
+
+    /**
+     * Returns the raw value of the field, without performing any normalization, conversion, or validation.
+     * To get a normalized and converted value see {@link #getValue}.
+     * @return {String} value The raw String value of the field
+     */
+    getRawValue: function() {
+        var me = this,
+            v = (me.inputEl ? me.inputEl.getValue() : Ext.value(me.rawValue, ''));
+        me.rawValue = v;
+        return v;
+    },
+
+    /**
+     * Sets the field's raw value directly, bypassing {@link #valueToRaw value conversion}, change detection, and
+     * validation. To set the value with these additional inspections see {@link #setValue}.
+     * @param {Mixed} value The value to set
+     * @return {Mixed} value The field value that is set
+     */
+    setRawValue: function(value) {
+        var me = this;
+        value = Ext.value(value, '');
+        me.rawValue = value;
+
+        // Some Field subclasses may not render an inputEl
+        if (me.inputEl) {
+            me.inputEl.dom.value = value;
+        }
+        return value;
+    },
+
+    /**
+     * <p>Converts a mixed-type value to a raw representation suitable for displaying in the field. This allows
+     * controlling how value objects passed to {@link #setValue} are shown to the user, including localization.
+     * For instance, for a {@link Ext.form.field.Date}, this would control how a Date object passed to {@link #setValue}
+     * would be converted to a String for display in the field.</p>
+     * <p>See {@link #rawToValue} for the opposite conversion.</p>
+     * <p>The base implementation simply does a standard toString conversion, and converts
+     * {@link Ext#isEmpty empty values} to an empty string.</p>
+     * @param {Mixed} value The mixed-type value to convert to the raw representation.
+     * @return {Mixed} The converted raw value.
+     */
+    valueToRaw: function(value) {
+        return '' + Ext.value(value, '');
+    },
+
+    /**
+     * <p>Converts a raw input field value into a mixed-type value that is suitable for this particular field type.
+     * This allows controlling the normalization and conversion of user-entered values into field-type-appropriate
+     * values, e.g. a Date object for {@link Ext.form.field.Date}, and is invoked by {@link #getValue}.</p>
+     * <p>It is up to individual implementations to decide how to handle raw values that cannot be successfully
+     * converted to the desired object type.</p>
+     * <p>See {@link #valueToRaw} for the opposite conversion.</p>
+     * <p>The base implementation does no conversion, returning the raw value untouched.</p>
+     * @param {Mixed} rawValue
+     * @return {Mixed} The converted value.
+     */
+    rawToValue: function(rawValue) {
+        return rawValue;
+    },
+
+    /**
+     * Performs any necessary manipulation of a raw field value to prepare it for {@link #rawToValue conversion}
+     * and/or {@link #validate validation}, for instance stripping out ignored characters. In the base implementation
+     * it does nothing; individual subclasses may override this as needed.
+     * @param {Mixed} value The unprocessed string value
+     * @return {Mixed} The processed string value
+     */
+    processRawValue: function(value) {
+        return value;
+    },
+
+    /**
+     * Returns the current data value of the field. The type of value returned is particular to the type of the
+     * particular field (e.g. a Date object for {@link Ext.form.field.Date}), as the result of calling {@link #rawToValue} on
+     * the field's {@link #processRawValue processed} String value. To return the raw String value, see {@link #getRawValue}.
+     * @return {Mixed} value The field value
+     */
+    getValue: function() {
+        var me = this,
+            val = me.rawToValue(me.processRawValue(me.getRawValue()));
+        me.value = val;
+        return val;
+    },
+
+    /**
+     * Sets a data value into the field and runs the change detection and validation. To set the value directly
+     * without these inspections see {@link #setRawValue}.
+     * @param {Mixed} value The value to set
+     * @return {Ext.form.field.Field} this
+     */
+    setValue: function(value) {
+        var me = this;
+        me.setRawValue(me.valueToRaw(value));
+        return me.mixins.field.setValue.call(me, value);
+    },
+
+
+    //private
+    onDisable: function() {
+        var me = this,
+            inputEl = me.inputEl;
+        me.callParent();
+        if (inputEl) {
+            inputEl.dom.disabled = true;
+        }
+    },
+
+    //private
+    onEnable: function() {
+        var me = this,
+            inputEl = me.inputEl;
+        me.callParent();
+        if (inputEl) {
+            inputEl.dom.disabled = false;
+        }
+    },
+
+    /**
+     * Sets the read only state of this field.
+     * @param {Boolean} readOnly Whether the field should be read only.
+     */
+    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;
+    },
+
+    // private
+    fireKey: function(e){
+        if(e.isSpecialKey()){
+            this.fireEvent('specialkey', this, Ext.create('Ext.EventObjectImpl', e));
+        }
+    },
+
+    // private
+    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);
+
+            // standardise buffer across all browsers + OS-es for consistent event order.
+            // (the 10ms buffer for Editors fixes a weird FF/Win editor issue when changing OS window focus)
+            me.mon(inputEl, 'blur', me.onBlur, me, me.inEditor ? {buffer:10} : null);
+
+            // listen for immediate value changes
+            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;
+
+        // In IE if propertychange is one of the checkChangeEvents, we need to remove
+        // the listener prior to layout and re-add it after, to prevent it from firing
+        // needlessly for attribute and style changes applied to the inputEl.
+        if (usesPropertychange) {
+            me.mun(inputEl, ename, onChangeEvent);
+        }
+        me.callParent(arguments);
+        if (usesPropertychange) {
+            me.mon(inputEl, ename, onChangeEvent);
+        }
+    },
+
+    // private
+    preFocus: Ext.emptyFn,
+
+    // private
+    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);
+        }
+    },
+
+    // private
+    beforeBlur : Ext.emptyFn,
+
+    // private
+    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();
+    },
+
+    // private
+    postBlur : Ext.emptyFn,
+
+
+    /**
+     * @private Called when the field's dirty state changes. Adds/removes the {@link #dirtyCls} on the main element.
+     * @param {Boolean} isDirty
+     */
+    onDirtyChange: function(isDirty) {
+        this[isDirty ? 'addCls' : 'removeCls'](this.dirtyCls);
+    },
+
+
+    /**
+     * Returns whether or not the field value is currently valid by
+     * {@link #getErrors validating} the {@link #processRawValue processed raw value}
+     * of the field. <b>Note</b>: {@link #disabled} fields are always treated as valid.
+     * @return {Boolean} True if the value is valid, else false
+     */
+    isValid : function() {
+        var me = this;
+        return me.disabled || me.validateValue(me.processRawValue(me.getRawValue()));
+    },
+
+
+    /**
+     * <p>Uses {@link #getErrors} to build an array of validation errors. If any errors are found, they are passed
+     * to {@link #markInvalid} and false is returned, otherwise true is returned.</p>
+     * <p>Previously, subclasses were invited to provide an implementation of this to process validations - from 3.2
+     * onwards {@link #getErrors} should be overridden instead.</p>
+     * @param {Mixed} value The value to validate
+     * @return {Boolean} True if all validations passed, false if one or more failed
+     */
+    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;
+    },
+
+    /**
+     * <p>Display one or more error messages associated with this field, using {@link #msgTarget} to determine how to
+     * display the messages and applying {@link #invalidCls} to the field's UI element.</p>
+     * <p><b>Note</b>: this method does not cause the Field's {@link #validate} or {@link #isValid} methods to
+     * return <code>false</code> if the value does <i>pass</i> validation. So simply marking a Field as invalid
+     * will not prevent submission of forms submitted with the {@link Ext.form.action.Submit#clientValidation}
+     * option set.</p>
+     * @param {String/Array} errors The validation message(s) to display.
+     */
+    markInvalid : function(errors) {
+        // Save the message and fire the 'invalid' event
+        var me = this,
+            oldMsg = me.getActiveError();
+        me.setActiveErrors(Ext.Array.from(errors));
+        if (oldMsg !== me.getActiveError()) {
+            me.doComponentLayout();
+        }
+    },
+
+    /**
+     * <p>Clear any invalid styles/messages for this field.</p>
+     * <p><b>Note</b>: this method does not cause the Field's {@link #validate} or {@link #isValid} methods to
+     * return <code>true</code> if the value does not <i>pass</i> validation. So simply clearing a field's errors
+     * will not necessarily allow submission of forms submitted with the {@link Ext.form.action.Submit#clientValidation}
+     * option set.</p>
+     */
+    clearInvalid : function() {
+        // Clear the message and fire the 'valid' event
+        var me = this,
+            hadError = me.hasActiveError();
+        me.unsetActiveError();
+        if (hadError) {
+            me.doComponentLayout();
+        }
+    },
+
+    /**
+     * @private Overrides the method from the Ext.form.Labelable mixin to also add the invalidCls to the inputEl,
+     * as that is required for proper styling in IE with nested fields (due to lack of child selector)
+     */
+    renderActiveError: function() {
+        var me = this,
+            hasError = me.hasActiveError();
+        if (me.inputEl) {
+            // Add/remove invalid class
+            me.inputEl[hasError ? 'addCls' : 'removeCls'](me.invalidCls + '-field');
+        }
+        me.mixins.labelable.renderActiveError.call(me);
+    },
+
+
+    getActionEl: function() {
+        return this.inputEl || this.el;
+    }
+
+});
+
+/**
+ * @class Ext.form.field.Text
+ * @extends Ext.form.field.Base
+A basic text field.  Can be used as a direct replacement for traditional text inputs,
+or as the base class for more sophisticated input controls (like {@link Ext.form.field.TextArea}
+and {@link Ext.form.field.ComboBox}). Has support for empty-field placeholder values (see {@link #emptyText}).
+
+#Validation#
+
+The Text field has a useful set of validations built in:
+
+- {@link #allowBlank} for making the field required
+- {@link #minLength} for requiring a minimum value length
+- {@link #maxLength} for setting a maximum value length (with {@link #enforceMaxLength} to add it
+  as the `maxlength` attribute on the input element)
+- {@link regex} to specify a custom regular expression for validation
+
+In addition, custom validations may be added:
+- {@link #vtype} specifies a virtual type implementation from {@link Ext.form.field.VTypes} which can contain
+  custom validation logic
+- {@link #validator} allows a custom arbitrary function to be called during validation
+
+The details around how and when each of these validation options get used are described in the
+documentation for {@link #getErrors}.
+
+By default, the field value is checked for validity immediately while the user is typing in the
+field. This can be controlled with the {@link #validateOnChange}, {@link #checkChangeEvents}, and
+{@link #checkChangeBugger} configurations. Also see the details on Form Validation in the
+{@link Ext.form.Panel} class documentation.
+
+#Masking and Character Stripping#
+
+Text fields can be configured with custom regular expressions to be applied to entered values before
+validation: see {@link #maskRe} and {@link #stripCharsRe} for details.
+{@img Ext.form.Text/Ext.form.Text.png Ext.form.Text component}
+#Example usage:#
+
+    Ext.create('Ext.form.Panel', {
+        title: 'Contact Info',
+        width: 300,
+        bodyPadding: 10,
+        renderTo: Ext.getBody(),        
+        items: [{
+            xtype: 'textfield',
+            name: 'name',
+            fieldLabel: 'Name',
+            allowBlank: false  // requires a non-empty value
+        }, {
+            xtype: 'textfield',
+            name: 'email',
+            fieldLabel: 'Email Address',
+            vtype: 'email'  // requires value to be a valid email address format
+        }]
+    }); 
+
+ * @constructor Creates a new TextField
+ * @param {Object} config Configuration options
+ *
+ * @xtype textfield
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+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'],
+
+    /**
+     * @cfg {String} vtypeText A custom error message to display in place of the default message provided
+     * for the <b><code>{@link #vtype}</code></b> currently set for this field (defaults to <tt>undefined</tt>).
+     * <b>Note</b>: only applies if <b><code>{@link #vtype}</code></b> is set, else ignored.
+     */
+    
+    /**
+     * @cfg {RegExp} stripCharsRe A JavaScript RegExp object used to strip unwanted content from the value
+     * before validation (defaults to <tt>undefined</tt>).
+     */
+
+    /**
+     * @cfg {Number} size An initial value for the 'size' attribute on the text input element. This is only
+     * used if the field has no configured {@link #width} and is not given a width by its container's layout.
+     * Defaults to <tt>20</tt>.
+     */
+    size: 20,
+
+    /**
+     * @cfg {Boolean} grow <tt>true</tt> if this field should automatically grow and shrink to its content
+     * (defaults to <tt>false</tt>)
+     */
+
+    /**
+     * @cfg {Number} growMin The minimum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
+     * to <tt>30</tt>)
+     */
+    growMin : 30,
+    
+    /**
+     * @cfg {Number} growMax The maximum width to allow when <code><b>{@link #grow}</b> = true</code> (defaults
+     * to <tt>800</tt>)
+     */
+    growMax : 800,
+
+    /**
+     * @cfg {String} growAppend
+     * A string that will be appended to the field's current value for the purposes of calculating the target
+     * field size. Only used when the {@link #grow} config is <tt>true</tt>. Defaults to a single capital "W"
+     * (the widest character in common fonts) to leave enough space for the next typed character and avoid the
+     * field value shifting before the width is adjusted.
+     */
+    growAppend: 'W',
+    
+    /**
+     * @cfg {String} vtype A validation type name as defined in {@link Ext.form.field.VTypes} (defaults to <tt>undefined</tt>)
+     */
+
+    /**
+     * @cfg {RegExp} maskRe An input mask regular expression that will be used to filter keystrokes that do
+     * not match (defaults to <tt>undefined</tt>)
+     */
+
+    /**
+     * @cfg {Boolean} disableKeyFilter Specify <tt>true</tt> to disable input keystroke filtering (defaults
+     * to <tt>false</tt>)
+     */
+
+    /**
+     * @cfg {Boolean} allowBlank Specify <tt>false</tt> to validate that the value's length is > 0 (defaults to
+     * <tt>true</tt>)
+     */
+    allowBlank : true,
+    
+    /**
+     * @cfg {Number} minLength Minimum input field length required (defaults to <tt>0</tt>)
+     */
+    minLength : 0,
+    
+    /**
+     * @cfg {Number} maxLength Maximum input field length allowed by validation (defaults to Number.MAX_VALUE).
+     * This behavior is intended to provide instant feedback to the user by improving usability to allow pasting
+     * and editing or overtyping and back tracking. To restrict the maximum number of characters that can be
+     * entered into the field use the <tt><b>{@link Ext.form.field.Text#enforceMaxLength enforceMaxLength}</b></tt> option.
+     */
+    maxLength : Number.MAX_VALUE,
+    
+    /**
+     * @cfg {Boolean} enforceMaxLength True to set the maxLength property on the underlying input field. Defaults to <tt>false</tt>
+     */
+
+    /**
+     * @cfg {String} minLengthText Error text to display if the <b><tt>{@link #minLength minimum length}</tt></b>
+     * validation fails (defaults to <tt>'The minimum length for this field is {minLength}'</tt>)
+     */
+    minLengthText : 'The minimum length for this field is {0}',
+    
+    /**
+     * @cfg {String} maxLengthText Error text to display if the <b><tt>{@link #maxLength maximum length}</tt></b>
+     * validation fails (defaults to <tt>'The maximum length for this field is {maxLength}'</tt>)
+     */
+    maxLengthText : 'The maximum length for this field is {0}',
+    
+    /**
+     * @cfg {Boolean} selectOnFocus <tt>true</tt> to automatically select any existing field text when the field
+     * receives input focus (defaults to <tt>false</tt>)
+     */
+    
+    /**
+     * @cfg {String} blankText The error text to display if the <b><tt>{@link #allowBlank}</tt></b> validation
+     * fails (defaults to <tt>'This field is required'</tt>)
+     */
+    blankText : 'This field is required',
+    
+    /**
+     * @cfg {Function} validator
+     * <p>A custom validation function to be called during field validation ({@link #getErrors})
+     * (defaults to <tt>undefined</tt>). If specified, this function will be called first, allowing the
+     * developer to override the default validation process.</p>
+     * <br><p>This function will be passed the following Parameters:</p>
+     * <div class="mdetail-params"><ul>
+     * <li><code>value</code>: <i>Mixed</i>
+     * <div class="sub-desc">The current field value</div></li>
+     * </ul></div>
+     * <br><p>This function is to Return:</p>
+     * <div class="mdetail-params"><ul>
+     * <li><code>true</code>: <i>Boolean</i>
+     * <div class="sub-desc"><code>true</code> if the value is valid</div></li>
+     * <li><code>msg</code>: <i>String</i>
+     * <div class="sub-desc">An error message if the value is invalid</div></li>
+     * </ul></div>
+     */
+
+    /**
+     * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation
+     * (defaults to <tt>undefined</tt>). If the test fails, the field will be marked invalid using
+     * <b><tt>{@link #regexText}</tt></b>.
+     */
+
+    /**
+     * @cfg {String} regexText The error text to display if <b><tt>{@link #regex}</tt></b> is used and the
+     * test fails during validation (defaults to <tt>''</tt>)
+     */
+    regexText : '',
+    
+    /**
+     * @cfg {String} emptyText
+     * <p>The default text to place into an empty field (defaults to <tt>undefined</tt>).</p>
+     * <p>Note that normally this value will be submitted to the server if this field is enabled; to prevent this
+     * you can set the {@link Ext.form.action.Action#submitEmptyText submitEmptyText} option of
+     * {@link Ext.form.Basic#submit} to <tt>false</tt>.</p>
+     * <p>Also note that if you use <tt>{@link #inputType inputType}:'file'</tt>, {@link #emptyText} is not
+     * supported and should be avoided.</p>
+     */
+
+    /**
+     * @cfg {String} emptyCls The CSS class to apply to an empty field to style the <b><tt>{@link #emptyText}</tt></b>
+     * (defaults to <tt>'x-form-empty-field'</tt>).  This class is automatically added and removed as needed
+     * depending on the current field value.
+     */
+    emptyCls : Ext.baseCSSPrefix + 'form-empty-field',
+
+    ariaRole: 'textbox',
+
+    /**
+     * @cfg {Boolean} enableKeyEvents <tt>true</tt> to enable the proxying of key events for the HTML input field (defaults to <tt>false</tt>)
+     */
+
+    componentLayout: 'textfield',
+
+    initComponent : function(){
+        this.callParent();
+        this.addEvents(
+            /**
+             * @event autosize
+             * Fires when the <tt><b>{@link #autoSize}</b></tt> function is triggered and the field is
+             * resized according to the {@link #grow}/{@link #growMin}/{@link #growMax} configs as a result.
+             * This event provides a hook for the developer to apply additional logic at runtime to resize the
+             * field if needed.
+             * @param {Ext.form.field.Text} this This text field
+             * @param {Number} width The new field width
+             */
+            'autosize',
+
+            /**
+             * @event keydown
+             * Keydown input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
+             * is set to true.
+             * @param {Ext.form.field.Text} this This text field
+             * @param {Ext.EventObject} e
+             */
+            'keydown',
+            /**
+             * @event keyup
+             * Keyup input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
+             * is set to true.
+             * @param {Ext.form.field.Text} this This text field
+             * @param {Ext.EventObject} e
+             */
+            'keyup',
+            /**
+             * @event keypress
+             * Keypress input field event. This event only fires if <tt><b>{@link #enableKeyEvents}</b></tt>
+             * is set to true.
+             * @param {Ext.form.field.Text} this This text field
+             * @param {Ext.EventObject} e
+             */
+            'keypress'
+        );
+    },
+
+    // private
+    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
+            });
+        }
+    },
+
+    /**
+     * @private override - treat undefined and null values as equal to an empty string value
+     */
+    isEqual: function(value1, value2) {
+        return String(Ext.value(value1, '')) === String(Ext.value(value2, ''));
+    },
+
+    /**
+     * @private
+     * If grow=true, invoke the autoSize method when the field's value is changed.
+     */
+    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 });
+        }
+    },
+
+    /**
+     * Performs any necessary manipulation of a raw String value to prepare it for {@link #stringToValue conversion}
+     * and/or {@link #validate validation}. For text fields this applies the configured {@link #stripCharsRe} to the
+     * raw value.
+     * @param {String} value The unprocessed string value
+     * @return {String} The processed string value
+     */
+    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;
+    },
+
+    //private
+    onDisable: function(){
+        this.callParent();
+        if (Ext.isIE) {
+            this.inputEl.dom.unselectable = 'on';
+        }
+    },
+
+    //private
+    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);
+    },
+
+    /**
+     * Resets the current field value to the originally-loaded value and clears any validation messages.
+     * Also adds <tt><b>{@link #emptyText}</b></tt> and <tt><b>{@link #emptyCls}</b></tt> if the
+     * original value was blank.
+     */
+    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);
+            }
+            
+            //all browsers need this because of a styling issue with chrome + placeholders.
+            //the text isnt vertically aligned when empty (and using the placeholder)
+            if (isEmpty) {
+                me.inputEl.addCls(me.emptyCls);
+            }
+
+            me.autoSize();
+        }
+    },
+
+    // private
+    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();
+        }
+    },
+
+    // private
+    postBlur : function(){
+        this.applyEmptyText();
+    },
+
+    // private
+    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();
+        }
+    },
+
+    /**
+     * Returns the raw String value of the field, without performing any normalization, conversion, or validation.
+     * Gets the current value of the input element if the field has been rendered, ignoring the value if it is the
+     * {@link #emptyText}. To get a normalized and converted value see {@link #getValue}.
+     * @return {String} value The raw String value of the field
+     */
+    getRawValue: function() {
+        var me = this,
+            v = me.callParent();
+        if (v === me.emptyText) {
+            v = '';
+        }
+        return v;
+    },
+
+    /**
+     * Sets a data value into the field and runs the change detection and validation. Also applies any configured
+     * {@link #emptyText} for text fields. To set the value directly without these inspections see {@link #setRawValue}.
+     * @param {Mixed} value The value to set
+     * @return {Ext.form.field.Text} this
+     */
+    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;
+    },
+
+    /**
+Validates a value according to the field's validation rules and returns an array of errors
+for any failing validations. Validation rules are processed in the following order:
+
+1. **Field specific validator**
+    
+    A validator offers a way to customize and reuse a validation specification.
+    If a field is configured with a `{@link #validator}`
+    function, it will be passed the current field value.  The `{@link #validator}`
+    function is expected to return either:
+    
+    - Boolean `true`  if the value is valid (validation continues).
+    - a String to represent the invalid message if invalid (validation halts).
+
+2. **Basic Validation**
+
+    If the `{@link #validator}` has not halted validation,
+    basic validation proceeds as follows:
+    
+    - `{@link #allowBlank}` : (Invalid message = `{@link #emptyText}`)
+    
+        Depending on the configuration of <code>{@link #allowBlank}</code>, a
+        blank field will cause validation to halt at this step and return
+        Boolean true or false accordingly.
+    
+    - `{@link #minLength}` : (Invalid message = `{@link #minLengthText}`)
+
+        If the passed value does not satisfy the `{@link #minLength}`
+        specified, validation halts.
+
+    -  `{@link #maxLength}` : (Invalid message = `{@link #maxLengthText}`)
+
+        If the passed value does not satisfy the `{@link #maxLength}`
+        specified, validation halts.
+
+3. **Preconfigured Validation Types (VTypes)**
+
+    If none of the prior validation steps halts validation, a field
+    configured with a `{@link #vtype}` will utilize the
+    corresponding {@link Ext.form.field.VTypes VTypes} validation function.
+    If invalid, either the field's `{@link #vtypeText}` or
+    the VTypes vtype Text property will be used for the invalid message.
+    Keystrokes on the field will be filtered according to the VTypes
+    vtype Mask property.
+
+4. **Field specific regex test**
+
+    If none of the prior validation steps halts validation, a field's
+    configured <code>{@link #regex}</code> test will be processed.
+    The invalid message for this test is configured with `{@link #regexText}`
+
+     * @param {Mixed} value The value to validate. The processed raw value will be used if nothing is passed
+     * @return {Array} Array of any validation errors
+     * @markdown
+     */
+    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);
+            }
+            //if value is blank, there cannot be any additional errors
+            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;
+    },
+
+    /**
+     * Selects text in this field
+     * @param {Number} start (optional) The index where the selection should start (defaults to 0)
+     * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
+     */
+    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();
+        }
+    },
+
+    /**
+     * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
+     * This only takes effect if <tt>{@link #grow} = true</tt>, and fires the {@link #autosize} event if the
+     * width changes.
+     */
+    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);
+    },
+
+    /**
+     * @protected override
+     * To get the natural width of the inputEl, we do a simple calculation based on the
+     * 'size' config. We use hard-coded numbers to approximate what browsers do natively,
+     * to avoid having to read any styles which would hurt performance.
+     */
+    getBodyNaturalWidth: function() {
+        return Math.round(this.size * 6.5) + 20;
+    }
+
+});
+
+/**
+ * @class Ext.form.field.TextArea
+ * @extends Ext.form.field.Text
+
+This class creates a multiline text field, which can be used as a direct replacement for traditional 
+textarea fields. In addition, it supports automatically {@link #grow growing} the height of the textarea to 
+fit its content.
+
+All of the configuration options from {@link Ext.form.field.Text} can be used on TextArea.
+{@img Ext.form.TextArea/Ext.form.TextArea.png Ext.form.TextArea component}
+Example usage:
+
+    Ext.create('Ext.form.FormPanel', {
+        title      : 'Sample TextArea',
+        width      : 400,
+        bodyPadding: 10,
+        renderTo   : Ext.getBody(),
+        items: [{
+            xtype     : 'textareafield',
+            grow      : true,
+            name      : 'message',
+            fieldLabel: 'Message',
+            anchor    : '100%'
+        }]
+    }); 
+
+Some other useful configuration options when using {@link #grow} are {@link #growMin} and {@link #growMax}. These 
+allow you to set the minimum and maximum grow heights for the textarea.
+
+ * @constructor
+ * Creates a new TextArea
+ * @param {Object} config Configuration options
+ * @xtype textareafield
+ * @docauthor Robert Dougan <rob@sencha.com>
+ */
+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
+        }
+    ],
+
+    /**
+     * @cfg {Number} growMin The minimum height to allow when <tt>{@link Ext.form.field.Text#grow grow}=true</tt>
+     * (defaults to <tt>60</tt>)
+     */
+    growMin: 60,
+
+    /**
+     * @cfg {Number} growMax The maximum height to allow when <tt>{@link Ext.form.field.Text#grow grow}=true</tt>
+     * (defaults to <tt>1000</tt>)
+     */
+    growMax: 1000,
+
+    /**
+     * @cfg {String} growAppend
+     * A string that will be appended to the field's current value for the purposes of calculating the target
+     * field size. Only used when the {@link #grow} config is <tt>true</tt>. Defaults to a newline for TextArea
+     * to ensure there is always a space below the current line.
+     */
+    growAppend: '\n-',
+
+    /**
+     * @cfg {Number} cols An initial value for the 'cols' attribute on the textarea element. This is only
+     * used if the component has no configured {@link #width} and is not given a width by its container's
+     * layout. Defaults to <tt>20</tt>.
+     */
+    cols: 20,
+
+    /**
+     * @cfg {Number} cols An initial value for the 'cols' attribute on the textarea element. This is only
+     * used if the component has no configured {@link #width} and is not given a width by its container's
+     * layout. Defaults to <tt>4</tt>.
+     */
+    rows: 4,
+
+    /**
+     * @cfg {Boolean} enterIsSpecial
+     * True if you want the enter key to be classed as a <tt>special</tt> key. Special keys are generally navigation
+     * keys (arrows, space, enter). Setting the config property to <tt>true</tt> would mean that you could not insert
+     * returns into the textarea.
+     * (defaults to <tt>false</tt>)
+     */
+    enterIsSpecial: false,
+
+    /**
+     * @cfg {Boolean} preventScrollbars <tt>true</tt> to prevent scrollbars from appearing regardless of how much text is
+     * in the field. This option is only relevant when {@link #grow} is <tt>true</tt>. Equivalent to setting overflow: hidden, defaults to
+     * <tt>false</tt>.
+     */
+    preventScrollbars: false,
+
+    // private
+    componentLayout: 'textareafield',
+
+    // private
+    onRender: function(ct, position) {
+        var me = this;
+        Ext.applyIf(me.subTplData, {
+            cols: me.cols,
+            rows: me.rows
+        });
+
+        me.callParent(arguments);
+    },
+
+    // private
+    afterRender: function(){
+        var me = this;
+
+        me.callParent(arguments);
+
+        if (me.grow) {
+            if (me.preventScrollbars) {
+                me.inputEl.setStyle('overflow', 'hidden');
+            }
+            me.inputEl.setHeight(me.growMin);
+        }
+    },
+
+    // private
+    fireKey: function(e) {
+        if (e.isSpecialKey() && (this.enterIsSpecial || (e.getKey() !== e.ENTER || e.hasModifier()))) {
+            this.fireEvent('specialkey', this, e);
+        }
+    },
+
+    /**
+     * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
+     * This only takes effect if <tt>{@link #grow} = true</tt>, and fires the {@link #autosize} event if
+     * the height changes.
+     */
+    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;
+            }
+        }
+    },
+
+    // private
+    initAria: function() {
+        this.callParent(arguments);
+        this.getActionEl().dom.setAttribute('aria-multiline', true);
+    },
+
+    /**
+     * @protected override
+     * To get the natural width of the textarea element, we do a simple calculation based on the
+     * 'cols' config. We use hard-coded numbers to approximate what browsers do natively,
+     * to avoid having to read any styles which would hurt performance.
+     */
+    getBodyNaturalWidth: function() {
+        return Math.round(this.cols * 6.5) + 20;
+    }
+
+});
+
+
+/**
+ * @class Ext.window.MessageBox
+ * @extends Ext.window.Window
+
+Utility class for generating different styles of message boxes.  The singleton instance, `Ext.Msg` can also be used.
+Note that a MessageBox is asynchronous.  Unlike a regular JavaScript `alert` (which will halt
+browser execution), showing a MessageBox will not cause the code to stop.  For this reason, if you have code
+that should only run *after* some user feedback from the MessageBox, you must use a callback function
+(see the `function` parameter for {@link #show} for more details).
+
+{@img Ext.window.MessageBox/messagebox1.png alert MessageBox}
+{@img Ext.window.MessageBox/messagebox2.png prompt MessageBox}
+{@img Ext.window.MessageBox/messagebox3.png show MessageBox}
+#Example usage:#
+
+    // Basic alert:
+    Ext.Msg.alert('Status', 'Changes saved successfully.');
+
+    // Prompt for user data and process the result using a callback:
+    Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
+        if (btn == 'ok'){
+            // process text value and close...
+        }
+    });
+
+    // Show a dialog using config options:
+    Ext.Msg.show({
+         title:'Save Changes?',
+         msg: 'You are closing a tab that has unsaved changes. Would you like to save your changes?',
+         buttons: Ext.Msg.YESNOCANCEL,
+         fn: processResult,
+         animateTarget: 'elId',
+         icon: Ext.window.MessageBox.QUESTION
+    });
+
+ * @markdown
+ * @singleton
+ * @xtype messagebox
+ */
+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',
+
+    /**
+     * Button config that displays a single OK button
+     * @type Number
+     */
+    OK : 1,
+    /**
+     * Button config that displays a single Yes button
+     * @type Number
+     */
+    YES : 2,
+    /**
+     * Button config that displays a single No button
+     * @type Number
+     */
+    NO : 4,
+    /**
+     * Button config that displays a single Cancel button
+     * @type Number
+     */
+    CANCEL : 8,
+    /**
+     * Button config that displays OK and Cancel buttons
+     * @type Number
+     */
+    OKCANCEL : 9,
+    /**
+     * Button config that displays Yes and No buttons
+     * @type Number
+     */
+    YESNO : 6,
+    /**
+     * Button config that displays Yes, No and Cancel buttons
+     * @type Number
+     */
+    YESNOCANCEL : 14,
+    /**
+     * The CSS class that provides the INFO icon image
+     * @type String
+     */
+    INFO : 'ext-mb-info',
+    /**
+     * The CSS class that provides the WARNING icon image
+     * @type String
+     */
+    WARNING : 'ext-mb-warning',
+    /**
+     * The CSS class that provides the QUESTION icon image
+     * @type String
+     */
+    QUESTION : 'ext-mb-question',
+    /**
+     * The CSS class that provides the ERROR icon image
+     * @type String
+     */
+    ERROR : 'ext-mb-error',
+
+    // hide it by offsets. Windows are hidden on render by default.
+    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'
+    },
+
+    /**
+     * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
+     * @type Number
+     */
+    defaultTextHeight : 75,
+    /**
+     * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
+     * for setting a different minimum width than text-only dialogs may need (defaults to 250).
+     * @type Number
+     */
+    minProgressWidth : 250,
+    /**
+     * The minimum width in pixels of the message box if it is a prompt dialog.  This is useful
+     * for setting a different minimum width than text-only dialogs may need (defaults to 250).
+     * @type Number
+     */
+    minPromptWidth: 250,
+    /**
+     * An object containing the default button text strings that can be overriden for localized language support.
+     * Supported properties are: ok, cancel, yes and no.  Generally you should include a locale-specific
+     * resource file for handling language support across the framework.
+     * Customize the default text like so: Ext.window.MessageBox.buttonText.yes = "oui"; //french
+     * @type Object
+     */
+    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();
+        }
+
+        // Important not to have focus remain in the hidden Window; Interferes with DnD.
+        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];
+
+        // Create the buttons based upon passed bitwise config
+        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;
+        }
+
+        // Default to allowing the Window to take focus.
+        delete me.defaultFocus;
+        
+        // clear any old animateTarget
+        me.animateTarget = cfg.animateTarget || undefined;
+
+        // Defaults to modal
+        me.modal = cfg.modal !== false;
+
+        // Show the title
+        if (cfg.title) {
+            me.setTitle(cfg.title||'&#160;');
+        }
+
+        if (!me.rendered) {
+            me.width = initialWidth;
+            me.render(Ext.getBody());
+        } else {
+            me.setSize(initialWidth, me.maxHeight);
+        }
+        me.setPosition(-10000, -10000);
+
+        // Hide or show the close tool
+        me.closable = cfg.closable && !cfg.wait;
+        if (cfg.closable === false) {
+            me.tools.close.hide();
+        } else {
+            me.tools.close.show();
+        }
+
+        // Hide or show the header
+        if (!cfg.title && !me.closable) {
+            me.header.hide();
+        } else {
+            me.header.show();
+        }
+
+        // Default to dynamic drag: drag the window, not a ghost
+        me.liveDrag = !cfg.proxyDrag;
+
+        // wrap the user callback
+        me.userCallback = Ext.Function.bind(cfg.callback ||cfg.fn || Ext.emptyFn, cfg.scope || Ext.global);
+
+        // Hide or show the icon Component
+        me.setIcon(cfg.icon);
+
+        // Hide or show the message area
+        if (cfg.msg) {
+            me.msg.update(cfg.msg);
+            me.msg.show();
+        } else {
+            me.msg.hide();
+        }
+
+        // Hide or show the input field
+        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();
+        }
+
+        // Hide or show the progress bar
+        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();
+        }
+
+        // Hide or show buttons depending on flag value sent.
+        for (i = 0; i < 4; i++) {
+            if (buttons & Math.pow(2, i)) {
+
+                // Default to focus on the first visible button if focus not already set
+                if (!me.defaultFocus) {
+                    me.defaultFocus = me.msgButtons[i];
+                }
+                me.msgButtons[i].show();
+                hideToolbar = false;
+            } else {
+                me.msgButtons[i].hide();
+            }
+        }
+
+        // Hide toolbar if no buttons to show
+        if (hideToolbar) {
+            me.bottomTb.hide();
+        } else {
+            me.bottomTb.show();
+        }
+        me.hidden = true;
+    },
+
+    /**
+     * Displays a new message box, or reinitializes an existing message box, based on the config options
+     * passed in. All display functions (e.g. prompt, alert, etc.) on MessageBox call this function internally,
+     * although those calls are basic shortcuts and do not support all of the config options allowed here.
+     * @param {Object} config The following config options are supported: <ul>
+     * <li><b>animateTarget</b> : String/Element<div class="sub-desc">An id or Element from which the message box should animate as it
+     * opens and closes (defaults to undefined)</div></li>
+     * <li><b>buttons</b> : Number<div class="sub-desc">A bitwise button specifier consisting of the sum of any of the following constants:<ul>
+     * <li>Ext.window.MessageBox.OK</li>
+     * <li>Ext.window.MessageBox.YES</li>
+     * <li>Ext.window.MessageBox.NO</li>
+     * <li>Ext.window.MessageBox.CANCEL</li>
+     * </ul>Or false to not show any buttons (defaults to false)</div></li>
+     * <li><b>closable</b> : Boolean<div class="sub-desc">False to hide the top-right close button (defaults to true). Note that
+     * progress and wait dialogs will ignore this property and always hide the close button as they can only
+     * be closed programmatically.</div></li>
+     * <li><b>cls</b> : String<div class="sub-desc">A custom CSS class to apply to the message box's container element</div></li>
+     * <li><b>defaultTextHeight</b> : Number<div class="sub-desc">The default height in pixels of the message box's multiline textarea
+     * if displayed (defaults to 75)</div></li>
+     * <li><b>fn</b> : Function<div class="sub-desc">A callback function which is called when the dialog is dismissed either
+     * by clicking on the configured buttons, or on the dialog close button, or by pressing
+     * the return button to enter input.
+     * <p>Progress and wait dialogs will ignore this option since they do not respond to user
+     * actions and can only be closed programmatically, so any required function should be called
+     * by the same code after it closes the dialog. Parameters passed:<ul>
+     * <li><b>buttonId</b> : String<div class="sub-desc">The ID of the button pressed, one of:<div class="sub-desc"><ul>
+     * <li><tt>ok</tt></li>
+     * <li><tt>yes</tt></li>
+     * <li><tt>no</tt></li>
+     * <li><tt>cancel</tt></li>
+     * </ul></div></div></li>
+     * <li><b>text</b> : String<div class="sub-desc">Value of the input field if either <tt><a href="#show-option-prompt" ext:member="show-option-prompt" ext:cls="Ext.window.MessageBox">prompt</a></tt>
+     * or <tt><a href="#show-option-multiline" ext:member="show-option-multiline" ext:cls="Ext.window.MessageBox">multiline</a></tt> is true</div></li>
+     * <li><b>opt</b> : Object<div class="sub-desc">The config object passed to show.</div></li>
+     * </ul></p></div></li>
+     * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code>this</code> reference) in which the function will be executed.</div></li>
+     * <li><b>icon</b> : String<div class="sub-desc">A CSS class that provides a background image to be used as the body icon for the
+     * dialog (e.g. Ext.window.MessageBox.WARNING or 'custom-class') (defaults to '')</div></li>
+     * <li><b>iconCls</b> : String<div class="sub-desc">The standard {@link Ext.window.Window#iconCls} to
+     * add an optional header icon (defaults to '')</div></li>
+     * <li><b>maxWidth</b> : Number<div class="sub-desc">The maximum width in pixels of the message box (defaults to 600)</div></li>
+     * <li><b>minWidth</b> : Number<div class="sub-desc">The minimum width in pixels of the message box (defaults to 100)</div></li>
+     * <li><b>modal</b> : Boolean<div class="sub-desc">False to allow user interaction with the page while the message box is
+     * displayed (defaults to true)</div></li>
+     * <li><b>msg</b> : String<div class="sub-desc">A string that will replace the existing message box body text (defaults to the
+     * XHTML-compliant non-breaking space character '&amp;#160;')</div></li>
+     * <li><a id="show-option-multiline"></a><b>multiline</b> : Boolean<div class="sub-desc">
+     * True to prompt the user to enter multi-line text (defaults to false)</div></li>
+     * <li><b>progress</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
+     * <li><b>progressText</b> : String<div class="sub-desc">The text to display inside the progress bar if progress = true (defaults to '')</div></li>
+     * <li><a id="show-option-prompt"></a><b>prompt</b> : Boolean<div class="sub-desc">True to prompt the user to enter single-line text (defaults to false)</div></li>
+     * <li><b>proxyDrag</b> : Boolean<div class="sub-desc">True to display a lightweight proxy while dragging (defaults to false)</div></li>
+     * <li><b>title</b> : String<div class="sub-desc">The title text</div></li>
+     * <li><b>value</b> : String<div class="sub-desc">The string value to set into the active textbox element if displayed</div></li>
+     * <li><b>wait</b> : Boolean<div class="sub-desc">True to display a progress bar (defaults to false)</div></li>
+     * <li><b>waitConfig</b> : Object<div class="sub-desc">A {@link Ext.ProgressBar#waitConfig} object (applies only if wait = true)</div></li>
+     * <li><b>width</b> : Number<div class="sub-desc">The width of the dialog in pixels</div></li>
+     * </ul>
+     * Example usage:
+     * <pre><code>
+Ext.Msg.show({
+title: 'Address',
+msg: 'Please enter your address:',
+width: 300,
+buttons: Ext.window.MessageBox.OKCANCEL,
+multiline: true,
+fn: saveAddress,
+animateTarget: 'addAddressBtn',
+icon: Ext.window.MessageBox.INFO
+});
+</code></pre>
+     * @return {Ext.window.MessageBox} this
+     */
+    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();
+        }
+        
+        // reset to the original dimensions
+        icon.setHeight(iconHeight);
+
+        // Allow per-invocation override of minWidth
+        me.minWidth = me.cfg.minWidth || Ext.getClass(this).prototype.minWidth;
+
+        // Set best possible size based upon allowing the text to wrap in the maximized Window, and
+        // then constraining it to within the max with. Then adding up constituent element heights.
+        me.topContainer.doLayout();
+        if (Ext.isIE6 || Ext.isIEQuirks) {
+            // In IE quirks, the initial full width of the prompt fields will prevent the container element
+            // from collapsing once sized down, so temporarily force them to a small width. They'll get
+            // layed out to their final width later when setting the final window size.
+            me.textField.setCalculatedSize(9);
+            me.textArea.setCalculatedSize(9);
+        }
+        var width = me.cfg.width || me.msg.getWidth() + icon.getWidth() + 25, /* topContainer's layout padding */
+            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 ;/* topContainer's layout padding */
+
+        // Update to the size of the content, this way the text won't wrap under the icon.
+        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);
+    },
+
+    /**
+     * Adds the specified icon to the dialog.  By default, the class 'ext-mb-icon' is applied for default
+     * styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')
+     * to clear any existing icon. This method must be called before the MessageBox is shown.
+     * The following built-in icon classes are supported, but you can also pass in a custom class name:
+     * <pre>
+Ext.window.MessageBox.INFO
+Ext.window.MessageBox.WARNING
+Ext.window.MessageBox.QUESTION
+Ext.window.MessageBox.ERROR
+     *</pre>
+     * @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon
+     * @return {Ext.window.MessageBox} this
+     */
+    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;
+    },
+
+    /**
+     * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
+     * initiated via {@link Ext.window.MessageBox#progress} or {@link Ext.window.MessageBox#wait},
+     * or by calling {@link Ext.window.MessageBox#show} with progress: true.
+     * @param {Number} value Any number between 0 and 1 (e.g., .5, defaults to 0)
+     * @param {String} progressText The progress text to display inside the progress bar (defaults to '')
+     * @param {String} msg The message box's body text is replaced with the specified string (defaults to undefined
+     * so that any existing body text will not get overwritten by default unless a new value is passed in)
+     * @return {Ext.window.MessageBox} this
+     */
+    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);
+        }
+    },
+
+    /**
+     * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).
+     * If a callback function is passed it will be called after the user clicks either button,
+     * and the id of the button that was clicked will be passed as the only parameter to the callback
+     * (could also be the top-right close button).
+     * @param {String} title The title bar text
+     * @param {String} msg The message box body text
+     * @param {Function} fn (optional) The callback function invoked after the message box is closed
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
+     * @return {Ext.window.MessageBox} this
+     */
+    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);
+    },
+
+    /**
+     * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).
+     * The prompt can be a single-line or multi-line textbox.  If a callback function is passed it will be called after the user
+     * clicks either button, and the id of the button that was clicked (could also be the top-right
+     * close button) and the text that was entered will be passed as the two parameters to the callback.
+     * @param {String} title The title bar text
+     * @param {String} msg The message box body text
+     * @param {Function} fn (optional) The callback function invoked after the message box is closed
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
+     * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
+     * property, or the height in pixels to create the textbox (defaults to false / single-line)
+     * @param {String} value (optional) Default value of the text input element (defaults to '')
+     * @return {Ext.window.MessageBox} this
+     */
+    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);
+    },
+
+    /**
+     * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
+     * interaction while waiting for a long-running process to complete that does not have defined intervals.
+     * You are responsible for closing the message box when the process is complete.
+     * @param {String} msg The message box body text
+     * @param {String} title (optional) The title bar text
+     * @param {Object} config (optional) A {@link Ext.ProgressBar#waitConfig} object
+     * @return {Ext.window.MessageBox} this
+     */
+    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);
+    },
+
+    /**
+     * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt).
+     * If a callback function is passed it will be called after the user clicks the button, and the
+     * id of the button that was clicked will be passed as the only parameter to the callback
+     * (could also be the top-right close button).
+     * @param {String} title The title bar text
+     * @param {String} msg The message box body text
+     * @param {Function} fn (optional) The callback function invoked after the message box is closed
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser wnidow.
+     * @return {Ext.window.MessageBox} this
+     */
+    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);
+    },
+
+    /**
+     * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
+     * the user.  You are responsible for updating the progress bar as needed via {@link Ext.window.MessageBox#updateProgress}
+     * and closing the message box when the process is complete.
+     * @param {String} title The title bar text
+     * @param {String} msg The message box body text
+     * @param {String} progressText (optional) The text to display inside the progress bar (defaults to '')
+     * @return {Ext.window.MessageBox} this
+     */
+    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();
+});
+/**
+ * @class Ext.form.Basic
+ * @extends Ext.util.Observable
+
+Provides input field management, validation, submission, and form loading services for the collection
+of {@link Ext.form.field.Field Field} instances within a {@link Ext.container.Container}. It is recommended
+that you use a {@link Ext.form.Panel} as the form container, as that has logic to automatically
+hook up an instance of {@link Ext.form.Basic} (plus other conveniences related to field configuration.)
+
+#Form Actions#
+
+The Basic class delegates the handling of form loads and submits to instances of {@link Ext.form.action.Action}.
+See the various Action implementations for specific details of each one's functionality, as well as the
+documentation for {@link #doAction} which details the configuration options that can be specified in
+each action call.
+
+The default submit Action is {@link Ext.form.action.Submit}, which uses an Ajax request to submit the
+form's values to a configured URL. To enable normal browser submission of an Ext form, use the
+{@link #standardSubmit} config option.
+
+Note: File uploads are not performed using normal 'Ajax' techniques; see the description for
+{@link #hasUpload} for details.
+
+#Example usage:#
+
+    Ext.create('Ext.form.Panel', {
+        title: 'Basic Form',
+        renderTo: Ext.getBody(),
+        bodyPadding: 5,
+        width: 350,
+
+        // Any configuration items here will be automatically passed along to
+        // the Ext.form.Basic instance when it gets created.
+
+        // The form will submit an AJAX request to this URL when submitted
+        url: 'save-form.php',
+
+        items: [{
+            fieldLabel: 'Field',
+            name: 'theField'
+        }],
+
+        buttons: [{
+            text: 'Submit',
+            handler: function() {
+                // The getForm() method returns the Ext.form.Basic instance:
+                var form = this.up('form').getForm();
+                if (form.isValid()) {
+                    // Submit the Ajax request and handle the response
+                    form.submit({
+                        success: function(form, action) {
+                           Ext.Msg.alert('Success', action.result.msg);
+                        },
+                        failure: function(form, action) {
+                            Ext.Msg.alert('Failed', action.result.msg);
+                        }
+                    });
+                }
+            }
+        }]
+    });
+
+ * @constructor
+ * @param {Ext.container.Container} owner The component that is the container for the form, usually a {@link Ext.form.Panel}
+ * @param {Object} config Configuration options. These are normally specified in the config to the
+ * {@link Ext.form.Panel} constructor, which passes them along to the BasicForm automatically.
+ *
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+
+
+
+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'],
+
+    constructor: function(owner, config) {
+        var me = this,
+            onItemAddOrRemove = me.onItemAddOrRemove;
+
+        /**
+         * @property owner
+         * @type Ext.container.Container
+         * The container component to which this BasicForm is attached.
+         */
+        me.owner = owner;
+
+        // Listen for addition/removal of fields in the owner container
+        me.mon(owner, {
+            add: onItemAddOrRemove,
+            remove: onItemAddOrRemove,
+            scope: me
+        });
+
+        Ext.apply(me, config);
+
+        // Normalize the paramOrder to an Array
+        if (Ext.isString(me.paramOrder)) {
+            me.paramOrder = me.paramOrder.split(/[\s,|]/);
+        }
+
+        me.addEvents(
+            /**
+             * @event beforeaction
+             * Fires before any action is performed. Return false to cancel the action.
+             * @param {Ext.form.Basic} this
+             * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} to be performed
+             */
+            'beforeaction',
+            /**
+             * @event actionfailed
+             * Fires when an action fails.
+             * @param {Ext.form.Basic} this
+             * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} that failed
+             */
+            'actionfailed',
+            /**
+             * @event actioncomplete
+             * Fires when an action is completed.
+             * @param {Ext.form.Basic} this
+             * @param {Ext.form.action.Action} action The {@link Ext.form.action.Action} that completed
+             */
+            'actioncomplete',
+            /**
+             * @event validitychange
+             * Fires when the validity of the entire form changes.
+             * @param {Ext.form.Basic} this
+             * @param {Boolean} valid <tt>true</tt> if the form is now valid, <tt>false</tt> if it is now invalid.
+             */
+            'validitychange',
+            /**
+             * @event dirtychange
+             * Fires when the dirty state of the entire form changes.
+             * @param {Ext.form.Basic} this
+             * @param {Boolean} dirty <tt>true</tt> if the form is now dirty, <tt>false</tt> if it is no longer dirty.
+             */
+            'dirtychange'
+        );
+        me.callParent();
+    },
+
+    /**
+     * Do any post constructor initialization
+     * @private
+     */
+    initialize: function(){
+        this.initialized = true;
+        this.onValidityChange(!this.hasInvalidField());
+    },
+
+    /**
+     * @cfg {String} method
+     * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
+     */
+    /**
+     * @cfg {Ext.data.reader.Reader} reader
+     * An Ext.data.DataReader (e.g. {@link Ext.data.reader.Xml}) to be used to read
+     * data when executing 'load' actions. This is optional as there is built-in
+     * support for processing JSON responses.
+     */
+    /**
+     * @cfg {Ext.data.reader.Reader} errorReader
+     * <p>An Ext.data.DataReader (e.g. {@link Ext.data.reader.Xml}) to be used to
+     * read field error messages returned from 'submit' actions. This is optional
+     * as there is built-in support for processing JSON responses.</p>
+     * <p>The Records which provide messages for the invalid Fields must use the
+     * Field name (or id) as the Record ID, and must contain a field called 'msg'
+     * which contains the error message.</p>
+     * <p>The errorReader does not have to be a full-blown implementation of a
+     * Reader. It simply needs to implement a <tt>read(xhr)</tt> function
+     * which returns an Array of Records in an object with the following
+     * structure:</p><pre><code>
+{
+    records: recordArray
+}
+</code></pre>
+     */
+
+    /**
+     * @cfg {String} url
+     * The URL to use for form actions if one isn't supplied in the
+     * {@link #doAction doAction} options.
+     */
+
+    /**
+     * @cfg {Object} baseParams
+     * <p>Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.</p>
+     * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode Ext.Object.toQueryString}.</p>
+     */
+
+    /**
+     * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
+     */
+    timeout: 30,
+
+    /**
+     * @cfg {Object} api (Optional) If specified, load and submit actions will be handled
+     * with {@link Ext.form.action.DirectLoad} and {@link Ext.form.action.DirectLoad}.
+     * Methods which have been imported by {@link Ext.direct.Manager} can be specified here to load and submit
+     * forms.
+     * Such as the following:<pre><code>
+api: {
+    load: App.ss.MyProfile.load,
+    submit: App.ss.MyProfile.submit
+}
+</code></pre>
+     * <p>Load actions can use <code>{@link #paramOrder}</code> or <code>{@link #paramsAsHash}</code>
+     * to customize how the load method is invoked.
+     * Submit actions will always use a standard form submit. The <tt>formHandler</tt> configuration must
+     * be set on the associated server-side method which has been imported by {@link Ext.direct.Manager}.</p>
+     */
+
+    /**
+     * @cfg {Array/String} paramOrder <p>A list of params to be executed server side.
+     * Defaults to <tt>undefined</tt>. Only used for the <code>{@link #api}</code>
+     * <code>load</code> configuration.</p>
+     * <p>Specify the params in the order in which they must be executed on the
+     * server-side as either (1) an Array of String values, or (2) a String of params
+     * delimited by either whitespace, comma, or pipe. For example,
+     * any of the following would be acceptable:</p><pre><code>
+paramOrder: ['param1','param2','param3']
+paramOrder: 'param1 param2 param3'
+paramOrder: 'param1,param2,param3'
+paramOrder: 'param1|param2|param'
+     </code></pre>
+     */
+
+    /**
+     * @cfg {Boolean} paramsAsHash Only used for the <code>{@link #api}</code>
+     * <code>load</code> configuration. If <tt>true</tt>, parameters will be sent as a
+     * single hash collection of named arguments (defaults to <tt>false</tt>). Providing a
+     * <tt>{@link #paramOrder}</tt> nullifies this configuration.
+     */
+    paramsAsHash: false,
+
+    /**
+     * @cfg {String} waitTitle
+     * The default title to show for the waiting message box (defaults to <tt>'Please Wait...'</tt>)
+     */
+    waitTitle: 'Please Wait...',
+
+    /**
+     * @cfg {Boolean} trackResetOnLoad If set to <tt>true</tt>, {@link #reset}() resets to the last loaded
+     * or {@link #setValues}() data instead of when the form was first created.  Defaults to <tt>false</tt>.
+     */
+    trackResetOnLoad: false,
+
+    /**
+     * @cfg {Boolean} standardSubmit
+     * <p>If set to <tt>true</tt>, a standard HTML form submit is used instead
+     * of a XHR (Ajax) style form submission. Defaults to <tt>false</tt>. All of
+     * the field values, plus any additional params configured via {@link #baseParams}
+     * and/or the <code>options</code> to {@link #submit}, will be included in the
+     * values submitted in the form.</p>
+     */
+
+    /**
+     * @cfg {Mixed} waitMsgTarget
+     * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
+     * element by passing it or its id or mask the form itself by passing in true. Defaults to <tt>undefined</tt>.
+     */
+
+
+    // Private
+    wasDirty: false,
+
+
+    /**
+     * Destroys this object.
+     */
+    destroy: function() {
+        this.clearListeners();
+    },
+
+    /**
+     * @private
+     * Handle addition or removal of descendant items. Invalidates the cached list of fields
+     * so that {@link #getFields} will do a fresh query next time it is called. Also adds listeners
+     * for state change events on added fields, and tracks components with formBind=true.
+     */
+    onItemAddOrRemove: function(parent, child) {
+        var me = this,
+            isAdding = !!child.ownerCt,
+            isContainer = child.isContainer;
+
+        function handleField(field) {
+            // Listen for state change events on fields
+            me[isAdding ? 'mon' : 'mun'](field, {
+                validitychange: me.checkValidity,
+                dirtychange: me.checkDirty,
+                scope: me,
+                buffer: 100 //batch up sequential calls to avoid excessive full-form validation
+            });
+            // Flush the cached list of fields
+            delete me._fields;
+        }
+
+        if (child.isFormField) {
+            handleField(child);
+        }
+        else if (isContainer) {
+            // Walk down
+            Ext.Array.forEach(child.query('[isFormField]'), handleField);
+        }
+
+        // Flush the cached list of formBind components
+        delete this._boundItems;
+
+        // Check form bind, but only after initial add
+        if (me.initialized) {
+            me.onValidityChange(!me.hasInvalidField());
+        }
+    },
+
+    /**
+     * Return all the {@link Ext.form.field.Field} components in the owner container.
+     * @return {Ext.util.MixedCollection} Collection of the Field objects
+     */
+    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;
+    },
+
+    /**
+     * Returns true if the form contains any invalid fields. No fields will be marked as invalid
+     * as a result of calling this; to trigger marking of fields use {@link #isValid} instead.
+     */
+    hasInvalidField: function() {
+        return !!this.getFields().findBy(function(field) {
+            var preventMark = field.preventMark,
+                isValid;
+            field.preventMark = true;
+            isValid = field.isValid();
+            field.preventMark = preventMark;
+            return !isValid;
+        });
+    },
+
+    /**
+     * Returns true if client-side validation on the form is successful. Any invalid fields will be
+     * marked as invalid. If you only want to determine overall form validity without marking anything,
+     * use {@link #hasInvalidField} instead.
+     * @return Boolean
+     */
+    isValid: function() {
+        var me = this,
+            invalid;
+        me.batchLayouts(function() {
+            invalid = me.getFields().filterBy(function(field) {
+                return !field.validate();
+            });
+        });
+        return invalid.length < 1;
+    },
+
+    /**
+     * Check whether the validity of the entire form has changed since it was last checked, and
+     * if so fire the {@link #validitychange validitychange} event. This is automatically invoked
+     * when an individual field's validity changes.
+     */
+    checkValidity: function() {
+        var me = this,
+            valid = !me.hasInvalidField();
+        if (valid !== me.wasValid) {
+            me.onValidityChange(valid);
+            me.fireEvent('validitychange', me, valid);
+            me.wasValid = valid;
+        }
+    },
+
+    /**
+     * @private
+     * Handle changes in the form's validity. If there are any sub components with
+     * formBind=true then they are enabled/disabled based on the new validity.
+     * @param {Boolean} valid
+     */
+    onValidityChange: function(valid) {
+        var boundItems = this.getBoundItems();
+        if (boundItems) {
+            boundItems.each(function(cmp) {
+                if (cmp.disabled === valid) {
+                    cmp.setDisabled(!valid);
+                }
+            });
+        }
+    },
+
+    /**
+     * <p>Returns true if any fields in this form have changed from their original values.</p>
+     * <p>Note that if this BasicForm was configured with {@link #trackResetOnLoad} then the
+     * Fields' <em>original values</em> are updated when the values are loaded by {@link #setValues}
+     * or {@link #loadRecord}.</p>
+     * @return Boolean
+     */
+    isDirty: function() {
+        return !!this.getFields().findBy(function(f) {
+            return f.isDirty();
+        });
+    },
+
+    /**
+     * Check whether the dirty state of the entire form has changed since it was last checked, and
+     * if so fire the {@link #dirtychange dirtychange} event. This is automatically invoked
+     * when an individual field's dirty state changes.
+     */
+    checkDirty: function() {
+        var dirty = this.isDirty();
+        if (dirty !== this.wasDirty) {
+            this.fireEvent('dirtychange', this, dirty);
+            this.wasDirty = dirty;
+        }
+    },
+
+    /**
+     * <p>Returns true if the form contains a file upload field. This is used to determine the
+     * method for submitting the form: File uploads are not performed using normal 'Ajax' techniques,
+     * that is they are <b>not</b> performed using XMLHttpRequests. Instead a hidden <tt>&lt;form></tt>
+     * element containing all the fields is created temporarily and submitted with its
+     * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
+     * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
+     * but removed after the return data has been gathered.</p>
+     * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
+     * server is using JSON to send the return object, then the
+     * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
+     * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
+     * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
+     * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
+     * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
+     * is created containing a <tt>responseText</tt> property in order to conform to the
+     * requirements of event handlers and callbacks.</p>
+     * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
+     * and some server technologies (notably JEE) may require some custom processing in order to
+     * retrieve parameter names and parameter values from the packet content.</p>
+     * @return Boolean
+     */
+    hasUpload: function() {
+        return !!this.getFields().findBy(function(f) {
+            return f.isFileUpload();
+        });
+    },
+
+    /**
+     * Performs a predefined action (an implementation of {@link Ext.form.action.Action})
+     * to perform application-specific processing.
+     * @param {String/Ext.form.action.Action} action The name of the predefined action type,
+     * or instance of {@link Ext.form.action.Action} to perform.
+     * @param {Object} options (optional) The options to pass to the {@link Ext.form.action.Action}
+     * that will get created, if the <tt>action</tt> argument is a String.
+     * <p>All of the config options listed below are supported by both the
+     * {@link Ext.form.action.Submit submit} and {@link Ext.form.action.Load load}
+     * actions unless otherwise noted (custom actions could also accept
+     * other config options):</p><ul>
+     *
+     * <li><b>url</b> : String<div class="sub-desc">The url for the action (defaults
+     * to the form's {@link #url}.)</div></li>
+     *
+     * <li><b>method</b> : String<div class="sub-desc">The form method to use (defaults
+     * to the form's method, or POST if not defined)</div></li>
+     *
+     * <li><b>params</b> : String/Object<div class="sub-desc"><p>The params to pass
+     * (defaults to the form's baseParams, or none if not defined)</p>
+     * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode Ext.Object.toQueryString}.</p></div></li>
+     *
+     * <li><b>headers</b> : Object<div class="sub-desc">Request headers to set for the action.</div></li>
+     *
+     * <li><b>success</b> : Function<div class="sub-desc">The callback that will
+     * be invoked after a successful response (see top of
+     * {@link Ext.form.action.Submit submit} and {@link Ext.form.action.Load load}
+     * for a description of what constitutes a successful response).
+     * The function is passed the following parameters:<ul>
+     * <li><tt>form</tt> : The {@link Ext.form.Basic} that requested the action.</li>
+     * <li><tt>action</tt> : The {@link Ext.form.action.Action Action} object which performed the operation.
+     * <div class="sub-desc">The action object contains these properties of interest:<ul>
+     * <li><tt>{@link Ext.form.action.Action#response response}</tt></li>
+     * <li><tt>{@link Ext.form.action.Action#result result}</tt> : interrogate for custom postprocessing</li>
+     * <li><tt>{@link Ext.form.action.Action#type type}</tt></li>
+     * </ul></div></li></ul></div></li>
+     *
+     * <li><b>failure</b> : Function<div class="sub-desc">The callback that will be invoked after a
+     * failed transaction attempt. The function is passed the following parameters:<ul>
+     * <li><tt>form</tt> : The {@link Ext.form.Basic} that requested the action.</li>
+     * <li><tt>action</tt> : The {@link Ext.form.action.Action Action} object which performed the operation.
+     * <div class="sub-desc">The action object contains these properties of interest:<ul>
+     * <li><tt>{@link Ext.form.action.Action#failureType failureType}</tt></li>
+     * <li><tt>{@link Ext.form.action.Action#response response}</tt></li>
+     * <li><tt>{@link Ext.form.action.Action#result result}</tt> : interrogate for custom postprocessing</li>
+     * <li><tt>{@link Ext.form.action.Action#type type}</tt></li>
+     * </ul></div></li></ul></div></li>
+     *
+     * <li><b>scope</b> : Object<div class="sub-desc">The scope in which to call the
+     * callback functions (The <tt>this</tt> reference for the callback functions).</div></li>
+     *
+     * <li><b>clientValidation</b> : Boolean<div class="sub-desc">Submit Action only.
+     * Determines whether a Form's fields are validated in a final call to
+     * {@link Ext.form.Basic#isValid isValid} prior to submission. Set to <tt>false</tt>
+     * to prevent this. If undefined, pre-submission field validation is performed.</div></li></ul>
+     *
+     * @return {Ext.form.Basic} this
+     */
+    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;
+    },
+
+    /**
+     * Shortcut to {@link #doAction do} a {@link Ext.form.action.Submit submit action}. This will use the
+     * {@link Ext.form.action.Submit AJAX submit action} by default. If the {@link #standardsubmit} config is
+     * enabled it will use a standard form element to submit, or if the {@link #api} config is present it will
+     * use the {@link Ext.form.action.DirectLoad Ext.direct.Direct submit action}.
+     * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
+     * <p>The following code:</p><pre><code>
+myFormPanel.getForm().submit({
+    clientValidation: true,
+    url: 'updateConsignment.php',
+    params: {
+        newStatus: 'delivered'
+    },
+    success: function(form, action) {
+       Ext.Msg.alert('Success', action.result.msg);
+    },
+    failure: function(form, action) {
+        switch (action.failureType) {
+            case Ext.form.action.Action.CLIENT_INVALID:
+                Ext.Msg.alert('Failure', 'Form fields may not be submitted with invalid values');
+                break;
+            case Ext.form.action.Action.CONNECT_FAILURE:
+                Ext.Msg.alert('Failure', 'Ajax communication failed');
+                break;
+            case Ext.form.action.Action.SERVER_INVALID:
+               Ext.Msg.alert('Failure', action.result.msg);
+       }
+    }
+});
+</code></pre>
+     * would process the following server response for a successful submission:<pre><code>
+{
+    "success":true, // note this is Boolean, not string
+    "msg":"Consignment updated"
+}
+</code></pre>
+     * and the following server response for a failed submission:<pre><code>
+{
+    "success":false, // note this is Boolean, not string
+    "msg":"You do not have permission to perform this operation"
+}
+</code></pre>
+     * @return {Ext.form.Basic} this
+     */
+    submit: function(options) {
+        return this.doAction(this.standardSubmit ? 'standardsubmit' : this.api ? 'directsubmit' : 'submit', options);
+    },
+
+    /**
+     * Shortcut to {@link #doAction do} a {@link Ext.form.action.Load load action}.
+     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
+     * @return {Ext.form.Basic} this
+     */
+    load: function(options) {
+        return this.doAction(this.api ? 'directload' : 'load', options);
+    },
+
+    /**
+     * Persists the values in this form into the passed {@link Ext.data.Model} object in a beginEdit/endEdit block.
+     * @param {Ext.data.Record} record The record to edit
+     * @return {Ext.form.Basic} this
+     */
+    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;
+    },
+
+    /**
+     * Loads an {@link Ext.data.Model} into this form by calling {@link #setValues} with the
+     * {@link Ext.data.Model#data record data}.
+     * See also {@link #trackResetOnLoad}.
+     * @param {Ext.data.Model} record The record to load
+     * @return {Ext.form.Basic} this
+     */
+    loadRecord: function(record) {
+        this._record = record;
+        return this.setValues(record.data);
+    },
+    
+    /**
+     * Returns the last Ext.data.Model instance that was loaded via {@link #loadRecord}
+     * @return {Ext.data.Model} The record
+     */
+    getRecord: function() {
+        return this._record;
+    },
+
+    /**
+     * @private
+     * Called before an action is performed via {@link #doAction}.
+     * @param {Ext.form.action.Action} action The Action instance that was invoked
+     */
+    beforeAction: function(action) {
+        var waitMsg = action.waitMsg,
+            maskCls = Ext.baseCSSPrefix + 'mask-loading',
+            waitMsgTarget;
+
+        // Call HtmlEditor's syncValue before actions
+        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);
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Called after an action is performed via {@link #doAction}.
+     * @param {Ext.form.action.Action} action The Action instance that was invoked
+     * @param {Boolean} success True if the action completed successfully, false, otherwise.
+     */
+    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);
+        }
+    },
+
+
+    /**
+     * Find a specific {@link Ext.form.field.Field} in this form by id or name.
+     * @param {String} id The value to search for (specify either a {@link Ext.Component#id id} or
+     * {@link Ext.form.field.Field#getName name or hiddenName}).
+     * @return Ext.form.field.Field The first matching field, or <tt>null</tt> if none was found.
+     */
+    findField: function(id) {
+        return this.getFields().findBy(function(f) {
+            return f.id === id || f.getName() === id;
+        });
+    },
+
+
+    /**
+     * Mark fields in this form invalid in bulk.
+     * @param {Array/Object} errors Either an array in the form <code>[{id:'fieldId', msg:'The message'}, ...]</code>,
+     * an object hash of <code>{id: msg, id2: msg2}</code>, or a {@link Ext.data.Errors} object.
+     * @return {Ext.form.Basic} this
+     */
+    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;
+    },
+
+    /**
+     * Set values for fields in this form in bulk.
+     * @param {Array/Object} values Either an array in the form:<pre><code>
+[{id:'clientName', value:'Fred. Olsen Lines'},
+ {id:'portOfLoading', value:'FXT'},
+ {id:'portOfDischarge', value:'OSL'} ]</code></pre>
+     * or an object hash of the form:<pre><code>
+{
+    clientName: 'Fred. Olsen Lines',
+    portOfLoading: 'FXT',
+    portOfDischarge: 'OSL'
+}</code></pre>
+     * @return {Ext.form.Basic} 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)) {
+            // array of objects
+            Ext.each(values, function(val) {
+                setVal(val.id, val.value);
+            });
+        } else {
+            // object hash
+            Ext.iterate(values, setVal);
+        }
+        return this;
+    },
+
+    /**
+     * Retrieves the fields in the form as a set of key/value pairs, using their
+     * {@link Ext.form.field.Field#getSubmitData getSubmitData()} method to collect the values.
+     * If multiple fields return values under the same name those values will be combined into an Array.
+     * This is similar to {@link #getFieldValues} except that this method collects only String values for
+     * submission, while getFieldValues collects type-specific data values (e.g. Date objects for date fields.)
+     * @param {Boolean} asString (optional) If true, will return the key/value collection as a single
+     * URL-encoded param string. Defaults to false.
+     * @param {Boolean} dirtyOnly (optional) If true, only fields that are dirty will be included in the result.
+     * Defaults to false.
+     * @param {Boolean} includeEmptyText (optional) If true, the configured emptyText of empty fields will be used.
+     * Defaults to false.
+     * @return {String/Object}
+     */
+    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;
+    },
+
+    /**
+     * Retrieves the fields in the form as a set of key/value pairs, using their
+     * {@link Ext.form.field.Field#getModelData getModelData()} method to collect the values.
+     * If multiple fields return values under the same name those values will be combined into an Array.
+     * This is similar to {@link #getValues} except that this method collects type-specific data values
+     * (e.g. Date objects for date fields) while getValues returns only String values for submission.
+     * @param {Boolean} dirtyOnly (optional) If true, only fields that are dirty will be included in the result.
+     * Defaults to false.
+     * @return {Object}
+     */
+    getFieldValues: function(dirtyOnly) {
+        return this.getValues(false, dirtyOnly, false, true);
+    },
+
+    /**
+     * Clears all invalid field messages in this form.
+     * @return {Ext.form.Basic} this
+     */
+    clearInvalid: function() {
+        var me = this;
+        me.batchLayouts(function() {
+            me.getFields().each(function(f) {
+                f.clearInvalid();
+            });
+        });
+        return me;
+    },
+
+    /**
+     * Resets all fields in this form.
+     * @return {Ext.form.Basic} this
+     */
+    reset: function() {
+        var me = this;
+        me.batchLayouts(function() {
+            me.getFields().each(function(f) {
+                f.reset();
+            });
+        });
+        return me;
+    },
+
+    /**
+     * Calls {@link Ext#apply Ext.apply} for all fields in this form with the passed object.
+     * @param {Object} obj The object to be applied
+     * @return {Ext.form.Basic} this
+     */
+    applyToFields: function(obj) {
+        this.getFields().each(function(f) {
+            Ext.apply(f, obj);
+        });
+        return this;
+    },
+
+    /**
+     * Calls {@link Ext#applyIf Ext.applyIf} for all field in this form with the passed object.
+     * @param {Object} obj The object to be applied
+     * @return {Ext.form.Basic} this
+     */
+    applyIfToFields: function(obj) {
+        this.getFields().each(function(f) {
+            Ext.applyIf(f, obj);
+        });
+        return this;
+    },
+
+    /**
+     * @private
+     * Utility wrapper that suspends layouts of all field parent containers for the duration of a given
+     * function. Used during full-form validation and resets to prevent huge numbers of layouts.
+     * @param {Function} fn
+     */
+    batchLayouts: function(fn) {
+        var me = this,
+            suspended = new Ext.util.HashMap();
+
+        // Temporarily suspend layout on each field's immediate owner so we don't get a huge layout cascade
+        me.getFields().each(function(field) {
+            var ownerCt = field.ownerCt;
+            if (!suspended.contains(ownerCt)) {
+                suspended.add(ownerCt);
+                ownerCt.oldSuspendLayout = ownerCt.suspendLayout;
+                ownerCt.suspendLayout = true;
+            }
+        });
+
+        // Invoke the function
+        fn();
+
+        // Un-suspend the container layouts
+        suspended.each(function(id, ct) {
+            ct.suspendLayout = ct.oldSuspendLayout;
+            delete ct.oldSuspendLayout;
+        });
+
+        // Trigger a single layout
+        me.owner.doComponentLayout();
+    }
+});
+
+/**
+ * @class Ext.form.FieldAncestor
+
+A mixin for {@link Ext.container.Container} components that are likely to have form fields in their
+items subtree. Adds the following capabilities:
+
+- Methods for handling the addition and removal of {@link Ext.form.Labelable} and {@link Ext.form.field.Field}
+  instances at any depth within the container.
+- Events ({@link #fieldvaliditychange} and {@link #fielderrorchange}) for handling changes to the state
+  of individual fields at the container level.
+- Automatic application of {@link #fieldDefaults} config properties to each field added within the
+  container, to facilitate uniform configuration of all fields.
+
+This mixin is primarily for internal use by {@link Ext.form.Panel} and {@link Ext.form.FieldContainer},
+and should not normally need to be used directly.
+
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+Ext.define('Ext.form.FieldAncestor', {
+
+    /**
+     * @cfg {Object} fieldDefaults
+     * <p>If specified, the properties in this object are used as default config values for each
+     * {@link Ext.form.Labelable} instance (e.g. {@link Ext.form.field.Base} or {@link Ext.form.FieldContainer})
+     * that is added as a descendant of this container. Corresponding values specified in an individual field's
+     * own configuration, or from the {@link Ext.container.Container#defaults defaults config} of its parent container,
+     * will take precedence. See the documentation for {@link Ext.form.Labelable} to see what config
+     * options may be specified in the <tt>fieldDefaults</tt>.</p>
+     * <p>Example:</p>
+     * <pre><code>new Ext.form.Panel({
+    fieldDefaults: {
+        labelAlign: 'left',
+        labelWidth: 100
+    },
+    items: [{
+        xtype: 'fieldset',
+        defaults: {
+            labelAlign: 'top'
+        },
+        items: [{
+            name: 'field1'
+        }, {
+            name: 'field2'
+        }]
+    }, {
+        xtype: 'fieldset',
+        items: [{
+            name: 'field3',
+            labelWidth: 150
+        }, {
+            name: 'field4'
+        }]
+    }]
+});</code></pre>
+     * <p>In this example, field1 and field2 will get labelAlign:'top' (from the fieldset's <tt>defaults</tt>)
+     * and labelWidth:100 (from <tt>fieldDefaults</tt>), field3 and field4 will both get labelAlign:'left' (from
+     * <tt>fieldDefaults</tt> and field3 will use the labelWidth:150 from its own config.</p>
+     */
+
+
+    /**
+     * @protected Initializes the FieldAncestor's state; this must be called from the initComponent method
+     * of any components importing this mixin.
+     */
+    initFieldAncestor: function() {
+        var me = this,
+            onSubtreeChange = me.onFieldAncestorSubtreeChange;
+
+        me.addEvents(
+            /**
+             * @event fielderrorchange
+             * Fires when the validity state of any one of the {@link Ext.form.field.Field} instances within this
+             * container changes.
+             * @param {Ext.form.FieldAncestor} this
+             * @param {Ext.form.Labelable} The Field instance whose validity changed
+             * @param {String} isValid The field's new validity state
+             */
+            'fieldvaliditychange',
+
+            /**
+             * @event fielderrorchange
+             * Fires when the active error message is changed for any one of the {@link Ext.form.Labelable}
+             * instances within this container.
+             * @param {Ext.form.FieldAncestor} this
+             * @param {Ext.form.Labelable} The Labelable instance whose active error was changed
+             * @param {String} error The active error message
+             */
+            'fielderrorchange'
+        );
+
+        // Catch addition and removal of descendant fields
+        me.on('add', onSubtreeChange, me);
+        me.on('remove', onSubtreeChange, me);
+
+        me.initFieldDefaults();
+    },
+
+    /**
+     * @private Initialize the {@link #fieldDefaults} object
+     */
+    initFieldDefaults: function() {
+        if (!this.fieldDefaults) {
+            this.fieldDefaults = {};
+        }
+    },
+
+    /**
+     * @private
+     * Handle the addition and removal of components in the FieldAncestor component's child tree.
+     */
+    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);
+    },
+
+    /**
+     * @protected Called when a {@link Ext.form.Labelable} instance is added to the container's subtree.
+     * @param {Ext.form.Labelable} labelable The instance that was added
+     */
+    onLabelableAdded: function(labelable) {
+        var me = this;
+
+        // buffer slightly to avoid excessive firing while sub-fields are changing en masse
+        me.mon(labelable, 'errorchange', me.handleFieldErrorChange, me, {buffer: 10});
+
+        labelable.setFieldDefaults(me.fieldDefaults);
+    },
+
+    /**
+     * @protected Called when a {@link Ext.form.field.Field} instance is added to the container's subtree.
+     * @param {Ext.form.field.Field} field The field which was added
+     */
+    onFieldAdded: function(field) {
+        var me = this;
+        me.mon(field, 'validitychange', me.handleFieldValidityChange, me);
+    },
+
+    /**
+     * @protected Called when a {@link Ext.form.Labelable} instance is removed from the container's subtree.
+     * @param {Ext.form.Labelable} labelable The instance that was removed
+     */
+    onLabelableRemoved: function(labelable) {
+        var me = this;
+        me.mun(labelable, 'errorchange', me.handleFieldErrorChange, me);
+    },
+
+    /**
+     * @protected Called when a {@link Ext.form.field.Field} instance is removed from the container's subtree.
+     * @param {Ext.form.field.Field} field The field which was removed
+     */
+    onFieldRemoved: function(field) {
+        var me = this;
+        me.mun(field, 'validitychange', me.handleFieldValidityChange, me);
+    },
+
+    /**
+     * @private Handle validitychange events on sub-fields; invoke the aggregated event and method
+     */
+    handleFieldValidityChange: function(field, isValid) {
+        var me = this;
+        me.fireEvent('fieldvaliditychange', me, field, isValid);
+        me.onFieldValidityChange();
+    },
+
+    /**
+     * @private Handle errorchange events on sub-fields; invoke the aggregated event and method
+     */
+    handleFieldErrorChange: function(labelable, activeError) {
+        var me = this;
+        me.fireEvent('fielderrorchange', me, labelable, activeError);
+        me.onFieldErrorChange();
+    },
+
+    /**
+     * @protected Fired when the validity of any field within the container changes.
+     * @param {Ext.form.field.Field} The sub-field whose validity changed
+     * @param {String} The new validity state
+     */
+    onFieldValidityChange: Ext.emptyFn,
+
+    /**
+     * @protected Fired when the error message of any field within the container changes.
+     * @param {Ext.form.Labelable} The sub-field whose active error changed
+     * @param {String} The new active error message
+     */
+    onFieldErrorChange: Ext.emptyFn
+
+});
+/**
+ * @class Ext.layout.container.CheckboxGroup
+ * @extends Ext.layout.container.Container
+ * <p>This layout implements the column arrangement for {@link Ext.form.CheckboxGroup} and {@link Ext.form.RadioGroup}.
+ * It groups the component's sub-items into columns based on the component's
+ * {@link Ext.form.CheckboxGroup#columns columns} and {@link Ext.form.CheckboxGroup#vertical} config properties.</p>
+ *
+ */
+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;
+
+        // Distribute the items into the appropriate column containers. We add directly to the
+        // containers' items collection rather than calling container.add(), because we need the
+        // checkboxes to maintain their original ownerCt. The distribution is done on each layout
+        // in case items have been added, removed, or reordered.
+
+        shadowItems.each(function(col) {
+            col.items.clear();
+        });
+
+        // If columns="auto", then the number of required columns may change as checkboxes are added/removed
+        // from the CheckboxGroup; adjust to match.
+        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 {
+            // Ensure all items are rendered in the correct place in the correct column - this won't
+            // get done by the column containers themselves if their dimensions are not changing.
+            shadowItems.each(function(col) {
+                var layout = col.getLayout();
+                layout.renderItems(layout.getLayoutItems(), layout.getRenderTarget());
+            });
+        }
+
+        shadowCt.doComponentLayout();
+    },
+
+
+    // We don't want to render any items to the owner directly, that gets handled by each column's own layout
+    renderItems: Ext.emptyFn,
+
+
+    /**
+     * @private
+     * Creates and returns the shadow hbox container that will be used to arrange the owner's items
+     * into columns.
+     */
+    getShadowCt: function() {
+        var me = this,
+            shadowCt = me.shadowCt,
+            owner, items, item, columns, columnsIsArray, numCols, i;
+
+        if (!shadowCt) {
+            // Create the column containers based on the owner's 'columns' config
+            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) {
+                    // Array can contain mixture of whole numbers, used as fixed pixel widths, and fractional
+                    // numbers, used as relative flex values.
+                    if (columns[i] < 1) {
+                        item.flex = columns[i];
+                    } else {
+                        item.width = columns[i];
+                    }
+                }
+                else {
+                    // All columns the same width
+                    item.flex = 1;
+                }
+                items.push(item);
+            }
+
+            // Create the shadow container; delay rendering until after items are added to the columns
+            shadowCt = me.shadowCt = Ext.createWidget('container', {
+                layout: 'hbox',
+                items: items,
+                ownerCt: owner
+            });
+        }
+        
+        return shadowCt;
+    },
+
+
+    /**
+     * @private Get the number of columns in the checkbox group
+     */
+    getColCount: function() {
+        var owner = this.owner,
+            colsCfg = owner.columns;
+        return Ext.isArray(colsCfg) ? colsCfg.length : (Ext.isNumber(colsCfg) ? colsCfg : owner.items.length);
+    }
+
+});
+
+/**
+ * @class Ext.form.FieldContainer
+ * @extends Ext.container.Container
+
+FieldContainer is a derivation of {@link Ext.container.Container Container} that implements the
+{@link Ext.form.Labelable Labelable} mixin. This allows it to be configured so that it is rendered with
+a {@link #fieldLabel field label} and optional {@link #msgTarget error message} around its sub-items.
+This is useful for arranging a group of fields or other components within a single item in a form, so
+that it lines up nicely with other fields. A common use is for grouping a set of related fields under
+a single label in a form.
+
+The container's configured {@link #items} will be layed out within the field body area according to the
+configured {@link #layout} type. The default layout is `'autocontainer'`.
+
+Like regular fields, FieldContainer can inherit its decoration configuration from the
+{@link Ext.form.Panel#fieldDefaults fieldDefaults} of an enclosing FormPanel. In addition,
+FieldContainer itself can pass {@link #fieldDefaults} to any {@link Ext.form.Labelable fields}
+it may itself contain.
+
+If you are grouping a set of {@link Ext.form.field.Checkbox Checkbox} or {@link Ext.form.field.Radio Radio}
+fields in a single labeled container, consider using a {@link Ext.form.CheckboxGroup}
+or {@link Ext.form.RadioGroup} instead as they are specialized for handling those types.
+{@img Ext.form.FieldContainer/Ext.form.FieldContainer1.png Ext.form.FieldContainer component}
+__Example usage:__
+
+    Ext.create('Ext.form.Panel', {
+        title: 'FieldContainer Example',
+        width: 550,
+        bodyPadding: 10,
+    
+        items: [{
+            xtype: 'fieldcontainer',
+            fieldLabel: 'Last Three Jobs',
+            labelWidth: 100,
+    
+            // The body area will contain three text fields, arranged
+            // horizontally, separated by draggable splitters.
+            layout: 'hbox',
+            items: [{
+                xtype: 'textfield',
+                flex: 1
+            }, {
+                xtype: 'splitter'
+            }, {
+                xtype: 'textfield',
+                flex: 1
+            }, {
+                xtype: 'splitter'
+            }, {
+                xtype: 'textfield',
+                flex: 1
+            }]
+        }],
+        renderTo: Ext.getBody()
+    });
+
+__Usage of {@link #fieldDefaults}:__
+{@img Ext.form.FieldContainer/Ext.form.FieldContainer2.png Ext.form.FieldContainer component}
+
+    Ext.create('Ext.form.Panel', {
+        title: 'FieldContainer Example',
+        width: 350,
+        bodyPadding: 10,
+    
+        items: [{
+            xtype: 'fieldcontainer',
+            fieldLabel: 'Your Name',
+            labelWidth: 75,
+            defaultType: 'textfield',
+    
+            // Arrange fields vertically, stretched to full width
+            layout: 'anchor',
+            defaults: {
+                layout: '100%'
+            },
+    
+            // These config values will be applied to both sub-fields, except
+            // for Last Name which will use its own msgTarget.
+            fieldDefaults: {
+                msgTarget: 'under',
+                labelAlign: 'top'
+            },
+    
+            items: [{
+                fieldLabel: 'First Name',
+                name: 'firstName'
+            }, {
+                fieldLabel: 'Last Name',
+                name: 'lastName',
+                msgTarget: 'under'
+            }]
+        }],
+        renderTo: Ext.getBody()
+    });
+
+
+ * @constructor
+ * Creates a new Ext.form.FieldContainer instance.
+ * @param {Object} config The component configuration.
+ *
+ * @xtype fieldcontainer
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+Ext.define('Ext.form.FieldContainer', {
+    extend: 'Ext.container.Container',
+    mixins: {
+        labelable: 'Ext.form.Labelable',
+        fieldAncestor: 'Ext.form.FieldAncestor'
+    },
+    alias: 'widget.fieldcontainer',
+
+    componentLayout: 'field',
+
+    /**
+     * @cfg {Boolean} combineLabels
+     * If set to true, and there is no defined {@link #fieldLabel}, the field container will automatically
+     * generate its label by combining the labels of all the fields it contains. Defaults to false.
+     */
+    combineLabels: false,
+
+    /**
+     * @cfg {String} labelConnector
+     * The string to use when joining the labels of individual sub-fields, when {@link #combineLabels} is
+     * set to true. Defaults to ', '.
+     */
+    labelConnector: ', ',
+
+    /**
+     * @cfg {Boolean} combineErrors
+     * If set to true, the field container will automatically combine and display the validation errors from
+     * all the fields it contains as a single error on the container, according to the configured
+     * {@link #msgTarget}. Defaults to false.
+     */
+    combineErrors: false,
+    
+    maskOnDisable: false,
+
+    initComponent: function() {
+        var me = this,
+            onSubCmpAddOrRemove = me.onSubCmpAddOrRemove;
+
+        // Init mixins
+        me.initLabelable();
+        me.initFieldAncestor();
+
+        me.callParent();
+    },
+
+    /**
+     * @protected Called when a {@link Ext.form.Labelable} instance is added to the container's subtree.
+     * @param {Ext.form.Labelable} labelable The instance that was added
+     */
+    onLabelableAdded: function(labelable) {
+        var me = this;
+        me.mixins.fieldAncestor.onLabelableAdded.call(this, labelable);
+        me.updateLabel();
+    },
+
+    /**
+     * @protected Called when a {@link Ext.form.Labelable} instance is removed from the container's subtree.
+     * @param {Ext.form.Labelable} labelable The instance that was removed
+     */
+    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());
+    },
+
+    /**
+     * Returns the combined field label if {@link #combineLabels} is set to true and if there is no
+     * set {@link #fieldLabel}. Otherwise returns the fieldLabel like normal. You can also override
+     * this method to provide a custom generated label.
+     */
+    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;
+    },
+
+    /**
+     * @private Updates the content of the labelEl if it is rendered
+     */
+    updateLabel: function() {
+        var me = this,
+            label = me.labelEl;
+        if (label) {
+            label.update(me.getFieldLabel());
+        }
+    },
+
+
+    /**
+     * @private Fired when the error message of any field within the container changes, and updates the
+     * combined error message to match.
+     */
+    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();
+            }
+        }
+    },
+
+    /**
+     * Takes an Array of invalid {@link Ext.form.field.Field} objects and builds a combined list of error
+     * messages from them. Defaults to prepending each message by the field name and a colon. This
+     * can be overridden to provide custom combined error message handling, for instance changing
+     * the format of each message or sorting the array (it is sorted in order of appearance by default).
+     * @param {Array} invalidFields An Array of the sub-fields which are currently invalid.
+     * @return {Array} The combined list of error messages
+     */
+    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();
+    }
+});
+
+/**
+ * @class Ext.form.CheckboxGroup
+ * @extends Ext.form.FieldContainer
+ * <p>A {@link Ext.form.FieldContainer field container} which has a specialized layout for arranging
+ * {@link Ext.form.field.Checkbox} controls into columns, and provides convenience {@link Ext.form.field.Field} methods
+ * for {@link #getValue getting}, {@link #setValue setting}, and {@link #validate validating} the group
+ * of checkboxes as a whole.</p>
+ * <p><b>Validation:</b> Individual checkbox fields themselves have no default validation behavior, but
+ * sometimes you want to require a user to select at least one of a group of checkboxes. CheckboxGroup
+ * allows this by setting the config <tt>{@link #allowBlank}:false</tt>; when the user does not check at
+ * least one of the checkboxes, the entire group will be highlighted as invalid and the
+ * {@link #blankText error message} will be displayed according to the {@link #msgTarget} config.</p>
+ * <p><b>Layout:</b> The default layout for CheckboxGroup makes it easy to arrange the checkboxes into
+ * columns; see the {@link #columns} and {@link #vertical} config documentation for details. You may also
+ * use a completely different layout by setting the {@link #layout} to one of the other supported layout
+ * types; for instance you may wish to use a custom arrangement of hbox and vbox containers. In that case
+ * the checkbox components at any depth will still be managed by the CheckboxGroup's validation.</p>
+ * {@img Ext.form.RadioGroup/Ext.form.RadioGroup.png Ext.form.RadioGroup component}
+ * <p>Example usage:</p>
+ * <pre><code>
+    Ext.create('Ext.form.Panel', {
+        title: 'RadioGroup Example',
+        width: 300,
+        height: 125,
+        bodyPadding: 10,
+        renderTo: Ext.getBody(),        
+        items:[{            
+            xtype: 'radiogroup',
+            fieldLabel: 'Two Columns',
+            // Arrange radio buttons into two columns, distributed vertically
+            columns: 2,
+            vertical: true,
+            items: [
+                {boxLabel: 'Item 1', name: 'rb', inputValue: '1'},
+                {boxLabel: 'Item 2', name: 'rb', inputValue: '2', checked: true},
+                {boxLabel: 'Item 3', name: 'rb', inputValue: '3'},
+                {boxLabel: 'Item 4', name: 'rb', inputValue: '4'},
+                {boxLabel: 'Item 5', name: 'rb', inputValue: '5'},
+                {boxLabel: 'Item 6', name: 'rb', inputValue: '6'}
+            ]
+        }]
+    });
+ * </code></pre>
+ * @constructor
+ * Creates a new CheckboxGroup
+ * @param {Object} config Configuration options
+ * @xtype checkboxgroup
+ */
+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'],
+
+    /**
+     * @cfg {String} name
+     * @hide
+     */
+
+    /**
+     * @cfg {Array} items An Array of {@link Ext.form.field.Checkbox Checkbox}es or Checkbox config objects
+     * to arrange in the group.
+     */
+
+    /**
+     * @cfg {String/Number/Array} columns Specifies the number of columns to use when displaying grouped
+     * checkbox/radio controls using automatic layout.  This config can take several types of values:
+     * <ul><li><b>'auto'</b> : <p class="sub-desc">The controls will be rendered one per column on one row and the width
+     * of each column will be evenly distributed based on the width of the overall field container. This is the default.</p></li>
+     * <li><b>Number</b> : <p class="sub-desc">If you specific a number (e.g., 3) that number of columns will be
+     * created and the contained controls will be automatically distributed based on the value of {@link #vertical}.</p></li>
+     * <li><b>Array</b> : <p class="sub-desc">You can also specify an array of column widths, mixing integer
+     * (fixed width) and float (percentage width) values as needed (e.g., [100, .25, .75]). Any integer values will
+     * be rendered first, then any float values will be calculated as a percentage of the remaining space. Float
+     * values do not have to add up to 1 (100%) although if you want the controls to take up the entire field
+     * container you should do so.</p></li></ul>
+     */
+    columns : 'auto',
+
+    /**
+     * @cfg {Boolean} vertical True to distribute contained controls across columns, completely filling each column
+     * top to bottom before starting on the next column.  The number of controls in each column will be automatically
+     * calculated to keep columns as even as possible.  The default value is false, so that controls will be added
+     * to columns one at a time, completely filling each row left to right before starting on the next row.
+     */
+    vertical : false,
+
+    /**
+     * @cfg {Boolean} allowBlank False to validate that at least one item in the group is checked (defaults to true).
+     * If no items are selected at validation time, {@link #blankText} will be used as the error text.
+     */
+    allowBlank : true,
+
+    /**
+     * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails (defaults to "You must
+     * select at least one item in this group")
+     */
+    blankText : "You must select at least one item in this group",
+
+    // private
+    defaultType : 'checkboxfield',
+
+    // private
+    groupCls : Ext.baseCSSPrefix + 'form-check-group',
+
+    /**
+     * @cfg {String} fieldBodyCls
+     * An extra CSS class to be applied to the body content element in addition to {@link #baseBodyCls}.
+     * Defaults to 'x-form-checkboxgroup-body'.
+     */
+    fieldBodyCls: Ext.baseCSSPrefix + 'form-checkboxgroup-body',
+
+    // private
+    layout: 'checkboxgroup',
+
+    initComponent: function() {
+        var me = this;
+        me.callParent();
+        me.initField();
+    },
+
+    /**
+     * @protected
+     * Initializes the field's value based on the initial config. If the {@link #value} config is specified
+     * then we use that to set the value; otherwise we initialize the originalValue by querying the values of
+     * all sub-checkboxes after they have been initialized.
+     */
+    initValue: function() {
+        var me = this,
+            valueCfg = me.value;
+        me.originalValue = me.lastValue = valueCfg || me.getValue();
+        if (valueCfg) {
+            me.setValue(valueCfg);
+        }
+    },
+
+    /**
+     * @protected
+     * When a checkbox is added to the group, monitor it for changes
+     */
+    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);
+    },
+
+    // private override - the group value is a complex object, compare using object serialization
+    isEqual: function(value1, value2) {
+        var toQueryString = Ext.Object.toQueryString;
+        return toQueryString(value1) === toQueryString(value2);
+    },
+
+    /**
+     * Runs CheckboxGroup's validations and returns an array of any errors. The only error by default
+     * is if allowBlank is set to true and no items are checked.
+     * @return {Array} Array of all validation errors
+     */
+    getErrors: function() {
+        var errors = [];
+        if (!this.allowBlank && Ext.isEmpty(this.getChecked())) {
+            errors.push(this.blankText);
+        }
+        return errors;
+    },
+
+    /**
+     * @private Returns all checkbox components within the container
+     */
+    getBoxes: function() {
+        return this.query('[isCheckbox]');
+    },
+
+    /**
+     * @private Convenience function which calls the given function for every checkbox in the group
+     * @param {Function} fn The function to call
+     * @param {Object} scope Optional scope object
+     */
+    eachBox: function(fn, scope) {
+        Ext.Array.forEach(this.getBoxes(), fn, scope || this);
+    },
+
+    /**
+     * Returns an Array of all checkboxes in the container which are currently checked
+     * @return {Array} Array of Ext.form.field.Checkbox components
+     */
+    getChecked: function() {
+        return Ext.Array.filter(this.getBoxes(), function(cb) {
+            return cb.getValue();
+        });
+    },
+
+    // private override
+    isDirty: function(){
+        return Ext.Array.some(this.getBoxes(), function(cb) {
+            return cb.isDirty();
+        });
+    },
+
+    // private override
+    setReadOnly: function(readOnly) {
+        this.eachBox(function(cb) {
+            cb.setReadOnly(readOnly);
+        });
+        this.readOnly = readOnly;
+    },
+
+    /**
+     * Resets the checked state of all {@link Ext.form.field.Checkbox checkboxes} in the group to their
+     * originally loaded values and clears any validation messages.
+     * See {@link Ext.form.Basic}.{@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad}
+     */
+    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();
+        }
+    },
+
+    // private override
+    resetOriginalValue: function() {
+        // Defer resetting of originalValue until after all sub-checkboxes have been reset so we get
+        // the correct data from getValue()
+        Ext.defer(function() {
+            this.callParent();
+        }, 1, this);
+    },
+
+
+    /**
+     * <p>Sets the value(s) of all checkboxes in the group. The expected format is an Object of
+     * name-value pairs corresponding to the names of the checkboxes in the group. Each pair can
+     * have either a single or multiple values:</p>
+     * <ul>
+     *   <li>A single Boolean or String value will be passed to the <code>setValue</code> method of the
+     *   checkbox with that name. See the rules in {@link Ext.form.field.Checkbox#setValue} for accepted values.</li>
+     *   <li>An Array of String values will be matched against the {@link Ext.form.field.Checkbox#inputValue inputValue}
+     *   of checkboxes in the group with that name; those checkboxes whose inputValue exists in the array will be
+     *   checked and others will be unchecked.</li>
+     * </ul>
+     * <p>If a checkbox's name is not in the mapping at all, it will be unchecked.</p>
+     * <p>An example:</p>
+     * <pre><code>var myCheckboxGroup = new Ext.form.CheckboxGroup({
+    columns: 3,
+    items: [{
+        name: 'cb1',
+        boxLabel: 'Single 1'
+    }, {
+        name: 'cb2',
+        boxLabel: 'Single 2'
+    }, {
+        name: 'cb3',
+        boxLabel: 'Single 3'
+    }, {
+        name: 'cbGroup',
+        boxLabel: 'Grouped 1'
+        inputValue: 'value1'
+    }, {
+        name: 'cbGroup',
+        boxLabel: 'Grouped 2'
+        inputValue: 'value2'
+    }, {
+        name: 'cbGroup',
+        boxLabel: 'Grouped 3'
+        inputValue: 'value3'
+    }]
+});
+
+myCheckboxGroup.setValue({
+    cb1: true,
+    cb3: false,
+    cbGroup: ['value1', 'value3']
+});</code></pre>
+     * <p>The above code will cause the checkbox named 'cb1' to be checked, as well as the first and third
+     * checkboxes named 'cbGroup'. The other three checkboxes will be unchecked.</p>
+     * @param {Object} value The mapping of checkbox names to values.
+     * @return {Ext.form.CheckboxGroup} 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 {
+                        // single value, let the checkbox's own setValue handle conversion
+                        cbValue = value[name];
+                    }
+                }
+                cb.setValue(cbValue);
+            });
+        });
+        return me;
+    },
+
+
+    /**
+     * <p>Returns an object containing the values of all checked checkboxes within the group. Each key-value pair
+     * in the object corresponds to a checkbox {@link Ext.form.field.Checkbox#name name}. If there is only one checked
+     * checkbox with a particular name, the value of that pair will be the String
+     * {@link Ext.form.field.Checkbox#inputValue inputValue} of that checkbox. If there are multiple checked checkboxes
+     * with that name, the value of that pair will be an Array of the selected inputValues.</p>
+     * <p>The object format returned from this method can also be passed directly to the {@link #setValue} method.</p>
+     * <p>NOTE: In Ext 3, this method returned an array of Checkbox components; this was changed to make it more
+     * consistent with other field components and with the {@link #setValue} argument signature. If you need the old
+     * behavior in Ext 4+, use the {@link #getChecked} method instead.</p>
+     */
+    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;
+    },
+
+    /*
+     * Don't return any data for submit; the form will get the info from the individual checkboxes themselves.
+     */
+    getSubmitData: function() {
+        return null;
+    },
+
+    /*
+     * Don't return any data for the model; the form will get the info from the individual checkboxes themselves.
+     */
+    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']);
+
+});
+
+
+/**
+ * @private
+ * Private utility class for managing all {@link Ext.form.field.Checkbox} fields grouped by name.
+ */
+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;
+        });
+    }
+});
+
+/**
+ * @class Ext.form.FieldSet
+ * @extends Ext.container.Container
+ * 
+ * A container for grouping sets of fields, rendered as a HTML `fieldset` element. The {@link #title}
+ * config will be rendered as the fieldset's `legend`.
+ * 
+ * While FieldSets commonly contain simple groups of fields, they are general {@link Ext.container.Container Containers}
+ * and may therefore contain any type of components in their {@link #items}, including other nested containers.
+ * The default {@link #layout} for the FieldSet's items is `'anchor'`, but it can be configured to use any other
+ * layout type.
+ * 
+ * FieldSets may also be collapsed if configured to do so; this can be done in two ways:
+ * 
+ * 1. Set the {@link #collapsible} config to true; this will result in a collapse button being rendered next to
+ *    the {@link #title legend title}, or:
+ * 2. Set the {@link #checkboxToggle} config to true; this is similar to using {@link #collapsible} but renders
+ *    a {@link Ext.form.field.Checkbox checkbox} in place of the toggle button. The fieldset will be expanded when the
+ *    checkbox is checked and collapsed when it is unchecked. The checkbox will also be included in the
+ *    {@link Ext.form.Basic#submit form submit parameters} using the {@link #checkboxName} as its parameter name.
+ *
+ * {@img Ext.form.FieldSet/Ext.form.FieldSet.png Ext.form.FieldSet component}
+ *
+ * ## Example usage
+ * 
+ *     Ext.create('Ext.form.Panel', {
+ *         title: 'Simple Form with FieldSets',
+ *         labelWidth: 75, // label settings here cascade unless overridden
+ *         url: 'save-form.php',
+ *         frame: true,
+ *         bodyStyle: 'padding:5px 5px 0',
+ *         width: 550,
+ *         renderTo: Ext.getBody(),
+ *         layout: 'column', // arrange fieldsets side by side
+ *         defaults: {
+ *             bodyPadding: 4
+ *         },
+ *         items: [{
+ *             // Fieldset in Column 1 - collapsible via toggle button
+ *             xtype:'fieldset',
+ *             columnWidth: 0.5,
+ *             title: 'Fieldset 1',
+ *             collapsible: true,
+ *             defaultType: 'textfield',
+ *             defaults: {anchor: '100%'},
+ *             layout: 'anchor',
+ *             items :[{
+ *                 fieldLabel: 'Field 1',
+ *                 name: 'field1'
+ *             }, {
+ *                 fieldLabel: 'Field 2',
+ *                 name: 'field2'
+ *             }]
+ *         }, {
+ *             // Fieldset in Column 2 - collapsible via checkbox, collapsed by default, contains a panel
+ *             xtype:'fieldset',
+ *             title: 'Show Panel', // title or checkboxToggle creates fieldset header
+ *             columnWidth: 0.5,
+ *             checkboxToggle: true,
+ *             collapsed: true, // fieldset initially collapsed
+ *             layout:'anchor',
+ *             items :[{
+ *                 xtype: 'panel',
+ *                 anchor: '100%',
+ *                 title: 'Panel inside a fieldset',
+ *                 frame: true,
+ *                 height: 52
+ *             }]
+ *         }]
+ *     });
+ * 
+ * @constructor
+ * Create a new FieldSet
+ * @param {Object} config Configuration options
+ * @xtype fieldset
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+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'],
+
+    /**
+     * @cfg {String} title
+     * A title to be displayed in the fieldset's legend. May contain HTML markup.
+     */
+
+    /**
+     * @cfg {Boolean} checkboxToggle
+     * Set to <tt>true</tt> to render a checkbox into the fieldset frame just
+     * in front of the legend to expand/collapse the fieldset when the checkbox is toggled. (defaults
+     * to <tt>false</tt>). This checkbox will be included in form submits using the {@link #checkboxName}.
+     */
+
+    /**
+     * @cfg {String} checkboxName
+     * The name to assign to the fieldset's checkbox if <tt>{@link #checkboxToggle} = true</tt>
+     * (defaults to <tt>'[fieldset id]-checkbox'</tt>).
+     */
+
+    /**
+     * @cfg {Boolean} collapsible
+     * Set to <tt>true</tt> to make the fieldset collapsible and have the expand/collapse toggle button automatically
+     * rendered into the legend element, <tt>false</tt> to keep the fieldset statically sized with no collapse
+     * button (defaults to <tt>false</tt>). Another option is to configure <tt>{@link #checkboxToggle}</tt>.
+     * Use the {@link #collapsed} config to collapse the fieldset by default.
+     */
+
+    /**
+     * @cfg {Boolean} collapsed
+     * Set to <tt>true</tt> to render the fieldset as collapsed by default. If {@link #checkboxToggle} is specified,
+     * the checkbox will also be unchecked by default.
+     */
+    collapsed: false,
+
+    /**
+     * @property legend
+     * @type Ext.Component
+     * The component for the fieldset's legend. Will only be defined if the configuration requires a legend
+     * to be created, by setting the {@link #title} or {@link #checkboxToggle} options.
+     */
+
+    /**
+     * @cfg {String} baseCls The base CSS class applied to the fieldset (defaults to <tt>'x-fieldset'</tt>).
+     */
+    baseCls: Ext.baseCSSPrefix + 'fieldset',
+
+    /**
+     * @cfg {String} layout The {@link Ext.container.Container#layout} for the fieldset's immediate child items.
+     * Defaults to <tt>'anchor'</tt>.
+     */
+    layout: 'anchor',
+
+    componentLayout: 'fieldset',
+
+    // No aria role necessary as fieldset has its own recognized semantics
+    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();
+
+        // Create the Legend component if needed
+        me.initLegend();
+
+        // Add body el selector
+        Ext.applyIf(me.renderSelectors, {
+            body: '.' + baseCls + '-body'
+        });
+
+        if (me.collapsed) {
+            me.addCls(baseCls + '-collapsed');
+            me.collapse();
+        }
+    },
+
+    // private
+    onRender: function(container, position) {
+        this.callParent(arguments);
+        // Make sure the legend is created and rendered
+        this.initLegend();
+    },
+
+    /**
+     * @private
+     * Initialize and render the legend component if necessary
+     */
+    initLegend: function() {
+        var me = this,
+            legendItems,
+            legend = me.legend;
+
+        // Create the legend component if needed and it hasn't been already
+        if (!legend && (me.title || me.checkboxToggle || me.collapsible)) {
+            legendItems = [];
+
+            // Checkbox
+            if (me.checkboxToggle) {
+                legendItems.push(me.createCheckboxCmp());
+            }
+            // Toggle button
+            else if (me.collapsible) {
+                legendItems.push(me.createToggleCmp());
+            }
+
+            // Title
+            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
+            });
+        }
+
+        // Make sure legend is rendered if the fieldset is rendered
+        if (legend && !legend.rendered && me.rendered) {
+            me.legend.render(me.el, me.body); //insert before body element
+        }
+    },
+
+    /**
+     * @protected
+     * Creates the legend title component. This is only called internally, but could be overridden in subclasses
+     * to customize the title component.
+     * @return Ext.Component
+     */
+    createTitleCmp: function() {
+        var me = this;
+        me.titleCmp = Ext.create('Ext.Component', {
+            html: me.title,
+            cls: me.baseCls + '-header-text'
+        });
+        return me.titleCmp;
+        
+    },
+
+    /**
+     * @property checkboxCmp
+     * @type Ext.form.field.Checkbox
+     * Refers to the {@link Ext.form.field.Checkbox} component that is added next to the title in the legend. Only
+     * populated if the fieldset is configured with <tt>{@link #checkboxToggle}:true</tt>.
+     */
+
+    /**
+     * @protected
+     * Creates the checkbox component. This is only called internally, but could be overridden in subclasses
+     * to customize the checkbox's configuration or even return an entirely different component type.
+     * @return Ext.Component
+     */
+    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;
+    },
+
+    /**
+     * @property toggleCmp
+     * @type Ext.panel.Tool
+     * Refers to the {@link Ext.panel.Tool} component that is added as the collapse/expand button next
+     * to the title in the legend. Only populated if the fieldset is configured with <tt>{@link #collapsible}:true</tt>.
+     */
+
+    /**
+     * @protected
+     * Creates the toggle button component. This is only called internally, but could be overridden in
+     * subclasses to customize the toggle component.
+     * @return Ext.Component
+     */
+    createToggleCmp: function() {
+        var me = this;
+        me.toggleCmp = Ext.create('Ext.panel.Tool', {
+            type: 'toggle',
+            handler: me.toggle,
+            scope: me
+        });
+        return me.toggleCmp;
+    },
+    
+    /**
+     * Sets the title of this fieldset
+     * @param {String} title The new title
+     * @return {Ext.form.FieldSet} this
+     */
+    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;
+    },
+    
+    /**
+     * @private
+     * Include the legend component in the items for ComponentQuery
+     */
+    getRefItems: function(deep) {
+        var refItems = this.callParent(arguments),
+            legend = this.legend;
+
+        // Prepend legend items to ensure correct order
+        if (legend) {
+            refItems.unshift(legend);
+            if (deep) {
+                refItems.unshift.apply(refItems, legend.getRefItems(true));
+            }
+        }
+        return refItems;
+    },
+
+    /**
+     * Expands the fieldset.
+     * @return {Ext.form.FieldSet} this
+     */
+    expand : function(){
+        return this.setExpanded(true);
+    },
+    
+    /**
+     * Collapses the fieldset.
+     * @return {Ext.form.FieldSet} this
+     */
+    collapse : function() {
+        return this.setExpanded(false);
+    },
+
+    /**
+     * @private Collapse or expand the fieldset
+     */
+    setExpanded: function(expanded) {
+        var me = this,
+            checkboxCmp = me.checkboxCmp,
+            toggleCmp = me.toggleCmp;
+
+        expanded = !!expanded;
+        
+        if (checkboxCmp) {
+            checkboxCmp.setValue(expanded);
+        }
+        
+        if (expanded) {
+            me.removeCls(me.baseCls + '-collapsed');
+        } else {
+            me.addCls(me.baseCls + '-collapsed');
+        }
+        me.collapsed = !expanded;
+        me.doComponentLayout();
+        return me;
+    },
+
+    /**
+     * Toggle the fieldset's collapsed state to the opposite of what it is currently
+     */
+    toggle: function() {
+        this.setExpanded(!!this.collapsed);
+    },
+
+    /**
+     * @private Handle changes in the checkbox checked state
+     */
+    onCheckChange: function(cmp, checked) {
+        this.setExpanded(checked);
+    },
+
+    beforeDestroy : function() {
+        var legend = this.legend;
+        if (legend) {
+            legend.destroy();
+        }
+        this.callParent();
+    }
+});
+
+/**
+ * @class Ext.form.Label
+ * @extends Ext.Component
+
+Produces a standalone `<label />` element which can be inserted into a form and be associated with a field
+in that form using the {@link #forId} property.
+
+**NOTE:** in most cases it will be more appropriate to use the {@link Ext.form.Labelable#fieldLabel fieldLabel}
+and associated config properties ({@link Ext.form.Labelable#labelAlign}, {@link Ext.form.Labelable#labelWidth},
+etc.) in field components themselves, as that allows labels to be uniformly sized throughout the form.
+Ext.form.Label should only be used when your layout can not be achieved with the standard
+{@link Ext.form.Labelable field layout}.
+
+You will likely be associating the label with a field component that extends {@link Ext.form.field.Base}, so
+you should make sure the {@link #forId} is set to the same value as the {@link Ext.form.field.Base#inputId inputId}
+of that field.
+
+The label's text can be set using either the {@link #text} or {@link #html} configuration properties; the
+difference between the two is that the former will automatically escape HTML characters when rendering, while
+the latter will not.
+{@img Ext.form.Label/Ext.form.Label.png Ext.form.Label component}
+#Example usage:#
+
+This example creates a Label after its associated Text field, an arrangement that cannot currently
+be achieved using the standard Field layout's labelAlign.
+
+    Ext.create('Ext.form.Panel', {
+        title: 'Field with Label',
+        width: 400,
+        bodyPadding: 10,
+        renderTo: Ext.getBody(),
+        layout: {
+            type: 'hbox',
+            align: 'middle'
+        },
+        items: [{
+            xtype: 'textfield',
+            hideLabel: true,
+            flex: 1
+        }, {
+            xtype: 'label',
+            forId: 'myFieldId',
+            text: 'My Awesome Field',
+            margins: '0 0 0 10'
+        }]
+    });
+
+ * @constructor
+ * Creates a new Label component.
+ * @param {Ext.core.Element/String/Object} config The configuration options.
+ * 
+ * @xtype label
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+Ext.define('Ext.form.Label', {
+    extend:'Ext.Component',
+    alias: 'widget.label',
+    requires: ['Ext.util.Format'],
+
+    /**
+     * @cfg {String} text The plain text to display within the label (defaults to ''). If you need to include HTML
+     * tags within the label's innerHTML, use the {@link #html} config instead.
+     */
+    /**
+     * @cfg {String} forId The id of the input element to which this label will be bound via the standard HTML 'for'
+     * attribute. If not specified, the attribute will not be added to the label. In most cases you will be
+     * associating the label with a {@link Ext.form.field.Base} component, so you should make sure this matches
+     * the {@link Ext.form.field.Base#inputId inputId} of that field.
+     */
+    /**
+     * @cfg {String} html An HTML fragment that will be used as the label's innerHTML (defaults to '').
+     * Note that if {@link #text} is specified it will take precedence and this value will be ignored.
+     */
+    
+    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 || '') 
+        };
+    },
+
+    /**
+     * Updates the label's innerHTML with the specified string.
+     * @param {String} text The new label text
+     * @param {Boolean} encode (optional) False to skip HTML-encoding the text when rendering it
+     * to the label (defaults to true which encodes the value). This might be useful if you want to include
+     * tags in the label's innerHTML rather than rendering them as string literals per the default logic.
+     * @return {Label} this
+     */
+    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;
+    }
+});
+
+
+/**
+ * @class Ext.form.Panel
+ * @extends Ext.panel.Panel
+
+FormPanel provides a standard container for forms. It is essentially a standard {@link Ext.panel.Panel} which
+automatically creates a {@link Ext.form.Basic BasicForm} for managing any {@link Ext.form.field.Field}
+objects that are added as descendants of the panel. It also includes conveniences for configuring and
+working with the BasicForm and the collection of Fields.
+
+__Layout__
+
+By default, FormPanel is configured with `{@link Ext.layout.container.Anchor layout:'anchor'}` for
+the layout of its immediate child items. This can be changed to any of the supported container layouts.
+The layout of sub-containers is configured in {@link Ext.container.Container#layout the standard way}.
+
+__BasicForm__
+
+Although **not listed** as configuration options of FormPanel, the FormPanel class accepts all
+of the config options supported by the {@link Ext.form.Basic} class, and will pass them along to
+the internal BasicForm when it is created.
+
+**Note**: If subclassing FormPanel, any configuration options for the BasicForm must be applied to
+the `initialConfig` property of the FormPanel. Applying {@link Ext.form.Basic BasicForm}
+configuration settings to `this` will *not* affect the BasicForm's configuration.
+
+The following events fired by the BasicForm will be re-fired by the FormPanel and can therefore be
+listened for on the FormPanel itself:
+
+- {@link Ext.form.Basic#beforeaction beforeaction}
+- {@link Ext.form.Basic#actionfailed actionfailed}
+- {@link Ext.form.Basic#actioncomplete actioncomplete}
+- {@link Ext.form.Basic#validitychange validitychange}
+- {@link Ext.form.Basic#dirtychange dirtychange}
+
+__Field Defaults__
+
+The {@link #fieldDefaults} config option conveniently allows centralized configuration of default values
+for all fields added as descendants of the FormPanel. Any config option recognized by implementations
+of {@link Ext.form.Labelable} may be included in this object. See the {@link #fieldDefaults} documentation
+for details of how the defaults are applied.
+
+__Form Validation__
+
+With the default configuration, form fields are validated on-the-fly while the user edits their values.
+This can be controlled on a per-field basis (or via the {@link #fieldDefaults} config) with the field
+config properties {@link Ext.form.field.Field#validateOnChange} and {@link Ext.form.field.Base#checkChangeEvents},
+and the FormPanel's config properties {@link #pollForChanges} and {@link #pollInterval}.
+
+Any component within the FormPanel can be configured with `formBind: true`. This will cause that
+component to be automatically disabled when the form is invalid, and enabled when it is valid. This is most
+commonly used for Button components to prevent submitting the form in an invalid state, but can be used on
+any component type.
+
+For more information on form validation see the following:
+
+- {@link Ext.form.field.Field#validateOnChange}
+- {@link #pollForChanges} and {@link #pollInterval}
+- {@link Ext.form.field.VTypes}
+- {@link Ext.form.Basic#doAction BasicForm.doAction clientValidation notes}
+
+__Form Submission__
+
+By default, Ext Forms are submitted through Ajax, using {@link Ext.form.action.Action}. See the documentation for
+{@link Ext.form.Basic} for details.
+{@img Ext.form.FormPanel/Ext.form.FormPanel.png Ext.form.FormPanel FormPanel component}
+__Example usage:__
+
+    Ext.create('Ext.form.Panel', {
+        title: 'Simple Form',
+        bodyPadding: 5,
+        width: 350,
+        
+        // The form will submit an AJAX request to this URL when submitted
+        url: 'save-form.php',
+        
+        // Fields will be arranged vertically, stretched to full width
+        layout: 'anchor',
+        defaults: {
+            anchor: '100%'
+        },
+        
+        // The fields
+        defaultType: 'textfield',
+        items: [{
+            fieldLabel: 'First Name',
+            name: 'first',
+            allowBlank: false
+        },{
+            fieldLabel: 'Last Name',
+            name: 'last',
+            allowBlank: false
+        }],
+        
+        // Reset and Submit buttons
+        buttons: [{
+            text: 'Reset',
+            handler: function() {
+                this.up('form').getForm().reset();
+            }
+        }, {
+            text: 'Submit',
+            formBind: true, //only enabled once the form is valid
+            disabled: true,
+            handler: function() {
+                var form = this.up('form').getForm();
+                if (form.isValid()) {
+                    form.submit({
+                        success: function(form, action) {
+                           Ext.Msg.alert('Success', action.result.msg);
+                        },
+                        failure: function(form, action) {
+                            Ext.Msg.alert('Failed', action.result.msg);
+                        }
+                    });
+                }
+            }
+        }],
+        renderTo: Ext.getBody()
+    });
+
+ * @constructor
+ * @param {Object} config Configuration options
+ * @xtype form
+ *
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+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'],
+
+    /**
+     * @cfg {Boolean} pollForChanges
+     * If set to <tt>true</tt>, sets up an interval task (using the {@link #pollInterval}) in which the 
+     * panel's fields are repeatedly checked for changes in their values. This is in addition to the normal detection
+     * each field does on its own input element, and is not needed in most cases. It does, however, provide a
+     * means to absolutely guarantee detection of all changes including some edge cases in some browsers which
+     * do not fire native events. Defaults to <tt>false</tt>.
+     */
+
+    /**
+     * @cfg {Number} pollInterval
+     * Interval in milliseconds at which the form's fields are checked for value changes. Only used if
+     * the {@link #pollForChanges} option is set to <tt>true</tt>. Defaults to 500 milliseconds.
+     */
+
+    /**
+     * @cfg {String} layout The {@link Ext.container.Container#layout} for the form panel's immediate child items.
+     * Defaults to <tt>'anchor'</tt>.
+     */
+    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'
+        ]);
+
+        // Start polling if configured
+        if (me.pollForChanges) {
+            me.startPolling(me.pollInterval || 500);
+        }
+    },
+
+    initItems: function() {
+        // Create the BasicForm
+        var me = this;
+        
+        me.form = me.createForm();
+        me.callParent();
+        me.form.initialize();
+    },
+
+    /**
+     * @private
+     */
+    createForm: function() {
+        return Ext.create('Ext.form.Basic', this, Ext.applyIf({listeners: {}}, this.initialConfig));
+    },
+
+    /**
+     * Provides access to the {@link Ext.form.Basic Form} which this Panel contains.
+     * @return {Ext.form.Basic} The {@link Ext.form.Basic Form} which this Panel contains.
+     */
+    getForm: function() {
+        return this.form;
+    },
+    
+    /**
+     * Loads an {@link Ext.data.Model} into this form (internally just calls {@link Ext.form.Basic#loadRecord})
+     * See also {@link #trackResetOnLoad}.
+     * @param {Ext.data.Model} record The record to load
+     * @return {Ext.form.Basic} The Ext.form.Basic attached to this FormPanel
+     */
+    loadRecord: function(record) {
+        return this.getForm().loadRecord(record);
+    },
+    
+    /**
+     * Returns the currently loaded Ext.data.Model instance if one was loaded via {@link #loadRecord}.
+     * @return {Ext.data.Model} The loaded instance
+     */
+    getRecord: function() {
+        return this.getForm().getRecord();
+    },
+    
+    /**
+     * Convenience function for fetching the current value of each field in the form. This is the same as calling
+     * {@link Ext.form.Basic#getValues this.getForm().getValues()}
+     * @return {Object} The current form field values, keyed by field name
+     */
+    getValues: function() {
+        return this.getForm().getValues();
+    },
+
+    beforeDestroy: function() {
+        this.stopPolling();
+        this.form.destroy();
+        this.callParent();
+    },
+
+    /**
+     * This is a proxy for the underlying BasicForm's {@link Ext.form.Basic#load} call.
+     * @param {Object} options The options to pass to the action (see {@link Ext.form.Basic#load} and
+     * {@link Ext.form.Basic#doAction} for details)
+     */
+    load: function(options) {
+        this.form.load(options);
+    },
+
+    /**
+     * This is a proxy for the underlying BasicForm's {@link Ext.form.Basic#submit} call.
+     * @param {Object} options The options to pass to the action (see {@link Ext.form.Basic#submit} and
+     * {@link Ext.form.Basic#doAction} for details)
+     */
+    submit: function(options) {
+        this.form.submit(options);
+    },
+
+    /*
+     * Inherit docs, not using onDisable because it only gets fired
+     * when the component is rendered.
+     */
+    disable: function(silent) {
+        this.callParent(arguments);
+        this.form.getFields().each(function(field) {
+            field.disable();
+        });
+    },
+
+    /*
+     * Inherit docs, not using onEnable because it only gets fired
+     * when the component is rendered.
+     */
+    enable: function(silent) {
+        this.callParent(arguments);
+        this.form.getFields().each(function(field) {
+            field.enable();
+        });
+    },
+
+    /**
+     * Start an interval task to continuously poll all the fields in the form for changes in their
+     * values. This is normally started automatically by setting the {@link #pollForChanges} config.
+     * @param {Number} interval The interval in milliseconds at which the check should run.
+     */
+    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;
+    },
+
+    /**
+     * Stop a running interval task that was started by {@link #startPolling}.
+     */
+    stopPolling: function() {
+        var task = this.pollTask;
+        if (task) {
+            task.stopAll();
+            delete this.pollTask;
+        }
+    },
+
+    /**
+     * Forces each field within the form panel to 
+     * {@link Ext.form.field.Field#checkChange check if its value has changed}.
+     */
+    checkChange: function() {
+        this.form.getFields().each(function(field) {
+            field.checkChange();
+        });
+    }
+});
+
+/**
+ * @class Ext.form.RadioGroup
+ * @extends Ext.form.CheckboxGroup
+ * <p>A {@link Ext.form.FieldContainer field container} which has a specialized layout for arranging
+ * {@link Ext.form.field.Radio} controls into columns, and provides convenience {@link Ext.form.field.Field} methods
+ * for {@link #getValue getting}, {@link #setValue setting}, and {@link #validate validating} the group
+ * of radio buttons as a whole.</p>
+ * <p><b>Validation:</b> Individual radio buttons themselves have no default validation behavior, but
+ * sometimes you want to require a user to select one of a group of radios. RadioGroup
+ * allows this by setting the config <tt>{@link #allowBlank}:false</tt>; when the user does not check at
+ * one of the radio buttons, the entire group will be highlighted as invalid and the
+ * {@link #blankText error message} will be displayed according to the {@link #msgTarget} config.</p>
+ * <p><b>Layout:</b> The default layout for RadioGroup makes it easy to arrange the radio buttons into
+ * columns; see the {@link #columns} and {@link #vertical} config documentation for details. You may also
+ * use a completely different layout by setting the {@link #layout} to one of the other supported layout
+ * types; for instance you may wish to use a custom arrangement of hbox and vbox containers. In that case
+ * the Radio components at any depth will still be managed by the RadioGroup's validation.</p>
+ * <p>Example usage:</p>
+ * <pre><code>
+var myRadioGroup = new Ext.form.RadioGroup({
+    id: 'myGroup',
+    xtype: 'radiogroup',
+    fieldLabel: 'Single Column',
+    // Arrange radio buttons into three columns, distributed vertically
+    columns: 3,
+    vertical: true,
+    items: [
+        {boxLabel: 'Item 1', name: 'rb', inputValue: '1'},
+        {boxLabel: 'Item 2', name: 'rb', inputValue: '2', checked: true},
+        {boxLabel: 'Item 3', name: 'rb', inputValue: '3'}
+        {boxLabel: 'Item 4', name: 'rb', inputValue: '4'}
+        {boxLabel: 'Item 5', name: 'rb', inputValue: '5'}
+        {boxLabel: 'Item 6', name: 'rb', inputValue: '6'}
+    ]
+});
+ * </code></pre>
+ * @constructor
+ * Creates a new RadioGroup
+ * @param {Object} config Configuration options
+ * @xtype radiogroup
+ */
+Ext.define('Ext.form.RadioGroup', {
+    extend: 'Ext.form.CheckboxGroup',
+    alias: 'widget.radiogroup',
+
+    /**
+     * @cfg {Array} items An Array of {@link Ext.form.field.Radio Radio}s or Radio config objects
+     * to arrange in the group.
+     */
+    /**
+     * @cfg {Boolean} allowBlank True to allow every item in the group to be blank (defaults to true).
+     * If allowBlank = false and no items are selected at validation time, {@link @blankText} will
+     * be used as the error text.
+     */
+    allowBlank : true,
+    /**
+     * @cfg {String} blankText Error text to display if the {@link #allowBlank} validation fails
+     * (defaults to 'You must select one item in this group')
+     */
+    blankText : 'You must select one item in this group',
+    
+    // private
+    defaultType : 'radiofield',
+    
+    // private
+    groupCls : Ext.baseCSSPrefix + 'form-radio-group',
+
+    getBoxes: function() {
+        return this.query('[isRadio]');
+    }
+
+});
+
+/**
+ * @private
+ * Private utility class for managing all {@link Ext.form.field.Radio} fields grouped by name.
+ */
+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;
+        });
+    }
+});
+
+/**
+ * @class Ext.form.action.DirectLoad
+ * @extends Ext.form.action.Load
+ * <p>Provides {@link Ext.direct.Manager} support for loading form data.</p>
+ * <p>This example illustrates usage of Ext.direct.Direct to <b>load</b> a form through Ext.Direct.</p>
+ * <pre><code>
+var myFormPanel = new Ext.form.Panel({
+    // configs for FormPanel
+    title: 'Basic Information',
+    renderTo: document.body,
+    width: 300, height: 160,
+    padding: 10,
+
+    // configs apply to child items
+    defaults: {anchor: '100%'},
+    defaultType: 'textfield',
+    items: [{
+        fieldLabel: 'Name',
+        name: 'name'
+    },{
+        fieldLabel: 'Email',
+        name: 'email'
+    },{
+        fieldLabel: 'Company',
+        name: 'company'
+    }],
+
+    // configs for BasicForm
+    api: {
+        // The server-side method to call for load() requests
+        load: Profile.getBasicInfo,
+        // The server-side must mark the submit handler as a 'formHandler'
+        submit: Profile.updateBasicInfo
+    },
+    // specify the order for the passed params
+    paramOrder: ['uid', 'foo']
+});
+
+// load the form
+myFormPanel.getForm().load({
+    // pass 2 arguments to server side getBasicInfo method (len=2)
+    params: {
+        foo: 'bar',
+        uid: 34
+    }
+});
+ * </code></pre>
+ * The data packet sent to the server will resemble something like:
+ * <pre><code>
+[
+    {
+        "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
+        "data":[34,"bar"] // note the order of the params
+    }
+]
+ * </code></pre>
+ * The form will process a data packet returned by the server that is similar
+ * to the following format:
+ * <pre><code>
+[
+    {
+        "action":"Profile","method":"getBasicInfo","type":"rpc","tid":2,
+        "result":{
+            "success":true,
+            "data":{
+                "name":"Fred Flintstone",
+                "company":"Slate Rock and Gravel",
+                "email":"fred.flintstone@slaterg.com"
+            }
+        }
+    }
+]
+ * </code></pre>
+ */
+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());
+    },
+
+    /**
+     * @private
+     * Build the arguments to be sent to the Direct call.
+     * @return Array
+     */
+    getArgs: function() {
+        var me = this,
+            args = [],
+            form = me.form,
+            paramOrder = form.paramOrder,
+            params = me.getParams(),
+            i, len;
+
+        // If a paramOrder was specified, add the params into the argument list in that order.
+        if (paramOrder) {
+            for (i = 0, len = paramOrder.length; i < len; i++) {
+                args.push(params[paramOrder[i]]);
+            }
+        }
+        // If paramsAsHash was specified, add all the params as a single object argument.
+        else if (form.paramsAsHash) {
+            args.push(params);
+        }
+
+        // Add the callback and scope to the end of the arguments list
+        args.push(me.onSuccess, me);
+
+        return args;
+    },
+
+    // Direct actions have already been processed and therefore
+    // we can directly set the result; Direct Actions do not have
+    // a this.response property.
+    processResponse: function(result) {
+        return (this.result = result);
+    },
+
+    onSuccess: function(result, trans) {
+        if (trans.type == Ext.direct.Manager.self.exceptions.SERVER) {
+            result = {};
+        }
+        this.callParent([result]);
+    }
+});
+
+
+
+/**
+ * @class Ext.form.action.DirectSubmit
+ * @extends Ext.form.action.Submit
+ * <p>Provides Ext.direct support for submitting form data.</p>
+ * <p>This example illustrates usage of Ext.direct.Direct to <b>submit</b> a form through Ext.Direct.</p>
+ * <pre><code>
+var myFormPanel = new Ext.form.Panel({
+    // configs for FormPanel
+    title: 'Basic Information',
+    renderTo: document.body,
+    width: 300, height: 160,
+    padding: 10,
+    buttons:[{
+        text: 'Submit',
+        handler: function(){
+            myFormPanel.getForm().submit({
+                params: {
+                    foo: 'bar',
+                    uid: 34
+                }
+            });
+        }
+    }],
+
+    // configs apply to child items
+    defaults: {anchor: '100%'},
+    defaultType: 'textfield',
+    items: [{
+        fieldLabel: 'Name',
+        name: 'name'
+    },{
+        fieldLabel: 'Email',
+        name: 'email'
+    },{
+        fieldLabel: 'Company',
+        name: 'company'
+    }],
+
+    // configs for BasicForm
+    api: {
+        // The server-side method to call for load() requests
+        load: Profile.getBasicInfo,
+        // The server-side must mark the submit handler as a 'formHandler'
+        submit: Profile.updateBasicInfo
+    },
+    // specify the order for the passed params
+    paramOrder: ['uid', 'foo']
+});
+ * </code></pre>
+ * The data packet sent to the server will resemble something like:
+ * <pre><code>
+{
+    "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
+    "result":{
+        "success":true,
+        "id":{
+            "extAction":"Profile","extMethod":"updateBasicInfo",
+            "extType":"rpc","extTID":"6","extUpload":"false",
+            "name":"Aaron Conran","email":"aaron@sencha.com","company":"Sencha Inc."
+        }
+    }
+}
+ * </code></pre>
+ * The form will process a data packet returned by the server that is similar
+ * to the following:
+ * <pre><code>
+// sample success packet (batched requests)
+[
+    {
+        "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":3,
+        "result":{
+            "success":true
+        }
+    }
+]
+
+// sample failure packet (one request)
+{
+        "action":"Profile","method":"updateBasicInfo","type":"rpc","tid":"6",
+        "result":{
+            "errors":{
+                "email":"already taken"
+            },
+            "success":false,
+            "foo":"bar"
+        }
+}
+ * </code></pre>
+ * Also see the discussion in {@link Ext.form.action.DirectLoad}.
+ */
+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);
+    },
+
+    // Direct actions have already been processed and therefore
+    // we can directly set the result; Direct Actions do not have
+    // a this.response property.
+    processResponse: function(result) {
+        return (this.result = result);
+    },
+
+    onSuccess: function(response, trans) {
+        if (trans.type === Ext.direct.Manager.self.exceptions.SERVER) {
+            response = {};
+        }
+        this.callParent([response]);
+    }
+});
+
+/**
+ * @class Ext.form.action.StandardSubmit
+ * @extends Ext.form.action.Submit
+ * <p>A class which handles submission of data from {@link Ext.form.Basic Form}s using a standard
+ * <tt>&lt;form&gt;</tt> element submit. It does not handle the response from the submit.</p>
+ * <p>If validation of the form fields fails, the Form's {@link Ext.form.Basic#afterAction} method
+ * will be called. Otherwise, afterAction will not be called.</p>
+ * <p>Instances of this class are only created by a {@link Ext.form.Basic Form} when
+ * {@link Ext.form.Basic#submit submit}ting, when the form's {@link Ext.form.Basic#standardSubmit}
+ * config option is <tt>true</tt>.</p>
+ */
+Ext.define('Ext.form.action.StandardSubmit', {
+    extend:'Ext.form.action.Submit',
+    alias: 'formaction.standardsubmit',
+
+    /**
+     * @cfg {String} target
+     * Optional <tt>target</tt> attribute to be used for the form when submitting. If not specified,
+     * the target will be the current window/frame.
+     */
+
+    /**
+     * @private
+     * Perform the form submit. Creates and submits a temporary form element containing an input element for each
+     * field value returned by {@link Ext.form.Basic#getValues}, plus any configured {@link #params params} or
+     * {@link Ext.form.Basic#baseParams baseParams}.
+     */
+    doSubmit: function() {
+        var form = this.buildForm();
+        form.submit();
+        Ext.removeNode(form);
+    }
+
+});
+
+/**
+ * @class Ext.form.field.Checkbox
+ * @extends Ext.form.field.Base
+
+Single checkbox field. Can be used as a direct replacement for traditional checkbox fields. Also serves as a
+parent class for {@link Ext.form.field.Radio radio buttons}.
+
+__Labeling:__ In addition to the {@link Ext.form.Labelable standard field labeling options}, checkboxes
+may be given an optional {@link #boxLabel} which will be displayed immediately after checkbox. Also see
+{@link Ext.form.CheckboxGroup} for a convenient method of grouping related checkboxes.
+
+__Values:__
+The main value of a checkbox is a boolean, indicating whether or not the checkbox is checked.
+The following values will check the checkbox:
+* `true`
+* `'true'`
+* `'1'`
+* `'on'`
+
+Any other value will uncheck the checkbox.
+
+In addition to the main boolean value, you may also specify a separate {@link #inputValue}. This will be
+sent as the parameter value when the form is {@link Ext.form.Basic#submit submitted}. You will want to set
+this value if you have multiple checkboxes with the same {@link #name}. If not specified, the value `on`
+will be used.
+{@img Ext.form.Checkbox/Ext.form.Checkbox.png Ext.form.Checkbox Checkbox component}
+__Example usage:__
+
+    Ext.create('Ext.form.Panel', {
+        bodyPadding: 10,
+        width      : 300,
+        title      : 'Pizza Order',
+        items: [
+            {
+                xtype      : 'fieldcontainer',
+                fieldLabel : 'Toppings',
+                defaultType: 'checkboxfield',
+                items: [
+                    {
+                        boxLabel  : 'Anchovies',
+                        name      : 'topping',
+                        inputValue: '1',
+                        id        : 'checkbox1'
+                    }, {
+                        boxLabel  : 'Artichoke Hearts',
+                        name      : 'topping',
+                        inputValue: '2',
+                        checked   : true,
+                        id        : 'checkbox2'
+                    }, {
+                        boxLabel  : 'Bacon',
+                        name      : 'topping',
+                        inputValue: '3',
+                        id        : 'checkbox3'
+                    }
+                ]
+            }
+        ],
+        bbar: [
+            {
+                text: 'Select Bacon',
+                handler: function() {
+                    var checkbox = Ext.getCmp('checkbox3');
+                    checkbox.setValue(true);
+                }
+            },
+            '-',
+            {
+                text: 'Select All',
+                handler: function() {
+                    var checkbox1 = Ext.getCmp('checkbox1'),
+                        checkbox2 = Ext.getCmp('checkbox2'),
+                        checkbox3 = Ext.getCmp('checkbox3');
+    
+                    checkbox1.setValue(true);
+                    checkbox2.setValue(true);
+                    checkbox3.setValue(true);
+                }
+            },
+            {
+                text: 'Deselect All',
+                handler: function() {
+                    var checkbox1 = Ext.getCmp('checkbox1'),
+                        checkbox2 = Ext.getCmp('checkbox2'),
+                        checkbox3 = Ext.getCmp('checkbox3');
+    
+                    checkbox1.setValue(false);
+                    checkbox2.setValue(false);
+                    checkbox3.setValue(false);
+                }
+            }
+        ],
+        renderTo: Ext.getBody()
+    });
+
+ * @constructor
+ * Creates a new Checkbox
+ * @param {Object} config Configuration options
+ * @xtype checkboxfield
+ * @docauthor Robert Dougan <rob@sencha.com>
+ * @markdown
+ */
+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>',
+        // Creates not an actual checkbox, but a button which is given aria role="checkbox" and
+        // styled with a custom checkbox image. This allows greater control and consistency in
+        // styling, and using a button allows it to gain focus and handle keyboard nav properly.
+        '<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,
+
+    /**
+     * @cfg {String} focusCls The CSS class to use when the checkbox receives focus
+     * (defaults to <tt>'x-form-cb-focus'</tt>)
+     */
+    focusCls: Ext.baseCSSPrefix + 'form-cb-focus',
+
+    /**
+     * @cfg {String} fieldCls The default CSS class for the checkbox (defaults to <tt>'x-form-field'</tt>)
+     */
+
+    /**
+     * @cfg {String} fieldBodyCls
+     * An extra CSS class to be applied to the body content element in addition to {@link #fieldBodyCls}.
+     * Defaults to 'x-form-cb-wrap.
+     */
+    fieldBodyCls: Ext.baseCSSPrefix + 'form-cb-wrap',
+
+    /**
+     * @cfg {Boolean} checked <tt>true</tt> if the checkbox should render initially checked (defaults to <tt>false</tt>)
+     */
+    checked: false,
+
+    /**
+     * @cfg {String} checkedCls The CSS class added to the component's main element when it is in the checked state.
+     */
+    checkedCls: Ext.baseCSSPrefix + 'form-cb-checked',
+
+    /**
+     * @cfg {String} boxLabel An optional text label that will appear next to the checkbox. Whether it appears before
+     * or after the checkbox is determined by the {@link #boxLabelAlign} config (defaults to after).
+     */
+
+    /**
+     * @cfg {String} boxLabelCls The CSS class to be applied to the {@link #boxLabel} element
+     */
+    boxLabelCls: Ext.baseCSSPrefix + 'form-cb-label',
+
+    /**
+     * @cfg {String} boxLabelAlign The position relative to the checkbox where the {@link #boxLabel} should
+     * appear. Recognized values are <tt>'before'</tt> and <tt>'after'</tt>. Defaults to <tt>'after'</tt>.
+     */
+    boxLabelAlign: 'after',
+
+    /**
+     * @cfg {String} inputValue The value that should go into the generated input element's value attribute and
+     * should be used as the parameter value when submitting as part of a form. Defaults to <tt>"on"</tt>.
+     */
+    inputValue: 'on',
+
+    /**
+     * @cfg {String} uncheckedValue If configured, this will be submitted as the checkbox's value during form
+     * submit if the checkbox is unchecked. By default this is undefined, which results in nothing being
+     * submitted for the checkbox field when the form is submitted (the default behavior of HTML checkboxes).
+     */
+
+    /**
+     * @cfg {Function} handler A function called when the {@link #checked} value changes (can be used instead of
+     * handling the {@link #change change event}). The handler is passed the following parameters:
+     * <div class="mdetail-params"><ul>
+     * <li><b>checkbox</b> : Ext.form.field.Checkbox<div class="sub-desc">The Checkbox being toggled.</div></li>
+     * <li><b>checked</b> : Boolean<div class="sub-desc">The new checked state of the checkbox.</div></li>
+     * </ul></div>
+     */
+
+    /**
+     * @cfg {Object} scope An object to use as the scope ('this' reference) of the {@link #handler} function
+     * (defaults to this Checkbox).
+     */
+
+    // private overrides
+    checkChangeEvents: [],
+    inputType: 'checkbox',
+    ariaRole: 'checkbox',
+
+    // private
+    onRe: /^on$/i,
+
+    initComponent: function(){
+        this.callParent(arguments);
+        this.getManager().add(this);
+    },
+
+    initValue: function() {
+        var me = this,
+            checked = !!me.checked;
+
+        /**
+         * The original value of the field as configured in the {@link #checked} configuration, or
+         * as loaded by the last form load operation if the form's {@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad}
+         * setting is <code>true</code>.
+         * @type Mixed
+         * @property originalValue
+         */
+        me.originalValue = me.lastValue = checked;
+
+        // Set the initial checked state
+        me.setValue(checked);
+    },
+
+    // private
+    onRender : function(ct, position) {
+        var me = this;
+        Ext.applyIf(me.renderSelectors, {
+            /**
+             * @property boxLabelEl
+             * @type Ext.core.Element
+             * A reference to the label element created for the {@link #boxLabel}. Only present if the
+             * component has been rendered and has a boxLabel configured.
+             */
+            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);
+    },
+
+    /**
+     * @private Handle click on the checkbox button
+     */
+    onBoxClick: function(e) {
+        var me = this;
+        if (!me.disabled && !me.readOnly) {
+            this.setValue(!this.checked);
+        }
+    },
+
+    /**
+     * Returns the checked state of the checkbox.
+     * @return {Boolean} True if checked, else false
+     */
+    getRawValue: function() {
+        return this.checked;
+    },
+
+    /**
+     * Returns the checked state of the checkbox.
+     * @return {Boolean} True if checked, else false
+     */
+    getValue: function() {
+        return this.checked;
+    },
+
+    /**
+     * Returns the submit value for the checkbox which can be used when submitting forms.
+     * @return {Boolean/null} True if checked; otherwise either the {@link #uncheckedValue} or null.
+     */
+    getSubmitValue: function() {
+        return this.checked ? this.inputValue : (this.uncheckedValue || null);
+    },
+
+    getModelData: function() {
+        return this.getSubmitData();
+    },
+
+    /**
+     * Sets the checked state of the checkbox.
+     * @param {Boolean/String} value The following values will check the checkbox:
+     * <code>true, 'true', '1', or 'on'</code>, as well as a String that matches the {@link #inputValue}.
+     * Any other value will uncheck the checkbox.
+     * @return {Boolean} the new checked state of the checkbox
+     */
+    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;
+    },
+
+    /**
+     * Sets the checked state of the checkbox, and invokes change detection.
+     * @param {Boolean/String} checked The following values will check the checkbox:
+     * <code>true, 'true', '1', or 'on'</code>, as well as a String that matches the {@link #inputValue}.
+     * Any other value will uncheck the checkbox.
+     * @return {Ext.form.field.Checkbox} this
+     */
+    setValue: function(checked) {
+        var me = this;
+
+        // If an array of strings is passed, find all checkboxes in the group with the same name as this
+        // one and check all those whose inputValue is in the array, unchecking all the others. This is to
+        // facilitate setting values from Ext.form.Basic#setValues, but is not publicly documented as we
+        // don't want users depending on this behavior.
+        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;
+    },
+
+    // private
+    valueToRaw: function(value) {
+        // No extra conversion for checkboxes
+        return value;
+    },
+
+    /**
+     * @private
+     * Called when the checkbox's checked state changes. Invokes the {@link #handler} callback
+     * function if specified.
+     */
+    onChange: function(newVal, oldVal) {
+        var me = this,
+            handler = me.handler;
+        if (handler) {
+            handler.call(me.scope || me, me, newVal);
+        }
+        me.callParent(arguments);
+    },
+
+    // inherit docs
+    getManager: function() {
+        return Ext.form.CheckboxManager;
+    },
+
+    onEnable: function() {
+        var me = this,
+            inputEl = me.inputEl;
+        me.callParent();
+        if (inputEl) {
+            // Can still be disabled if the field is readOnly
+            inputEl.dom.disabled = me.readOnly;
+        }
+    },
+
+    setReadOnly: function(readOnly) {
+        var me = this,
+            inputEl = me.inputEl;
+        if (inputEl) {
+            // Set the button to disabled when readonly
+            inputEl.dom.disabled = readOnly || me.disabled;
+        }
+        me.readOnly = readOnly;
+    },
+
+    /**
+     * @protected Calculate and return the natural width of the bodyEl. It's possible that the initial
+     * rendering will cause the boxLabel to wrap and give us a bad width, so we must prevent wrapping
+     * while measuring.
+     */
+    getBodyNaturalWidth: function() {
+        var me = this,
+            bodyEl = me.bodyEl,
+            ws = 'white-space',
+            width;
+        bodyEl.setStyle(ws, 'nowrap');
+        width = bodyEl.getWidth();
+        bodyEl.setStyle(ws, '');
+        return width;
+    }
+
+});
+
+/**
+ * @private
+ * @class Ext.layout.component.field.Trigger
+ * @extends Ext.layout.component.field.Field
+ * Layout class for {@link Ext.form.field.Trigger} fields. Adjusts the input field size to accommodate
+ * the trigger button(s).
+ * @private
+ */
+
+Ext.define('Ext.layout.component.field.Trigger', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.triggerfield'],
+
+    extend: 'Ext.layout.component.field.Field',
+
+    /* End Definitions */
+
+    type: 'triggerfield',
+
+    sizeBodyContents: function(width, height) {
+        var me = this,
+            owner = me.owner,
+            inputEl = owner.inputEl,
+            triggerWrap = owner.triggerWrap,
+            triggerWidth = owner.getTriggerWidth();
+
+        // If we or our ancestor is hidden, we can get a triggerWidth calculation
+        // of 0.  We don't want to resize in this case.
+        if (owner.hideTrigger || owner.readOnly || triggerWidth > 0) {
+            // Decrease the field's width by the width of the triggers. Both the field and the triggerWrap
+            // are floated left in CSS so they'll stack up side by side.
+            me.setElementSize(inputEl, Ext.isNumber(width) ? width - triggerWidth : width);
+    
+            // Explicitly set the triggerWrap's width, to prevent wrapping
+            triggerWrap.setWidth(triggerWidth);
+        }
+    }
+});
+/**
+ * @class Ext.view.View
+ * @extends Ext.view.AbstractView
+ *
+ * A mechanism for displaying data using custom layout templates and formatting. DataView uses an {@link Ext.XTemplate}
+ * as its internal templating mechanism, and is bound to an {@link Ext.data.Store}
+ * so that as the data in the store changes the view is automatically updated to reflect the changes.  The view also
+ * provides built-in behavior for many common events that can occur for its contained items including click, doubleclick,
+ * mouseover, mouseout, etc. as well as a built-in selection model. <b>In order to use these features, an {@link #itemSelector}
+ * config must be provided for the DataView to determine what nodes it will be working with.</b>
+ *
+ * <p>The example below binds a DataView to a {@link Ext.data.Store} and renders it into an {@link Ext.panel.Panel}.</p>
+ * {@img Ext.DataView/Ext.DataView.png Ext.DataView component}
+ * <pre><code>
+    Ext.regModel('Image', {
+        Fields: [
+            {name:'src', type:'string'},
+            {name:'caption', type:'string'}
+        ]
+    });
+    
+    Ext.create('Ext.data.Store', {
+        id:'imagesStore',
+        model: 'Image',
+        data: [
+            {src:'http://www.sencha.com/img/20110215-feat-drawing.png', caption:'Drawing & Charts'},
+            {src:'http://www.sencha.com/img/20110215-feat-data.png', caption:'Advanced Data'},
+            {src:'http://www.sencha.com/img/20110215-feat-html5.png', caption:'Overhauled Theme'},
+            {src:'http://www.sencha.com/img/20110215-feat-perf.png', caption:'Performance Tuned'}            
+        ]
+    });
+    
+    var imageTpl = new Ext.XTemplate(
+        '<tpl for=".">',
+            '<div style="thumb-wrap">',
+              '<img src="{src}" />',
+              '<br/><span>{caption}</span>',
+            '</div>',
+        '</tpl>'
+    );
+    
+    Ext.create('Ext.DataView', {
+        store: Ext.data.StoreManager.lookup('imagesStore'),
+        tpl: imageTpl,
+        itemSelector: 'div.thumb-wrap',
+        emptyText: 'No images available',
+        renderTo: Ext.getBody()
+    });
+ * </code></pre>
+ * @xtype dataview
+ */
+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(
+            /**
+             * @event beforeitemmousedown
+             * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemmousedown',
+            /**
+             * @event beforeitemmouseup
+             * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemmouseup',
+            /**
+             * @event beforeitemmouseenter
+             * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemmouseenter',
+            /**
+             * @event beforeitemmouseleave
+             * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemmouseleave',
+            /**
+             * @event beforeitemclick
+             * Fires before the click event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemclick',
+            /**
+             * @event beforeitemdblclick
+             * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemdblclick',
+            /**
+             * @event beforeitemcontextmenu
+             * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforeitemcontextmenu',
+            /**
+             * @event beforeitemkeydown
+             * Fires before the keydown event on an item is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
+             */
+            'beforeitemkeydown',
+            /**
+             * @event itemmousedown
+             * Fires when there is a mouse down on an item
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemmousedown',
+            /**
+             * @event itemmouseup
+             * Fires when there is a mouse up on an item
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemmouseup',
+            /**
+             * @event itemmouseenter
+             * Fires when the mouse enters an item.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemmouseenter',
+            /**
+             * @event itemmouseleave
+             * Fires when the mouse leaves an item.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemmouseleave',
+            /**
+             * @event itemclick
+             * Fires when an item is clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemclick',
+            /**
+             * @event itemdblclick
+             * Fires when an item is double clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemdblclick',
+            /**
+             * @event itemcontextmenu
+             * Fires when an item is right clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'itemcontextmenu',
+            /**
+             * @event itemkeydown
+             * Fires when a key is pressed while an item is currently selected.
+             * @param {Ext.view.View} this
+             * @param {Ext.data.Model} record The record that belongs to the item
+             * @param {HTMLElement} item The item's element
+             * @param {Number} index The item's index
+             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
+             */
+            'itemkeydown',
+            /**
+             * @event beforecontainermousedown
+             * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainermousedown',
+            /**
+             * @event beforecontainermouseup
+             * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainermouseup',
+            /**
+             * @event beforecontainermouseover
+             * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainermouseover',
+            /**
+             * @event beforecontainermouseout
+             * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainermouseout',
+            /**
+             * @event beforecontainerclick
+             * Fires before the click event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainerclick',
+            /**
+             * @event beforecontainerdblclick
+             * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainerdblclick',
+            /**
+             * @event beforecontainercontextmenu
+             * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'beforecontainercontextmenu',
+            /**
+             * @event beforecontainerkeydown
+             * Fires before the keydown event on the container is processed. Returns false to cancel the default action.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
+             */
+            'beforecontainerkeydown',
+            /**
+             * @event containermouseup
+             * Fires when there is a mouse up on the container
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containermouseup',
+            /**
+             * @event containermouseover
+             * Fires when you move the mouse over the container.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containermouseover',
+            /**
+             * @event containermouseout
+             * Fires when you move the mouse out of the container.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containermouseout',
+            /**
+             * @event containerclick
+             * Fires when the container is clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containerclick',
+            /**
+             * @event containerdblclick
+             * Fires when the container is double clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containerdblclick',
+            /**
+             * @event containercontextmenu
+             * Fires when the container is right clicked.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object
+             */
+            'containercontextmenu',
+            /**
+             * @event containerkeydown
+             * Fires when a key is pressed while the container is focused, and no item is currently selected.
+             * @param {Ext.view.View} this
+             * @param {Ext.EventObject} e The raw event object. Use {@link Ext.EventObject#getKey getKey()} to retrieve the key that was pressed.
+             */
+            'containerkeydown',
+            
+            /**
+             * @event selectionchange
+             * Fires when the selected nodes change. Relayed event from the underlying selection model.
+             * @param {Ext.view.View} this
+             * @param {Array} selections Array of the selected nodes
+             */
+            'selectionchange',
+            /**
+             * @event beforeselect
+             * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+             * @param {Ext.view.View} this
+             * @param {HTMLElement} node The node to be selected
+             * @param {Array} selections Array of currently selected nodes
+             */
+            'beforeselect'
+        );
+    },
+    // private
+    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);
+        }
+    },
+    
+    // Private template method
+    processItemEvent: Ext.emptyFn,
+    processContainerEvent: Ext.emptyFn,
+    processSpecialEvent: Ext.emptyFn,
+    
+    processUIEvent: function(e, type) {
+        type = type || e.type;
+        var me = this,
+            item = e.getTarget(me.getItemSelector(), me.getTargetEl()),
+            map = this.statics().EventMap,
+            index, record;
+        
+        if (!item) {
+            // There is this weird bug when you hover over the border of a cell it is saying
+            // the target is the table.
+            // BrowserBug: IE6 & 7. If me.mouseOverItem has been removed and is no longer
+            // in the DOM then accessing .offsetParent will throw an "Unspecified error." exception.
+            // typeof'ng and checking to make sure the offsetParent is an object will NOT throw
+            // this hard exception.
+            if (type == 'mouseover' && me.mouseOverItem && typeof me.mouseOverItem.offsetParent === "object" && Ext.fly(me.mouseOverItem).getRegion().contains(e.getPoint())) {
+                item = me.mouseOverItem;
+            }
+            
+            // Try to get the selected item to handle the keydown event, otherwise we'll just fire a container keydown event
+            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(type, record, item, index, e) === false) {
+                return false;
+            }
+            
+            type = me.isNewItemEvent(type, item, e);
+            if (type === false) {
+                return false;
+            }
+            
+            if (
+                (me['onBeforeItem' + map[type]](record, item, index, e) === false) ||
+                (me.fireEvent('beforeitem' + type, me, record, item, index, e) === false) ||
+                (me['onItem' + map[type]](record, item, index, e) === false)
+            ) { 
+                return false;
+            }
+            
+            me.fireEvent('item' + type, me, record, item, index, e);
+        } 
+        else {
+            if (
+                (me.processContainerEvent(type, 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(type, item, e) {
+        var me = this,
+            overItem = me.mouseOverItem,
+            contains,
+            isItem;
+            
+        switch (type) {
+            case 'mouseover':
+                if (item === overItem) {
+                    return false;
+                }
+                me.mouseOverItem = item;
+                return 'mouseenter';
+            break;
+            
+            case 'mouseout':
+               /*
+                * Need an extra check here to see if it's the parent element. See the
+                * comment re: the browser bug at the start of processUIEvent
+                */
+                if (overItem && typeof overItem.offsetParent === "object") {
+                    contains = Ext.fly(me.mouseOverItem).getRegion().contains(e.getPoint());
+                    isItem = Ext.fly(e.getTarget()).hasCls(me.itemSelector);
+                    if (contains && isItem) {
+                        return false;
+                    }
+                }
+                me.mouseOverItem = null;
+                return 'mouseleave';
+            break;
+        }
+        return type;
+    },
+    
+    // private
+    onItemMouseEnter: function(record, item, index, e) {
+        if (this.trackOver) {
+            this.highlightItem(item);
+        }
+    },
+
+    // private
+    onItemMouseLeave : function(record, item, index, e) {
+        if (this.trackOver) {
+            this.clearHighlight();
+        }
+    },
+
+    // @private, template methods
+    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,
+    
+    // @private, template methods
+    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,
+    
+    /**
+     * Highlight a given item in the DataView. This is called by the mouseover handler if {@link #overItemCls}
+     * and {@link #trackOver} are configured, but can also be called manually by other code, for instance to
+     * handle stepping through the list via keyboard navigation.
+     * @param {HTMLElement} item The item to highlight
+     */
+    highlightItem: function(item) {
+        var me = this;
+        me.clearHighlight();
+        me.highlightedItem = item;
+        Ext.fly(item).addCls(me.overItemCls);
+    },
+
+    /**
+     * Un-highlight the currently highlighted item, if any.
+     */
+    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);
+    }
+});
+/**
+ * Component layout for {@link Ext.view.BoundList}. Handles constraining the height to the configured maxHeight.
+ * @class Ext.layout.component.BoundList
+ * @extends Ext.layout.component.Component
+ * @private
+ */
+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) {
+            // Position offscreen so the natural width is not affected by the viewport's right edge
+            el.setXY([-9999,-9999]);
+        }
+
+        // Calculate initial layout
+        me.setTargetSize(width, height);
+
+        // Handle min/maxWidth for auto-width
+        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);
+                }
+            }
+        }
+        // Handle min/maxHeight for auto-height
+        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) {
+            // Restore position
+            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;
+
+        // Size the listEl
+        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);
+    }
+
+});
+
+/**
+ * @class Ext.toolbar.TextItem
+ * @extends Ext.toolbar.Item
+ *
+ * A simple class that renders text directly into a toolbar.
+ *
+ * ## Example usage
+ *
+ * {@img Ext.toolbar.TextItem/Ext.toolbar.TextItem.png TextItem component}
+ *
+ *     Ext.create('Ext.panel.Panel', {
+ *         title: 'Panel with TextItem',
+ *         width: 300,
+ *         height: 200,
+ *         tbar: [
+ *             {xtype: 'tbtext', text: 'Sample TextItem'}
+ *         ],
+ *         renderTo: Ext.getBody()
+ *     });
+ *
+ * @constructor
+ * Creates a new TextItem
+ * @param {Object} text A text string, or a config object containing a <tt>text</tt> property
+ * @xtype tbtext
+ */
+Ext.define('Ext.toolbar.TextItem', {
+    extend: 'Ext.toolbar.Item',
+    requires: ['Ext.XTemplate'],
+    alias: 'widget.tbtext',
+    alternateClassName: 'Ext.Toolbar.TextItem',
+    
+    /**
+     * @cfg {String} text The text to be used as innerHTML (html tags are accepted)
+     */
+    text: '',
+    
+    renderTpl: '{text}',
+    //
+    baseCls: Ext.baseCSSPrefix + 'toolbar-text',
+    
+    onRender : function() {
+        Ext.apply(this.renderData, {
+            text: this.text
+        });
+        this.callParent(arguments);
+    },
+
+    /**
+     * Updates this item's text, setting the text to be used as innerHTML.
+     * @param {String} t The text to display (html accepted).
+     */
+    setText : function(t) {
+        if (this.rendered) {
+            this.el.update(t);
+            this.ownerCt.doLayout(); // In case an empty text item (centered at zero height) receives new text.
+        } else {
+            this.text = t;
+        }
+    }
+});
+/**
+ * @class Ext.form.field.Trigger
+ * @extends Ext.form.field.Text
+ * <p>Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
+ * The trigger has no default action, so you must assign a function to implement the trigger click handler by
+ * overriding {@link #onTriggerClick}. You can create a Trigger field directly, as it renders exactly like a combobox
+ * for which you can provide a custom implementation. 
+ * {@img Ext.form.field.Trigger/Ext.form.field.Trigger.png Ext.form.field.Trigger component}
+ * For example:</p>
+ * <pre><code>
+    Ext.define('Ext.ux.CustomTrigger', {
+        extend: 'Ext.form.field.Trigger',
+        alias: 'widget.customtrigger',
+        
+        // override onTriggerClick
+        onTriggerClick: function() {
+            Ext.Msg.alert('Status', 'You clicked my trigger!');
+        }
+    });
+    
+    Ext.create('Ext.form.FormPanel', {
+        title: 'Form with TriggerField',
+        bodyPadding: 5,
+        width: 350,
+        renderTo: Ext.getBody(),
+        items:[{
+            xtype: 'customtrigger',
+            fieldLabel: 'Sample Trigger',
+            emptyText: 'click the trigger',
+        }]
+    });
+</code></pre>
+ *
+ * <p>However, in general you will most likely want to use Trigger as the base class for a reusable component.
+ * {@link Ext.form.field.Date} and {@link Ext.form.field.ComboBox} are perfect examples of this.</p>
+ *
+ * @constructor
+ * Create a new Trigger field.
+ * @param {Object} config Configuration options (valid {@Ext.form.field.Text} config options will also be applied
+ * to the base Text field)
+ * @xtype triggerfield
+ */
+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
+        }
+    ],
+
+    /**
+     * @cfg {String} triggerCls
+     * An additional CSS class used to style the trigger button.  The trigger will always get the
+     * {@link #triggerBaseCls} by default and <tt>triggerCls</tt> will be <b>appended</b> if specified.
+     * Defaults to undefined.
+     */
+
+    /**
+     * @cfg {String} triggerBaseCls
+     * The base CSS class that is always added to the trigger button. The {@link #triggerCls} will be
+     * appended in addition to this class.
+     */
+    triggerBaseCls: Ext.baseCSSPrefix + 'form-trigger',
+
+    /**
+     * @cfg {String} triggerWrapCls
+     * The CSS class that is added to the div wrapping the trigger button(s).
+     */
+    triggerWrapCls: Ext.baseCSSPrefix + 'form-trigger-wrap',
+
+    /**
+     * @cfg {Boolean} hideTrigger <tt>true</tt> to hide the trigger element and display only the base
+     * text field (defaults to <tt>false</tt>)
+     */
+    hideTrigger: false,
+
+    /**
+     * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field;
+     * the field can only have its value set via an action invoked by the trigger. (defaults to <tt>true</tt>).
+     */
+    editable: true,
+
+    /**
+     * @cfg {Boolean} readOnly <tt>true</tt> to prevent the user from changing the field, and
+     * hides the trigger.  Supercedes the editable and hideTrigger options if the value is true.
+     * (defaults to <tt>false</tt>)
+     */
+    readOnly: false,
+
+    /**
+     * @cfg {Boolean} selectOnFocus <tt>true</tt> to select any existing text in the field immediately on focus.
+     * Only applies when <tt>{@link #editable editable} = true</tt> (defaults to <tt>false</tt>).
+     */
+
+    /**
+     * @cfg {Boolean} repeatTriggerClick <tt>true</tt> to attach a {@link Ext.util.ClickRepeater click repeater}
+     * to the trigger. Defaults to <tt>false</tt>.
+     */
+    repeatTriggerClick: false,
+
+
+    /**
+     * @hide
+     * @method autoSize
+     */
+    autoSize: Ext.emptyFn,
+    // private
+    monitorTab: true,
+    // private
+    mimicing: false,
+    // private
+    triggerIndexRe: /trigger-index-(\d+)/,
+
+    componentLayout: 'triggerfield',
+
+    initComponent: function() {
+        this.wrapFocusCls = this.triggerWrapCls + '-focus';
+        this.callParent(arguments);
+    },
+
+    // private
+    onRender: function(ct, position) {
+        var me = this,
+            triggerCls,
+            triggerBaseCls = me.triggerBaseCls,
+            triggerWrapCls = me.triggerWrapCls,
+            triggerConfigs = [],
+            i;
+
+        // triggerCls is a synonym for trigger1Cls, so copy it.
+        // TODO this trigger<n>Cls API design doesn't feel clean, especially where it butts up against the
+        // single triggerCls config. Should rethink this, perhaps something more structured like a list of
+        // trigger config objects that hold cls, handler, etc.
+        if (!me.trigger1Cls) {
+            me.trigger1Cls = me.triggerCls;
+        }
+
+        // Create as many trigger elements as we have trigger<n>Cls configs, but always at least one
+        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, {
+            /**
+             * @property triggerWrap
+             * @type Ext.core.Element
+             * A reference to the div element wrapping the trigger button(s). Only set after the field has been rendered.
+             */
+            triggerWrap: '.' + triggerWrapCls
+        });
+        Ext.applyIf(me.subTplData, {
+            triggerWrapCls: triggerWrapCls,
+            triggerEl: Ext.core.DomHelper.markup(triggerConfigs),
+            clearCls: me.clearCls
+        });
+
+        me.callParent(arguments);
+
+        /**
+         * @property triggerEl
+         * @type Ext.CompositeElement
+         * A composite of all the trigger button elements. Only set after the field has been rendered.
+         */
+        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();
+        }
+    },
+
+    /**
+     * Get the total width of the trigger button area. Only useful after the field has been rendered.
+     * @return {Number} The trigger width
+     */
+    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();
+        }
+    },
+
+    /**
+     * @param {Boolean} editable True to allow the user to directly edit the field text
+     * Allow or prevent the user from directly editing the field text.  If false is passed,
+     * the user will only be able to modify the field using the trigger.  Will also add
+     * a click event to the text field which will call the trigger. This method
+     * is the runtime equivalent of setting the 'editable' config option at config time.
+     */
+    setEditable: function(editable) {
+        if (editable != this.editable) {
+            this.editable = editable;
+            this.updateEditState();
+        }
+    },
+
+    /**
+     * @param {Boolean} readOnly True to prevent the user changing the field and explicitly
+     * hide the trigger.
+     * Setting this to true will superceed settings editable and hideTrigger.
+     * Setting this to false will defer back to editable and hideTrigger. This method
+     * is the runtime equivalent of setting the 'readOnly' config option at config time.
+     */
+    setReadOnly: function(readOnly) {
+        if (readOnly != this.readOnly) {
+            this.readOnly = readOnly;
+            this.updateEditState();
+        }
+    },
+
+    // private
+    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');
+        });
+    },
+
+    // private
+    onDestroy: function() {
+        var me = this;
+        Ext.destroyMembers(me, 'triggerRepeater', 'triggerWrap', 'triggerEl');
+        delete me.doc;
+        me.callParent();
+    },
+
+    // private
+    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);
+            }
+        }
+    },
+
+    // private
+    checkTab: function(me, e) {
+        if (!this.ignoreMonitorTab && e.getKey() == e.TAB) {
+            this.triggerBlur();
+        }
+    },
+
+    // private
+    onBlur: Ext.emptyFn,
+
+    // private
+    mimicBlur: function(e) {
+        if (!this.isDestroyed && !this.bodyEl.contains(e.target) && this.validateBlur(e)) {
+            this.triggerBlur();
+        }
+    },
+
+    // private
+    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,
+
+    // private
+    // This should be overridden by any subclass that needs to check whether or not the field can be blurred.
+    validateBlur: function(e) {
+        return true;
+    },
+
+    // private
+    // process clicks upon triggers.
+    // determine which trigger index, and dispatch to the appropriate click handler
+    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);
+            }
+        }
+    },
+
+    /**
+     * The function that should handle the trigger's click event.  This method does nothing by default
+     * until overridden by an implementing function.  See Ext.form.field.ComboBox and Ext.form.field.Date for
+     * sample implementations.
+     * @method
+     * @param {Ext.EventObject} e
+     */
+    onTriggerClick: Ext.emptyFn
+
+    /**
+     * @cfg {Boolean} grow @hide
+     */
+    /**
+     * @cfg {Number} growMin @hide
+     */
+    /**
+     * @cfg {Number} growMax @hide
+     */
+});
+
+/**
+ * @class Ext.form.field.Picker
+ * @extends Ext.form.field.Trigger
+ * <p>An abstract class for fields that have a single trigger which opens a "picker" popup below
+ * the field, e.g. a combobox menu list or a date picker. It provides a base implementation for
+ * toggling the picker's visibility when the trigger is clicked, as well as keyboard navigation
+ * and some basic events. Sizing and alignment of the picker can be controlled via the {@link #matchFieldWidth}
+ * and {@link #pickerAlign}/{@link #pickerOffset} config properties respectively.</p>
+ * <p>You would not normally use this class directly, but instead use it as the parent class for
+ * a specific picker field implementation. Subclasses must implement the {@link #createPicker} method
+ * to create a picker component appropriate for the field.</p>
+ *
+ * @xtype pickerfield
+ * @constructor
+ * Create a new picker field
+ * @param {Object} config
+ */
+Ext.define('Ext.form.field.Picker', {
+    extend: 'Ext.form.field.Trigger',
+    alias: 'widget.pickerfield',
+    alternateClassName: 'Ext.form.Picker',
+    requires: ['Ext.util.KeyNav'],
+
+    /**
+     * @cfg {Boolean} matchFieldWidth
+     * Whether the picker dropdown's width should be explicitly set to match the width of the field.
+     * Defaults to <tt>true</tt>.
+     */
+    matchFieldWidth: true,
+
+    /**
+     * @cfg {String} pickerAlign
+     * The {@link Ext.core.Element#alignTo alignment position} with which to align the picker. Defaults
+     * to <tt>"tl-bl?"</tt>
+     */
+    pickerAlign: 'tl-bl?',
+
+    /**
+     * @cfg {Array} pickerOffset
+     * An offset [x,y] to use in addition to the {@link #pickerAlign} when positioning the picker.
+     * Defaults to undefined.
+     */
+
+    /**
+     * @cfg {String} openCls
+     * A class to be added to the field's {@link #bodyEl} element when the picker is opened. Defaults
+     * to 'x-pickerfield-open'.
+     */
+    openCls: Ext.baseCSSPrefix + 'pickerfield-open',
+
+    /**
+     * @property isExpanded
+     * @type Boolean
+     * True if the picker is currently expanded, false if not.
+     */
+
+    /**
+     * @cfg {Boolean} editable <tt>false</tt> to prevent the user from typing text directly into the field;
+     * the field can only have its value set via selecting a value from the picker. In this state, the picker
+     * can also be opened by clicking directly on the input field itself.
+     * (defaults to <tt>true</tt>).
+     */
+    editable: true,
+
+
+    initComponent: function() {
+        this.callParent();
+
+        // Custom events
+        this.addEvents(
+            /**
+             * @event expand
+             * Fires when the field's picker is expanded.
+             * @param {Ext.form.field.Picker} field This field instance
+             */
+            'expand',
+            /**
+             * @event collapse
+             * Fires when the field's picker is collapsed.
+             * @param {Ext.form.field.Picker} field This field instance
+             */
+            'collapse',
+            /**
+             * @event select
+             * Fires when a value is selected via the picker.
+             * @param {Ext.form.field.Picker} field This field instance
+             * @param {Mixed} value The value that was selected. The exact type of this value is dependent on
+             * the individual field and picker implementations.
+             */
+            'select'
+        );
+    },
+
+
+    initEvents: function() {
+        var me = this;
+        me.callParent();
+
+        // Add handlers for keys to expand/collapse the picker
+        me.keyNav = Ext.create('Ext.util.KeyNav', me.inputEl, {
+            down: function() {
+                if (!me.isExpanded) {
+                    // Don't call expand() directly as there may be additional processing involved before
+                    // expanding, e.g. in the case of a ComboBox query.
+                    me.onTriggerClick();
+                }
+            },
+            esc: me.collapse,
+            scope: me,
+            forceKeyDown: true
+        });
+
+        // Non-editable allows opening the picker by clicking the field
+        if (!me.editable) {
+            me.mon(me.inputEl, 'click', me.onTriggerClick, me);
+        }
+
+        // Disable native browser autocomplete
+        if (Ext.isGecko) {
+            me.inputEl.dom.setAttribute('autocomplete', 'off');
+        }
+    },
+
+
+    /**
+     * Expand this field's picker dropdown.
+     */
+    expand: function() {
+        var me = this,
+            bodyEl, picker, collapseIf;
+
+        if (me.rendered && !me.isExpanded && !me.isDestroyed) {
+            bodyEl = me.bodyEl;
+            picker = me.getPicker();
+            collapseIf = me.collapseIf;
+
+            // show the picker and set isExpanded flag
+            picker.show();
+            me.isExpanded = true;
+            me.alignPicker();
+            bodyEl.addCls(me.openCls);
+
+            // monitor clicking and mousewheel
+            me.mon(Ext.getDoc(), {
+                mousewheel: collapseIf,
+                mousedown: collapseIf,
+                scope: me
+            });
+
+            me.fireEvent('expand', me);
+            me.onExpand();
+        }
+    },
+
+    onExpand: Ext.emptyFn,
+
+    /**
+     * @protected
+     * Aligns the picker to the
+     */
+    alignPicker: function() {
+        var me = this,
+            picker, isAbove,
+            aboveSfx = '-above';
+
+        if (this.isExpanded) {
+            picker = me.getPicker();
+            if (me.matchFieldWidth) {
+                // Auto the height (it will be constrained by min and max width) unless there are no records to display.
+                picker.setSize(me.bodyEl.getWidth(), picker.store && picker.store.getCount() ? null : 0);
+            }
+            if (picker.isFloating()) {
+                picker.alignTo(me.inputEl, me.pickerAlign, me.pickerOffset);
+
+                // add the {openCls}-above class if the picker was aligned above
+                // the field due to hitting the bottom of the viewport
+                isAbove = picker.el.getY() < me.inputEl.getY();
+                me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
+                picker.el[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
+            }
+        }
+    },
+
+    /**
+     * Collapse this field's picker dropdown.
+     */
+    collapse: function() {
+        if (this.isExpanded && !this.isDestroyed) {
+            var me = this,
+                openCls = me.openCls,
+                picker = me.picker,
+                doc = Ext.getDoc(),
+                collapseIf = me.collapseIf,
+                aboveSfx = '-above';
+
+            // hide the picker and set isExpanded flag
+            picker.hide();
+            me.isExpanded = false;
+
+            // remove the openCls
+            me.bodyEl.removeCls([openCls, openCls + aboveSfx]);
+            picker.el.removeCls(picker.baseCls + aboveSfx);
+
+            // remove event listeners
+            doc.un('mousewheel', collapseIf, me);
+            doc.un('mousedown', collapseIf, me);
+
+            me.fireEvent('collapse', me);
+            me.onCollapse();
+        }
+    },
+
+    onCollapse: Ext.emptyFn,
+
+
+    /**
+     * @private
+     * Runs on mousewheel and mousedown of doc to check to see if we should collapse the picker
+     */
+    collapseIf: function(e) {
+        var me = this;
+        if (!me.isDestroyed && !e.within(me.bodyEl, false, true) && !e.within(me.picker.el, false, true)) {
+            me.collapse();
+        }
+    },
+
+    /**
+     * Return a reference to the picker component for this field, creating it if necessary by
+     * calling {@link #createPicker}.
+     * @return {Ext.Component} The picker component
+     */
+    getPicker: function() {
+        var me = this;
+        return me.picker || (me.picker = me.createPicker());
+    },
+
+    /**
+     * Create and return the component to be used as this field's picker. Must be implemented
+     * by subclasses of Picker.
+     * @return {Ext.Component} The picker component
+     */
+    createPicker: Ext.emptyFn,
+
+    /**
+     * Handles the trigger click; by default toggles between expanding and collapsing the
+     * picker component.
+     */
+    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;
+        // ignore mousedown events within the picker element
+        if (!picker || !e.within(picker.el, false, true)) {
+            me.callParent(arguments);
+        }
+    },
+
+    onDestroy : function(){
+        var me = this;
+        Ext.destroy(me.picker, me.keyNav);
+        me.callParent();
+    }
+
+});
+
+
+/**
+ * @class Ext.form.field.Spinner
+ * @extends Ext.form.field.Trigger
+ * <p>A field with a pair of up/down spinner buttons. This class is not normally instantiated directly,
+ * instead it is subclassed and the {@link #onSpinUp} and {@link #onSpinDown} methods are implemented
+ * to handle when the buttons are clicked. A good example of this is the {@link Ext.form.field.Number} field
+ * which uses the spinner to increment and decrement the field's value by its {@link Ext.form.field.Number#step step}
+ * config value.</p>
+ * {@img Ext.form.field.Spinner/Ext.form.field.Spinner.png Ext.form.field.Spinner field}
+ * For example:
+     Ext.define('Ext.ux.CustomSpinner', {
+        extend: 'Ext.form.field.Spinner',
+        alias: 'widget.customspinner',
+        
+        // override onSpinUp (using step isn't neccessary)
+        onSpinUp: function() {
+            var me = this;
+            if (!me.readOnly) {
+                var val = me.step; // set the default value to the step value
+                if(me.getValue() !== '') {
+                    val = parseInt(me.getValue().slice(0, -5)); // gets rid of " Pack"
+                }                          
+                me.setValue((val + me.step) + ' Pack');
+            }
+        },
+        
+        // override onSpinDown
+        onSpinDown: function() {
+            var me = this;
+            if (!me.readOnly) {
+                if(me.getValue() !== '') {
+                    val = parseInt(me.getValue().slice(0, -5)); // gets rid of " Pack"
+                }            
+                me.setValue((val - me.step) + ' Pack');
+            }
+        }
+    });
+    
+    Ext.create('Ext.form.FormPanel', {
+        title: 'Form with SpinnerField',
+        bodyPadding: 5,
+        width: 350,
+        renderTo: Ext.getBody(),
+        items:[{
+            xtype: 'customspinner',
+            fieldLabel: 'How Much Beer?',
+            step: 6
+        }]
+    });
+ * <p>By default, pressing the up and down arrow keys will also trigger the onSpinUp and onSpinDown methods;
+ * to prevent this, set <tt>{@link #keyNavEnabled} = false</tt>.</p>
+ *
+ * @constructor
+ * Creates a new Spinner field
+ * @param {Object} config Configuration options
+ * @xtype spinnerfield
+ */
+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',
+
+    /**
+     * @cfg {Boolean} spinUpEnabled
+     * Specifies whether the up spinner button is enabled. Defaults to <tt>true</tt>. To change this
+     * after the component is created, use the {@link #setSpinUpEnabled} method.
+     */
+    spinUpEnabled: true,
+
+    /**
+     * @cfg {Boolean} spinDownEnabled
+     * Specifies whether the down spinner button is enabled. Defaults to <tt>true</tt>. To change this
+     * after the component is created, use the {@link #setSpinDownEnabled} method.
+     */
+    spinDownEnabled: true,
+
+    /**
+     * @cfg {Boolean} keyNavEnabled
+     * Specifies whether the up and down arrow keys should trigger spinning up and down.
+     * Defaults to <tt>true</tt>.
+     */
+    keyNavEnabled: true,
+
+    /**
+     * @cfg {Boolean} mouseWheelEnabled
+     * Specifies whether the mouse wheel should trigger spinning up and down while the field has
+     * focus. Defaults to <tt>true</tt>.
+     */
+    mouseWheelEnabled: true,
+
+    /**
+     * @cfg {Boolean} repeatTriggerClick Whether a {@link Ext.util.ClickRepeater click repeater} should be
+     * attached to the spinner buttons. Defaults to <tt>true</tt>.
+     */
+    repeatTriggerClick: true,
+
+    /**
+     * This method is called when the spinner up button is clicked, or when the up arrow key is pressed
+     * if {@link #keyNavEnabled} is <tt>true</tt>. Must be implemented by subclasses.
+     */
+    onSpinUp: Ext.emptyFn,
+
+    /**
+     * This method is called when the spinner down button is clicked, or when the down arrow key is pressed
+     * if {@link #keyNavEnabled} is <tt>true</tt>. Must be implemented by subclasses.
+     */
+    onSpinDown: Ext.emptyFn,
+
+    initComponent: function() {
+        this.callParent();
+
+        this.addEvents(
+            /**
+             * @event spin
+             * Fires when the spinner is made to spin up or down.
+             * @param {Ext.form.field.Spinner} this
+             * @param {String} direction Either 'up' if spinning up, or 'down' if spinning down.
+             */
+            'spin',
+
+            /**
+             * @event spinup
+             * Fires when the spinner is made to spin up.
+             * @param {Ext.form.field.Spinner} this
+             */
+            'spinup',
+
+            /**
+             * @event spindown
+             * Fires when the spinner is made to spin down.
+             * @param {Ext.form.field.Spinner} this
+             */
+            'spindown'
+        );
+    },
+
+    /**
+     * @private override
+     */
+    onRender: function() {
+        var me = this,
+            triggers;
+
+        me.callParent(arguments);
+        triggers = me.triggerEl;
+
+        /**
+         * @property spinUpEl
+         * @type Ext.core.Element
+         * The spinner up button element
+         */
+        me.spinUpEl = triggers.item(0);
+        /**
+         * @property spinDownEl
+         * @type Ext.core.Element
+         * The spinner down button element
+         */
+        me.spinDownEl = triggers.item(1);
+
+        // Set initial enabled/disabled states
+        me.setSpinUpEnabled(me.spinUpEnabled);
+        me.setSpinDownEnabled(me.spinDownEnabled);
+
+        // Init up/down arrow keys
+        if (me.keyNavEnabled) {
+            me.spinnerKeyNav = Ext.create('Ext.util.KeyNav', me.inputEl, {
+                scope: me,
+                up: me.spinUp,
+                down: me.spinDown
+            });
+        }
+
+        // Init mouse wheel
+        if (me.mouseWheelEnabled) {
+            me.mon(me.bodyEl, 'mousewheel', me.onMouseWheel, me);
+        }
+    },
+
+    /**
+     * @private override
+     * Since the triggers are stacked, only measure the width of one of them.
+     */
+    getTriggerWidth: function() {
+        return this.hideTrigger || this.readOnly ? 0 : this.spinUpEl.getWidth() + this.triggerWrap.getFrameWidth('lr');
+    },
+
+    /**
+     * @private Handles the spinner up button clicks.
+     */
+    onTrigger1Click: function() {
+        this.spinUp();
+    },
+
+    /**
+     * @private Handles the spinner down button clicks.
+     */
+    onTrigger2Click: function() {
+        this.spinDown();
+    },
+
+    /**
+     * Triggers the spinner to step up; fires the {@link #spin} and {@link #spinup} events and calls the
+     * {@link #onSpinUp} method. Does nothing if the field is {@link #disabled} or if {@link #spinUpEnabled}
+     * is false.
+     */
+    spinUp: function() {
+        var me = this;
+        if (me.spinUpEnabled && !me.disabled) {
+            me.fireEvent('spin', me, 'up');
+            me.fireEvent('spinup', me);
+            me.onSpinUp();
+        }
+    },
+
+    /**
+     * Triggers the spinner to step down; fires the {@link #spin} and {@link #spindown} events and calls the
+     * {@link #onSpinDown} method. Does nothing if the field is {@link #disabled} or if {@link #spinDownEnabled}
+     * is false.
+     */
+    spinDown: function() {
+        var me = this;
+        if (me.spinDownEnabled && !me.disabled) {
+            me.fireEvent('spin', me, 'down');
+            me.fireEvent('spindown', me);
+            me.onSpinDown();
+        }
+    },
+
+    /**
+     * Sets whether the spinner up button is enabled.
+     * @param {Boolean} enabled true to enable the button, false to disable it.
+     */
+    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');
+        }
+    },
+
+    /**
+     * Sets whether the spinner down button is enabled.
+     * @param {Boolean} enabled true to enable the button, false to disable it.
+     */
+    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');
+        }
+    },
+
+    /**
+     * @private
+     * Handles mousewheel events on the field
+     */
+    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();
+    }
+
+});
+/**
+ * @class Ext.form.field.Number
+ * @extends Ext.form.field.Spinner
+
+A numeric text field that provides automatic keystroke filtering to disallow non-numeric characters,
+and numeric validation to limit the value to a range of valid numbers. The range of acceptable number
+values can be controlled by setting the {@link #minValue} and {@link #maxValue} configs, and fractional
+decimals can be disallowed by setting {@link #allowDecimals} to `false`.
+
+By default, the number field is also rendered with a set of up/down spinner buttons and has
+up/down arrow key and mouse wheel event listeners attached for incrementing/decrementing the value by the
+{@link #step} value. To hide the spinner buttons set `{@link #hideTrigger hideTrigger}:true`; to disable the arrow key
+and mouse wheel handlers set `{@link #keyNavEnabled keyNavEnabled}:false` and
+`{@link #mouseWheelEnabled mouseWheelEnabled}:false`. See the example below.
+
+#Example usage:#
+{@img Ext.form.Number/Ext.form.Number1.png Ext.form.Number component}
+    Ext.create('Ext.form.Panel', {
+        title: 'On The Wall',
+        width: 300,
+        bodyPadding: 10,
+        renderTo: Ext.getBody(),
+        items: [{
+            xtype: 'numberfield',
+            anchor: '100%',
+            name: 'bottles',
+            fieldLabel: 'Bottles of Beer',
+            value: 99,
+            maxValue: 99,
+            minValue: 0
+        }],
+        buttons: [{
+            text: 'Take one down, pass it around',
+            handler: function() {
+                this.up('form').down('[name=bottles]').spinDown();
+            }
+        }]
+    });
+
+#Removing UI Enhancements#
+{@img Ext.form.Number/Ext.form.Number2.png Ext.form.Number component}
+    Ext.create('Ext.form.Panel', {
+        title: 'Personal Info',
+        width: 300,
+        bodyPadding: 10,
+        renderTo: Ext.getBody(),        
+        items: [{
+            xtype: 'numberfield',
+            anchor: '100%',
+            name: 'age',
+            fieldLabel: 'Age',
+            minValue: 0, //prevents negative numbers
+    
+            // Remove spinner buttons, and arrow key and mouse wheel listeners
+            hideTrigger: true,
+            keyNavEnabled: false,
+            mouseWheelEnabled: false
+        }]
+    });
+
+#Using Step#
+    Ext.create('Ext.form.Panel', {
+        renderTo: Ext.getBody(),
+        title: 'Step',
+        width: 300,
+        bodyPadding: 10,
+        items: [{
+            xtype: 'numberfield',
+            anchor: '100%',
+            name: 'evens',
+            fieldLabel: 'Even Numbers',
+
+            // Set step so it skips every other number
+            step: 2,
+            value: 0,
+
+            // Add change handler to force user-entered numbers to evens
+            listeners: {
+                change: function(field, value) {
+                    value = parseInt(value, 10);
+                    field.setValue(value + value % 2);
+                }
+            }
+        }]
+    });
+
+
+ * @constructor
+ * Creates a new Number field
+ * @param {Object} config Configuration options
+ *
+ * @xtype numberfield
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+Ext.define('Ext.form.field.Number', {
+    extend:'Ext.form.field.Spinner',
+    alias: 'widget.numberfield',
+    alternateClassName: ['Ext.form.NumberField', 'Ext.form.Number'],
+
+    /**
+     * @cfg {RegExp} stripCharsRe @hide
+     */
+    /**
+     * @cfg {RegExp} maskRe @hide
+     */
+
+    /**
+     * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
+     */
+    allowDecimals : true,
+
+    /**
+     * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
+     */
+    decimalSeparator : '.',
+
+    /**
+     * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
+     */
+    decimalPrecision : 2,
+
+    /**
+     * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY). Will be used by
+     * the field's validation logic, and for
+     * {@link Ext.form.field.Spinner#setSpinUpEnabled enabling/disabling the down spinner button}.
+     */
+    minValue: Number.NEGATIVE_INFINITY,
+
+    /**
+     * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE). Will be used by
+     * the field's validation logic, and for
+     * {@link Ext.form.field.Spinner#setSpinUpEnabled enabling/disabling the up spinner button}.
+     */
+    maxValue: Number.MAX_VALUE,
+
+    /**
+     * @cfg {Number} step Specifies a numeric interval by which the field's value will be incremented or
+     * decremented when the user invokes the spinner. Defaults to <tt>1</tt>.
+     */
+    step: 1,
+
+    /**
+     * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to 'The minimum
+     * value for this field is {minValue}')
+     */
+    minText : 'The minimum value for this field is {0}',
+
+    /**
+     * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to 'The maximum
+     * value for this field is {maxValue}')
+     */
+    maxText : 'The maximum value for this field is {0}',
+
+    /**
+     * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
+     * if a valid character like '.' or '-' is left in the field with no number (defaults to '{value} is not a valid number')
+     */
+    nanText : '{0} is not a valid number',
+
+    /**
+     * @cfg {String} negativeText Error text to display if the value is negative and {@link #minValue} is set to
+     * <tt>0</tt>. This is used instead of the {@link #minText} in that circumstance only.
+     */
+    negativeText : 'The value cannot be negative',
+
+    /**
+     * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789').
+     */
+    baseChars : '0123456789',
+
+    /**
+     * @cfg {Boolean} autoStripChars True to automatically strip not allowed characters from the field. Defaults to <tt>false</tt>
+     */
+    autoStripChars: false,
+
+    initComponent: function() {
+        var me = this,
+            allowed;
+
+        this.callParent();
+
+        me.setMinValue(me.minValue);
+        me.setMaxValue(me.maxValue);
+
+        // Build regexes for masking and stripping based on the configured options
+        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');
+        }
+    },
+
+    /**
+     * Runs all of Number's validations and returns an array of any errors. Note that this first
+     * runs Text's validations, so the returned array is an amalgamation of all field errors.
+     * The additional validations run test that the value is a number, and that it is within the
+     * configured min and max values.
+     * @param {Mixed} value The value to get errors for (defaults to the current field value)
+     * @return {Array} All validation errors for this field
+     */
+    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) { // if it's blank and textfield didn't flag it then it's valid
+             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);
+
+        // Update the spinner buttons
+        me.setSpinUpEnabled(valueIsNull || value < me.maxValue);
+        me.setSpinDownEnabled(valueIsNull || value > me.minValue);
+    },
+
+    /**
+     * Replaces any existing {@link #minValue} with the new value.
+     * @param {Number} value The minimum value
+     */
+    setMinValue : function(value) {
+        this.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY);
+    },
+
+    /**
+     * Replaces any existing {@link #maxValue} with the new value.
+     * @param {Number} value The maximum value
+     */
+    setMaxValue: function(value) {
+        this.maxValue = Ext.Number.from(value, Number.MAX_VALUE);
+    },
+
+    // private
+    parseValue : function(value) {
+        value = parseFloat(String(value).replace(this.decimalSeparator, '.'));
+        return isNaN(value) ? null : value;
+    },
+
+    /**
+     * @private
+     *
+     */
+    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));
+        }
+    }
+});
+
+/**
+ * @class Ext.toolbar.Paging
+ * @extends Ext.toolbar.Toolbar
+ * <p>As the amount of records increases, the time required for the browser to render
+ * them increases. Paging is used to reduce the amount of data exchanged with the client.
+ * Note: if there are more records/rows than can be viewed in the available screen area, vertical
+ * scrollbars will be added.</p>
+ * <p>Paging is typically handled on the server side (see exception below). The client sends
+ * parameters to the server side, which the server needs to interpret and then respond with the
+ * appropriate data.</p>
+ * <p><b>Ext.toolbar.Paging</b> is a specialized toolbar that is bound to a {@link Ext.data.Store}
+ * and provides automatic paging control. This Component {@link Ext.data.Store#load load}s blocks
+ * of data into the <tt>{@link #store}</tt> by passing {@link Ext.data.Store#paramNames paramNames} used for
+ * paging criteria.</p>
+ *
+ * {@img Ext.toolbar.Paging/Ext.toolbar.Paging.png Ext.toolbar.Paging component}
+ *
+ * <p>PagingToolbar is typically used as one of the Grid's toolbars:</p>
+ * <pre><code>
+ *    var itemsPerPage = 2;   // set the number of items you want per page
+ *    
+ *    var store = Ext.create('Ext.data.Store', {
+ *        id:'simpsonsStore',
+ *        autoLoad: false,
+ *        fields:['name', 'email', 'phone'],
+ *        pageSize: itemsPerPage, // items per page
+ *        proxy: {
+ *            type: 'ajax',
+ *            url: 'pagingstore.js',  // url that will load data with respect to start and limit params
+ *            reader: {
+ *                type: 'json',
+ *                root: 'items',
+ *                totalProperty: 'total'
+ *            }
+ *        }
+ *    });
+ *    
+ *    // specify segment of data you want to load using params
+ *    store.load({
+ *        params:{
+ *            start:0,    
+ *            limit: itemsPerPage
+ *        }
+ *    });
+ *    
+ *    Ext.create('Ext.grid.Panel', {
+ *        title: 'Simpsons',
+ *        store: store,
+ *        columns: [
+ *            {header: 'Name',  dataIndex: 'name'},
+ *            {header: 'Email', dataIndex: 'email', flex:1},
+ *            {header: 'Phone', dataIndex: 'phone'}
+ *        ],
+ *        width: 400,
+ *        height: 125,
+ *        dockedItems: [{
+ *            xtype: 'pagingtoolbar',
+ *            store: store,   // same store GridPanel is using
+ *            dock: 'bottom',
+ *            displayInfo: true
+ *        }],
+ *        renderTo: Ext.getBody()
+ *    });
+ * </code></pre>
+ *
+ * <p>To use paging, pass the paging requirements to the server when the store is first loaded.</p>
+ * <pre><code>
+store.load({
+    params: {
+        // specify params for the first page load if using paging
+        start: 0,          
+        limit: myPageSize,
+        // other params
+        foo:   'bar'
+    }
+});
+ * </code></pre>
+ * 
+ * <p>If using {@link Ext.data.Store#autoLoad store's autoLoad} configuration:</p>
+ * <pre><code>
+var myStore = new Ext.data.Store({
+    {@link Ext.data.Store#autoLoad autoLoad}: {start: 0, limit: 25},
+    ...
+});
+ * </code></pre>
+ * 
+ * <p>The packet sent back from the server would have this form:</p>
+ * <pre><code>
+{
+    "success": true,
+    "results": 2000, 
+    "rows": [ // <b>*Note:</b> this must be an Array 
+        { "id":  1, "name": "Bill", "occupation": "Gardener" },
+        { "id":  2, "name":  "Ben", "occupation": "Horticulturalist" },
+        ...
+        { "id": 25, "name":  "Sue", "occupation": "Botanist" }
+    ]
+}
+ * </code></pre>
+ * <p><u>Paging with Local Data</u></p>
+ * <p>Paging can also be accomplished with local data using extensions:</p>
+ * <div class="mdetail-params"><ul>
+ * <li><a href="http://sencha.com/forum/showthread.php?t=71532">Ext.ux.data.PagingStore</a></li>
+ * <li>Paging Memory Proxy (examples/ux/PagingMemoryProxy.js)</li>
+ * </ul></div>
+ * @constructor Create a new PagingToolbar
+ * @param {Object} config The config object
+ * @xtype pagingtoolbar
+ */
+Ext.define('Ext.toolbar.Paging', {
+    extend: 'Ext.toolbar.Toolbar',
+    alias: 'widget.pagingtoolbar',
+    alternateClassName: 'Ext.PagingToolbar',
+    requires: ['Ext.toolbar.TextItem', 'Ext.form.field.Number'],
+    /**
+     * @cfg {Ext.data.Store} store
+     * The {@link Ext.data.Store} the paging toolbar should use as its data source (required).
+     */
+    /**
+     * @cfg {Boolean} displayInfo
+     * <tt>true</tt> to display the displayMsg (defaults to <tt>false</tt>)
+     */
+    displayInfo: false,
+    /**
+     * @cfg {Boolean} prependButtons
+     * <tt>true</tt> to insert any configured <tt>items</tt> <i>before</i> the paging buttons.
+     * Defaults to <tt>false</tt>.
+     */
+    prependButtons: false,
+    /**
+     * @cfg {String} displayMsg
+     * The paging status message to display (defaults to <tt>'Displaying {0} - {1} of {2}'</tt>).
+     * Note that this string is formatted using the braced numbers <tt>{0}-{2}</tt> as tokens
+     * that are replaced by the values for start, end and total respectively. These tokens should
+     * be preserved when overriding this string if showing those values is desired.
+     */
+    displayMsg : 'Displaying {0} - {1} of {2}',
+    /**
+     * @cfg {String} emptyMsg
+     * The message to display when no records are found (defaults to 'No data to display')
+     */
+    emptyMsg : 'No data to display',
+    /**
+     * @cfg {String} beforePageText
+     * The text displayed before the input item (defaults to <tt>'Page'</tt>).
+     */
+    beforePageText : 'Page',
+    /**
+     * @cfg {String} afterPageText
+     * Customizable piece of the default paging text (defaults to <tt>'of {0}'</tt>). Note that
+     * this string is formatted using <tt>{0}</tt> as a token that is replaced by the number of
+     * total pages. This token should be preserved when overriding this string if showing the
+     * total page count is desired.
+     */
+    afterPageText : 'of {0}',
+    /**
+     * @cfg {String} firstText
+     * The quicktip text displayed for the first page button (defaults to <tt>'First Page'</tt>).
+     * <b>Note</b>: quick tips must be initialized for the quicktip to show.
+     */
+    firstText : 'First Page',
+    /**
+     * @cfg {String} prevText
+     * The quicktip text displayed for the previous page button (defaults to <tt>'Previous Page'</tt>).
+     * <b>Note</b>: quick tips must be initialized for the quicktip to show.
+     */
+    prevText : 'Previous Page',
+    /**
+     * @cfg {String} nextText
+     * The quicktip text displayed for the next page button (defaults to <tt>'Next Page'</tt>).
+     * <b>Note</b>: quick tips must be initialized for the quicktip to show.
+     */
+    nextText : 'Next Page',
+    /**
+     * @cfg {String} lastText
+     * The quicktip text displayed for the last page button (defaults to <tt>'Last Page'</tt>).
+     * <b>Note</b>: quick tips must be initialized for the quicktip to show.
+     */
+    lastText : 'Last Page',
+    /**
+     * @cfg {String} refreshText
+     * The quicktip text displayed for the Refresh button (defaults to <tt>'Refresh'</tt>).
+     * <b>Note</b>: quick tips must be initialized for the quicktip to show.
+     */
+    refreshText : 'Refresh',
+    /**
+     * @cfg {Number} inputItemWidth
+     * The width in pixels of the input field used to display and change the current page number (defaults to 30).
+     */
+    inputItemWidth : 30,
+    
+    /**
+     * Gets the standard paging items in the toolbar
+     * @private
+     */
+    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(
+            /**
+             * @event change
+             * Fires after the active page has been changed.
+             * @param {Ext.toolbar.Paging} this
+             * @param {Object} pageData An object that has these properties:<ul>
+             * <li><code>total</code> : Number <div class="sub-desc">The total number of records in the dataset as
+             * returned by the server</div></li>
+             * <li><code>currentPage</code> : Number <div class="sub-desc">The current page number</div></li>
+             * <li><code>pageCount</code> : Number <div class="sub-desc">The total number of pages (calculated from
+             * the total number of records in the dataset as returned by the server and the current {@link #pageSize})</div></li>
+             * <li><code>toRecord</code> : Number <div class="sub-desc">The starting record index for the current page</div></li>
+             * <li><code>fromRecord</code> : Number <div class="sub-desc">The ending record index for the current page</div></li>
+             * </ul>
+             */
+            'change',
+            /**
+             * @event beforechange
+             * Fires just before the active page is changed.
+             * Return false to prevent the active page from being changed.
+             * @param {Ext.toolbar.Paging} this
+             * @param {Number} page The page number that will be loaded on change 
+             */
+            'beforechange'
+        );
+        me.on('afterlayout', me.onLoad, me, {single: true});
+
+        me.bindStore(me.store, true);
+    },
+    // private
+    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();
+        }
+    },
+
+    // private
+    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);
+    },
+
+    // private
+    getPageData : function(){
+        var store = this.store,
+            totalCount = store.getTotalCount();
+            
+        return {
+            total : totalCount,
+            currentPage : store.currentPage,
+            pageCount: Math.ceil(totalCount / store.pageSize),
+            //pageCount :  store.getPageCount(),
+            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
+            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
+            
+        };
+    },
+
+    // private
+    onLoadError : function(){
+        if (!this.rendered) {
+            return;
+        }
+        this.child('#refresh').enable();
+    },
+
+    // private
+    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();
+    },
+
+    //private
+    onPagingBlur : function(e){
+        var curPage = this.getPageData().currentPage;
+        this.child('#inputItem').setValue(curPage);
+    },
+
+    // private
+    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);
+                }
+            }
+        }
+    },
+
+    // private
+    beforeLoad : function(){
+        if(this.rendered && this.refresh){
+            this.refresh.disable();
+        }
+    },
+
+    // private
+    doLoad : function(start){
+        if(this.fireEvent('beforechange', this, o) !== false){
+            this.store.load();
+        }
+    },
+
+    /**
+     * Move to the first page, has the same effect as clicking the 'first' button.
+     */
+    moveFirst : function(){
+        var me = this;
+        if(me.fireEvent('beforechange', me, 1) !== false){
+            me.store.loadPage(1);
+        }
+    },
+
+    /**
+     * Move to the previous page, has the same effect as clicking the 'previous' button.
+     */
+    movePrevious : function(){
+        var me = this,
+            prev = me.store.currentPage - 1;
+        
+        if(me.fireEvent('beforechange', me, prev) !== false){
+            me.store.previousPage();
+        }
+    },
+
+    /**
+     * Move to the next page, has the same effect as clicking the 'next' button.
+     */
+    moveNext : function(){
+        var me = this;        
+        if(me.fireEvent('beforechange', me, me.store.currentPage + 1) !== false){
+            me.store.nextPage();
+        }
+    },
+
+    /**
+     * Move to the last page, has the same effect as clicking the 'last' button.
+     */
+    moveLast : function(){
+        var me = this, 
+            last = this.getPageData().pageCount;
+        
+        if(me.fireEvent('beforechange', me, last) !== false){
+            me.store.loadPage(last);
+        }
+    },
+
+    /**
+     * Refresh the current page, has the same effect as clicking the 'refresh' button.
+     */
+    doRefresh : function(){
+        var me = this,
+            current = me.store.currentPage;
+        
+        if(me.fireEvent('beforechange', me, current) !== false){
+            me.store.loadPage(current);
+        }
+    },
+
+    /**
+     * Binds the paging toolbar to the specified {@link Ext.data.Store}
+     * @param {Store} store The store to bind to this toolbar
+     * @param {Boolean} initial (Optional) true to not remove listeners
+     */
+    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;
+    },
+
+    /**
+     * Unbinds the paging toolbar from the specified {@link Ext.data.Store} <b>(deprecated)</b>
+     * @param {Ext.data.Store} store The data store to unbind
+     */
+    unbind : function(store){
+        this.bindStore(null);
+    },
+
+    /**
+     * Binds the paging toolbar to the specified {@link Ext.data.Store} <b>(deprecated)</b>
+     * @param {Ext.data.Store} store The data store to bind
+     */
+    bind : function(store){
+        this.bindStore(store);
+    },
+
+    // private
+    onDestroy : function(){
+        this.bindStore(null);
+        this.callParent();
+    }
+});
+
+/**
+ * @class Ext.view.BoundList
+ * @extends Ext.view.View
+ * An internal used DataView for ComboBox, MultiSelect and ItemSelector.
+ */
+Ext.define('Ext.view.BoundList', {
+    extend: 'Ext.view.View',
+    alias: 'widget.boundlist',
+    alternateClassName: 'Ext.BoundList',
+    requires: ['Ext.layout.component.BoundList', 'Ext.toolbar.Paging'],
+
+    /**
+     * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.toolbar.Paging} is displayed at the
+     * bottom of the list and store queries will execute with page start and
+     * {@link Ext.toolbar.Paging#pageSize limit} parameters.
+     */
+    pageSize: 0,
+
+    /**
+     * @property pagingToolbar
+     * @type {Ext.toolbar.Paging}
+     * A reference to the PagingToolbar instance in this view. Only populated if {@link #pageSize} is greater
+     * than zero and the BoundList has been rendered.
+     */
+
+    // private overrides
+    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');
+        }
+
+        // should be setting aria-posinset based on entire set of data
+        // not filtered set
+        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();
+        
+        // TODO: subscribe to mode changes or allow the selModel to manipulate this attribute.
+        if (mode !== 'SINGLE') {
+            actionEl.dom.setAttribute('aria-multiselectable', true);
+        }
+    },
+
+    onDestroy: function() {
+        Ext.destroyMembers(this, 'pagingToolbar', 'listEl');
+        this.callParent();
+    }
+});
+
+/**
+ * @class Ext.view.BoundListKeyNav
+ * @extends Ext.util.KeyNav
+ * A specialized {@link Ext.util.KeyNav} implementation for navigating a {@link Ext.view.BoundList} using
+ * the keyboard. The up, down, pageup, pagedown, home, and end keys move the active highlight
+ * through the list. The enter key invokes the selection model's select action using the highlighted item.
+ */
+Ext.define('Ext.view.BoundListKeyNav', {
+    extend: 'Ext.util.KeyNav',
+    requires: 'Ext.view.BoundList',
+
+    /**
+     * @cfg {Ext.view.BoundList} boundList
+     * @required
+     * The {@link Ext.view.BoundList} instance for which key navigation will be managed. This is required.
+     */
+
+    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; //wraps around
+            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; //wraps around
+            me.highlightAt(newItemIdx);
+        },
+
+        pageup: function() {
+            //TODO
+        },
+
+        pagedown: function() {
+            //TODO
+        },
+
+        home: function() {
+            this.highlightAt(0);
+        },
+
+        end: function() {
+            var me = this;
+            me.highlightAt(me.boundList.all.getCount() - 1);
+        },
+
+        enter: function(e) {
+            this.selectHighlighted(e);
+        }
+    },
+
+    /**
+     * Highlights the item at the given index.
+     * @param {Number} index
+     */
+    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);
+        }
+    },
+
+    /**
+     * Triggers selection of the currently highlighted item according to the behavior of
+     * the configured SelectionModel.
+     */
+    selectHighlighted: function(e) {
+        var me = this,
+            boundList = me.boundList,
+            highlighted = boundList.highlightedItem,
+            selModel = boundList.getSelectionModel();
+        if (highlighted) {
+            selModel.selectWithEvent(boundList.getRecord(highlighted), e);
+        }
+    }
+
+});
+/**
+ * @class Ext.form.field.ComboBox
+ * @extends Ext.form.field.Picker
+ *
+ * A combobox control with support for autocomplete, remote loading, and many other features.
+ *
+ * A ComboBox is like a combination of a traditional HTML text `&lt;input&gt;` field and a `&lt;select&gt;`
+ * field; the user is able to type freely into the field, and/or pick values from a dropdown selection
+ * list. The user can input any value by default, even if it does not appear in the selection list;
+ * to prevent free-form values and restrict them to items in the list, set {@link #forceSelection} to `true`.
+ *
+ * The selection list's options are populated from any {@link Ext.data.Store}, including remote
+ * stores. The data items in the store are mapped to each option's displayed text and backing value via
+ * the {@link #valueField} and {@link #displayField} configurations, respectively.
+ *
+ * If your store is not remote, i.e. it depends only on local data and is loaded up front, you should be
+ * sure to set the {@link #queryMode} to `'local'`, as this will improve responsiveness for the user.
+ *
+ * {@img Ext.form.ComboBox/Ext.form.ComboBox.png Ext.form.ComboBox component}
+ *
+ * ## Example usage:
+ *
+ *     // The data store containing the list of states
+ *     var states = Ext.create('Ext.data.Store', {
+ *         fields: ['abbr', 'name'],
+ *         data : [
+ *             {"abbr":"AL", "name":"Alabama"},
+ *             {"abbr":"AK", "name":"Alaska"},
+ *             {"abbr":"AZ", "name":"Arizona"}
+ *             //...
+ *         ]
+ *     });
+ *
+ *     // Create the combo box, attached to the states data store
+ *     Ext.create('Ext.form.ComboBox', {
+ *         fieldLabel: 'Choose State',
+ *         store: states,
+ *         queryMode: 'local',
+ *         displayField: 'name',
+ *         valueField: 'abbr',
+ *         renderTo: Ext.getBody()
+ *     });
+ *
+ * ## Events
+ *
+ * To do something when something in ComboBox is selected, configure the select event:
+ *
+ *     var cb = Ext.create('Ext.form.ComboBox', {
+ *         // all of your config options
+ *         listeners:{
+ *              scope: yourScope,
+ *              'select': yourFunction
+ *         }
+ *     });
+ *
+ *     // Alternatively, you can assign events after the object is created:
+ *     var cb = new Ext.form.field.ComboBox(yourOptions);
+ *     cb.on('select', yourFunction, yourScope);
+ *
+ * ## Multiple Selection
+ *
+ * ComboBox also allows selection of multiple items from the list; to enable multi-selection set the
+ * {@link #multiSelect} config to `true`.
+ *
+ * @constructor
+ * Create a new ComboBox.
+ * @param {Object} config Configuration options
+ * @xtype combo
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+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'],
+
+    /**
+     * @cfg {String} triggerCls
+     * An additional CSS class used to style the trigger button. The trigger will always get the
+     * {@link #triggerBaseCls} by default and <tt>triggerCls</tt> will be <b>appended</b> if specified.
+     * Defaults to 'x-form-arrow-trigger' for ComboBox.
+     */
+    triggerCls: Ext.baseCSSPrefix + 'form-arrow-trigger',
+
+    /**
+     * @cfg {Ext.data.Store/Array} store The data source to which this combo is bound (defaults to <tt>undefined</tt>).
+     * Acceptable values for this property are:
+     * <div class="mdetail-params"><ul>
+     * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
+     * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.Store} internally,
+     * automatically generating {@link Ext.data.Field#name field names} to work with all data components.
+     * <div class="mdetail-params"><ul>
+     * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
+     * A 1-dimensional array will automatically be expanded (each array item will be used for both the combo
+     * {@link #valueField} and {@link #displayField})</div></li>
+     * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
+     * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
+     * {@link #valueField}, while the value at index 1 is assumed to be the combo {@link #displayField}.
+     * </div></li></ul></div></li></ul></div>
+     * <p>See also <tt>{@link #queryMode}</tt>.</p>
+     */
+
+    /**
+     * @cfg {Boolean} multiSelect
+     * If set to <tt>true</tt>, allows the combo field to hold more than one value at a time, and allows selecting
+     * multiple items from the dropdown list. The combo's text field will show all selected values separated by
+     * the {@link #delimiter}. (Defaults to <tt>false</tt>.)
+     */
+    multiSelect: false,
+
+    /**
+     * @cfg {String} delimiter
+     * The character(s) used to separate the {@link #displayField display values} of multiple selected items
+     * when <tt>{@link #multiSelect} = true</tt>. Defaults to <tt>', '</tt>.
+     */
+    delimiter: ', ',
+
+    /**
+     * @cfg {String} displayField The underlying {@link Ext.data.Field#name data field name} to bind to this
+     * ComboBox (defaults to 'text').
+     * <p>See also <tt>{@link #valueField}</tt>.</p>
+     */
+    displayField: 'text',
+
+    /**
+     * @cfg {String} valueField
+     * @required
+     * The underlying {@link Ext.data.Field#name data value name} to bind to this ComboBox (defaults to match
+     * the value of the {@link #displayField} config).
+     * <p><b>Note</b>: use of a <tt>valueField</tt> requires the user to make a selection in order for a value to be
+     * mapped. See also <tt>{@link #displayField}</tt>.</p>
+     */
+
+    /**
+     * @cfg {String} triggerAction The action to execute when the trigger is clicked.
+     * <div class="mdetail-params"><ul>
+     * <li><b><tt>'all'</tt></b> : <b>Default</b>
+     * <p class="sub-desc">{@link #doQuery run the query} specified by the <tt>{@link #allQuery}</tt> config option</p></li>
+     * <li><b><tt>'query'</tt></b> :
+     * <p class="sub-desc">{@link #doQuery run the query} using the {@link Ext.form.field.Base#getRawValue raw value}.</p></li>
+     * </ul></div>
+     * <p>See also <code>{@link #queryParam}</code>.</p>
+     */
+    triggerAction: 'all',
+
+    /**
+     * @cfg {String} allQuery The text query to send to the server to return all records for the list
+     * with no filtering (defaults to '')
+     */
+    allQuery: '',
+
+    /**
+     * @cfg {String} queryParam Name of the query ({@link Ext.data.Store#baseParam baseParam} name for the store)
+     * as it will be passed on the querystring (defaults to <tt>'query'</tt>)
+     */
+    queryParam: 'query',
+
+    /**
+     * @cfg {String} queryMode
+     * The mode for queries. Acceptable values are:
+     * <div class="mdetail-params"><ul>
+     * <li><b><tt>'remote'</tt></b> : <b>Default</b>
+     * <p class="sub-desc">Automatically loads the <tt>{@link #store}</tt> the <b>first</b> time the trigger
+     * is clicked. If you do not want the store to be automatically loaded the first time the trigger is
+     * clicked, set to <tt>'local'</tt> and manually load the store.  To force a requery of the store
+     * <b>every</b> time the trigger is clicked see <tt>{@link #lastQuery}</tt>.</p></li>
+     * <li><b><tt>'local'</tt></b> :
+     * <p class="sub-desc">ComboBox loads local data</p>
+     * <pre><code>
+var combo = new Ext.form.field.ComboBox({
+    renderTo: document.body,
+    queryMode: 'local',
+    store: new Ext.data.ArrayStore({
+        id: 0,
+        fields: [
+            'myId',  // numeric value is the key
+            'displayText'
+        ],
+        data: [[1, 'item1'], [2, 'item2']]  // data is local
+    }),
+    valueField: 'myId',
+    displayField: 'displayText',
+    triggerAction: 'all'
+});
+     * </code></pre></li>
+     * </ul></div>
+     */
+    queryMode: 'remote',
+
+    queryCaching: true,
+
+    /**
+     * @cfg {Number} pageSize If greater than <tt>0</tt>, a {@link Ext.toolbar.Paging} is displayed in the
+     * footer of the dropdown list and the {@link #doQuery filter queries} will execute with page start and
+     * {@link Ext.toolbar.Paging#pageSize limit} parameters. Only applies when <tt>{@link #queryMode} = 'remote'</tt>
+     * (defaults to <tt>0</tt>).
+     */
+    pageSize: 0,
+
+    /**
+     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and
+     * sending the query to filter the dropdown list (defaults to <tt>500</tt> if <tt>{@link #queryMode} = 'remote'</tt>
+     * or <tt>10</tt> if <tt>{@link #queryMode} = 'local'</tt>)
+     */
+
+    /**
+     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and
+     * {@link #typeAhead} activate (defaults to <tt>4</tt> if <tt>{@link #queryMode} = 'remote'</tt> or <tt>0</tt> if
+     * <tt>{@link #queryMode} = 'local'</tt>, does not apply if <tt>{@link Ext.form.field.Trigger#editable editable} = false</tt>).
+     */
+
+    /**
+     * @cfg {Boolean} autoSelect <tt>true</tt> to select the first result gathered by the data store (defaults
+     * to <tt>true</tt>).  A false value would require a manual selection from the dropdown list to set the components value
+     * unless the value of ({@link #typeAhead}) were true.
+     */
+    autoSelect: true,
+
+    /**
+     * @cfg {Boolean} typeAhead <tt>true</tt> to populate and autoselect the remainder of the text being
+     * typed after a configurable delay ({@link #typeAheadDelay}) if it matches a known value (defaults
+     * to <tt>false</tt>)
+     */
+    typeAhead: false,
+
+    /**
+     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
+     * if <tt>{@link #typeAhead} = true</tt> (defaults to <tt>250</tt>)
+     */
+    typeAheadDelay: 250,
+
+    /**
+     * @cfg {Boolean} selectOnTab
+     * Whether the Tab key should select the currently highlighted item. Defaults to <tt>true</tt>.
+     */
+    selectOnTab: true,
+
+    /**
+     * @cfg {Boolean} forceSelection <tt>true</tt> to restrict the selected value to one of the values in the list,
+     * <tt>false</tt> to allow the user to set arbitrary text into the field (defaults to <tt>false</tt>)
+     */
+    forceSelection: false,
+
+    /**
+     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
+     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined). If this
+     * default text is used, it means there is no value set and no validation will occur on this field.
+     */
+
+    /**
+     * The value of the match string used to filter the store. Delete this property to force a requery.
+     * Example use:
+     * <pre><code>
+var combo = new Ext.form.field.ComboBox({
+    ...
+    queryMode: 'remote',
+    listeners: {
+        // delete the previous query in the beforequery event or set
+        // combo.lastQuery = null (this will reload the store the next time it expands)
+        beforequery: function(qe){
+            delete qe.combo.lastQuery;
+        }
+    }
+});
+     * </code></pre>
+     * To make sure the filter in the store is not cleared the first time the ComboBox trigger is used
+     * configure the combo with <tt>lastQuery=''</tt>. Example use:
+     * <pre><code>
+var combo = new Ext.form.field.ComboBox({
+    ...
+    queryMode: 'local',
+    triggerAction: 'all',
+    lastQuery: ''
+});
+     * </code></pre>
+     * @property lastQuery
+     * @type String
+     */
+
+    /**
+     * @cfg {Object} defaultListConfig
+     * Set of options that will be used as defaults for the user-configured {@link #listConfig} object.
+     */
+    defaultListConfig: {
+        emptyText: '',
+        loadingText: 'Loading...',
+        loadingHeight: 70,
+        minWidth: 70,
+        maxHeight: 300,
+        shadow: 'sides'
+    },
+
+    /**
+     * @cfg {Mixed} transform
+     * The id, DOM node or {@link Ext.core.Element} of an existing HTML <tt>&lt;select&gt;</tt> element to
+     * convert into a ComboBox. The target select's options will be used to build the options in the ComboBox
+     * dropdown; a configured {@link #store} will take precedence over this.
+     */
+
+    /**
+     * @cfg {Object} listConfig
+     * <p>An optional set of configuration properties that will be passed to the {@link Ext.view.BoundList}'s
+     * constructor. Any configuration that is valid for BoundList can be included. Some of the more useful
+     * ones are:</p>
+     * <ul>
+     *     <li>{@link Ext.view.BoundList#cls} - defaults to empty</li>
+     *     <li>{@link Ext.view.BoundList#emptyText} - defaults to empty string</li>
+     *     <li>{@link Ext.view.BoundList#getInnerTpl} - defaults to the template defined in BoundList</li>
+     *     <li>{@link Ext.view.BoundList#itemSelector} - defaults to the value defined in BoundList</li>
+     *     <li>{@link Ext.view.BoundList#loadingText} - defaults to <tt>'Loading...'</tt></li>
+     *     <li>{@link Ext.view.BoundList#minWidth} - defaults to <tt>70</tt></li>
+     *     <li>{@link Ext.view.BoundList#maxWidth} - defaults to <tt>undefined</tt></li>
+     *     <li>{@link Ext.view.BoundList#maxHeight} - defaults to <tt>300</tt></li>
+     *     <li>{@link Ext.view.BoundList#resizable} - defaults to <tt>false</tt></li>
+     *     <li>{@link Ext.view.BoundList#shadow} - defaults to <tt>'sides'</tt></li>
+     *     <li>{@link Ext.view.BoundList#width} - defaults to <tt>undefined</tt> (automatically set to the width
+     *         of the ComboBox field if {@link #matchFieldWidth} is true)</li>
+     * </ul>
+     */
+
+    //private
+    ignoreSelection: 0,
+
+    initComponent: function() {
+        var me = this,
+            isDefined = Ext.isDefined,
+            store = me.store,
+            transform = me.transform,
+            transformSelect, isLocalMode;
+
+        //<debug>
+        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.');
+        }
+        //</debug>
+
+        this.addEvents(
+            // TODO need beforeselect?
+
+            /**
+             * @event beforequery
+             * Fires before all queries are processed. Return false to cancel the query or set the queryEvent's
+             * cancel property to true.
+             * @param {Object} queryEvent An object that has these properties:<ul>
+             * <li><code>combo</code> : Ext.form.field.ComboBox <div class="sub-desc">This combo box</div></li>
+             * <li><code>query</code> : String <div class="sub-desc">The query string</div></li>
+             * <li><code>forceAll</code> : Boolean <div class="sub-desc">True to force "all" query</div></li>
+             * <li><code>cancel</code> : Boolean <div class="sub-desc">Set to true to cancel the query</div></li>
+             * </ul>
+             */
+            'beforequery',
+
+            /*
+             * @event select
+             * Fires when at least one list item is selected.
+             * @param {Ext.form.field.ComboBox} combo This combo box
+             * @param {Array} records The selected records
+             */
+            'select'
+        );
+
+        // Build store from 'transform' HTML select element's options
+        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);
+
+        // store has already been loaded, setValue
+        if (me.store.getCount() > 0) {
+            me.setValue(me.value);
+        }
+
+        // render in place of 'transform' select
+        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();
+        }
+    },
+
+    // private
+    assertValue: function() {
+        var me = this,
+            value = me.getRawValue(),
+            rec;
+
+        if (me.multiSelect) {
+            // For multiselect, check that the current displayed value matches the current
+            // selection, if it does not then revert to the most recent selection.
+            if (value !== me.getDisplayValue()) {
+                me.setValue(me.lastSelection);
+            }
+        } else {
+            // For single-select, match the displayed value to a record and select it,
+            // if it does not match a record then revert to the most recent selection.
+            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);
+            }
+        }
+    },
+
+    // invoked when a different store is bound to this combo
+    // than the original
+    resetToDefault: function() {
+
+    },
+
+    bindStore: function(store, initial) {
+        var me = this,
+            oldStore = me.store;
+
+        // this code directly accesses this.picker, bc invoking getPicker
+        // would create it when we may be preping to destroy it
+        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();
+        }
+    },
+
+    /**
+     * @private
+     * Execute the query with the raw contents within the textfield.
+     */
+    doRawQuery: function() {
+        this.doQuery(this.getRawValue());
+    },
+
+    /**
+     * Executes a query to filter the dropdown list. Fires the {@link #beforequery} event prior to performing the
+     * query allowing the query action to be canceled if needed.
+     * @param {String} queryString The SQL query to execute
+     * @param {Boolean} forceAll <tt>true</tt> to force the query to execute even if there are currently fewer
+     * characters in the field than the minimum specified by the <tt>{@link #minChars}</tt> config option.  It
+     * also clears any filter previously saved in the current store (defaults to <tt>false</tt>)
+     * @return {Boolean} true if the query was permitted to run, false if it was cancelled by a {@link #beforequery} handler.
+     */
+    doQuery: function(queryString, forceAll) {
+        queryString = queryString || '';
+
+        // store in object and pass by reference in 'beforequery'
+        // so that client code can modify values.
+        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;
+        }
+
+        // get back out possibly modified values
+        queryString = qe.query;
+        forceAll = qe.forceAll;
+
+        // query permitted to run
+        if (forceAll || (queryString.length >= me.minChars)) {
+            // expand before starting query so LoadMask can position itself correctly
+            me.expand();
+
+            // make sure they aren't querying the same thing
+            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)
+                    });
+                }
+            }
+
+            // Clear current selection if it does not match the current value in the field
+            if (me.getRawValue() !== me.getDisplayValue()) {
+                me.ignoreSelection++;
+                me.picker.getSelectionModel().deselectAll();
+                me.ignoreSelection--;
+            }
+
+            if (isLocalMode) {
+                me.doAutoSelect();
+            }
+            if (me.typeAhead) {
+                me.doTypeAhead();
+            }
+        }
+        return true;
+    },
+
+    // private
+    getParams: function(queryString) {
+        var p = {},
+            pageSize = this.pageSize;
+        p[this.queryParam] = queryString;
+        if (pageSize) {
+            p.start = 0;
+            p.limit = pageSize;
+        }
+        return p;
+    },
+
+    /**
+     * @private
+     * If the autoSelect config is true, and the picker is open, highlights the first item.
+     */
+    doAutoSelect: function() {
+        var me = this,
+            picker = me.picker,
+            lastSelected, itemNode;
+        if (picker && me.autoSelect && me.store.getCount() > 0) {
+            // Highlight the last selected item and scroll it into view
+            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();
+        }
+    },
+
+
+    // store the last key and doQuery if relevant
+    onKeyUp: function(e, t) {
+        var me = this,
+            key = e.getKey();
+
+        if (!me.readOnly && !me.disabled && me.editable) {
+            me.lastKey = key;
+            // we put this in a task so that we can cancel it if a user is
+            // in and out before the queryDelay elapses
+
+            // perform query w/ any normal key or backspace or delete
+            if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
+                me.doQueryTask.delay(me.queryDelay);
+            }
+        }
+    },
+
+    initEvents: function() {
+        var me = this;
+        me.callParent();
+
+        // setup keyboard handling
+        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){
+        /*
+         * If we're doing single selection, the selection change events won't fire when
+         * clicking on the selected element. Detect it here.
+         */
+        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;
+        // Only react to selection if it is not called from setValue, and if our list is
+        // expanded (ignores changes to the selection model triggered elsewhere)
+        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();
+        }
+    },
+
+    /**
+     * @private
+     * Enables the key nav for the BoundList when it is expanded.
+     */
+    onExpand: function() {
+        var me = this,
+            keyNav = me.listKeyNav,
+            selectOnTab = me.selectOnTab,
+            picker = me.getPicker();
+
+        // Handle BoundList navigation from the input field. Insert a tab listener specially to enable selectOnTab.
+        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();
+                    }
+                    // Tab key event is allowed to propagate to field
+                    return true;
+                }
+            });
+        }
+
+        // While list is expanded, stop tab monitoring from Ext.form.field.Trigger so it doesn't short-circuit selectOnTab
+        if (selectOnTab) {
+            me.ignoreMonitorTab = true;
+        }
+
+        Ext.defer(keyNav.enable, 1, keyNav); //wait a bit so it doesn't react to the down arrow opening the picker
+        me.inputEl.focus();
+    },
+
+    /**
+     * @private
+     * Disables the key nav for the BoundList when it is collapsed.
+     */
+    onCollapse: function() {
+        var me = this,
+            keyNav = me.listKeyNav;
+        if (keyNav) {
+            keyNav.disable();
+            me.ignoreMonitorTab = false;
+        }
+    },
+
+    /**
+     * Selects an item by a {@link Ext.data.Model Model}, or by a key value.
+     * @param r
+     */
+    select: function(r) {
+        this.setValue(r, true);
+    },
+
+    /**
+     * Find the record by searching for a specific field/value combination
+     * Returns an Ext.data.Record or false
+     * @private
+     */
+    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);
+    },
+
+    /**
+     * Sets the specified value(s) into the field. For each value, if a record is found in the {@link #store} that
+     * matches based on the {@link #valueField}, then that record's {@link #displayField} will be displayed in the
+     * field.  If no match is found, and the {@link #valueNotFoundText} config option is defined, then that will be
+     * displayed as the default field text. Otherwise a blank value will be shown, although the value will still be set.
+     * @param {String|Array} value The value(s) to be set. Can be either a single String or {@link Ext.data.Model},
+     * or an Array of Strings or Models.
+     * @return {Ext.form.field.Field} this
+     */
+    setValue: function(value, doSelect) {
+        var me = this,
+            valueNotFoundText = me.valueNotFoundText,
+            inputEl = me.inputEl,
+            i, len, record,
+            models = [],
+            displayTplData = [],
+            processedValue = [];
+
+        if (me.store.loading) {
+            // Called while the Store is loading. Ensure it is processed by the onLoad method.
+            me.value = value;
+            return me;
+        }
+
+        // This method processes multi-values, so ensure value is an array.
+        value = Ext.Array.from(value);
+
+        // Loop through values
+        for (i = 0, len = value.length; i < len; i++) {
+            record = value[i];
+            if (!record || !record.isModel) {
+                record = me.findRecordByValue(record);
+            }
+            // record found, select it.
+            if (record) {
+                models.push(record);
+                displayTplData.push(record.data);
+                processedValue.push(record.get(me.valueField));
+            }
+            // record was not found, this could happen because
+            // store is not loaded or they set a value not in the store
+            else {
+                // if valueNotFoundText is defined, display it, otherwise display nothing for this value
+                if (Ext.isDefined(valueNotFoundText)) {
+                    displayTplData.push(valueNotFoundText);
+                }
+                processedValue.push(value[i]);
+            }
+        }
+
+        // Set the value of this field. If we are multiselecting, then that is an array.
+        me.value = me.multiSelect ? processedValue : processedValue[0];
+        if (!Ext.isDefined(me.value)) {
+            me.value = null;
+        }
+        me.displayTplData = displayTplData; //store for getDisplayValue method
+        me.lastSelection = me.valueModels = models;
+
+        if (inputEl && me.emptyText && !Ext.isEmpty(value)) {
+            inputEl.removeCls(me.emptyCls);
+        }
+
+        // Calculate raw value from the collection of Model data
+        me.setRawValue(me.getDisplayValue());
+        me.checkChange();
+
+        if (doSelect !== false) {
+            me.syncSelection();
+        }
+        me.applyEmptyText();
+
+        return me;
+    },
+
+    /**
+     * @private Generate the string value to be displayed in the text field for the currently stored value
+     */
+    getDisplayValue: function() {
+        return this.displayTpl.apply(this.displayTplData);
+    },
+
+    getValue: function() {
+        // If the user has not changed the raw field value since a value was selected from the list,
+        // then return the structured value from the selection. If the raw field value is different
+        // than what would be displayed due to selection, return that raw value.
+        var me = this,
+            picker = me.picker,
+            rawValue = me.getRawValue(), //current value of text field
+            value = me.value; //stored value from last selection or setValue() call
+
+        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;
+    },
+
+    /**
+     * Clears any value currently set in the ComboBox.
+     */
+    clearValue: function() {
+        this.setValue([]);
+    },
+
+    /**
+     * @private Synchronizes the selection in the picker to match the current value of the combobox.
+     */
+    syncSelection: function() {
+        var me = this,
+            ExtArray = Ext.Array,
+            picker = me.picker,
+            selection, selModel;
+        if (picker) {
+            // From the value, find the Models that are in the store's current data
+            selection = [];
+            ExtArray.forEach(me.valueModels || [], function(value) {
+                if (value && value.isModel && me.store.indexOf(value) >= 0) {
+                    selection.push(value);
+                }
+            });
+
+            // Update the selection to match
+            me.ignoreSelection++;
+            selModel = picker.getSelectionModel();
+            selModel.deselectAll();
+            if (selection.length) {
+                selModel.select(selection);
+            }
+            me.ignoreSelection--;
+        }
+    }
+});
+
+/**
+ * @private
+ * @class Ext.picker.Month
+ * @extends Ext.Component
+ * <p>A month picker component. This class is used by the {@link Ext.picker.Date DatePicker} class
+ * to allow browsing and selection of year/months combinations.</p>
+ * @constructor
+ * Create a new MonthPicker
+ * @param {Object} config The config object
+ * @xtype monthpicker
+ * @private
+ */
+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>'
+    ],
+
+    /**
+     * @cfg {String} okText The text to display on the ok button. Defaults to <tt>'OK'</tt>
+     */
+    okText: 'OK',
+
+    /**
+     * @cfg {String} cancelText The text to display on the cancel button. Defaults to <tt>'Cancel'</tt>
+     */
+    cancelText: 'Cancel',
+
+    /**
+     * @cfg {String} baseCls The base CSS class to apply to the picker element. Defaults to <tt>'x-monthpicker'</tt>
+     */
+    baseCls: Ext.baseCSSPrefix + 'monthpicker',
+
+    /**
+     * @cfg {Boolean} showButtons True to show ok and cancel buttons below the picker. Defaults to <tt>true</tt>.
+     */
+    showButtons: true,
+
+    /**
+     * @cfg {String} selectedCls The class to be added to selected items in the picker. Defaults to
+     * <tt>'x-monthpicker-selected'</tt>
+     */
+
+    /**
+     * @cfg {Date/Array} value The default value to set. See {#setValue setValue}
+     */
+
+    width: 175,
+
+    height: 195,
+
+
+    // private
+    totalYears: 10,
+    yearOffset: 5, // 10 years in total, 2 per row
+    monthOffset: 6, // 12 months, 2 per row
+
+    // private, inherit docs
+    initComponent: function(){
+        var me = this;
+
+        me.selectedCls = me.baseCls + '-selected';
+        me.addEvents(
+            /**
+             * @event cancelclick
+             * Fires when the cancel button is pressed.
+             * @param {Ext.picker.Month} this
+             */
+            'cancelclick',
+
+            /**
+             * @event monthclick
+             * Fires when a month is clicked.
+             * @param {Ext.picker.Month} this
+             * @param {Array} value The current value
+             */
+            'monthclick',
+
+            /**
+             * @event monthdblclick
+             * Fires when a month is clicked.
+             * @param {Ext.picker.Month} this
+             * @param {Array} value The current value
+             */
+            'monthdblclick',
+
+            /**
+             * @event okclick
+             * Fires when the ok button is pressed.
+             * @param {Ext.picker.Month} this
+             * @param {Array} value The current value
+             */
+            'okclick',
+
+            /**
+             * @event select
+             * Fires when a month/year is selected.
+             * @param {Ext.picker.Month} this
+             * @param {Array} value The current value
+             */
+            'select',
+
+            /**
+             * @event yearclick
+             * Fires when a year is clicked.
+             * @param {Ext.picker.Month} this
+             * @param {Array} value The current value
+             */
+            'yearclick',
+
+            /**
+             * @event yeardblclick
+             * Fires when a year is clicked.
+             * @param {Ext.picker.Month} this
+             * @param {Array} value The current value
+             */
+            'yeardblclick'
+        );
+
+        me.setValue(me.value);
+        me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);
+        this.callParent();
+    },
+
+    // private, inherit docs
+    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]);
+    },
+
+    // private, inherit docs
+    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);
+
+        // keep a reference to the year/month elements since we'll be re-using them
+        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();
+    },
+
+    /**
+     * Set the value for the picker.
+     * @param {Date/Array} value The value to set. It can be a Date object, where the month/year will be extracted, or
+     * it can be an array, with the month as the first index and the year as the second.
+     * @return {Ext.picker.Month} this
+     */
+    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;
+    },
+
+    /**
+     * Gets the selected value. It is returned as an array [month, year]. It may
+     * be a partial value, for example [null, 2010]. The month is returned as
+     * 0 based.
+     * @return {Array} The selected value
+     */
+    getValue: function(){
+        return this.value;
+    },
+
+    /**
+     * Checks whether the picker has a selection
+     * @return {Boolean} Returns true if both a month and year have been selected
+     */
+    hasSelection: function(){
+        var value = this.value;
+        return value[0] !== null && value[1] !== null;
+    },
+
+    /**
+     * Get an array of years to be pushed in the template. It is not in strict
+     * numerical order because we want to show them in columns.
+     * @private
+     * @return {Array} An array of years
+     */
+    getYears: function(){
+        var me = this,
+            offset = me.yearOffset,
+            start = me.activeYear, // put the "active" year on the left
+            end = start + offset,
+            i = start,
+            years = [];
+
+        for (; i < end; ++i) {
+            years.push(i, i + offset);
+        }
+
+        return years;
+    },
+
+    /**
+     * Update the years in the body based on any change
+     * @private
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * Gets the current year value, or the default.
+     * @private
+     * @param {Number} defaultValue The default value to use if the year is not defined.
+     * @param {Number} offset A number to offset the value by
+     * @return {Number} The year value
+     */
+    getYear: function(defaultValue, offset) {
+        var year = this.value[1];
+        offset = offset || 0;
+        return year === null ? defaultValue : year + offset;
+    },
+
+    /**
+     * React to clicks on the body
+     * @private
+     */
+    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);
+        }
+    },
+
+    /**
+     * Modify the year display by passing an offset.
+     * @param {Number} offset The offset to move by. If not specified, it defaults to 10.
+     */
+    adjustYear: function(offset){
+        if (typeof offset != 'number') {
+            offset = this.totalYears;
+        }
+        this.activeYear += offset;
+        this.updateBody();
+    },
+
+    /**
+     * React to the ok button being pressed
+     * @private
+     */
+    onOkClick: function(){
+        this.fireEvent('okclick', this, this.value);
+    },
+
+    /**
+     * React to the cancel button being pressed
+     * @private
+     */
+    onCancelClick: function(){
+        this.fireEvent('cancelclick', this);
+    },
+
+    /**
+     * React to a month being clicked
+     * @private
+     * @param {HTMLElement} target The element that was clicked
+     * @param {Boolean} isDouble True if the event was a doubleclick
+     */
+    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);
+    },
+
+    /**
+     * React to a year being clicked
+     * @private
+     * @param {HTMLElement} target The element that was clicked
+     * @param {Boolean} isDouble True if the event was a doubleclick
+     */
+    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);
+
+    },
+
+    /**
+     * Returns an offsetted number based on the position in the collection. Since our collections aren't
+     * numerically ordered, this function helps to normalize those differences.
+     * @private
+     * @param {Object} index
+     * @param {Object} offset
+     * @return {Number} The correctly offsetted number
+     */
+    resolveOffset: function(index, offset){
+        if (index % 2 === 0) {
+            return (index / 2);
+        } else {
+            return offset + Math.floor(index / 2);
+        }
+    },
+
+    // private, inherit docs
+    beforeDestroy: function(){
+        var me = this;
+        me.years = me.months = null;
+        Ext.destroyMembers('backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
+        this.callParent();
+    }
+});
+
+/**
+ * @class Ext.picker.Date
+ * @extends Ext.Component
+ * <p>A date picker. This class is used by the {@link Ext.form.field.Date} field to allow browsing and
+ * selection of valid dates in a popup next to the field, but may also be used with other components.</p>
+ * <p>Typically you will need to implement a handler function to be notified when the user chooses a color from the
+ * picker; you can register the handler using the {@link #select} event, or by implementing the {@link #handler}
+ * method.</p>
+ * <p>By default the user will be allowed to pick any date; this can be changed by using the {@link #minDate},
+ * {@link #maxDate}, {@link #disabledDays}, {@link #disabledDatesRE}, and/or {@link #disabledDates} configs.</p>
+ * <p>All the string values documented below may be overridden by including an Ext locale file in your page.</p>
+ * <p>Example usage:</p>
+ * <pre><code>new Ext.panel.Panel({
+    title: 'Choose a future date:',
+    width: 200,
+    bodyPadding: 10,
+    renderTo: Ext.getBody(),
+    items: [{
+        xtype: 'datepicker',
+        minDate: new Date(),
+        handler: function(picker, date) {
+            // do something with the selected date
+        }
+    }]
+});</code></pre>
+ * {@img Ext.picker.Date/Ext.picker.Date.png Ext.picker.Date component}
+ *
+ * @constructor
+ * Create a new DatePicker
+ * @param {Object} config The config object
+ *
+ * @xtype datepicker
+ */
+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) {
+                // convert from 1 based index to 0 based
+                // by decrementing value once.
+                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',
+    /**
+     * @cfg {String} todayText
+     * The text to display on the button that selects the current date (defaults to <code>'Today'</code>)
+     */
+    todayText : 'Today',
+    /**
+     * @cfg {Function} handler
+     * Optional. A function that will handle the select event of this picker.
+     * The handler is passed the following parameters:<div class="mdetail-params"><ul>
+     * <li><code>picker</code> : Ext.picker.Date <div class="sub-desc">This Date picker.</div></li>
+     * <li><code>date</code> : Date <div class="sub-desc">The selected date.</div></li>
+     * </ul></div>
+     */
+    /**
+     * @cfg {Object} scope
+     * The scope (<code><b>this</b></code> reference) in which the <code>{@link #handler}</code>
+     * function will be called.  Defaults to this DatePicker instance.
+     */
+    /**
+     * @cfg {String} todayTip
+     * A string used to format the message for displaying in a tooltip over the button that
+     * selects the current date. Defaults to <code>'{0} (Spacebar)'</code> where
+     * the <code>{0}</code> token is replaced by today's date.
+     */
+    todayTip : '{0} (Spacebar)',
+    /**
+     * @cfg {String} minText
+     * The error text to display if the minDate validation fails (defaults to <code>'This date is before the minimum date'</code>)
+     */
+    minText : 'This date is before the minimum date',
+    /**
+     * @cfg {String} maxText
+     * The error text to display if the maxDate validation fails (defaults to <code>'This date is after the maximum date'</code>)
+     */
+    maxText : 'This date is after the maximum date',
+    /**
+     * @cfg {String} format
+     * The default date format string which can be overriden for localization support.  The format must be
+     * valid according to {@link Ext.Date#parse} (defaults to {@link Ext.Date#defaultFormat}).
+     */
+    /**
+     * @cfg {String} disabledDaysText
+     * The tooltip to display when the date falls on a disabled day (defaults to <code>'Disabled'</code>)
+     */
+    disabledDaysText : 'Disabled',
+    /**
+     * @cfg {String} disabledDatesText
+     * The tooltip text to display when the date falls on a disabled date (defaults to <code>'Disabled'</code>)
+     */
+    disabledDatesText : 'Disabled',
+    /**
+     * @cfg {Array} monthNames
+     * An array of textual month names which can be overriden for localization support (defaults to Ext.Date.monthNames)
+     */
+    /**
+     * @cfg {Array} dayNames
+     * An array of textual day names which can be overriden for localization support (defaults to Ext.Date.dayNames)
+     */
+    /**
+     * @cfg {String} nextText
+     * The next month navigation button tooltip (defaults to <code>'Next Month (Control+Right)'</code>)
+     */
+    nextText : 'Next Month (Control+Right)',
+    /**
+     * @cfg {String} prevText
+     * The previous month navigation button tooltip (defaults to <code>'Previous Month (Control+Left)'</code>)
+     */
+    prevText : 'Previous Month (Control+Left)',
+    /**
+     * @cfg {String} monthYearText
+     * The header month selector tooltip (defaults to <code>'Choose a month (Control+Up/Down to move years)'</code>)
+     */
+    monthYearText : 'Choose a month (Control+Up/Down to move years)',
+    /**
+     * @cfg {Number} startDay
+     * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
+     */
+    startDay : 0,
+    /**
+     * @cfg {Boolean} showToday
+     * False to hide the footer area containing the Today button and disable the keyboard handler for spacebar
+     * that selects the current date (defaults to <code>true</code>).
+     */
+    showToday : true,
+    /**
+     * @cfg {Date} minDate
+     * Minimum allowable date (JavaScript date object, defaults to null)
+     */
+    /**
+     * @cfg {Date} maxDate
+     * Maximum allowable date (JavaScript date object, defaults to null)
+     */
+    /**
+     * @cfg {Array} disabledDays
+     * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
+     */
+    /**
+     * @cfg {RegExp} disabledDatesRE
+     * JavaScript regular expression used to disable a pattern of dates (defaults to null).  The {@link #disabledDates}
+     * config will generate this regex internally, but if you specify disabledDatesRE it will take precedence over the
+     * disabledDates value.
+     */
+    /**
+     * @cfg {Array} disabledDates
+     * An array of 'dates' to disable, as strings. These strings will be used to build a dynamic regular
+     * expression so they are very powerful. Some examples:
+     * <ul>
+     * <li>['03/08/2003', '09/16/2003'] would disable those exact dates</li>
+     * <li>['03/08', '09/16'] would disable those days for every year</li>
+     * <li>['^03/08'] would only match the beginning (useful if you are using short years)</li>
+     * <li>['03/../2006'] would disable every day in March 2006</li>
+     * <li>['^03'] would disable every day in every March</li>
+     * </ul>
+     * Note that the format of the dates included in the array should exactly match the {@link #format} config.
+     * In order to support regular expressions, if you are using a date format that has '.' in it, you will have to
+     * escape the dot when restricting dates. For example: ['03\\.08\\.03'].
+     */
+
+    /**
+     * @cfg {Boolean} disableAnim True to disable animations when showing the month picker. Defaults to <tt>false</tt>.
+     */
+    disableAnim: true,
+
+    /**
+     * @cfg {String} baseCls
+     * The base CSS class to apply to this components element (defaults to <tt>'x-datepicker'</tt>).
+     */
+    baseCls: Ext.baseCSSPrefix + 'datepicker',
+
+    /**
+     * @cfg {String} selectedCls
+     * The class to apply to the selected cell. Defaults to <tt>'x-datepicker-selected'</tt>
+     */
+
+    /**
+     * @cfg {String} disabledCellCls
+     * The class to apply to disabled cells. Defaults to <tt>'x-datepicker-disabled'</tt>
+     */
+
+    /**
+     * @cfg {String} longDayFormat
+     * The format for displaying a date in a longer format. Defaults to <tt>'F d, Y'</tt>
+     */
+    longDayFormat: 'F d, Y',
+
+    /**
+     * @cfg {Object} keyNavConfig Specifies optional custom key event handlers for the {@link Ext.util.KeyNav}
+     * attached to this date picker. Must conform to the config format recognized by the {@link Ext.util.KeyNav}
+     * constructor. Handlers specified in this object will replace default handlers of the same name.
+     */
+
+    /**
+     * @cfg {Boolean} focusOnShow
+     * True to automatically focus the picker on show. Defaults to <tt>false</tt>.
+     */
+    focusOnShow: false,
+
+    // private
+    // Set by other components to stop the picker focus being updated when the value changes.
+    focusOnSelect: true,
+
+    width: 178,
+
+    // default value used to initialise each date in the DatePicker
+    // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
+    initHour: 12, // 24-hour format
+
+    numDays: 42,
+
+    // private, inherit docs
+    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(
+            /**
+             * @event select
+             * Fires when a date is selected
+             * @param {DatePicker} this DatePicker
+             * @param {Date} date The selected date
+             */
+            'select'
+        );
+
+        me.initDisabledDays();
+    },
+
+    // private, inherit docs
+    onRender : function(container, position){
+        /*
+         * days array for looping through 6 full weeks (6 weeks * 7 days)
+         * Note that we explicitly force the size here so the template creates
+         * all the appropriate cells.
+         */
+
+        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.middleBtnEl.down('button').addCls(Ext.baseCSSPrefix + 'btn-arrow');
+
+
+        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
+        });
+    },
+
+    // private, inherit docs
+    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);
+    },
+
+    /**
+     * Setup the disabled dates regex based on config options
+     * @private
+     */
+    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 + ')');
+        }
+    },
+
+    /**
+     * Replaces any existing disabled dates with new values and refreshes the DatePicker.
+     * @param {Array/RegExp} disabledDates An array of date strings (see the {@link #disabledDates} config
+     * for details on supported values), or a JavaScript regular expression used to disable a pattern of dates.
+     * @return {Ext.picker.Date} this
+     */
+    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;
+    },
+
+    /**
+     * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.
+     * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config
+     * for details on supported values.
+     * @return {Ext.picker.Date} this
+     */
+    setDisabledDays : function(dd){
+        this.disabledDays = dd;
+        return this.update(this.value, true);
+    },
+
+    /**
+     * Replaces any existing {@link #minDate} with the new value and refreshes the DatePicker.
+     * @param {Date} value The minimum date that can be selected
+     * @return {Ext.picker.Date} this
+     */
+    setMinDate : function(dt){
+        this.minDate = dt;
+        return this.update(this.value, true);
+    },
+
+    /**
+     * Replaces any existing {@link #maxDate} with the new value and refreshes the DatePicker.
+     * @param {Date} value The maximum date that can be selected
+     * @return {Ext.picker.Date} this
+     */
+    setMaxDate : function(dt){
+        this.maxDate = dt;
+        return this.update(this.value, true);
+    },
+
+    /**
+     * Sets the value of the date field
+     * @param {Date} value The date to set
+     * @return {Ext.picker.Date} this
+     */
+    setValue : function(value){
+        this.value = Ext.Date.clearTime(value, true);
+        return this.update(this.value);
+    },
+
+    /**
+     * Gets the current selected value of the date field
+     * @return {Date} The selected date
+     */
+    getValue : function(){
+        return this.value;
+    },
+
+    // private
+    focus : function(){
+        this.update(this.activeDate);
+    },
+
+    // private, inherit docs
+    onEnable: function(){
+        this.callParent();
+        this.setDisabledStatus(false);
+        this.update(this.activeDate);
+
+    },
+
+    // private, inherit docs
+    onDisable : function(){
+        this.callParent();
+        this.setDisabledStatus(true);
+    },
+
+    /**
+     * Set the disabled state of various internal components
+     * @private
+     * @param {Boolean} disabled
+     */
+    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);
+        }
+    },
+
+    /**
+     * Get the current active date.
+     * @private
+     * @return {Date} The active date
+     */
+    getActive: function(){
+        return this.activeDate || me.value;
+    },
+
+    /**
+     * Run any animation required to hide/show the month picker.
+     * @private
+     * @param {Boolean} isHide True if it's a hide operation
+     */
+    runAnimation: function(isHide){
+        var options = {
+                target: this.monthPicker,
+                duration: 200
+            };
+
+        Ext.fx.Manager.run();
+        if (isHide) {
+            //TODO: slideout
+        } else {
+            //TODO: slidein
+        }
+        Ext.create('Ext.fx.Anim', options);
+    },
+
+    /**
+     * Hides the month picker, if it's visible.
+     * @return {Ext.picker.Date} this
+     */
+    hideMonthPicker : function(){
+        var me = this,
+            picker = me.monthPicker;
+
+        if (picker) {
+            if (me.disableAnim) {
+                picker.hide();
+            } else {
+                this.runAnimation(true);
+            }
+        }
+        return me;
+    },
+
+    /**
+     * Show the month picker
+     * @return {Ext.picker.Date} this
+     */
+    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;
+    },
+
+    /**
+     * Create the month picker instance
+     * @private
+     * @return {Ext.picker.Month} picker
+     */
+    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;
+    },
+
+    /**
+     * Respond to an ok click on the month picker
+     * @private
+     */
+    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) {
+            // 'fix' the JS rolling date conversion if needed
+            date = new Date(year, month, 1).getLastDateOfMonth();
+        }
+        me.update(date);
+        me.hideMonthPicker();
+    },
+
+    /**
+     * Respond to a cancel click on the month picker
+     * @private
+     */
+    onCancelClick: function(){
+        this.hideMonthPicker();
+    },
+
+    /**
+     * Show the previous month.
+     * @return {Ext.picker.Date} this
+     */
+    showPrevMonth : function(e){
+        return this.update(Ext.Date.add(this.activeDate, Ext.Date.MONTH, -1));
+    },
+
+    /**
+     * Show the next month.
+     * @return {Ext.picker.Date} this
+     */
+    showNextMonth : function(e){
+        return this.update(Ext.Date.add(this.activeDate, Ext.Date.MONTH, 1));
+    },
+
+    /**
+     * Show the previous year.
+     * @return {Ext.picker.Date} this
+     */
+    showPrevYear : function(){
+        this.update(Ext.Date.add(this.activeDate, Ext.Date.YEAR, -1));
+    },
+
+    /**
+     * Show the next year.
+     * @return {Ext.picker.Date} this
+     */
+    showNextYear : function(){
+        this.update(Ext.Date.add(this.activeDate, Ext.Date.YEAR, 1));
+    },
+
+    /**
+     * Respond to the mouse wheel event
+     * @private
+     * @param {Ext.EventObject} e
+     */
+    handleMouseWheel : function(e){
+        e.stopEvent();
+        if(!this.disabled){
+            var delta = e.getWheelDelta();
+            if(delta > 0){
+                this.showPrevMonth();
+            } else if(delta < 0){
+                this.showNextMonth();
+            }
+        }
+    },
+
+    /**
+     * Respond to a date being clicked in the picker
+     * @private
+     * @param {Ext.EventObject} e
+     * @param {HTMLElement} t
+     */
+    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);
+            }
+            // event handling is turned off on hide
+            // when we are using the picker in a field
+            // therefore onSelect comes AFTER the select
+            // event.
+            me.onSelect();
+        }
+    },
+
+    /**
+     * Perform any post-select actions
+     * @private
+     */
+    onSelect: function() {
+        if (this.hideOnSelect) {
+             this.hide();
+         }
+    },
+
+    /**
+     * Sets the current value to today.
+     * @return {Ext.picker.Date} this
+     */
+    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;
+    },
+
+    /**
+     * Update the selected cell
+     * @private
+     * @param {Date} date The new date
+     * @param {Date} active The active date
+     */
+    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);
+    },
+
+    /**
+     * Update the contents of the picker for a new month
+     * @private
+     * @param {Date} date The new date
+     * @param {Date} active The active date
+     */
+    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);
+            // store dateValue number as an expando
+            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);
+                }
+            }
+            // disabling
+            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 the contents of the picker
+     * @private
+     * @param {Date} date The new date
+     * @param {Boolean} forceRefresh True to force a full refresh
+     */
+    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;
+    },
+
+    // private, inherit docs
+    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;
+        }
+    },
+
+    // private, inherit docs
+    onShow: function() {
+        this.callParent(arguments);
+        if (this.focusOnShow) {
+            this.focus();
+        }
+    }
+},
+
+// After dependencies have loaded:
+function() {
+    var proto = this.prototype;
+
+    proto.monthNames = Ext.Date.monthNames;
+
+    proto.dayNames = Ext.Date.dayNames;
+
+    proto.format = Ext.Date.defaultFormat;
+});
+
+/**
+ * @class Ext.form.field.Date
+ * @extends Ext.form.field.Picker
+
+Provides a date input field with a {@link Ext.picker.Date date picker} dropdown and automatic date
+validation.
+
+This field recognizes and uses the JavaScript Date object as its main {@link #value} type. In addition,
+it recognizes string values which are parsed according to the {@link #format} and/or {@link #altFormats}
+configs. These may be reconfigured to use date formats appropriate for the user's locale.
+
+The field may be limited to a certain range of dates by using the {@link #minValue}, {@link #maxValue},
+{@link #disabledDays}, and {@link #disabledDates} config parameters. These configurations will be used both
+in the field's validation, and in the date picker dropdown by preventing invalid dates from being selected.
+{@img Ext.form.Date/Ext.form.Date.png Ext.form.Date component}
+#Example usage:#
+
+    Ext.create('Ext.form.Panel', {
+        width: 300,
+        bodyPadding: 10,
+        title: 'Dates',
+        items: [{
+            xtype: 'datefield',
+            anchor: '100%',
+            fieldLabel: 'From',
+            name: 'from_date',
+            maxValue: new Date()  // limited to the current date or prior
+        }, {
+            xtype: 'datefield',
+            anchor: '100%',
+            fieldLabel: 'To',
+            name: 'to_date',
+            value: new Date()  // defaults to today
+        }],
+            renderTo: Ext.getBody()
+    });
+
+#Date Formats Examples#
+
+This example shows a couple of different date format parsing scenarios. Both use custom date format
+configurations; the first one matches the configured `format` while the second matches the `altFormats`.
+
+    Ext.create('Ext.form.Panel', {
+        renderTo: Ext.getBody(),
+        width: 300,
+        bodyPadding: 10,
+        title: 'Dates',
+        items: [{
+            xtype: 'datefield',
+            anchor: '100%',
+            fieldLabel: 'Date',
+            name: 'date',
+            // The value matches the format; will be parsed and displayed using that format.
+            format: 'm d Y',
+            value: '2 4 1978'
+        }, {
+            xtype: 'datefield',
+            anchor: '100%',
+            fieldLabel: 'Date',
+            name: 'date',
+            // The value does not match the format, but does match an altFormat; will be parsed
+            // using the altFormat and displayed using the format.
+            format: 'm d Y',
+            altFormats: 'm,d,Y|m.d.Y',
+            value: '2.4.1978'
+        }]
+    });
+
+ * @constructor
+ * Create a new Date field
+ * @param {Object} config
+ * 
+ * @xtype datefield
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+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'],
+
+    /**
+     * @cfg {String} format
+     * The default date format string which can be overriden for localization support.  The format must be
+     * valid according to {@link Ext.Date#parse} (defaults to <tt>'m/d/Y'</tt>).
+     */
+    format : "m/d/Y",
+    /**
+     * @cfg {String} altFormats
+     * Multiple date formats separated by "<tt>|</tt>" to try when parsing a user input value and it
+     * does not match the defined format (defaults to
+     * <tt>'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'</tt>).
+     */
+    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",
+    /**
+     * @cfg {String} disabledDaysText
+     * The tooltip to display when the date falls on a disabled day (defaults to <tt>'Disabled'</tt>)
+     */
+    disabledDaysText : "Disabled",
+    /**
+     * @cfg {String} disabledDatesText
+     * The tooltip text to display when the date falls on a disabled date (defaults to <tt>'Disabled'</tt>)
+     */
+    disabledDatesText : "Disabled",
+    /**
+     * @cfg {String} minText
+     * The error text to display when the date in the cell is before <tt>{@link #minValue}</tt> (defaults to
+     * <tt>'The date in this field must be after {minValue}'</tt>).
+     */
+    minText : "The date in this field must be equal to or after {0}",
+    /**
+     * @cfg {String} maxText
+     * The error text to display when the date in the cell is after <tt>{@link #maxValue}</tt> (defaults to
+     * <tt>'The date in this field must be before {maxValue}'</tt>).
+     */
+    maxText : "The date in this field must be equal to or before {0}",
+    /**
+     * @cfg {String} invalidText
+     * The error text to display when the date in the field is invalid (defaults to
+     * <tt>'{value} is not a valid date - it must be in the format {format}'</tt>).
+     */
+    invalidText : "{0} is not a valid date - it must be in the format {1}",
+    /**
+     * @cfg {String} triggerCls
+     * An additional CSS class used to style the trigger button.  The trigger will always get the
+     * class <tt>'x-form-trigger'</tt> and <tt>triggerCls</tt> will be <b>appended</b> if specified
+     * (defaults to <tt>'x-form-date-trigger'</tt> which displays a calendar icon).
+     */
+    triggerCls : Ext.baseCSSPrefix + 'form-date-trigger',
+    /**
+     * @cfg {Boolean} showToday
+     * <tt>false</tt> to hide the footer area of the Date picker containing the Today button and disable
+     * the keyboard handler for spacebar that selects the current date (defaults to <tt>true</tt>).
+     */
+    showToday : true,
+    /**
+     * @cfg {Date/String} minValue
+     * The minimum allowed date. Can be either a Javascript date object or a string date in a
+     * valid format (defaults to undefined).
+     */
+    /**
+     * @cfg {Date/String} maxValue
+     * The maximum allowed date. Can be either a Javascript date object or a string date in a
+     * valid format (defaults to undefined).
+     */
+    /**
+     * @cfg {Array} disabledDays
+     * An array of days to disable, 0 based (defaults to undefined). Some examples:<pre><code>
+// disable Sunday and Saturday:
+disabledDays:  [0, 6]
+// disable weekdays:
+disabledDays: [1,2,3,4,5]
+     * </code></pre>
+     */
+    /**
+     * @cfg {Array} disabledDates
+     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
+     * expression so they are very powerful. Some examples:<pre><code>
+// disable these exact dates:
+disabledDates: ["03/08/2003", "09/16/2003"]
+// disable these days for every year:
+disabledDates: ["03/08", "09/16"]
+// only match the beginning (useful if you are using short years):
+disabledDates: ["^03/08"]
+// disable every day in March 2006:
+disabledDates: ["03/../2006"]
+// disable every day in every March:
+disabledDates: ["^03"]
+     * </code></pre>
+     * Note that the format of the dates included in the array should exactly match the {@link #format} config.
+     * In order to support regular expressions, if you are using a {@link #format date format} that has "." in
+     * it, you will have to escape the dot when restricting dates. For example: <tt>["03\\.08\\.03"]</tt>.
+     */
+    
+    /**
+     * @cfg {String} submitFormat The date format string which will be submitted to the server.  
+     * The format must be valid according to {@link Ext.Date#parse} (defaults to <tt>{@link #format}</tt>).
+     */
+
+    // in the absence of a time value, a default value of 12 noon will be used
+    // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
+    initTime: '12', // 24 hour format
+
+    initTimeFormat: 'H',
+
+    matchFieldWidth: false,
+    /**
+     * @cfg {Number} startDay
+     * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
+     */
+    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 a String value was supplied, try to convert it to a proper Date
+        if (Ext.isString(value)) {
+            me.value = me.rawToValue(value);
+        }
+
+        me.callParent();
+    },
+
+    // private
+    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 + ')');
+        }
+    },
+
+    /**
+     * Replaces any existing disabled dates with new values and refreshes the Date picker.
+     * @param {Array} disabledDates An array of date strings (see the <tt>{@link #disabledDates}</tt> config
+     * for details on supported values) used to disable a pattern of dates.
+     */
+    setDisabledDates : function(dd){
+        var me = this,
+            picker = me.picker;
+            
+        me.disabledDates = dd;
+        me.initDisabledDays();
+        if (picker) {
+            picker.setDisabledDates(me.disabledDatesRE);
+        }
+    },
+
+    /**
+     * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the Date picker.
+     * @param {Array} disabledDays An array of disabled day indexes. See the <tt>{@link #disabledDays}</tt>
+     * config for details on supported values.
+     */
+    setDisabledDays : function(dd){
+        var picker = this.picker;
+            
+        this.disabledDays = dd;
+        if (picker) {
+            picker.setDisabledDays(dd);
+        }
+    },
+
+    /**
+     * Replaces any existing <tt>{@link #minValue}</tt> with the new value and refreshes the Date picker.
+     * @param {Date} value The minimum date that can be selected
+     */
+    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);
+        }
+    },
+
+    /**
+     * Replaces any existing <tt>{@link #maxValue}</tt> with the new value and refreshes the Date picker.
+     * @param {Date} value The maximum date that can be selected
+     */
+    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);
+        }
+    },
+
+    /**
+     * Runs all of Date's validations and returns an array of any errors. Note that this first
+     * runs Text's validations, so the returned array is an amalgamation of all field errors.
+     * The additional validation checks are testing that the date format is valid, that the chosen
+     * date is within the min and max date constraints set, that the date chosen is not in the disabledDates
+     * regex and that the day chosed is not one of the disabledDays.
+     * @param {Mixed} value The value to get errors for (defaults to the current field value)
+     * @return {Array} All validation errors for this field
+     */
+    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) { // if it's blank and textfield didn't flag it then it's valid
+             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));
+    },
+
+    /**
+     * Sets the value of the date field.  You can pass a date object or any string that can be
+     * parsed into a valid date, using <tt>{@link #format}</tt> as the date format, according
+     * to the same rules as {@link Ext.Date#parse} (the default format used is <tt>"m/d/Y"</tt>).
+     * <br />Usage:
+     * <pre><code>
+//All of these calls set the same date value (May 4, 2006)
+
+//Pass a date object:
+var dt = new Date('5/4/2006');
+dateField.setValue(dt);
+
+//Pass a date string (default format):
+dateField.setValue('05/04/2006');
+
+//Pass a date string (custom format):
+dateField.format = 'Y-m-d';
+dateField.setValue('2006-05-04');
+</code></pre>
+     * @param {String/Date} date The date or valid date string
+     * @return {Ext.form.field.Date} this
+     * @method setValue
+     */
+
+    /**
+     * Attempts to parse a given string value using a given {@link Ext.Date#parse date format}.
+     * @param {String} value The value to attempt to parse
+     * @param {String} format A valid date format (see {@link Ext.Date#parse})
+     * @return {Date} The parsed Date object, or null if the value could not be successfully parsed.
+     */
+    safeParse : function(value, format) {
+        var me = this,
+            utilDate = Ext.Date,
+            parsedDate,
+            result = null;
+            
+        if (utilDate.formatContainsHourInfo(format)) {
+            // if parse format contains hour information, no DST adjustment is necessary
+            result = utilDate.parse(value, format);
+        } else {
+            // set time to 12 noon, then clear the time
+            parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat);
+            if (parsedDate) {
+                result = utilDate.clearTime(parsedDate);
+            }
+        }
+        return result;
+    },
+    
+    // @private
+    getSubmitValue: function() {
+        var me = this,
+            format = me.submitFormat || me.format,
+            value = me.getValue();
+            
+        return value ? Ext.Date.format(value, format) : null;
+    },
+
+    /**
+     * @private
+     */
+    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;
+    },
+
+    // private
+    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: this.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) {
+        this.setValue(d);
+        this.fireEvent('select', this, d);
+        this.collapse();
+    },
+
+    /**
+     * @private
+     * Sets the Date picker's value to match the current field value when expanding.
+     */
+    onExpand: function() {
+        var me = this,
+            value = me.getValue();
+        me.picker.setValue(value instanceof Date ? value : new Date());
+    },
+
+    /**
+     * @private
+     * Focuses the field when collapsing the Date picker.
+     */
+    onCollapse: function() {
+        this.focus(false, 60);
+    },
+
+    // private
+    beforeBlur : function(){
+        var v = this.parseDate(this.getRawValue());
+        if(v){
+            this.setValue(v);
+        }
+    }
+
+    /**
+     * @cfg {Boolean} grow @hide
+     */
+    /**
+     * @cfg {Number} growMin @hide
+     */
+    /**
+     * @cfg {Number} growMax @hide
+     */
+    /**
+     * @hide
+     * @method autoSize
+     */
+});
+
+/**
+ * @class Ext.form.field.Display
+ * @extends Ext.form.field.Base
+ * <p>A display-only text field which is not validated and not submitted. This is useful for when you want
+ * to display a value from a form's {@link Ext.form.Basic#load loaded data} but do not want to allow the
+ * user to edit or submit that value. The value can be optionally {@link #htmlEncode HTML encoded} if it contains
+ * HTML markup that you do not want to be rendered.</p>
+ * <p>If you have more complex content, or need to include components within the displayed content, also
+ * consider using a {@link Ext.form.FieldContainer} instead.</p>
+ * {@img Ext.form.Display/Ext.form.Display.png Ext.form.Display component}
+ * <p>Example:</p>
+ * <pre><code>
+    Ext.create('Ext.form.Panel', {
+        width: 175,
+        height: 120,
+        bodyPadding: 10,
+        title: 'Final Score',
+        items: [{
+            xtype: 'displayfield',
+            fieldLabel: 'Home',
+            name: 'home_score',
+            value: '10'
+        }, {
+            xtype: 'displayfield',
+            fieldLabel: 'Visitor',
+            name: 'visitor_score',
+            value: '11'
+        }],
+        buttons: [{
+            text: 'Update',
+        }],
+        renderTo: Ext.getBody()
+    });
+</code></pre>
+
+ * @constructor
+ * Creates a new DisplayField.
+ * @param {Object} config Configuration options
+ *
+ * @xtype displayfield
+ */
+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
+        }
+    ],
+
+    /**
+     * @cfg {String} fieldCls The default CSS class for the field (defaults to <tt>"x-form-display-field"</tt>)
+     */
+    fieldCls: Ext.baseCSSPrefix + 'form-display-field',
+
+    /**
+     * @cfg {Boolean} htmlEncode <tt>false</tt> to skip HTML-encoding the text when rendering it (defaults to
+     * <tt>false</tt>). This might be useful if you want to include tags in the field's innerHTML rather than
+     * rendering them as string literals per the default logic.
+     */
+    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;
+    },
+
+    // private
+    getContentTarget: function() {
+        return this.inputEl;
+    }
+
+    /**
+     * @cfg {String} inputType
+     * @hide
+     */
+    /**
+     * @cfg {Boolean} disabled
+     * @hide
+     */
+    /**
+     * @cfg {Boolean} readOnly
+     * @hide
+     */
+    /**
+     * @cfg {Boolean} validateOnChange
+     * @hide
+     */
+    /**
+     * @cfg {Number} checkChangeEvents
+     * @hide
+     */
+    /**
+     * @cfg {Number} checkChangeBuffer
+     * @hide
+     */
+});
+
+/**
+ * @class Ext.form.field.File
+ * @extends Ext.form.field.Text
+
+A file upload field which has custom styling and allows control over the button text and other
+features of {@link Ext.form.field.Text text fields} like {@link Ext.form.field.Text#emptyText empty text}.
+It uses a hidden file input element behind the scenes to allow user selection of a file and to
+perform the actual upload during {@link Ext.form.Basic#submit form submit}.
+
+Because there is no secure cross-browser way to programmatically set the value of a file input,
+the standard Field `setValue` method is not implemented. The `{@link #getValue}` method will return
+a value that is browser-dependent; some have just the file name, some have a full path, some use
+a fake path.
+{@img Ext.form.File/Ext.form.File.png Ext.form.File component}
+#Example Usage:#
+
+    Ext.create('Ext.form.Panel', {
+        title: 'Upload a Photo',
+        width: 400,
+        bodyPadding: 10,
+        frame: true,
+        renderTo: Ext.getBody(),    
+        items: [{
+            xtype: 'filefield',
+            name: 'photo',
+            fieldLabel: 'Photo',
+            labelWidth: 50,
+            msgTarget: 'side',
+            allowBlank: false,
+            anchor: '100%',
+            buttonText: 'Select Photo...'
+        }],
+    
+        buttons: [{
+            text: 'Upload',
+            handler: function() {
+                var form = this.up('form').getForm();
+                if(form.isValid()){
+                    form.submit({
+                        url: 'photo-upload.php',
+                        waitMsg: 'Uploading your photo...',
+                        success: function(fp, o) {
+                            Ext.Msg.alert('Success', 'Your photo "' + o.result.file + '" has been uploaded.');
+                        }
+                    });
+                }
+            }
+        }]
+    });
+
+ * @constructor
+ * Create a new File field
+ * @param {Object} config Configuration options
+ * @xtype filefield
+ * @markdown
+ * @docauthor Jason Johnston <jason@sencha.com>
+ */
+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'],
+
+    /**
+     * @cfg {String} buttonText The button text to display on the upload button (defaults to
+     * 'Browse...').  Note that if you supply a value for {@link #buttonConfig}, the buttonConfig.text
+     * value will be used instead if available.
+     */
+    buttonText: 'Browse...',
+
+    /**
+     * @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible
+     * text field (defaults to false).  If true, all inherited Text members will still be available.
+     */
+    buttonOnly: false,
+
+    /**
+     * @cfg {Number} buttonMargin The number of pixels of space reserved between the button and the text field
+     * (defaults to 3).  Note that this only applies if {@link #buttonOnly} = false.
+     */
+    buttonMargin: 3,
+
+    /**
+     * @cfg {Object} buttonConfig A standard {@link Ext.button.Button} config object.
+     */
+
+    /**
+     * @event change
+     * Fires when the underlying file input field's value has changed from the user
+     * selecting a new file from the system file selection dialog.
+     * @param {Ext.ux.form.FileUploadField} this
+     * @param {String} value The file value returned by the underlying file input field
+     */
+
+    /**
+     * @property fileInputEl
+     * @type {Ext.core.Element}
+     * A reference to the invisible file input element created for this upload field. Only
+     * populated after this component is rendered.
+     */
+
+    /**
+     * @property button
+     * @type {Ext.button.Button}
+     * A reference to the trigger Button component created for this upload field. Only
+     * populated after this component is rendered.
+     */
+
+    /**
+     * @cfg {String} fieldBodyCls
+     * An extra CSS class to be applied to the body content element in addition to {@link #fieldBodyCls}.
+     * Defaults to 'x-form-file-wrap' for file upload field.
+     */
+    fieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap',
+
+
+    // private
+    readOnly: true,
+    componentLayout: 'filefield',
+
+    // private
+    onRender: function() {
+        var me = this,
+            inputEl;
+
+        me.callParent(arguments);
+
+        me.createButton();
+        me.createFileInput();
+        
+        // we don't create the file/button til after onRender, the initial disable() is
+        // called in the onRender of the component.
+        if (me.disabled) {
+            me.disableItems();
+        }
+
+        inputEl = me.inputEl;
+        inputEl.dom.removeAttribute('name'); //name goes on the fileInput, not the text input
+        if (me.buttonOnly) {
+            inputEl.setDisplayed(false);
+        }
+    },
+
+    /**
+     * @private
+     * Creates the custom trigger Button component. The fileInput will be inserted into this.
+     */
+    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,
+            ownerCt: me,
+            style: me.buttonOnly ? '' : 'margin-left:' + me.buttonMargin + 'px'
+        }, me.buttonConfig));
+    },
+
+    /**
+     * @private
+     * Creates the file input element. It is inserted into the trigger button component, made
+     * invisible, and floated on top of the button's other content so that it will receive the
+     * button's clicks.
+     */
+    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);
+    },
+
+    /**
+     * @private Event handler fired when the user selects a file.
+     */
+    onFileChange: function() {
+        this.lastValue = null; // force change event to get fired even if the user selects a file with the same name
+        Ext.form.field.File.superclass.setValue.call(this, this.fileInputEl.dom.value);
+    },
+
+    /**
+     * Overridden to do nothing
+     * @hide
+     */
+    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();
+    }
+
+
+});
+
+/**
+ * @class Ext.form.field.Hidden
+ * @extends Ext.form.field.Base
+ * <p>A basic hidden field for storing hidden values in forms that need to be passed in the form submit.</p>
+ * <p>This creates an actual input element with type="submit" in the DOM. While its label is
+ * {@link #hideLabel not rendered} by default, it is still a real component and may be sized according to
+ * its owner container's layout.</p>
+ * <p>Because of this, in most cases it is more convenient and less problematic to simply
+ * {@link Ext.form.action.Action#params pass hidden parameters} directly when
+ * {@link Ext.form.Basic#submit submitting the form}.</p>
+ * <p>Example:</p>
+ * <pre><code>new Ext.form.Panel({
+    title: 'My Form',
+    items: [{
+        xtype: 'textfield',
+        fieldLabel: 'Text Field',
+        name: 'text_field',
+        value: 'value from text field'
+    }, {
+        xtype: 'hiddenfield',
+        name: 'hidden_field_1',
+        value: 'value from hidden field'
+    }],
+
+    buttons: [{
+        text: 'Submit',
+        handler: function() {
+            this.up('form').getForm().submit({
+                params: {
+                    hidden_field_2: 'value from submit call'
+                }
+            });
+        }
+    }]
+});</code></pre>
+ * <p>Submitting the above form will result in three values sent to the server:
+ * <code>text_field=value+from+text+field&hidden_field_1=value+from+hidden+field&<br>hidden_field_2=value+from+submit+call</code></p>
+ *
+ * @constructor
+ * Create a new Hidden field.
+ * @param {Object} config Configuration options
+ * 
+ * @xtype hiddenfield
+ */
+Ext.define('Ext.form.field.Hidden', {
+    extend:'Ext.form.field.Base',
+    alias: ['widget.hiddenfield', 'widget.hidden'],
+    alternateClassName: 'Ext.form.Hidden',
+
+    // private
+    inputType : 'hidden',
+    hideLabel: true,
+    
+    initComponent: function(){
+        this.formItemCls += '-hidden';
+        this.callParent();    
+    },
+
+    // These are all private overrides
+    initEvents: Ext.emptyFn,
+    setSize : Ext.emptyFn,
+    setWidth : Ext.emptyFn,
+    setHeight : Ext.emptyFn,
+    setPosition : Ext.emptyFn,
+    setPagePosition : Ext.emptyFn,
+    markInvalid : Ext.emptyFn,
+    clearInvalid : Ext.emptyFn
+});
+
+/**
+ * @class Ext.picker.Color
+ * @extends Ext.Component
+ * <p>ColorPicker provides a simple color palette for choosing colors. The picker can be rendered to any container.
+ * The available default to a standard 40-color palette; this can be customized with the {@link #colors} config.</p>
+ * <p>Typically you will need to implement a handler function to be notified when the user chooses a color from the
+ * picker; you can register the handler using the {@link #select} event, or by implementing the {@link #handler}
+ * method.</p>
+ * <p>Here's an example of typical usage:</p>
+ * <pre><code>var cp = new Ext.picker.Color({
+    value: '993300',  // initial selected color
+    renderTo: 'my-div'
+});
+
+cp.on('select', function(picker, selColor){
+    // do something with selColor
+});
+</code></pre>
+ * {@img Ext.picker.Color/Ext.picker.Color.png Ext.picker.Color component}
+ *
+ * @constructor
+ * Create a new ColorPicker
+ * @param {Object} config The config object
+ * 
+ * @xtype colorpicker
+ */
+Ext.define('Ext.picker.Color', {
+    extend: 'Ext.Component',
+    requires: 'Ext.XTemplate',
+    alias: 'widget.colorpicker',
+    alternateClassName: 'Ext.ColorPalette',
+    
+    /**
+     * @cfg {String} componentCls
+     * The CSS class to apply to the containing element (defaults to 'x-color-picker')
+     */
+    componentCls : Ext.baseCSSPrefix + 'color-picker',
+    
+    /**
+     * @cfg {String} selectedCls
+     * The CSS class to apply to the selected element
+     */
+    selectedCls: Ext.baseCSSPrefix + 'color-picker-selected',
+    
+    /**
+     * @cfg {String} value
+     * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
+     * the hex codes are case-sensitive.
+     */
+    value : null,
+    
+    /**
+     * @cfg {String} clickEvent
+     * The DOM event that will cause a color to be selected. This can be any valid event name (dblclick, contextmenu).
+     * Defaults to <tt>'click'</tt>.
+     */
+    clickEvent :'click',
+
+    /**
+     * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the {@link #select} event
+     */
+    allowReselect : false,
+
+    /**
+     * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
+     * of colors, and each hex code should be unique.  The width of the picker is controlled via CSS by adjusting
+     * the width property of the 'x-color-picker' class (or assigning a custom class), so you can balance the number
+     * of colors with the width setting until the box is symmetrical.</p>
+     * <p>You can override individual colors if needed:</p>
+     * <pre><code>
+var cp = new Ext.picker.Color();
+cp.colors[0] = 'FF0000';  // change the first box to red
+</code></pre>
+
+Or you can provide a custom array of your own for complete control:
+<pre><code>
+var cp = new Ext.picker.Color();
+cp.colors = ['000000', '993300', '333300'];
+</code></pre>
+     * @type Array
+     */
+    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'
+    ],
+
+    /**
+     * @cfg {Function} handler
+     * Optional. A function that will handle the select event of this picker.
+     * The handler is passed the following parameters:<div class="mdetail-params"><ul>
+     * <li><code>picker</code> : ColorPicker<div class="sub-desc">The {@link #picker Ext.picker.Color}.</div></li>
+     * <li><code>color</code> : String<div class="sub-desc">The 6-digit color hex code (without the # symbol).</div></li>
+     * </ul></div>
+     */
+    /**
+     * @cfg {Object} scope
+     * The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
+     * function will be called.  Defaults to this ColorPicker instance.
+     */
+    
+    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);
+    },
+    
+    // private
+    initComponent : function(){
+        var me = this;
+        
+        this.callParent(arguments);
+        me.addEvents(
+            /**
+             * @event select
+             * Fires when a color is selected
+             * @param {Ext.picker.Color} this
+             * @param {String} color The 6-digit color hex code (without the # symbol)
+             */
+            'select'
+        );
+
+        if (me.handler) {
+            me.on('select', me.handler, me.scope, true);
+        }
+    },
+
+
+    // private
+    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'});
+        // always stop following the anchors
+        if(clickEvent != 'click'){
+            me.mon(me.el, 'click', Ext.emptyFn, me, {delegate: 'a', stopEvent: true});
+        }
+    },
+
+    // private
+    afterRender : function(){
+        var me = this,
+            value;
+            
+        this.callParent(arguments);
+        if (me.value) {
+            value = me.value;
+            me.value = null;
+            me.select(value, true);
+        }
+    },
+
+    // private
+    handleClick : function(event, target){
+        var me = this,
+            color;
+            
+        event.stopEvent();
+        if (!me.disabled) {
+            color = target.className.match(me.colorRe)[1];
+            me.select(color.toUpperCase());
+        }
+    },
+
+    /**
+     * Selects the specified color in the picker (fires the {@link #select} event)
+     * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
+     * @param {Boolean} suppressEvent (optional) True to stop the select event from firing. Defaults to <tt>false</tt>.
+     */
+    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);
+            }
+        }
+    },
+    
+    /**
+     * Get the currently selected color value.
+     * @return {String} value The selected value. Null if nothing is selected.
+     */
+    getValue: function(){
+        return this.value || null;
+    }
+});
+
+/**
+ * @private
+ * @class Ext.layout.component.field.HtmlEditor
+ * @extends Ext.layout.component.field.Field
+ * Layout class for {@link Ext.form.field.HtmlEditor} fields. Sizes the toolbar, textarea, and iframe elements.
+ * @private
+ */
+
+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 fixed height, subtract toolbar height from the input area height
+        if (Ext.isNumber(height)) {
+            editorHeight = height - toolbar.getHeight() - bodyEl.getFrameWidth('tb');
+            textarea.setHeight(editorHeight);
+            iframe.setHeight(editorHeight);
+        }
+    }
+});
+/**
+ * @class Ext.form.field.HtmlEditor
+ * @extends Ext.Component
+ *
+ * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
+ * automatically hidden when needed. These are noted in the config options where appropriate.
+ * 
+ * The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
+ * enabled by default unless the global {@link Ext.tip.QuickTipManager} singleton is {@link Ext.tip.QuickTipManager#init initialized}.
+ * 
+ * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
+ * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
+ *
+ * {@img Ext.form.HtmlEditor/Ext.form.HtmlEditor1.png Ext.form.HtmlEditor component}
+ *
+ * ## Example usage
+ *
+ * {@img Ext.form.HtmlEditor/Ext.form.HtmlEditor2.png Ext.form.HtmlEditor component}
+ *
+ *     // Simple example rendered with default options:
+ *     Ext.tip.QuickTips.init();  // enable tooltips
+ *     Ext.create('Ext.form.HtmlEditor', {
+ *         width: 580,
+ *         height: 250,
+ *         renderTo: Ext.getBody()        
+ *     });
+ * 
+ * {@img Ext.form.HtmlEditor/Ext.form.HtmlEditor2.png Ext.form.HtmlEditor component}
+ * 
+ *     // Passed via xtype into a container and with custom options:
+ *     Ext.tip.QuickTips.init();  // enable tooltips
+ *     new Ext.panel.Panel({
+ *         title: 'HTML Editor',
+ *         renderTo: Ext.getBody(),
+ *         width: 550,
+ *         height: 250,
+ *         frame: true,
+ *         layout: 'fit',
+ *         items: {
+ *             xtype: 'htmleditor',
+ *             enableColors: false,
+ *             enableAlignments: false
+ *         }
+ *     });
+ *
+ * @constructor
+ * Create a new HtmlEditor
+ * @param {Object} config
+ * @xtype htmleditor
+ */
+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
+        }
+    ],
+
+    /**
+     * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
+     */
+    enableFormat : true,
+    /**
+     * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
+     */
+    enableFontSize : true,
+    /**
+     * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
+     */
+    enableColors : true,
+    /**
+     * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
+     */
+    enableAlignments : true,
+    /**
+     * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
+     */
+    enableLists : true,
+    /**
+     * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
+     */
+    enableSourceEdit : true,
+    /**
+     * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
+     */
+    enableLinks : true,
+    /**
+     * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
+     */
+    enableFont : true,
+    /**
+     * @cfg {String} createLinkText The default text for the create link prompt
+     */
+    createLinkText : 'Please enter the URL for the link:',
+    /**
+     * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
+     */
+    defaultLinkValue : 'http:/'+'/',
+    /**
+     * @cfg {Array} fontFamilies An array of available font families
+     */
+    fontFamilies : [
+        'Arial',
+        'Courier New',
+        'Tahoma',
+        'Times New Roman',
+        'Verdana'
+    ],
+    defaultFont: 'tahoma',
+    /**
+     * @cfg {String} defaultValue A default value to be put into the editor to resolve focus issues (defaults to &#160; (Non-breaking space) in Opera and IE6, &#8203; (Zero-width space) in all other browsers).
+     */
+    defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',
+
+    fieldBodyCls: Ext.baseCSSPrefix + 'html-editor-wrap',
+
+    componentLayout: 'htmleditor',
+
+    // private properties
+    initialized : false,
+    activated : false,
+    sourceEditMode : false,
+    iframePad:3,
+    hideMode:'offsets',
+
+    maskOnDisable: true,
+    
+    // private
+    initComponent : function(){
+        var me = this;
+
+        me.addEvents(
+            /**
+             * @event initialize
+             * Fires when the editor is fully initialized (including the iframe)
+             * @param {Ext.form.field.HtmlEditor} this
+             */
+            'initialize',
+            /**
+             * @event activate
+             * Fires when the editor is first receives the focus. Any insertion must wait
+             * until after this event.
+             * @param {Ext.form.field.HtmlEditor} this
+             */
+            'activate',
+             /**
+             * @event beforesync
+             * Fires before the textarea is updated with content from the editor iframe. Return false
+             * to cancel the sync.
+             * @param {Ext.form.field.HtmlEditor} this
+             * @param {String} html
+             */
+            'beforesync',
+             /**
+             * @event beforepush
+             * Fires before the iframe editor is updated with content from the textarea. Return false
+             * to cancel the push.
+             * @param {Ext.form.field.HtmlEditor} this
+             * @param {String} html
+             */
+            'beforepush',
+             /**
+             * @event sync
+             * Fires when the textarea is updated with content from the editor iframe.
+             * @param {Ext.form.field.HtmlEditor} this
+             * @param {String} html
+             */
+            'sync',
+             /**
+             * @event push
+             * Fires when the iframe editor is updated with content from the textarea.
+             * @param {Ext.form.field.HtmlEditor} this
+             * @param {String} html
+             */
+            'push',
+             /**
+             * @event editmodechange
+             * Fires when the editor switches edit modes
+             * @param {Ext.form.field.HtmlEditor} this
+             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
+             */
+            'editmodechange'
+        );
+
+        me.callParent(arguments);
+
+        // Init mixins
+        me.initLabelable();
+        me.initField();
+    },
+
+    /*
+     * Protected method that will not generally be called directly. It
+     * is called when the editor creates its toolbar. Override this method if you need to
+     * add custom toolbar buttons.
+     * @param {Ext.form.field.HtmlEditor} editor
+     */
+    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);
+                    })
+                );
+            }
+        }
+
+        // build the toolbar
+        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();
+            });
+        }
+
+        // stop form submits
+        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);
+    },
+
+    /**
+     * Sets the read only state of this field.
+     * @param {Boolean} readOnly Whether the field should be read only.
+     */
+    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) {
+                // Hide the iframe while setting contentEditable so it doesn't grab focus
+                iframeEl.setDisplayed(false);
+                body.contentEditable = !readOnly;
+                iframeEl.setDisplayed(true);
+            } else {
+                me.setDesignMode(!readOnly);
+            }
+            if (body) {
+                body.style.cursor = readOnly ? 'default' : 'text';
+            }
+            me.disableItems(readOnly);
+        }
+    },
+
+    /**
+     * Protected method that will not generally be called directly. It
+     * is called when the editor initializes the iframe with HTML contents. Override this method if you
+     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
+     *
+     * Note: IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility.
+     * Also note that forcing IE7 mode works when the page is loaded normally, but if you are using IE's Web
+     * Developer Tools to manually set the document mode, that will take precedence and override what this
+     * code sets by default. This can be confusing when developing, but is not a user-facing issue.
+     */
+    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);
+    },
+
+    // private
+    getEditorBody: function() {
+        var doc = this.getDoc();
+        return doc.body || doc.documentElement;
+    },
+
+    // private
+    getDoc: function() {
+        return (!Ext.isIE && this.iframeEl.dom.contentDocument) || this.getWin().document;
+    },
+
+    // private
+    getWin: function() {
+        return Ext.isIE ? this.iframeEl.dom.contentWindow : window.frames[this.iframeEl.dom.name];
+    },
+
+    // private
+    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 || '';
+
+        // Start polling for when the iframe document is ready to be manipulated
+        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 = { // must defer to wait for browser to be ready
+            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();
+        }
+    },
+
+    /* private
+     * set current design mode. To enable, mode can be true or 'on', off otherwise
+     */
+    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';
+        }
+    },
+
+    // private
+    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);
+            }
+        });
+    },
+
+    /**
+     * Toggles the editor between standard and source edit mode.
+     * @param {Boolean} sourceEditMode (optional) True for source edit, false for standard
+     */
+    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();
+    },
+
+    // private used internally
+    createLink : function() {
+        var url = prompt(this.createLinkText, this.defaultLinkValue);
+        if (url && url !== 'http:/'+'/') {
+            this.relayCmd('createlink', url);
+        }
+    },
+
+    clearInvalid: Ext.emptyFn,
+
+    // docs inherit from Field
+    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;
+    },
+
+    /**
+     * Protected method that will not generally be called directly. If you need/want
+     * custom HTML cleanup, this is the method you should override.
+     * @param {String} html The HTML to be cleaned
+     * @return {String} The cleaned HTML
+     */
+    cleanHtml: function(html) {
+        html = String(html);
+        if (Ext.isWebKit) { // strip safari nonsense
+            html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
+        }
+
+        /*
+         * Neat little hack. Strips out all the non-digit characters from the default
+         * value and compares it to the character code of the first character in the string
+         * because it can cause encoding issues when posted to the server.
+         */
+        if (html.charCodeAt(0) === this.defaultValue.replace(/\D/g, '')) {
+            html = html.substring(1);
+        }
+        return html;
+    },
+
+    /**
+     * @protected method that will not generally be called directly. Syncs the contents
+     * of the editor iframe with the textarea.
+     */
+    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'); // Safari puts text-align styles on the body element!
+                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);
+            }
+        }
+    },
+
+    //docs inherit from Field
+    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;
+    },
+
+    /**
+     * @protected method that will not generally be called directly. Pushes the value of the textarea
+     * into the iframe editor.
+     */
+    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) {
+                    // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8
+                    me.setDesignMode(false);  //toggle off first
+                    me.setDesignMode(true);
+                }
+                me.fireEvent('push', me, v);
+            }
+        }
+    },
+
+    // private
+    deferFocus : function(){
+         this.focus(false, true);
+    },
+
+    getFocusEl: function() {
+        var me = this,
+            win = me.win;
+        return win && !me.sourceEditMode ? win : me.textareaEl;
+    },
+
+    // private
+    initEditor : function(){
+        //Destroying the component during/before initEditor can cause issues.
+        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'; // w3c
+            dbody.bgProperties = 'fixed'; // ie
+
+            Ext.core.DomHelper.applyStyles(dbody, ss);
+
+            doc = me.getDoc();
+
+            if (doc) {
+                try {
+                    Ext.EventManager.removeAll(doc);
+                } catch(e) {}
+            }
+
+            /*
+             * We need to use createDelegate here, because when using buffer, the delayed task is added
+             * as a property to the function. When the listener is removed, the task is deleted from the function.
+             * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors
+             * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.
+             */
+            fn = Ext.Function.bind(me.onEditorEvent, me);
+            Ext.EventManager.on(doc, {
+                mousedown: fn,
+                dblclick: fn,
+                click: fn,
+                keyup: fn,
+                buffer:100
+            });
+
+            // These events need to be relayed from the inner document (where they stop
+            // bubbling) up to the outer document. This has to be done at the DOM level so
+            // the event reaches listeners on elements like the document body. The effected
+            // mechanisms that depend on this bubbling behavior are listed to the right
+            // of the event.
+            fn = me.onRelayedEvent;
+            Ext.EventManager.on(doc, {
+                mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)
+                mousemove: fn, // window resize drag detection
+                mouseup: fn,   // window resize termination
+                click: fn,     // not sure, but just to be safe
+                dblclick: fn,  // not sure again
+                scope: me
+            });
+
+            if (Ext.isGecko) {
+                Ext.EventManager.on(doc, 'keypress', me.applyCommand, me);
+            }
+            if (me.fixKeys) {
+                Ext.EventManager.on(doc, 'keydown', me.fixKeys, me);
+            }
+
+            // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!
+            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) {
+            // ignore (why?)
+        }
+    },
+
+    // private
+    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) {
+                // ignore (why?)
+            }
+            Ext.destroyMembers('tb', 'toolbarWrap', 'iframeEl', 'textareaEl');
+        }
+        me.callParent();
+    },
+
+    // private
+    onRelayedEvent: function (event) {
+        // relay event from the iframe's document to the document that owns the iframe...
+
+        var iframeEl = this.iframeEl,
+            iframeXY = iframeEl.getXY(),
+            eventXY = event.getXY();
+
+        // the event from the inner document has XY relative to that document's origin,
+        // so adjust it to use the origin of the iframe in the outer document:
+        event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];
+
+        event.injectEvent(iframeEl); // blame the iframe for the event...
+
+        event.xy = eventXY; // restore the original XY (just for safety)
+    },
+
+    // private
+    onFirstFocus : function(){
+        var me = this,
+            selection, range;
+        me.activated = true;
+        me.disableItems(me.readOnly);
+        if (Ext.isGecko) { // prevent silly gecko errors
+            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) {
+                // ignore (why?)
+            }
+        }
+        me.fireEvent('activate', me);
+    },
+
+    // private
+    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) {
+            // Safari 3 values
+            // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
+            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) { // safari
+                adjust *= 2;
+            }
+            size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0);
+        }
+        this.execCmd('FontSize', size);
+    },
+
+    // private
+    onEditorEvent: function(e) {
+        this.updateToolbar();
+    },
+
+    /**
+     * Protected method that will not generally be called directly. It triggers
+     * a toolbar update by reading the markup state of the current selection in the editor.
+     */
+    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();
+    },
+
+    // private
+    relayBtnCmd: function(btn) {
+        this.relayCmd(btn.getItemId());
+    },
+
+    /**
+     * Executes a Midas editor command on the editor document and performs necessary focus and
+     * toolbar updates. <b>This should only be called after the editor is initialized.</b>
+     * @param {String} cmd The Midas command
+     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
+     */
+    relayCmd: function(cmd, value) {
+        Ext.defer(function() {
+            var me = this;
+            me.focus();
+            me.execCmd(cmd, value);
+            me.updateToolbar();
+        }, 10, this);
+    },
+
+    /**
+     * Executes a Midas editor command directly on the editor document.
+     * For visual commands, you should use {@link #relayCmd} instead.
+     * <b>This should only be called after the editor is initialized.</b>
+     * @param {String} cmd The Midas command
+     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
+     */
+    execCmd : function(cmd, value){
+        var me = this,
+            doc = me.getDoc(),
+            undef;
+        doc.execCommand(cmd, false, value === undef ? null : value);
+        me.syncValue();
+    },
+
+    // private
+    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();
+                }
+            }
+        }
+    },
+
+    /**
+     * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
+     * to insert text.
+     * @param {String} text
+     */
+    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();
+            }
+        }
+    },
+
+    // private
+    fixKeys: function() { // load time branching for fastest keydown performance
+        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; // not needed, so null
+    }(),
+
+    /**
+     * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
+     * @return {Ext.toolbar.Toolbar}
+     */
+    getToolbar : function(){
+        return this.toolbar;
+    },
+
+    /**
+     * Object collection of toolbar tooltips for the buttons in the editor. The key
+     * is the command id associated with that button and the value is a valid QuickTips object.
+     * For example:
+<pre><code>
+{
+    bold : {
+        title: 'Bold (Ctrl+B)',
+        text: 'Make the selected text bold.',
+        cls: 'x-html-editor-tip'
+    },
+    italic : {
+        title: 'Italic (Ctrl+I)',
+        text: 'Make the selected text italic.',
+        cls: 'x-html-editor-tip'
+    },
+    ...
+</code></pre>
+    * @type Object
+     */
+    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'
+        }
+    }
+
+    // hide stuff that is not compatible
+    /**
+     * @event blur
+     * @hide
+     */
+    /**
+     * @event change
+     * @hide
+     */
+    /**
+     * @event focus
+     * @hide
+     */
+    /**
+     * @event specialkey
+     * @hide
+     */
+    /**
+     * @cfg {String} fieldCls @hide
+     */
+    /**
+     * @cfg {String} focusCls @hide
+     */
+    /**
+     * @cfg {String} autoCreate @hide
+     */
+    /**
+     * @cfg {String} inputType @hide
+     */
+    /**
+     * @cfg {String} invalidCls @hide
+     */
+    /**
+     * @cfg {String} invalidText @hide
+     */
+    /**
+     * @cfg {String} msgFx @hide
+     */
+    /**
+     * @cfg {Boolean} allowDomMove  @hide
+     */
+    /**
+     * @cfg {String} applyTo @hide
+     */
+    /**
+     * @cfg {String} readOnly  @hide
+     */
+    /**
+     * @cfg {String} tabIndex  @hide
+     */
+    /**
+     * @method validate
+     * @hide
+     */
+});
+
+/**
+ * @class Ext.form.field.Radio
+ * @extends Ext.form.field.Checkbox
+
+Single radio field. Similar to checkbox, but automatically handles making sure only one radio is checked
+at a time within a group of radios with the same name.
+
+__Labeling:__
+In addition to the {@link Ext.form.Labelable standard field labeling options}, radio buttons
+may be given an optional {@link #boxLabel} which will be displayed immediately to the right of the input. Also
+see {@link Ext.form.RadioGroup} for a convenient method of grouping related radio buttons.
+
+__Values:__
+The main value of a Radio field is a boolean, indicating whether or not the radio is checked.
+
+The following values will check the radio:
+* `true`
+* `'true'`
+* `'1'`
+* `'on'`
+
+Any other value will uncheck it.
+
+In addition to the main boolean value, you may also specify a separate {@link #inputValue}. This will be sent
+as the parameter value when the form is {@link Ext.form.Basic#submit submitted}. You will want to set this
+value if you have multiple radio buttons with the same {@link #name}, as is almost always the case.
+{@img Ext.form.Radio/Ext.form.Radio.png Ext.form.Radio component}
+__Example usage:__
+
+    Ext.create('Ext.form.Panel', {
+        title      : 'Order Form',
+        width      : 300,
+        bodyPadding: 10,
+        renderTo   : Ext.getBody(),
+        items: [
+            {
+                xtype      : 'fieldcontainer',
+                fieldLabel : 'Size',
+                defaultType: 'radiofield',
+                defaults: {
+                    flex: 1
+                },
+                layout: 'hbox',
+                items: [
+                    {
+                        boxLabel  : 'M',
+                        name      : 'size',
+                        inputValue: 'm',
+                        id        : 'radio1'
+                    }, {
+                        boxLabel  : 'L',
+                        name      : 'size',
+                        inputValue: 'l',
+                        id        : 'radio2'
+                    }, {
+                        boxLabel  : 'XL',
+                        name      : 'size',
+                        inputValue: 'xl',
+                        id        : 'radio3'
+                    }
+                ]
+            },
+            {
+                xtype      : 'fieldcontainer',
+                fieldLabel : 'Color',
+                defaultType: 'radiofield',
+                defaults: {
+                    flex: 1
+                },
+                layout: 'hbox',
+                items: [
+                    {
+                        boxLabel  : 'Blue',
+                        name      : 'color',
+                        inputValue: 'blue',
+                        id        : 'radio4'
+                    }, {
+                        boxLabel  : 'Grey',
+                        name      : 'color',
+                        inputValue: 'grey',
+                        id        : 'radio5'
+                    }, {
+                        boxLabel  : 'Black',
+                        name      : 'color',
+                        inputValue: 'black',
+                        id        : 'radio6'
+                    }
+                ]
+            }
+        ],
+        bbar: [
+            {
+                text: 'Smaller Size',
+                handler: function() {
+                    var radio1 = Ext.getCmp('radio1'),
+                        radio2 = Ext.getCmp('radio2'),
+                        radio3 = Ext.getCmp('radio3');
+    
+                    //if L is selected, change to M
+                    if (radio2.getValue()) {
+                        radio1.setValue(true);
+                        return;
+                    }
+    
+                    //if XL is selected, change to L
+                    if (radio3.getValue()) {
+                        radio2.setValue(true);
+                        return;
+                    }
+    
+                    //if nothing is set, set size to S
+                    radio1.setValue(true);
+                }
+            },
+            {
+                text: 'Larger Size',
+                handler: function() {
+                    var radio1 = Ext.getCmp('radio1'),
+                        radio2 = Ext.getCmp('radio2'),
+                        radio3 = Ext.getCmp('radio3');
+    
+                    //if M is selected, change to L
+                    if (radio1.getValue()) {
+                        radio2.setValue(true);
+                        return;
+                    }
+    
+                    //if L is selected, change to XL
+                    if (radio2.getValue()) {
+                        radio3.setValue(true);
+                        return;
+                    }
+    
+                    //if nothing is set, set size to XL
+                    radio3.setValue(true);
+                }
+            },
+            '-',
+            {
+                text: 'Select color',
+                menu: {
+                    indent: false,
+                    items: [
+                        {
+                            text: 'Blue',
+                            handler: function() {
+                                var radio = Ext.getCmp('radio4');
+                                radio.setValue(true);
+                            }
+                        },
+                        {
+                            text: 'Grey',
+                            handler: function() {
+                                var radio = Ext.getCmp('radio5');
+                                radio.setValue(true);
+                            }
+                        },
+                        {
+                            text: 'Black',
+                            handler: function() {
+                                var radio = Ext.getCmp('radio6');
+                                radio.setValue(true);
+                            }
+                        }
+                    ]
+                }
+            }
+        ]
+    });
+
+
+ * @constructor
+ * Creates a new Radio
+ * @param {Object} config Configuration options
+ * @xtype radio
+ * @docauthor Robert Dougan <rob@sencha.com>
+ * @markdown
+ */
+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,
+
+    /**
+     * @cfg {String} uncheckedValue @hide
+     */
+
+    // private
+    inputType: 'radio',
+    ariaRole: 'radio',
+
+    /**
+     * If this radio is part of a group, it will return the selected value
+     * @return {String}
+     */
+    getGroupValue: function() {
+        var selected = this.getManager().getChecked(this.name);
+        return selected ? selected.inputValue : null;
+    },
+
+    /**
+     * @private Handle click on the radio button
+     */
+    onBoxClick: function(e) {
+        var me = this;
+        if (!me.disabled && !me.readOnly) {
+            this.setValue(true);
+        }
+    },
+
+    /**
+     * Sets either the checked/unchecked status of this Radio, or, if a string value
+     * is passed, checks a sibling Radio of the same name whose value is the value specified.
+     * @param value {String/Boolean} Checked value, or the value of the sibling radio button to check.
+     * @return {Ext.form.field.Radio} this
+     */
+    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;
+    },
+
+    /**
+     * Returns the submit value for the checkbox which can be used when submitting forms.
+     * @return {Boolean/null} True if checked, null if not.
+     */
+    getSubmitValue: function() {
+        return this.checked ? this.inputValue : null;
+    },
+
+    // inherit docs
+    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);
+        }
+    },
+
+    // inherit docs
+    beforeDestroy: function(){
+        this.callParent();
+        this.getManager().removeAtKey(this.id);
+    },
+
+    // inherit docs
+    getManager: function() {
+        return Ext.form.RadioManager;
+    }
+});
+
+/**
+ * @class Ext.picker.Time
+ * @extends Ext.view.BoundList
+ * <p>A time picker which provides a list of times from which to choose. This is used by the
+ * {@link Ext.form.field.Time} class to allow browsing and selection of valid times, but could also be used
+ * with other components.</p>
+ * <p>By default, all times starting at midnight and incrementing every 15 minutes will be presented.
+ * This list of available times can be controlled using the {@link #minValue}, {@link #maxValue}, and
+ * {@link #increment} configuration properties. The format of the times presented in the list can be
+ * customized with the {@link #format} config.</p>
+ * <p>To handle when the user selects a time from the list, you can subscribe to the {@link #selectionchange}
+ * event.</p>
+ *
+ * {@img Ext.picker.Time/Ext.picker.Time.png Ext.picker.Time component}
+ *
+ * ## Code
+     new Ext.create('Ext.picker.Time', {
+        width: 60,
+        minValue: Ext.Date.parse('04:30:00 AM', 'h:i:s A'),
+        maxValue: Ext.Date.parse('08:00:00 AM', 'h:i:s A'),
+        renderTo: Ext.getBody()
+    });
+ *
+ * @constructor
+ * Create a new TimePicker
+ * @param {Object} config The config object
+ *
+ * @xtype timepicker
+ */
+Ext.define('Ext.picker.Time', {
+    extend: 'Ext.view.BoundList',
+    alias: 'widget.timepicker',
+    requires: ['Ext.data.Store', 'Ext.Date'],
+
+    /**
+     * @cfg {Date} minValue
+     * The minimum time to be shown in the list of times. This must be a Date object (only the time fields
+     * will be used); no parsing of String values will be done. Defaults to undefined.
+     */
+
+    /**
+     * @cfg {Date} maxValue
+     * The maximum time to be shown in the list of times. This must be a Date object (only the time fields
+     * will be used); no parsing of String values will be done. Defaults to undefined.
+     */
+
+    /**
+     * @cfg {Number} increment
+     * The number of minutes between each time value in the list (defaults to 15).
+     */
+    increment: 15,
+
+    /**
+     * @cfg {String} format
+     * The default time format string which can be overriden for localization support. The format must be
+     * valid according to {@link Ext.Date#parse} (defaults to 'g:i A', e.g., '3:15 PM'). For 24-hour time
+     * format try 'H:i' instead.
+     */
+    format : "g:i A",
+
+    /**
+     * @hide
+     * The field in the implicitly-generated Model objects that gets displayed in the list. This is
+     * an internal field name only and is not useful to change via config.
+     */
+    displayField: 'disp',
+
+    /**
+     * @private
+     * Year, month, and day that all times will be normalized into internally.
+     */
+    initDate: [2008,1,1],
+
+    componentCls: Ext.baseCSSPrefix + 'timepicker',
+
+    /**
+     * @hide
+     */
+    loadingText: '',
+
+    initComponent: function() {
+        var me = this,
+            dateUtil = Ext.Date,
+            clearTime = dateUtil.clearTime,
+            initDate = me.initDate.join('/');
+
+        // Set up absolute min and max for the entire day
+        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();
+    },
+
+    /**
+     * Set the {@link #minValue} and update the list of available times. This must be a Date
+     * object (only the time fields will be used); no parsing of String values will be done.
+     * @param {Date} value
+     */
+    setMinValue: function(value) {
+        this.minValue = value;
+        this.updateList();
+    },
+
+    /**
+     * Set the {@link #maxValue} and update the list of available times. This must be a Date
+     * object (only the time fields will be used); no parsing of String values will be done.
+     * @param {Date} value
+     */
+    setMaxValue: function(value) {
+        this.maxValue = value;
+        this.updateList();
+    },
+
+    /**
+     * @private
+     * Sets the year/month/day of the given Date object to the {@link #initDate}, so that only
+     * the time fields are significant. This makes values suitable for time comparison.
+     * @param {Date} date
+     */
+    normalizeDate: function(date) {
+        var initDate = this.initDate;
+        date.setFullYear(initDate[0], initDate[1] - 1, initDate[2]);
+        return date;
+    },
+
+    /**
+     * Update the list of available times in the list to be constrained within the
+     * {@link #minValue} and {@link #maxValue}.
+     */
+    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;
+        });
+    },
+
+    /**
+     * @private
+     * Creates the internal {@link Ext.data.Store} that contains the available times. The store
+     * is loaded with all possible times, and it is later filtered to hide those times outside
+     * the minValue/maxValue.
+     */
+    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
+        });
+    }
+
+});
+
+/**
+ * @class Ext.form.field.Time
+ * @extends Ext.form.field.Picker
+ * <p>Provides a time input field with a time dropdown and automatic time validation.</p>
+ * <p>This field recognizes and uses JavaScript Date objects as its main {@link #value} type (only the time
+ * portion of the date is used; the month/day/year are ignored). In addition, it recognizes string values which
+ * are parsed according to the {@link #format} and/or {@link #altFormats} configs. These may be reconfigured
+ * to use time formats appropriate for the user's locale.</p>
+ * <p>The field may be limited to a certain range of times by using the {@link #minValue} and {@link #maxValue}
+ * configs, and the interval between time options in the dropdown can be changed with the {@link #increment} config.</p>
+ * {@img Ext.form.Time/Ext.form.Time.png Ext.form.Time component}
+ * <p>Example usage:</p>
+ * <pre><code>
+    Ext.create('Ext.form.Panel', {
+        title: 'Time Card',
+        width: 300,
+        bodyPadding: 10,
+        renderTo: Ext.getBody(),        
+        items: [{
+            xtype: 'timefield',
+            name: 'in',
+            fieldLabel: 'Time In',
+            minValue: '6:00 AM',
+            maxValue: '8:00 PM',
+            increment: 30,
+            anchor: '100%'
+        }, {
+            xtype: 'timefield',
+            name: 'out',
+            fieldLabel: 'Time Out',
+            minValue: '6:00 AM',
+            maxValue: '8:00 PM',
+            increment: 30,
+            anchor: '100%'
+       }]
+    });
+</code></pre>
+ * @constructor
+ * Create a new Time field
+ * @param {Object} config
+ * @xtype timefield
+ */
+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'],
+
+    /**
+     * @cfg {String} triggerCls
+     * An additional CSS class used to style the trigger button.  The trigger will always get the
+     * {@link #triggerBaseCls} by default and <tt>triggerCls</tt> will be <b>appended</b> if specified.
+     * Defaults to <tt>'x-form-time-trigger'</tt> for the Time field trigger.
+     */
+    triggerCls: Ext.baseCSSPrefix + 'form-time-trigger',
+
+    /**
+     * @cfg {Date/String} minValue
+     * The minimum allowed time. Can be either a Javascript date object with a valid time value or a string
+     * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
+     */
+
+    /**
+     * @cfg {Date/String} maxValue
+     * The maximum allowed time. Can be either a Javascript date object with a valid time value or a string
+     * time in a valid format -- see {@link #format} and {@link #altFormats} (defaults to undefined).
+     */
+
+    /**
+     * @cfg {String} minText
+     * The error text to display when the entered time is before {@link #minValue} (defaults to
+     * 'The time in this field must be equal to or after {0}').
+     */
+    minText : "The time in this field must be equal to or after {0}",
+
+    /**
+     * @cfg {String} maxText
+     * The error text to display when the entered time is after {@link #maxValue} (defaults to
+     * 'The time in this field must be equal to or before {0}').
+     */
+    maxText : "The time in this field must be equal to or before {0}",
+
+    /**
+     * @cfg {String} invalidText
+     * The error text to display when the time in the field is invalid (defaults to
+     * '{value} is not a valid time').
+     */
+    invalidText : "{0} is not a valid time",
+
+    /**
+     * @cfg {String} format
+     * The default time format string which can be overriden for localization support.  The format must be
+     * valid according to {@link Ext.Date#parse} (defaults to 'g:i A', e.g., '3:15 PM').  For 24-hour time
+     * format try 'H:i' instead.
+     */
+    format : "g:i A",
+
+    /**
+     * @cfg {String} submitFormat The date format string which will be submitted to the server.
+     * The format must be valid according to {@link Ext.Date#parse} (defaults to <tt>{@link #format}</tt>).
+     */
+
+    /**
+     * @cfg {String} altFormats
+     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
+     * format (defaults to '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').
+     */
+    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",
+
+    /**
+     * @cfg {Number} increment
+     * The number of minutes between each time value in the list (defaults to 15).
+     */
+    increment: 15,
+
+    /**
+     * @cfg {Number} pickerMaxHeight
+     * The maximum height of the {@link Ext.picker.Time} dropdown. Defaults to 300.
+     */
+    pickerMaxHeight: 300,
+
+    /**
+     * @cfg {Boolean} selectOnTab
+     * Whether the Tab key should select the currently highlighted item. Defaults to <tt>true</tt>.
+     */
+    selectOnTab: true,
+
+    /**
+     * @private
+     * This is the date to use when generating time values in the absence of either minValue
+     * or maxValue.  Using the current date causes DST issues on DST boundary dates, so this is an
+     * arbitrary "safe" date that can be any date aside from DST boundary dates.
+     */
+    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 a String value was supplied, try to convert it to a proper Date object
+        if (Ext.isString(value)) {
+            me.value = me.rawToValue(value);
+        }
+
+        me.callParent();
+    },
+
+    /**
+     * Replaces any existing {@link #minValue} with the new time and refreshes the picker's range.
+     * @param {Date/String} value The minimum time that can be selected
+     */
+    setMinValue: function(value) {
+        var me = this,
+            picker = me.picker;
+        me.setLimit(value, true);
+        if (picker) {
+            picker.setMinValue(me.minValue);
+        }
+    },
+
+    /**
+     * Replaces any existing {@link #maxValue} with the new time and refreshes the picker's range.
+     * @param {Date/String} value The maximum time that can be selected
+     */
+    setMaxValue: function(value) {
+        var me = this,
+            picker = me.picker;
+        me.setLimit(value, false);
+        if (picker) {
+            picker.setMaxValue(me.maxValue);
+        }
+    },
+
+    /**
+     * @private
+     * Updates either the min or max value. Converts the user's value into a Date object whose
+     * year/month/day is set to the {@link #initDate} so that only the time fields are significant.
+     */
+    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));
+    },
+
+    /**
+     * Runs all of Time's validations and returns an array of any errors. Note that this first
+     * runs Text's validations, so the returned array is an amalgamation of all field errors.
+     * The additional validation checks are testing that the time format is valid, that the chosen
+     * time is within the {@link #minValue} and {@link #maxValue} constraints set.
+     * @param {Mixed} value The value to get errors for (defaults to the current field value)
+     * @return {Array} All validation errors for this field
+     */
+    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) { // if it's blank and textfield didn't flag it then it's valid
+             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);
+    },
+
+    /**
+     * @private
+     * Parses an input value into a valid Date object.
+     * @param {String/Date} value
+     */
+    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)) {
+            // assume we've been given a full date
+            result = utilDate.parse(value, format);
+        } else {
+            // Use our initial safe date
+            parsedDate = utilDate.parse(me.initDate + ' ' + value, me.initDateFormat + ' ' + format);
+            if (parsedDate) {
+                result = parsedDate;
+            }
+        }
+        return result;
+    },
+
+    // @private
+    getSubmitValue: function() {
+        var me = this,
+            format = me.submitFormat || me.format,
+            value = me.getValue();
+
+        return value ? Ext.Date.format(value, format) : null;
+    },
+
+    /**
+     * @private
+     * Creates the {@link Ext.picker.Time}
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * Enables the key nav for the Time picker when it is expanded.
+     * TODO this is largely the same logic as ComboBox, should factor out.
+     */
+    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();
+                    }
+                    // Tab key event is allowed to propagate to field
+                    return true;
+                }
+            });
+            // stop tab monitoring from Ext.form.field.Trigger so it doesn't short-circuit selectOnTab
+            if (selectOnTab) {
+                me.ignoreMonitorTab = true;
+            }
+        }
+        Ext.defer(keyNav.enable, 1, keyNav); //wait a bit so it doesn't react to the down arrow opening the picker
+
+        // Highlight the last selected item and scroll it into view
+        if (lastSelected) {
+            itemNode = picker.getNode(lastSelected);
+            if (itemNode) {
+                picker.highlightItem(itemNode);
+                picker.el.scrollChildIntoView(itemNode, false);
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Disables the key nav for the Time picker when it is collapsed.
+     */
+    onCollapse: function() {
+        var me = this,
+            keyNav = me.pickerKeyNav;
+        if (keyNav) {
+            keyNav.disable();
+            me.ignoreMonitorTab = false;
+        }
+    },
+
+    /**
+     * @private
+     * Handles a time being selected from the Time picker.
+     */
+    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();
+    }
+});
+
+
+/**
+ * @class Ext.grid.CellEditor
+ * @extends Ext.Editor
+ * Internal utility class that provides default configuration for cell editing.
+ * @ignore
+ */
+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);
+    },
+    
+    /**
+     * @private
+     * Hide the grid cell when editor is shown.
+     */
+    onShow: function() {
+        var first = this.boundEl.first();
+        if (first) {
+            first.hide();
+        }
+        this.callParent(arguments);
+    },
+    
+    /**
+     * @private
+     * Show grid cell when editor is hidden.
+     */
+    onHide: function() {
+        var first = this.boundEl.first();
+        if (first) {
+            first.show();
+        }
+        this.callParent(arguments);
+    },
+    
+    /**
+     * @private
+     * Fix checkbox blur when it is clicked.
+     */
+    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);
+        }
+    },
+    
+    /**
+     * @private
+     * Because when checkbox is clicked it loses focus  completeEdit is bypassed.
+     */
+    onCheckBoxMouseDown: function() {
+        this.completeEdit = Ext.emptyFn;
+    },
+    
+    /**
+     * @private
+     * Restore checkbox focus and completeEdit method.
+     */
+    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
+});
+/**
+ * @class Ext.grid.ColumnLayout
+ * @extends Ext.layout.container.HBox
+ * @private
+ *
+ * <p>This class is used only by the grid's HeaderContainer docked child.</p>
+ *
+ * <p>This class adds the ability to shrink the vertical size of the inner container element back if a grouped
+ * column header has all its child columns dragged out, and the whole HeaderContainer needs to shrink back down.</p>
+ *
+ * <p>It also enforces the grid's HeaderContainer's forceFit config by, after every calaculateChildBoxes call, converting
+ * all pixel widths into flex values, so that propertions are maintained upon width change of the grid.</p>
+ *
+ * <p>Also, after every layout, after all headers have attained their 'stretchmax' height, it goes through and calls
+ * <code>setPadding</code> on the columns so that they lay out correctly. TODO: implement a ColumnHeader component
+ * layout which takes responsibility for this, and will run upon resize.</p>
+ */
+Ext.define('Ext.grid.ColumnLayout', {
+    extend: 'Ext.layout.container.HBox',
+    alias: 'layout.gridcolumn',
+    type : 'column',
+
+    // Height-stretched innerCt must be able to revert back to unstretched height
+    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);
+
+        // Size to a sane minimum height before possibly being stretched to accommodate grouped headers
+        me.innerCt.setHeight(23);
+
+        // Unstretch child items before the layout which stretches them.
+        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;
+    },
+
+    // Override to enforce the forceFit config.
+    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 configured forceFit then all columns will be flexed
+            if (me.owner.forceFit) {
+
+                for (; i < len; i++) {
+                    box = boxes[i];
+                    item = box.component;
+
+                    // Set a sane minWidth for the Box layout to be able to squeeze flexed Headers down to.
+                    item.minWidth = Ext.grid.plugin.HeaderResizer.prototype.minColWidth;
+
+                    // For forceFit, just use allocated width as the flex value, and the proportions
+                    // will end up the same whatever HeaderContainer width they are being forced into.
+                    item.flex = box.width;
+                }
+
+                // Recalculate based upon all columns now being flexed instead of sized.
+                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);
+
+        // Set up padding in items
+        if (me.align == 'stretchmax') {
+            for (; i < len; i++) {
+                items[i].setPadding();
+            }
+        }
+    },
+
+    // FIX: when flexing we actually don't have enough space as we would
+    // typically because of the scrollOffset on the GridView, must reserve this
+    updateInnerCtSize: function(tSize, calcs) {
+        var me    = this,
+            extra = 0;
+
+        // Columns must not account for scroll offset
+        if (!me.isColumn && calcs.meta.tooNarrow) {
+            if (
+                Ext.isWebKit ||
+                Ext.isGecko ||
+                (Ext.isIEQuirks && (Ext.isIE6 || Ext.isIE7 || Ext.isIE8))
+            ) {
+                extra = 1;
+            // IE6-8 not quirks
+            } else if (Ext.isIE6 || Ext.isIE7 || Ext.isIE8) {
+                extra = 2;
+            }
+            
+            // this is the 1px accounted for in the Scroller when subtracting 1px.
+            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();
+        }
+    }
+});
+/**
+ * @class Ext.grid.LockingView
+ * This class is used internally to provide a single interface when using
+ * a locking grid. Internally, the locking grid creates 2 separate grids,
+ * so this class is used to map calls appropriately.
+ * @ignore
+ */
+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);
+        
+        // relay events
+        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){
+        // default to the normal view
+        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;
+    },
+    
+    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);
+    }
+       
+});
+/**
+ * @class Ext.grid.Lockable
+ * @private
+ *
+ * Lockable is a private mixin which injects lockable behavior into any
+ * TablePanel subclass such as GridPanel or TreePanel. TablePanel will
+ * automatically inject the Ext.grid.Lockable mixin in when one of the
+ * these conditions are met:
+ * - The TablePanel has the lockable configuration set to true
+ * - One of the columns in the TablePanel has locked set to true/false
+ *
+ * Each TablePanel subclass *must* register an alias. It should have an array
+ * of configurations to copy to the 2 separate tablepanel's that will be generated
+ * to note what configurations should be copied. These are named normalCfgCopy and
+ * lockedCfgCopy respectively.
+ *
+ * Columns which are locked must specify a fixed width. They do *NOT* support a
+ * flex width.
+ *
+ * Configurations which are specified in this class will be available on any grid or
+ * tree which is using the lockable functionality.
+ */
+Ext.define('Ext.grid.Lockable', {
+    
+    requires: ['Ext.grid.LockingView'],
+    
+    /**
+     * @cfg {Boolean} syncRowHeight Synchronize rowHeight between the normal and
+     * locked grid view. This is turned on by default. If your grid is guaranteed
+     * to have rows of all the same height, you should set this to false to
+     * optimize performance.
+     */
+    syncRowHeight: true,
+    
+    /**
+     * @cfg {String} subGridXType The xtype of the subgrid to specify. If this is
+     * not specified lockable will determine the subgrid xtype to create by the
+     * following rule. Use the superclasses xtype if the superclass is NOT
+     * tablepanel, otherwise use the xtype itself.
+     */
+    
+    /**
+     * @cfg {Object} lockedViewConfig A view configuration to be applied to the
+     * locked side of the grid. Any conflicting configurations between lockedViewConfig
+     * and viewConfig will be overwritten by the lockedViewConfig.
+     */
+
+    /**
+     * @cfg {Object} normalViewConfig A view configuration to be applied to the
+     * normal/unlocked side of the grid. Any conflicting configurations between normalViewConfig
+     * and viewConfig will be overwritten by the normalViewConfig.
+     */
+    
+    // private variable to track whether or not the spacer is hidden/visible
+    spacerHidden: true,
+    
+    // i8n text
+    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 will be invoked before initComponent's parent class implementation
+    // is called, so throughout this method this. are configurations
+    injectLockable: function() {
+        // ensure lockable is set to true in the TablePanel
+        this.lockable = true;
+        // Instruct the TablePanel it already has a view and not to create one.
+        // We are going to aggregate 2 copies of whatever TablePanel we are using
+        this.hasView = true;
+
+        var me = this,
+            // xtype of this class, 'treepanel' or 'gridpanel'
+            // (Note: this makes it a requirement that any subclass that wants to use lockable functionality needs to register an
+            // alias.)
+            xtype = me.determineXTypeToCreate(),
+            // share the selection model
+            selModel = me.getSelectionModel(),
+            lockedGrid = {
+                xtype: xtype,
+                // Lockable does NOT support animations for Tree
+                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');
+        
+        // copy appropriate configurations to the respective
+        // aggregated tablepanel instances and then delete them
+        // from the master tablepanel.
+        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;
+        
+        // normal grid should flex the rest of the width
+        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){
+        // split apart normal and lockedWidths
+        var i = 0,
+            len = columns.length,
+            lockedWidth = 0,
+            lockedHeaders = [],
+            normalHeaders = [],
+            column;
+            
+        for (; i < len; ++i) {
+            column = columns[i];
+            // mark the column as processed so that the locked attribute does not
+            // trigger trying to aggregate the columns again.
+            column.processed = true;
+            if (column.locked) {
+                // <debug>
+                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.");
+                }
+                // </debug>
+                lockedWidth += column.width;
+                lockedHeaders.push(column);
+            } else {
+                normalHeaders.push(column);
+            }
+        }
+        return {
+            lockedWidth: lockedWidth,
+            locked: lockedHeaders,
+            normal: normalHeaders    
+        };
+    },
+    
+    // create a new spacer after the table is refreshed
+    onLockedGridAfterLayout: function() {
+        var me         = this,
+            lockedView = me.lockedGrid.getView();
+        lockedView.on({
+            refresh: me.createSpacer,
+            beforerefresh: me.destroySpacer,
+            scope: me
+        });
+    },
+    
+    // trigger a pseudo refresh on the normal side
+    onLockedHeaderMove: function() {
+        if (this.syncRowHeight) {
+            this.onNormalGridAfterRefresh();
+        }
+    },
+    
+    // trigger a pseudo refresh on the locked side
+    onNormalHeaderMove: function() {
+        if (this.syncRowHeight) {
+            this.onLockedGridAfterRefresh();
+        }
+    },
+    
+    // create a spacer in lockedsection and store a reference
+    // TODO: Should destroy before refreshing content
+    createSpacer: function() {
+        var me   = this,
+            // This affects scrolling all the way to the bottom of a locked grid
+            // additional test, sort a column and make sure it synchronizes
+            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;
+        }
+    },
+    
+    // cache the heights of all locked rows and sync rowheights
+    onLockedGridAfterRefresh: function() {
+        var me     = this,
+            view   = me.lockedGrid.getView(),
+            el     = view.el,
+            rowEls = el.query(view.getItemSelector()),
+            ln     = rowEls.length,
+            i = 0;
+            
+        // reset heights each time.
+        me.lockedHeights = [];
+        
+        for (; i < ln; i++) {
+            me.lockedHeights[i] = rowEls[i].clientHeight;
+        }
+        me.syncRowHeights();
+    },
+    
+    // cache the heights of all normal rows and sync rowheights
+    onNormalGridAfterRefresh: function() {
+        var me     = this,
+            view   = me.normalGrid.getView(),
+            el     = view.el,
+            rowEls = el.query(view.getItemSelector()),
+            ln     = rowEls.length,
+            i = 0;
+            
+        // reset heights each time.
+        me.normalHeights = [];
+        
+        for (; i < ln; i++) {
+            me.normalHeights[i] = rowEls[i].clientHeight;
+        }
+        me.syncRowHeights();
+    },
+    
+    // rows can get bigger/smaller
+    onLockedGridAfterUpdate: function(record, index, node) {
+        this.lockedHeights[index] = node.clientHeight;
+        this.syncRowHeights();
+    },
+    
+    // rows can get bigger/smaller
+    onNormalGridAfterUpdate: function(record, index, node) {
+        this.normalHeights[index] = node.clientHeight;
+        this.syncRowHeights();
+    },
+    
+    // match the rowheights to the biggest rowheight on either
+    // side
+    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;
+
+        // ensure there are an equal num of locked and normal
+        // rows before synchronization
+        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());
+
+            // loop thru all of the heights and sync to the other side
+            for (; i < ln; i++) {
+                // ensure both are numbers
+                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]);
+                    }
+                }
+            }
+
+            // invalidate the scroller and sync the scrollers
+            me.normalGrid.invalidateScroller();
+            
+            // synchronize the view with the scroller, if we have a virtualScrollTop
+            // then the user is using a PagingScroller 
+            if (vertScroller && vertScroller.setViewScrollTop) {
+                vertScroller.setViewScrollTop(me.virtualScrollTop);
+            } else {
+                // We don't use setScrollTop here because if the scrollTop is
+                // set to the exact same value some browsers won't fire the scroll
+                // event. Instead, we directly set the scrollTop.
+                scrollTop = normalView.el.dom.scrollTop;
+                normalView.el.dom.scrollTop = scrollTop;
+                lockedView.el.dom.scrollTop = scrollTop;
+            }
+            
+            // reset the heights
+            me.lockedHeights = [];
+            me.normalHeights = [];
+        }
+    },
+    
+    // track when scroller is shown
+    onScrollerShow: function(scroller, direction) {
+        if (direction === 'horizontal') {
+            this.spacerHidden = false;
+            this.spacerEl.removeCls(Ext.baseCSSPrefix + 'hidden');
+        }
+    },
+    
+    // track when scroller is hidden
+    onScrollerHide: function(scroller, direction) {
+        if (direction === 'horizontal') {
+            this.spacerHidden = true;
+            this.spacerEl.addCls(Ext.baseCSSPrefix + 'hidden');
+        }
+    },
+
+    
+    // inject Lock and Unlock text
+    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,
+            // TODO: Refactor to use Ext.baseCSSPrefix
+            unlockCls     = 'xg-hmenu-unlock',
+            lockCls       = 'xg-hmenu-lock',
+            unlockHandler = Ext.Function.bind(me.onUnlockMenuClick, me),
+            lockHandler   = Ext.Function.bind(me.onLockMenuClick, me);
+        
+        // runs in the scope of headerCt
+        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;
+        };
+    },
+    
+    // going from unlocked section to locked
+    /**
+     * Locks the activeHeader as determined by which menu is open OR a header
+     * as specified.
+     * @param {Ext.grid.column.Column} header (Optional) Header to unlock from the locked section. Defaults to the header which has the menu open currently.
+     * @param {Number} toIdx (Optional) The index to move the unlocked header to. Defaults to appending as the last item.
+     * @private
+     */
+    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 column was previously flexed, get/set current width
+        // and remove the flex
+        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();
+    },
+    
+    // adjust the locked section to the width of its respective
+    // headerCt
+    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) {
+            // no real header, and silence the event so we dont get into an
+            // infinite loop
+            this.normalGrid.headerCt.clearOtherSortStates(null, true);
+        }
+    },
+    
+    onNormalHeaderSortChange: function(headerCt, header, sortState) {
+        if (sortState) {
+            // no real header, and silence the event so we dont get into an
+            // infinite loop
+            this.lockedGrid.headerCt.clearOtherSortStates(null, true);
+        }
+    },
+    
+    // going from locked section to unlocked
+    /**
+     * Unlocks the activeHeader as determined by which menu is open OR a header
+     * as specified.
+     * @param {Ext.grid.column.Column} header (Optional) Header to unlock from the locked section. Defaults to the header which has the menu open currently.
+     * @param {Number} toIdx (Optional) The index to move the unlocked header to. Defaults to 0.
+     * @private
+     */
+    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();
+    },
+    
+    // we want to totally override the reconfigure behaviour here, since we're creating 2 sub-grids
+    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();
+        }
+    }
+});
+
+/**
+ * @class Ext.grid.Scroller
+ * @extends Ext.Component
+ *
+ * Docked in an Ext.grid.Panel, controls virtualized scrolling and synchronization
+ * across different sections.
+ *
+ * @private
+ */
+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',
+            // Subtracting 2px would give us a perfect fit of the scroller
+            // however, some browsers wont allow us to scroll content thats not
+            // visible, therefore we use 1px.
+            // Note: This 1px offset matches code in Ext.grid.ColumnLayout when
+            // reserving room for the scrollbar
+            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') {
+            // TODO: Must gravitate to a single region..
+            // Horizontal scrolling only scrolls virtualized region
+            var items  = owner.query('tableview'),
+                center = items[1] || items[0];
+            
+            if (!center) {
+                return false;
+            }
+            // center is not guaranteed to have content, such as when there
+            // are zero rows in the grid/tree. We read the width from the
+            // headerCt instead.
+            width = center.headerCt.getFullWidth();
+            
+            if (Ext.isIEQuirks) {
+                width--;
+            }
+            // Account for the 1px removed in Scroller.
+            width--;
+        } else {            
+            view = owner.down('tableview:not([lockableInjected])');
+            if (!view) {
+                return false;
+            }
+            tbl = view.el;
+            if (!tbl) {
+                return false;
+            }
+            
+            // needs to also account for header and scroller (if still in picture)
+            // should calculate from headerCt.
+            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);
+        
+            // BrowserBug: IE7
+            // This makes the scroller enabled, when initially rendering.
+            elDom.scrollTop = elDom.scrollTop;
+        }
+    },
+
+    onOwnerAfterLayout: function(owner, layout) {
+        this.invalidate();
+    },
+
+    /**
+     * Sets the scrollTop and constrains the value between 0 and max.
+     * @param {Number} scrollTop
+     * @return {Number} The resulting scrollTop value after being constrained
+     */
+    setScrollTop: function(scrollTop) {
+        if (this.el) {
+            var elDom = this.el.dom;
+            return elDom.scrollTop = Ext.Number.constrain(scrollTop, 0, elDom.scrollHeight - elDom.clientHeight);
+        }
+    },
+
+    /**
+     * Sets the scrollLeft and constrains the value between 0 and max.
+     * @param {Number} scrollLeft
+     * @return {Number} The resulting scrollLeft value after being constrained
+     */
+    setScrollLeft: function(scrollLeft) {
+        if (this.el) {
+            var elDom = this.el.dom;
+            return elDom.scrollLeft = Ext.Number.constrain(scrollLeft, 0, elDom.scrollWidth - elDom.clientWidth);
+        }
+    },
+
+    /**
+     * Scroll by deltaY
+     * @param {Number} delta
+     * @return {Number} The resulting scrollTop value
+     */
+    scrollByDeltaY: function(delta) {
+        if (this.el) {
+            var elDom = this.el.dom;
+            return this.setScrollTop(elDom.scrollTop + delta);
+        }
+    },
+
+    /**
+     * Scroll by deltaX
+     * @param {Number} delta
+     * @return {Number} The resulting scrollLeft value
+     */
+    scrollByDeltaX: function(delta) {
+        if (this.el) {
+            var elDom = this.el.dom;
+            return this.setScrollLeft(elDom.scrollLeft + delta);
+        }
+    },
+    
+    
+    /**
+     * Scroll to the top.
+     */
+    scrollToTop : function(){
+        this.setScrollTop(0);
+    },
+    
+    // synchronize the scroller with the bound gridviews
+    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;
+    }
+});
+
+
+/**
+ * @class Ext.grid.PagingScroller
+ * @extends Ext.grid.Scroller
+ *
+ * @private
+ */
+Ext.define('Ext.grid.PagingScroller', {
+    extend: 'Ext.grid.Scroller',
+    alias: 'widget.paginggridscroller',
+    //renderTpl: null,
+    //tpl: [
+    //    '<tpl for="pages">',
+    //        '<div class="' + Ext.baseCSSPrefix + 'stretcher" style="width: {width}px;height: {height}px;"></div>',
+    //    '</tpl>'
+    //],
+    /**
+     * @cfg {Number} percentageFromEdge This is a number above 0 and less than 1 which specifies
+     * at what percentage to begin fetching the next page. For example if the pageSize is 100
+     * and the percentageFromEdge is the default of 0.35, the paging scroller will prefetch pages
+     * when scrolling up between records 0 and 34 and when scrolling down between records 65 and 99.
+     */
+    percentageFromEdge: 0.35,
+    
+    /**
+     * @cfg {Number} scrollToLoadBuffer This is the time in milliseconds to buffer load requests
+     * when scrolling the PagingScrollbar.
+     */
+    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;
+        // this should never happen
+        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 {
+            // adjust to visible
+            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;
+        
+        // BrowserBug: clientHeight reports 0 in IE9 StrictMode
+        // Instead we are using offsetHeight and hardcoding borders
+        if (Ext.isIE9 && Ext.isStrict) {
+            clientHeight = scrollerElDom.offsetHeight + 2;
+        }
+
+        // This should always be zero or greater than zero but staying
+        // safe and less than 0 we'll scroll to the bottom.        
+        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 = visibleStart,
+            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) {
+            // end of request was past what the total is, grab from the end back a pageSize
+            if (requestEnd > totalCount - 1) {
+                this.cancelLoad();
+                if (store.rangeSatisfied(totalCount - pageSize, totalCount - 1)) {
+                    me.syncScroll = true;
+                }
+                store.guaranteeRange(totalCount - pageSize, totalCount - 1);
+            // Out of range, need to reset the current data set
+            } else if (visibleStart < guaranteedStart || visibleEnd > guaranteedEnd) {
+                if (store.rangeSatisfied(requestStart, requestEnd)) {
+                    this.cancelLoad();
+                    store.guaranteeRange(requestStart, requestEnd);
+                } else {
+                    store.mask();
+                    me.attemptLoad(requestStart, requestEnd);
+                }
+                // dont sync the scroll view immediately, sync after the range has been guaranteed
+                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() {
+        // Use the direct ownerCt here rather than the scrollerOwner
+        // because we are calculating widths/heights.
+        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) {
+            //Ext.Error.raise("Calculated scrollTop was larger than maxScrollTop");
+            return;
+            // calcScrollTop = maxScrollTop;
+        }
+        for (; i < len; i++) {
+            items[i].el.dom.scrollTop = calcScrollTop;
+        }
+    }
+});
+
+
+/**
+ * @class Ext.panel.Table
+ * @extends Ext.panel.Panel
+ * @xtype tablepanel
+ * @private
+ * @author Nicolas Ferrero
+ * TablePanel is a private class and the basis of both TreePanel and GridPanel.
+ *
+ * TablePanel aggregates:
+ *
+ *  - a Selection Model
+ *  - a View
+ *  - a Store
+ *  - Scrollers
+ *  - Ext.grid.header.Container
+ *
+ */
+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',
+    /**
+     * Boolean to indicate that a view has been injected into the panel.
+     * @property hasView
+     */
+    hasView: false,
+
+    // each panel should dictate what viewType and selType to use
+    viewType: null,
+    selType: 'rowmodel',
+
+    /**
+     * @cfg {Number} scrollDelta
+     * Number of pixels to scroll when scrolling with mousewheel.
+     * Defaults to 40.
+     */
+    scrollDelta: 40,
+
+    /**
+     * @cfg {String/Boolean} scroll
+     * Valid values are 'both', 'horizontal' or 'vertical'. true implies 'both'. false implies 'none'.
+     * Defaults to true.
+     */
+    scroll: true,
+
+    /**
+     * @cfg {Array} columns
+     * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this grid. Each
+     * column definition provides the header text for the column, and a definition of where the data for that column comes from.
+     */
+
+    /**
+     * @cfg {Boolean} forceFit
+     * Specify as <code>true</code> to force the columns to fit into the available width. Headers are first sized according to configuration, whether that be
+     * a specific width, or flex. Then they are all proportionally changed in width so that the entire content width is used..
+     */
+
+    /**
+     * @cfg {Boolean} hideHeaders
+     * Specify as <code>true</code> to hide the headers.
+     */
+
+    /**
+     * @cfg {Boolean} sortableColumns
+     * Defaults to true. Set to false to disable column sorting via clicking the
+     * header and via the Sorting menu items.
+     */
+    sortableColumns: true,
+
+    verticalScrollDock: 'right',
+    verticalScrollerType: 'gridscroller',
+
+    horizontalScrollerPresentCls: Ext.baseCSSPrefix + 'horizontal-scroller-present',
+    verticalScrollerPresentCls: Ext.baseCSSPrefix + 'vertical-scroller-present',
+
+    // private property used to determine where to go down to find views
+    // this is here to support locking.
+    scrollerOwner: true,
+
+    invalidateScrollerOnRefresh: true,
+    
+    enableColumnMove: true,
+    enableColumnResize: true,
+
+
+    initComponent: function() {
+        //<debug>
+        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.");
+        }
+        //</debug>
+
+        var me          = this,
+            scroll      = me.scroll,
+            vertical    = false,
+            horizontal  = false,
+            headerCtCfg = me.columns || me.colModel,
+            i           = 0,
+            view,
+            border = me.border;
+
+        // Set our determinScrollbars method to reference a buffered call to determinScrollbars which fires on a 30ms buffer.
+        me.determineScrollbars = Ext.Function.createBuffered(me.determineScrollbars, 30);
+        me.injectView = Ext.Function.createBuffered(me.injectView, 30);
+
+        if (me.hideHeaders) {
+            border = false;
+        }
+
+        // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
+        // Either way, we extract a columns property referencing an array of Column definitions.
+        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 any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
+             // special view will be injected by the Ext.grid.Lockable mixin, so no processing of .
+             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(
+            /**
+             * @event scrollerhide
+             * Fires when a scroller is hidden
+             * @param {Ext.grid.Scroller} scroller
+             * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
+             */
+            'scrollerhide',
+            /**
+             * @event scrollershow
+             * Fires when a scroller is shown
+             * @param {Ext.grid.Scroller} scroller
+             * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
+             */
+            'scrollershow'
+        );
+
+        me.bodyCls = me.bodyCls || '';
+        me.bodyCls += (' ' + me.extraBodyCls);
+
+        // autoScroll is not a valid configuration
+        delete me.autoScroll;
+
+        // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)
+        // than a special lockable view containing 2 side-by-side grids will have been injected so we do not need to set up any UI.
+        if (!me.hasView) {
+
+            // If we were not configured with a ready-made headerCt (either by direct config with a headerCt property, or by passing
+            // a HeaderContainer instance as the 'columns' property, then go ahead and create one from the config object created above.
+            if (!me.headerCt) {
+                me.headerCt = Ext.create('Ext.grid.header.Container', headerCtCfg);
+            }
+
+            // Extract the array of Column objects
+            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');
+                // IE Quirks Mode fix
+                // If hidden configuration option was used, several layout calculations will be bypassed.
+                if (Ext.isIEQuirks) {
+                    me.headerCt.style = {
+                        display: 'none'
+                    };
+                }
+            }
+
+            // turn both on.
+            if (scroll === true || scroll === 'both') {
+                vertical = horizontal = true;
+            } else if (scroll === 'horizontal') {
+                horizontal = true;
+            } else if (scroll === 'vertical') {
+                vertical = true;
+            // All other values become 'none' or false.
+            } 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;
+
+            // AbstractDataView will look up a Store configured as an object
+            // getView converts viewConfig into a View instance
+            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, [
+                    /**
+                     * @event beforeitemmousedown
+                     * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemmousedown',
+                    /**
+                     * @event beforeitemmouseup
+                     * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemmouseup',
+                    /**
+                     * @event beforeitemmouseenter
+                     * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemmouseenter',
+                    /**
+                     * @event beforeitemmouseleave
+                     * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemmouseleave',
+                    /**
+                     * @event beforeitemclick
+                     * Fires before the click event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemclick',
+                    /**
+                     * @event beforeitemdblclick
+                     * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemdblclick',
+                    /**
+                     * @event beforeitemcontextmenu
+                     * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforeitemcontextmenu',
+                    /**
+                     * @event itemmousedown
+                     * Fires when there is a mouse down on an item
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemmousedown',
+                    /**
+                     * @event itemmouseup
+                     * Fires when there is a mouse up on an item
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemmouseup',
+                    /**
+                     * @event itemmouseenter
+                     * Fires when the mouse enters an item.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemmouseenter',
+                    /**
+                     * @event itemmouseleave
+                     * Fires when the mouse leaves an item.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemmouseleave',
+                    /**
+                     * @event itemclick
+                     * Fires when an item is clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemclick',
+                    /**
+                     * @event itemdblclick
+                     * Fires when an item is double clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemdblclick',
+                    /**
+                     * @event itemcontextmenu
+                     * Fires when an item is right clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.data.Model} record The record that belongs to the item
+                     * @param {HTMLElement} item The item's element
+                     * @param {Number} index The item's index
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'itemcontextmenu',
+                    /**
+                     * @event beforecontainermousedown
+                     * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainermousedown',
+                    /**
+                     * @event beforecontainermouseup
+                     * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainermouseup',
+                    /**
+                     * @event beforecontainermouseover
+                     * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainermouseover',
+                    /**
+                     * @event beforecontainermouseout
+                     * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainermouseout',
+                    /**
+                     * @event beforecontainerclick
+                     * Fires before the click event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainerclick',
+                    /**
+                     * @event beforecontainerdblclick
+                     * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainerdblclick',
+                    /**
+                     * @event beforecontainercontextmenu
+                     * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'beforecontainercontextmenu',
+                    /**
+                     * @event containermouseup
+                     * Fires when there is a mouse up on the container
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containermouseup',
+                    /**
+                     * @event containermouseover
+                     * Fires when you move the mouse over the container.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containermouseover',
+                    /**
+                     * @event containermouseout
+                     * Fires when you move the mouse out of the container.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containermouseout',
+                    /**
+                     * @event containerclick
+                     * Fires when the container is clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containerclick',
+                    /**
+                     * @event containerdblclick
+                     * Fires when the container is double clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containerdblclick',
+                    /**
+                     * @event containercontextmenu
+                     * Fires when the container is right clicked.
+                     * @param {Ext.view.View} this
+                     * @param {Ext.EventObject} e The raw event object
+                     */
+                    'containercontextmenu',
+
+                    /**
+                     * @event selectionchange
+                     * Fires when the selected nodes change. Relayed event from the underlying selection model.
+                     * @param {Ext.view.View} this
+                     * @param {Array} selections Array of the selected nodes
+                     */
+                    'selectionchange',
+                    /**
+                     * @event beforeselect
+                     * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+                     * @param {Ext.view.View} this
+                     * @param {HTMLElement} node The node to be selected
+                     * @param {Array} selections Array of currently selected nodes
+                     */
+                    'beforeselect'
+                ]);
+            }
+        }
+        me.callParent(arguments);
+    },
+
+    // state management
+    initStateEvents: function(){
+        var events = this.stateEvents;
+        // push on stateEvents if they don't exist
+        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);
+            }
+        }
+    },
+
+    /**
+     * Returns the store associated with this Panel.
+     * @return {Ext.data.Store} The store
+     */
+    getStore: function(){
+        return this.store;
+    },
+
+    /**
+     * Gets the view for this panel.
+     * @return {Ext.view.Table}
+     */
+    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;
+    },
+
+    /**
+     * @private
+     * @override
+     * autoScroll is never valid for all classes which extend TablePanel.
+     */
+    setAutoScroll: Ext.emptyFn,
+
+    // This method hijacks Ext.view.Table's el scroll method.
+    // This enables us to keep the virtualized scrollbars in sync
+    // with the view. It currently does NOT support animation.
+    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();
+    },
+    
+
+    /**
+     * @private
+     * Called after this Component has achieved its correct initial size, after all layouts have done their thing.
+     * This is so we can add the View only after the initial size is known. This method is buffered 30ms.
+     */
+    injectView: function() {
+        if (!this.hasView && !this.collapsed) {
+            var me   = this,
+                view = me.getView();
+
+            me.hasView = true;
+            me.add(view);
+
+            // hijack the view el's scroll method
+            view.el.scroll = Ext.Function.bind(me.elScroll, me);
+            // We use to listen to document.body wheel events, but that's a
+            // little much. We scope just to the view now.
+            me.mon(view.el, {
+                mousewheel: me.onMouseWheel,
+                scope: me
+            });
+        }
+    },
+
+    afterExpand: function() {
+        this.callParent(arguments);
+        if (!this.hasView) {
+            this.injectView();
+        }
+    },
+
+    /**
+     * @private
+     * Process UI events from the view. Propagate them to whatever internal Components need to process them
+     * @param {String} type Event type, eg 'click'
+     * @param {TableView} view TableView Component
+     * @param {HtmlElement} cell Cell HtmlElement the event took place within
+     * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
+     * @param {Number} cellIndex Cell index within the row
+     * @param {EventObject} e Original event
+     */
+    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);
+        }
+    },
+
+    /**
+     * Request a recalculation of scrollbars and put them in if they are needed.
+     */
+    determineScrollbars: function() {
+        var me = this,
+            viewElDom,
+            centerScrollWidth,
+            centerClientWidth,
+            scrollHeight,
+            clientHeight;
+
+        if (!me.collapsed && me.view && me.view.el) {
+            viewElDom = me.view.el.dom;
+            //centerScrollWidth = viewElDom.scrollWidth;
+            centerScrollWidth = me.headerCt.getFullWidth();
+            /**
+             * clientWidth often returns 0 in IE resulting in an
+             * infinity result, here we use offsetWidth bc there are
+             * no possible scrollbars and we don't care about margins
+             */
+            centerClientWidth = viewElDom.offsetWidth;
+            if (me.verticalScroller && me.verticalScroller.el) {
+                scrollHeight = me.verticalScroller.getSizeCalculation().height;
+            } else {
+                scrollHeight = viewElDom.scrollHeight;
+            }
+
+            clientHeight = viewElDom.clientHeight;
+
+            if (!me.collapsed && scrollHeight > clientHeight) {
+                me.showVerticalScroller();
+            } else {
+                me.hideVerticalScroller();
+            }
+
+            if (!me.collapsed && centerScrollWidth > (centerClientWidth + Ext.getScrollBarWidth() - 2)) {
+                me.showHorizontalScroller();
+            } else {
+                me.hideHorizontalScroller();
+            }
+        }
+    },
+
+    onHeaderResize: function() {
+        if (this.view && this.view.rendered) {
+            this.determineScrollbars();
+            this.invalidateScroller();
+        }
+    },
+
+    /**
+     * Hide the verticalScroller and remove the horizontalScrollerPresentCls.
+     */
+    hideHorizontalScroller: function() {
+        var me = this;
+
+        if (me.horizontalScroller && me.horizontalScroller.ownerCt === me) {
+            me.verticalScroller.offsets.bottom = 0;
+            me.removeDocked(me.horizontalScroller, false);
+            me.removeCls(me.horizontalScrollerPresentCls);
+            me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
+        }
+
+    },
+
+    /**
+     * Show the horizontalScroller and add the horizontalScrollerPresentCls.
+     */
+    showHorizontalScroller: function() {
+        var me = this;
+
+        if (me.verticalScroller) {
+            me.verticalScroller.offsets.bottom = Ext.getScrollBarWidth() - 2;
+        }
+        if (me.horizontalScroller && me.horizontalScroller.ownerCt !== me) {
+            me.addDocked(me.horizontalScroller);
+            me.addCls(me.horizontalScrollerPresentCls);
+            me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
+        }
+    },
+
+    /**
+     * Hide the verticalScroller and remove the verticalScrollerPresentCls.
+     */
+    hideVerticalScroller: function() {
+        var me = this,
+            headerCt = me.headerCt;
+
+        // only trigger a layout when reserveOffset is changing
+        if (headerCt && headerCt.layout.reserveOffset) {
+            headerCt.layout.reserveOffset = false;
+            headerCt.doLayout();
+        }
+        if (me.verticalScroller && me.verticalScroller.ownerCt === me) {
+            me.removeDocked(me.verticalScroller, false);
+            me.removeCls(me.verticalScrollerPresentCls);
+            me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
+        }
+    },
+
+    /**
+     * Show the verticalScroller and add the verticalScrollerPresentCls.
+     */
+    showVerticalScroller: function() {
+        var me = this,
+            headerCt = me.headerCt;
+
+        // only trigger a layout when reserveOffset is changing
+        if (headerCt && !headerCt.layout.reserveOffset) {
+            headerCt.layout.reserveOffset = true;
+            headerCt.doLayout();
+        }
+        if (me.verticalScroller && me.verticalScroller.ownerCt !== me) {
+            me.addDocked(me.verticalScroller);
+            me.addCls(me.verticalScrollerPresentCls);
+            me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
+        }
+    },
+
+    /**
+     * Invalides scrollers that are present and forces a recalculation.
+     * (Not related to showing/hiding the scrollers)
+     */
+    invalidateScroller: function() {
+        var me = this,
+            vScroll = me.verticalScroller,
+            hScroll = me.horizontalScroller;
+
+        if (vScroll) {
+            vScroll.invalidate();
+        }
+        if (hScroll) {
+            hScroll.invalidate();
+        }
+    },
+
+    // refresh the view when a header moves
+    onHeaderMove: function(headerCt, header, fromIdx, toIdx) {
+        this.view.refresh();
+    },
+
+    // Section onHeaderHide is invoked after view.
+    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,
+            origScrollLeft, origScrollTop,
+            newScrollLeft, newScrollTop;
+
+        // Track original scroll values, so we can see if we've
+        // reached the end of our scroll height/width.
+        if (horizScroller) {
+            horizScrollerEl = horizScroller.el;
+            if (horizScrollerEl) {
+                origScrollLeft = horizScrollerEl.dom.scrollLeft;
+            }
+        }
+        if (vertScroller) {
+            vertScrollerEl = vertScroller.el;
+            if (vertScrollerEl) {
+                origScrollTop = vertScrollerEl.dom.scrollTop;
+            }
+        }
+
+        // Webkit Horizontal Axis
+        if (browserEvent.wheelDeltaX || browserEvent.wheelDeltaY) {
+            deltaX = -browserEvent.wheelDeltaX / 120 * scrollDelta / 3;
+            deltaY = -browserEvent.wheelDeltaY / 120 * scrollDelta / 3;
+            if (horizScroller) {
+                newScrollLeft = horizScroller.scrollByDeltaX(deltaX);
+            }
+            if (vertScroller) {
+                newScrollTop = vertScroller.scrollByDeltaY(deltaY);
+            }
+        } else {
+            // Gecko Horizontal Axis
+            if (browserEvent.axis && browserEvent.axis === 1) {
+                if (horizScroller) {
+                    deltaX = -(scrollDelta * e.getWheelDelta()) / 3;
+                    newScrollLeft = horizScroller.scrollByDeltaX(deltaX);
+                }
+            } else {
+                if (vertScroller) {
+
+                    deltaY = -(scrollDelta * e.getWheelDelta() / 3);
+                    newScrollTop = vertScroller.scrollByDeltaY(deltaY);
+                }
+            }
+        }
+
+        // If after given our delta, the scroller has not progressed, then we're
+        // at the end of our scroll range and shouldn't stop the browser event.
+        if ((deltaX !== 0 && newScrollLeft !== origScrollLeft) ||
+            (deltaY !== 0 && newScrollTop !== origScrollTop)) {
+            e.stopEvent();
+        }
+    },
+
+    /**
+     * @private
+     * Determine and invalidate scrollers on view refresh
+     */
+    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]);
+        }
+    },
+
+    // BrowserBug: IE will not stretch the td to fit the height of the entire
+    // tr, so manually sync cellheights on refresh and when an item has been
+    // updated.
+    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;
+        // Reading loop
+        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));
+        }
+
+        // Setting loop
+        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]);
+                    }
+                }
+                
+            }
+        }
+    },
+
+    /**
+     * Sets the scrollTop of the TablePanel.
+     * @param {Number} deltaY
+     */
+    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;
+    },
+
+    /**
+     * Scrolls the TablePanel by deltaY
+     * @param {Number} deltaY
+     */
+    scrollByDeltaY: function(deltaY) {
+        var rootCmp = this.getScrollerOwner(),
+            scrollerRight;
+        scrollerRight = rootCmp.down('gridscroller[dock=' + this.verticalScrollDock + ']');
+        if (scrollerRight) {
+            scrollerRight.scrollByDeltaY(deltaY);
+        }
+    },
+
+
+    /**
+     * Scrolls the TablePanel by deltaX
+     * @param {Number} deltaY
+     */
+    scrollByDeltaX: function(deltaX) {
+        this.horizontalScroller.scrollByDeltaX(deltaX);
+    },
+
+    /**
+     * Get left hand side marker for header resizing.
+     * @private
+     */
+    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;
+    },
+
+    /**
+     * Get right hand side marker for header resizing.
+     * @private
+     */
+    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;
+    },
+
+    /**
+     * Returns the selection model being used and creates it via the configuration
+     * if it has not been created already.
+     * @return {Ext.selection.Model} selModel
+     */
+    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;
+        }
+
+        // lock the selection model if user
+        // has disabled selection
+        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;
+    },
+
+    // template method meant to be overriden
+    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() {
+        this.callParent(arguments);
+        this.determineScrollbars();
+        this.invalidateScroller();
+    }
+});
+/**
+ * @class Ext.view.Table
+ * @extends Ext.view.View
+
+This class encapsulates the user interface for a tabular data set.
+It acts as a centralized manager for controlling the various interface
+elements of the view. This includes handling events, such as row and cell
+level based DOM events. It also reacts to events from the underlying {@link Ext.selection.Model}
+to provide visual feedback to the user. 
+
+This class does not provide ways to manipulate the underlying data of the configured
+{@link Ext.data.Store}.
+
+This is the base class for both {@link Ext.grid.View} and {@link Ext.tree.View} and is not
+to be used directly.
+
+ * @markdown
+ * @abstract
+ * @xtype tableview
+ * @author Nicolas Ferrero
+ */
+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',
+
+    // row
+    itemSelector: '.' + Ext.baseCSSPrefix + 'grid-row',
+    // cell
+    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]+) ', ''),
+
+    // cfg docs inherited
+    trackOver: true,
+
+    /**
+     * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
+     * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
+     * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
+     * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
+     * (e.g., 'my-class another-class'). Example usage:
+    <pre><code>
+viewConfig: {
+    forceFit: true,
+    showPreview: true, // custom property
+    enableRowBody: true, // required to create a second, full-width row to show expanded Record data
+    getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
+        if(this.showPreview){
+            rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
+            return 'x-grid3-row-expanded';
+        }
+        return 'x-grid3-row-collapsed';
+    }
+},
+    </code></pre>
+     * @param {Model} model The {@link Ext.data.Model} corresponding to the current row.
+     * @param {Number} index The row index.
+     * @param {Object} rowParams (DEPRECATED) A config object that is passed to the row template during rendering that allows
+     * customization of various aspects of a grid row.
+     * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
+     * by this function, and will be used to render a full-width expansion row below each grid row:</p>
+     * <ul>
+     * <li><code>body</code> : String <div class="sub-desc">An HTML fragment to be used as the expansion row's body content (defaults to '').</div></li>
+     * <li><code>bodyStyle</code> : String <div class="sub-desc">A CSS style specification that will be applied to the expansion row's &lt;tr> element. (defaults to '').</div></li>
+     * </ul>
+     * The following property will be passed in, and may be appended to:
+     * <ul>
+     * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
+     * both the standard grid row, and any expansion row.</div></li>
+     * </ul>
+     * @param {Store} store The {@link Ext.data.Store} this grid is bound to
+     * @method getRowClass
+     * @return {String} a CSS class name to add to the row.
+     */
+    getRowClass: null,
+
+    initComponent: function() {
+        this.scrollState = {};
+        this.selModel.view = this;
+        this.headerCt.view = this;
+        this.initFeatures();
+        this.setNewTemplate();
+        this.callParent();
+        this.mon(this.store, {
+            load: this.onStoreLoad,
+            scope: this
+        });
+
+        // this.addEvents(
+        //     /**
+        //      * @event rowfocus
+        //      * @param {Ext.data.Record} record
+        //      * @param {HTMLElement} row
+        //      * @param {Number} rowIdx
+        //      */
+        //     'rowfocus'
+        // );
+    },
+
+    // scroll to top of the grid when store loads
+    onStoreLoad: function(){
+        if (this.invalidateScrollerOnRefresh) {
+            if (Ext.isGecko) {
+                if (!this.scrollToTopTask) {
+                    this.scrollToTopTask = Ext.create('Ext.util.DelayedTask', this.scrollToTop, this);
+                }
+                this.scrollToTopTask.delay(1);
+            } else {
+                this.scrollToTop();
+            }
+        }
+    },
+
+    // scroll the view to the top
+    scrollToTop: Ext.emptyFn,
+    
+    /**
+     * Get the columns used for generating a template via TableChunker.
+     * See {@link Ext.grid.header.Container#getGridColumns}.
+     * @private
+     */
+    getGridColumns: function() {
+        return this.headerCt.getGridColumns();    
+    },
+    
+    /**
+     * Get a leaf level header by index regardless of what the nesting
+     * structure is.
+     * @private
+     * @param {Number} index The index
+     */
+    getHeaderAtIndex: function(index) {
+        return this.headerCt.getHeaderAtIndex(index);
+    },
+    
+    /**
+     * Get the cell (td) for a particular record and column.
+     * @param {Ext.data.Model} record
+     * @param {Ext.grid.column.Colunm} column
+     * @private
+     */
+    getCell: function(record, column) {
+        var row = this.getNode(record);
+        return Ext.fly(row).down(column.getCellSelector());
+    },
+
+    /**
+     * Get a reference to a feature
+     * @param {String} id The id of the feature
+     * @return {Ext.grid.feature.Feature} The feature. Undefined if not found
+     */
+    getFeature: function(id) {
+        var features = this.featuresMC;
+        if (features) {
+            return features.get(id);
+        }
+    },
+
+    /**
+     * Initializes each feature and bind it to this view.
+     * @private
+     */
+    initFeatures: function() {
+        this.features = this.features || [];
+        var features = this.features,
+            ln       = features.length,
+            i        = 0;
+
+        this.featuresMC = Ext.create('Ext.util.MixedCollection');
+        for (; i < ln; i++) {
+            // ensure feature hasnt already been instantiated
+            if (!features[i].isFeature) {
+                features[i] = Ext.create('feature.'+features[i].ftype, features[i]);
+            }
+            // inject a reference to view
+            features[i].view = this;
+            this.featuresMC.add(features[i]);
+        }
+    },
+
+    /**
+     * Gives features an injection point to attach events to the markup that
+     * has been created for this view.
+     * @private
+     */
+    attachEventsForFeatures: function() {
+        var features = this.features,
+            ln       = features.length,
+            i        = 0;
+
+        for (; i < ln; i++) {
+            if (features[i].isFeature) {
+                features[i].attachEvents();
+            }
+        }
+    },
+
+    afterRender: function() {
+        this.callParent();
+        this.mon(this.el, {
+            scroll: this.fireBodyScroll,
+            scope: this
+        });
+        this.attachEventsForFeatures();
+    },
+
+    fireBodyScroll: function(e, t) {
+        this.fireEvent('bodyscroll', e, t);
+    },
+
+    // TODO: Refactor headerCt dependency here to colModel
+    /**
+     * Uses the headerCt to transform data from dataIndex keys in a record to
+     * headerId keys in each header and then run them through each feature to
+     * get additional data for variables they have injected into the view template.
+     * @private
+     */
+    prepareData: function(data, idx, record) {
+        var orig     = this.headerCt.prepareData(data, idx, record, this),
+            features = this.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, this));
+            }
+        }
+
+        return orig;
+    },
+
+    // TODO: Refactor headerCt dependency here to colModel
+    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;
+        // process row classes, rowParams has been deprecated and has been moved
+        // to the individual features that implement the behavior. 
+        if (this.getRowClass) {
+            for (; j < jln; j++) {
+                rowParams = {};
+                preppedRecords[j]['rowCls'] = this.getRowClass(records[j], j, rowParams, this.store);
+                //<debug>
+                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.");
+                }
+                //</debug>
+            }
+        }
+        // currently only one feature may implement collectData. This is to modify
+        // what's returned to the view before its rendered
+        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;
+    },
+
+    // TODO: Refactor header resizing to column resizing
+    /**
+     * When a header is resized, setWidth on the individual columns resizer class,
+     * the top level table, save/restore scroll state, generate a new template and
+     * restore focus to the grid view's element so that keyboard navigation
+     * continues to work.
+     * @private
+     */
+    onHeaderResize: function(header, w, suppressFocus) {
+        var el = this.el;
+        if (el) {
+            this.saveScrollState();
+            // Grab the col and set the width, css
+            // class is generated in TableChunker.
+            // Select composites because there may be several chunks.
+            el.select('.' + Ext.baseCSSPrefix + 'grid-col-resizer-'+header.id).setWidth(w);
+            el.select('.' + Ext.baseCSSPrefix + 'grid-table-resizer').setWidth(this.headerCt.getFullWidth());
+            this.restoreScrollState();
+            this.setNewTemplate();
+            if (!suppressFocus) {
+                this.el.focus();
+            }
+        }
+    },
+
+    /**
+     * When a header is shown restore its oldWidth if it was previously hidden.
+     * @private
+     */
+    onHeaderShow: function(headerCt, header, suppressFocus) {
+        // restore headers that were dynamically hidden
+        if (header.oldWidth) {
+            this.onHeaderResize(header, header.oldWidth, suppressFocus);
+            delete header.oldWidth;
+        // flexed headers will have a calculated size set
+        // this additional check has to do with the fact that
+        // defaults: {width: 100} will fight with a flex value
+        } else if (header.width && !header.flex) {
+            this.onHeaderResize(header, header.width, suppressFocus);
+        }
+        this.setNewTemplate();
+    },
+
+    /**
+     * When the header hides treat it as a resize to 0.
+     * @private
+     */
+    onHeaderHide: function(headerCt, header, suppressFocus) {
+        this.onHeaderResize(header, 0, suppressFocus);
+    },
+
+    /**
+     * Set a new template based on the current columns displayed in the
+     * grid.
+     * @private
+     */
+    setNewTemplate: function() {
+        var columns = this.headerCt.getColumnsForTpl(true);
+        this.tpl = this.getTableChunker().getTableTpl({
+            columns: columns,
+            features: this.features
+        });
+    },
+
+    /**
+     * Get the configured chunker or default of Ext.view.TableChunker
+     */
+    getTableChunker: function() {
+        return this.chunker || Ext.view.TableChunker;
+    },
+
+    /**
+     * Add a CSS Class to a specific row.
+     * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model representing this row
+     * @param {String} cls
+     */
+    addRowCls: function(rowInfo, cls) {
+        var row = this.getNode(rowInfo);
+        if (row) {
+            Ext.fly(row).addCls(cls);
+        }
+    },
+
+    /**
+     * Remove a CSS Class from a specific row.
+     * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model representing this row
+     * @param {String} cls
+     */
+    removeRowCls: function(rowInfo, cls) {
+        var row = this.getNode(rowInfo);
+        if (row) {
+            Ext.fly(row).removeCls(cls);
+        }
+    },
+
+    // GridSelectionModel invokes onRowSelect as selection changes
+    onRowSelect : function(rowIdx) {
+        this.addRowCls(rowIdx, this.selectedItemCls);
+    },
+
+    // GridSelectionModel invokes onRowDeselect as selection changes
+    onRowDeselect : function(rowIdx) {
+        this.removeRowCls(rowIdx, this.selectedItemCls);
+        this.removeRowCls(rowIdx, this.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) {
+        //var cell = this.getCellByPosition(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) {
+            cellSelector = header.getCellSelector();
+            cell = Ext.fly(node).down(cellSelector);
+        }
+        return cell;
+    },
+
+    // GridSelectionModel invokes onRowFocus to 'highlight'
+    // the last row focused
+    onRowFocus: function(rowIdx, highlight, supressFocus) {
+        var row = this.getNode(rowIdx);
+
+        if (highlight) {
+            this.addRowCls(rowIdx, this.focusedItemCls);
+            if (!supressFocus) {
+                this.focusRow(rowIdx);
+            }
+            //this.el.dom.setAttribute('aria-activedescendant', row.id);
+        } else {
+            this.removeRowCls(rowIdx, this.focusedItemCls);
+        }
+    },
+
+    /**
+     * Focus a particular row and bring it into view. Will fire the rowfocus event.
+     * @cfg {Mixed} An HTMLElement template node, index of a template node, the
+     * id of a template node or the record associated with the node.
+     */
+    focusRow: function(rowIdx) {
+        var row        = this.getNode(rowIdx),
+            el         = this.el,
+            adjustment = 0,
+            panel      = this.ownerCt,
+            rowRegion,
+            elRegion,
+            record;
+            
+        if (row && this.el) {
+            elRegion  = el.getRegion();
+            rowRegion = Ext.fly(row).getRegion();
+            // row is above
+            if (rowRegion.top < elRegion.top) {
+                adjustment = rowRegion.top - elRegion.top;
+            // row is below
+            } else if (rowRegion.bottom > elRegion.bottom) {
+                adjustment = rowRegion.bottom - elRegion.bottom;
+            }
+            record = this.getRecord(row);
+            rowIdx = this.store.indexOf(record);
+
+            if (adjustment) {
+                // scroll the grid itself, so that all gridview's update.
+                panel.scrollByDeltaY(adjustment);
+            }
+            this.fireEvent('rowfocus', record, row, rowIdx);
+        }
+    },
+
+    focusCell: function(position) {
+        var cell        = this.getCellByPosition(position),
+            el          = this.el,
+            adjustmentY = 0,
+            adjustmentX = 0,
+            elRegion    = el.getRegion(),
+            panel       = this.ownerCt,
+            cellRegion,
+            record;
+
+        if (cell) {
+            cellRegion = cell.getRegion();
+            // cell is above
+            if (cellRegion.top < elRegion.top) {
+                adjustmentY = cellRegion.top - elRegion.top;
+            // cell is below
+            } else if (cellRegion.bottom > elRegion.bottom) {
+                adjustmentY = cellRegion.bottom - elRegion.bottom;
+            }
+
+            // cell is left
+            if (cellRegion.left < elRegion.left) {
+                adjustmentX = cellRegion.left - elRegion.left;
+            // cell is right
+            } else if (cellRegion.right > elRegion.right) {
+                adjustmentX = cellRegion.right - elRegion.right;
+            }
+
+            if (adjustmentY) {
+                // scroll the grid itself, so that all gridview's update.
+                panel.scrollByDeltaY(adjustmentY);
+            }
+            if (adjustmentX) {
+                panel.scrollByDeltaX(adjustmentX);
+            }
+            el.focus();
+            this.fireEvent('cellfocus', record, cell, position);
+        }
+    },
+
+    /**
+     * Scroll by delta. This affects this individual view ONLY and does not
+     * synchronize across views or scrollers.
+     * @param {Number} delta
+     * @param {String} dir (optional) Valid values are scrollTop and scrollLeft. Defaults to scrollTop.
+     * @private
+     */
+    scrollByDelta: function(delta, dir) {
+        dir = dir || 'scrollTop';
+        var elDom = this.el.dom;
+        elDom[dir] = (elDom[dir] += delta);
+    },
+
+    onUpdate: function(ds, index) {
+        this.callParent(arguments);
+    },
+
+    /**
+     * Save the scrollState in a private variable.
+     * Must be used in conjunction with restoreScrollState
+     */
+    saveScrollState: function() {
+        var dom = this.el.dom,
+            state = this.scrollState;
+
+        state.left = dom.scrollLeft;
+        state.top = dom.scrollTop;
+    },
+
+    /**
+     * Restore the scrollState.
+     * Must be used in conjunction with saveScrollState
+     * @private
+     */
+    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 the grid view.
+     * Saves and restores the scroll state, generates a new template, stripes rows
+     * and invalidates the scrollers.
+     * @param {Boolean} firstPass This is a private flag for internal use only.
+     */
+    refresh: function(firstPass) {
+        var me = this,
+            table;
+
+        //this.saveScrollState();
+        me.setNewTemplate();
+        
+        // The table.unselectable() call below adds a selectstart listener to the table element.
+        // Before we clear the whole dataview in the callParent, we remove all the listeners from the
+        // table. This prevents a big memory leak on IE6 and IE7.
+        if (me.rendered) {
+            table = me.el.child('table');
+            if (table) {
+                table.removeAllListeners();
+            }
+        }
+        
+        me.callParent(arguments);
+
+        //this.restoreScrollState();
+        if (me.rendered) {
+            // Make the table view unselectable
+            table = me.el.child('table');
+            if (table) {
+                table.unselectable();
+            }
+            
+            if (!firstPass) {
+                // give focus back to gridview
+                me.el.focus();
+            }
+        }
+    },
+
+    processItemEvent: function(type, 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(),
+            result;
+
+        if (type == 'keydown' && !cell && selModel.getCurrentPosition) {
+            // CellModel, otherwise we can't tell which cell to invoke
+            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;
+        }
+
+        // Don't handle cellmouseenter and cellmouseleave events for now
+        if (type == 'mouseover' || type == 'mouseout') {
+            return true;
+        }
+
+        return !(
+            // We are adding cell and feature events  
+            (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 = this.statics().EventMap,
+            features = this.features,
+            ln = features.length,
+            type = e.type,
+            i, feature, prefix, featureTarget,
+            beforeArgs, args,
+            panel = me.ownerCt;
+
+        this.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;
+                    // allows features to implement getFireEventArgs to change the
+                    // fireEvent signature
+                    beforeArgs = feature.getFireEventArgs('before' + prefix + type, me, featureTarget);
+                    args = feature.getFireEventArgs(prefix + type, me, featureTarget);
+                    
+                    if (
+                        // before view event
+                        (me.fireEvent.apply(me, beforeArgs) === false) ||
+                        // panel grid event
+                        (panel.fireEvent.apply(panel, beforeArgs) === false) ||
+                        // view event
+                        (me.fireEvent.apply(me, args) === false) ||
+                        // panel event
+                        (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,
+
+    /**
+     * Expand a particular header to fit the max content width.
+     * This will ONLY expand, not contract.
+     * @private
+     */
+    expandToFit: function(header) {
+        var maxWidth = this.getMaxContentWidth(header);
+        delete header.flex;
+        header.setWidth(maxWidth);
+    },
+
+    /**
+     * Get the max contentWidth of the header's text and all cells
+     * in the grid under this header.
+     * @private
+     */
+    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 cellNode = e.getTarget(this.cellSelector),
+            rowNode  = e.getTarget(this.itemSelector),
+            record   = this.getRecord(rowNode),
+            header   = this.getHeaderByCell(cellNode);
+
+        return this.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;
+    },
+
+    /**
+     * @param {Object} position The current row and column: an object containing the following properties:<ul>
+     * <li>row<div class="sub-desc"> The row <b>index</b></div></li>
+     * <li>column<div class="sub-desc">The column <b>index</b></div></li>
+     * </ul>
+     * @param {String} direction 'up', 'down', 'right' and 'left'
+     * @param {Ext.EventObject} e event
+     * @param {Boolean} preventWrap Set to true to prevent wrap around to the next or previous row.
+     * @param {Function} verifierFn A function to verify the validity of the calculated position. When using this function, you must return true to allow the newPosition to be returned.
+     * @param {Scope} scope Scope to run the verifierFn in
+     * @returns {Object} newPosition An object containing the following properties:<ul>
+     * <li>row<div class="sub-desc"> The row <b>index</b></div></li>
+     * <li>column<div class="sub-desc">The column <b>index</b></div></li>
+     * </ul>
+     * @private
+     */
+    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
+        var row      = pos.row,
+            column   = pos.column,
+            rowCount = this.store.getCount(),
+            firstCol = this.getFirstVisibleColumnIndex(),
+            lastCol  = this.getLastVisibleColumnIndex(),
+            newPos   = {row: row, column: column},
+            activeHeader = this.headerCt.getHeaderAtIndex(column);
+
+        // no active header or its currently hidden
+        if (!activeHeader || activeHeader.hidden) {
+            return false;
+        }
+
+        e = e || {};
+        direction = direction.toLowerCase();
+        switch (direction) {
+            case 'right':
+                // has the potential to wrap if its last
+                if (column === lastCol) {
+                    // if bottom row and last column, deny right
+                    if (preventWrap || row === rowCount - 1) {
+                        return false;
+                    }
+                    if (!e.ctrlKey) {
+                        // otherwise wrap to nextRow and firstCol
+                        newPos.row = row + 1;
+                        newPos.column = firstCol;
+                    }
+                // go right
+                } else {
+                    if (!e.ctrlKey) {
+                        newPos.column = column + this.getRightGap(activeHeader);
+                    } else {
+                        newPos.column = lastCol;
+                    }
+                }
+                break;
+
+            case 'left':
+                // has the potential to wrap
+                if (column === firstCol) {
+                    // if top row and first column, deny left
+                    if (preventWrap || row === 0) {
+                        return false;
+                    }
+                    if (!e.ctrlKey) {
+                        // otherwise wrap to prevRow and lastCol
+                        newPos.row = row - 1;
+                        newPos.column = lastCol;
+                    }
+                // go left
+                } else {
+                    if (!e.ctrlKey) {
+                        newPos.column = column + this.getLeftGap(activeHeader);
+                    } else {
+                        newPos.column = firstCol;
+                    }
+                }
+                break;
+
+            case 'up':
+                // if top row, deny up
+                if (row === 0) {
+                    return false;
+                // go up
+                } else {
+                    if (!e.ctrlKey) {
+                        newPos.row = row - 1;
+                    } else {
+                        newPos.row = 0;
+                    }
+                }
+                break;
+
+            case 'down':
+                // if bottom row, deny down
+                if (row === rowCount - 1) {
+                    return false;
+                // go down
+                } 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)
+        };
+    },
+
+    /**
+     * Determines the 'gap' between the closest adjacent header to the right
+     * that is not hidden.
+     * @private
+     */
+    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) {
+            table = this.el.child('table');
+            if (table) {
+                table.removeAllListeners();
+            }
+        }
+        this.callParent(arguments);
+    },
+
+    /**
+     * Determines the 'gap' between the closest adjacent header to the left
+     * that is not hidden.
+     * @private
+     */
+    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;
+    }
+});
+/**
+ * @class Ext.grid.View
+ * @extends Ext.view.Table
+
+The grid View class provides extra {@link Ext.grid.Panel} specific functionality to the
+{@link Ext.view.Table}. In general, this class is not instanced directly, instead a viewConfig
+option is passed to the grid:
+
+    Ext.create('Ext.grid.Panel', {
+        // other options
+        viewConfig: {
+            stripeRows: false
+        }
+    });
+    
+__Drag Drop__
+Drag and drop functionality can be achieved in the grid by attaching a {@link Ext.grid.plugin.DragDrop} plugin
+when creating the view.
+
+    Ext.create('Ext.grid.Panel', {
+        // other options
+        viewConfig: {
+            plugins: {
+                ddGroup: 'people-group',
+                ptype: 'gridviewdragdrop',
+                enableDrop: false
+            }
+        }
+    });
+
+ * @markdown
+ */
+Ext.define('Ext.grid.View', {
+    extend: 'Ext.view.Table',
+    alias: 'widget.gridview',
+
+    /**
+     * @cfg {Boolean} stripeRows <tt>true</tt> to stripe the rows. Default is <tt>false</tt>.
+     * <p>This causes the CSS class <tt><b>x-grid-row-alt</b></tt> to be added to alternate rows of
+     * the grid. A default CSS rule is provided which sets a background color, but you can override this
+     * with a rule which either overrides the <b>background-color</b> style using the '!important'
+     * modifier, or which uses a CSS selector of higher specificity.</p>
+     */
+    stripeRows: true,
+    
+    invalidateScrollerOnRefresh: true,
+    
+    /**
+     * Scroll the GridView to the top by scrolling the scroller.
+     * @private
+     */
+    scrollToTop : function(){
+        if (this.rendered) {
+            var section = this.ownerCt,
+                verticalScroller = section.verticalScroller;
+                
+            if (verticalScroller) {
+                verticalScroller.scrollToTop();
+            }
+        }
+    },
+
+    // after adding a row stripe rows from then on
+    onAdd: function(ds, records, index) {
+        this.callParent(arguments);
+        this.doStripeRows(index);
+    },
+    
+    // after removing a row stripe rows from then on
+    onRemove: function(ds, records, index) {
+        this.callParent(arguments);
+        this.doStripeRows(index);
+    },
+    
+    /**
+     * Stripe rows from a particular row index
+     * @param {Number} startRow
+     * @private
+     */
+    doStripeRows: function(startRow) {
+        // ensure stripeRows configuration is turned on
+        if (this.stripeRows) {
+            var rows   = this.getNodes(startRow),
+                rowsLn = rows.length,
+                i      = 0,
+                row;
+                
+            for (; i < rowsLn; i++) {
+                row = rows[i];
+                // Remove prior applied row classes.
+                row.className = row.className.replace(this.rowClsRe, ' ');
+                // Every odd row will get an additional cls
+                if (i % 2 === 1) {
+                    row.className += (' ' + this.altRowCls);
+                }
+            }
+        }
+    },
+    
+    refresh: function(firstPass) {
+        this.callParent(arguments);
+        this.doStripeRows(0);
+        // TODO: Remove gridpanel dependency
+        var g = this.up('gridpanel');
+        if (g && this.invalidateScrollerOnRefresh) {
+            g.invalidateScroller();
+        }
+    }
+});
+
+/**
+ * @author Aaron Conran
+ * @class Ext.grid.Panel
+ * @extends Ext.panel.Table
+ *
+ * Grids are an excellent way of showing large amounts of tabular data on the client side. Essentially a supercharged 
+ * `<table>`, GridPanel makes it easy to fetch, sort and filter large amounts of data.
+ * 
+ * Grids are composed of 2 main pieces - a {@link Ext.data.Store Store} full of data and a set of columns to render.
+ *
+ * {@img Ext.grid.Panel/Ext.grid.Panel1.png Ext.grid.Panel component}
+ *
+ * ## Basic GridPanel
+ *
+ *     Ext.create('Ext.data.Store', {
+ *         storeId:'simpsonsStore',
+ *         fields:['name', 'email', 'phone'],
+ *         data:{'items':[
+ *             {"name":"Lisa", "email":"lisa@simpsons.com", "phone":"555-111-1224"},
+ *             {"name":"Bart", "email":"bart@simpsons.com", "phone":"555--222-1234"},
+ *             {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"},                        
+ *             {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"}            
+ *         ]},
+ *         proxy: {
+ *             type: 'memory',
+ *             reader: {
+ *                 type: 'json',
+ *                 root: 'items'
+ *             }
+ *         }
+ *     });
+ *     
+ *     Ext.create('Ext.grid.Panel', {
+ *         title: 'Simpsons',
+ *         store: Ext.data.StoreManager.lookup('simpsonsStore'),
+ *         columns: [
+ *             {header: 'Name',  dataIndex: 'name'},
+ *             {header: 'Email', dataIndex: 'email', flex:1},
+ *             {header: 'Phone', dataIndex: 'phone'}
+ *         ],
+ *         height: 200,
+ *         width: 400,
+ *         renderTo: Ext.getBody()
+ *     });
+ * 
+ * The code above produces a simple grid with three columns. We specified a Store which will load JSON data inline. 
+ * In most apps we would be placing the grid inside another container and wouldn't need to use the
+ * {@link #height}, {@link #width} and {@link #renderTo} configurations but they are included here to make it easy to get
+ * up and running.
+ * 
+ * The grid we created above will contain a header bar with a title ('Simpsons'), a row of column headers directly underneath
+ * and finally the grid rows under the headers.
+ * 
+ * ## Configuring columns
+ * 
+ * By default, each column is sortable and will toggle between ASC and DESC sorting when you click on its header. Each
+ * column header is also reorderable by default, and each gains a drop-down menu with options to hide and show columns.
+ * It's easy to configure each column - here we use the same example as above and just modify the columns config:
+ * 
+ *     columns: [
+ *         {
+ *             header: 'Name',
+ *             dataIndex: 'name',
+ *             sortable: false,
+ *             hideable: false,
+ *             flex: 1
+ *         },
+ *         {
+ *             header: 'Email',
+ *             dataIndex: 'email',
+ *             hidden: true
+ *         },
+ *         {
+ *             header: 'Phone',
+ *             dataIndex: 'phone',
+ *             width: 100
+ *         }
+ *     ]
+ * 
+ * We turned off sorting and hiding on the 'Name' column so clicking its header now has no effect. We also made the Email
+ * column hidden by default (it can be shown again by using the menu on any other column). We also set the Phone column to
+ * a fixed with of 100px and flexed the Name column, which means it takes up all remaining width after the other columns 
+ * have been accounted for. See the {@link Ext.grid.column.Column column docs} for more details.
+ * 
+ * ## Renderers
+ * 
+ * As well as customizing columns, it's easy to alter the rendering of individual cells using renderers. A renderer is 
+ * tied to a particular column and is passed the value that would be rendered into each cell in that column. For example,
+ * we could define a renderer function for the email column to turn each email address into a mailto link:
+ * 
+ *     columns: [
+ *         {
+ *             header: 'Email',
+ *             dataIndex: 'email',
+ *             renderer: function(value) {
+ *                 return Ext.String.format('<a href="mailto:{0}">{1}</a>', value, value);
+ *             }
+ *         }
+ *     ]
+ * 
+ * See the {@link Ext.grid.column.Column column docs} for more information on renderers.
+ * 
+ * ## Selection Models
+ * 
+ * Sometimes all you want is to render data onto the screen for viewing, but usually it's necessary to interact with or 
+ * update that data. Grids use a concept called a Selection Model, which is simply a mechanism for selecting some part of
+ * the data in the grid. The two main types of Selection Model are RowSelectionModel, where entire rows are selected, and
+ * CellSelectionModel, where individual cells are selected.
+ * 
+ * Grids use a Row Selection Model by default, but this is easy to customise like so:
+ * 
+ *     Ext.create('Ext.grid.Panel', {
+ *         selType: 'cellmodel',
+ *         store: ...
+ *     });
+ * 
+ * Specifying the `cellmodel` changes a couple of things. Firstly, clicking on a cell now
+ * selects just that cell (using a {@link Ext.selection.RowModel rowmodel} will select the entire row), and secondly the
+ * keyboard navigation will walk from cell to cell instead of row to row. Cell-based selection models are usually used in
+ * conjunction with editing.
+ * 
+ * {@img Ext.grid.Panel/Ext.grid.Panel2.png Ext.grid.Panel cell editing}
+ *
+ * ## Editing
+ * 
+ * Grid has built-in support for in-line editing. There are two chief editing modes - cell editing and row editing. Cell
+ * editing is easy to add to your existing column setup - here we'll just modify the example above to include an editor
+ * on both the name and the email columns:
+ * 
+ *     Ext.create('Ext.grid.Panel', {
+ *         title: 'Simpsons',
+ *         store: Ext.data.StoreManager.lookup('simpsonsStore'),
+ *         columns: [
+ *             {header: 'Name',  dataIndex: 'name', field: 'textfield'},
+ *             {header: 'Email', dataIndex: 'email', flex:1, 
+ *                 field:{
+ *                     xtype:'textfield',
+ *                     allowBlank:false
+ *                 }
+ *             },
+ *             {header: 'Phone', dataIndex: 'phone'}
+ *         ],
+ *         selType: 'cellmodel',
+ *         plugins: [
+ *             Ext.create('Ext.grid.plugin.CellEditing', {
+ *                 clicksToEdit: 1
+ *             })
+ *         ],
+ *         height: 200,
+ *         width: 400,
+ *         renderTo: Ext.getBody()
+ *     });
+ * 
+ * This requires a little explanation. We're passing in {@link #store store} and {@link #columns columns} as normal, but 
+ * this time we've also specified a {@link #field field} on two of our columns. For the Name column we just want a default
+ * textfield to edit the value, so we specify 'textfield'. For the Email column we customized the editor slightly by 
+ * passing allowBlank: false, which will provide inline validation.
+ * 
+ * To support cell editing, we also specified that the grid should use the 'cellmodel' {@link #selType}, and created an
+ * instance of the {@link Ext.grid.plugin.CellEditing CellEditing plugin}, which we configured to activate each editor after a
+ * single click.
+ * 
+ * {@img Ext.grid.Panel/Ext.grid.Panel3.png Ext.grid.Panel row editing}
+ *
+ * ## Row Editing
+ * 
+ * The other type of editing is row-based editing, using the RowEditor component. This enables you to edit an entire row
+ * at a time, rather than editing cell by cell. Row Editing works in exactly the same way as cell editing, all we need to
+ * do is change the plugin type to {@link Ext.grid.plugin.RowEditing}, and set the selType to 'rowmodel':
+ * 
+ *     Ext.create('Ext.grid.Panel', {
+ *         title: 'Simpsons',
+ *         store: Ext.data.StoreManager.lookup('simpsonsStore'),
+ *         columns: [
+ *             {header: 'Name',  dataIndex: 'name', field: 'textfield'},
+ *             {header: 'Email', dataIndex: 'email', flex:1, 
+ *                 field:{
+ *                     xtype:'textfield',
+ *                     allowBlank:false
+ *                 }
+ *             },
+ *             {header: 'Phone', dataIndex: 'phone'}
+ *         ],
+ *         selType: 'rowmodel',
+ *         plugins: [
+ *             Ext.create('Ext.grid.plugin.RowEditing', {
+ *                 clicksToEdit: 1
+ *             })
+ *         ],
+ *         height: 200,
+ *         width: 400,
+ *         renderTo: Ext.getBody()
+ *     });
+ * 
+ * Again we passed some configuration to our {@link Ext.grid.plugin.RowEditing} plugin, and now when we click each row a row
+ * editor will appear and enable us to edit each of the columns we have specified an editor for.
+ * 
+ * ## Sorting & Filtering
+ * 
+ * Every grid is attached to a {@link Ext.data.Store Store}, which provides multi-sort and filtering capabilities. It's
+ * easy to set up a grid to be sorted from the start:
+ * 
+ *     var myGrid = Ext.create('Ext.grid.Panel', {
+ *         store: {
+ *             fields: ['name', 'email', 'phone'],
+ *             sorters: ['name', 'phone']
+ *         },
+ *         columns: [
+ *             {text: 'Name',  dataIndex: 'name'},
+ *             {text: 'Email', dataIndex: 'email'}
+ *         ]
+ *     });
+ * 
+ * Sorting at run time is easily accomplished by simply clicking each column header. If you need to perform sorting on 
+ * more than one field at run time it's easy to do so by adding new sorters to the store:
+ * 
+ *     myGrid.store.sort([
+ *         {property: 'name',  direction: 'ASC'},
+ *         {property: 'email', direction: 'DESC'},
+ *     ]);
+ * 
+ * {@img Ext.grid.Panel/Ext.grid.Panel4.png Ext.grid.Panel grouping}
+ * 
+ * ## Grouping
+ * 
+ * Grid supports the grouping of rows by any field. For example if we had a set of employee records, we might want to 
+ * group by the department that each employee works in. Here's how we might set that up:
+ * 
+ *     var store = Ext.create('Ext.data.Store', {
+ *         storeId:'employeeStore',
+ *         fields:['name', 'senority', 'department'],
+ *         groupField: 'department',
+ *         data:{'employees':[
+ *             {"name":"Michael Scott", "senority":7, "department":"Manangement"},
+ *             {"name":"Dwight Schrute", "senority":2, "department":"Sales"},
+ *             {"name":"Jim Halpert", "senority":3, "department":"Sales"},
+ *             {"name":"Kevin Malone", "senority":4, "department":"Accounting"},
+ *             {"name":"Angela Martin", "senority":5, "department":"Accounting"}                        
+ *         ]},
+ *         proxy: {
+ *             type: 'memory',
+ *             reader: {
+ *                 type: 'json',
+ *                 root: 'employees'
+ *             }
+ *         }
+ *     });
+ *     
+ *     Ext.create('Ext.grid.Panel', {
+ *         title: 'Employees',
+ *         store: Ext.data.StoreManager.lookup('employeeStore'),
+ *         columns: [
+ *             {header: 'Name',  dataIndex: 'name'},
+ *             {header: 'Senority', dataIndex: 'senority'}
+ *         ],        
+ *         features: [{ftype:'grouping'}],
+ *         width: 200,
+ *         height: 275,
+ *         renderTo: Ext.getBody()
+ *     });
+ * 
+ * ## Infinite Scrolling
+ *
+ * Grid supports infinite scrolling as an alternative to using a paging toolbar. Your users can scroll through thousands
+ * of records without the performance penalties of renderering all the records on screen at once. The grid should be bound
+ * to a store with a pageSize specified.
+ *
+ *     var grid = Ext.create('Ext.grid.Panel', {
+ *         // Use a PagingGridScroller (this is interchangeable with a PagingToolbar)
+ *         verticalScrollerType: 'paginggridscroller',
+ *         // do not reset the scrollbar when the view refreshs
+ *         invalidateScrollerOnRefresh: false,
+ *         // infinite scrolling does not support selection
+ *         disableSelection: true,
+ *         // ...
+ *     });
+ * 
+ * ## Paging
+ *
+ * Grid supports paging through large sets of data via a PagingToolbar or PagingGridScroller (see the Infinite Scrolling section above).
+ * To leverage paging via a toolbar or scroller, you need to set a pageSize configuration on the Store.
+ *
+ *     var itemsPerPage = 2;   // set the number of items you want per page
+ *     
+ *     var store = Ext.create('Ext.data.Store', {
+ *         id:'simpsonsStore',
+ *         autoLoad: false,
+ *         fields:['name', 'email', 'phone'],
+ *         pageSize: itemsPerPage, // items per page
+ *         proxy: {
+ *             type: 'ajax',
+ *             url: 'pagingstore.js',  // url that will load data with respect to start and limit params
+ *             reader: {
+ *                 type: 'json',
+ *                 root: 'items',
+ *                 totalProperty: 'total'
+ *             }
+ *         }
+ *     });
+ *     
+ *     // specify segment of data you want to load using params
+ *     store.load({
+ *         params:{
+ *             start:0,    
+ *             limit: itemsPerPage
+ *         }
+ *     });
+ *     
+ *     Ext.create('Ext.grid.Panel', {
+ *         title: 'Simpsons',
+ *         store: store,
+ *         columns: [
+ *             {header: 'Name',  dataIndex: 'name'},
+ *             {header: 'Email', dataIndex: 'email', flex:1},
+ *             {header: 'Phone', dataIndex: 'phone'}
+ *         ],
+ *         width: 400,
+ *         height: 125,
+ *         dockedItems: [{
+ *             xtype: 'pagingtoolbar',
+ *             store: store,   // same store GridPanel is using
+ *             dock: 'bottom',
+ *             displayInfo: true
+ *         }],
+ *         renderTo: Ext.getBody()
+ *     }); 
+ * 
+ * {@img Ext.grid.Panel/Ext.grid.Panel5.png Ext.grid.Panel grouping}
+ * 
+ * @docauthor Ed Spencer
+ */
+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,
+    
+    // Required for the Lockable Mixin. These are the configurations which will be copied to the
+    // normal and locked sub tablepanels
+    normalCfgCopy: ['invalidateScrollerOnRefresh', 'verticalScroller', 'verticalScrollDock', 'verticalScrollerType', 'scroll'],
+    lockedCfgCopy: ['invalidateScrollerOnRefresh'],
+    
+    /**
+     * @cfg {Boolean} columnLines Adds column line styling
+     */
+    
+    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')
+    }
+});
+// Currently has the following issues:
+// - Does not handle postEditValue
+// - Fields without editors need to sync with their values in Store
+// - starting to edit another record while already editing and dirty should probably prevent it
+// - aggregating validation messages
+// - tabIndex is not managed bc we leave elements in dom, and simply move via positioning
+// - layout issues when changing sizes/width while hidden (layout bug)
+
+/**
+ * @class Ext.grid.RowEditor
+ * @extends Ext.form.Panel
+ *
+ * Internal utility class used to provide row editing functionality. For developers, they should use
+ * the RowEditing plugin to use this functionality with a grid.
+ *
+ * @ignore
+ */
+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,
+
+    initComponent: function() {
+        var me = this,
+            form;
+
+        me.cls = Ext.baseCSSPrefix + 'grid-row-editor';
+
+        me.layout = {
+            type: 'hbox',
+            align: 'middle'
+        };
+
+        // Maintain field-to-column mapping
+        // It's easy to get a field from a column, but not vice versa
+        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 });
+
+        // Prevent from bubbling click events to the grid view
+        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);
+
+        // Recover our row node after a view refresh
+        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(hm, fieldId, column) {
+        var me = this,
+            colIdx = me.editingPlugin.grid.headerCt.getHeaderIndex(column),
+            field = column.getEditor({ xtype: 'displayfield' });
+
+        me.insert(colIdx, field);
+    },
+
+    onFieldRemove: function(hm, fieldId, column) {
+        var me = this,
+            field = column.getEditor(),
+            fieldDom = field.el.dom;
+        me.remove(field, false);
+        fieldDom.parentNode.removeChild(fieldDom);
+    },
+
+    onFieldReplace: function(hm, fieldId, column, oldColumn) {
+        var me = this;
+        me.onFieldRemove(hm, fieldId, oldColumn);
+    },
+
+    clearFields: function() {
+        var me = this,
+            hm = me.columns;
+        hm.each(function(fieldId) {
+            hm.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
+                }]
+            });
+
+            // Prevent from bubbling click events to the grid view
+            me.mon(btns.el, {
+                // BrowserBug: Opera 11.01
+                //   causes the view to scroll when a button is focused from mousedown
+                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,
+
+            // always get data from ColumnModel as its what drives
+            // the GridView's sizing
+            mainBodyWidth = grid.headerCt.getFullWidth(),
+            scrollerWidth = grid.getWidth(),
+
+            // use the minimum as the columns may not fill up the entire grid
+            // width
+            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);
+                }
+            };
+
+        // need to set both top/left
+        if (row && Ext.isElement(row.dom)) {
+            // Bring our row into view if necessary, so a row editor that's already
+            // visible and animated to the row will appear smooth
+            row.scrollIntoView(viewEl, false);
+
+            // Get the y position of the row relative to its top-most static parent.
+            // offsetTop will be relative to the table, and is incorrect
+            // when mixed with certain grid features (e.g., grouping).
+            y = row.getXY()[1] - 5;
+            rowH = row.getHeight();
+            newHeight = rowH + 10;
+
+            // IE doesn't set the height quite right.
+            // This isn't a border-box issue, it even happens
+            // in IE8 and IE7 quirks.
+            // TODO: Test in IE9!
+            if (Ext.isIE) {
+                newHeight += 2;
+            }
+
+            // Set editor height to match the row height
+            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)) {
+            // Query only form fields. This just future-proofs us in case we add
+            // other components to RowEditor later on.  Don't want to mess with
+            // indices.
+            return me.query('>[isFormField]')[fieldInfo];
+        } else if (fieldInfo instanceof Ext.grid.column.Column) {
+            return fieldInfo.getEditor();
+        }
+    },
+
+    removeField: function(field) {
+        var me = this;
+
+        // Incase we pass a column instead, which is fine
+        field = me.getEditor(field);
+        me.mun(field, 'validitychange', me.onValidityChange, me);
+
+        // Remove field/column from our mapping, which will fire the event to
+        // remove the field from our container
+        me.columns.removeKey(field.id);
+    },
+
+    setField: function(column) {
+        var me = this,
+            field;
+
+        if (Ext.isArray(column)) {
+            Ext.Array.forEach(column, me.setField, me);
+            return;
+        }
+
+        // Get a default display field if necessary
+        field = column.getEditor(null, { xtype: 'displayfield' });
+        field.margins = '0 0 0 2';
+        field.setWidth(column.getWidth() - 2);
+        me.mon(field, 'change', me.onFieldChange, me);
+
+        // Maintain mapping of fields-to-columns
+        // This will fire events that maintain our container items
+        me.columns.add(field.id, column);
+    },
+
+    loadRecord: function(record) {
+        var me = this,
+            form = me.getForm();
+        form.loadRecord(record);
+        if (form.isValid()) {
+            me.hideToolTip();
+        } else {
+            me.showToolTip();
+        }
+
+        // render display fields so they honor the column renderer/template
+        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 = field.getRawValue();
+
+        // honor our column's renderer (TemplateHeader sets renderer for us!)
+        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;
+        }
+    },
+
+    /**
+     * Start editing the specified grid at the specified position.
+     * @param {Model} record The Store data record which backs the row to be edited.
+     * @param {Model} columnHeader The Column object defining the column to be edited.
+     */
+    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
+            });
+
+        // make sure our row is selected before editing
+        context.grid.getSelectionModel().select(record);
+
+        // Reload the record data
+        me.loadRecord(record);
+
+        if (!me.isVisible()) {
+            me.show();
+            me.focusContextCell();
+        } else {
+            me.reposition({
+                callback: this.focusContextCell
+            });
+        }
+    },
+
+    // Focus the cell on start edit based upon the current context
+    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();
+        }
+    }
+});
+/**
+ * @class Ext.grid.header.Container
+ * @extends Ext.container.Container
+ * @private
+ *
+ * Container which holds headers and is docked at the top or bottom of a TablePanel.
+ * The HeaderContainer drives resizing/moving/hiding of columns within the TableView.
+ * As headers are hidden, moved or resized the headercontainer is responsible for
+ * triggering changes within the view.
+ *
+ * @xtype headercontainer
+ */
+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',
+
+    /**
+     * @cfg {Number} weight
+     * HeaderContainer overrides the default weight of 0 for all docked items to 100.
+     * This is so that it has more priority over things like toolbars.
+     */
+    weight: 100,
+    defaultType: 'gridcolumn',
+    /**
+     * @cfg {Number} defaultWidth
+     * Width of the header if no width or flex is specified. Defaults to 100.
+     */
+    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',
+
+    // private; will probably be removed by 4.0
+    triStateSort: false,
+
+    ddLock: false,
+
+    dragging: false,
+
+    /**
+     * <code>true</code> if this HeaderContainer is in fact a group header which contains sub headers.
+     * @type Boolean
+     * @property isGroupHeader
+     */
+
+    /**
+     * @cfg {Boolean} sortable
+     * Provides the default sortable state for all Headers within this HeaderContainer.
+     * Also turns on or off the menus in the HeaderContainer. Note that the menu is
+     * shared across every header and therefore turning it off will remove the menu
+     * items for every header.
+     */
+    sortable: true,
+    
+    initComponent: function() {
+        var me = this;
+        
+        me.headerCounter = 0;
+        me.plugins = me.plugins || [];
+
+        // TODO: Pass in configurations to turn on/off dynamic
+        //       resizing and disable resizing all together
+
+        // Only set up a Resizer and Reorderer for the topmost HeaderContainer.
+        // Nested Group Headers are themselves HeaderContainers
+        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);
+        }
+
+        // Base headers do not need a box layout
+        if (me.isHeader && !me.items) {
+            me.layout = 'auto';
+        }
+        // HeaderContainer and Group header needs a gridcolumn layout.
+        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(
+            /**
+             * @event columnresize
+             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+             * @param {Number} width
+             */
+            'columnresize',
+
+            /**
+             * @event headerclick
+             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+             * @param {Ext.EventObject} e
+             * @param {HTMLElement} t
+             */
+            'headerclick',
+
+            /**
+             * @event headertriggerclick
+             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+             * @param {Ext.EventObject} e
+             * @param {HTMLElement} t
+             */
+            'headertriggerclick',
+
+            /**
+             * @event columnmove
+             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+             * @param {Number} fromIdx
+             * @param {Number} toIdx
+             */
+            'columnmove',
+            /**
+             * @event columnhide
+             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+             */
+            'columnhide',
+            /**
+             * @event columnshow
+             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+             */
+            'columnshow',
+            /**
+             * @event sortchange
+             * @param {Ext.grid.header.Container} ct The grid's header Container which encapsulates all column headers.
+             * @param {Ext.grid.column.Column} column The Column header Component which provides the column definition
+             * @param {String} direction
+             */
+            'sortchange',
+            /**
+             * @event menucreate
+             * Fired immediately after the column header menu is created.
+             * @param {Ext.grid.header.Container} ct This instance
+             * @param {Ext.menu.Menu} menu The Menu that was created
+             */
+            'menucreate'
+        );
+    },
+
+    onDestroy: function() {
+        Ext.destroy(this.resizer, this.reorderer);
+        this.callParent();
+    },
+
+    // Invalidate column cache on add
+    // We cannot refresh the View on every add because this method is called
+    // when the HeaderDropZone moves Headers around, that will also refresh the view
+    onAdd: function(c) {
+        var me = this;
+        if (!c.headerId) {
+            c.headerId = 'h' + (++me.headerCounter);
+        }
+        me.callParent(arguments);
+        me.purgeCache();
+    },
+
+    // Invalidate column cache on remove
+    // We cannot refresh the View on every remove because this method is called
+    // when the HeaderDropZone moves Headers around, that will also refresh the view
+    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;
+
+            me.callParent(arguments);
+
+            if (topHeaders.length) {
+                topHeaders[0].el.radioCls(me.firstHeaderCls);
+                topHeaders[topHeaders.length - 1].el.radioCls(me.lastHeaderCls);
+            }
+        }
+    },
+
+    onHeaderShow: function(header) {
+        // Pass up to the GridSection
+        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);
+            }
+
+            // There's more than one header visible, and we've disabled some checked items... re-enable them
+            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 = [];
+                }
+            }
+        }
+
+        // Only update the grid UI when we are notified about base level Header shows;
+        // Group header shows just cause a layout of the HeaderContainer
+        if (!header.isGroupHeader) {
+            if (me.view) {
+                me.view.onHeaderShow(me, header, true);
+            }
+            if (gridSection) {
+                gridSection.onHeaderShow(me, header);
+            }
+        }
+        me.fireEvent('columnshow', me, header);
+
+        // The header's own hide suppresses cascading layouts, so lay the headers out now
+        me.doLayout();
+    },
+
+    onHeaderHide: function(header, suppressLayout) {
+        // Pass up to the GridSection
+        var me = this,
+            gridSection = me.ownerCt,
+            menu = me.getMenu(),
+            colCheckItem;
+
+        if (menu) {
+
+            // If the header was hidden programmatically, sync the Menu state
+            colCheckItem = menu.down('menucheckitem[headerId=' + header.id + ']');
+            if (colCheckItem) {
+                colCheckItem.setChecked(false, true);
+            }
+            me.setDisabledItems();
+        }
+
+        // Only update the UI when we are notified about base level Header hides;
+        if (!header.isGroupHeader) {
+            if (me.view) {
+                me.view.onHeaderHide(me, header, true);
+            }
+            if (gridSection) {
+                gridSection.onHeaderHide(me, header);
+            }
+
+            // The header's own hide suppresses cascading layouts, so lay the headers out now
+            if (!suppressLayout) {
+                me.doLayout();
+            }
+        }
+        me.fireEvent('columnhide', me, header);
+    },
+
+    setDisabledItems: function(){
+        var me = this,
+            menu = me.getMenu(),
+            i = 0,
+            len,
+            itemsToDisable,
+            itemToDisable;
+
+        // Find what to disable. If only one top level item remaining checked, we have to disable stuff.
+        itemsToDisable = menu.query('#columnItem>menucheckitem[checked]');
+        if ((itemsToDisable.length === 1)) {
+            if (!me.disabledMenuItems) {
+                me.disabledMenuItems = [];
+            }
+
+            // If down to only one column visible, also disable any descendant checkitems
+            if ((me.getVisibleGridColumns().length === 1) && itemsToDisable[0].menu) {
+                itemsToDisable = itemsToDisable.concat(itemsToDisable[0].menu.query('menucheckitem[checked]'));
+            }
+
+            len = itemsToDisable.length;
+            // Disable any further unchecking at any level.
+            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);
+                }
+            }
+        }
+    },
+
+    /**
+     * Temporarily lock the headerCt. This makes it so that clicking on headers
+     * don't trigger actions like sorting or opening of the header menu. This is
+     * done because extraneous events may be fired on the headers after interacting
+     * with a drag drop operation.
+     * @private
+     */
+    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) {
+        // generate and cache menu, provide ability to cancel/etc
+        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);
+        // TODO: remove coupling to Header's titleContainer el
+        header.titleContainer.addCls(this.headerOpenCls);
+
+        // enable or disable asc & desc menu items based on header being sortable
+        sortableMth = header.sortable ? 'enable' : 'disable';
+        if (ascItem) {
+            ascItem[sortableMth]();
+        }
+        if (descItem) {
+            descItem[sortableMth]();
+        }
+        menu.showBy(t);
+    },
+
+    // remove the trigger open class when the menu is hidden
+    onMenuDeactivate: function() {
+        var menu = this.getMenu();
+        // TODO: remove coupling to Header's titleContainer el
+        menu.activeHeader.titleContainer.removeCls(this.headerOpenCls);
+    },
+
+    moveHeader: function(fromIdx, toIdx) {
+
+        // An automatically expiring lock
+        this.tempLock();
+        this.onHeaderMoved(this.move(fromIdx, toIdx), fromIdx, toIdx);
+    },
+
+    purgeCache: function() {
+        var me = this;
+        // Delete column cache - column order has changed.
+        delete me.gridDataColumns;
+
+        // Menu changes when columns are moved. It will be recreated.
+        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);
+    },
+
+    /**
+     * Gets the menu (and will create it if it doesn't already exist)
+     * @private
+     */
+    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;
+    },
+
+    /**
+     * Returns an array of menu items to be placed into the shared menu
+     * across all headers in this header container.
+     * @returns {Array} menuItems
+     */
+    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;
+    },
+
+    // sort asc when clicking on item in menu
+    onSortAscClick: function() {
+        var menu = this.getMenu(),
+            activeHeader = menu.activeHeader;
+
+        activeHeader.setSortState('ASC');
+    },
+
+    // sort desc when clicking on item in menu
+    onSortDescClick: function() {
+        var menu = this.getMenu(),
+            activeHeader = menu.activeHeader;
+
+        activeHeader.setSortState('DESC');
+    },
+
+    /**
+     * Returns an array of menu CheckItems corresponding to all immediate children of the passed Container which have been configured as hideable.
+     */
+    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);
+
+            // If the header is ever destroyed - for instance by dragging out the last remaining sub header,
+            // then the associated menu item must also be destroyed.
+            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']();
+    },
+
+    /**
+     * Get the columns used for generating a template via TableChunker.
+     * Returns an array of all columns and their
+     *  - dataIndex
+     *  - align
+     *  - width
+     *  - id
+     *  - columnId - used to create an identifying CSS class
+     *  - cls The tdCls configuration from the Column object
+     *  @private
+     */
+    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;
+    },
+
+    /**
+     * Returns the number of <b>grid columns</b> descended from this HeaderContainer.
+     * Group Columns are HeaderContainers. All grid columns are returned, including hidden ones.
+     */
+    getColumnCount: function() {
+        return this.getGridColumns().length;
+    },
+
+    /**
+     * Gets the full width of all columns that are visible.
+     */
+    getFullWidth: function(flushCache) {
+        var fullWidth = 0,
+            headers     = this.getVisibleGridColumns(flushCache),
+            headersLn   = headers.length,
+            i         = 0;
+
+        for (; i < headersLn; i++) {
+            if (!isNaN(headers[i].width)) {
+                // use headers getDesiredWidth if its there
+                if (headers[i].getDesiredWidth) {
+                    fullWidth += headers[i].getDesiredWidth();
+                // if injected a diff cmp use getWidth
+                } else {
+                    fullWidth += headers[i].getWidth();
+                }
+            }
+        }
+        return fullWidth;
+    },
+
+    // invoked internally by a header when not using triStateSorting
+    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;
+                // unset the sortstate and dont recurse
+                headers[i].setSortState(null, true);
+                //if (!silent && oldSortState !== null) {
+                //    this.fireEvent('sortchange', this, headers[i], null);
+                //}
+            }
+        }
+    },
+
+    /**
+     * Returns an array of the <b>visible<b> columns in the grid. This goes down to the lowest column header
+     * level, and does not return <i>grouped</i> headers which contain sub headers.
+     * @param {Boolean} refreshCache If omitted, the cached set of columns will be returned. Pass true to refresh the cache.
+     * @returns {Array}
+     */
+    getVisibleGridColumns: function(refreshCache) {
+        return Ext.ComponentQuery.query(':not([hidden])', this.getGridColumns(refreshCache));
+    },
+
+    /**
+     * Returns an array of all columns which map to Store fields. This goes down to the lowest column header
+     * level, and does not return <i>grouped</i> headers which contain sub headers.
+     * @param {Boolean} refreshCache If omitted, the cached set of columns will be returned. Pass true to refresh the cache.
+     * @returns {Array}
+     */
+    getGridColumns: function(refreshCache) {
+        var me = this,
+            result = refreshCache ? null : me.gridDataColumns;
+
+        // Not already got the column cache, so collect the base columns
+        if (!result) {
+            me.gridDataColumns = result = [];
+            me.cascade(function(c) {
+                if ((c !== me) && !c.isGroupHeader) {
+                    result.push(c);
+                }
+            });
+        }
+
+        return result;
+    },
+
+    /**
+     * Get the index of a leaf level header regardless of what the nesting
+     * structure is.
+     */
+    getHeaderIndex: function(header) {
+        var columns = this.getGridColumns();
+        return Ext.Array.indexOf(columns, header);
+    },
+
+    /**
+     * Get a leaf level header by index regardless of what the nesting
+     * structure is.
+     */
+    getHeaderAtIndex: function(index) {
+        var columns = this.getGridColumns();
+        return columns[index];
+    },
+
+    /**
+     * Maps the record data to base it on the header id's.
+     * This correlates to the markup/template generated by
+     * TableChunker.
+     */
+    prepareData: function(data, rowIdx, record, view) {
+        var obj       = {},
+            headers   = this.getGridColumns(),
+            headersLn = headers.length,
+            colIdx    = 0,
+            header, value,
+            metaData,
+            g = this.up('tablepanel'),
+            store = g.store;
+
+        for (; colIdx < headersLn; colIdx++) {
+            metaData = {
+                tdCls: '',
+                style: ''
+            };
+            header = headers[colIdx];
+            value = data[header.dataIndex];
+
+            // When specifying a renderer as a string, it always resolves
+            // to Ext.util.Format
+            if (Ext.isString(header.renderer)) {
+                header.renderer = Ext.util.Format[header.renderer];
+            }
+
+            if (Ext.isFunction(header.renderer)) {
+                value = header.renderer.call(
+                    header.scope || this.ownerCt,
+                    value,
+                    // metadata per cell passing an obj by reference so that
+                    // it can be manipulated inside the renderer
+                    metaData,
+                    record,
+                    rowIdx,
+                    colIdx,
+                    store,
+                    view
+                );
+            }
+
+            // <debug>
+            if (metaData.css) {
+                // This warning attribute is used by the compat layer
+                obj.cssWarning = true;
+                metaData.tdCls = metaData.css;
+                delete metaData.css;
+            }
+            // </debug>
+            obj[header.id+'-modified'] = record.isModified(header.dataIndex) ? Ext.baseCSSPrefix + 'grid-dirty-cell' : Ext.baseCSSPrefix + 'grid-clean-cell';
+            obj[header.id+'-tdCls'] = metaData.tdCls;
+            obj[header.id+'-tdAttr'] = metaData.tdAttr;
+            obj[header.id+'-style'] = metaData.style;
+            if (value === undefined || value === null || value === '') {
+                value = '&#160;';
+            }
+            obj[header.id] = value;
+        }
+        return obj;
+    },
+
+    expandToFit: function(header) {
+        if (this.view) {
+            this.view.expandToFit(header);
+        }
+    }
+});
+
+/**
+ * @class Ext.grid.column.Column
+ * @extends Ext.grid.header.Container
+ * 
+ * This class specifies the definition for a column inside a {@link Ext.grid.Panel}. It encompasses
+ * both the grid header configuration as well as displaying data within the grid itself. If the
+ * {@link #columns} configuration is specified, this column will become a column group and can
+ * container other columns inside. In general, this class will not be created directly, rather
+ * an array of column configurations will be passed to the grid:
+ * 
+ * {@img Ext.grid.column.Column/Ext.grid.column.Column.png Ext.grid.column.Column grid column}
+ *
+ * ## Code
+ *    Ext.create('Ext.data.Store', {
+ *        storeId:'employeeStore',
+ *        fields:['firstname', 'lastname', 'senority', 'dep', 'hired'],
+ *        data:[
+ *            {firstname:"Michael", lastname:"Scott", senority:7, dep:"Manangement", hired:"01/10/2004"},
+ *            {firstname:"Dwight", lastname:"Schrute", senority:2, dep:"Sales", hired:"04/01/2004"},
+ *            {firstname:"Jim", lastname:"Halpert", senority:3, dep:"Sales", hired:"02/22/2006"},
+ *            {firstname:"Kevin", lastname:"Malone", senority:4, dep:"Accounting", hired:"06/10/2007"},
+ *            {firstname:"Angela", lastname:"Martin", senority:5, dep:"Accounting", hired:"10/21/2008"}                        
+ *        ]
+ *    });
+ *    
+ *    Ext.create('Ext.grid.Panel', {
+ *        title: 'Column Demo',
+ *        store: Ext.data.StoreManager.lookup('employeeStore'),
+ *        columns: [
+ *            {text: 'First Name',  dataIndex:'firstname'},
+ *            {text: 'Last Name',  dataIndex:'lastname'},
+ *            {text: 'Hired Month',  dataIndex:'hired', xtype:'datecolumn', format:'M'},              
+ *            {text: 'Deparment (Yrs)', xtype:'templatecolumn', tpl:'{dep} ({senority})'}
+ *        ],
+ *        width: 400,
+ *        renderTo: Ext.getBody()
+ *    });
+ *     
+ * ## Convenience Subclasses
+ * There are several column subclasses that provide default rendering for various data types
+ *
+ *  - {@link Ext.grid.column.Action}: Renders icons that can respond to click events inline
+ *  - {@link Ext.grid.column.Boolean}: Renders for boolean values 
+ *  - {@link Ext.grid.column.Date}: Renders for date values
+ *  - {@link Ext.grid.column.Number}: Renders for numeric values
+ *  - {@link Ext.grid.column.Template}: Renders a value using an {@link Ext.XTemplate} using the record data 
+ * 
+ * ## Setting Sizes
+ * The columns are laid out by a {@link Ext.layout.container.HBox} layout, so a column can either
+ * be given an explicit width value or a flex configuration. If no width is specified the grid will
+ * automatically the size the column to 100px. For column groups, the size is calculated by measuring
+ * the width of the child columns, so a width option should not be specified in that case.
+ * 
+ * ## Header Options
+ *  - {@link #text}: Sets the header text for the column
+ *  - {@link #sortable}: Specifies whether the column can be sorted by clicking the header or using the column menu
+ *  - {@link #hideable}: Specifies whether the column can be hidden using the column menu
+ *  - {@link #menuDisabled}: Disables the column header menu
+ *  - {@link #draggable}: Specifies whether the column header can be reordered by dragging
+ *  - {@link #groupable}: Specifies whether the grid can be grouped by the column dataIndex. See also {@link Ext.grid.feature.Grouping}
+ * 
+ * ## Data Options
+ *  - {@link #dataIndex}: The dataIndex is the field in the underlying {@link Ext.data.Store} to use as the value for the column.
+ *  - {@link #renderer}: Allows the underlying store value to be transformed before being displayed in the grid
+ * 
+ * @xtype gridcolumn
+ */
+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',
+
+    // Not the standard, automatically applied overCls because we must filter out overs of child headers.
+    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>',
+
+    /**
+     * @cfg {Array} columns
+     * <p>An optional array of sub-column definitions. This column becomes a group, and houses the columns defined in the <code>columns</code> config.</p>
+     * <p>Group columns may not be sortable. But they may be hideable and moveable. And you may move headers into and out of a group. Note that
+     * if all sub columns are dragged out of a group, the group is destroyed.
+     */
+
+    /**
+     * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
+     * grid's {@link Ext.data.Store}'s {@link Ext.data.Model} definition from
+     * which to draw the column's value.</p>
+     */
+    dataIndex: null,
+
+    /**
+     * @cfg {String} text Optional. The header text to be used as innerHTML
+     * (html tags are accepted) to display in the Grid.  <b>Note</b>: to
+     * have a clickable header with no text displayed you can use the
+     * default of <tt>'&#160;'</tt>.
+     */
+    text: '&#160',
+
+    /**
+     * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.
+     * Whether local/remote sorting is used is specified in <code>{@link Ext.data.Store#remoteSort}</code>.
+     */
+    sortable: true,
+    
+    /**
+     * @cfg {Boolean} groupable Optional. If the grid uses a {@link Ext.grid.feature.Grouping}, this option
+     * may be used to disable the header menu item to group by the column selected. By default,
+     * the header menu group option is enabled. Set to false to disable (but still show) the
+     * group option in the header menu for the column.
+     */
+     
+    /**
+     * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column
+     * (defaults to true).
+     */
+    hideable: true,
+
+    /**
+     * @cfg {Boolean} menuDisabled
+     * True to disabled the column header menu containing sort/hide options. Defaults to false.
+     */
+    menuDisabled: false,
+
+    /**
+     * @cfg {Function} renderer
+     * <p>A renderer is an 'interceptor' method which can be used transform data (value, appearance, etc.) before it
+     * is rendered. Example:</p>
+     * <pre><code>{
+    renderer: function(value){
+        if (value === 1) {
+            return '1 person';
+        }
+        return value + ' people';
+    }
+}
+     * </code></pre>
+     * @param {Mixed} value The data value for the current cell
+     * @param {Object} metaData A collection of metadata about the current cell; can be used or modified by
+     * the renderer. Recognized properties are: <tt>tdCls</tt>, <tt>tdAttr</tt>, and <tt>style</tt>.
+     * @param {Ext.data.Model} record The record for the current row
+     * @param {Number} rowIndex The index of the current row
+     * @param {Number} colIndex The index of the current column
+     * @param {Ext.data.Store} store The data store
+     * @param {Ext.view.View} view The current view
+     * @return {String} The HTML to be rendered
+     */
+    renderer: false,
+
+    /**
+     * @cfg {String} align Sets the alignment of the header and rendered columns.
+     * Defaults to 'left'.
+     */
+    align: 'left',
+
+    /**
+     * @cfg {Boolean} draggable Indicates whether or not the header can be drag and drop re-ordered.
+     * Defaults to true.
+     */
+    draggable: true,
+
+    // Header does not use the typical ComponentDraggable class and therefore we
+    // override this with an emptyFn. It is controlled at the HeaderDragZone.
+    initDraggable: Ext.emptyFn,
+
+    /**
+     * @cfg {String} tdCls <p>Optional. A CSS class names to apply to the table cells for this column.</p>
+     */
+
+    /**
+     * @property {Ext.core.Element} triggerEl
+     */
+
+    /**
+     * @property {Ext.core.Element} textEl
+     */
+
+    /**
+     * @private
+     * Set in this class to identify, at runtime, instances which are not instances of the
+     * HeaderContainer base class, but are in fact, the subclass: Header.
+     */
+    isHeader: true,
+
+    initComponent: function() {
+        var me = this,
+            i,
+            len;
+        
+        if (Ext.isDefined(me.header)) {
+            me.text = me.header;
+            delete me.header;
+        }
+
+        // Flexed Headers need to have a minWidth defined so that they can never be squeezed out of existence by the
+        // HeaderContainer's specialized Box layout, the ColumnLayout. The ColumnLayout's overridden calculateChildboxes
+        // method extends the available layout space to accommodate the "desiredWidth" of all the columns.
+        if (me.flex) {
+            me.minWidth = me.minWidth || Ext.grid.plugin.HeaderResizer.prototype.minColWidth;
+        }
+        // Non-flexed Headers may never be squeezed in the event of a shortfall so
+        // always set their minWidth to their current width.
+        else {
+            me.minWidth = me.width;
+        }
+
+        if (!me.triStateSort) {
+            me.possibleSortStates.length = 2;
+        }
+
+        // A group header; It contains items which are themselves Headers
+        if (Ext.isDefined(me.columns)) {
+            me.isGroupHeader = true;
+
+            //<debug>
+            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.');
+            }
+            //</debug>
+
+            // The headers become child items
+            me.items = me.columns;
+            delete me.columns;
+            delete me.flex;
+            me.width = 0;
+
+            // Acquire initial width from sub headers
+            for (i = 0, len = me.items.length; i < len; i++) {
+                me.width += me.items[i].width || Ext.grid.header.Container.prototype.defaultWidth;
+                //<debug>
+                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.');
+                }
+                //</debug>
+            }
+            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'
+        });
+
+        // Initialize as a HeaderContainer
+        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);
+    },
+
+    // note that this should invalidate the menu cache
+    setText: function(text) {
+        this.text = text;
+        if (this.rendered) {
+            this.textEl.update(text);
+        } 
+    },
+
+    // Find the topmost HeaderContainer: An ancestor which is NOT a Header.
+    // Group Headers are themselves HeaderContainers
+    getOwnerHeaderCt: function() {
+        return this.up(':not([isHeader])');
+    },
+
+    /**
+     * Returns the true grid column index assiciated with this Column only if this column is a base level Column.
+     * If it is a group column, it returns <code>false</code>
+     */
+    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
+        });
+        
+        // BrowserBug: Ie8 Strict Mode, this will break the focus for this browser,
+        // must be fixed when focus management will be implemented.
+        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) {
+
+            // Bubble size changes upwards to group headers
+            if (headerCt.isGroupHeader) {
+
+                siblings = headerCt.items.items;
+                len = siblings.length;
+
+                // Size the owning group to the size of its sub headers 
+                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);
+
+        // Only changes at the base level inform the grid's HeaderContainer which will update the View
+        // Skip this if the width is null or undefined which will be the Box layout's initial pass  through the child Components
+        // Skip this if it's the initial size setting in which case there is no ownerheaderCt yet - that is set afterRender
+        if (width && !me.isGroupHeader && ownerHeaderCt) {
+            ownerHeaderCt.onHeaderResize(me, width, true);
+        }
+    },
+
+    // private
+    // After the container has laid out and stretched, it calls this to correctly pad the inner to center the text vertically
+    setPadding: function() {
+        var me = this,
+            headerHeight,
+            lineHeight = parseInt(me.textEl.getStyle('line-height'), 10);
+
+        // Top title containing element must stretch to match height of sibling group headers
+        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;
+
+        // Vertically center the header text in potentially vertically stretched header
+        if (lineHeight) {
+            me.titleContainer.setStyle({
+                paddingTop: Math.max(((headerHeight - lineHeight) / 2), 0) + 'px'
+            });
+        }
+
+        // Only IE needs this
+        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);
+    },
+
+    /**
+     * @private
+     * Double click 
+     * @param e
+     * @param t
+     */
+    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) {
+
+        // The grid's docked HeaderContainer.
+        var me = this,
+            ownerHeaderCt = me.getOwnerHeaderCt();
+
+        if (ownerHeaderCt && !ownerHeaderCt.ddLock) {
+            // Firefox doesn't check the current target in a within check.
+            // Therefore we check the target directly and then within (ancestors)
+            if (me.triggerEl && (e.target === me.triggerEl.dom || t === me.triggerEl.dom || e.within(me.triggerEl))) {
+                ownerHeaderCt.onHeaderTriggerClick(me, e, t);
+            // if its not on the left hand edge, sort
+            } else if (e.getKey() || (!me.isOnLeftEdge(e) && !me.isOnRightEdge(e))) {
+                me.toggleSortState();
+                ownerHeaderCt.onHeaderClick(me, e, t);
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Process UI events from the view. The owning TablePanel calls this method, relaying events from the TableView
+     * @param {String} type Event type, eg 'click'
+     * @param {TableView} view TableView Component
+     * @param {HtmlElement} cell Cell HtmlElement the event took place within
+     * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
+     * @param {Number} cellIndex Cell index within the row
+     * @param {EventObject} e Original event
+     */
+    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
+        });
+    },
+
+    /**
+     * Returns the parameter to sort upon when sorting this header. By default
+     * this returns the dataIndex and will not need to be overriden in most cases.
+     */
+    getSortParam: function() {
+        return this.dataIndex;
+    },
+
+    //setSortState: function(state, updateUI) {
+    //setSortState: function(state, doSort) {
+    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);
+            // don't trigger a sort on the first time, we just want to update the UI
+            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();
+
+        // Hiding means setting to zero width, so cache the width
+        me.oldWidth = me.getWidth();
+
+        // Hiding a group header hides itself, and then informs the HeaderContainer about its sub headers (Suppressing header layout)
+        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;
+        }
+
+        // TODO: Work with Jamie to produce a scheme where we can show/hide/resize without triggering a layout cascade
+        lb = me.ownerCt.componentLayout.layoutBusy;
+        me.ownerCt.componentLayout.layoutBusy = true;
+        me.callParent(arguments);
+        me.ownerCt.componentLayout.layoutBusy = lb;
+
+        // Notify owning HeaderContainer
+        ownerHeaderCt.onHeaderHide(me);
+
+        if (me.ownerCt.isGroupHeader) {
+            // If we've just hidden the last header in a group, then hide the group
+            items = me.ownerCt.query('>:not([hidden])');
+            if (!items.length) {
+                me.ownerCt.hide();
+            }
+            // Size the group down to accommodate fewer sub headers
+            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;
+
+        // TODO: Work with Jamie to produce a scheme where we can show/hide/resize without triggering a layout cascade
+        lb = me.ownerCt.componentLayout.layoutBusy;
+        me.ownerCt.componentLayout.layoutBusy = true;
+        me.callParent(arguments);
+        me.ownerCt.componentLayout.layoutBusy = lb;
+
+        // If a sub header, ensure that the group header is visible
+        if (me.isSubHeader) {
+            if (!me.ownerCt.isVisible()) {
+                me.ownerCt.show();
+            }
+        }
+
+        // If we've just shown a group with all its sub headers hidden, then show all its sub headers
+        if (me.isGroupHeader && !me.query(':not([hidden])').length) {
+            items = me.query('>*');
+            for (i = 0, len = items.length; i < len; i++) {
+                items[i].show();
+            }
+        }
+
+        // Resize the owning group to accommodate
+        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);
+        }
+
+        // Notify owning HeaderContainer
+        if (ownerCt) {
+            ownerCt.onHeaderShow(me);
+        }
+    },
+
+    getDesiredWidth: function() {
+        var me = this;
+        if (me.rendered && me.componentLayout && me.componentLayout.lastComponentSize) {
+            // headers always have either a width or a flex
+            // because HeaderContainer sets a defaults width
+            // therefore we can ignore the natural width
+            // we use the componentLayout's tracked width so that
+            // we can calculate the desired width when rendered
+            // but not visible because its being obscured by a layout
+            return me.componentLayout.lastComponentSize.width;
+        // Flexed but yet to be rendered this could be the case
+        // where a HeaderContainer and Headers are simply used as data
+        // structures and not rendered.
+        }
+        else if (me.flex) {
+            // this is going to be wrong, the defaultWidth
+            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);
+    }
+    
+    /**
+     * Retrieves the editing field for editing associated with this header. Returns false if there
+     * is no field associated with the Header the method will return false. If the
+     * field has not been instantiated it will be created. Note: These methods only has an implementation
+     * if a Editing plugin has been enabled on the grid.
+     * @param record The {@link Ext.data.Model Model} instance being edited.
+     * @param {Mixed} defaultField An object representing a default field to be created
+     * @returns {Ext.form.field.Field} field
+     * @method getEditor
+     */
+    // intentionally omit getEditor and setEditor definitions bc we applyIf into columns
+    // when the editing plugin is injected
+    
+    
+    /**
+     * Sets the form field to be used for editing. Note: This method only has an implementation
+     * if an Editing plugin has been enabled on the grid.
+     * @param {Mixed} field An object representing a field to be created. If no xtype is specified a 'textfield' is assumed.
+     * @method setEditor
+     */
+});
+/**
+ * @class Ext.grid.RowNumberer
+ * @extends Ext.grid.column.Column
+ * This is a utility class that can be passed into a {@link Ext.grid.column.Column} as a column config that provides
+ * an automatic row numbering column.
+ * <br>Usage:<br><pre><code>
+columns: [
+    Ext.create('Ext.grid.RowNumberer'),
+    {text: "Company", flex: 1, sortable: true, dataIndex: 'company'},
+    {text: "Price", width: 120, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
+    {text: "Change", width: 120, sortable: true, dataIndex: 'change'},
+    {text: "% Change", width: 120, sortable: true, dataIndex: 'pctChange'},
+    {text: "Last Updated", width: 120, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
+]
+ *</code></pre>
+ * @constructor
+ * @param {Object} config The configuration options
+ */
+Ext.define('Ext.grid.RowNumberer', {
+    extend: 'Ext.grid.column.Column',
+    alias: 'widget.rownumberer',
+    /**
+     * @cfg {String} text Any valid text or HTML fragment to display in the header cell for the row
+     * number column (defaults to '&#160').
+     */
+    text: "&#160",
+
+    /**
+     * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).
+     */
+    width: 23,
+
+    /**
+     * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).
+     * @hide
+     */
+    sortable: false,
+
+    align: 'right',
+
+    constructor : function(config){
+        this.callParent(arguments);
+        if (this.rowspan) {
+            this.renderer = Ext.Function.bind(this.renderer, this);
+        }
+    },
+
+    // private
+    fixed: true,
+    hideable: false,
+    menuDisabled: true,
+    dataIndex: '',
+    cls: Ext.baseCSSPrefix + 'row-numberer',
+    rowspan: undefined,
+
+    // private
+    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;
+    }
+});
+
+/**
+ * @class Ext.view.DropZone
+ * @extends Ext.dd.DropZone
+ * @private
+ */
+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);
+
+        // Create a ddGroup unless one has been configured.
+        // User configuration of ddGroups allows users to specify which
+        // DD instances can interact with each other. Using one
+        // based on the id of the View would isolate it and mean it can only
+        // interact with a DragZone on the same View also using a generated ID.
+        if (!me.ddGroup) {
+            me.ddGroup = 'view-dd-zone-' + me.view.id;
+        }
+
+        // The DropZone's encapsulating element is the View's main element. It must be this because drop gestures
+        // may require scrolling on hover near a scrolling boundary. In Ext 4.x two DD instances may not use the
+        // same element, so a DragZone on this same View must use the View's parent element as its element.
+        me.callParent([me.view.el]);
+    },
+
+//  Fire an event through the client DataView. Lock this DropZone during the event processing so that
+//  its data does not become corrupted by processing mouse events.
+    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;
+
+//      Not over a row node: The content may be narrower than the View's encapsulating element, so return the closest.
+//      If we fall through because the mouse is below the nodes (or there are no nodes), we'll get an onContainerOver call.
+        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;
+    },
+
+    /**
+     * @private Determines whether the record at the specified offset from the passed record
+     * is in the drag payload.
+     * @param records
+     * @param record
+     * @param offset
+     * @returns {Boolean} True if the targeted record is in the drag payload
+     */
+    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);
+
+                // Cache the overRecord and the 'before' or 'after' indicator.
+                me.overRecord = overRecord;
+                me.currentPosition = pos;
+            }
+        } else {
+            me.invalidateDrop();
+        }
+    },
+
+    invalidateDrop: function() {
+        if (this.valid) {
+            this.valid = false;
+            this.getIndicator().hide();
+        }
+    },
+
+    // The mouse is over a View node
+    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;
+    },
+
+    // Moved out of the DropZone without dropping.
+    // Remove drop position indicator
+    notifyOut: function(node, dragZone, e, data) {
+        this.callParent(arguments);
+        delete this.overRecord;
+        delete this.currentPosition;
+        if (this.indicator) {
+            this.indicator.hide();
+        }
+    },
+
+    // The mouse is past the end of all nodes (or there are no nodes)
+    onContainerOver : function(dd, e, data) {
+        var v = this.view,
+            c = v.store.getCount();
+
+        // There are records, so position after the last one
+        if (c) {
+            this.positionIndicator(v.getNode(c - 1), data, e);
+        }
+
+        // No records, position the indicator at the top
+        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,
+
+            // Create a closure to perform the operation which the event handler may use.
+            // Users may now return <code>0</code> from the beforedrop handler, and perform any kind
+            // of asynchronous processing such as an Ext.Msg.confirm, or an Ajax request,
+            // and complete the drop gesture at some point in the future by calling this function.
+            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 the processDrop function was called in the event handler, do not do it again.
+                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 the copy flag is set, create a copy of the Models with the same IDs
+        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 {
+            /*
+             * Remove from the source store. We do this regardless of whether the store
+             * is the same bacsue the store currently doesn't handle moving records
+             * within the store. In the future it should be possible to do this.
+             * Here was pass the isMove parameter if we're moving to the same view.
+             */
+            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);
+    }
+});
+/**
+ * @class Ext.grid.column.Action
+ * @extends Ext.grid.column.Column
+ * <p>A Grid header type which renders an icon, or a series of icons in a grid cell, and offers a scoped click
+ * handler for each icon.</p>
+ *
+ * {@img Ext.grid.column.Action/Ext.grid.column.Action.png Ext.grid.column.Action grid column}
+ *  
+ * ## Code
+ *     Ext.create('Ext.data.Store', {
+ *         storeId:'employeeStore',
+ *         fields:['firstname', 'lastname', 'senority', 'dep', 'hired'],
+ *         data:[
+ *             {firstname:"Michael", lastname:"Scott"},
+ *             {firstname:"Dwight", lastname:"Schrute"},
+ *             {firstname:"Jim", lastname:"Halpert"},
+ *             {firstname:"Kevin", lastname:"Malone"},
+ *             {firstname:"Angela", lastname:"Martin"}                        
+ *         ]
+ *     });
+ *     
+ *     Ext.create('Ext.grid.Panel', {
+ *         title: 'Action Column Demo',
+ *         store: Ext.data.StoreManager.lookup('employeeStore'),
+ *         columns: [
+ *             {text: 'First Name',  dataIndex:'firstname'},
+ *             {text: 'Last Name',  dataIndex:'lastname'},
+ *             {
+ *                 xtype:'actioncolumn', 
+ *                 width:50,
+ *                 items: [{
+ *                     icon: 'images/edit.png',  // Use a URL in the icon config
+ *                     tooltip: 'Edit',
+ *                     handler: function(grid, rowIndex, colIndex) {
+ *                         var rec = grid.getStore().getAt(rowIndex);
+ *                         alert("Edit " + rec.get('firstname'));
+ *                     }
+ *                 },{
+ *                     icon: 'images/delete.png',
+ *                     tooltip: 'Delete',
+ *                     handler: function(grid, rowIndex, colIndex) {
+ *                         var rec = grid.getStore().getAt(rowIndex);
+ *                         alert("Terminate " + rec.get('firstname'));
+ *                     }                
+ *                 }]
+ *             }
+ *         ],
+ *         width: 250,
+ *         renderTo: Ext.getBody()
+ *     });
+ * <p>The action column can be at any index in the columns array, and a grid can have any number of
+ * action columns. </p>
+ * @xtype actioncolumn
+ */
+Ext.define('Ext.grid.column.Action', {
+    extend: 'Ext.grid.column.Column',
+    alias: ['widget.actioncolumn'],
+    alternateClassName: 'Ext.grid.ActionColumn',
+
+    /**
+     * @cfg {String} icon
+     * The URL of an image to display as the clickable element in the column. 
+     * Optional - defaults to <code>{@link Ext#BLANK_IMAGE_URL Ext.BLANK_IMAGE_URL}</code>.
+     */
+    /**
+     * @cfg {String} iconCls
+     * A CSS class to apply to the icon image. To determine the class dynamically, configure the Column with a <code>{@link #getClass}</code> function.
+     */
+    /**
+     * @cfg {Function} handler A function called when the icon is clicked.
+     * The handler is passed the following parameters:<div class="mdetail-params"><ul>
+     * <li><code>view</code> : TableView<div class="sub-desc">The owning TableView.</div></li>
+     * <li><code>rowIndex</code> : Number<div class="sub-desc">The row index clicked on.</div></li>
+     * <li><code>colIndex</code> : Number<div class="sub-desc">The column index clicked on.</div></li>
+     * <li><code>item</code> : Object<div class="sub-desc">The clicked item (or this Column if multiple 
+     * {@link #items} were not configured).</div></li>
+     * <li><code>e</code> : Event<div class="sub-desc">The click event.</div></li>
+     * </ul></div>
+     */
+    /**
+     * @cfg {Object} scope The scope (<tt><b>this</b></tt> reference) in which the <code>{@link #handler}</code>
+     * and <code>{@link #getClass}</code> fuctions are executed. Defaults to this Column.
+     */
+    /**
+     * @cfg {String} tooltip A tooltip message to be displayed on hover. {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must have 
+     * been initialized.
+     */
+    /**
+     * @cfg {Boolean} stopSelection Defaults to <code>true</code>. Prevent grid <i>row</i> selection upon mousedown.
+     */
+    /**
+     * @cfg {Function} getClass A function which returns the CSS class to apply to the icon image.
+     * The function is passed the following parameters:<ul>
+     *     <li><b>v</b> : Object<p class="sub-desc">The value of the column's configured field (if any).</p></li>
+     *     <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
+     *         <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
+     *         <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
+     *         (e.g. 'style="color:red;"').</p></li>
+     *     </ul></p></li>
+     *     <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data.</p></li>
+     *     <li><b>rowIndex</b> : Number<p class="sub-desc">The row index..</p></li>
+     *     <li><b>colIndex</b> : Number<p class="sub-desc">The column index.</p></li>
+     *     <li><b>store</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
+     * </ul>
+     */
+    /**
+     * @cfg {Array} items An Array which may contain multiple icon definitions, each element of which may contain:
+     * <div class="mdetail-params"><ul>
+     * <li><code>icon</code> : String<div class="sub-desc">The url of an image to display as the clickable element 
+     * in the column.</div></li>
+     * <li><code>iconCls</code> : String<div class="sub-desc">A CSS class to apply to the icon image.
+     * To determine the class dynamically, configure the item with a <code>getClass</code> function.</div></li>
+     * <li><code>getClass</code> : Function<div class="sub-desc">A function which returns the CSS class to apply to the icon image.
+     * The function is passed the following parameters:<ul>
+     *     <li><b>v</b> : Object<p class="sub-desc">The value of the column's configured field (if any).</p></li>
+     *     <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
+     *         <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
+     *         <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell
+     *         (e.g. 'style="color:red;"').</p></li>
+     *     </ul></p></li>
+     *     <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data.</p></li>
+     *     <li><b>rowIndex</b> : Number<p class="sub-desc">The row index..</p></li>
+     *     <li><b>colIndex</b> : Number<p class="sub-desc">The column index.</p></li>
+     *     <li><b>store</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
+     * </ul></div></li>
+     * <li><code>handler</code> : Function<div class="sub-desc">A function called when the icon is clicked.</div></li>
+     * <li><code>scope</code> : Scope<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the 
+     * <code>handler</code> and <code>getClass</code> functions are executed. Fallback defaults are this Column's
+     * configured scope, then this Column.</div></li>
+     * <li><code>tooltip</code> : String<div class="sub-desc">A tooltip message to be displayed on hover. 
+     * {@link Ext.tip.QuickTipManager#init Ext.tip.QuickTipManager} must have been initialized.</div></li>
+     * </ul></div>
+     */
+    header: '&#160;',
+
+    actionIdRe: /x-action-col-(\d+)/,
+
+    /**
+     * @cfg {String} altText The alt text to use for the image element. Defaults to <tt>''</tt>.
+     */
+    altText: '',
+    
+    sortable: false,
+
+    constructor: function(config) {
+        var me = this,
+            cfg = Ext.apply({}, config),
+            items = cfg.items || [me],
+            l = items.length,
+            i,
+            item;
+
+        // This is a Container. Delete the items config to be reinstated after construction.
+        delete cfg.items;
+        this.callParent([cfg]);
+
+        // Items is an array property of ActionColumns
+        me.items = items;
+
+//      Renderer closure iterates through items creating an <img> element for each and tagging with an identifying 
+//      class name x-action-col-{n}
+        me.renderer = function(v, meta) {
+//          Allow a configured renderer to create initial value (And set the other values in the "metadata" argument!)
+            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);
+    },
+
+    /**
+     * @private
+     * Process and refire events routed from the GridView's processEvent method.
+     * Also fires any configured click handlers. By default, cancels the mousedown event to prevent selection.
+     * Returns the event handler's status to allow canceling of GridView's bubbling process.
+     */
+    processEvent : function(type, view, cell, recordIndex, cellIndex, e){
+        var m = e.getTarget().className.match(this.actionIdRe),
+            item, fn;
+        if (m && (item = this.items[parseInt(m[1], 10)])) {
+            if (type == 'click') {
+                fn = item.handler;
+                if (fn || this.handler) {
+                    fn.call(item.scope||this.scope||this, view, recordIndex, cellIndex, item, e);
+                }
+            } else if ((type == 'mousedown') && (item.stopSelection !== false)) {
+                return false;
+            }
+        }
+        return this.callParent(arguments);
+    },
+
+    cascade: function(fn, scope) {
+        fn.call(scope||this, this);
+    },
+
+    // Private override because this cannot function as a Container, and it has an items property which is an Array, NOT a MixedCollection.
+    getRefItems: function() {
+        return [];
+    }
+});
+/**
+ * @class Ext.grid.column.Boolean
+ * @extends Ext.grid.column.Column
+ * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.column.Column#xtype xtype}
+ * config option of {@link Ext.grid.column.Column} for more details.</p>
+ *
+ * {@img Ext.grid.column.Boolean/Ext.grid.column.Boolean.png Ext.grid.column.Boolean grid column}
+ *
+ *  ## Code
+ *     Ext.create('Ext.data.Store', {
+ *        storeId:'sampleStore',
+ *        fields:[
+ *            {name: 'framework', type: 'string'},
+ *            {name: 'rocks', type: 'boolean'}
+ *        ],
+ *        data:{'items':[
+ *            {"framework":"Ext JS 4", "rocks":true},
+ *            {"framework":"Sencha Touch", "rocks":true},
+ *            {"framework":"Ext GWT", "rocks":true},            
+ *            {"framework":"Other Guys", "rocks":false}            
+ *        ]},
+ *        proxy: {
+ *            type: 'memory',
+ *            reader: {
+ *                type: 'json',
+ *                root: 'items'
+ *            }
+ *        }
+ *    });
+ *    
+ *    Ext.create('Ext.grid.Panel', {
+ *        title: 'Boolean Column Demo',
+ *        store: Ext.data.StoreManager.lookup('sampleStore'),
+ *        columns: [
+ *            {text: 'Framework',  dataIndex: 'framework', flex: 1},
+ *            {
+ *                xtype: 'booleancolumn', 
+ *                text: 'Rocks',
+ *                trueText: 'Yes',
+ *                falseText: 'No', 
+ *                dataIndex: 'rocks'}
+ *        ],
+ *        height: 200,
+ *        width: 400,
+ *        renderTo: Ext.getBody()
+ *    });
+ * 
+ * @xtype booleancolumn
+ */
+Ext.define('Ext.grid.column.Boolean', {
+    extend: 'Ext.grid.column.Column',
+    alias: ['widget.booleancolumn'],
+    alternateClassName: 'Ext.grid.BooleanColumn',
+
+    /**
+     * @cfg {String} trueText
+     * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
+     */
+    trueText: 'true',
+
+    /**
+     * @cfg {String} falseText
+     * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
+     * <tt>'false'</tt>).
+     */
+    falseText: 'false',
+
+    /**
+     * @cfg {String} undefinedText
+     * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
+     */
+    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;
+        };
+    }
+});
+/**
+ * @class Ext.grid.column.Date
+ * @extends Ext.grid.column.Column
+ * <p>A Column definition class which renders a passed date according to the default locale, or a configured
+ * {@link #format}.</p>
+ *
+ * {@img Ext.grid.column.Date/Ext.grid.column.Date.png Ext.grid.column.Date grid column}
+ *
+ * ## Code
+ *    Ext.create('Ext.data.Store', {
+ *        storeId:'sampleStore',
+ *        fields:[
+ *            {name: 'symbol', type: 'string'},
+ *            {name: 'date', type: 'date'},
+ *            {name: 'change', type: 'number'},
+ *            {name: 'volume', type: 'number'},
+ *            {name: 'topday', type: 'date'}                        
+ *        ],
+ *        data:[
+ *            {symbol:"msft", date:'2011/04/22', change:2.43, volume:61606325, topday:'04/01/2010'},
+ *            {symbol:"goog", date:'2011/04/22', change:0.81, volume:3053782, topday:'04/11/2010'},
+ *            {symbol:"apple", date:'2011/04/22', change:1.35, volume:24484858, topday:'04/28/2010'},            
+ *            {symbol:"sencha", date:'2011/04/22', change:8.85, volume:5556351, topday:'04/22/2010'}            
+ *        ]
+ *    });
+ *    
+ *    Ext.create('Ext.grid.Panel', {
+ *        title: 'Date Column Demo',
+ *        store: Ext.data.StoreManager.lookup('sampleStore'),
+ *        columns: [
+ *            {text: 'Symbol',  dataIndex: 'symbol', flex: 1},
+ *            {text: 'Date',  dataIndex: 'date', xtype: 'datecolumn', format:'Y-m-d'},
+ *            {text: 'Change',  dataIndex: 'change', xtype: 'numbercolumn', format:'0.00'},
+ *            {text: 'Volume',  dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000'},
+ *            {text: 'Top Day',  dataIndex: 'topday', xtype: 'datecolumn', format:'l'}            
+ *        ],
+ *        height: 200,
+ *        width: 450,
+ *        renderTo: Ext.getBody()
+ *    });
+ *    
+ * @xtype datecolumn
+ */
+Ext.define('Ext.grid.column.Date', {
+    extend: 'Ext.grid.column.Column',
+    alias: ['widget.datecolumn'],
+    requires: ['Ext.Date'],
+    alternateClassName: 'Ext.grid.DateColumn',
+
+    /**
+     * @cfg {String} format
+     * A formatting string as used by {@link Date#format Date.format} to format a Date for this Column.
+     * This defaults to the default date from {@link Ext.Date#defaultFormat} which itself my be overridden
+     * in a locale file.
+     */
+    format : Ext.Date.defaultFormat,
+
+    constructor: function(cfg){
+        this.callParent(arguments);
+        this.renderer = Ext.util.Format.dateRenderer(this.format);
+    }
+});
+/**
+ * @class Ext.grid.column.Number
+ * @extends Ext.grid.column.Column
+ * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.</p>
+ *
+ * {@img Ext.grid.column.Number/Ext.grid.column.Number.png Ext.grid.column.Number cell editing}
+ *
+ * ## Code
+ *     Ext.create('Ext.data.Store', {
+ *        storeId:'sampleStore',
+ *        fields:[
+ *            {name: 'symbol', type: 'string'},
+ *            {name: 'price', type: 'number'},
+ *            {name: 'change', type: 'number'},
+ *            {name: 'volume', type: 'number'},            
+ *        ],
+ *        data:[
+ *            {symbol:"msft", price:25.76, change:2.43, volume:61606325},
+ *            {symbol:"goog", price:525.73, change:0.81, volume:3053782},
+ *            {symbol:"apple", price:342.41, change:1.35, volume:24484858},            
+ *            {symbol:"sencha", price:142.08, change:8.85, volume:5556351}            
+ *        ]
+ *    });
+ *    
+ *    Ext.create('Ext.grid.Panel', {
+ *        title: 'Number Column Demo',
+ *        store: Ext.data.StoreManager.lookup('sampleStore'),
+ *        columns: [
+ *            {text: 'Symbol',  dataIndex: 'symbol', flex: 1},
+ *            {text: 'Current Price',  dataIndex: 'price', renderer: Ext.util.Format.usMoney},
+ *            {text: 'Change',  dataIndex: 'change', xtype: 'numbercolumn', format:'0.00'},
+ *            {text: 'Volume',  dataIndex: 'volume', xtype: 'numbercolumn', format:'0,000'}
+ *        ],
+ *        height: 200,
+ *        width: 400,
+ *        renderTo: Ext.getBody()
+ *    });
+ * 
+ * @xtype numbercolumn
+ */
+Ext.define('Ext.grid.column.Number', {
+    extend: 'Ext.grid.column.Column',
+    alias: ['widget.numbercolumn'],
+    requires: ['Ext.util.Format'],
+    alternateClassName: 'Ext.grid.NumberColumn',
+
+    /**
+     * @cfg {String} format
+     * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
+     * (defaults to <code>'0,000.00'</code>).
+     */
+    format : '0,000.00',
+    constructor: function(cfg) {
+        this.callParent(arguments);
+        this.renderer = Ext.util.Format.numberRenderer(this.format);
+    }
+});
+/**
+ * @class Ext.grid.column.Template
+ * @extends Ext.grid.column.Column
+ * 
+ * A Column definition class which renders a value by processing a {@link Ext.data.Model Model}'s
+ * {@link Ext.data.Model#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.
+ * 
+ *  {@img Ext.grid.column.Template/Ext.grid.column.Template.png Ext.grid.column.Template grid column}
+ * 
+ * ## Code
+ *     Ext.create('Ext.data.Store', {
+ *         storeId:'employeeStore',
+ *         fields:['firstname', 'lastname', 'senority', 'department'],
+ *         groupField: 'department',
+ *         data:[
+ *             {firstname:"Michael", lastname:"Scott", senority:7, department:"Manangement"},
+ *             {firstname:"Dwight", lastname:"Schrute", senority:2, department:"Sales"},
+ *             {firstname:"Jim", lastname:"Halpert", senority:3, department:"Sales"},
+ *             {firstname:"Kevin", lastname:"Malone", senority:4, department:"Accounting"},
+ *             {firstname:"Angela", lastname:"Martin", senority:5, department:"Accounting"}                        
+ *         ]
+ *     });
+ *     
+ *     Ext.create('Ext.grid.Panel', {
+ *         title: 'Column Template Demo',
+ *         store: Ext.data.StoreManager.lookup('employeeStore'),
+ *         columns: [
+ *             {text: 'Full Name',  xtype:'templatecolumn', tpl:'{firstname} {lastname}', flex:1},
+ *             {text: 'Deparment (Yrs)', xtype:'templatecolumn', tpl:'{department} ({senority})'}
+ *         ],
+ *         height: 200,
+ *         width: 300,
+ *         renderTo: Ext.getBody()
+ *     });
+ * 
+ * @markdown
+ * @xtype templatecolumn
+ */
+Ext.define('Ext.grid.column.Template', {
+    extend: 'Ext.grid.column.Column',
+    alias: ['widget.templatecolumn'],
+    requires: ['Ext.XTemplate'],
+    alternateClassName: 'Ext.grid.TemplateColumn',
+
+    /**
+     * @cfg {String/XTemplate} tpl
+     * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a
+     * {@link Ext.data.Model Model}'s {@link Ext.data.Model#data data} to produce a column's rendered value.
+     */
+    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);
+        };
+    }
+});
+
+/**
+ * @class Ext.grid.feature.Feature
+ * @extends Ext.util.Observable
+ * 
+ * A feature is a type of plugin that is specific to the {@link Ext.grid.Panel}. It provides several
+ * hooks that allows the developer to inject additional functionality at certain points throughout the 
+ * grid creation cycle. This class provides the base template methods that are available to the developer,
+ * it should be extended.
+ * 
+ * There are several built in features that extend this class, for example:
+ *
+ *  - {@link Ext.grid.feature.Grouping} - Shows grid rows in groups as specified by the {@link Ext.data.Store}
+ *  - {@link Ext.grid.feature.RowBody} - Adds a body section for each grid row that can contain markup.
+ *  - {@link Ext.grid.feature.Summary} - Adds a summary row at the bottom of the grid with aggregate totals for a column.
+ * 
+ * ## Using Features
+ * A feature is added to the grid by specifying it an array of features in the configuration:
+ * 
+ *     var groupingFeature = Ext.create('Ext.grid.feature.Grouping');
+ *     Ext.create('Ext.grid.Panel', {
+ *         // other options
+ *         features: [groupingFeature]
+ *     });
+ * 
+ * @abstract
+ */
+Ext.define('Ext.grid.feature.Feature', {
+    extend: 'Ext.util.Observable',
+    alias: 'feature.feature',
+    
+    isFeature: true,
+    disabled: false,
+    
+    /**
+     * @property {Boolean}
+     * Most features will expose additional events, some may not and will
+     * need to change this to false.
+     */
+    hasFeatureEvent: true,
+    
+    /**
+     * @property {String}
+     * Prefix to use when firing events on the view.
+     * For example a prefix of group would expose "groupclick", "groupcontextmenu", "groupdblclick".
+     */
+    eventPrefix: null,
+    
+    /**
+     * @property {String}
+     * Selector used to determine when to fire the event with the eventPrefix.
+     */
+    eventSelector: null,
+    
+    /**
+     * @property {Ext.view.Table}
+     * Reference to the TableView.
+     */
+    view: null,
+    
+    /**
+     * @property {Ext.grid.Panel}
+     * Reference to the grid panel
+     */
+    grid: null,
+    
+    /**
+     * Most features will not modify the data returned to the view.
+     * This is limited to one feature that manipulates the data per grid view.
+     */
+    collectData: false,
+        
+    getFeatureTpl: function() {
+        return '';
+    },
+    
+    /**
+     * Abstract method to be overriden when a feature should add additional
+     * arguments to its event signature. By default the event will fire:
+     * - view - The underlying Ext.view.Table
+     * - featureTarget - The matched element by the defined {@link eventSelector}
+     *
+     * The method must also return the eventName as the first index of the array
+     * to be passed to fireEvent.
+     */
+    getFireEventArgs: function(eventName, view, featureTarget) {
+        return [eventName, view, featureTarget];
+    },
+    
+    /**
+     * Approriate place to attach events to the view, selectionmodel, headerCt, etc
+     */
+    attachEvents: function() {
+        
+    },
+    
+    getFragmentTpl: function() {
+        return;
+    },
+    
+    /**
+     * Allows a feature to mutate the metaRowTpl.
+     * The array received as a single argument can be manipulated to add things
+     * on the end/begining of a particular row.
+     */
+    mutateMetaRowTpl: function(metaRowTplArray) {
+        
+    },
+    
+    /**
+     * Allows a feature to inject member methods into the metaRowTpl. This is
+     * important for embedding functionality which will become part of the proper
+     * row tpl.
+     */
+    getMetaRowTplFragments: function() {
+        return {};
+    },
+
+    getTableFragments: function() {
+        return {};
+    },
+    
+    /**
+     * Provide additional data to the prepareData call within the grid view.
+     * @param {Object} data The data for this particular record.
+     * @param {Number} idx The row index for this record.
+     * @param {Ext.data.Model} record The record instance
+     * @param {Object} orig The original result from the prepareData call to massage.
+     */
+    getAdditionalData: function(data, idx, record, orig) {
+        return {};
+    },
+    
+    /**
+     * Enable a feature
+     */
+    enable: function() {
+        this.disabled = false;
+    },
+    
+    /**
+     * Disable a feature
+     */
+    disable: function() {
+        this.disabled = true;
+    }
+    
+});
+/**
+ * A small abstract class that contains the shared behaviour for any summary
+ * calculations to be used in the grid.
+ * @class Ext.grid.feature.AbstractSummary
+ * @extends Ext.grid.feature.Feature
+ * @ignore
+ */
+Ext.define('Ext.grid.feature.AbstractSummary', {
+    
+    /* Begin Definitions */
+   
+    extend: 'Ext.grid.feature.Feature',
+    
+    alias: 'feature.abstractsummary',
+   
+    /* End Definitions */
+   
+   /**
+    * @cfg {Boolean} showSummaryRow True to show the summary row. Defaults to <tt>true</tt>.
+    */
+    showSummaryRow: true,
+    
+    // @private
+    nestedIdRe: /\{\{id\}([\w\-]*)\}/g,
+    
+    /**
+     * Toggle whether or not to show the summary row.
+     * @param {Boolan} visible True to show the summary row
+     */
+    toggleSummaryRow: function(visible){
+        this.showSummaryRow = !!visible;
+    },
+    
+    /**
+     * Gets any fragments to be used in the tpl
+     * @private
+     * @return {Object} The fragments
+     */
+    getSummaryFragments: function(){
+        var fragments = {};
+        if (this.showSummaryRow) {
+            Ext.apply(fragments, {
+                printSummaryRow: Ext.bind(this.printSummaryRow, this)
+            });
+        }
+        return fragments;
+    },
+    
+    /**
+     * Prints a summary row
+     * @private
+     * @param {Object} index The index in the template
+     * @return {String} The value of the summary row
+     */
+    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)
+        });
+    },
+    
+    /**
+     * Gets the value for the column from the attached data.
+     * @param {Ext.grid.column.Column} column The header
+     * @param {Object} data The current data
+     * @return {String} The value to be rendered
+     */
+    getColumnValue: function(column, data){
+        var comp = Ext.getCmp(column.id),
+            value = data[column.dataIndex],
+            renderer = comp.summaryRenderer || comp.renderer;
+            
+        if (renderer) {
+            value = renderer.call(comp.scope || this, value, data, column.dataIndex);
+        }
+        return value;
+    },
+    
+    /**
+     * Get the summary data for a field.
+     * @private
+     * @param {Ext.data.Store} store The store to get the data from
+     * @param {String/Function} type The type of aggregation. If a function is specified it will
+     * be passed to the stores aggregate function.
+     * @param {String} field The field to aggregate on
+     * @param {Boolean} group True to aggregate in grouped mode 
+     * @return {Mixed} See the return type for the store functions.
+     */
+    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 ? {} : '';
+                    
+            }
+        }
+    }
+    
+});
+
+/**
+ * @class Ext.grid.feature.Chunking
+ * @extends Ext.grid.feature.Feature
+ */
+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});
+        //this.view.on('bodyscroll', 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;
+            //BrowserBug: IE6,7,8 quirks mode takes setting scrollTop 2x.
+            view.el.dom.scrollTop = top;
+        }
+    },
+    
+    collectData: function(records, preppedRecords, startIndex, fullWidth, orig) {
+        var o = {
+            fullWidth: orig.fullWidth,
+            chunks: []
+        },
+        //headerCt = this.view.headerCt,
+        //colums = headerCt.getColumnsForTpl(),
+        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>';
+            }
+        };
+    }
+});
+
+/**
+ * @class Ext.grid.feature.Grouping
+ * @extends Ext.grid.feature.Feature
+ * 
+ * This feature allows to display the grid rows aggregated into groups as specified by the {@link Ext.data.Store#groupers}
+ * specified on the Store. The group will show the title for the group name and then the appropriate records for the group
+ * underneath. The groups can also be expanded and collapsed.
+ * 
+ * ## Extra Events
+ * This feature adds several extra events that will be fired on the grid to interact with the groups:
+ *
+ *  - {@link #groupclick}
+ *  - {@link #groupdblclick}
+ *  - {@link #groupcontextmenu}
+ *  - {@link #groupexpand}
+ *  - {@link #groupcollapse}
+ * 
+ * ## Menu Augmentation
+ * This feature adds extra options to the grid column menu to provide the user with functionality to modify the grouping.
+ * This can be disabled by setting the {@link #enableGroupingMenu} option. The option to disallow grouping from being turned off
+ * by thew user is {@link #enableNoGroups}.
+ * 
+ * ## Controlling Group Text
+ * The {@link #groupHeaderTpl} is used to control the rendered title for each group. It can modified to customized
+ * the default display.
+ * 
+ * ## Example Usage
+ * 
+ *     var groupingFeature = Ext.create('Ext.grid.feature.Grouping', {
+ *         groupHeaderTpl: 'Group: {name} ({rows.length})', //print the number of items in the group
+ *         startCollapsed: true // start all groups collapsed
+ *     });
+ * 
+ * @ftype grouping
+ * @author Nicolas Ferrero
+ */
+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);
+    },
+    
+    /**
+     * @event groupclick
+     * @param {Ext.view.Table} view
+     * @param {HTMLElement} node
+     * @param {Number} unused
+     * @param {Number} unused
+     * @param {Ext.EventObject} e
+     */
+
+    /**
+     * @event groupdblclick
+     * @param {Ext.view.Table} view
+     * @param {HTMLElement} node
+     * @param {Number} unused
+     * @param {Number} unused
+     * @param {Ext.EventObject} e
+     */
+
+    /**
+     * @event groupcontextmenu
+     * @param {Ext.view.Table} view
+     * @param {HTMLElement} node
+     * @param {Number} unused
+     * @param {Number} unused
+     * @param {Ext.EventObject} e
+     */
+
+    /**
+     * @event groupcollapse
+     * @param {Ext.view.Table} view
+     * @param {HTMLElement} node
+     * @param {Number} unused
+     * @param {Number} unused
+     * @param {Ext.EventObject} e
+     */
+
+    /**
+     * @event groupexpand
+     * @param {Ext.view.Table} view
+     * @param {HTMLElement} node
+     * @param {Number} unused
+     * @param {Number} unused
+     * @param {Ext.EventObject} e
+     */
+
+    /**
+     * @cfg {String} groupHeaderTpl
+     * Template snippet, this cannot be an actual template. {name} will be replaced with the current group.
+     * Defaults to 'Group: {name}'
+     */
+    groupHeaderTpl: 'Group: {name}',
+
+    /**
+     * @cfg {Number} depthToIndent
+     * Number of pixels to indent per grouping level
+     */
+    depthToIndent: 17,
+
+    collapsedCls: Ext.baseCSSPrefix + 'grid-group-collapsed',
+    hdCollapsedCls: Ext.baseCSSPrefix + 'grid-group-hd-collapsed',
+
+    /**
+     * @cfg {String} groupByText Text displayed in the grid header menu for grouping by header
+     * (defaults to 'Group By This Field').
+     */
+    groupByText : 'Group By This Field',
+    /**
+     * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping
+     * (defaults to 'Show in Groups').
+     */
+    showGroupsText : 'Show in Groups',
+
+    /**
+     * @cfg {Boolean} hideGroupedHeader<tt>true</tt> to hide the header that is currently grouped (defaults to <tt>false</tt>)
+     */
+    hideGroupedHeader : false,
+
+    /**
+     * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)
+     */
+    startCollapsed : false,
+
+    /**
+     * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the header menu (defaults to <tt>true</tt>)
+     */
+    enableGroupingMenu : true,
+
+    /**
+     * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)
+     */
+    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\'">',
+                // group row tpl
+                '<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>',
+                // this is the rowbody
+                '<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;"';
+    },
+
+    // Containers holding these components are responsible for
+    // destroying them, we are just deleting references.
+    destroy: function() {
+        var me = this;
+        
+        delete me.view;
+        delete me.prunedHeader;
+    },
+
+    // perhaps rename to afterViewRender
+    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)
+        
+        // runs in the scope of headerCt
+        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;
+        };
+    },
+
+
+    /**
+     * Group by the header the user has clicked on.
+     * @private
+     */
+    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();
+        
+    },
+
+    /**
+     * Turn on and off grouping via the menu
+     * @private
+     */
+    onGroupToggleMenuItemClick: function(menuItem, checked) {
+        this[checked ? 'enable' : 'disable']();
+    },
+
+    /**
+     * Prunes the grouped header from the header container
+     * @private
+     */
+    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 ''; 
+    },
+
+    /**
+     * When a row gains focus, expand the groups above it
+     * @private
+     */
+    onRowFocus: function(rowIdx) {
+        var node    = this.view.getNode(rowIdx),
+            groupBd = Ext.fly(node).up('.' + this.collapsedCls);
+
+        if (groupBd) {
+            // for multiple level groups, should expand every groupBd
+            // above
+            this.expand(groupBd);
+        }
+    },
+
+    /**
+     * Expand a group by the groupBody
+     * @param {Ext.core.Element} groupBd
+     * @private
+     */
+    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 a group by the groupBody
+     * @param {Ext.core.Element} groupBd
+     * @private
+     */
+    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();
+    },
+
+    /**
+     * Toggle between expanded/collapsed state when clicking on
+     * the group.
+     * @private
+     */
+    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);
+        }
+    },
+
+    // Injects isRow and closeRow into the metaRowTpl.
+    getMetaRowTplFragments: function() {
+        return {
+            isRow: this.isRow,
+            closeRow: this.closeRow
+        };
+    },
+
+    // injected into rowtpl and wrapped around metaRowTpl
+    // becomes part of the standard tpl
+    isRow: function() {
+        return '<tpl if="typeof rows === \'undefined\'">';
+    },
+
+    // injected into rowtpl and wrapped around metaRowTpl
+    // becomes part of the standard tpl
+    closeRow: function() {
+        return '</tpl>';
+    },
+
+    // isRow and closeRow are injected via getMetaRowTplFragments
+    mutateMetaRowTpl: function(metaRowTpl) {
+        metaRowTpl.unshift('{[this.isRow()]}');
+        metaRowTpl.push('{[this.closeRow()]}');
+    },
+
+    // injects an additional style attribute via tdAttrKey with the proper
+    // amount of padding
+    getAdditionalData: function(data, idx, record, orig) {
+        var view = this.view,
+            hCt  = view.headerCt,
+            col  = hCt.items.getAt(0),
+            o = {},
+            tdAttrKey = col.id + '-tdAttr';
+
+        // maintain the current tdAttr that a user may ahve set.
+        o[tdAttrKey] = this.indentByDepth(data) + " " + (orig[tdAttrKey] ? orig[tdAttrKey] : '');
+        o.collapsed = 'true';
+        return o;
+    },
+
+    // return matching preppedRecords
+    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;
+    },
+
+    // return the data in a grouped format.
+    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;
+    },
+    
+    // adds the groupName to the groupclick, groupdblclick, groupcontextmenu
+    // events that are fired on the view. Chose not to return the actual
+    // group itself because of its expense and because developers can simply
+    // grab the group via store.getGroups(groupName)
+    getFireEventArgs: function(type, view, featureTarget) {
+        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);
+        
+        return returnArray;
+    }
+});
+
+/**
+ * @class Ext.grid.feature.GroupingSummary
+ * @extends Ext.grid.feature.Grouping
+ * 
+ * This feature adds an aggregate summary row at the bottom of each group that is provided
+ * by the {@link Ext.grid.feature.Grouping} feature. There are 2 aspects to the summary:
+ * 
+ * ## Calculation
+ * 
+ * The summary value needs to be calculated for each column in the grid. This is controlled
+ * by the summaryType option specified on the column. There are several built in summary types,
+ * which can be specified as a string on the column configuration. These call underlying methods
+ * on the store:
+ *
+ *  - {@link Ext.data.Store#count count}
+ *  - {@link Ext.data.Store#sum sum}
+ *  - {@link Ext.data.Store#min min}
+ *  - {@link Ext.data.Store#max max}
+ *  - {@link Ext.data.Store#average average}
+ *
+ * Alternatively, the summaryType can be a function definition. If this is the case,
+ * the function is called with an array of records to calculate the summary value.
+ * 
+ * ## Rendering
+ * 
+ * Similar to a column, the summary also supports a summaryRenderer function. This
+ * summaryRenderer is called before displaying a value. The function is optional, if
+ * not specified the default calculated value is shown. The summaryRenderer is called with:
+ *
+ *  - value {Object} - The calculated value.
+ *  - data {Object} - Contains all raw summary values for the row.
+ *  - field {String} - The name of the field we are calculating
+ * 
+ * ## Example Usage
+ *
+ *     Ext.define('TestResult', {
+ *         extend: 'Ext.data.Model',
+ *         fields: ['student', 'subject', {
+ *             name: 'mark',
+ *             type: 'int'
+ *         }]
+ *     });
+ *     
+ *     Ext.create('Ext.grid.Panel', {
+ *         width: 200,
+ *         height: 240,
+ *         renderTo: document.body,
+ *         features: [{
+ *             groupHeaderTpl: 'Subject: {name}',
+ *             ftype: 'groupingsummary'
+ *         }],
+ *         store: {
+ *             model: 'TestResult',
+ *             groupField: 'subject',
+ *             data: [{
+ *                 student: 'Student 1',
+ *                 subject: 'Math',
+ *                 mark: 84
+ *             },{
+ *                 student: 'Student 1',
+ *                 subject: 'Science',
+ *                 mark: 72
+ *             },{
+ *                 student: 'Student 2',
+ *                 subject: 'Math',
+ *                 mark: 96
+ *             },{
+ *                 student: 'Student 2',
+ *                 subject: 'Science',
+ *                 mark: 68
+ *             }]
+ *         },
+ *         columns: [{
+ *             dataIndex: 'student',
+ *             text: 'Name',
+ *             summaryType: 'count',
+ *             summaryRenderer: function(value){
+ *                 return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : ''); 
+ *             }
+ *         }, {
+ *             dataIndex: 'mark',
+ *             text: 'Mark',
+ *             summaryType: 'average'
+ *         }]
+ *     });
+ */
+Ext.define('Ext.grid.feature.GroupingSummary', {
+    
+    /* Begin Definitions */
+    
+    extend: 'Ext.grid.feature.Grouping',
+    
+    alias: 'feature.groupingsummary',
+    
+    mixins: {
+        summary: 'Ext.grid.feature.AbstractSummary'
+    },
+    
+    /* End Definitions */
+
+     
+   /**
+    * Modifies the row template to include the summary row.
+    * @private
+    * @return {String} The modified template
+    */
+   getFeatureTpl: function() {
+        var tpl = this.callParent(arguments);
+            
+        if (this.showSummaryRow) {
+            // lop off the end </tpl> so we can attach it
+            tpl = tpl.replace('</tpl>', '');
+            tpl += '{[this.printSummaryRow(xindex)]}</tpl>';
+        }
+        return tpl;
+    },
+    
+    /**
+     * Gets any fragments needed for the template.
+     * @private
+     * @return {Object} The fragments
+     */
+    getFragmentTpl: function() {
+        var me = this,
+            fragments = me.callParent();
+            
+        Ext.apply(fragments, me.getSummaryFragments());
+        if (me.showSummaryRow) {
+            // this gets called before render, so we'll setup the data here.
+            me.summaryGroups = me.view.store.getGroups();
+            me.summaryData = me.generateSummaryData();
+        }
+        return fragments;
+    },
+    
+    /**
+     * Gets the data for printing a template row
+     * @private
+     * @param {Number} index The index in the template
+     * @return {Array} The template values
+     */
+    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;
+    },
+    
+    /**
+     * Generates all of the summary data to be used when processing the template
+     * @private
+     * @return {Object} The summary 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] = {};
+        }
+        
+    /**
+     * @cfg {String} remoteRoot.  The name of the property
+     * which contains the Array of summary objects.  Defaults to <tt>undefined</tt>.
+     * It allows to use server-side calculated summaries.
+     */
+        if (me.remoteRoot && reader.rawData) {
+            // reset reader root and rebuild extractors to extract summaries data
+            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;
+            });
+            // restore initial reader configuration
+            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;
+    }
+});
+
+/**
+ * @class Ext.grid.feature.RowBody
+ * @extends Ext.grid.feature.Feature
+ *
+ * The rowbody feature enhances the grid's markup to have an additional
+ * tr -> td -> div which spans the entire width of the original row.
+ *
+ * This is useful to to associate additional information with a particular
+ * record in a grid.
+ *
+ * Rowbodies are initially hidden unless you override getAdditionalData.
+ *
+ * Will expose additional events on the gridview with the prefix of 'rowbody'.
+ * For example: 'rowbodyclick', 'rowbodydblclick', 'rowbodycontextmenu'.
+ *
+ * @ftype rowbody
+ */
+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('');
+    },
+    
+    // injects getRowBody into the metaRowTpl.
+    getMetaRowTplFragments: function() {
+        return {
+            getRowBody: this.getRowBody,
+            rowBodyTrCls: this.rowBodyTrCls,
+            rowBodyTdCls: this.rowBodyTdCls,
+            rowBodyDivCls: this.rowBodyDivCls
+        };
+    },
+
+    mutateMetaRowTpl: function(metaRowTpl) {
+        metaRowTpl.push('{[this.getRowBody(values)]}');
+    },
+
+    /**
+     * Provide additional data to the prepareData call within the grid view.
+     * The rowbody feature adds 3 additional variables into the grid view's template.
+     * These are rowBodyCls, rowBodyColspan, and rowBody.
+     * @param {Object} data The data for this particular record.
+     * @param {Number} idx The row index for this record.
+     * @param {Ext.data.Model} record The record instance
+     * @param {Object} orig The original result from the prepareData call to massage.
+     */
+    getAdditionalData: function(data, idx, record, orig) {
+        var headerCt = this.view.headerCt,
+            colspan  = headerCt.getColumnCount();
+
+        return {
+            rowBody: "",
+            rowBodyCls: this.rowBodyCls,
+            rowBodyColspan: colspan
+        };
+    }
+});
+/**
+ * @class Ext.grid.feature.RowWrap
+ * @extends Ext.grid.feature.Feature
+ * @private
+ */
+Ext.define('Ext.grid.feature.RowWrap', {
+    extend: 'Ext.grid.feature.Feature',
+    alias: 'feature.rowwrap',
+
+    // turn off feature events.
+    hasFeatureEvent: false,
+    
+    mutateMetaRowTpl: function(metaRowTpl) {        
+        // Remove "x-grid-row" from the first row, note this could be wrong
+        // if some other feature unshifted things in front.
+        metaRowTpl[0] = metaRowTpl[0].replace(Ext.baseCSSPrefix + 'grid-row', '');
+        metaRowTpl[0] = metaRowTpl[0].replace("{[this.embedRowCls()]}", "");
+        // 2
+        metaRowTpl.unshift('<table class="' + Ext.baseCSSPrefix + 'grid-table ' + Ext.baseCSSPrefix + 'grid-table-resizer" style="width: {[this.embedFullWidth()]}px;">');
+        // 1
+        metaRowTpl.unshift('<tr class="' + Ext.baseCSSPrefix + 'grid-row {[this.embedRowCls()]}"><td colspan="{[this.embedColSpan()]}"><div class="' + Ext.baseCSSPrefix + 'grid-rowwrap-div">');
+        
+        // 3
+        metaRowTpl.push('</table>');
+        // 4
+        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;
+            // give the inner td's the resizer class
+            // while maintaining anything a user may have injected via a custom
+            // renderer
+            o[tdClsKey] = colResizerCls + " " + (orig[tdClsKey] ? orig[tdClsKey] : '');
+            // TODO: Unhackify the initial rendering width's
+            o[id+'-tdAttr'] = " style=\"width: " + (items[i].hidden ? 0 : items[i].getDesiredWidth()) + "px;\" "/* + (i === 0 ? " rowspan=\"2\"" : "")*/;
+            if (orig[id+'-tdAttr']) {
+                o[id+'-tdAttr'] += orig[id+'-tdAttr'];
+            }
+            
+        }
+
+        return o;
+    },
+    
+    getMetaRowTplFragments: function() {
+        return {
+            embedFullWidth: this.embedFullWidth,
+            embedColSpan: this.embedColSpan
+        };
+    }
+    
+});
+/**
+ * @class Ext.grid.feature.Summary
+ * @extends Ext.grid.feature.AbstractSummary
+ * 
+ * This feature is used to place a summary row at the bottom of the grid. If using a grouping, 
+ * see {@link Ext.grid.feature.GroupingSummary}. There are 2 aspects to calculating the summaries, 
+ * calculation and rendering.
+ * 
+ * ## Calculation
+ * The summary value needs to be calculated for each column in the grid. This is controlled
+ * by the summaryType option specified on the column. There are several built in summary types,
+ * which can be specified as a string on the column configuration. These call underlying methods
+ * on the store:
+ *
+ *  - {@link Ext.data.Store#count count}
+ *  - {@link Ext.data.Store#sum sum}
+ *  - {@link Ext.data.Store#min min}
+ *  - {@link Ext.data.Store#max max}
+ *  - {@link Ext.data.Store#average average}
+ *
+ * Alternatively, the summaryType can be a function definition. If this is the case,
+ * the function is called with an array of records to calculate the summary value.
+ * 
+ * ## Rendering
+ * Similar to a column, the summary also supports a summaryRenderer function. This
+ * summaryRenderer is called before displaying a value. The function is optional, if
+ * not specified the default calculated value is shown. The summaryRenderer is called with:
+ *
+ *  - value {Object} - The calculated value.
+ *  - data {Object} - Contains all raw summary values for the row.
+ *  - field {String} - The name of the field we are calculating
+ * 
+ * ## Example Usage
+ *
+ *     Ext.define('TestResult', {
+ *         extend: 'Ext.data.Model',
+ *         fields: ['student', {
+ *             name: 'mark',
+ *             type: 'int'
+ *         }]
+ *     });
+ *     
+ *     Ext.create('Ext.grid.Panel', {
+ *         width: 200,
+ *         height: 140,
+ *         renderTo: document.body,
+ *         features: [{
+ *             ftype: 'summary'
+ *         }],
+ *         store: {
+ *             model: 'TestResult',
+ *             data: [{
+ *                 student: 'Student 1',
+ *                 mark: 84
+ *             },{
+ *                 student: 'Student 2',
+ *                 mark: 72
+ *             },{
+ *                 student: 'Student 3',
+ *                 mark: 96
+ *             },{
+ *                 student: 'Student 4',
+ *                 mark: 68
+ *             }]
+ *         },
+ *         columns: [{
+ *             dataIndex: 'student',
+ *             text: 'Name',
+ *             summaryType: 'count',
+ *             summaryRenderer: function(value){
+ *                 return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : ''); 
+ *             }
+ *         }, {
+ *             dataIndex: 'mark',
+ *             text: 'Mark',
+ *             summaryType: 'average'
+ *         }]
+ *     });
+ */
+Ext.define('Ext.grid.feature.Summary', {
+    
+    /* Begin Definitions */
+    
+    extend: 'Ext.grid.feature.AbstractSummary',
+    
+    alias: 'feature.summary',
+    
+    /* End Definitions */
+    
+    /**
+     * Gets any fragments needed for the template.
+     * @private
+     * @return {Object} The fragments
+     */
+    getFragmentTpl: function() {
+        // this gets called before render, so we'll setup the data here.
+        this.summaryData = this.generateSummaryData(); 
+        return this.getSummaryFragments();
+    },
+    
+    /**
+     * Overrides the closeRows method on the template so we can include our own custom
+     * footer.
+     * @private
+     * @return {Object} The custom fragments
+     */
+    getTableFragments: function(){
+        if (this.showSummaryRow) {
+            return {
+                closeRows: this.closeRows
+            };
+        }
+    },
+    
+    /**
+     * Provide our own custom footer for the grid.
+     * @private
+     * @return {String} The custom footer
+     */
+    closeRows: function() {
+        return '</tpl>{[this.printSummaryRow()]}';
+    },
+    
+    /**
+     * Gets the data for printing a template row
+     * @private
+     * @param {Number} index The index in the template
+     * @return {Array} The template values
+     */
+    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;
+    },
+    
+    /**
+     * Generates all of the summary data to be used when processing the template
+     * @private
+     * @return {Object} The summary 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;
+    }
+});
+/**
+ * @class Ext.grid.header.DragZone
+ * @extends Ext.dd.DragZone
+ * @private
+ */
+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;
+    }
+});
+
+/**
+ * @class Ext.grid.header.DropZone
+ * @extends Ext.dd.DropZone
+ * @private
+ */
+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;
+
+        // Cannot drag beyond non-draggable start column
+        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)) {
+
+            // As we move in between different DropZones that are in the same
+            // group (such as the case when in a locked grid), invalidateDrop
+            // on the other dropZones.
+            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);
+
+            // constrain the indicators to the viewable section
+            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);
+
+            // adjust by offsets, this is to center the arrows so that they point
+            // at the split point
+            topXY[0] -= 4;
+            topXY[1] -= 9;
+            bottomXY[0] -= 4;
+
+            // position and show indicators
+            topIndicator.setXY(topXY);
+            bottomIndicator.setXY(bottomXY);
+            topIndicator.show();
+            bottomIndicator.show();
+        // invalidate drop operation and hide indicators
+        } 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), // Container.items is a MixedCollection
+                toCt    = lastLocation.header.ownerCt,
+                toIdx   = toCt.items.indexOf(lastLocation.header),
+                headerCt = this.headerCt,
+                groupCt,
+                scrollerOwner;
+
+            if (lastLocation.pos === 'after') {
+                toIdx++;
+            }
+
+            // If we are dragging in between two HeaderContainers that have had the lockable
+            // mixin injected we will lock/unlock headers in between sections. Note that lockable
+            // does NOT currently support grouped headers.
+            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 dragging rightwards, then after removal, the insertion index will be one less when moving
+                // in between the same container.
+                if ((fromCt === toCt) && (toIdx > fromCt.items.indexOf(hd))) {
+                    toIdx--;
+                }
+
+                // Remove dragged header from where it was without destroying it or relaying its Container
+                if (fromCt !== toCt) {
+                    fromCt.suspendLayout = true;
+                    fromCt.remove(hd, false);
+                    fromCt.suspendLayout = false;
+                }
+
+                // Dragged the last header out of the fromCt group... The fromCt group must die
+                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);
+                    }
+                }
+
+                // Move dragged header into its drop position
+                toCt.suspendLayout = true;
+                if (fromCt === toCt) {
+                    toCt.move(fromIdx, toIdx);
+                } else {
+                    toCt.insert(toIdx, hd);
+                }
+                toCt.suspendLayout = false;
+
+                // Group headers acquire the aggregate width of their child headers
+                // Therefore a child header may not flex; it must contribute a fixed width.
+                // But we restore the flex value when moving back into the main header container
+                if (toCt.isGroupHeader) {
+                    hd.savedFlex = hd.flex;
+                    delete hd.flex;
+                    hd.width = hd.getWidth();
+                    // When there was previously a flex, we need to ensure we don't count for the
+                    // border twice.
+                    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;
+                    }
+                }
+
+
+                // Refresh columns cache in case we remove an emptied group column
+                headerCt.purgeCache();
+                headerCt.doLayout();
+                headerCt.onHeaderMoved(hd, fromIdx, toIdx);
+                // Emptied group header can only be destroyed after the header and grid have been refreshed
+                if (!fromCt.items.getCount()) {
+                    fromCt.destroy();
+                }
+            }
+        }
+    }
+});
+
+
+/**
+ * @class Ext.grid.plugin.Editing
+
+This class provides an abstract grid editing plugin on selected {@link Ext.grid.column.Column columns}.
+The editable columns are specified by providing an {@link Ext.grid.column.Column#editor editor}
+in the {@link Ext.grid.column.Column column configuration}.
+
+*Note:* This class should not be used directly. See {@link Ext.grid.plugin.CellEditing} and
+{@link Ext.grid.plugin.RowEditing}.
+
+ * @markdown
+ */
+Ext.define('Ext.grid.plugin.Editing', {
+    alias: 'editing.editing',
+
+    requires: [
+        'Ext.grid.column.Column',
+        'Ext.util.KeyNav'
+    ],
+
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+
+    /**
+     * @cfg {Number} clicksToEdit
+     * The number of clicks on a grid required to display the editor (defaults to 2).
+     */
+    clicksToEdit: 2,
+
+    // private
+    defaultFieldXType: 'textfield',
+
+    // cell, row, form
+    editStyle: '',
+
+    constructor: function(config) {
+        var me = this;
+        Ext.apply(me, config);
+
+        me.addEvents(
+            // Doc'ed in separate editing plugins
+            'beforeedit',
+
+            // Doc'ed in separate editing plugins
+            'edit',
+
+            // Doc'ed in separate editing plugins
+            'validateedit'
+        );
+        me.mixins.observable.constructor.call(me);
+        // TODO: Deprecated, remove in 5.0
+        me.relayEvents(me, ['afteredit'], 'after');
+    },
+
+    // private
+    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']);
+        // Marks the grid as editable, so that the SelectionModel
+        // can make appropriate decisions during navigation
+        grid.isEditable = true;
+        grid.editingPlugin = grid.view.editingPlugin = me;
+    },
+
+    /**
+     * @private
+     * AbstractComponent calls destroy on all its plugins at destroy time.
+     */
+    destroy: function() {
+        var me = this,
+            grid = me.grid,
+            headerCt = grid.headerCt,
+            events = grid.events;
+
+        Ext.destroy(me.keyNav);
+        me.removeFieldAccessors(grid.getView().getGridColumns());
+
+        // Clear all listeners from all our events, clear all managed listeners we added to other Observables
+        me.clearListeners();
+
+        delete me.grid.editingPlugin;
+        delete me.grid.view.editingPlugin;
+        delete me.grid;
+        delete me.view;
+        delete me.editor;
+        delete me.keyNav;
+    },
+
+    // private
+    getEditStyle: function() {
+        return this.editStyle;
+    },
+
+    // private
+    initFieldAccessors: function(column) {
+        var me = this;
+
+        if (Ext.isArray(column)) {
+            Ext.Array.forEach(column, me.initFieldAccessors, me);
+            return;
+        }
+
+        // Augment the Header class to have a getEditor and setEditor method
+        // Important: Only if the header does not have its own implementation.
+        Ext.applyIf(column, {
+            getEditor: function(record, defaultField) {
+                return me.getColumnField(this, defaultField);
+            },
+
+            setEditor: function(field) {
+                me.setColumnField(this, field);
+            }
+        });
+    },
+
+    // private
+    removeFieldAccessors: function(column) {
+        var me = this;
+
+        if (Ext.isArray(column)) {
+            Ext.Array.forEach(column, me.removeFieldAccessors, me);
+            return;
+        }
+
+        delete column.getEditor;
+        delete column.setEditor;
+    },
+
+    // private
+    // remaps to the public API of Ext.grid.column.Column.getEditor
+    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;
+        }
+    },
+
+    // private
+    // remaps to the public API of Ext.grid.column.Column.setEditor
+    setColumnField: function(column, field) {
+        if (Ext.isObject(field) && !field.isFormField) {
+            field = Ext.ComponentManager.create(field, this.defaultFieldXType);
+        }
+        column.field = field;
+    },
+
+    // private
+    initEvents: function() {
+        var me = this;
+        me.initEditTriggers();
+        me.initCancelTriggers();
+    },
+
+    // @abstract
+    initCancelTriggers: Ext.emptyFn,
+
+    // private
+    initEditTriggers: function() {
+        var me = this,
+            view = me.view,
+            clickEvent = me.clicksToEdit === 1 ? 'click' : 'dblclick';
+
+        // Start editing
+        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 });
+    },
+
+    // private
+    onEnterKey: function(e) {
+        var me = this,
+            grid = me.grid,
+            selModel = grid.getSelectionModel(),
+            record,
+            columnHeader = grid.headerCt.getHeaderAtIndex(0);
+
+        // Calculate editing start position from SelectionModel
+        // CellSelectionModel
+        if (selModel.getCurrentPosition) {
+            pos = selModel.getCurrentPosition();
+            record = grid.store.getAt(pos.row);
+            columnHeader = grid.headerCt.getHeaderAtIndex(pos.column);
+        }
+        // RowSelectionModel
+        else {
+            record = selModel.getLastSelected();
+        }
+        me.startEdit(record, columnHeader);
+    },
+
+    // private
+    onEscKey: function(e) {
+        this.cancelEdit();
+    },
+
+    // private
+    startEditByClick: function(view, cell, colIdx, record, row, rowIdx, e) {
+        this.startEdit(record, view.getHeaderAtIndex(colIdx));
+    },
+
+    /**
+     * @private
+     * @abstract. Template method called before editing begins.
+     * @param {Object} context The current editing context
+     * @return {Boolean} Return false to cancel the editing process
+     */
+    beforeEdit: Ext.emptyFn,
+
+    /**
+     * Start editing the specified record, using the specified Column definition to define which field is being edited.
+     * @param {Model} record The Store data record which backs the row to be edited.
+     * @param {Model} columnHeader The Column object defining the column to be edited.
+     */
+    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;
+    },
+
+    /**
+     * @private Collects all information necessary for any subclasses to perform their editing functions.
+     * @param record
+     * @param columnHeader
+     * @returns {Object} The editing context based upon the passed record and column
+     */
+    getEditingContext: function(record, columnHeader) {
+        var me = this,
+            grid = me.grid,
+            store = grid.store,
+            rowIdx,
+            colIdx,
+            view = grid.getView(),
+            value;
+
+        // If they'd passed numeric row, column indices, look them up.
+        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
+        };
+    },
+
+    /**
+     * Cancel any active edit that is in progress.
+     */
+    cancelEdit: function() {
+        this.editing = false;
+    },
+
+    /**
+     * Complete the edit if there is an active edit in progress.
+     */
+    completeEdit: function() {
+        var me = this;
+
+        if (me.editing && me.validateEdit()) {
+            me.fireEvent('edit', me.context);
+        }
+
+        delete me.context;
+        me.editing = false;
+    },
+
+    // @abstract
+    validateEdit: function() {
+        var me = this,
+            context = me.context;
+
+        return me.fireEvent('validateedit', me, context) !== false && !context.cancel;
+    }
+});
+/**
+ * @class Ext.grid.plugin.CellEditing
+ * @extends Ext.grid.plugin.Editing
+ *
+ * The Ext.grid.plugin.CellEditing plugin injects editing at a cell level for a Grid. Only a single
+ * cell will be editable at a time. The field that will be used for the editor is defined at the
+ * {@link Ext.grid.column.Column#field field}. The editor can be a field instance or a field configuration.
+ *
+ * If an editor is not specified for a particular column then that cell will not be editable and it will
+ * be skipped when activated via the mouse or the keyboard.
+ *
+ * The editor may be shared for each column in the grid, or a different one may be specified for each column.
+ * An appropriate field type should be chosen to match the data structure that it will be editing. For example,
+ * to edit a date, it would be useful to specify {@link Ext.form.field.Date} as the editor.
+ *
+ * {@img Ext.grid.plugin.CellEditing/Ext.grid.plugin.CellEditing.png Ext.grid.plugin.CellEditing plugin}
+ *
+ * ## Example Usage
+ *    Ext.create('Ext.data.Store', {
+ *        storeId:'simpsonsStore',
+ *        fields:['name', 'email', 'phone'],
+ *        data:{'items':[
+ *            {"name":"Lisa", "email":"lisa@simpsons.com", "phone":"555-111-1224"},
+ *            {"name":"Bart", "email":"bart@simpsons.com", "phone":"555--222-1234"},
+ *            {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"},
+ *            {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"}
+ *        ]},
+ *        proxy: {
+ *            type: 'memory',
+ *            reader: {
+ *                type: 'json',
+ *                root: 'items'
+ *            }
+ *        }
+ *    });
+ *
+ *    Ext.create('Ext.grid.Panel', {
+ *        title: 'Simpsons',
+ *        store: Ext.data.StoreManager.lookup('simpsonsStore'),
+ *        columns: [
+ *            {header: 'Name',  dataIndex: 'name', field: 'textfield'},
+ *            {header: 'Email', dataIndex: 'email', flex:1,
+ *                editor: {
+ *                    xtype:'textfield',
+ *                    allowBlank:false
+ *                }
+ *            },
+ *            {header: 'Phone', dataIndex: 'phone'}
+ *        ],
+ *        selType: 'cellmodel',
+ *        plugins: [
+ *            Ext.create('Ext.grid.plugin.CellEditing', {
+ *                clicksToEdit: 1
+ *            })
+ *        ],
+ *        height: 200,
+ *        width: 400,
+ *        renderTo: Ext.getBody()
+ *    });
+ *
+ */
+Ext.define('Ext.grid.plugin.CellEditing', {
+    alias: 'plugin.cellediting',
+    extend: 'Ext.grid.plugin.Editing',
+    requires: ['Ext.grid.CellEditor'],
+
+    constructor: function() {
+        /**
+         * @event beforeedit
+         * Fires before cell editing is triggered. The edit event object has the following properties <br />
+         * <ul style="padding:5px;padding-left:16px;">
+         * <li>grid - The grid</li>
+         * <li>record - The record being edited</li>
+         * <li>field - The field name being edited</li>
+         * <li>value - The value for the field being edited.</li>
+         * <li>row - The grid table row</li>
+         * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that is being edited.</li>
+         * <li>rowIdx - The row index that is being edited</li>
+         * <li>colIdx - The column index that is being edited</li>
+         * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
+         * </ul>
+         * @param {Ext.grid.plugin.Editing} editor
+         * @param {Object} e An edit event (see above for description)
+         */
+        /**
+         * @event edit
+         * Fires after a cell is edited. The edit event object has the following properties <br />
+         * <ul style="padding:5px;padding-left:16px;">
+         * <li>grid - The grid</li>
+         * <li>record - The record that was edited</li>
+         * <li>field - The field name that was edited</li>
+         * <li>value - The value being set</li>
+         * <li>originalValue - The original value for the field, before the edit.</li>
+         * <li>row - The grid table row</li>
+         * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that was edited.</li>
+         * <li>rowIdx - The row index that was edited</li>
+         * <li>colIdx - The column index that was edited</li>
+         * </ul>
+         *
+         * <pre><code>
+grid.on('edit', onEdit, this);
+
+function onEdit(e) {
+    // execute an XHR to send/commit data to the server, in callback do (if successful):
+    e.record.commit();
+};
+         * </code></pre>
+         * @param {Ext.grid.plugin.Editing} editor
+         * @param {Object} e An edit event (see above for description)
+         */
+        /**
+         * @event validateedit
+         * Fires after a cell is edited, but before the value is set in the record. Return false
+         * to cancel the change. The edit event object has the following properties <br />
+         * <ul style="padding:5px;padding-left:16px;">
+         * <li>grid - The grid</li>
+         * <li>record - The record being edited</li>
+         * <li>field - The field name being edited</li>
+         * <li>value - The value being set</li>
+         * <li>originalValue - The original value for the field, before the edit.</li>
+         * <li>row - The grid table row</li>
+         * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that is being edited.</li>
+         * <li>rowIdx - The row index that is being edited</li>
+         * <li>colIdx - The column index that is being edited</li>
+         * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
+         * </ul>
+         * Usage example showing how to remove the red triangle (dirty record indicator) from some
+         * records (not all).  By observing the grid's validateedit event, it can be cancelled if
+         * the edit occurs on a targeted row (for example) and then setting the field's new value
+         * in the Record directly:
+         * <pre><code>
+grid.on('validateedit', function(e) {
+  var myTargetRow = 6;
+
+  if (e.row == myTargetRow) {
+    e.cancel = true;
+    e.record.data[e.field] = e.value;
+  }
+});
+         * </code></pre>
+         * @param {Ext.grid.plugin.Editing} editor
+         * @param {Object} e An edit event (see above for description)
+         */
+        this.callParent(arguments);
+        this.editors = Ext.create('Ext.util.MixedCollection', false, function(editor) {
+            return editor.editorId;
+        });
+    },
+
+    /**
+     * @private
+     * AbstractComponent calls destroy on all its plugins at destroy time.
+     */
+    destroy: function() {
+        var me = this;
+        me.editors.each(Ext.destroy, Ext);
+        me.editors.clear();
+        me.callParent(arguments);
+    },
+
+    // private
+    // Template method called from base class's initEvents
+    initCancelTriggers: function() {
+        var me   = this;
+            grid = me.grid,
+            view   = grid.view;
+
+        me.mon(view, {
+            mousewheel: {
+                element: 'el',
+                fn: me.cancelEdit,
+                scope: me
+            }
+        });
+        me.mon(grid, {
+            columnresize: me.cancelEdit,
+            columnmove: me.cancelEdit,
+            scope: me
+        });
+    },
+
+    /**
+     * Start editing the specified record, using the specified Column definition to define which field is being edited.
+     * @param {Model} record The Store data record which backs the row to be edited.
+     * @param {Model} columnHeader The Column object defining the column to be edited.
+     * @override
+     */
+    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;
+
+        // Complete the edit now, before getting the editor's target
+        // cell DOM element. Completing the edit causes a view refresh.
+        me.completeEdit();
+
+        // See if the field is editable for the requested record
+        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);
+
+            // Defer, so we have some time between view scroll to sync up the editor
+            Ext.defer(ed.startEdit, 15, ed, [me.getCell(record, columnHeader), value]);
+        } else {
+            // BrowserBug: WebKit & IE refuse to focus the element, rather
+            // it will focus it and then immediately focus the body. This
+            // temporary hack works for Webkit and IE6. IE7 and 8 are still
+            // broken
+            me.grid.getView().el.focus((Ext.isWebKit || Ext.isIE) ? 10 : false);
+        }
+    },
+
+    completeEdit: function() {
+        var activeEd = this.getActiveEditor();
+        if (activeEd) {
+            activeEd.completeEdit();
+        }
+    },
+
+    // internal getters/setters
+    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 editors = this.editors,
+            editorId = column.itemId || column.id,
+            editor = editors.getByKey(editorId);
+
+        if (editor) {
+            return editor;
+        } else {
+            editor = column.getEditor(record);
+            if (!editor) {
+                return false;
+            }
+
+            // Allow them to specify a CellEditor in the Column
+            if (!(editor instanceof Ext.grid.CellEditor)) {
+                editor = Ext.create('Ext.grid.CellEditor', {
+                    editorId: editorId,
+                    field: editor
+                });
+            }
+            editor.parentEl = this.grid.getEditorParent();
+            // editor.parentEl should be set here.
+            editor.on({
+                scope: this,
+                specialkey: this.onSpecialKey,
+                complete: this.onEditComplete,
+                canceledit: this.cancelEdit
+            });
+            editors.add(editor);
+            return editor;
+        }
+    },
+
+    /**
+     * Get the cell (td) for a particular record and column.
+     * @param {Ext.data.Model} record
+     * @param {Ext.grid.column.Colunm} column
+     * @private
+     */
+    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(),
+            dataIndex = me.getActiveColumn().dataIndex;
+
+        me.setActiveEditor(null);
+        me.setActiveColumn(null);
+        me.setActiveRecord(null);
+        delete sm.wasEditing;
+
+        if (!me.validateEdit()) {
+            return;
+        }
+        me.context.record.set(dataIndex, value);
+        me.fireEvent('edit', me, me.context);
+    },
+
+    /**
+     * Cancel any active editing.
+     */
+    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();
+        }
+    },
+
+    /**
+     * Starts editing by position (row/column)
+     * @param {Object} position A position with keys of row and column.
+     */
+    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);
+    }
+});
+/**
+ * @class Ext.grid.plugin.DragDrop
+ * <p>This plugin provides drag and/or drop functionality for a GridView.</p>
+ * <p>It creates a specialized instance of {@link Ext.dd.DragZone DragZone} which knows how to drag out of a {@link Ext.grid.View GridView}
+ * and loads the data object which is passed to a cooperating {@link Ext.dd.DragZone DragZone}'s methods with the following properties:<ul>
+ * <li>copy : Boolean
+ *  <div class="sub-desc">The value of the GridView's <code>copy</code> property, or <code>true</code> if the GridView was configured
+ *  with <code>allowCopy: true</code> <u>and</u> the control key was pressed when the drag operation was begun.</div></li>
+ * <li>view : GridView
+ *  <div class="sub-desc">The source GridView from which the drag originated.</div></li>
+ * <li>ddel : HtmlElement
+ *  <div class="sub-desc">The drag proxy element which moves with the mouse</div></li>
+ * <li>item : HtmlElement
+ *  <div class="sub-desc">The GridView node upon which the mousedown event was registered.</div></li>
+ * <li>records : Array
+ *  <div class="sub-desc">An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView.</div></li>
+ * </ul></p>
+ * <p>It also creates a specialized instance of {@link Ext.dd.DropZone} which cooperates with other DropZones which are members of the same
+ * ddGroup which processes such data objects.</p>
+ * <p>Adding this plugin to a view means that two new events may be fired from the client GridView, <code>{@link #event-beforedrop beforedrop}</code> and
+ * <code>{@link #event-drop drop}</code></p>
+ */
+Ext.define('Ext.grid.plugin.DragDrop', {
+    extend: 'Ext.AbstractPlugin',
+    alias: 'plugin.gridviewdragdrop',
+
+    uses: [
+        'Ext.view.DragZone',
+        'Ext.grid.ViewDropZone'
+    ],
+
+    /**
+     * @event beforedrop
+     * <p><b>This event is fired through the GridView. Add listeners to the GridView object</b></p>
+     * <p>Fired when a drop gesture has been triggered by a mouseup event in a valid drop position in the GridView.
+     * @param {HtmlElement} node The GridView node <b>if any</b> over which the mouse was positioned.</p>
+     * <p>Returning <code>false</code> to this event signals that the drop gesture was invalid, and if the drag proxy
+     * will animate back to the point from which the drag began.</p>
+     * <p>Returning <code>0</code> To this event signals that the data transfer operation should not take place, but
+     * that the gesture was valid, and that the repair operation should not take place.</p>
+     * <p>Any other return value continues with the data transfer operation.</p>
+     * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone DragZone}'s
+     * {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties:<ul>
+     * <li>copy : Boolean
+     *  <div class="sub-desc">The value of the GridView's <code>copy</code> property, or <code>true</code> if the GridView was configured
+     *  with <code>allowCopy: true</code> and the control key was pressed when the drag operation was begun</div></li>
+     * <li>view : GridView
+     *  <div class="sub-desc">The source GridView from which the drag originated.</div></li>
+     * <li>ddel : HtmlElement
+     *  <div class="sub-desc">The drag proxy element which moves with the mouse</div></li>
+     * <li>item : HtmlElement
+     *  <div class="sub-desc">The GridView node upon which the mousedown event was registered.</div></li>
+     * <li>records : Array
+     *  <div class="sub-desc">An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView.</div></li>
+     * </ul>
+     * @param {Ext.data.Model} overModel The Model over which the drop gesture took place.
+     * @param {String} dropPosition <code>"before"</code> or <code>"after"</code> depending on whether the mouse is above or below the midline of the node.
+     * @param {Function} dropFunction <p>A function to call to complete the data transfer operation and either move or copy Model instances from the source
+     * View's Store to the destination View's Store.</p>
+     * <p>This is useful when you want to perform some kind of asynchronous processing before confirming
+     * the drop, such as an {@link Ext.window.MessageBox#confirm confirm} call, or an Ajax request.</p>
+     * <p>Return <code>0</code> from this event handler, and call the <code>dropFunction</code> at any time to perform the data transfer.</p>
+     */
+
+    /**
+     * @event drop
+     * <b>This event is fired through the GridView. Add listeners to the GridView object</b>
+     * Fired when a drop operation has been completed and the data has been moved or copied.
+     * @param {HtmlElement} node The GridView node <b>if any</b> over which the mouse was positioned.
+     * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone DragZone}'s
+     * {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties:<ul>
+     * <li>copy : Boolean
+     *  <div class="sub-desc">The value of the GridView's <code>copy</code> property, or <code>true</code> if the GridView was configured
+     *  with <code>allowCopy: true</code> and the control key was pressed when the drag operation was begun</div></li>
+     * <li>view : GridView
+     *  <div class="sub-desc">The source GridView from which the drag originated.</div></li>
+     * <li>ddel : HtmlElement
+     *  <div class="sub-desc">The drag proxy element which moves with the mouse</div></li>
+     * <li>item : HtmlElement
+     *  <div class="sub-desc">The GridView node upon which the mousedown event was registered.</div></li>
+     * <li>records : Array
+     *  <div class="sub-desc">An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source GridView.</div></li>
+     * </ul>
+     * @param {Ext.data.Model} overModel The Model over which the drop gesture took place.
+     * @param {String} dropPosition <code>"before"</code> or <code>"after"</code> depending on whether the mouse is above or below the midline of the node.
+     */
+
+    dragText : '{0} selected row{1}',
+
+    /**
+     * @cfg {String} ddGroup
+     * A named drag drop group to which this object belongs.  If a group is specified, then both the DragZones and DropZone
+     * used by this plugin will only interact with other drag drop objects in the same group (defaults to 'TreeDD').
+     */
+    ddGroup : "GridDD",
+
+    /**
+     * @cfg {String} dragGroup
+     * <p>The ddGroup to which the DragZone will belong.</p>
+     * <p>This defines which other DropZones the DragZone will interact with. Drag/DropZones only interact with other Drag/DropZones
+     * which are members of the same ddGroup.</p>
+     */
+
+    /**
+     * @cfg {String} dropGroup
+     * <p>The ddGroup to which the DropZone will belong.</p>
+     * <p>This defines which other DragZones the DropZone will interact with. Drag/DropZones only interact with other Drag/DropZones
+     * which are members of the same ddGroup.</p>
+     */
+
+    /**
+     * @cfg {Boolean} enableDrop
+     * <p>Defaults to <code>true</code></p>
+     * <p>Set to <code>false</code> to disallow the View from accepting drop gestures</p>
+     */
+    enableDrop: true,
+
+    /**
+     * @cfg {Boolean} enableDrag
+     * <p>Defaults to <code>true</code></p>
+     * <p>Set to <code>false</code> to disallow dragging items from the View </p>
+     */
+    enableDrag: true,
+
+    init : function(view) {
+        view.on('render', this.onViewRender, this, {single: true});
+    },
+
+    /**
+     * @private
+     * AbstractComponent calls destroy on all its plugins at destroy time.
+     */
+    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
+            });
+        }
+    }
+});
+/**
+ * @class Ext.grid.plugin.HeaderReorderer
+ * @extends Ext.util.Observable
+ * @private
+ */
+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);
+    },
+
+    /**
+     * @private
+     * AbstractComponent calls destroy on all its plugins at destroy time.
+     */
+    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();
+        }
+    }
+});
+/**
+ * @class Ext.grid.plugin.HeaderResizer
+ * @extends Ext.util.Observable
+ * 
+ * Plugin to add header resizing functionality to a HeaderContainer.
+ * Always resizing header to the left of the splitter you are resizing.
+ * 
+ * Todo: Consider RTL support, columns would always calculate to the right of
+ *    the splitter instead of to the left.
+ */
+Ext.define('Ext.grid.plugin.HeaderResizer', {
+    extend: 'Ext.util.Observable',
+    requires: ['Ext.dd.DragTracker', 'Ext.util.Region'],
+    alias: 'plugin.gridheaderresizer',
+    
+    disabled: false,
+
+    /**
+     * @cfg {Boolean} dynamic
+     * Set to true to resize on the fly rather than using a proxy marker. Defaults to false.
+     */
+    configs: {
+        dynamic: true
+    },
+
+    colHeaderCls: Ext.baseCSSPrefix + 'column-header',
+
+    minColWidth: 40,
+    maxColWidth: 1000,
+    wResizeCursor: 'col-resize',
+    eResizeCursor: 'col-resize',
+    // not using w and e resize bc we are only ever resizing one
+    // column
+    //wResizeCursor: Ext.isWebKit ? 'w-resize' : 'col-resize',
+    //eResizeCursor: Ext.isWebKit ? 'e-resize' : 'col-resize',
+
+    init: function(headerCt) {
+        this.headerCt = headerCt;
+        headerCt.on('render', this.afterHeaderRender, this, {single: true});
+    },
+
+    /**
+     * @private
+     * AbstractComponent calls destroy on all its plugins at destroy time.
+     */
+    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
+        });
+    },
+
+    // As we mouse over individual headers, change the cursor to indicate
+    // that resizing is available, and cache the resize target header for use
+    // if/when they mousedown.
+    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);
+
+                // On left edge, we are resizing the previous non-hidden, base level column.
+                if (overHeader.isOnLeftEdge(e)) {
+                    resizeHeader = overHeader.previousNode('gridcolumn:not([hidden]):not([isGroupHeader])');
+                }
+                // Else, if on the right edge, we're resizing the column we are over
+                else if (overHeader.isOnRightEdge(e)) {
+                    resizeHeader = overHeader;
+                }
+                // Between the edges: we are not resizing
+                else {
+                    resizeHeader = null;
+                }
+
+                // We *are* resizing
+                if (resizeHeader) {
+                    // If we're attempting to resize a group header, that cannot be resized,
+                    // so find its last base level column header; Group headers are sized
+                    // by the size of their child headers.
+                    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;
+                    }
+                // reset
+                } else {
+                    overHeader.el.dom.style.cursor = '';
+                    delete this.activeHd;
+                }
+            }
+        }
+    },
+
+    // only start when there is an activeHd
+    onBeforeStart : function(e){
+        var t = e.getTarget();
+        // cache the activeHd because it will be cleared.
+        this.dragHd = this.activeHd;
+
+        if (!!this.dragHd && !Ext.fly(t).hasCls('x-column-header-trigger') && !this.headerCt.dragging) {
+            //this.headerCt.dragging = true;
+            this.tracker.constrainTo = this.getConstrainRegion();
+            return true;
+        } else {
+            this.headerCt.dragging = false;
+            return false;
+        }
+    },
+
+    // get the region to constrain to, takes into account max and min col widths
+    getConstrainRegion: function() {
+        var dragHdEl = this.dragHd.el,
+            region   = Ext.util.Region.getRegion(dragHdEl);
+
+        return region.adjust(
+            0,
+            this.maxColWidth - dragHdEl.getWidth(),
+            0,
+            this.minColWidth
+        );
+    },
+
+    // initialize the left and right hand side markers around
+    // the header that we are resizing
+    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;
+
+        // setup marker proxies
+        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);
+        }
+    },
+
+    // synchronize the rhsMarker with the mouse movement
+    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);
+        // Resize as user interacts
+        } 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;
+
+                // hide markers
+                lhsMarker.setLeft(offscreen);
+                rhsMarker.setLeft(offscreen);
+            }
+            this.doResize();
+        }
+    },
+
+    doResize: function() {
+        if (this.dragHd) {
+            var dragHd = this.dragHd,
+                nextHd,
+                offset = this.tracker.getOffset('point');
+
+            // resize the dragHd
+            if (dragHd.flex) {
+                delete dragHd.flex;
+            }
+
+            // If HeaderContainer is configured forceFit, inhibit upstream layout notification, so that
+            // we can also shrink the following Header by an equal amount, and *then* inform the upstream layout.
+            if (this.headerCt.forceFit) {
+                nextHd = dragHd.nextNode('gridcolumn:not([hidden]):not([isGroupHeader])');
+                if (nextHd) {
+                    this.headerCt.componentLayout.layoutBusy = true;
+                }
+            }
+
+            // Non-flexed Headers may never be squeezed in the event of a shortfall so
+            // always set the minWidth to their current width.
+            dragHd.minWidth = this.origWidth + offset[0];
+            dragHd.setWidth(dragHd.minWidth);
+
+            // In the case of forceFit, change the following Header width.
+            // Then apply the two width changes by laying out the owning HeaderContainer
+            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();
+        }
+    }
+});
+/**
+ * @class Ext.grid.plugin.RowEditing
+ * @extends Ext.grid.plugin.Editing
+ * 
+ * The Ext.grid.plugin.RowEditing plugin injects editing at a row level for a Grid. When editing begins,
+ * a small floating dialog will be shown for the appropriate row. Each editable column will show a field
+ * for editing. There is a button to save or cancel all changes for the edit.
+ * 
+ * The field that will be used for the editor is defined at the
+ * {@link Ext.grid.column.Column#field field}. The editor can be a field instance or a field configuration.
+ * If an editor is not specified for a particular column then that column won't be editable and the value of
+ * the column will be displayed.
+ * The editor may be shared for each column in the grid, or a different one may be specified for each column.
+ * An appropriate field type should be chosen to match the data structure that it will be editing. For example,
+ * to edit a date, it would be useful to specify {@link Ext.form.field.Date} as the editor.
+ * 
+ * {@img Ext.grid.plugin.RowEditing/Ext.grid.plugin.RowEditing.png Ext.grid.plugin.RowEditing plugin}
+ *
+ * ## Example Usage
+ *    Ext.create('Ext.data.Store', {
+ *        storeId:'simpsonsStore',
+ *        fields:['name', 'email', 'phone'],
+ *        data:{'items':[
+ *            {"name":"Lisa", "email":"lisa@simpsons.com", "phone":"555-111-1224"},
+ *            {"name":"Bart", "email":"bart@simpsons.com", "phone":"555--222-1234"},
+ *            {"name":"Homer", "email":"home@simpsons.com", "phone":"555-222-1244"},                        
+ *            {"name":"Marge", "email":"marge@simpsons.com", "phone":"555-222-1254"}            
+ *        ]},
+ *        proxy: {
+ *            type: 'memory',
+ *            reader: {
+ *                type: 'json',
+ *                root: 'items'
+ *            }
+ *        }
+ *    });
+ *   
+ *    Ext.create('Ext.grid.Panel', {
+ *        title: 'Simpsons',
+ *        store: Ext.data.StoreManager.lookup('simpsonsStore'),
+ *        columns: [
+ *            {header: 'Name',  dataIndex: 'name', field: 'textfield'},
+ *            {header: 'Email', dataIndex: 'email', flex:1, 
+ *                editor: {
+ *                    xtype:'textfield',
+ *                    allowBlank:false
+ *                }
+ *            },
+ *            {header: 'Phone', dataIndex: 'phone'}
+ *        ],
+ *        selType: 'rowmodel',
+ *        plugins: [
+ *            Ext.create('Ext.grid.plugin.RowEditing', {
+ *                clicksToEdit: 1
+ *            })
+ *        ],
+ *        height: 200,
+ *        width: 400,
+ *        renderTo: Ext.getBody()
+ *    });
+ * 
+ * @markdown
+ *
+ */
+Ext.define('Ext.grid.plugin.RowEditing', {
+    extend: 'Ext.grid.plugin.Editing',
+    alias: 'plugin.rowediting',
+
+    requires: [
+        'Ext.grid.RowEditor'
+    ],
+
+    editStyle: 'row',
+
+    /**
+     * @cfg {Boolean} autoCancel
+     * `true` to automatically cancel any pending changes when the row editor begins editing a new row.
+     * `false` to force the user to explicitly cancel the pending changes. Defaults to `true`.
+     * @markdown
+     */
+    autoCancel: true,
+
+    /**
+     * @cfg {Number} clicksToMoveEditor
+     * The number of clicks to move the row editor to a new row while it is visible and actively editing another row.
+     * This will default to the same value as {@link Ext.grid.plugin.Editing#clicksToEdit clicksToEdit}.
+     * @markdown
+     */
+
+    /**
+     * @cfg {Boolean} errorSummary
+     * `true` to show a {@link Ext.tip.ToolTip tooltip} that summarizes all validation errors present
+     * in the row editor. Set to `false` to prevent the tooltip from showing. Defaults to `true`.
+     * @markdown
+     */
+    errorSummary: true,
+
+    /**
+     * @event beforeedit
+     * Fires before row editing is triggered. The edit event object has the following properties <br />
+     * <ul style="padding:5px;padding-left:16px;">
+     * <li>grid - The grid this editor is on</li>
+     * <li>view - The grid view</li>
+     * <li>store - The grid store</li>
+     * <li>record - The record being edited</li>
+     * <li>row - The grid table row</li>
+     * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit</li>
+     * <li>rowIdx - The row index that is being edited</li>
+     * <li>colIdx - The column index that initiated the edit</li>
+     * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
+     * </ul>
+     * @param {Ext.grid.plugin.Editing} editor
+     * @param {Object} e An edit event (see above for description)
+     */
+    /**
+     * @event edit
+     * Fires after a row is edited. The edit event object has the following properties <br />
+     * <ul style="padding:5px;padding-left:16px;">
+     * <li>grid - The grid this editor is on</li>
+     * <li>view - The grid view</li>
+     * <li>store - The grid store</li>
+     * <li>record - The record being edited</li>
+     * <li>row - The grid table row</li>
+     * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit</li>
+     * <li>rowIdx - The row index that is being edited</li>
+     * <li>colIdx - The column index that initiated the edit</li>
+     * </ul>
+     *
+     * <pre><code>
+grid.on('edit', onEdit, this);
+
+function onEdit(e) {
+    // execute an XHR to send/commit data to the server, in callback do (if successful):
+    e.record.commit();
+};
+     * </code></pre>
+     * @param {Ext.grid.plugin.Editing} editor
+     * @param {Object} e An edit event (see above for description)
+     */
+    /**
+     * @event validateedit
+     * Fires after a cell is edited, but before the value is set in the record. Return false
+     * to cancel the change. The edit event object has the following properties <br />
+     * <ul style="padding:5px;padding-left:16px;">
+     * <li>grid - The grid this editor is on</li>
+     * <li>view - The grid view</li>
+     * <li>store - The grid store</li>
+     * <li>record - The record being edited</li>
+     * <li>row - The grid table row</li>
+     * <li>column - The grid {@link Ext.grid.column.Column Column} defining the column that initiated the edit</li>
+     * <li>rowIdx - The row index that is being edited</li>
+     * <li>colIdx - The column index that initiated the edit</li>
+     * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
+     * </ul>
+     * Usage example showing how to remove the red triangle (dirty record indicator) from some
+     * records (not all).  By observing the grid's validateedit event, it can be cancelled if
+     * the edit occurs on a targeted row (for example) and then setting the field's new value
+     * in the Record directly:
+     * <pre><code>
+grid.on('validateedit', function(e) {
+  var myTargetRow = 6;
+
+  if (e.rowIdx == myTargetRow) {
+    e.cancel = true;
+    e.record.data[e.field] = e.value;
+  }
+});
+     * </code></pre>
+     * @param {Ext.grid.plugin.Editing} editor
+     * @param {Object} e An edit event (see above for description)
+     */
+
+    constructor: function() {
+        var me = this;
+        me.callParent(arguments);
+
+        if (!me.clicksToMoveEditor) {
+            me.clicksToMoveEditor = me.clicksToEdit;
+        }
+
+        me.autoCancel = !!me.autoCancel;
+    },
+
+    /**
+     * @private
+     * AbstractComponent calls destroy on all its plugins at destroy time.
+     */
+    destroy: function() {
+        var me = this;
+        Ext.destroy(me.editor);
+        me.callParent(arguments);
+    },
+
+    /**
+     * Start editing the specified record, using the specified Column definition to define which field is being edited.
+     * @param {Model} record The Store data record which backs the row to be edited.
+     * @param {Model} columnHeader The Column object defining the column to be edited.
+     * @override
+     */
+    startEdit: function(record, columnHeader) {
+        var me = this,
+            editor = me.getEditor();
+
+        if (me.callParent(arguments) === false) {
+            return false;
+        }
+
+        // Fire off our editor
+        if (editor.beforeEdit() !== false) {
+            editor.startEdit(me.context.record, me.context.column);
+        }
+    },
+
+    // private
+    cancelEdit: function() {
+        var me = this;
+
+        if (me.editing) {
+            me.getEditor().cancelEdit();
+            me.callParent(arguments);
+        }
+    },
+
+    // private
+    completeEdit: function() {
+        var me = this;
+
+        if (me.editing && me.validateEdit()) {
+            me.editing = false;
+            me.fireEvent('edit', me.context);
+        }
+    },
+
+    // private
+    validateEdit: function() {
+        var me = this;
+        return me.callParent(arguments) && me.getEditor().completeEdit();
+    },
+
+    // private
+    getEditor: function() {
+        var me = this;
+
+        if (!me.editor) {
+            me.editor = me.initEditor();
+        }
+        return me.editor;
+    },
+
+    // private
+    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,
+
+            // keep a reference..
+            editingPlugin: me,
+            renderTo: view.el
+        });
+    },
+
+    // private
+    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() {
+            // Column events
+            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);
+        }
+    },
+
+    // private
+    onColumnAdd: function(ct, column) {
+        var me = this,
+            editor = me.getEditor();
+
+        me.initFieldAccessors(column);
+        if (editor && editor.onColumnAdd) {
+            editor.onColumnAdd(column);
+        }
+    },
+
+    // private
+    onColumnRemove: function(ct, column) {
+        var me = this,
+            editor = me.getEditor();
+
+        if (editor && editor.onColumnRemove) {
+            editor.onColumnRemove(column);
+        }
+        me.removeFieldAccessors(column);
+    },
+
+    // private
+    onColumnResize: function(ct, column, width) {
+        var me = this,
+            editor = me.getEditor();
+
+        if (editor && editor.onColumnResize) {
+            editor.onColumnResize(column, width);
+        }
+    },
+
+    // private
+    onColumnHide: function(ct, column) {
+        var me = this,
+            editor = me.getEditor();
+
+        if (editor && editor.onColumnHide) {
+            editor.onColumnHide(column);
+        }
+    },
+
+    // private
+    onColumnShow: function(ct, column) {
+        var me = this,
+            editor = me.getEditor();
+
+        if (editor && editor.onColumnShow) {
+            editor.onColumnShow(column);
+        }
+    },
+
+    // private
+    onColumnMove: function(ct, column, fromIdx, toIdx) {
+        var me = this,
+            editor = me.getEditor();
+
+        if (editor && editor.onColumnMove) {
+            editor.onColumnMove(column, fromIdx, toIdx);
+        }
+    },
+
+    // private
+    setColumnField: function(column, field) {
+        var me = this;
+        me.callParent(arguments);
+        me.getEditor().setField(column.field, column);
+    }
+});
+/**
+ * @class Ext.grid.property.Grid
+ * @extends Ext.grid.Panel
+ * A specialized grid implementation intended to mimic the traditional property grid as typically seen in
+ * development IDEs.  Each row in the grid represents a property of some object, and the data is stored
+ * as a set of name/value pairs in {@link Ext.grid.property.Property Properties}.  Example usage:
+ * <pre><code>
+var grid = new Ext.grid.property.Grid({
+    title: 'Properties Grid',
+    width: 300,
+    renderTo: 'grid-ct',
+    source: {
+        "(name)": "My Object",
+        "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'),
+        "Available": false,
+        "Version": .01,
+        "Description": "A test object"
+    }
+});
+</code></pre>
+ * @constructor
+ * @param {Object} config The grid config object
+ */
+Ext.define('Ext.grid.property.Grid', {
+
+    extend: 'Ext.grid.Panel',
+
+    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'
+    ],
+
+   /**
+    * @cfg {Object} propertyNames An object containing custom property name/display name pairs.
+    * If specified, the display name will be shown in the name column instead of the property name.
+    */
+
+    /**
+    * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
+    */
+
+    /**
+    * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow
+    * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
+    * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
+    * associated with a custom input control by specifying a custom editor.  The name of the editor
+    * type should correspond with the name of the property that will use the editor.  Example usage:
+    * <pre><code>
+var grid = new Ext.grid.property.Grid({
+
+    // Custom editors for certain property names
+    customEditors: {
+        evtStart: Ext.create('Ext.form.TimeField' {selectOnFocus:true})
+    },
+
+    // Displayed name for property names in the source
+    propertyNames: {
+        evtStart: 'Start Time'
+    },
+
+    // Data object containing properties to edit
+    source: {
+        evtStart: '10:00 AM'
+    }
+});
+</code></pre>
+    */
+
+    /**
+    * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).
+    */
+
+    /**
+    * @cfg {Object} customRenderers An object containing name/value pairs of custom renderer type definitions that allow
+    * the grid to support custom rendering of fields.  By default, the grid supports strongly-typed rendering
+    * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
+    * associated with the type of the value.  The name of the renderer type should correspond with the name of the property
+    * that it will render.  Example usage:
+    * <pre><code>
+var grid = Ext.create('Ext.grid.property.Grid', {
+    customRenderers: {
+        Available: function(v){
+            if (v) {
+                return '<span style="color: green;">Yes</span>';
+            } else {
+                return '<span style="color: red;">No</span>';
+            }
+        }
+    },
+    source: {
+        Available: true
+    }
+});
+</code></pre>
+    */
+
+    /**
+     * @cfg {String} valueField
+     * Optional. The name of the field from the property store to use as the value field name. Defaults to <code>'value'</code>
+     * This may be useful if you do not configure the property Grid from an object, but use your own store configuration.
+     */
+    valueField: 'value',
+
+    /**
+     * @cfg {String} nameField
+     * Optional. The name of the field from the property store to use as the property field name. Defaults to <code>'name'</code>
+     * This may be useful if you do not configure the property Grid from an object, but use your own store configuration.
+     */
+    nameField: 'name',
+
+    // private config overrides
+    enableColumnMove: false,
+    columnLines: true,
+    stripeRows: false,
+    trackMouseOver: false,
+    clicksToEdit: 1,
+    enableHdMenu: false,
+
+    // private
+    initComponent : function(){
+        var me = this;
+
+        me.addCls(Ext.baseCSSPrefix + 'property-grid');
+        me.plugins = me.plugins || [];
+
+        // Enable cell editing. Inject a custom startEdit which always edits column 1 regardless of which column was clicked.
+        me.plugins.push(Ext.create('Ext.grid.plugin.CellEditing', {
+            clicksToEdit: me.clicksToEdit,
+
+            // Inject a startEdit which always edits the value column
+            startEdit: function(record, column) {
+                // Maintainer: Do not change this 'this' to 'me'! It is the CellEditing object's own scope.
+                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 || {};
+
+        // Create a property.Store from the source object unless configured with a store
+        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(
+            /**
+             * @event beforepropertychange
+             * Fires before a property value changes.  Handlers can return false to cancel the property change
+             * (this will internally call {@link Ext.data.Record#reject} on the property's record).
+             * @param {Object} source The source data object for the grid (corresponds to the same object passed in
+             * as the {@link #source} config property).
+             * @param {String} recordId The record's id in the data store
+             * @param {Mixed} value The current edited property value
+             * @param {Mixed} oldValue The original property value prior to editing
+             */
+            'beforepropertychange',
+            /**
+             * @event propertychange
+             * Fires after a property value has changed.
+             * @param {Object} source The source data object for the grid (corresponds to the same object passed in
+             * as the {@link #source} config property).
+             * @param {String} recordId The record's id in the data store
+             * @param {Mixed} value The current edited property value
+             * @param {Mixed} oldValue The original property value prior to editing
+             */
+            'propertychange'
+        );
+        me.callParent();
+
+        // Inject a custom implementation of walkCells which only goes up or down
+        me.getView().walkCells = this.walkCells;
+
+        // Set up our default editor set for the 4 atomic data types
+        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 ]]
+            })})
+        };
+
+        // Track changes to the data so we can fire our events.
+        this.store.on('update', me.onUpdate, me);
+    },
+
+    // private
+    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.id, v, oldValue) !== false) {
+                if (me.source) {
+                    me.source[record.id] = v;
+                }
+                record.commit();
+                me.fireEvent('propertychange', me.source, record.id, v, oldValue);
+            } else {
+                record.reject();
+            }
+        }
+    },
+
+    // Custom implementation of walkCells which only goes up and down.
+    walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
+        if (direction == 'left') {
+            direction = 'up';
+        } else if (direction == 'right') {
+            direction = 'down';
+        }
+        var pos = Ext.view.Table.prototype.walkCells.call(this, pos, direction, e, preventWrap, verifierFn, scope);
+        if (!pos.column) {
+            pos.column = 1;
+        }
+        return pos;
+    },
+
+    // private
+    // returns the correct editor type for the property type, or a custom one keyed by the property name
+    getCellEditor : function(record, column) {
+        var me = this,
+            propName = record.get(me.nameField), 
+            val = record.get(me.valueField),
+            editor = me.customEditors[propName];
+
+        // A custom editor was found. If not already wrapped with a CellEditor, wrap it, and stash it back
+        // If it's not even a Field, just a config object, instantiate it before wrapping it.
+        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;
+        }
+
+        // Give the editor a unique ID because the CellEditing plugin caches them
+        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) {
+            Ext.destroy(editors[ed]);
+        }
+    },
+
+    /**
+     * Sets the source data object containing the property data.  The data object can contain one or more name/value
+     * pairs representing all of the properties of an object to display in the grid, and this data will automatically
+     * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,
+     * otherwise string type will be assumed.  If the grid already contains data, this method will replace any
+     * existing data.  See also the {@link #source} config value.  Example usage:
+     * <pre><code>
+grid.setSource({
+    "(name)": "My Object",
+    "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'),  // date type
+    "Available": false,  // boolean type
+    "Version": .01,      // decimal type
+    "Description": "A test object"
+});
+</code></pre>
+     * @param {Object} source The data object
+     */
+    setSource: function(source) {
+        this.source = source;
+        this.propStore.setSource(source);
+    },
+
+    /**
+     * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the
+     * format of the data object.
+     * @return {Object} The data object
+     */
+    getSource: function() {
+        return this.propStore.getSource();
+    },
+
+    /**
+     * Sets the value of a property.
+     * @param {String} prop The name of the property to set
+     * @param {Mixed} value The value to test
+     * @param {Boolean} create (Optional) True to create the property if it doesn't already exist. Defaults to <tt>false</tt>.
+     */
+    setProperty: function(prop, value, create) {
+        this.propStore.setValue(prop, value, create);
+    },
+
+    /**
+     * Removes a property from the grid.
+     * @param {String} prop The name of the property to remove
+     */
+    removeProperty: function(prop) {
+        this.propStore.remove(prop);
+    }
+
+    /**
+     * @cfg store
+     * @hide
+     */
+    /**
+     * @cfg colModel
+     * @hide
+     */
+    /**
+     * @cfg cm
+     * @hide
+     */
+    /**
+     * @cfg columns
+     * @hide
+     */
+});
+/**
+ * @class Ext.grid.property.HeaderContainer
+ * @extends Ext.grid.header.Container
+ * A custom HeaderContainer for the {@link Ext.grid.property.Grid}.  Generally it should not need to be used directly.
+ * @constructor
+ * @param {Ext.grid.property.Grid} grid The grid this store will be bound to
+ * @param {Object} source The source data config object
+ */
+Ext.define('Ext.grid.property.HeaderContainer', {
+
+    extend: 'Ext.grid.header.Container',
+
+    alternateClassName: 'Ext.grid.PropertyColumnModel',
+
+    // private - strings used for locale support
+    nameText : 'Name',
+    valueText : 'Value',
+    dateFormat : 'm/j/Y',
+    trueText: 'true',
+    falseText: 'false',
+
+    // private
+    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
+            }]
+        }]);
+    },
+
+    // private
+    // Render a property name cell
+    renderProp : function(v) {
+        return this.getPropertyName(v);
+    },
+
+    // private
+    // Render a property value cell
+    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);
+    },
+
+    // private
+    renderDate : Ext.util.Format.date,
+
+    // private
+    renderBool : function(bVal) {
+        return this[bVal ? 'trueText' : 'falseText'];
+    },
+
+    // private
+    // Renders custom property names instead of raw names if defined in the Grid
+    getPropertyName : function(name) {
+        var pn = this.grid.propertyNames;
+        return pn && pn[name] ? pn[name] : name;
+    }
+});
+/**
+ * @class Ext.grid.property.Property
+ * A specific {@link Ext.data.Model} type that represents a name/value pair and is made to work with the
+ * {@link Ext.grid.property.Grid}.  Typically, Properties do not need to be created directly as they can be
+ * created implicitly by simply using the appropriate data configs either via the {@link Ext.grid.property.Grid#source}
+ * config property or by calling {@link Ext.grid.property.Grid#setSource}.  However, if the need arises, these records
+ * can also be created explicitly as shown below.  Example usage:
+ * <pre><code>
+var rec = new Ext.grid.property.Property({
+    name: 'birthday',
+    value: Ext.Date.parse('17/06/1962', 'd/m/Y')
+});
+// Add record to an already populated grid
+grid.store.addSorted(rec);
+</code></pre>
+ * @constructor
+ * @param {Object} config A data object in the format:<pre><code>
+{
+    name: [name],
+    value: [value]
+}</code></pre>
+ * The specified value's type
+ * will be read automatically by the grid to determine the type of editor to use when displaying it.
+ */
+Ext.define('Ext.grid.property.Property', {
+    extend: 'Ext.data.Model',
+
+    alternateClassName: 'Ext.PropGridProperty',
+
+    fields: [{
+        name: 'name',
+        type: 'string'
+    }, {
+        name: 'value'
+    }],
+    idProperty: 'name'
+});
+/**
+ * @class Ext.grid.property.Store
+ * @extends Ext.data.Store
+ * A custom {@link Ext.data.Store} for the {@link Ext.grid.property.Grid}. This class handles the mapping
+ * between the custom data source objects supported by the grid and the {@link Ext.grid.property.Property} format
+ * used by the {@link Ext.data.Store} base class.
+ * @constructor
+ * @param {Ext.grid.Grid} grid The grid this store will be bound to
+ * @param {Object} source The source data config object
+ */
+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){
+        this.grid = grid;
+        this.source = source;
+        this.callParent([{
+            data: source,
+            model: Ext.grid.property.Property,
+            proxy: this.getProxy()
+        }]);
+    },
+
+    // Return a singleton, customized Proxy object which configures itself with a custom Reader
+    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;
+    },
+
+    // Return a singleton, customized Reader object which reads Ext.grid.property.Property records from an object.
+    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,
+                        result = {
+                            records: [],
+                            success: true
+                        };
+
+                    for (var propName in dataObject) {
+                        val = dataObject[propName];
+                        if (dataObject.hasOwnProperty(propName) && 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);
+                },
+
+                // private
+                isEditableValue: function(val){
+                    return Ext.isPrimitive(val) || Ext.isDate(val);
+                }
+            });
+        }
+        return this.reader;
+    },
+
+    // protected - should only be called by the grid.  Use grid.setSource instead.
+    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);
+    },
+
+    // private
+    getProperty : function(row) {
+       return Ext.isNumber(row) ? this.store.getAt(row) : this.store.getById(row);
+    },
+
+    // private
+    setValue : function(prop, value, create){
+        var r = this.getRec(prop);
+        if (r) {
+            r.set('value', value);
+            this.source[prop] = value;
+        } else if (create) {
+            // only create if specified.
+            this.source[prop] = value;
+            r = new Ext.grid.property.Property({name: prop, value: value}, prop);
+            this.store.add(r);
+        }
+    },
+
+    // private
+    remove : function(prop) {
+        var r = this.getRec(prop);
+        if(r) {
+            this.store.remove(r);
+            delete this.source[prop];
+        }
+    },
+
+    // private
+    getRec : function(prop) {
+        return this.store.getById(prop);
+    },
+
+    // protected - should only be called by the grid.  Use grid.getSource instead.
+    getSource : function() {
+        return this.source;
+    }
+});
+/**
+ * Component layout for components which maintain an inner body element which must be resized to synchronize with the
+ * Component size.
+ * @class Ext.layout.component.Body
+ * @extends Ext.layout.component.Component
+ * @private
+ */
+
+Ext.define('Ext.layout.component.Body', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.body'],
+
+    extend: 'Ext.layout.component.Component',
+
+    uses: ['Ext.layout.container.Container'],
+
+    /* End Definitions */
+
+    type: 'body',
+    
+    onLayout: function(width, height) {
+        var me = this,
+            owner = me.owner;
+
+        // Size the Component's encapsulating element according to the dimensions
+        me.setTargetSize(width, height);
+
+        // Size the Component's body element according to the content box of the encapsulating element
+        me.setBodySize.apply(me, arguments);
+
+        // We need to bind to the owner whenever we do not have a user set height or width.
+        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);
+    },
+
+    /**
+     * @private
+     * <p>Sizes the Component's body element to fit exactly within the content box of the Component's encapsulating element.<p>
+     */
+    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);
+    }
+});
+/**
+ * Component layout for Ext.form.FieldSet components
+ * @class Ext.layout.component.FieldSet
+ * @extends Ext.layout.component.Body
+ * @private
+ */
+Ext.define('Ext.layout.component.FieldSet', {
+    extend: 'Ext.layout.component.Body',
+    alias: ['layout.fieldset'],
+
+    type: 'fieldset',
+
+    doContainerLayout: function() {
+        // Prevent layout/rendering of children if the fieldset is collapsed
+        if (!this.owner.collapsed) {
+            this.callParent();
+        }
+    }
+});
+/**
+ * Component layout for tabs
+ * @class Ext.layout.component.Tab
+ * @extends Ext.layout.component.Button
+ * @private
+ */
+Ext.define('Ext.layout.component.Tab', {
+
+    alias: ['layout.tab'],
+
+    extend: 'Ext.layout.component.Button',
+
+    //type: '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;
+    }
+});
+/**
+ * @private
+ * @class Ext.layout.component.field.File
+ * @extends Ext.layout.component.field.Field
+ * Layout class for {@link Ext.form.field.File} fields. Adjusts the input field size to accommodate
+ * the file picker trigger button.
+ * @private
+ */
+
+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) {
+            // Decrease the field's width by the width of the button and the configured buttonMargin.
+            // Both the text field and the button are floated left in CSS so they'll stack up side by side.
+            me.setElementSize(owner.inputEl, Ext.isNumber(width) ? width - owner.button.getWidth() - owner.buttonMargin : width);
+        }
+    }
+});
+/**
+ * @class Ext.layout.component.field.Slider
+ * @extends Ext.layout.component.field.Field
+ * @private
+ */
+
+Ext.define('Ext.layout.component.field.Slider', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.sliderfield'],
+
+    extend: 'Ext.layout.component.field.Field',
+
+    /* End Definitions */
+
+    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;
+
+        /*
+         * If we happen to be animating during a resize, the position of the thumb will likely be off
+         * when the animation stops. As such, just stop any animations before syncing the thumbs.
+         */
+        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();
+    }
+});
+
+/**
+ * @class Ext.layout.container.Absolute
+ * @extends Ext.layout.container.Anchor
+ * <p>This is a layout that inherits the anchoring of <b>{@link Ext.layout.container.Anchor}</b> and adds the
+ * ability for x/y positioning using the standard x and y component config options.</p>
+ * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.container.Container#layout layout}</b></tt>
+ * configuration property.  See <tt><b>{@link Ext.container.Container#layout}</b></tt> for additional details.</p>
+ * {@img Ext.layout.container.Absolute/Ext.layout.container.Absolute.png Ext.layout.container.Absolute container layout}
+ * <p>Example usage:</p>
+ * <pre><code>
+    Ext.create('Ext.form.Panel', {
+        title: 'Absolute Layout',
+        width: 300,
+        height: 275,
+        layout:'absolute',
+        layoutConfig: {
+            // layout-specific configs go here
+            //itemCls: 'x-abs-layout-item',
+        },
+        url:'save-form.php',
+        defaultType: 'textfield',
+        items: [{
+            x: 10,
+            y: 10,
+            xtype:'label',
+            text: 'Send To:'
+        },{
+            x: 80,
+            y: 10,
+            name: 'to',
+            anchor:'90%'  // anchor width by percentage
+        },{
+            x: 10,
+            y: 40,
+            xtype:'label',
+            text: 'Subject:'
+        },{
+            x: 80,
+            y: 40,
+            name: 'subject',
+            anchor: '90%'  // anchor width by percentage
+        },{
+            x:0,
+            y: 80,
+            xtype: 'textareafield',
+            name: 'msg',
+            anchor: '100% 100%'  // anchor width and height
+        }],
+        renderTo: Ext.getBody()
+    });
+</code></pre>
+ */
+
+Ext.define('Ext.layout.container.Absolute', {
+
+    /* Begin Definitions */
+
+    alias: 'layout.absolute',
+    extend: 'Ext.layout.container.Anchor',
+    requires: ['Ext.chart.axis.Axis', 'Ext.fx.Anim'],
+    alternateClassName: 'Ext.layout.AbsoluteLayout',
+
+    /* End Definitions */
+
+    itemCls: Ext.baseCSSPrefix + 'abs-layout-item',
+
+    type: 'absolute',
+
+    onLayout: function() {
+        var me = this,
+            target = me.getTarget(),
+            targetIsBody = target.dom === document.body;
+
+        // Do not set position: relative; when the absolute layout target is the body
+        if (!targetIsBody) {
+            target.position();
+        }
+        me.paddingLeft = target.getPadding('l');
+        me.paddingTop = target.getPadding('t');
+        me.callParent(arguments);
+    },
+
+    // private
+    adjustWidthAnchor: function(value, comp) {
+        //return value ? value - comp.getPosition(true)[0] + this.paddingLeft: value;
+        return value ? value - comp.getPosition(true)[0] : value;
+    },
+
+    // private
+    adjustHeightAnchor: function(value, comp) {
+        //return value ? value - comp.getPosition(true)[1] + this.paddingTop: value;
+        return value ? value - comp.getPosition(true)[1] : value;
+    }
+});
+/**
+ * @class Ext.layout.container.Accordion
+ * @extends Ext.layout.container.VBox
+ * <p>This is a layout that manages multiple Panels in an expandable accordion style such that only
+ * <b>one Panel can be expanded at any given time</b>. Each Panel has built-in support for expanding and collapsing.</p>
+ * <p>Note: Only Ext.Panels <b>and all subclasses of Ext.panel.Panel</b> may be used in an accordion layout Container.</p>
+ * {@img Ext.layout.container.Accordion/Ext.layout.container.Accordion.png Ext.layout.container.Accordion container layout}
+ * <p>Example usage:</p>
+ * <pre><code>
+    Ext.create('Ext.panel.Panel', {
+        title: 'Accordion Layout',
+        width: 300,
+        height: 300,
+        layout:'accordion',
+        defaults: {
+            // applied to each contained panel
+            bodyStyle: 'padding:15px'
+        },
+        layoutConfig: {
+            // layout-specific configs go here
+            titleCollapse: false,
+            animate: true,
+            activeOnTop: true
+        },
+        items: [{
+            title: 'Panel 1',
+            html: '<p>Panel content!</p>'
+        },{
+            title: 'Panel 2',
+            html: '<p>Panel content!</p>'
+        },{
+            title: 'Panel 3',
+            html: '<p>Panel content!</p>'
+        }],
+        renderTo: Ext.getBody()
+    });
+</code></pre>
+ */
+Ext.define('Ext.layout.container.Accordion', {
+    extend: 'Ext.layout.container.VBox',
+    alias: ['layout.accordion'],
+    alternateClassName: 'Ext.layout.AccordionLayout',
+    
+    align: 'stretch',
+
+    /**
+     * @cfg {Boolean} fill
+     * True to adjust the active item's height to fill the available space in the container, false to use the
+     * item's current height, or auto height if not explicitly set (defaults to true).
+     */
+    fill : true,
+    /**
+     * @cfg {Boolean} autoWidth
+     * <p><b>This config is ignored in ExtJS 4.x.</b></p>
+     * Child Panels have their width actively managed to fit within the accordion's width.
+     */
+    autoWidth : true,
+    /**
+     * @cfg {Boolean} titleCollapse
+     * <p><b>Not implemented in PR2.</b></p>
+     * True to allow expand/collapse of each contained panel by clicking anywhere on the title bar, false to allow
+     * expand/collapse only when the toggle tool button is clicked (defaults to true).  When set to false,
+     * {@link #hideCollapseTool} should be false also.
+     */
+    titleCollapse : true,
+    /**
+     * @cfg {Boolean} hideCollapseTool
+     * True to hide the contained Panels' collapse/expand toggle buttons, false to display them (defaults to false).
+     * When set to true, {@link #titleCollapse} is automatically set to <code>true</code>.
+     */
+    hideCollapseTool : false,
+    /**
+     * @cfg {Boolean} collapseFirst
+     * True to make sure the collapse/expand toggle button always renders first (to the left of) any other tools
+     * in the contained Panels' title bars, false to render it last (defaults to false).
+     */
+    collapseFirst : false,
+    /**
+     * @cfg {Boolean} animate
+     * True to slide the contained panels open and closed during expand/collapse using animation, false to open and
+     * close directly with no animation (defaults to <code>true</code>). Note: The layout performs animated collapsing
+     * and expanding, <i>not</i> the child Panels.
+     */
+    animate : true,
+    /**
+     * @cfg {Boolean} activeOnTop
+     * <p><b>Not implemented in PR4.</b></p>
+     * <p>Only valid when {@link #multi" is <code>false</code>.</p>
+     * True to swap the position of each panel as it is expanded so that it becomes the first item in the container,
+     * false to keep the panels in the rendered order. <b>This is NOT compatible with "animate:true"</b> (defaults to false).
+     */
+    activeOnTop : false,
+    /**
+     * @cfg {Boolean} multi
+     * Defaults to <code>false</code>. Set to <code>true</code> to enable multiple accordion items to be open at once.
+     */
+    multi: false,
+
+    constructor: function() {
+        var me = this;
+
+        me.callParent(arguments);
+
+        // animate flag must be false during initial render phase so we don't get animations.
+        me.initialAnimate = me.animate;
+        me.animate = false;
+
+        // Child Panels are not absolutely positioned if we are not filling, so use a different itemCls.
+        if (me.fill === false) {
+            me.itemCls = Ext.baseCSSPrefix + 'accordion-item';
+        }
+    },
+
+    // Cannot lay out a fitting accordion before we have been allocated a height.
+    // So during render phase, layout will not be performed.
+    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);
+
+                // Set up initial properties for Panels in an accordion.
+                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);
+
+                // Set initial sizes
+                comp.width = targetSize.width;
+                if (me.fill) {
+                    delete comp.height;
+                    delete comp.flex;
+
+                    // If there is an expanded item, all others must be rendered collapsed.
+                    if (me.expandedItem !== undefined) {
+                        comp.collapsed = true;
+                    }
+                    // Otherwise expand the first item with collapsed explicitly configured as false
+                    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 no collapsed:false Panels found, make the first one expanded.
+        if (ln && me.expandedItem === undefined) {
+            me.expandedItem = 0;
+            comp = items[0];
+            comp.collapsed = false;
+            if (me.fill) {
+                comp.flex = 1;
+            }
+        }
+        
+        // Render all Panels.
+        me.callParent(arguments);
+                
+        // Postprocess rendered Panels.
+        ln = renderedPanels.length;
+        for (i = 0; i < ln; i++) {
+            comp = renderedPanels[i];
+
+            // Delete the dimension property so that our align: 'stretch' processing manages the width from here
+            delete comp.width;
+
+            comp.header.addCls(Ext.baseCSSPrefix + 'accordion-hd');
+            comp.body.addCls(Ext.baseCSSPrefix + 'accordion-body');
+            
+            // If we are fitting, then intercept expand/collapse requests. 
+            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;
+        }
+    },
+
+    // When a Component expands, adjust the heights of the other Components to be just enough to accommodate
+    // their headers.
+    // The expanded Component receives the only flex value, and so gets all remaining space.
+    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 we are allowing multi, and the "toCollapse" component is NOT the only expanded Component,
+        // then ask the box layout to collapse it to its header.
+        if (me.multi) {
+            me.setCollapsed(comp);
+
+            // If the collapsing Panel is the only expanded one, expand the following Component.
+            // All this is handling fill: true, so there must be at least one expanded,
+            if (expanded.length === 1 && expanded[0] === comp) {
+                me.setExpanded(toExpand);
+            }
+            
+            me.animate = me.initialAnimate;
+            me.layout();
+            me.animate = false;
+        }
+        // Not allowing multi: expand the next sibling if possible, prev sibling if we collapsed the last
+        else if (toExpand) {
+            me.onComponentExpand(toExpand);
+        }
+        return false;
+    },
+
+    onComponentShow: function(comp) {
+        // Showing a Component means that you want to see it, so expand it.
+        this.onComponentExpand(comp);
+    },
+
+    setCollapsed: function(comp) {
+        var otherDocks = comp.getDockedItems(),
+            dockItem,
+            len = otherDocks.length,
+            i = 0;
+
+        // Hide all docked items except the header
+        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;
+
+        // Show temporarily hidden docked items
+        for (; i < len; i++) {
+            otherDocks[i].hidden = false;
+        }
+
+        // If it was an initial native collapse which hides the body
+        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);
+    }
+});
+/**
+ * @class Ext.resizer.Splitter
+ * @extends Ext.Component
+ * <p>This class functions <b>between siblings of a {@link Ext.layout.container.VBox VBox} or {@link Ext.layout.container.HBox HBox}
+ * layout</b> to resize both immediate siblings.</p>
+ * <p>By default it will set the size of both siblings. <b>One</b> of the siblings may be configured with
+ * <code>{@link Ext.Component#maintainFlex maintainFlex}: true</code> which will cause it not to receive a new size explicitly, but to be resized
+ * by the layout.</p>
+ * <p>A Splitter may be configured to show a centered mini-collapse tool orientated to collapse the {@link #collapseTarget}.
+ * The Splitter will then call that sibling Panel's {@link Ext.panel.Panel#collapse collapse} or {@link Ext.panel.Panel#expand expand} method
+ * to perform the appropriate operation (depending on the sibling collapse state). To create the mini-collapse tool but take care
+ * of collapsing yourself, configure the splitter with <code>{@link #performCollapse} false</code>.</p>
+ *
+ * @xtype splitter
+ */
+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',
+
+    /**
+     * @cfg {Boolean} collapsible
+     * <code>true</code> to show a mini-collapse tool in the Splitter to toggle expand and collapse on the {@link #collapseTarget} Panel.
+     * Defaults to the {@link Ext.panel.Panel#collapsible collapsible} setting of the Panel.
+     */
+    collapsible: false,
+
+    /**
+     * @cfg {Boolean} performCollapse
+     * <p>Set to <code>false</code> to prevent this Splitter's mini-collapse tool from managing the collapse
+     * state of the {@link #collapseTarget}.</p>
+     */
+
+    /**
+     * @cfg {Boolean} collapseOnDblClick
+     * <code>true</code> to enable dblclick to toggle expand and collapse on the {@link #collapseTarget} Panel.
+     */
+    collapseOnDblClick: true,
+
+    /**
+     * @cfg {Number} defaultSplitMin
+     * Provides a default minimum width or height for the two components
+     * that the splitter is between.
+     */
+    defaultSplitMin: 40,
+
+    /**
+     * @cfg {Number} defaultSplitMax
+     * Provides a default maximum width or height for the two components
+     * that the splitter is between.
+     */
+    defaultSplitMax: 1000,
+
+    width: 5,
+    height: 5,
+
+    /**
+     * @cfg {Mixed} collapseTarget
+     * <p>A string describing the relative position of the immediate sibling Panel to collapse. May be 'prev' or 'next' (Defaults to 'next')</p>
+     * <p>Or the immediate sibling Panel to collapse.</p>
+     * <p>The orientation of the mini-collapse tool will be inferred from this setting.</p>
+     * <p><b>Note that only Panels may be collapsed.</b></p>
+     */
+    collapseTarget: 'next',
+
+    /**
+     * @property orientation
+     * @type String
+     * Orientation of this Splitter. <code>'vertical'</code> when used in an hbox layout, <code>'horizontal'</code>
+     * when used in a vbox layout.
+     */
+
+    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);
+
+        // Add listeners on the mini-collapse tool unless performCollapse is set to false
+        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);
+            }
+        }
+
+        // Ensure the mini collapse icon is set to the correct direction when the target is collapsed/expanded by any means
+        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
+        });
+    },
+
+    getCollapseDirection: function() {
+        var me = this,
+            idx,
+            type = me.ownerCt.layout.type;
+
+        // Avoid duplication of string tests.
+        // Create a two bit truth table of the configuration of the Splitter:
+        // Collapse Target | orientation
+        //        0              0             = next, horizontal
+        //        0              1             = next, vertical
+        //        1              0             = prev, horizontal
+        //        1              1             = prev, vertical
+        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');
+        }
+
+        // Read the data out the truth table
+        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()) {
+            // restore
+            if (cmp.collapsed) {
+                cmp.expand(cmp.animCollapse);
+            // collapse
+            } else {
+                cmp.collapse(this.renderData.collapseDir, cmp.animCollapse);
+            }
+        }
+    },
+
+    /*
+     * Work around IE bug. %age margins do not get recalculated on element resize unless repaint called.
+     */
+    setSize: function() {
+        var me = this;
+        me.callParent(arguments);
+        if (Ext.isIE) {
+            me.el.repaint();
+        }
+    }
+});
+
+/**
+ * @class Ext.layout.container.Border
+ * @extends Ext.layout.container.Container
+ * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
+ * nested panels, automatic bars between regions and built-in
+ * {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions.</p>
+ * <p>This class is intended to be extended or created via the <code>layout:'border'</code>
+ * {@link Ext.container.Container#layout} config, and should generally not need to be created directly
+ * via the new keyword.</p>
+ * {@img Ext.layout.container.Border/Ext.layout.container.Border.png Ext.layout.container.Border container layout}
+ * <p>Example usage:</p>
+ * <pre><code>
+     Ext.create('Ext.panel.Panel', {
+        width: 500,
+        height: 400,
+        title: 'Border Layout',
+        layout: 'border',
+        items: [{
+            title: 'South Region is resizable',
+            region: 'south',     // position for region
+            xtype: 'panel',
+            height: 100,
+            split: true,         // enable resizing
+            margins: '0 5 5 5'
+        },{
+            // xtype: 'panel' implied by default
+            title: 'West Region is collapsible',
+            region:'west',
+            xtype: 'panel',
+            margins: '5 0 0 5',
+            width: 200,
+            collapsible: true,   // make collapsible
+            id: 'west-region-container',
+            layout: 'fit'
+        },{
+            title: 'Center Region',
+            region: 'center',     // center region is required, no width/height specified
+            xtype: 'panel',
+            layout: 'fit',
+            margins: '5 5 0 0'
+        }],
+        renderTo: Ext.getBody()
+    });
+</code></pre>
+ * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
+ * <li>Any Container using the Border layout <b>must</b> have a child item with <code>region:'center'</code>.
+ * The child item in the center region will always be resized to fill the remaining space not used by
+ * the other regions in the layout.</li>
+ * <li>Any child items with a region of <code>west</code> or <code>east</code> may be configured with either
+ * an initial <code>width</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
+ * <li>Any child items with a region of <code>north</code> or <code>south</code> may be configured with either
+ * an initial <code>height</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
+ * <li>The regions of a BorderLayout are <b>fixed at render time</b> and thereafter, its child Components may not be removed or added</b>.To add/remove
+ * Components within a BorderLayout, have them wrapped by an additional Container which is directly
+ * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
+ * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.panel.Panel)
+ * is added to the west region:<pre><code>
+wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
+wrc.{@link Ext.container.Container#removeAll removeAll}();
+wrc.{@link Ext.container.Container#add add}({
+    title: 'Added Panel',
+    html: 'Some content'
+});
+ * </code></pre>
+ * </li>
+ * <li><b>There is no BorderLayout.Region class in ExtJS 4.0+</b></li>
+ * </ul></div>
+ */
+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();
+        }
+
+        // Delegate this operation to the shadow "V" or "H" box layout, and then down to any embedded layout.
+        me.shadowLayout.onLayout();
+        if (me.embeddedContainer) {
+            me.embeddedContainer.layout.onLayout();
+        }
+
+        // If the panel was originally configured with collapsed: true, it will have
+        // been initialized with a "borderCollapse" flag: Collapse it now before the first layout.
+        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();
+        }
+
+        // Delegate this operation to the shadow "V" or "H" box layout.
+        return this.shadowLayout.isValidParent(item, target, position);
+    },
+
+    beforeLayout: function() {
+        if (!this.borderLayoutInitialized) {
+            this.initializeBorderLayout();
+        }
+
+        // Delegate this operation to the shadow "V" or "H" box layout.
+        this.shadowLayout.beforeLayout();
+    },
+
+    renderItems: function(items, target) {
+        //<debug>
+        Ext.Error.raise('This should not be called');
+        //</debug>
+    },
+
+    renderItem: function(item) {
+        //<debug>
+        Ext.Error.raise('This should not be called');
+        //</debug>
+    },
+
+    initializeBorderLayout: function() {
+        var me = this,
+            i = 0,
+            items = me.getLayoutItems(),
+            ln = items.length,
+            regions = (me.regions = {}),
+            vBoxItems = [],
+            hBoxItems = [],
+            horizontalFlex = 0,
+            verticalFlex = 0,
+            comp, percentage;
+
+        // Map of Splitters for each region
+        me.splitters = {};
+
+        // Map of regions
+        for (; i < ln; i++) {
+            comp = items[i];
+            regions[comp.region] = comp;
+
+            // Intercept collapsing to implement showing an alternate Component as a collapsed placeholder
+            if (comp.region != 'center' && comp.collapsible && comp.collapseMode != 'header') {
+
+                // This layout intercepts any initial collapsed state. Panel must not do this itself.
+                comp.borderCollapse = comp.collapsed;
+                delete comp.collapsed;
+
+                comp.on({
+                    beforecollapse: me.onBeforeRegionCollapse,
+                    beforeexpand: me.onBeforeRegionExpand,
+                    destroy: me.onRegionDestroy,
+                    scope: me
+                });
+                me.setupState(comp);
+            }
+        }
+        //<debug>
+        if (!regions.center) {
+            Ext.Error.raise("You must specify a center region when defining a BorderLayout.");
+        }
+        //</debug>
+        comp = regions.center;
+        if (!comp.flex) {
+            comp.flex = 1;
+        }
+        delete comp.width;
+        comp.maintainFlex = true;
+
+        // Begin the VBox and HBox item list.
+        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;
+            }
+        }
+
+        // Decide into which Collection the center region goes.
+        if (regions.north || regions.south) {
+            if (regions.east || regions.west) {
+
+                // Create the embedded center. Mark it with the region: 'center' property so that it can be identified as the center.
+                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);
+            }
+            // No east or west: the original center goes straight into the vbox
+            else {
+                vBoxItems.push(regions.center);
+            }
+        }
+        // If we have no north or south, then the center is part of the HBox items
+        else {
+            hBoxItems.push(regions.center);
+        }
+
+        // Finish off the VBox and HBox item list.
+        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);
+        }
+
+        // Create the injected "items" collections for the Containers.
+        // If we have north or south, then the shadow Container will be a VBox.
+        // If there are also east or west regions, its center will be a shadow HBox.
+        // If there are *only* east or west regions, then the shadow layout will be an HBox (or Fit).
+        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);
+
+            // Allow the Splitters to orientate themselves
+            if (me.splitters.north) {
+                me.splitters.north.ownerCt = me.shadowContainer;
+            }
+            if (me.splitters.south) {
+                me.splitters.south.ownerCt = me.shadowContainer;
+            }
+
+            // Inject items into the HBox Container if there is one - if there was an east or west.
+            if (me.embeddedContainer) {
+                me.embeddedContainer.ownerCt = me.shadowContainer;
+                me.createItems(me.embeddedContainer, hBoxItems);
+
+                // Allow the Splitters to orientate themselves
+                if (me.splitters.east) {
+                    me.splitters.east.ownerCt = me.embeddedContainer;
+                }
+                if (me.splitters.west) {
+                    me.splitters.west.ownerCt = me.embeddedContainer;
+                }
+
+                // The east or west region wanted a percentage
+                if (horizontalFlex) {
+                    regions.center.flex -= horizontalFlex;
+                }
+                // The north or south region wanted a percentage
+                if (verticalFlex) {
+                    me.embeddedContainer.flex -= verticalFlex;
+                }
+            } else {
+                // The north or south region wanted a percentage
+                if (verticalFlex) {
+                    regions.center.flex -= verticalFlex;
+                }
+            }
+        }
+        // If we have no north or south, then there's only one Container, and it's
+        // an HBox, or, if only a center region was specified, a Fit.
+        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);
+
+            // Allow the Splitters to orientate themselves
+            if (me.splitters.east) {
+                me.splitters.east.ownerCt = me.shadowContainer;
+            }
+            if (me.splitters.west) {
+                me.splitters.west.ownerCt = me.shadowContainer;
+            }
+
+            // The east or west region wanted a percentage
+            if (horizontalFlex) {
+                regions.center.flex -= verticalFlex;
+            }
+        }
+
+        // Create upward links from the region Components to their shadow ownerCts
+        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;
+            }
+        }
+
+        // This is the layout that we delegate all operations to
+        me.shadowLayout = me.shadowContainer.getLayout();
+
+        me.borderLayoutInitialized = true;
+    },
+
+
+    setupState: function(comp){
+        var getState = comp.getState;
+        comp.getState = function(){
+            // call the original getState
+            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']);
+    },
+
+    /**
+     * Create the items collection for our shadow/embedded containers
+     * @private
+     */
+    createItems: function(container, items){
+        // Have to inject an items Collection *after* construction.
+        // The child items of the shadow layout must retain their original, user-defined ownerCt
+        delete container.items;
+        container.initItems();
+        container.items.addAll(items);
+    },
+
+    // Private
+    // Create a splitter for a child of the layout.
+    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
+        });
+
+        // Mini collapse means that the splitter is the placeholder Component
+        if (comp.collapseMode == 'mini') {
+            comp.placeholder = resizer;
+        }
+
+        // Arrange to hide/show a region's associated splitter when the region is hidden/shown
+        comp.on({
+            hide: me.onRegionVisibilityChange,
+            show: me.onRegionVisibilityChange,
+            scope: me
+        });
+        return resizer;
+    },
+
+    // Hide/show a region's associated splitter when the region is hidden/shown
+    onRegionVisibilityChange: function(comp){
+        this.splitters[comp.region][comp.hidden ? 'hide' : 'show']();
+        this.layout();
+    },
+
+    // Called when a splitter mini-collapse tool is clicked on.
+    // The listener is only added if this layout is controlling collapsing,
+    // not if the component's collapseMode is 'mini' or 'header'.
+    onSplitterCollapseClick: function(comp) {
+        if (comp.collapsed) {
+            this.onPlaceHolderToolClick(null, null, null, {client: comp});
+        } else {
+            comp.collapse();
+        }
+    },
+
+    /**
+     * <p>Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the layout will collapse.
+     * By default, this will be a {@link Ext.panel.Header Header} component (Docked to the appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}.
+     * config to customize this.</p>
+     * <p><b>Note that this will be a fully instantiated Component, but will only be <i>rendered</i> when the Panel is first collapsed.</b></p>
+     * @param {Panel} panel The child Panel of the layout for which to return the {@link Ext.panel.Panel#placeholder placeholder}.
+     * @returns {Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link Ext.panel.Panel#collapseMode collapseMode} is
+     * <code>'header'</code>, in which case <i>undefined</i> is returned.
+     */
+    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');
+
+        // No placeholder if the collapse mode is not the Border layout default
+        if (comp.collapseMode == 'header') {
+            return;
+        }
+
+        // Provide a replacement Container with an expand tool
+        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
+                };
+                // Hack for IE6/7/IEQuirks's inability to display an inline-block
+                if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) && !horiz) {
+                    placeholder.width = 25;
+                }
+                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
+                });
+            }
+        }
+
+        // The collapsed Component holds a reference to its placeholder and vice versa
+        comp.placeholder = placeholder;
+        placeholder.comp = comp;
+
+        return placeholder;
+    },
+
+    /**
+     * @private
+     * Update the placeholder title when panel title has been set or changed.
+     */
+    onRegionTitleChange: function(comp, newTitle) {
+        comp.placeholder.setTitle(newTitle);
+    },
+
+    /**
+     * @private
+     * Update the placeholder iconCls when panel iconCls has been set or changed.
+     */
+    onRegionIconChange: function(comp, newIconCls) {
+        comp.placeholder.setIconCls(newIconCls);
+    },
+
+    /**
+     * @private
+     * Calculates the size and positioning of the passed child item. Must be present because Panel's expand,
+     * when configured with a flex, calls this method on its ownerCt's layout.
+     * @param {Component} child The child Component to calculate the box for
+     * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
+     */
+    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);
+        }
+    },
+
+    /**
+     * @private
+     * Intercepts the Panel's own collapse event and perform's substitution of the Panel
+     * with a placeholder Header orientated in the appropriate dimension.
+     * @param comp The Panel being collapsed.
+     * @param direction
+     * @param animate
+     * @returns {Boolean} false to inhibit the Panel from performing its own collapse.
+     */
+    onBeforeRegionCollapse: function(comp, direction, animate) {
+        var me = this,
+            compEl = comp.el,
+            miniCollapse = comp.collapseMode == 'mini',
+            shadowContainer = comp.shadowOwnerCt,
+            shadowLayout = shadowContainer.layout,
+            placeholder = comp.placeholder,
+            placeholderBox,
+            targetSize = shadowLayout.getLayoutTargetSize(),
+            sl = me.owner.suspendLayout,
+            scsl = shadowContainer.suspendLayout,
+            isNorthOrWest = (comp.region == 'north' || comp.region == 'west'); // Flag to keep the placeholder non-adjacent to any Splitter
+
+        // Do not trigger a layout during transition to collapsed Component
+        me.owner.suspendLayout = true;
+        shadowContainer.suspendLayout = true;
+
+        // Prevent upward notifications from downstream layouts
+        shadowLayout.layoutBusy = true;
+        if (shadowContainer.componentLayout) {
+            shadowContainer.componentLayout.layoutBusy = true;
+        }
+        me.shadowContainer.layout.layoutBusy = true;
+        me.layoutBusy = true;
+        me.owner.componentLayout.layoutBusy = true;
+
+        // Provide a replacement Container with an expand tool
+        if (!placeholder) {
+            placeholder = me.getPlaceholder(comp);
+        }
+
+        // placeholder already in place; show it.
+        if (placeholder.shadowOwnerCt === shadowContainer) {
+            placeholder.show();
+        }
+        // Insert the collapsed placeholder Component into the appropriate Box layout shadow Container
+        // It must go next to its client Component, but non-adjacent to the splitter so splitter can find its collapse client.
+        // Inject an ownerCt value pointing to the owner, border layout Container as the user will expect.
+        else {
+            shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
+            placeholder.shadowOwnerCt = shadowContainer;
+            placeholder.ownerCt = me.owner;
+        }
+
+        // Flag the collapsing Component as hidden and show the placeholder.
+        // This causes the shadow Box layout's calculateChildBoxes to calculate the correct new arrangement.
+        // We hide or slideOut the Component's element
+        comp.hidden = true;
+
+        if (!placeholder.rendered) {
+            shadowLayout.renderItem(placeholder, shadowLayout.innerCt);
+        }
+
+        // Jobs to be done after the collapse has been done
+        function afterCollapse() {
+
+            // Reinstate automatic laying out.
+            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;
+
+            // Fire the collapse event: The Panel has in fact been collapsed, but by substitution of an alternative Component
+            comp.collapsed = true;
+            comp.fireEvent('collapse', comp);
+        }
+
+        /*
+         * Set everything to the new positions. Note that we
+         * only want to animate the collapse if it wasn't configured
+         * initially with collapsed: true
+         */
+        if (comp.animCollapse && me.initialCollapsedComplete) {
+            shadowLayout.layout();
+            compEl.dom.style.zIndex = 100;
+
+            // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
+            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 we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
+                       if (!miniCollapse) {
+                            placeholder.el.slideIn(me.slideDirection[comp.region], {
+                                easing: 'linear',
+                                duration: 100
+                            });
+                        }
+                        afterCollapse();
+                    }
+                }
+            });
+        } else {
+            compEl.setLeftTop(-10000, -10000);
+            shadowLayout.layout();
+            afterCollapse();
+
+            // Horrible workaround for https://sencha.jira.com/browse/EXTJSIV-1562
+            if (Ext.isIE) {
+                placeholder.setCalculatedSize(placeholder.el.getWidth());
+            }
+        }
+
+        return false;
+    },
+
+    // Hijack the expand operation to remove the placeholder and slide the region back in.
+    onBeforeRegionExpand: function(comp, animate) {
+        this.onPlaceHolderToolClick(null, null, null, {client: comp});
+        return false;
+    },
+
+    // Called when the collapsed placeholder is clicked to reinstate a "collapsed" (in reality hidden) Panel.
+    onPlaceHolderToolClick: function(e, target, owner, tool) {
+        var me = this,
+            comp = tool.client,
+
+            // Hide the placeholder unless it was the Component's preexisting splitter
+            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 the slide in is still going, stop it.
+        // This will either leave the Component in its fully floated state (which is processed below)
+        // or in its collapsed state. Either way, we expand it..
+        if (comp.getActiveAnimation()) {
+            comp.stopAnimation();
+        }
+
+        // If the Component is fully floated when they click the placeholder Tool,
+        // it will be primed with a slide out animation object... so delete that
+        // and remove the mouseout listeners
+        if (comp.slideOutAnim) {
+            // Remove mouse leave monitors
+            compEl.un(comp.panelMouseMon);
+            placeholderEl.un(comp.placeholderMouseMon);
+
+            delete comp.slideOutAnim;
+            delete comp.panelMouseMon;
+            delete comp.placeholderMouseMon;
+
+            // If the Panel was floated and primed with a slideOut animation, we don't want to animate its layout operation.
+            isFloating = true;
+        }
+
+        // Do not trigger a layout during transition to expanded Component
+        me.owner.suspendLayout = true;
+        shadowContainer.suspendLayout = true;
+
+        // Prevent upward notifications from downstream layouts
+        shadowLayout.layoutBusy = true;
+        if (shadowContainer.componentLayout) {
+            shadowContainer.componentLayout.layoutBusy = true;
+        }
+        me.shadowContainer.layout.layoutBusy = true;
+        me.layoutBusy = true;
+        me.owner.componentLayout.layoutBusy = true;
+
+        // Unset the hidden and collapsed flags set in onBeforeRegionCollapse. The shadowLayout will now take it into account
+        // Find where the shadow Box layout plans to put the expanding Component.
+        comp.hidden = false;
+        comp.collapsed = false;
+        if (hidePlaceholder) {
+            placeholder.hidden = true;
+        }
+        toCompBox = shadowLayout.calculateChildBox(comp);
+
+        // Show the collapse tool in case it was hidden by the slide-in
+        if (comp.collapseTool) {
+            comp.collapseTool.show();
+        }
+
+        // If we're going to animate, we need to hide the component before moving it back into position
+        if (comp.animCollapse && !isFloating) {
+            compEl.setStyle('visibility', 'hidden');
+        }
+        compEl.setLeftTop(toCompBox.left, toCompBox.top);
+
+        // Equalize the size of the expanding Component prior to animation
+        // in case the layout area has changed size during the time it was collapsed.
+        curSize = comp.getSize();
+        if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
+            me.setItemSize(comp, toCompBox.width, toCompBox.height);
+        }
+
+        // Jobs to be done after the expand has been done
+        function afterExpand() {
+            // Reinstate automatic laying out.
+            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;
+
+            // In case it was floated out and they clicked the re-expand tool
+            comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
+
+            // Fire the expand event: The Panel has in fact been expanded, but by removal of an alternative Component
+            comp.fireEvent('expand', comp);
+        }
+
+        // Hide the placeholder
+        if (hidePlaceholder) {
+            placeholder.el.hide();
+        }
+
+        // Slide the expanding Component to its new position.
+        // When that is done, layout the layout.
+        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;
+
+        // Ignore clicks on tools.
+        if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
+            return;
+        }
+
+        // It's *being* animated, ignore the click.
+        // Possible future enhancement: Stop and *reverse* the current active Fx.
+        if (compEl.getActiveAnimation()) {
+            return;
+        }
+
+        // If the Component is already fully floated when they click the placeholder,
+        // it will be primed with a slide out animation object... so slide it out.
+        if (comp.slideOutAnim) {
+            me.slideOutFloatedComponent(comp);
+            return;
+        }
+
+        // Function to be called when the mouse leaves the floated Panel
+        // Slide out when the mouse leaves the region bounded by the slid Component and its placeholder.
+        function onMouseLeaveFloated(e) {
+            var slideRegion = compEl.getRegion().union(placeholderEl.getRegion()).adjust(1, -1, -1, 1);
+
+            // If mouse is not within slide Region, slide it out
+            if (!slideRegion.contains(e.getPoint())) {
+                me.slideOutFloatedComponent(comp);
+            }
+        }
+
+        // Monitor for mouseouting of the placeholder. Hide it if they exit for half a second or more
+        comp.placeholderMouseMon = placeholderEl.monitorMouseLeave(500, onMouseLeaveFloated);
+
+        // Do not trigger a layout during slide out of the Component
+        shadowContainer.suspendLayout = true;
+
+        // Prevent upward notifications from downstream layouts
+        me.layoutBusy = true;
+        me.owner.componentLayout.layoutBusy = true;
+
+        // The collapse tool is hidden while slid.
+        // It is re-shown on expand.
+        if (comp.collapseTool) {
+            comp.collapseTool.hide();
+        }
+
+        // Set flags so that the layout will calculate the boxes for what we want
+        comp.hidden = false;
+        comp.collapsed = false;
+        placeholder.hidden = true;
+
+        // Recalculate new arrangement of the Component being floated.
+        toCompBox = shadowLayout.calculateChildBox(comp);
+        placeholder.hidden = false;
+
+        // Component to appear just after the placeholder, whatever "after" means in the context of the shadow Box layout.
+        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);
+
+        // Equalize the size of the expanding Component prior to animation
+        // in case the layout area has changed size during the time it was collapsed.
+        curSize = comp.getSize();
+        if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
+            me.setItemSize(comp, toCompBox.width, toCompBox.height);
+        }
+
+        // This animation slides the collapsed Component's el out to just beyond its placeholder
+        compAnim = {
+            listeners: {
+                afteranimate: function() {
+                    shadowContainer.suspendLayout = scsl;
+                    delete me.layoutBusy;
+                    delete me.owner.componentLayout.layoutBusy;
+
+                    // Prime the Component with an Anim config object to slide it back out
+                    compAnim.listeners = {
+                        afterAnimate: function() {
+                            compEl.show().removeCls(Ext.baseCSSPrefix + 'border-region-slide-in').setLeftTop(-10000, -10000);
+
+                            // Reinstate the correct, current state after slide out animation finishes
+                            comp.hidden = true;
+                            comp.collapsed = true;
+                            delete comp.slideOutAnim;
+                            delete comp.panelMouseMon;
+                            delete comp.placeholderMouseMon;
+                        }
+                    };
+                    comp.slideOutAnim = compAnim;
+                }
+            },
+            duration: 500
+        };
+
+        // Give the element the correct class which places it at a high z-index
+        compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');
+
+        // Begin the slide in
+        compEl.slideIn(me.slideDirection[comp.region], compAnim);
+
+        // Monitor for mouseouting of the slid area. Hide it if they exit for half a second or more
+        comp.panelMouseMon = compEl.monitorMouseLeave(500, onMouseLeaveFloated);
+
+    },
+
+    slideOutFloatedComponent: function(comp) {
+        var compEl = comp.el,
+            slideOutAnim;
+
+        // Remove mouse leave monitors
+        compEl.un(comp.panelMouseMon);
+        comp.placeholder.el.un(comp.placeholderMouseMon);
+
+        // Slide the Component out
+        compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);
+
+        delete comp.slideOutAnim;
+        delete comp.panelMouseMon;
+        delete comp.placeholderMouseMon;
+    },
+
+    /*
+     * @private
+     * Ensure any collapsed placeholder Component is destroyed along with its region.
+     * Can't do this in onDestroy because they may remove a Component and use it elsewhere.
+     */
+    onRegionDestroy: function(comp) {
+        var placeholder = comp.placeholder;
+        if (placeholder) {
+            delete placeholder.ownerCt;
+            placeholder.destroy();
+        }
+    },
+
+    /*
+     * @private
+     * Ensure any shadow Containers are destroyed.
+     * Ensure we don't keep references to Components.
+     */
+    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);
+    }
+});
+
+/**
+ * @class Ext.layout.container.Card
+ * @extends Ext.layout.container.AbstractCard
+  * <p>This layout manages multiple child Components, each fitted to the Container, where only a single child Component can be
+  * visible at any given time.  This layout style is most commonly used for wizards, tab implementations, etc.
+  * This class is intended to be extended or created via the layout:'card' {@link Ext.container.Container#layout} config,
+  * and should generally not need to be created directly via the new keyword.</p>
+  * <p>The CardLayout's focal method is {@link #setActiveItem}.  Since only one panel is displayed at a time,
+  * the only way to move from one Component to the next is by calling setActiveItem, passing the id or index of
+  * the next panel to display.  The layout itself does not provide a user interface for handling this navigation,
+  * so that functionality must be provided by the developer.</p>
+  * <p>In the following example, a simplistic wizard setup is demonstrated.  A button bar is added
+  * to the footer of the containing panel to provide navigation buttons.  The buttons will be handled by a
+  * common navigation routine -- for this example, the implementation of that routine has been ommitted since
+  * it can be any type of custom logic.  Note that other uses of a CardLayout (like a tab control) would require a
+  * completely different implementation.  For serious implementations, a better approach would be to extend
+  * CardLayout to provide the custom functionality needed.  
+  * {@img Ext.layout.container.Card/Ext.layout.container.Card.png Ext.layout.container.Card container layout}
+  * Example usage:</p>
+  * <pre><code>
+    var navHandler = function(direction){
+         // This routine could contain business logic required to manage the navigation steps.
+         // It would call setActiveItem as needed, manage navigation button state, handle any
+         // branching logic that might be required, handle alternate actions like cancellation
+         // or finalization, etc.  A complete wizard implementation could get pretty
+         // sophisticated depending on the complexity required, and should probably be
+         // done as a subclass of CardLayout in a real-world implementation.
+     };
+
+    Ext.create('Ext.panel.Panel', {
+        title: 'Example Wizard',
+        width: 300,
+        height: 200,
+        layout: 'card',
+        activeItem: 0, // make sure the active item is set on the container config!
+        bodyStyle: 'padding:15px',
+        defaults: {
+            // applied to each contained panel
+            border:false
+        },
+        // just an example of one possible navigation scheme, using buttons
+        bbar: [
+        {
+            id: 'move-prev',
+            text: 'Back',
+            handler: navHandler(this, [-1]),
+            disabled: true
+        },
+        '->', // greedy spacer so that the buttons are aligned to each side
+        {
+            id: 'move-next',
+            text: 'Next',
+            handler: navHandler(this, [1])
+        }],
+        // the panels (or "cards") within the layout
+        items: [{
+            id: 'card-0',
+            html: '<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>'
+        },{
+            id: 'card-1',
+            html: '<p>Step 2 of 3</p>'
+        },{
+            id: 'card-2',
+            html: '<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>'
+        }],
+        renderTo: Ext.getBody()
+    });  
+ </code></pre>
+  */
+Ext.define('Ext.layout.container.Card', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.card'],
+    alternateClassName: 'Ext.layout.CardLayout',
+
+    extend: 'Ext.layout.container.AbstractCard',
+
+    /* End Definitions */
+
+    setActiveItem: function(newCard) {
+        var me = this,
+            owner = me.owner,
+            oldCard = me.activeItem,
+            newIndex;
+
+        // Block upward layouts until we are done.
+        me.layoutBusy = true;
+
+        newCard = me.parseActiveItem(newCard);
+        newIndex = owner.items.indexOf(newCard);
+
+        // If the card is not a child of the owner, then add it
+        if (newIndex == -1) {
+            newIndex = owner.items.items.length;
+            owner.add(newCard);
+        }
+
+        // Is this a valid, different card?
+        if (newCard && oldCard != newCard) {
+            // If the card has not been rendered yet, now is the time to do so.
+            if (!newCard.rendered) {
+                me.renderItem(newCard, me.getRenderTarget(), owner.items.length);
+                me.configureItem(newCard, 0);
+            }
+
+            me.activeItem = newCard;
+
+            // Fire the beforeactivate and beforedeactivate events on the cards
+            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 the card hasnt been sized yet, do it now
+            if (!me.sizeAllCards) {
+                me.setItemBox(newCard, me.getTargetBox());
+            }
+            else {
+                // onLayout calls setItemBox
+                me.onLayout();
+            }
+
+            if (oldCard) {
+                if (me.hideInactive) {
+                    oldCard.hide();
+                }
+                oldCard.fireEvent('deactivate', oldCard, newCard);
+            }
+
+            // Make sure the new card is shown
+            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;
+    }
+});
+/**
+ * @class Ext.layout.container.Column
+ * @extends Ext.layout.container.Auto
+ * <p>This is the layout style of choice for creating structural layouts in a multi-column format where the width of
+ * each column can be specified as a percentage or fixed width, but the height is allowed to vary based on the content.
+ * This class is intended to be extended or created via the layout:'column' {@link Ext.container.Container#layout} config,
+ * and should generally not need to be created directly via the new keyword.</p>
+ * <p>ColumnLayout does not have any direct config options (other than inherited ones), but it does support a
+ * specific config property of <b><tt>columnWidth</tt></b> that can be included in the config of any panel added to it.  The
+ * layout will use the columnWidth (if present) or width of each panel during layout to determine how to size each panel.
+ * If width or columnWidth is not specified for a given panel, its width will default to the panel's width (or auto).</p>
+ * <p>The width property is always evaluated as pixels, and must be a number greater than or equal to 1.
+ * The columnWidth property is always evaluated as a percentage, and must be a decimal value greater than 0 and
+ * less than 1 (e.g., .25).</p>
+ * <p>The basic rules for specifying column widths are pretty simple.  The logic makes two passes through the
+ * set of contained panels.  During the first layout pass, all panels that either have a fixed width or none
+ * specified (auto) are skipped, but their widths are subtracted from the overall container width.  During the second
+ * pass, all panels with columnWidths are assigned pixel widths in proportion to their percentages based on
+ * the total <b>remaining</b> container width.  In other words, percentage width panels are designed to fill the space
+ * left over by all the fixed-width and/or auto-width panels.  Because of this, while you can specify any number of columns
+ * with different percentages, the columnWidths must always add up to 1 (or 100%) when added together, otherwise your
+ * layout may not render as expected.  
+ * {@img Ext.layout.container.Column/Ext.layout.container.Column1.png Ext.layout.container.Column container layout}
+ * Example usage:</p>
+ * <pre><code>
+    // All columns are percentages -- they must add up to 1
+    Ext.create('Ext.panel.Panel', {
+        title: 'Column Layout - Percentage Only',
+        width: 350,
+        height: 250,
+        layout:'column',
+        items: [{
+            title: 'Column 1',
+            columnWidth: .25
+        },{
+            title: 'Column 2',
+            columnWidth: .55
+        },{
+            title: 'Column 3',
+            columnWidth: .20
+        }],
+        renderTo: Ext.getBody()
+    }); 
+
+// {@img Ext.layout.container.Column/Ext.layout.container.Column2.png Ext.layout.container.Column container layout}
+// Mix of width and columnWidth -- all columnWidth values must add up
+// to 1. The first column will take up exactly 120px, and the last two
+// columns will fill the remaining container width.
+
+    Ext.create('Ext.Panel', {
+        title: 'Column Layout - Mixed',
+        width: 350,
+        height: 250,
+        layout:'column',
+        items: [{
+            title: 'Column 1',
+            width: 120
+        },{
+            title: 'Column 2',
+            columnWidth: .7
+        },{
+            title: 'Column 3',
+            columnWidth: .3
+        }],
+        renderTo: Ext.getBody()
+    }); 
+</code></pre>
+ */
+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) {
+
+            // the innerCt prevents wrapping and shuffling while
+            // the container is resizing
+            this.innerCt = this.getTarget().createChild({
+                cls: Ext.baseCSSPrefix + 'column-inner'
+            });
+
+            // Column layout uses natural HTML flow to arrange the child items.
+            // To ensure that all browsers (I'm looking at you IE!) add the bottom margin of the last child to the
+            // containing element height, we create a zero-sized element with style clear:both to force a "new line"
+            this.clearEl = this.innerCt.createChild({
+                cls: Ext.baseCSSPrefix + 'clear',
+                role: 'presentation'
+            });
+        }
+        return this.innerCt;
+    },
+
+    // private
+    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) { // Don't lay out in impossibly small target (probably display:none, or initial, unsized Container)
+            return;
+        }
+
+        // On the first pass, for all except IE6-7, we lay out the items with no scrollbars visible using style overflow: hidden.
+        // If, after the layout, it is detected that there is vertical overflow,
+        // we will recurse back through here. Do not adjust overflow style at that time.
+        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);
+
+        // some columns can be percentages while others are fixed
+        // so we need to make 2 passes
+        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);
+                }
+            }
+        }
+
+        // After the first pass on an autoScroll layout, restore the overflow settings if it had been changed (only changed for non-IE6)
+        if (!me.adjustmentPass && me.autoScroll) {
+
+            // If there's a vertical overflow, relay with scrollbars
+            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 the layout caused height overflow, recurse back and recalculate (with overflow setting restored on non-IE6)
+            if (me.adjustmentPass) {
+                me.onLayout();
+            }
+        }
+        delete me.adjustmentPass;
+    }
+});
+/**
+ * @class Ext.layout.container.Table
+ * @extends Ext.layout.container.Auto
+ * <p>This layout allows you to easily render content into an HTML table.  The total number of columns can be
+ * specified, and rowspan and colspan can be used to create complex layouts within the table.
+ * This class is intended to be extended or created via the <code>layout: {type: 'table'}</code>
+ * {@link Ext.container.Container#layout} config, and should generally not need to be created directly via the new keyword.</p>
+ * <p>Note that when creating a layout via config, the layout-specific config properties must be passed in via
+ * the {@link Ext.container.Container#layout} object which will then be applied internally to the layout.  In the
+ * case of TableLayout, the only valid layout config properties are {@link #columns} and {@link #tableAttrs}.
+ * However, the items added to a TableLayout can supply the following table-specific config properties:</p>
+ * <ul>
+ * <li><b>rowspan</b> Applied to the table cell containing the item.</li>
+ * <li><b>colspan</b> Applied to the table cell containing the item.</li>
+ * <li><b>cellId</b> An id applied to the table cell containing the item.</li>
+ * <li><b>cellCls</b> A CSS class name added to the table cell containing the item.</li>
+ * </ul>
+ * <p>The basic concept of building up a TableLayout is conceptually very similar to building up a standard
+ * HTML table.  You simply add each panel (or "cell") that you want to include along with any span attributes
+ * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.
+ * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the
+ * total column count in the layoutConfig and start adding panels in their natural order from left to right,
+ * top to bottom.  The layout will automatically figure out, based on the column count, rowspans and colspans,
+ * how to position each panel within the table.  Just like with HTML tables, your rowspans and colspans must add
+ * up correctly in your overall layout or you'll end up with missing and/or extra cells!  Example usage:</p>
+ * {@img Ext.layout.container.Table/Ext.layout.container.Table.png Ext.layout.container.Table container layout}
+ * <pre><code>
+// This code will generate a layout table that is 3 columns by 2 rows
+// with some spanning included.  The basic layout will be:
+// +--------+-----------------+
+// |   A    |   B             |
+// |        |--------+--------|
+// |        |   C    |   D    |
+// +--------+--------+--------+
+    Ext.create('Ext.panel.Panel', {
+        title: 'Table Layout',
+        width: 300,
+        height: 150,
+        layout: {
+            type: 'table',
+            // The total column count must be specified here
+            columns: 3
+        },
+        defaults: {
+            // applied to each contained panel
+            bodyStyle:'padding:20px'
+        },
+        items: [{
+            html: '<p>Cell A content</p>',
+            rowspan: 2
+        },{
+            html: '<p>Cell B content</p>',
+            colspan: 2
+        },{
+            html: '<p>Cell C content</p>',
+            cellCls: 'highlight'
+        },{
+            html: '<p>Cell D content</p>'
+        }],
+        renderTo: Ext.getBody()
+    });
+</code></pre>
+ */
+
+Ext.define('Ext.layout.container.Table', {
+
+    /* Begin Definitions */
+
+    alias: ['layout.table'],
+    extend: 'Ext.layout.container.Auto',
+    alternateClassName: 'Ext.layout.TableLayout',
+
+    /* End Definitions */
+
+    /**
+     * @cfg {Number} columns
+     * The total number of columns to create in the table for this layout.  If not specified, all Components added to
+     * this layout will be rendered into a single row using one column per Component.
+     */
+
+    // private
+    monitorResize:false,
+
+    type: 'table',
+
+    // Table layout is a self-sizing layout. When an item of for example, a dock layout, the Panel must expand to accommodate
+    // a table layout. See in particular AbstractDock::onLayout for use of this flag.
+    autoSize: true,
+
+    clearEl: true, // Base class will not create it if already truthy. Not needed in tables.
+
+    targetCls: Ext.baseCSSPrefix + 'table-layout-ct',
+    tableCls: Ext.baseCSSPrefix + 'table-layout',
+    cellCls: Ext.baseCSSPrefix + 'table-layout-cell',
+
+    /**
+     * @cfg {Object} tableAttrs
+     * <p>An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification
+     * used to create the layout's <tt>&lt;table&gt;</tt> element. Example:</p><pre><code>
+{
+    xtype: 'panel',
+    layout: {
+        type: 'table',
+        columns: 3,
+        tableAttrs: {
+            style: {
+                width: '100%'
+            }
+        }
+    }
+}</code></pre>
+     */
+    tableAttrs:null,
+
+    /**
+     * @private
+     * Iterates over all passed items, ensuring they are rendered in a cell in the proper
+     * location in the table structure.
+     */
+    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;
+
+        // Calculate the correct cell structure for the current items
+        cells = this.calculateCells(items);
+
+        // Loop over each cell and compare to the current cells in the table, inserting/
+        // removing/moving cells as needed, and making sure each item is rendered into
+        // the correct cell.
+        for (; i < len; i++) {
+            curCell = cells[i];
+            rowIdx = curCell.rowIdx;
+            cellIdx = curCell.cellIdx;
+            item = items[i];
+
+            // If no row present, create and insert one
+            trEl = rows[rowIdx];
+            if (!trEl) {
+                trEl = tbody.insertRow(rowIdx);
+            }
+
+            // If no cell present, create and insert one
+            itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx));
+            if (this.needsDivWrap()) { //create wrapper div if needed - see docs below
+                itemCt = tdEl.first() || tdEl.createChild({tag: 'div'});
+                itemCt.setWidth(null);
+            }
+
+            // Render or move the component into the cell
+            if (!item.rendered) {
+                this.renderItem(item, itemCt, 0);
+            }
+            else if (!this.isValidParent(item, itemCt, 0)) {
+                this.moveItem(item, itemCt, 0);
+            }
+
+            // Set the cell properties
+            tdEl.set({
+                colSpan: item.colspan || 1,
+                rowSpan: item.rowspan || 1,
+                id: item.cellId || '',
+                cls: this.cellCls + ' ' + (item.cellCls || '')
+            });
+
+            // If at the end of a row, remove any extra cells
+            if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) {
+                cellIdx++;
+                while (trEl.cells[cellIdx]) {
+                    trEl.deleteCell(cellIdx);
+                }
+            }
+        }
+
+        // Delete any extra rows
+        rowIdx++;
+        while (tbody.rows[rowIdx]) {
+            tbody.deleteRow(rowIdx);
+        }
+    },
+
+    afterLayout: function() {
+        this.callParent();
+
+        if (this.needsDivWrap()) {
+            // set wrapper div width to match layed out item - see docs below
+            Ext.Array.forEach(this.getLayoutItems(), function(item) {
+                Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth());
+            });
+        }
+    },
+
+    /**
+     * @private
+     * Determine the row and cell indexes for each component, taking into consideration
+     * the number of columns and each item's configured colspan/rowspan values.
+     * @param {Array} items The layout components
+     * @return {Array} List of row and cell indexes for each of the components
+     */
+    calculateCells: function(items) {
+        var cells = [],
+            rowIdx = 0,
+            colIdx = 0,
+            cellIdx = 0,
+            totalCols = this.columns || Infinity,
+            rowspans = [], //rolling list of active rowspans for each column
+            i = 0, j,
+            len = items.length,
+            item;
+
+        for (; i < len; i++) {
+            item = items[i];
+
+            // Find the first available row/col slot not taken up by a spanning cell
+            while (colIdx >= totalCols || rowspans[colIdx] > 0) {
+                if (colIdx >= totalCols) {
+                    // move down to next row
+                    colIdx = 0;
+                    cellIdx = 0;
+                    rowIdx++;
+
+                    // decrement all rowspans
+                    for (j = 0; j < totalCols; j++) {
+                        if (rowspans[j] > 0) {
+                            rowspans[j]--;
+                        }
+                    }
+                } else {
+                    colIdx++;
+                }
+            }
+
+            // Add the cell info to the list
+            cells.push({
+                rowIdx: rowIdx,
+                cellIdx: cellIdx
+            });
+
+            // Increment
+            rowspans[colIdx] = item.rowspan || 1;
+            colIdx += item.colspan || 1;
+            cellIdx++;
+        }
+
+        return cells;
+    },
+
+    /**
+     * @private
+     * Return the layout's table element, creating it if necessary.
+     */
+    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, //TODO should this be specified or should CSS handle it?
+                    cn: {tag: 'tbody'}
+                }, this.tableAttrs),
+                null, true
+            );
+        }
+        return table;
+    },
+
+    /**
+     * @private
+     * Opera 10.5 has a bug where if a table cell's child has box-sizing:border-box and padding, it
+     * will include that padding in the size of the cell, making it always larger than the
+     * shrink-wrapped size of its contents. To get around this we have to wrap the contents in a div
+     * and then set that div's width to match the item rendered within it afterLayout. This method
+     * determines whether we need the wrapper div; it currently does a straight UA sniff as this bug
+     * seems isolated to just Opera 10.5, but feature detection could be added here if needed.
+     */
+    needsDivWrap: function() {
+        return Ext.isOpera10_5;
+    }
+});
+/**
+ * @class Ext.menu.Item
+ * @extends Ext.Component
+
+ * A base class for all menu items that require menu-related functionality such as click handling,
+ * sub-menus, icons, etc.
+ * {@img Ext.menu.Menu/Ext.menu.Menu.png Ext.menu.Menu component}
+__Example Usage:__
+    Ext.create('Ext.menu.Menu', {
+               width: 100,
+               height: 100,
+               floating: false,  // usually you want this set to True (default)
+               renderTo: Ext.getBody(),  // usually rendered by it's containing component
+               items: [{
+                   text: 'icon item',
+                   iconCls: 'add16'
+               },{
+                       text: 'text item',
+               },{                        
+                       text: 'plain item',
+                       plain: true        
+               }]
+       }); 
+
+ * @xtype menuitem
+ * @markdown
+ * @constructor
+ * @param {Object} config The config object
+ */
+Ext.define('Ext.menu.Item', {
+    extend: 'Ext.Component',
+    alias: 'widget.menuitem',
+    alternateClassName: 'Ext.menu.TextItem',
+    
+    /**
+     * @property {Boolean} activated
+     * Whether or not this item is currently activated
+     */
+
+    /**
+     * @cfg {String} activeCls
+     * The CSS class added to the menu item when the item is activated (focused/mouseover).
+     * Defaults to `Ext.baseCSSPrefix + 'menu-item-active'`.
+     * @markdown
+     */
+    activeCls: Ext.baseCSSPrefix + 'menu-item-active',
+    
+    /**
+     * @cfg {String} ariaRole @hide
+     */
+    ariaRole: 'menuitem',
+    
+    /**
+     * @cfg {Boolean} canActivate
+     * Whether or not this menu item can be activated when focused/mouseovered. Defaults to `true`.
+     * @markdown
+     */
+    canActivate: true,
+    
+    /**
+     * @cfg {Number} clickHideDelay
+     * The delay in milliseconds to wait before hiding the menu after clicking the menu item.
+     * This only has an effect when `hideOnClick: true`. Defaults to `1`.
+     * @markdown
+     */
+    clickHideDelay: 1,
+    
+    /**
+     * @cfg {Boolean} destroyMenu
+     * Whether or not to destroy any associated sub-menu when this item is destroyed. Defaults to `true`.
+     */
+    destroyMenu: true,
+    
+    /**
+     * @cfg {String} disabledCls
+     * The CSS class added to the menu item when the item is disabled.
+     * Defaults to `Ext.baseCSSPrefix + 'menu-item-disabled'`.
+     * @markdown
+     */
+    disabledCls: Ext.baseCSSPrefix + 'menu-item-disabled',
+    
+    /**
+     * @cfg {String} href
+     * The href attribute to use for the underlying anchor link. Defaults to `#`.
+     * @markdown
+     */
+     
+     /**
+      * @cfg {String} hrefTarget
+      * The target attribute to use for the underlying anchor link. Defaults to `undefined`.
+      * @markdown
+      */
+    
+    /**
+     * @cfg {Boolean} hideOnClick
+     * Whether to not to hide the owning menu when this item is clicked. Defaults to `true`.
+     * @markdown
+     */
+    hideOnClick: true,
+    
+    /**
+     * @cfg {String} icon
+     * The path to an icon to display in this item. Defaults to `Ext.BLANK_IMAGE_URL`.
+     * @markdown
+     */
+     
+    /**
+     * @cfg {String} iconCls
+     * A CSS class that specifies a `background-image` to use as the icon for this item. Defaults to `undefined`.
+     * @markdown
+     */
+    
+    isMenuItem: true,
+    
+    /**
+     * @cfg {Mixed} menu
+     * Either an instance of {@link Ext.menu.Menu} or a config object for an {@link Ext.menu.Menu}
+     * which will act as a sub-menu to this item.
+     * @markdown
+     * @property {Ext.menu.Menu} menu The sub-menu associated with this item, if one was configured.
+     */
+    
+    /**
+     * @cfg {String} menuAlign
+     * The default {@link Ext.core.Element#getAlignToXY Ext.Element.getAlignToXY} anchor position value for this
+     * item's sub-menu relative to this item's position. Defaults to `'tl-tr?'`.
+     * @markdown
+     */
+    menuAlign: 'tl-tr?',
+    
+    /**
+     * @cfg {Number} menuExpandDelay
+     * The delay in milliseconds before this item's sub-menu expands after this item is moused over. Defaults to `200`.
+     * @markdown
+     */
+    menuExpandDelay: 200,
+    
+    /**
+     * @cfg {Number} menuHideDelay
+     * The delay in milliseconds before this item's sub-menu hides after this item is moused out. Defaults to `200`.
+     * @markdown
+     */
+    menuHideDelay: 200,
+    
+    /**
+     * @cfg {Boolean} plain
+     * Whether or not this item is plain text/html with no icon or visual activation. Defaults to `false`.
+     * @markdown
+     */
+    
+    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,
+    
+    /**
+     * @cfg {String} text
+     * The text/html to display in this item. Defaults to `undefined`.
+     * @markdown
+     */
+    
+    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(
+            /**
+             * @event activate
+             * Fires when this item is activated
+             * @param {Ext.menu.Item} item The activated item
+             */
+            'activate',
+            
+            /**
+             * @event click
+             * Fires when this item is clicked
+             * @param {Ext.menu.Item} item The item that was clicked
+             * @param {Ext.EventObject} e The underyling {@link Ext.EventObject}.
+             */
+            'click',
+            
+            /**
+             * @event deactivate
+             * Fires when this tiem is deactivated
+             * @param {Ext.menu.Item} item The deactivated item
+             */
+            '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);
+    },
+    
+    /**
+     * Sets the {@link #click} handler of this item
+     * @param {Function} fn The handler function
+     * @param {Object} scope (optional) The scope of the handler function
+     */
+    setHandler: function(fn, scope) {
+        this.handler = fn || null;
+        this.scope = scope;
+    },
+    
+    /**
+     * Sets the {@link #iconCls} of this item
+     * @param {String} iconCls The CSS class to set to {@link #iconCls}
+     */
+    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;
+    },
+    
+    /**
+     * Sets the {@link #text} of this item
+     * @param {String} text The {@link #text}
+     */
+    setText: function(text) {
+        var me = this,
+            el = me.textEl || me.el,
+            newWidth;
+        
+        if (text && el) {
+            el.update(text);
+                
+            if (me.textEl) {
+                // Resize the menu to fit the text
+                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;
+    }
+});
+
+/**
+ * @class Ext.menu.CheckItem
+ * @extends Ext.menu.Item
+
+A menu item that contains a togglable checkbox by default, but that can also be a part of a radio group.
+{@img Ext.menu.CheckItem/Ext.menu.CheckItem.png Ext.menu.CheckItem component}
+__Example Usage__    
+    Ext.create('Ext.menu.Menu', {
+               width: 100,
+               height: 110,
+               floating: false,  // usually you want this set to True (default)
+               renderTo: Ext.getBody(),  // usually rendered by it's containing component
+               items: [{
+                   xtype: 'menucheckitem',
+                   text: 'select all'
+               },{
+                   xtype: 'menucheckitem',
+                       text: 'select specific',
+               },{
+            iconCls: 'add16',
+                   text: 'icon item' 
+               },{
+                   text: 'regular item'
+               }]
+       }); 
+       
+ * @xtype menucheckitem
+ * @markdown
+ * @constructor
+ * @param {Object} config The config object
+ */
+
+Ext.define('Ext.menu.CheckItem', {
+    extend: 'Ext.menu.Item',
+    alias: 'widget.menucheckitem',
+
+    /**
+     * @cfg {String} checkedCls
+     * The CSS class used by {@link #cls} to show the checked state.
+     * Defaults to `Ext.baseCSSPrefix + 'menu-item-checked'`.
+     * @markdown
+     */
+    checkedCls: Ext.baseCSSPrefix + 'menu-item-checked',
+    /**
+     * @cfg {String} uncheckedCls
+     * The CSS class used by {@link #cls} to show the unchecked state.
+     * Defaults to `Ext.baseCSSPrefix + 'menu-item-unchecked'`.
+     * @markdown
+     */
+    uncheckedCls: Ext.baseCSSPrefix + 'menu-item-unchecked',
+    /**
+     * @cfg {String} groupCls
+     * The CSS class applied to this item's icon image to denote being a part of a radio group.
+     * Defaults to `Ext.baseCSSClass + 'menu-group-icon'`.
+     * Any specified {@link #iconCls} overrides this.
+     * @markdown
+     */
+    groupCls: Ext.baseCSSPrefix + 'menu-group-icon',
+
+    /**
+     * @cfg {Boolean} hideOnClick
+     * Whether to not to hide the owning menu when this item is clicked.
+     * Defaults to `false` for checkbox items, and to `true` for radio group items.
+     * @markdown
+     */
+    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(
+            /**
+             * @event beforecheckchange
+             * Fires before a change event. Return false to cancel.
+             * @param {Ext.menu.CheckItem} this
+             * @param {Boolean} checked
+             */
+            'beforecheckchange',
+
+            /**
+             * @event checkchange
+             * Fires after a change event.
+             * @param {Ext.menu.CheckItem} this
+             * @param {Boolean} checked
+             */
+            '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;
+            }
+        }
+    },
+
+    /**
+     * Disables just the checkbox functionality of this menu Item. If this menu item has a submenu, that submenu
+     * will still be accessible
+     */
+    disableCheckChange: function() {
+        var me = this;
+
+        me.iconEl.addCls(me.disabledCls);
+        me.checkChangeDisabled = true;
+    },
+
+    /**
+     * Reenables the checkbox functionality of this menu item after having been disabled by {@link #disableCheckChange}
+     */
+    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);
+    },
+
+    /**
+     * Sets the checked state of the item
+     * @param {Boolean} checked True to check, false to uncheck
+     * @param {Boolean} suppressEvents (optional) True to prevent firing the checkchange events. Defaults to `false`.
+     * @markdown
+     */
+    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);
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.menu.KeyNav
+ * @private
+ */
+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);
+    }
+});
+/**
+ * @class Ext.menu.Separator
+ * @extends Ext.menu.Item
+ *
+ * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
+ * add one of these by using "-" in your call to add() or in your items config rather than creating one directly.
+ *
+ * {@img Ext.menu.Separator/Ext.menu.Separator.png Ext.menu.Separator component}
+ *
+ * ## Code 
+ *
+ *     Ext.create('Ext.menu.Menu', {
+ *         width: 100,
+ *         height: 100,
+ *         floating: false,  // usually you want this set to True (default)
+ *         renderTo: Ext.getBody(),  // usually rendered by it's containing component
+ *         items: [{
+ *             text: 'icon item',
+ *             iconCls: 'add16'
+ *         },{
+ *             xtype: 'menuseparator'
+ *         },{
+ *            text: 'seperator above',
+ *         },{
+ *            text: 'regular item',
+ *         }]
+ *     }); 
+ *
+ * @xtype menuseparator
+ * @markdown
+ * @constructor
+ * @param {Object} config The config object
+ */
+Ext.define('Ext.menu.Separator', {
+    extend: 'Ext.menu.Item',
+    alias: 'widget.menuseparator',
+    
+    /**
+     * @cfg {String} activeCls @hide
+     */
+    
+    /**
+     * @cfg {Boolean} canActivate @hide
+     */
+    canActivate: false,
+    
+    /**
+     * @cfg {Boolean} clickHideDelay @hide
+     */
+     
+    /**
+     * @cfg {Boolean} destroyMenu @hide
+     */
+     
+    /**
+     * @cfg {Boolean} disabledCls @hide
+     */
+     
+    focusable: false,
+     
+    /**
+     * @cfg {String} href @hide
+     */
+    
+    /**
+     * @cfg {String} hrefTarget @hide
+     */
+    
+    /**
+     * @cfg {Boolean} hideOnClick @hide
+     */
+    hideOnClick: false,
+    
+    /**
+     * @cfg {String} icon @hide
+     */
+    
+    /**
+     * @cfg {String} iconCls @hide
+     */
+    
+    /**
+     * @cfg {Mixed} menu @hide
+     */
+    
+    /**
+     * @cfg {String} menuAlign @hide
+     */
+    
+    /**
+     * @cfg {Number} menuExpandDelay @hide
+     */
+    
+    /**
+     * @cfg {Number} menuHideDelay @hide
+     */
+    
+    /**
+     * @cfg {Boolean} plain @hide
+     */
+    plain: true,
+    
+    /**
+     * @cfg {String} separatorCls
+     * The CSS class used by the separator item to show the incised line.
+     * Defaults to `Ext.baseCSSPrefix + 'menu-item-separator'`.
+     * @markdown
+     */
+    separatorCls: Ext.baseCSSPrefix + 'menu-item-separator',
+    
+    /**
+     * @cfg {String} text @hide
+     */
+    text: '&#160;',
+    
+    onRender: function(ct, pos) {
+        var me = this,
+            sepCls = me.separatorCls;
+            
+        me.cls += ' ' + sepCls;
+        
+        Ext.applyIf(me.renderSelectors, {
+            itemSepEl: '.' + sepCls
+        });
+        
+        me.callParent(arguments);
+    }
+});
+/**
+ * @class Ext.menu.Menu
+ * @extends Ext.panel.Panel
+ *
+ * A menu object. This is the container to which you may add {@link Ext.menu.Item menu items}.
+ *
+ * Menus may contain either {@link Ext.menu.Item menu items}, or general {@link Ext.Component Components}.
+ * Menus may also contain {@link Ext.panel.AbstractPanel#dockedItems docked items} because it extends {@link Ext.panel.Panel}.
+ *
+ * To make a contained general {@link Ext.Component Component} line up with other {@link Ext.menu.Item menu items},
+ * specify `{@link Ext.menu.Item#iconCls iconCls}: 'no-icon'` _or_ `{@link Ext.menu.Item#indent indent}: true`.
+ * This reserves a space for an icon, and indents the Component in line with the other menu items.
+ * See {@link Ext.form.field.ComboBox}.{@link Ext.form.field.ComboBox#getListParent getListParent} for an example.
+
+ * By default, Menus are absolutely positioned, floating Components. By configuring a Menu with `{@link #floating}:false`,
+ * a Menu may be used as a child of a {@link Ext.container.Container Container}.
+ * {@img Ext.menu.Item/Ext.menu.Item.png Ext.menu.Item component}
+__Example Usage__
+        Ext.create('Ext.menu.Menu', {
+                width: 100,
+                height: 100,
+                margin: '0 0 10 0',
+                floating: false,  // usually you want this set to True (default)
+                renderTo: Ext.getBody(),  // usually rendered by it's containing component
+                items: [{                        
+                        text: 'regular item 1'        
+                },{
+                    text: 'regular item 2'
+                },{
+                        text: 'regular item 3'  
+                }]
+        }); 
+        
+        Ext.create('Ext.menu.Menu', {
+                width: 100,
+                height: 100,
+                plain: true,
+                floating: false,  // usually you want this set to True (default)
+                renderTo: Ext.getBody(),  // usually rendered by it's containing component
+                items: [{                        
+                        text: 'plain item 1'    
+                },{
+                    text: 'plain item 2'
+                },{
+                        text: 'plain item 3'
+                }]
+        }); 
+ * @xtype menu
+ * @markdown
+ * @constructor
+ * @param {Object} config The config object
+ */
+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'
+    ],
+
+    /**
+     * @cfg {Boolean} allowOtherMenus
+     * True to allow multiple menus to be displayed at the same time. Defaults to `false`.
+     * @markdown
+     */
+    allowOtherMenus: false,
+
+    /**
+     * @cfg {String} ariaRole @hide
+     */
+    ariaRole: 'menu',
+
+    /**
+     * @cfg {Boolean} autoRender @hide
+     * floating is true, so autoRender always happens
+     */
+
+    /**
+     * @cfg {String} defaultAlign
+     * The default {@link Ext.core.Element#getAlignToXY Ext.core.Element#getAlignToXY} anchor position value for this menu
+     * relative to its element of origin. Defaults to `'tl-bl?'`.
+     * @markdown
+     */
+    defaultAlign: 'tl-bl?',
+
+    /**
+     * @cfg {Boolean} floating
+     * A Menu configured as `floating: true` (the default) will be rendered as an absolutely positioned,
+     * {@link Ext.Component#floating floating} {@link Ext.Component Component}. If configured as `floating: false`, the Menu may be
+     * used as a child item of another {@link Ext.container.Container Container}.
+     * @markdown
+     */
+    floating: true,
+
+    /**
+     * @cfg {Boolean} @hide
+     * Menu performs its own size changing constraining, so ensure Component's constraining is not applied
+     */
+    constrain: false,
+
+    /**
+     * @cfg {Boolean} hidden
+     * True to initially render the Menu as hidden, requiring to be shown manually.
+     * Defaults to `true` when `floating: true`, and defaults to `false` when `floating: false`.
+     * @markdown
+     */
+    hidden: true,
+
+    /**
+     * @cfg {Boolean} ignoreParentClicks
+     * True to ignore clicks on any item in this menu that is a parent item (displays a submenu)
+     * so that the submenu is not dismissed when clicking the parent item. Defaults to `false`.
+     * @markdown
+     */
+    ignoreParentClicks: false,
+
+    isMenu: true,
+
+    /**
+     * @cfg {String/Object} layout @hide
+     */
+
+    /**
+     * @cfg {Boolean} showSeparator True to show the icon separator. (defaults to true).
+     */
+    showSeparator : true,
+
+    /**
+     * @cfg {Number} minWidth
+     * The minimum width of the Menu. Defaults to `120`.
+     * @markdown
+     */
+    minWidth: 120,
+
+    /**
+     * @cfg {Boolean} plain
+     * True to remove the incised line down the left side of the menu and to not
+     * indent general Component items. Defaults to `false`.
+     * @markdown
+     */
+
+    initComponent: function() {
+        var me = this,
+            prefix = Ext.baseCSSPrefix;
+
+        me.addEvents(
+            /**
+             * @event click
+             * Fires when this menu is clicked
+             * @param {Ext.menu.Menu} menu The menu which has been clicked
+             * @param {Ext.Component} item The menu item that was clicked. `undefined` if not applicable.
+             * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}.
+             * @markdown
+             */
+            'click',
+
+            /**
+             * @event mouseenter
+             * Fires when the mouse enters this menu
+             * @param {Ext.menu.Menu} menu The menu
+             * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
+             * @markdown
+             */
+            'mouseenter',
+
+            /**
+             * @event mouseleave
+             * Fires when the mouse leaves this menu
+             * @param {Ext.menu.Menu} menu The menu
+             * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
+             * @markdown
+             */
+            'mouseleave',
+
+            /**
+             * @event mouseover
+             * Fires when the mouse is hovering over this menu
+             * @param {Ext.menu.Menu} menu The menu
+             * @param {Ext.Component} item The menu item that the mouse is over. `undefined` if not applicable.
+             * @param {Ext.EventObject} e The underlying {@link Ext.EventObject}
+             */
+            'mouseover'
+        );
+
+        Ext.menu.Manager.register(me);
+
+        // Menu classes
+        var cls = [prefix + 'menu'];
+        if (me.plain) {
+            cls.push(prefix + 'menu-plain');
+        }
+        me.cls = cls.join(' ');
+
+        // Menu body classes
+        var bodyCls = me.bodyCls ? [me.bodyCls] : [];
+        bodyCls.unshift(prefix + 'menu-body');
+        me.bodyCls = bodyCls.join(' ');
+
+        // Internal vbox layout, with scrolling overflow
+        // Placed in initComponent (rather than prototype) in order to support dynamic layout/scroller
+        // options if we wish to allow for such configurations on the Menu.
+        // e.g., scrolling speed, vbox align stretch, etc.
+        me.layout = {
+            type: 'vbox',
+            align: 'stretchmax',
+            autoSize: true,
+            clearInnerCtOnLayout: true,
+            overflowHandler: 'Scroller'
+        };
+
+        // hidden defaults to false if floating is configured as false
+        if (me.floating === false && me.initialConfig.hidden !== true) {
+            me.hidden = false;
+        }
+
+        me.callParent(arguments);
+
+        me.on('beforeshow', function() {
+            var hasItems = !!me.items.length;
+            // FIXME: When a menu has its show cancelled because of no items, it
+            // gets a visibility: hidden applied to it (instead of the default display: none)
+            // Not sure why, but we remove this style when we want to show again.
+            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);
+
+        // TODO: Move this to a subTemplate When we support them in the future
+        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);
+
+        // For IE6 & IE quirks, we have to resize the el and body since position: absolute
+        // floating elements inherit their parent's width, making them the width of
+        // document.body instead of the width of their contents.
+        // This includes left/right dock items.
+        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');
+
+            // First set the body to the new width
+            me.body.setWidth(newWidth);
+
+            // Now we calculate additional width (docked items) and set the el's width
+            for (; i < l, di = dis.getAt(i); i++) {
+                if (di.dock == 'left' || di.dock == 'right') {
+                    newWidth += di.getWidth();
+                }
+            }
+            me.el.setWidth(newWidth);
+        }
+    },
+
+    /**
+     * Returns whether a menu item can be activated or not.
+     * @return {Boolean}
+     */
+    canActivateItem: function(item) {
+        return item && !item.isDisabled() && item.isVisible() && (item.canActivate || item.getXTypes().indexOf('menuitem') < 0);
+    },
+
+    /**
+     * Deactivates the current active item on the menu, if one exists.
+     */
+    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;
+            }
+        }
+    },
+
+    // inherit docs
+    getFocusEl: function() {
+        return this.focusEl;
+    },
+
+    // inherit docs
+    hide: function() {
+        this.deactivateActiveItem();
+        this.callParent(arguments);
+    },
+
+    // private
+    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);
+        }
+
+        // Apply our minWidth to all of our child components so it's accounted
+        // for in our VBox layout
+        cmp.minWidth = cmp.minWidth || me.minWidth;
+
+        return cmp;
+    },
+
+    // private
+    lookupItemFromObject: function(cmp) {
+        var me = this,
+            prefix = Ext.baseCSSPrefix;
+
+        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) {
+            var cls = [
+                    prefix + 'menu-item',
+                    prefix + 'menu-item-cmp'
+                ],
+                intercept = Ext.Function.createInterceptor;
+
+            // Wrap focus/blur to control component focus
+            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;
+    },
+
+    // private
+    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());
+        }
+    },
+
+    /**
+     * Shows the floating menu by the specified {@link Ext.Component Component} or {@link Ext.core.Element Element}.
+     * @param {Mixed component} The {@link Ext.Component} or {@link Ext.core.Element} to show the menu by.
+     * @param {String} position (optional) Alignment position as used by {@link Ext.core.Element#getAlignToXY Ext.core.Element.getAlignToXY}. Defaults to `{@link #defaultAlign}`.
+     * @param {Array} offsets (optional) Alignment offsets as used by {@link Ext.core.Element#getAlignToXY Ext.core.Element.getAlignToXY}. Defaults to `undefined`.
+     * @return {Menu} This Menu.
+     * @markdown
+     */
+    showBy: function(cmp, pos, off) {
+        var me = this;
+
+        if (me.floating && cmp) {
+            me.layout.autoSize = true;
+            me.show();
+
+            // Component or Element
+            cmp = cmp.el || cmp;
+
+            // Convert absolute to floatParent-relative coordinates if necessary.
+            var xy = me.el.getAlignToXY(cmp, pos || me.defaultAlign, off);
+            if (me.floatParent) {
+                var r = me.floatParent.getTargetEl().getViewRegion();
+                xy[0] -= r.x;
+                xy[1] -= r.y;
+            }
+            me.showAt(xy);
+            me.doConstrain();
+        }
+        return me;
+    },
+
+    doConstrain : function() {
+        var me = this,
+            y = this.el.getY(),
+            max, full,
+            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;
+            //Normalize y by the scroll position for the parent element.  Need to move it into the coordinate space
+            //of the view.
+            normalY = y - scrollTop;
+            max = me.maxHeight ? me.maxHeight : viewHeight - normalY;
+            if (full > viewHeight) {
+                max = viewHeight;
+                //Set returnY equal to (0,0) in view space by reducing y by the value of normalY
+                returnY = y - normalY;
+            } else if (max < full) {
+                returnY = y - (full - max);
+                max = full;
+            }
+        }else{
+            max = me.getHeight();
+        }
+        // Always respect maxHeight
+        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);
+            }
+        }
+        me.el.setY(returnY);
+    }
+});
+/**
+ * @class Ext.menu.ColorPicker
+ * @extends Ext.menu.Menu
+ * <p>A menu containing a {@link Ext.picker.Color} Component.</p>
+ * <p>Notes:</p><div class="mdetail-params"><ul>
+ * <li>Although not listed here, the <b>constructor</b> for this class
+ * accepts all of the configuration options of <b>{@link Ext.picker.Color}</b>.</li>
+ * <li>If subclassing ColorMenu, any configuration options for the ColorPicker must be
+ * applied to the <tt><b>initialConfig</b></tt> property of the ColorMenu.
+ * Applying {@link Ext.picker.Color ColorPicker} configuration settings to
+ * <b><tt>this</tt></b> will <b>not</b> affect the ColorPicker's configuration.</li>
+ * </ul></div>
+ * {@img Ext.menu.ColorPicker/Ext.menu.ColorPicker.png Ext.menu.ColorPicker component}
+ * __Example Usage__
+     var colorPicker = Ext.create('Ext.menu.ColorPicker', {
+        value: '000000'
+    });
+
+    Ext.create('Ext.menu.Menu', {
+               width: 100,
+               height: 90,
+               floating: false,  // usually you want this set to True (default)
+               renderTo: Ext.getBody(),  // usually rendered by it's containing component
+               items: [{
+                   text: 'choose a color',
+                   menu: colorPicker
+               },{
+            iconCls: 'add16',
+                   text: 'icon item'
+               },{
+                   text: 'regular item'
+               }]
+       });
+
+ * @xtype colormenu
+ * @author Nicolas Ferrero
+ */
+ Ext.define('Ext.menu.ColorPicker', {
+     extend: 'Ext.menu.Menu',
+
+     alias: 'widget.colormenu',
+
+     requires: [
+        'Ext.picker.Color'
+     ],
+
+    /**
+     * @cfg {Boolean} hideOnClick
+     * False to continue showing the menu after a date is selected, defaults to true.
+     */
+    hideOnClick : true,
+
+    /**
+     * @cfg {String} pickerId
+     * An id to assign to the underlying color picker. Defaults to <tt>null</tt>.
+     */
+    pickerId : null,
+
+    /**
+     * @cfg {Number} maxHeight
+     * @hide
+     */
+
+    /**
+     * The {@link Ext.picker.Color} instance for this ColorMenu
+     * @property picker
+     * @type ColorPicker
+     */
+
+    /**
+     * @event click
+     * @hide
+     */
+
+    /**
+     * @event itemclick
+     * @hide
+     */
+
+    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');
+
+        /**
+         * @event select
+         * Fires when a date is selected from the {@link #picker Ext.picker.Color}
+         * @param {Ext.picker.Color} picker The {@link #picker Ext.picker.Color}
+         * @param {String} color The 6-digit color hex code (without the # symbol)
+         */
+        me.relayEvents(me.picker, ['select']);
+
+        if (me.hideOnClick) {
+            me.on('select', me.hidePickerOnSelect, me);
+        }
+    },
+
+    /**
+     * Hides picker on select if hideOnClick is true
+     * @private
+     */
+    hidePickerOnSelect: function() {
+        Ext.menu.Manager.hideAll();
+    }
+ });
+/**
+ * @class Ext.menu.DatePicker
+ * @extends Ext.menu.Menu
+ * <p>A menu containing an {@link Ext.picker.Date} Component.</p>
+ * <p>Notes:</p><div class="mdetail-params"><ul>
+ * <li>Although not listed here, the <b>constructor</b> for this class
+ * accepts all of the configuration options of <b>{@link Ext.picker.Date}</b>.</li>
+ * <li>If subclassing DateMenu, any configuration options for the DatePicker must be
+ * applied to the <tt><b>initialConfig</b></tt> property of the DateMenu.
+ * Applying {@link Ext.picker.Date DatePicker} configuration settings to
+ * <b><tt>this</tt></b> will <b>not</b> affect the DatePicker's configuration.</li>
+ * </ul></div>
+ * {@img Ext.menu.DatePicker/Ext.menu.DatePicker.png Ext.menu.DatePicker component}
+ * __Example Usage__
+     var dateMenu = Ext.create('Ext.menu.DatePicker', {
+        handler: function(dp, date){
+            Ext.Msg.alert('Date Selected', 'You choose {0}.', Ext.Date.format(date, 'M j, Y'));
+
+        }
+    });
+
+    Ext.create('Ext.menu.Menu', {
+               width: 100,
+               height: 90,
+               floating: false,  // usually you want this set to True (default)
+               renderTo: Ext.getBody(),  // usually rendered by it's containing component
+               items: [{
+                   text: 'choose a date',
+                   menu: dateMenu
+               },{
+            iconCls: 'add16',
+                   text: 'icon item'
+               },{
+                   text: 'regular item'
+               }]
+       });
+
+ * @xtype datemenu
+ * @author Nicolas Ferrero
+ */
+ Ext.define('Ext.menu.DatePicker', {
+     extend: 'Ext.menu.Menu',
+
+     alias: 'widget.datemenu',
+
+     requires: [
+        'Ext.picker.Date'
+     ],
+
+    /**
+     * @cfg {Boolean} hideOnClick
+     * False to continue showing the menu after a date is selected, defaults to true.
+     */
+    hideOnClick : true,
+
+    /**
+     * @cfg {String} pickerId
+     * An id to assign to the underlying date picker. Defaults to <tt>null</tt>.
+     */
+    pickerId : null,
+
+    /**
+     * @cfg {Number} maxHeight
+     * @hide
+     */
+
+    /**
+     * The {@link Ext.picker.Date} instance for this DateMenu
+     * @property picker
+     * @type Ext.picker.Date
+     */
+
+    /**
+     * @event click
+     * @hide
+     */
+
+    /**
+     * @event itemclick
+     * @hide
+     */
+
+    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');
+        /**
+         * @event select
+         * Fires when a date is selected from the {@link #picker Ext.picker.Date}
+         * @param {Ext.picker.Date} picker The {@link #picker Ext.picker.Date}
+         * @param {Date} date The selected date
+         */
+        me.relayEvents(me.picker, ['select']);
+
+        if (me.hideOnClick) {
+            me.on('select', me.hidePickerOnSelect, me);
+        }
+    },
+
+    hidePickerOnSelect: function() {
+        Ext.menu.Manager.hideAll();
+    }
+ });
+/**
+ * @class Ext.panel.Tool
+ * @extends Ext.Component
+
+This class is used to display small visual icons in the header of a panel. There are a set of
+25 icons that can be specified by using the {@link #type} config. The {@link #handler} config
+can be used to provide a function that will respond to any click events. In general, this class
+will not be instantiated directly, rather it will be created by specifying the {@link Ext.panel.Panel#tools}
+configuration on the Panel itself.
+
+__Example Usage__
+
+    Ext.create('Ext.panel.Panel', {
+       width: 200,
+       height: 200,
+       renderTo: document.body,
+       title: 'A Panel',
+       tools: [{
+           type: 'help',
+           handler: function(){
+               // show help here
+           }
+       }, {
+           itemId: 'refresh',
+           type: 'refresh',
+           hidden: true,
+           handler: function(){
+               // do refresh
+           }
+       }, {
+           type: 'search',
+           handler: function(event, target, owner, tool){
+               // do search
+               owner.child('#refresh').show();
+           }
+       }]
+    });
+
+ * @markdown
+ * @xtype tool
+ */
+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"/>'],
+    
+    /**
+     * @cfg {Function} handler
+     * A function to execute when the tool is clicked.
+     * Arguments passed are:
+     * <ul>
+     * <li><b>event</b> : Ext.EventObject<div class="sub-desc">The click event.</div></li>
+     * <li><b>toolEl</b> : Ext.core.Element<div class="sub-desc">The tool Element.</div></li>
+     * <li><b>panel</b> : Ext.panel.Panel<div class="sub-desc">The host Panel</div></li>
+     * <li><b>tool</b> : Ext.panel.Tool<div class="sub-desc">The tool object</div></li>
+     * </ul>
+     */
+    
+    /**
+     * @cfg {Object} scope
+     * The scope to execute the {@link #handler} function. Defaults to the tool.
+     */
+    
+    /**
+     * @cfg {String} type
+     * The type of tool to render. The following types are available:
+     * <ul>
+     * <li>close</li>
+     * <li>collapse</li>
+     * <li>down</li>
+     * <li>expand</li>
+     * <li>gear</li>
+     * <li>help</li>
+     * <li>left</li>
+     * <li>maximize</li>
+     * <li>minimize</li>
+     * <li>minus</li>
+     * <li>move</li>
+     * <li>next</li>
+     * <li>pin</li>
+     * <li>plus</li>
+     * <li>prev</li>
+     * <li>print</li>
+     * <li>refresh</li>
+     * <li>resize</li>
+     * <li>restore</li>
+     * <li>right</li>
+     * <li>save</li>
+     * <li>search</li>
+     * <li>toggle</li>
+     * <li>unpin</li>
+     * <li>up</li>
+     * </ul>
+     */
+    
+    /**
+     * @cfg {String/Object} tooltip 
+     * The tooltip for the tool - can be a string to be used as innerHTML (html tags are accepted) or QuickTips config object
+     */
+    
+    /**
+     * @cfg {Boolean} stopEvent
+     * Defaults to true. Specify as false to allow click event to propagate.
+     */
+    stopEvent: true,
+
+    initComponent: function() {
+        var me = this;
+        me.addEvents(
+            /**
+             * @event click
+             * Fires when the tool is clicked
+             * @param {Ext.panel.Tool} this
+             * @param {Ext.EventObject} e The event object
+             */
+            'click'
+        );
+        
+        //<debug>
+        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.warn('When specifying a tool you should use the type option, the id can conflict now that tool is a Component');
+        }
+        //</debug>
+        
+        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();
+    },
+
+    // inherit docs
+    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
+        });
+    },
+
+    /**
+     * Set the type of the tool. Allows the icon to be changed.
+     * @param {String} type The new type. See the {@link #type} config.
+     * @return {Ext.panel.Tool} this
+     */
+    setType: function(type) {
+        var me = this;
+        
+        me.type = type;
+        if (me.rendered) {
+            me.toolEl.dom.className = me.baseCls + '-' + type;
+        }
+        return me;
+    },
+
+    /**
+     * Binds this tool to a component.
+     * @private
+     * @param {Ext.Component} component The component
+     */
+    bindTo: function(component) {
+        this.owner = component;
+    },
+
+    /**
+     * Fired when the tool element is clicked
+     * @private
+     * @param {Ext.EventObject} e
+     * @param {HTMLElement} target The target element
+     */
+    onClick: function(e, target) {
+        var me = this,
+            owner;
+            
+        if (me.disabled) {
+            return false;
+        }
+        owner = me.owner || me.ownerCt;
+
+        //remove the pressed + over class
+        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;
+    },
+    
+    // inherit docs
+    onDestroy: function(){
+        if (Ext.isObject(this.tooltip)) {
+            Ext.tip.QuickTipManager.unregister(this.id);
+        }    
+        this.callParent();
+    },
+
+    /**
+     * Called then the user pressing their mouse button down on a tool
+     * Adds the press class ({@link #toolPressedCls})
+     * @private
+     */
+    onMouseDown: function() {
+        if (this.disabled) {
+            return false;
+        }
+
+        this.el.addCls(this.toolPressedCls);
+    },
+
+    /**
+     * Called when the user rolls over a tool
+     * Adds the over class ({@link #toolOverCls})
+     * @private
+     */
+    onMouseOver: function() {
+        if (this.disabled) {
+            return false;
+        }
+        this.el.addCls(this.toolOverCls);
+    },
+
+    /**
+     * Called when the user rolls out from a tool.
+     * Removes the over class ({@link #toolOverCls})
+     * @private
+     */
+    onMouseOut: function() {
+        this.el.removeCls(this.toolOverCls);
+    }
+});
+/**
+ * @class Ext.resizer.Handle
+ * @extends Ext.Component
+ *
+ * Provides a handle for 9-point resizing of Elements or Components.
+ */
+Ext.define('Ext.resizer.Handle', {
+    extend: 'Ext.Component',
+    handleCls: '',
+    baseHandleCls: Ext.baseCSSPrefix + 'resizable-handle',
+    // Ext.resizer.Resizer.prototype.possiblePositions define the regions
+    // which will be passed in as a region configuration.
+    region: '',
+
+    onRender: function() {
+        this.addCls(
+            this.baseHandleCls,
+            this.baseHandleCls + '-' + this.region,
+            this.handleCls
+        );
+        this.callParent(arguments);
+        this.el.unselectable();
+    }
+});
+
+/**
+ * @class Ext.resizer.Resizer
+ * <p>Applies drag handles to an element or component to make it resizable. The
+ * drag handles are inserted into the element (or component's element) and
+ * positioned absolute.</p>
+ *
+ * <p>Textarea and img elements will be wrapped with an additional div because
+ * these elements do not support child nodes. The original element can be accessed
+ * through the originalTarget property.</p>
+ *
+ * <p>Here is the list of valid resize handles:</p>
+ * <pre>
+Value   Description
+------  -------------------
+ 'n'     north
+ 's'     south
+ 'e'     east
+ 'w'     west
+ 'nw'    northwest
+ 'sw'    southwest
+ 'se'    southeast
+ 'ne'    northeast
+ 'all'   all
+</pre>
+ * {@img Ext.resizer.Resizer/Ext.resizer.Resizer.png Ext.resizer.Resizer component}
+ * <p>Here's an example showing the creation of a typical Resizer:</p>
+ * <pre><code>
+    <div id="elToResize" style="width:200px; height:100px; background-color:#000000;"></div>
+
+    Ext.create('Ext.resizer.Resizer', {
+        el: 'elToResize',
+        handles: 'all',
+        minWidth: 200,
+        minHeight: 100,
+        maxWidth: 500,
+        maxHeight: 400,
+        pinned: true
+    });
+</code></pre>
+*/
+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',
+
+    /**
+     * @cfg {Boolean} dynamic
+     * <p>Specify as true to update the {@link #target} (Element or {@link Ext.Component Component}) dynamically during dragging.
+     * This is <code>true</code> by default, but the {@link Ext.Component Component} class passes <code>false</code> when it
+     * is configured as {@link Ext.Component#resizable}.</p>
+     * <p>If specified as <code>false</code>, a proxy element is displayed during the resize operation, and the {@link #target}
+     * is updated on mouseup.</p>
+     */
+    dynamic: true,
+
+    /**
+     * @cfg {String} handles String consisting of the resize handles to display. Defaults to 's e se' for
+     * Elements and fixed position Components. Defaults to 8 point resizing for floating Components (such as Windows).
+     * Specify either <code>'all'</code> or any of <code>'n s e w ne nw se sw'</code>.
+     */
+    handles: 's e se',
+
+    /**
+     * @cfg {Number} height Optional. The height to set target to in pixels (defaults to null)
+     */
+    height : null,
+
+    /**
+     * @cfg {Number} width Optional. The width to set the target to in pixels (defaults to null)
+     */
+    width : null,
+
+    /**
+     * @cfg {Number} minHeight The minimum height for the element (defaults to 20)
+     */
+    minHeight : 20,
+
+    /**
+     * @cfg {Number} minWidth The minimum width for the element (defaults to 20)
+     */
+    minWidth : 20,
+
+    /**
+     * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
+     */
+    maxHeight : 10000,
+
+    /**
+     * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
+     */
+    maxWidth : 10000,
+
+    /**
+     * @cfg {Boolean} pinned True to ensure that the resize handles are always
+     * visible, false indicates resizing by cursor changes only (defaults to false)
+     */
+    pinned: false,
+
+    /**
+     * @cfg {Boolean} preserveRatio True to preserve the original ratio between height
+     * and width during resize (defaults to false)
+     */
+    preserveRatio: false,
+
+    /**
+     * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
+     */
+    transparent: false,
+
+    /**
+     * @cfg {Mixed} constrainTo Optional. An element, or a {@link Ext.util.Region} into which the resize operation
+     * must be constrained.
+     */
+
+    possiblePositions: {
+        n:  'north',
+        s:  'south',
+        e:  'east',
+        w:  'west',
+        se: 'southeast',
+        sw: 'southwest',
+        nw: 'northwest',
+        ne: 'northeast'
+    },
+
+    /**
+     * @cfg {Mixed} target The Element or Component to resize.
+     */
+
+    /**
+     * Outer element for resizing behavior.
+     * @type Ext.core.Element
+     * @property el
+     */
+
+    constructor: function(config) {
+        var me = this,
+            target,
+            tag,
+            handles = me.handles,
+            handleCls,
+            possibles,
+            len,
+            i = 0,
+            pos;
+
+        this.addEvents(
+            /**
+             * @event beforeresize
+             * Fired before resize is allowed. Return false to cancel resize.
+             * @param {Ext.resizer.Resizer} this
+             * @param {Number} width The start width
+             * @param {Number} height The start height
+             * @param {Ext.EventObject} e The mousedown event
+             */
+            'beforeresize',
+            /**
+             * @event resizedrag
+             * Fires during resizing. Return false to cancel resize.
+             * @param {Ext.resizer.Resizer} this
+             * @param {Number} width The new width
+             * @param {Number} height The new height
+             * @param {Ext.EventObject} e The mousedown event
+             */
+            'resizedrag',
+            /**
+             * @event resize
+             * Fired after a resize.
+             * @param {Ext.resizer.Resizer} this
+             * @param {Number} width The new width
+             * @param {Number} height The new height
+             * @param {Ext.EventObject} e The mouseup event
+             */
+            'resize'
+        );
+
+        if (Ext.isString(config) || Ext.isElement(config) || config.dom) {
+            target = config;
+            config = arguments[1] || {};
+            config.target = target;
+        }
+        // will apply config to this
+        me.mixins.observable.constructor.call(me, config);
+
+        // If target is a Component, ensure that we pull the element out.
+        // Resizer must examine the underlying Element.
+        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);
+            }
+        }
+        // Backwards compatibility with Ext3.x's Resizable which used el as a config.
+        else {
+            me.target = me.el = Ext.get(me.el);
+        }
+
+        // Tags like textarea and img cannot
+        // have children and therefore must
+        // be wrapped
+        tag = me.el.dom.tagName;
+        if (tag == 'TEXTAREA' || tag == 'IMG') {
+            /**
+             * Reference to the original resize target if the element of the original
+             * resize target was an IMG or a TEXTAREA which must be wrapped in a DIV.
+             * @type Mixed
+             * @property originalTarget
+             */
+            me.originalTarget = me.target;
+            me.target = me.el = me.el.wrap({
+                cls: me.wrapCls,
+                id: me.el.id + '-rzwrap'
+            });
+
+            // Transfer originalTarget's positioning/sizing
+            me.el.setPositioning(me.originalTarget.getPositioning());
+            me.originalTarget.clearPositioning();
+            var box = me.originalTarget.getBox();
+            me.el.setBox(box);
+        }
+
+        // Position the element, this enables us to absolute position
+        // the handles within this.el
+        me.el.position();
+        if (me.pinned) {
+            me.el.addCls(me.pinnedCls);
+        }
+
+        /**
+         * @type Ext.resizer.ResizeTracker
+         * @property resizeTracker
+         */
+        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
+        });
+
+        // Relay the ResizeTracker's superclass events as our own resize events
+        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 specified and possible, create
+            if (handles[i] && possibles[handles[i]]) {
+                pos = possibles[handles[i]];
+                // store a reference in this.east, this.west, etc
+
+                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);
+                }
+            }
+        }
+
+        // Constrain within configured maxima
+        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);
+        }
+
+        // Size the element
+        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();
+    },
+
+    /**
+     * @private Relay the Tracker's mousedown event as beforeresize
+     * @param tracker The Resizer
+     * @param e The Event
+     */
+    onBeforeResize: function(tracker, e) {
+        var b = this.target.getBox();
+        return this.fireEvent('beforeresize', this, b.width, b.height, e);
+    },
+
+    /**
+     * @private Relay the Tracker's drag event as resizedrag
+     * @param tracker The Resizer
+     * @param e The Event
+     */
+    onResize: function(tracker, e) {
+        var me = this,
+            b = me.target.getBox();
+        me.forceHandlesHeight();
+        return me.fireEvent('resizedrag', me, b.width, b.height, e);
+    },
+
+    /**
+     * @private Relay the Tracker's dragend event as resize
+     * @param tracker The Resizer
+     * @param e The Event
+     */
+    onResizeEnd: function(tracker, e) {
+        var me = this,
+            b = me.target.getBox();
+        me.forceHandlesHeight();
+        return me.fireEvent('resize', me, b.width, b.height, e);
+    },
+
+    /**
+     * Perform a manual resize and fires the 'resize' event.
+     * @param {Number} width
+     * @param {Number} height
+     */
+    resizeTo : function(width, height){
+        this.target.setSize(width, height);
+        this.fireEvent('resize', this, width, height, null);
+    },
+
+    /**
+     * <p>Returns the element that was configured with the el or target config property.
+     * If a component was configured with the target property then this will return the
+     * element of this component.<p>
+     * <p>Textarea and img elements will be wrapped with an additional div because
+      * these elements do not support child nodes. The original element can be accessed
+     * through the originalTarget property.</p>
+     * @return {Element} element
+     */
+    getEl : function() {
+        return this.el;
+    },
+
+    /**
+     * <p>Returns the element or component that was configured with the target config property.<p>
+     * <p>Textarea and img elements will be wrapped with an additional div because
+      * these elements do not support child nodes. The original element can be accessed
+     * through the originalTarget property.</p>
+     * @return {Element/Component}
+     */
+    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);
+        }
+    },
+
+    /**
+     * @private
+     * Fix IE6 handle height issue.
+     */
+    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();
+        }
+    }
+});
+
+/**
+ * @class Ext.resizer.ResizeTracker
+ * @extends Ext.dd.DragTracker
+ */
+Ext.define('Ext.resizer.ResizeTracker', {
+    extend: 'Ext.dd.DragTracker',
+    dynamic: true,
+    preserveRatio: false,
+
+    // Default to no constraint
+    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);
+
+        // Ensure that if we are preserving aspect ratio, the largest minimum is honoured
+        if (me.preserveRatio && me.minWidth && me.minHeight) {
+            var widthRatio = me.minWidth / me.el.getWidth(),
+                heightRatio = me.minHeight / me.el.getHeight();
+
+            // largest ratio of minimum:size must be preserved.
+            // So if a 400x200 pixel image has
+            // minWidth: 50, maxWidth: 50, the maxWidth will be 400 * (50/200)... that is 100
+            if (heightRatio > widthRatio) {
+                me.minWidth = me.el.getWidth() * heightRatio;
+            } else {
+                me.minHeight = me.el.getHeight() * widthRatio;
+            }
+        }
+
+        // If configured as throttled, create an instance version of resize which calls
+        // a throttled function to perform the resize operation.
+        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) {
+        // record the startBox
+        this.startBox = this.el.getBox();
+    },
+
+    /**
+     * @private
+     * Returns the object that will be resized on every mousemove event.
+     * If dynamic is false, this will be a proxy, otherwise it will be our actual target.
+     */
+    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) {
+        // returns the Ext.ResizeHandle that the user started dragging
+        this.activeResizeHandle = Ext.getCmp(this.getDragTarget().id);
+
+        // If we are using a proxy, ensure it is sized.
+        if (!this.dynamic) {
+            this.resize(this.startBox, {
+                horizontal: 'none',
+                vertical: 'none'
+            });
+        }
+    },
+
+    onDrag: function(e) {
+        // dynamic resizing, update dimensions during resize
+        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; // 1 = x, 2 = y, 3 = x and y.
+
+        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
+        };
+
+        // out of bounds
+        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 this is configured to preserve the aspect ratio, or they are dragging using the shift key
+        if (me.preserveRatio || e.shiftKey) {
+            var newHeight,
+                newWidth;
+
+            ratio = me.startBox.width / me.startBox.height;
+
+            // Calculate aspect ratio constrained values.
+            newHeight = Math.min(Math.max(me.minHeight, newBox.width / ratio), me.maxHeight);
+            newWidth = Math.min(Math.max(me.minWidth, newBox.height * ratio), me.maxWidth);
+
+            // X axis: width-only change, height must obey
+            if (axis == 1) {
+                newBox.height = newHeight;
+            }
+
+            // Y axis: height-only change, width must obey
+            else if (axis == 2) {
+                newBox.width = newWidth;
+            }
+
+            // Corner drag.
+            else {
+                // Drag ratio is the ratio of the mouse point from the opposite corner.
+                // Basically what edge we are dragging, a horizontal edge or a vertical edge.
+                dragRatio = Math.abs(oppositeCorner[0] - this.lastXY[0]) / Math.abs(oppositeCorner[1] - this.lastXY[1]);
+
+                // If drag ratio > aspect ratio then width is dominant and height must obey
+                if (dragRatio > ratio) {
+                    newBox.height = newHeight;
+                } else {
+                    newBox.width = newWidth;
+                }
+
+                // Handle dragging start coordinates
+                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);
+            // update the originalTarget if this was wrapped.
+            if (this.originalTarget) {
+                this.originalTarget.setBox(box);
+            }
+        }
+    },
+
+    onEnd: function(e) {
+        this.updateDimensions(e, true);
+        if (this.proxy) {
+            this.proxy.hide();
+        }
+    }
+});
+
+/**
+ * @class Ext.resizer.SplitterTracker
+ * @extends Ext.dd.DragTracker
+ * Private utility class for Ext.Splitter.
+ * @private
+ */
+Ext.define('Ext.resizer.SplitterTracker', {
+    extend: 'Ext.dd.DragTracker',
+    requires: ['Ext.util.Region'],
+    enabled: true,
+
+    getPrevCmp: function() {
+        var splitter = this.getSplitter();
+        return splitter.previousSibling();
+    },
+
+    getNextCmp: function() {
+        var splitter = this.getSplitter();
+        return splitter.nextSibling();
+    },
+
+    // ensure the tracker is enabled, store boxes of previous and next
+    // components and calculate the constrain region
+    onBeforeStart: function(e) {
+        var prevCmp = this.getPrevCmp(),
+            nextCmp = this.getNextCmp();
+
+        // SplitterTracker is disabled if any of its adjacents are collapsed.
+        if (nextCmp.collapsed || prevCmp.collapsed) {
+            return false;
+        }
+        // store boxes of previous and next
+        this.prevBox  = prevCmp.getEl().getBox();
+        this.nextBox  = nextCmp.getEl().getBox();
+        this.constrainTo = this.calculateConstrainRegion();
+    },
+
+    // We move the splitter el. Add the proxy class.
+    onStart: function(e) {
+        var splitter = this.getSplitter();
+        splitter.addCls(splitter.baseCls + '-active');
+    },
+
+    // calculate the constrain Region in which the splitter el may be moved.
+    calculateConstrainRegion: function() {
+        var splitter   = this.getSplitter(),
+            topPad     = 0,
+            bottomPad  = 0,
+            splitWidth = splitter.getWidth(),
+            defaultMin = splitter.defaultSplitMin,
+            orient     = splitter.orientation,
+            prevBox    = this.prevBox,
+            prevCmp    = this.getPrevCmp(),
+            nextBox    = this.nextBox,
+            nextCmp    = this.getNextCmp(),
+            // prev and nextConstrainRegions are the maximumBoxes minus the
+            // minimumBoxes. The result is always the intersection
+            // of these two boxes.
+            prevConstrainRegion, nextConstrainRegion;
+
+        // vertical splitters, so resizing left to right
+        if (orient === 'vertical') {
+
+            // Region constructor accepts (top, right, bottom, left)
+            // anchored/calculated from the left
+            prevConstrainRegion = Ext.create('Ext.util.Region',
+                prevBox.y,
+                // Right boundary is x + maxWidth if there IS a maxWidth.
+                // Otherwise it is calculated based upon the minWidth of the next Component
+                (prevCmp.maxWidth ? prevBox.x + prevCmp.maxWidth : nextBox.right - (nextCmp.minWidth || defaultMin)) + splitWidth,
+                prevBox.bottom,
+                prevBox.x + (prevCmp.minWidth || defaultMin)
+            );
+            // anchored/calculated from the right
+            nextConstrainRegion = Ext.create('Ext.util.Region',
+                nextBox.y,
+                nextBox.right - (nextCmp.minWidth || defaultMin),
+                nextBox.bottom,
+                // Left boundary is right - maxWidth if there IS a maxWidth.
+                // Otherwise it is calculated based upon the minWidth of the previous Component
+                (nextCmp.maxWidth ? nextBox.right - nextCmp.maxWidth : prevBox.x + (prevBox.minWidth || defaultMin)) - splitWidth
+            );
+        } else {
+            // anchored/calculated from the top
+            prevConstrainRegion = Ext.create('Ext.util.Region',
+                prevBox.y + (prevCmp.minHeight || defaultMin),
+                prevBox.right,
+                // Bottom boundary is y + maxHeight if there IS a maxHeight.
+                // Otherwise it is calculated based upon the minWidth of the next Component
+                (prevCmp.maxHeight ? prevBox.y + prevCmp.maxHeight : nextBox.bottom - (nextCmp.minHeight || defaultMin)) + splitWidth,
+                prevBox.x
+            );
+            // anchored/calculated from the bottom
+            nextConstrainRegion = Ext.create('Ext.util.Region',
+                // Top boundary is bottom - maxHeight if there IS a maxHeight.
+                // Otherwise it is calculated based upon the minHeight of the previous Component
+                (nextCmp.maxHeight ? nextBox.bottom - nextCmp.maxHeight : prevBox.y + (prevCmp.minHeight || defaultMin)) - splitWidth,
+                nextBox.right,
+                nextBox.bottom - (nextCmp.minHeight || defaultMin),
+                nextBox.x
+            );
+        }
+
+        // intersection of the two regions to provide region draggable
+        return  prevConstrainRegion.intersect(nextConstrainRegion);
+    },
+
+    // Performs the actual resizing of the previous and next components
+    performResize: function(e) {
+        var offset   = this.getOffset('dragTarget'),
+            splitter = this.getSplitter(),
+            orient   = splitter.orientation,
+            prevCmp  = this.getPrevCmp(),
+            nextCmp  = this.getNextCmp(),
+            owner    = splitter.ownerCt,
+            layout   = owner.getLayout();
+
+        // Inhibit automatic container layout caused by setSize calls below.
+        owner.suspendLayout = true;
+
+        if (orient === 'vertical') {
+            if (prevCmp) {
+                if (!prevCmp.maintainFlex) {
+                    delete prevCmp.flex;
+                    prevCmp.setSize(this.prevBox.width + offset[0], prevCmp.getHeight());
+                }
+            }
+            if (nextCmp) {
+                if (!nextCmp.maintainFlex) {
+                    delete nextCmp.flex;
+                    nextCmp.setSize(this.nextBox.width - offset[0], nextCmp.getHeight());
+                }
+            }
+        // verticals
+        } else {
+            if (prevCmp) {
+                if (!prevCmp.maintainFlex) {
+                    delete prevCmp.flex;
+                    prevCmp.setSize(prevCmp.getWidth(), this.prevBox.height + offset[1]);
+                }
+            }
+            if (nextCmp) {
+                if (!nextCmp.maintainFlex) {
+                    delete nextCmp.flex;
+                    nextCmp.setSize(prevCmp.getWidth(), this.nextBox.height - offset[1]);
+                }
+            }
+        }
+        delete owner.suspendLayout;
+        layout.onLayout();
+    },
+
+    // perform the resize and remove the proxy class from the splitter el
+    onEnd: function(e) {
+        var splitter = this.getSplitter();
+        splitter.removeCls(splitter.baseCls + '-active');
+        this.performResize();
+    },
+
+    // Track the proxy and set the proper XY coordinates
+    // while constraining the drag
+    onDrag: function(e) {
+        var offset    = this.getOffset('dragTarget'),
+            splitter  = this.getSplitter(),
+            splitEl   = splitter.getEl(),
+            orient    = splitter.orientation;
+
+        if (orient === "vertical") {
+            splitEl.setX(this.startRegion.left + offset[0]);
+        } else {
+            splitEl.setY(this.startRegion.top + offset[1]);
+        }
+    },
+
+    getSplitter: function() {
+        return Ext.getCmp(this.getDragCt().id);
+    }
+});
+/**
+ * @class Ext.selection.CellModel
+ * @extends Ext.selection.Model
+ * @private
+ */
+Ext.define('Ext.selection.CellModel', {
+    extend: 'Ext.selection.Model',
+    alias: 'selection.cellmodel',
+    requires: ['Ext.util.KeyNav'],
+    
+    /**
+     * @cfg {Boolean} enableKeyNav
+     * Turns on/off keyboard navigation within the grid. Defaults to true.
+     */
+    enableKeyNav: true,
+    
+    /**
+     * @cfg {Boolean} preventWrap
+     * Set this configuration to true to prevent wrapping around of selection as
+     * a user navigates to the first or last column. Defaults to false.
+     */
+    preventWrap: false,
+
+    constructor: function(){
+        this.addEvents(
+            /**
+             * @event deselect
+             * Fired after a cell is deselected
+             * @param {Ext.selection.CellModel} this
+             * @param {Ext.data.Model} record The record of the deselected cell
+             * @param {Number} row The row index deselected
+             * @param {Number} column The column index deselected
+             */
+            'deselect',
+            
+            /**
+             * @event select
+             * Fired after a cell is selected
+             * @param {Ext.selection.CellModel} this
+             * @param {Ext.data.Model} record The record of the selected cell
+             * @param {Number} row The row index selected
+             * @param {Number} column The column index selected
+             */
+            '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
+        });
+
+        // view.el has tabIndex -1 to allow for
+        // keyboard events to be passed to it.
+        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;
+    },
+
+    /**
+     * Returns the current position in the format {row: row, column: column}
+     */
+    getCurrentPosition: function() {
+        return this.position;
+    },
+    
+    /**
+     * Sets the current position
+     * @param {Object} position The position to set.
+     */
+    setCurrentPosition: function(pos) {
+        var me = this;
+        
+        if (me.position) {
+            me.onCellDeselect(me.position);
+        }
+        if (pos) {
+            me.onCellSelect(pos);
+        }
+        me.position = pos;
+    },
+
+    /**
+     * Set the current position based on where the user clicks.
+     * @private
+     */
+    onMouseDown: function(view, cell, cellIndex, record, row, rowIndex, e) {
+        this.setCurrentPosition({
+            row: rowIndex,
+            column: cellIndex
+        });
+    },
+
+    // notify the view that the cell has been selected to update the ui
+    // appropriately and bring the cell into focus
+    onCellSelect: function(position) {
+        var me = this,
+            store = me.view.getStore(),
+            record = store.getAt(position.row);
+
+        me.doSelect(record);
+        me.primaryView.onCellSelect(position);
+        // TODO: Remove temporary cellFocus call here.
+        me.primaryView.onCellFocus(position);
+        me.fireEvent('select', me, record, position.row, position.column);
+    },
+
+    // notify view that the cell has been deselected to update the ui
+    // appropriately
+    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);
+    }
+});
+/**
+ * @class Ext.selection.RowModel
+ * @extends Ext.selection.Model
+ * 
+ * Implement row based navigation via keyboard.
+ *
+ * Must synchronize across grid sections
+ */
+Ext.define('Ext.selection.RowModel', {
+    extend: 'Ext.selection.Model',
+    alias: 'selection.rowmodel',
+    requires: ['Ext.util.KeyNav'],
+    
+    /**
+     * @private
+     * Number of pixels to scroll to the left/right when pressing
+     * left/right keys.
+     */
+    deltaScroll: 5,
+    
+    /**
+     * @cfg {Boolean} enableKeyNav
+     * 
+     * Turns on/off keyboard navigation within the grid. Defaults to true.
+     */
+    enableKeyNav: true,
+    
+    constructor: function(){
+        this.addEvents(
+            /**
+             * @event deselect
+             * Fired after a record is deselected
+             * @param {Ext.selection.RowSelectionModel} this
+             * @param {Ext.data.Model} record The deselected record
+             * @param {Number} index The row index deselected
+             */
+            'deselect',
+            
+            /**
+             * @event select
+             * Fired after a record is selected
+             * @param {Ext.selection.RowSelectionModel} this
+             * @param {Ext.data.Model} record The selected record
+             * @param {Number} index The row index selected
+             */
+            '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
+        });
+
+        // view.el has tabIndex -1 to allow for
+        // keyboard events to be passed to it.
+        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);
+    },
+
+    // Returns the number of rows currently visible on the screen or
+    // false if there were no rows. This assumes that all rows are
+    // of the same height and the first view is accurate.
+    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;
+    },
+
+    // go to last visible record in grid.
+    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);
+            }
+        }
+    },
+
+    // go to first visible record in grid.
+    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);
+            }
+        }
+    },
+
+    // Go one page up from the lastFocused record in the grid.
+    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);
+            }
+
+        }
+    },
+
+    // Go one page down from the lastFocused record in the grid.
+    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) {
+                // some browsers, this means go thru browser tabs
+                // attempt to stop.
+                e.preventDefault();
+                me.setLastFocused(nextRecord);
+            } else {
+                me.doSelect(nextRecord);
+            }
+        }
+    },
+
+    // Select/Deselect based on pressing Spacebar.
+    // Assumes a SIMPLE selectionmode style
+    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);
+                }
+            }
+        }
+    },
+
+    // Navigate one record up. This could be a selection or
+    // could be simply focusing a record for discontiguous
+    // selection. Provides bounds checking.
+    onKeyUp: function(e, t) {
+        var me = this,
+            view = me.views[0],
+            idx  = me.store.indexOf(me.lastFocused),
+            record;
+            
+        if (idx > 0) {
+            // needs to be the filtered count as thats what
+            // will be visible.
+            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);
+                //view.focusRow(idx - 1);
+            }
+        }
+        // There was no lastFocused record, and the user has pressed up
+        // Ignore??
+        //else if (this.selected.getCount() == 0) {
+        //    
+        //    this.doSelect(record);
+        //    //view.focusRow(idx - 1);
+        //}
+    },
+
+    // Navigate one record down. This could be a selection or
+    // could be simply focusing a record for discontiguous
+    // selection. Provides bounds checking.
+    onKeyDown: function(e, t) {
+        var me = this,
+            view = me.views[0],
+            idx  = me.store.indexOf(me.lastFocused),
+            record;
+            
+        // needs to be the filtered count as thats what
+        // will be visible.
+        if (idx + 1 < me.store.getCount()) {
+            record = me.store.getAt(idx + 1);
+            if (me.selected.getCount() === 0) {
+                me.doSelect(record);
+                //view.focusRow(idx + 1);
+            } 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);
+                //view.focusRow(idx + 1);
+            }
+        }
+    },
+    
+    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);
+    },
+
+    // Select the record with the event included so that
+    // we can take into account ctrlKey, shiftKey, etc
+    onRowMouseDown: function(view, record, item, index, e) {
+        view.el.focus();
+        this.selectWithEvent(record, e);
+    },
+
+    // Allow the GridView to update the UI by
+    // adding/removing a CSS class from the row.
+    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);
+                }
+            }
+        }
+    },
+
+    // Provide indication of what row was last focused via
+    // the gridview.
+    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);
+    }
+});
+/**
+ * @class Ext.selection.CheckboxModel
+ * @extends Ext.selection.RowModel
+ *
+ * A selection model that renders a column of checkboxes that can be toggled to
+ * select or deselect rows. The default mode for this selection model is MULTI.
+ *
+ * The selection model will inject a header for the checkboxes in the first view
+ * and according to the 'injectCheckbox' configuration.
+ */
+Ext.define('Ext.selection.CheckboxModel', {
+    extend: 'Ext.selection.RowModel',
+
+    /**
+     * @cfg {String} mode
+     * Modes of selection.
+     * Valid values are SINGLE, SIMPLE, and MULTI. Defaults to 'MULTI'
+     */
+    mode: 'MULTI',
+
+    /**
+     * @cfg {Mixed} injectCheckbox
+     * Instructs the SelectionModel whether or not to inject the checkbox header
+     * automatically or not. (Note: By not placing the checkbox in manually, the
+     * grid view will need to be rendered 2x on initial render.)
+     * Supported values are a Number index, false and the strings 'first' and 'last'.
+     * Default is 0.
+     */
+    injectCheckbox: 0,
+
+    /**
+     * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
+     * checkbox column (defaults to <tt>false</tt>).
+     */
+    checkOnly: false,
+
+    // private
+    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);
+    },
+
+    /**
+     * Toggle the ui header between checked and unchecked state.
+     * @param {Boolean} isChecked
+     * @private
+     */
+    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);
+            }
+        }
+    },
+
+    /**
+     * Toggle between selecting all and deselecting all when clicking on
+     * a checkbox header.
+     */
+    onHeaderClick: function(headerCt, header, e) {
+        if (header.isCheckerHd) {
+            e.stopEvent();
+            var isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
+            if (isChecked) {
+                // We have to supress the event or it will scrollTo the change
+                this.deselectAll(true);
+            } else {
+                // We have to supress the event or it will scrollTo the change
+                this.selectAll(true);
+            }
+        }
+    },
+
+    /**
+     * Retrieve a configuration to be used in a HeaderContainer.
+     * This should be used when injectCheckbox is set to false.
+     */
+    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)
+        };
+    },
+
+    /**
+     * Generates the HTML to be rendered in the injected checkbox column for each row.
+     * Creates the standard checkbox markup by default; can be overridden to provide custom rendering.
+     * See {@link Ext.grid.column.Column#renderer} for description of allowed parameters.
+     */
+    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>';
+    },
+
+    // override
+    onRowMouseDown: function(view, record, item, index, e) {
+        view.el.focus();
+        var me = this,
+            checker = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-checker');
+
+        // checkOnly set, but we didn't click on a checker.
+        if (me.checkOnly && !checker) {
+            return;
+        }
+
+        if (checker) {
+            var mode = me.getSelectionMode();
+            // dont change the mode if its single otherwise
+            // we would get multiple selection
+            if (mode !== 'SINGLE') {
+                me.setSelectionMode('SIMPLE');
+            }
+            me.selectWithEvent(record, e);
+            me.setSelectionMode(mode);
+        } else {
+            me.selectWithEvent(record, e);
+        }
+    },
+
+    /**
+     * Synchronize header checker value as selection changes.
+     * @private
+     */
+    onSelectChange: function(record, isSelected) {
+        this.callParent([record, isSelected]);
+        // check to see if all records are selected
+        var hdSelectStatus = this.selected.getCount() === this.store.getCount();
+        this.toggleUiHeader(hdSelectStatus);
+    }
+});
+
+/**
+ * @class Ext.selection.TreeModel
+ * @extends Ext.selection.RowModel
+ *
+ * Adds custom behavior for left/right keyboard navigation for use with a tree.
+ * Depends on the view having an expand and collapse method which accepts a
+ * record.
+ * 
+ * @private
+ */
+Ext.define('Ext.selection.TreeModel', {
+    extend: 'Ext.selection.RowModel',
+    alias: 'selection.treemodel',
+    
+    // typically selection models prune records from the selection
+    // model when they are removed, because the TreeView constantly
+    // adds/removes records as they are expanded/collapsed
+    pruneRemoved: false,
+    
+    onKeyRight: function(e, t) {
+        var focused = this.getLastFocused(),
+            view    = this.view;
+            
+        if (focused) {
+            // tree node is already expanded, go down instead
+            // this handles both the case where we navigate to firstChild and if
+            // there are no children to the nextSibling
+            if (focused.isExpanded()) {
+                this.onKeyDown(e, t);
+            // if its not a leaf node, expand it
+            } 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 node is already expanded, collapse it
+            if (focused.isExpanded()) {
+                view.collapse(focused);
+            // has a parentNode and its not root
+            // TODO: this needs to cover the case where the root isVisible
+            } else if (parentNode && !parentNode.isRoot()) {
+                // Select a range of records when doing multiple selection.
+                if (e.shiftKey) {
+                    viewSm.selectRange(parentNode, focused, e.ctrlKey, 'up');
+                    viewSm.setLastFocused(parentNode);
+                // just move focus, not selection
+                } else if (e.ctrlKey) {
+                    viewSm.setLastFocused(parentNode);
+                // select it
+                } 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);
+        }
+    }
+});
+
+/**
+ * @private
+ * @class Ext.slider.Thumb
+ * @extends Ext.Base
+ * @private
+ * Represents a single thumb element on a Slider. This would not usually be created manually and would instead
+ * be created internally by an {@link Ext.slider.Multi Ext.Slider}.
+ */
+Ext.define('Ext.slider.Thumb', {
+    requires: ['Ext.dd.DragTracker', 'Ext.util.Format'],
+    /**
+     * @private
+     * @property topThumbZIndex
+     * @type Number
+     * The number used internally to set the z index of the top thumb (see promoteThumb for details)
+     */
+    topZIndex: 10000,
+    /**
+     * @constructor
+     * @cfg {Ext.slider.MultiSlider} slider The Slider to render to (required)
+     */
+    constructor: function(config) {
+        var me = this;
+        
+        /**
+         * @property slider
+         * @type Ext.slider.MultiSlider
+         * The slider this thumb is contained within
+         */
+        Ext.apply(me, config || {}, {
+            cls: Ext.baseCSSPrefix + 'slider-thumb',
+
+            /**
+             * @cfg {Boolean} constrain True to constrain the thumb so that it cannot overlap its siblings
+             */
+            constrain: false
+        });
+        me.callParent([config]);
+
+        if (me.slider.vertical) {
+            Ext.apply(me, Ext.slider.Thumb.Vertical);
+        }
+    },
+
+    /**
+     * Renders the thumb into a slider
+     */
+    render: function() {
+        var me = this;
+        
+        me.el = me.slider.innerEl.insertFirst({cls: me.cls});
+        if (me.disabled) {
+            me.disable();
+        }
+        me.initEvents();
+    },
+    
+    /**
+     * @private
+     * move the thumb
+     */
+    move: function(v, animate){
+        if(!animate){
+            this.el.setLeft(v);
+        }else{
+            Ext.create('Ext.fx.Anim', {
+                target: this.el,
+                duration: 350,
+                to: {
+                    left: v
+                }
+            });
+        }
+    },
+
+    /**
+     * @private
+     * Bring thumb dom element to front.
+     */
+    bringToFront: function() {
+        this.el.setStyle('zIndex', this.topZIndex);
+    },
+    
+    /**
+     * @private
+     * Send thumb dom element to back.
+     */
+    sendToBack: function() {
+        this.el.setStyle('zIndex', '');
+    },
+    
+    /**
+     * Enables the thumb if it is currently disabled
+     */
+    enable: function() {
+        var me = this;
+        
+        me.disabled = false;
+        if (me.el) {
+            me.el.removeCls(me.slider.disabledCls);
+        }
+    },
+
+    /**
+     * Disables the thumb if it is currently enabled
+     */
+    disable: function() {
+        var me = this;
+        
+        me.disabled = true;
+        if (me.el) {
+            me.el.addCls(me.slider.disabledCls);
+        }
+    },
+
+    /**
+     * Sets up an Ext.dd.DragTracker for this thumb
+     */
+    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);
+    },
+
+    /**
+     * @private
+     * This is tied into the internal Ext.dd.DragTracker. If the slider is currently disabled,
+     * this returns false to disable the DragTracker too.
+     * @return {Boolean} False if the slider is currently disabled
+     */
+    onBeforeDragStart : function(e) {
+        if (this.disabled) {
+            return false;
+        } else {
+            this.slider.promoteThumb(this);
+            return true;
+        }
+    },
+
+    /**
+     * @private
+     * This is tied into the internal Ext.dd.DragTracker's onStart template method. Adds the drag CSS class
+     * to the thumb and fires the 'dragstart' event
+     */
+    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);
+    },
+
+    /**
+     * @private
+     * This is tied into the internal Ext.dd.DragTracker's onDrag template method. This is called every time
+     * the DragTracker detects a drag movement. It updates the Slider's value using the position of the drag
+     */
+    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);
+    },
+
+    /**
+     * @private
+     * This is tied to the internal Ext.dd.DragTracker's onEnd template method. Removes the drag CSS class and
+     * fires the 'changecomplete' event with the new value
+     */
+    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: {
+        // Method overrides to support vertical dragging of thumb within slider
+        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
+                        }
+                    });
+                }
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.slider.Tip
+ * @extends Ext.tip.Tip
+ * Simple plugin for using an Ext.tip.Tip with a slider to show the slider value. In general this
+ * class is not created directly, instead pass the {@link Ext.slider.Multi#useTips} and 
+ * {@link Ext.slider.Multi#tipText} configuration options to the slider directly.
+ * {@img Ext.slider.Tip/Ext.slider.Tip1.png Ext.slider.Tip component}
+ * Example usage:
+<pre>
+    Ext.create('Ext.slider.Single', {
+        width: 214,
+        minValue: 0,
+        maxValue: 100,
+        useTips: true,
+        renderTo: Ext.getBody()
+    });   
+</pre>
+ * Optionally provide your own tip text by passing tipText:
+ <pre>
+ new Ext.slider.Single({
+     width: 214,
+     minValue: 0,
+     maxValue: 100,
+     useTips: true,
+     tipText: function(thumb){
+         return Ext.String.format('<b>{0}% complete</b>', thumb.value);
+     }
+ });
+ </pre>
+ * @xtype slidertip
+ */
+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
+        });
+    },
+    /**
+     * @private
+     * Called whenever a dragstart or drag event is received on the associated Thumb. 
+     * Aligns the Tip with the Thumb's new position.
+     * @param {Ext.slider.MultiSlider} slider The slider
+     * @param {Ext.EventObject} e The Event object
+     * @param {Ext.slider.Thumb} thumb The thumb that the Tip is attached to
+     */
+    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);
+    },
+
+    /**
+     * Used to create the text that appears in the Tip's body. By default this just returns
+     * the value of the Slider Thumb that the Tip is attached to. Override to customize.
+     * @param {Ext.slider.Thumb} thumb The Thumb that the Tip is attached to
+     * @return {String} The text to display in the tip
+     */
+    getText : function(thumb) {
+        return String(thumb.value);
+    }
+});
+/**
+ * @class Ext.slider.Multi
+ * @extends Ext.form.field.Base
+ * <p>Slider which supports vertical or horizontal orientation, keyboard adjustments, configurable snapping, axis
+ * clicking and animation. Can be added as an item to any container. In addition,  
+ * {@img Ext.slider.Multi/Ext.slider.Multi.png Ext.slider.Multi component}
+ * <p>Example usage:</p>
+ * Sliders can be created with more than one thumb handle by passing an array of values instead of a single one:
+<pre>
+    Ext.create('Ext.slider.Multi', {
+        width: 200,
+        values: [25, 50, 75],
+        increment: 5,
+        minValue: 0,
+        maxValue: 100,
+
+        //this defaults to true, setting to false allows the thumbs to pass each other
+        {@link #constrainThumbs}: false,
+        renderTo: Ext.getBody()
+    });  
+</pre>
+ * @xtype multislider
+ */
+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
+        }
+    ],
+
+    /**
+     * @cfg {Number} value
+     * A value with which to initialize the slider. Defaults to minValue. Setting this will only
+     * result in the creation of a single slider thumb; if you want multiple thumbs then use the
+     * {@link #values} config instead.
+     */
+
+    /**
+     * @cfg {Array} values
+     * Array of Number values with which to initalize the slider. A separate slider thumb will be created for
+     * each value in this array. This will take precedence over the single {@link #value} config.
+     */
+
+    /**
+     * @cfg {Boolean} vertical Orient the Slider vertically rather than horizontally, defaults to false.
+     */
+    vertical: false,
+    /**
+     * @cfg {Number} minValue The minimum value for the Slider. Defaults to 0.
+     */
+    minValue: 0,
+    /**
+     * @cfg {Number} maxValue The maximum value for the Slider. Defaults to 100.
+     */
+    maxValue: 100,
+    /**
+     * @cfg {Number/Boolean} decimalPrecision.
+     * <p>The number of decimal places to which to round the Slider's value. Defaults to 0.</p>
+     * <p>To disable rounding, configure as <tt><b>false</b></tt>.</p>
+     */
+    decimalPrecision: 0,
+    /**
+     * @cfg {Number} keyIncrement How many units to change the Slider when adjusting with keyboard navigation. Defaults to 1. If the increment config is larger, it will be used instead.
+     */
+    keyIncrement: 1,
+    /**
+     * @cfg {Number} increment How many units to change the slider when adjusting by drag and drop. Use this option to enable 'snapping'.
+     */
+    increment: 0,
+
+    /**
+     * @private
+     * @property clickRange
+     * @type Array
+     * Determines whether or not a click to the slider component is considered to be a user request to change the value. Specified as an array of [top, bottom],
+     * the click event's 'top' property is compared to these numbers and the click only considered a change request if it falls within them. e.g. if the 'top'
+     * value of the click event is 4 or 16, the click is not considered a change request as it falls outside of the [5, 15] range
+     */
+    clickRange: [5,15],
+
+    /**
+     * @cfg {Boolean} clickToChange Determines whether or not clicking on the Slider axis will change the slider. Defaults to true
+     */
+    clickToChange : true,
+    /**
+     * @cfg {Boolean} animate Turn on or off animation. Defaults to true
+     */
+    animate: true,
+
+    /**
+     * True while the thumb is in a drag operation
+     * @type Boolean
+     */
+    dragging: false,
+
+    /**
+     * @cfg {Boolean} constrainThumbs True to disallow thumbs from overlapping one another. Defaults to true
+     */
+    constrainThumbs: true,
+
+    componentLayout: 'sliderfield',
+
+    /**
+     * @cfg {Boolean} useTips
+     * True to use an Ext.slider.Tip to display tips for the value. Defaults to <tt>true</tt>.
+     */
+    useTips : true,
+
+    /**
+     * @cfg {Function} tipText
+     * A function used to display custom text for the slider tip. Defaults to <tt>null</tt>, which will
+     * use the default on the plugin.
+     */
+    tipText : null,
+
+    ariaRole: 'slider',
+
+    // private override
+    initValue: function() {
+        var me = this,
+            extValue = Ext.value,
+            // Fallback for initial values: values config -> value config -> minValue config -> 0
+            values = extValue(me.values, [extValue(me.value, extValue(me.minValue, 0))]),
+            i = 0,
+            len = values.length;
+
+        // Store for use in dirty check
+        me.originalValue = values;
+
+        // Add a thumb for each value
+        for (; i < len; i++) {
+            me.addThumb(values[i]);
+        }
+    },
+
+    // private override
+    initComponent : function() {
+        var me = this,
+            tipPlug,
+            hasTip;
+        
+        /**
+         * @property thumbs
+         * @type Array
+         * Array containing references to each thumb
+         */
+        me.thumbs = [];
+
+        me.keyIncrement = Math.max(me.increment, me.keyIncrement);
+
+        me.addEvents(
+            /**
+             * @event beforechange
+             * Fires before the slider value is changed. By returning false from an event handler,
+             * you can cancel the event and prevent the slider from changing.
+             * @param {Ext.slider.Multi} slider The slider
+             * @param {Number} newValue The new value which the slider is being changed to.
+             * @param {Number} oldValue The old value which the slider was previously.
+             */
+            'beforechange',
+
+            /**
+             * @event change
+             * Fires when the slider value is changed.
+             * @param {Ext.slider.Multi} slider The slider
+             * @param {Number} newValue The new value which the slider has been changed to.
+             * @param {Ext.slider.Thumb} thumb The thumb that was changed
+             */
+            'change',
+
+            /**
+             * @event changecomplete
+             * Fires when the slider value is changed by the user and any drag operations have completed.
+             * @param {Ext.slider.Multi} slider The slider
+             * @param {Number} newValue The new value which the slider has been changed to.
+             * @param {Ext.slider.Thumb} thumb The thumb that was changed
+             */
+            'changecomplete',
+
+            /**
+             * @event dragstart
+             * Fires after a drag operation has started.
+             * @param {Ext.slider.Multi} slider The slider
+             * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
+             */
+            'dragstart',
+
+            /**
+             * @event drag
+             * Fires continuously during the drag operation while the mouse is moving.
+             * @param {Ext.slider.Multi} slider The slider
+             * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
+             */
+            'drag',
+
+            /**
+             * @event dragend
+             * Fires after the drag operation has completed.
+             * @param {Ext.slider.Multi} slider The slider
+             * @param {Ext.EventObject} e The event fired from Ext.dd.DragTracker
+             */
+            'dragend'
+        );
+
+        if (me.vertical) {
+            Ext.apply(me, Ext.slider.Multi.Vertical);
+        }
+
+        me.callParent();
+
+        // only can use it if it exists.
+        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));
+            }
+        }
+    },
+
+    /**
+     * Creates a new thumb and adds it to the slider
+     * @param {Number} value The initial value to set on the thumb. Defaults to 0
+     * @return {Ext.slider.Thumb} The thumb
+     */
+    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);
+
+        //render the thumb now if needed
+        if (me.rendered) {
+            thumb.render();
+        }
+
+        return thumb;
+    },
+
+    /**
+     * @private
+     * Moves the given thumb above all other by increasing its z-index. This is called when as drag
+     * any thumb, so that the thumb that was just dragged is always at the highest z-index. This is
+     * required when the thumbs are stacked on top of each other at one of the ends of the slider's
+     * range, which can result in the user not being able to move any of them.
+     * @param {Ext.slider.Thumb} topThumb The thumb to move to the top
+     */
+    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();
+            }
+        }
+    },
+
+    // private override
+    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);
+
+        //render each thumb
+        for (; i < len; i++) {
+            thumbs[i].render();
+        }
+
+        //calculate the size of half a thumb
+        thumb = me.innerEl.down('.' + Ext.baseCSSPrefix + 'slider-thumb');
+        me.halfThumb = (me.vertical ? thumb.getHeight() : thumb.getWidth()) / 2;
+
+    },
+
+    /**
+     * Utility method to set the value of the field when the slider changes.
+     * @param {Object} slider The slider object.
+     * @param {Object} v The new value.
+     * @private
+     */
+    onChange : function(slider, v) {
+        this.setValue(v, undefined, true);
+    },
+
+    /**
+     * @private
+     * Adds keyboard and mouse listeners on this.el. Ignores click events on the internal focus element.
+     */
+    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);
+    },
+
+    /**
+     * @private
+     * Mousedown handler for the slider. If the clickToChange is enabled and the click was not on the draggable 'thumb',
+     * this calculates the new value of the slider and tells the implementation (Horizontal or Vertical) to move the thumb
+     * @param {Ext.EventObject} e The click event
+     */
+    onMouseDown : function(e) {
+        var me = this,
+            thumbClicked = false,
+            i = 0,
+            thumbs = me.thumbs,
+            len = thumbs.length,
+            local;
+            
+        if (me.disabled) {
+            return;
+        }
+
+        //see if the click was on any of the thumbs
+        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();
+    },
+
+    /**
+     * @private
+     * Moves the thumb to the indicated position. Note that a Vertical implementation is provided in Ext.slider.Multi.Vertical.
+     * Only changes the value if the click was within this.clickRange.
+     * @param {Object} local Object containing top and left values for the click event.
+     */
+    onClickChange : function(local) {
+        var me = this,
+            thumb, index;
+            
+        if (local.top > me.clickRange[0] && local.top < me.clickRange[1]) {
+            //find the nearest thumb to the click event
+            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);
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Returns the nearest thumb to a click event, along with its distance
+     * @param {Object} local Object containing top and left values from a click event
+     * @param {String} prop The property of local to compare on. Use 'left' for horizontal sliders, 'top' for vertical ones
+     * @return {Object} The closest thumb object and its distance from the click event
+     */
+    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, //add a small fudge for the end of the slider
+            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;
+    },
+
+    /**
+     * @private
+     * Handler for any keypresses captured by the slider. If the key is UP or RIGHT, the thumb is moved along to the right
+     * by this.keyIncrement. If DOWN or LEFT it is moved left. Pressing CTRL moves the slider to the end in either direction
+     * @param {Ext.EventObject} e The Event object
+     */
+    onKeyDown : function(e) {
+        /*
+         * The behaviour for keyboard handling with multiple thumbs is currently undefined.
+         * There's no real sane default for it, so leave it like this until we come up
+         * with a better way of doing it.
+         */
+        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();
+        }
+    },
+
+    /**
+     * @private
+     * If using snapping, this takes a desired new value and returns the closest snapped
+     * value to it
+     * @param {Number} value The unsnapped value
+     * @return {Number} The value of the nearest snap target
+     */
+    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);
+    },
+
+    // private
+    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) {
+                    // delete this.value;
+                    me.setValue(i, v, false);
+                } else {
+                    thumb.move(me.translateValue(v), false);
+                }
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Returns the ratio of pixels to mapped values. e.g. if the slider is 200px wide and maxValue - minValue is 100,
+     * the ratio is 2
+     * @return {Number} The ratio of pixels to mapped values
+     */
+    getRatio : function() {
+        var w = this.innerEl.getWidth(),
+            v = this.maxValue - this.minValue;
+        return v === 0 ? w : (w/v);
+    },
+
+    /**
+     * @private
+     * Returns a snapped, constrained value when given a desired value
+     * @param {Number} value Raw number value
+     * @return {Number} The raw value rounded to the correct d.p. and constrained within the set max and min values
+     */
+    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;
+    },
+
+    /**
+     * Sets the minimum value for the slider instance. If the current value is less than the
+     * minimum value, the current value will be changed.
+     * @param {Number} val The new minimum value
+     */
+    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();
+    },
+
+    /**
+     * Sets the maximum value for the slider instance. If the current value is more than the
+     * maximum value, the current value will be changed.
+     * @param {Number} val The new maximum value
+     */
+    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();
+    },
+
+    /**
+     * Programmatically sets the value of the Slider. Ensures that the value is constrained within
+     * the minValue and maxValue.
+     * @param {Number} index Index of the thumb to move
+     * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
+     * @param {Boolean} animate Turn on or off animation, defaults to true
+     */
+    setValue : function(index, value, animate, changeComplete) {
+        var me = this,
+            thumb = me.thumbs[index];
+
+        // ensures value is contstrained and snapped
+        value = me.normalizeValue(value);
+
+        if (value !== thumb.value && me.fireEvent('beforechange', me, value, thumb.value, thumb) !== false) {
+            thumb.value = value;
+            if (me.rendered) {
+                // TODO this only handles a single value; need a solution for exposing multiple values to aria.
+                // Perhaps this should go on each thumb element rather than the outer element.
+                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);
+                }
+            }
+        }
+    },
+
+    /**
+     * @private
+     */
+    translateValue : function(v) {
+        var ratio = this.getRatio();
+        return (v * ratio) - (this.minValue * ratio) - this.halfThumb;
+    },
+
+    /**
+     * @private
+     * Given a pixel location along the slider, returns the mapped slider value for that pixel.
+     * E.g. if we have a slider 200px wide with minValue = 100 and maxValue = 500, reverseValue(50)
+     * returns 200
+     * @param {Number} pos The position along the slider to return a mapped value for
+     * @return {Number} The mapped value for the given position
+     */
+    reverseValue : function(pos) {
+        var ratio = this.getRatio();
+        return (pos + (this.minValue * ratio)) / ratio;
+    },
+
+    // private
+    focus : function() {
+        this.focusEl.focus(10);
+    },
+
+    //private
+    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) {
+                //IE breaks when using overflow visible and opacity other than 1.
+                //Create a place holder for the thumb and display it.
+                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);
+            }
+        }
+    },
+
+    //private
+    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();
+            }
+        }
+    },
+
+    /**
+     * Synchronizes thumbs position to the proper proportion of the total component width based
+     * on the current slider {@link #value}.  This will be called automatically when the Slider
+     * is resized by a layout, but if it is rendered auto width, this method can be called from
+     * another resize handler to sync the Slider if necessary.
+     */
+    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));
+            }
+        }
+    },
+
+    /**
+     * Returns the current value of the slider
+     * @param {Number} index The index of the thumb to return a value for
+     * @return {Number/Array} The current value of the slider at the given index, or an array of
+     * all thumb values if no index is given.
+     */
+    getValue : function(index) {
+        return Ext.isNumber(index) ? this.thumbs[index].value : this.getValues();
+    },
+
+    /**
+     * Returns an array of values - one for the location of each thumb
+     * @return {Array} The set of thumb values
+     */
+    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 here so we reset back to the original state
+        delete me.wasValid;
+    },
+
+    // private
+    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: {
+        // Method overrides to support slider with vertical orientation
+        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);
+                    }
+                }
+            }
+        }
+    }
+});
+
+/**
+ * @class Ext.slider.Single
+ * @extends Ext.slider.Multi
+ * Slider which supports vertical or horizontal orientation, keyboard adjustments,
+ * configurable snapping, axis clicking and animation. Can be added as an item to
+ * any container. 
+ * {@img Ext.slider.Single/Ext.slider.Single.png Ext.slider.Single component}
+ * Example usage:
+<pre><code>
+    Ext.create('Ext.slider.Single', {
+        width: 200,
+        value: 50,
+        increment: 10,
+        minValue: 0,
+        maxValue: 100,
+        renderTo: Ext.getBody()
+    });
+</code></pre>
+ * The class Ext.slider.Single is aliased to Ext.Slider for backwards compatibility.
+ * @xtype slider
+ */
+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'],
+
+    /**
+     * Returns the current value of the slider
+     * @return {Number} The current value of the slider
+     */
+    getValue: function() {
+        //just returns the value of the first thumb, which should be the only one in a single slider
+        return this.callParent([0]);
+    },
+
+    /**
+     * Programmatically sets the value of the Slider. Ensures that the value is constrained within
+     * the minValue and maxValue.
+     * @param {Number} value The value to set the slider to. (This will be constrained within minValue and maxValue)
+     * @param {Boolean} animate Turn on or off animation, defaults to true
+     */
+    setValue: function(value, animate) {
+        var args = Ext.toArray(arguments),
+            len  = args.length;
+
+        //this is to maintain backwards compatiblity for sliders with only one thunb. Usually you must pass the thumb
+        //index to setValue, but if we only have one thumb we inject the index here first if given the multi-slider
+        //signature without the required index. The index will always be 0 for a single slider
+        if (len == 1 || (len <= 3 && typeof arguments[1] != 'number')) {
+            args.unshift(0);
+        }
+
+        return this.callParent(args);
+    },
+
+    // private
+    getNearest : function(){
+        // Since there's only 1 thumb, it's always the nearest
+        return this.thumbs[0];
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.tab.Tab
+ * @extends Ext.button.Button
+ * 
+ * <p>Represents a single Tab in a {@link Ext.tab.Panel TabPanel}. A Tab is simply a slightly customized {@link Ext.button.Button Button}, 
+ * styled to look like a tab. Tabs are optionally closable, and can also be disabled. 99% of the time you will not
+ * need to create Tabs manually as the framework does so automatically when you use a {@link Ext.tab.Panel TabPanel}</p>
+ *
+ * @xtype tab
+ */
+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',
+
+    /**
+     * @cfg {String} activeCls
+     * The CSS class to be applied to a Tab when it is active. Defaults to 'x-tab-active'.
+     * Providing your own CSS for this class enables you to customize the active state.
+     */
+    activeCls: 'active',
+    
+    /**
+     * @cfg {String} disabledCls
+     * The CSS class to be applied to a Tab when it is disabled. Defaults to 'x-tab-disabled'.
+     */
+
+    /**
+     * @cfg {String} closableCls
+     * The CSS class which is added to the tab when it is closable
+     */
+    closableCls: 'closable',
+
+    /**
+     * @cfg {Boolean} closable True to make the Tab start closable (the close icon will be visible). Defaults to true
+     */
+    closable: true,
+
+    /**
+     * @cfg {String} closeText 
+     * The accessible text label for the close button link; only used when {@link #closable} = true.
+     * Defaults to 'Close Tab'.
+     */
+    closeText: 'Close Tab',
+
+    /**
+     * @property Boolean
+     * Read-only property indicating that this tab is currently active. This is NOT a public configuration.
+     */
+    active: false,
+
+    /**
+     * @property closable
+     * @type Boolean
+     * True if the tab is currently closable
+     */
+
+    scale: false,
+
+    position: 'top',
+    
+    initComponent: function() {
+        var me = this;
+
+        me.addEvents(
+            /**
+             * @event activate
+             * @param {Ext.tab.Tab} this
+             */
+            'activate',
+
+            /**
+             * @event deactivate
+             * @param {Ext.tab.Tab} this
+             */
+            'deactivate',
+
+            /**
+             * @event beforeclose
+             * Fires if the user clicks on the Tab's close button, but before the {@link #close} event is fired. Return
+             * false from any listener to stop the close event being fired
+             * @param {Ext.tab.Tab} tab The Tab object
+             */
+            'beforeclose',
+
+            /**
+             * @event beforeclose
+             * Fires to indicate that the tab is to be closed, usually because the user has clicked the close button.
+             * @param {Ext.tab.Tab} tab The Tab object
+             */
+            'close'
+        );
+        
+        me.callParent(arguments);
+
+        if (me.card) {
+            me.setCard(me.card);
+        }
+    },
+
+    /**
+     * @ignore
+     */
+    onRender: function() {
+        var me = this;
+        
+        me.addClsWithUI(me.position);
+        
+        // Set all the state classNames, as they need to include the UI
+        // me.disabledCls = me.getClsWithUIs('disabled');
+
+        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
+        });
+    },
+    
+    // inherit docs
+    enable : function(silent) {
+        var me = this;
+
+        me.callParent(arguments);
+        
+        me.removeClsWithUI(me.position + '-disabled');
+
+        return me;
+    },
+
+    // inherit docs
+    disable : function(silent) {
+        var me = this;
+        
+        me.callParent(arguments);
+        
+        me.addClsWithUI(me.position + '-disabled');
+
+        return me;
+    },
+    
+    /**
+     * @ignore
+     */
+    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);
+    },
+
+    /**
+     * Sets the tab as either closable or not
+     * @param {Boolean} closable Pass false to make the tab not closable. Otherwise the tab will be made closable (eg a
+     * close button will appear on the tab)
+     */
+    setClosable: function(closable) {
+        var me = this;
+
+        // Closable must be true if no args
+        closable = (!arguments.length || !!closable);
+
+        if (me.closable != closable) {
+            me.closable = closable;
+
+            // set property on the user-facing item ('card'):
+            if (me.card) {
+                me.card.closable = closable;
+            }
+
+            me.syncClosableUI();
+
+            if (me.rendered) {
+                me.syncClosableElements();
+
+                // Tab will change width to accommodate close icon
+                me.doComponentLayout();
+                if (me.ownerCt) {
+                    me.ownerCt.doLayout();
+                }
+            }
+        }
+    },
+
+    /**
+     * This method ensures that the closeBtn element exists or not based on 'closable'.
+     * @private
+     */
+    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);  // mon ???
+            }
+        } else {
+            var closeEl = me.closeEl;
+            if (closeEl) {
+                closeEl.un('click', Ext.EventManager.preventDefault);
+                closeEl.remove();
+                me.closeEl = null;
+            }
+        }
+    },
+
+    /**
+     * This method ensures that the UI classes are added or removed based on 'closable'.
+     * @private
+     */
+    syncClosableUI: function () {
+        var me = this, classes = [me.closableCls, me.closableCls + '-' + me.position];
+
+        if (me.closable) {
+            me.addClsWithUI(classes);
+        } else {
+            me.removeClsWithUI(classes);
+        }
+    },
+
+    /**
+     * Sets this tab's attached card. Usually this is handled automatically by the {@link Ext.tab.Panel} that this Tab
+     * belongs to and would not need to be done by the developer
+     * @param {Ext.Component} card The card to set
+     */
+    setCard: function(card) {
+        var me = this;
+
+        me.card = card;
+        me.setText(me.title || card.title);
+        me.setIconCls(me.iconCls || card.iconCls);
+    },
+
+    /**
+     * @private
+     * Listener attached to click events on the Tab's close button
+     */
+    onCloseClick: function() {
+        var me = this;
+
+        if (me.fireEvent('beforeclose', me) !== false) {
+            if (me.tabBar) {
+                me.tabBar.closeTab(me);
+            }
+
+            me.fireEvent('close', me);
+        }
+    },
+    
+    /**
+     * @private
+     */
+    onEnterKey: function(e) {
+        var me = this;
+        
+        if (me.tabBar) {
+            me.tabBar.onClick(e, me.el);
+        }
+    },
+    
+   /**
+     * @private
+     */
+    onDeleteKey: function(e) {
+        var me = this;
+        
+        if (me.closable) {
+            me.onCloseClick();
+        }
+    },
+    
+    // @private
+    activate : function(supressEvent) {
+        var me = this;
+        
+        me.active = true;
+        me.addClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);
+
+        if (supressEvent !== true) {
+            me.fireEvent('activate', me);
+        }
+    },
+
+    // @private
+    deactivate : function(supressEvent) {
+        var me = this;
+        
+        me.active = false;
+        me.removeClsWithUI([me.activeCls, me.position + '-' + me.activeCls]);
+        
+        if (supressEvent !== true) {
+            me.fireEvent('deactivate', me);
+        }
+    }
+});
+
+/**
+ * @author Ed Spencer
+ * @class Ext.tab.Bar
+ * @extends Ext.panel.Header
+ * <p>TabBar is used internally by a {@link Ext.tab.Panel TabPanel} and wouldn't usually need to be created manually.</p>
+ *
+ * @xtype tabbar
+ */
+Ext.define('Ext.tab.Bar', {
+    extend: 'Ext.panel.Header',
+    alias: 'widget.tabbar',
+    baseCls: Ext.baseCSSPrefix + 'tab-bar',
+
+    requires: [
+        'Ext.tab.Tab',
+        'Ext.FocusManager'
+    ],
+
+    // @private
+    defaultType: 'tab',
+
+    /**
+     * @cfg Boolean plain
+     * True to not show the full background on the tabbar
+     */
+    plain: false,
+
+    // @private
+    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>'
+    ],
+
+    /**
+     * @cfg {Number} minTabWidth The minimum width for each tab. Defaults to <tt>30</tt>.
+     */
+    minTabWidth: 30,
+
+    /**
+     * @cfg {Number} maxTabWidth The maximum width for each tab. Defaults to <tt>undefined</tt>.
+     */
+    maxTabWidth: undefined,
+
+    // @private
+    initComponent: function() {
+        var me = this,
+            keys;
+
+        if (me.plain) {
+            me.setUI(me.ui + '-plain');
+        }
+        
+        me.addClsWithUI(me.dock);
+
+        me.addEvents(
+            /**
+             * @event change
+             * Fired when the currently-active tab has changed
+             * @param {Ext.tab.Bar} tabBar The TabBar
+             * @param {Ext.Tab} tab The new Tab
+             * @param {Ext.Component} card The card that was just shown in the TabPanel
+             */
+            'change'
+        );
+
+        Ext.applyIf(this.renderSelectors, {
+            body : '.' + this.baseCls + '-body',
+            strip: '.' + this.baseCls + '-strip'
+        });
+        me.callParent(arguments);
+
+        // TabBar must override the Header's align setting.
+        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() - 1);
+        me.items.removeAt(me.items.getCount() - 1);
+        
+        // Subscribe to Ext.FocusManager for key navigation
+        keys = me.orientation == 'vertical' ? ['up', 'down'] : ['left', 'right'];
+        Ext.FocusManager.subscribe(me, {
+            keys: keys
+        });
+    },
+
+    // @private
+    onAdd: function(tab) {
+        var me = this,
+            tabPanel = me.tabPanel,
+            hasOwner = !!tabPanel;
+
+        me.callParent(arguments);
+        tab.position = me.dock;
+        if (hasOwner) {
+            tab.minWidth = tabPanel.minTabWidth;
+        }
+        else {
+            tab.minWidth = me.minTabWidth + (tab.iconCls ? 25 : 0);
+        }
+        tab.maxWidth = me.maxTabWidth || (hasOwner ? tabPanel.maxTabWidth : undefined);
+    },
+
+    // @private
+    afterRender: function() {
+        var me = this;
+
+        me.mon(me.el, {
+            scope: me,
+            click: me.onClick,
+            delegate: '.' + Ext.baseCSSPrefix + 'tab'
+        });
+        me.callParent(arguments);
+        
+    },
+
+    afterComponentLayout : function() {
+        var me = this;
+        
+        me.callParent(arguments);
+        me.strip.setWidth(me.el.getWidth());
+    },
+
+    // @private
+    onClick: function(e, target) {
+        // The target might not be a valid tab el.
+        var tab = Ext.getCmp(target.id),
+            tabPanel = this.tabPanel;
+
+        target = e.getTarget();
+
+        if (tab && tab.isDisabled && !tab.isDisabled()) {
+            if (tab.closable && target === tab.closeEl.dom) {
+                tab.onCloseClick();
+            } else {
+                this.setActiveTab(tab);
+                if (tabPanel) {
+                    tabPanel.setActiveTab(tab.card);
+                }
+                tab.focus();
+            }
+        }
+    },
+
+    /**
+     * @private
+     * Closes the given tab by removing it from the TabBar and removing the corresponding card from the TabPanel
+     * @param {Ext.Tab} tab The tab to close
+     */
+    closeTab: function(tab) {
+        var card    = tab.card,
+            tabPanel = this.tabPanel,
+            nextTab;
+
+        if (tab.active && this.items.getCount() > 1) {
+            nextTab = tab.next('tab') || this.items.items[0];
+            this.setActiveTab(nextTab);
+            if (tabPanel) {
+                tabPanel.setActiveTab(nextTab.card);
+            }
+        }
+        this.remove(tab);
+
+        if (tabPanel && card) {
+            tabPanel.remove(card);
+        }
+        
+        if (nextTab) {
+            nextTab.focus();
+        }
+    },
+
+    /**
+     * @private
+     * Marks the given tab as active
+     * @param {Ext.Tab} tab The tab to mark active
+     */
+    setActiveTab: function(tab) {
+        if (tab.disabled) {
+            return;
+        }
+        var me = this;
+        if (me.activeTab) {
+            me.activeTab.deactivate();
+        }
+        tab.activate();
+        
+        if (me.rendered) {
+            me.layout.layout();
+            tab.el.scrollIntoView(me.layout.getRenderTarget());
+        }
+        me.activeTab = tab;
+        me.fireEvent('change', me, tab, tab.card);
+    }
+});
+/**
+ * @author Ed Spencer, Tommy Maintz, Brian Moeskau
+ * @class Ext.tab.Panel
+ * @extends Ext.panel.Panel
+
+A basic tab container. TabPanels can be used exactly like a standard {@link Ext.panel.Panel} for layout purposes, but also 
+have special support for containing child Components (`{@link Ext.container.Container#items items}`) that are managed 
+using a {@link Ext.layout.container.Card CardLayout layout manager}, and displayed as separate tabs.
+
+__Note:__
+
+By default, a tab's close tool _destroys_ the child tab Component and all its descendants. This makes the child tab 
+Component, and all its descendants __unusable__. To enable re-use of a tab, configure the TabPanel with `{@link #autoDestroy autoDestroy: false}`.
+
+__TabPanel's layout:__
+
+TabPanels use a Dock layout to position the {@link Ext.tab.Bar TabBar} at the top of the widget. Panels added to the TabPanel will have their 
+header hidden by default because the Tab will automatically take the Panel's configured title and icon.
+
+TabPanels use their {@link Ext.panel.Panel#header header} or {@link Ext.panel.Panel#footer footer} element (depending on the {@link #tabPosition} 
+configuration) to accommodate the tab selector buttons. This means that a TabPanel will not display any configured title, and will not display any 
+configured header {@link Ext.panel.Panel#tools tools}.
+
+To display a header, embed the TabPanel in a {@link Ext.panel.Panel Panel} which uses `{@link Ext.container.Container#layout layout:'fit'}`.
+
+__Examples:__
+
+Here is a basic TabPanel rendered to the body. This also shows the useful configuration {@link #activeTab}, which allows you to set the active tab on render. 
+If you do not set an {@link #activeTab}, no tabs will be active by default.
+{@img Ext.tab.Panel/Ext.tab.Panel1.png TabPanel component}
+Example usage:
+
+    Ext.create('Ext.tab.Panel', {
+        width: 300,
+        height: 200,
+        activeTab: 0,
+        items: [
+            {
+                title: 'Tab 1',
+                bodyPadding: 10,
+                html : 'A simple tab'
+            },
+            {
+                title: 'Tab 2',
+                html : 'Another one'
+            }
+        ],
+        renderTo : Ext.getBody()
+    }); 
+    
+It is easy to control the visibility of items in the tab bar. Specify hidden: true to have the
+tab button hidden initially. Items can be subsequently hidden and show by accessing the
+tab property on the child item.
+
+Example usage:
+    
+    var tabs = Ext.create('Ext.tab.Panel', {
+        width: 400,
+        height: 400,
+        renderTo: document.body,
+        items: [{
+            title: 'Home',
+            html: 'Home',
+            itemId: 'home'
+        }, {
+            title: 'Users',
+            html: 'Users',
+            itemId: 'users',
+            hidden: true
+        }, {
+            title: 'Tickets',
+            html: 'Tickets',
+            itemId: 'tickets'
+        }]    
+    });
+    
+    setTimeout(function(){
+        tabs.child('#home').tab.hide();
+        var users = tabs.child('#users');
+        users.tab.show();
+        tabs.setActiveTab(users);
+    }, 1000);
+
+You can remove the background of the TabBar by setting the {@link #plain} property to `false`.
+
+Example usage:
+
+    Ext.create('Ext.tab.Panel', {
+        width: 300,
+        height: 200,
+        activeTab: 0,
+        plain: true,
+        items: [
+            {
+                title: 'Tab 1',
+                bodyPadding: 10,
+                html : 'A simple tab'
+            },
+            {
+                title: 'Tab 2',
+                html : 'Another one'
+            }
+        ],
+        renderTo : Ext.getBody()
+    }); 
+
+Another useful configuration of TabPanel is {@link #tabPosition}. This allows you to change the position where the tabs are displayed. The available 
+options for this are `'top'` (default) and `'bottom'`.
+{@img Ext.tab.Panel/Ext.tab.Panel2.png TabPanel component}
+Example usage:
+
+    Ext.create('Ext.tab.Panel', {
+        width: 300,
+        height: 200,
+        activeTab: 0,
+        bodyPadding: 10,        
+        tabPosition: 'bottom',
+        items: [
+            {
+                title: 'Tab 1',
+                html : 'A simple tab'
+            },
+            {
+                title: 'Tab 2',
+                html : 'Another one'
+            }
+        ],
+        renderTo : Ext.getBody()
+    }); 
+
+The {@link #setActiveTab} is a very useful method in TabPanel which will allow you to change the current active tab. You can either give it an index or 
+an instance of a tab.
+
+Example usage:
+
+    var tabs = Ext.create('Ext.tab.Panel', {
+        items: [
+            {
+                id   : 'my-tab',
+                title: 'Tab 1',
+                html : 'A simple tab'
+            },
+            {
+                title: 'Tab 2',
+                html : 'Another one'
+            }
+        ],
+        renderTo : Ext.getBody()
+    });
+    
+    var tab = Ext.getCmp('my-tab');
+    
+    Ext.create('Ext.button.Button', {
+        renderTo: Ext.getBody(),
+        text    : 'Select the first tab',
+        scope   : this,
+        handler : function() {
+            tabs.setActiveTab(tab);
+        }
+    });
+    
+    Ext.create('Ext.button.Button', {
+        text    : 'Select the second tab',
+        scope   : this,
+        handler : function() {
+            tabs.setActiveTab(1);
+        },
+        renderTo : Ext.getBody()        
+    });
+
+The {@link #getActiveTab} is a another useful method in TabPanel which will return the current active tab.
+
+Example usage:
+
+    var tabs = Ext.create('Ext.tab.Panel', {
+        items: [
+            {
+                title: 'Tab 1',
+                html : 'A simple tab'
+            },
+            {
+                title: 'Tab 2',
+                html : 'Another one'
+            }
+        ],
+        renderTo : Ext.getBody()        
+    });
+    
+    Ext.create('Ext.button.Button', {
+        text    : 'Get active tab',
+        scope   : this,
+        handler : function() {
+            var tab = tabs.getActiveTab();
+            alert('Current tab: ' + tab.title);
+        },
+        renderTo : Ext.getBody()        
+    });
+
+Adding a new tab is very simple with a TabPanel. You simple call the {@link #add} method with an config object for a panel.
+
+Example usage:
+
+    var tabs = Ext.Create('Ext.tab.Panel', {
+        items: [
+            {
+                title: 'Tab 1',
+                html : 'A simple tab'
+            },
+            {
+                title: 'Tab 2',
+                html : 'Another one'
+            }
+        ],
+        renderTo : Ext.getBody()        
+    });
+    
+    Ext.create('Ext.button.Button', {
+        text    : 'New tab',
+        scope   : this,
+        handler : function() {
+            var tab = tabs.add({
+                title: 'Tab ' + (tabs.items.length + 1), //we use the tabs.items property to get the length of current items/tabs
+                html : 'Another one'
+            });
+            
+            tabs.setActiveTab(tab);
+        },
+        renderTo : Ext.getBody()
+    });
+
+Additionally, removing a tab is very also simple with a TabPanel. You simple call the {@link #remove} method with an config object for a panel.
+
+Example usage:
+
+    var tabs = Ext.Create('Ext.tab.Panel', {        
+        items: [
+            {
+                title: 'Tab 1',
+                html : 'A simple tab'
+            },
+            {
+                id   : 'remove-this-tab',
+                title: 'Tab 2',
+                html : 'Another one'
+            }
+        ],
+        renderTo : Ext.getBody()
+    });
+    
+    Ext.Create('Ext.button.Button', {
+        text    : 'Remove tab',
+        scope   : this,
+        handler : function() {
+            var tab = Ext.getCmp('remove-this-tab');
+            tabs.remove(tab);
+        },
+        renderTo : Ext.getBody()
+    });
+
+ * @extends Ext.Panel
+ * @constructor
+ * @param {Object} config The configuration options
+ * @xtype tabpanel
+ * @markdown
+ */
+Ext.define('Ext.tab.Panel', {
+    extend: 'Ext.panel.Panel',
+    alias: 'widget.tabpanel',
+    alternateClassName: ['Ext.TabPanel'],
+
+    requires: ['Ext.layout.container.Card', 'Ext.tab.Bar'],
+
+    /**
+     * @cfg {String} tabPosition The position where the tab strip should be rendered (defaults to <code>'top'</code>).
+     * In 4.0, The only other supported value is <code>'bottom'</code>.
+     */
+    tabPosition : 'top',
+    
+    /**
+     * @cfg {Object} tabBar Optional configuration object for the internal {@link Ext.tab.Bar}. If present, this is 
+     * passed straight through to the TabBar's constructor
+     */
+
+    /**
+     * @cfg {Object} layout Optional configuration object for the internal {@link Ext.layout.container.Card card layout}.
+     * If present, this is passed straight through to the layout's constructor
+     */
+
+    /**
+     * @cfg {Boolean} removePanelHeader True to instruct each Panel added to the TabContainer to not render its header 
+     * element. This is to ensure that the title of the panel does not appear twice. Defaults to true.
+     */
+    removePanelHeader: true,
+
+    /**
+     * @cfg Boolean plain
+     * True to not show the full background on the TabBar
+     */
+    plain: false,
+
+    /**
+     * @cfg {String} itemCls The class added to each child item of this TabPanel. Defaults to 'x-tabpanel-child'.
+     */
+    itemCls: 'x-tabpanel-child',
+
+    /**
+     * @cfg {Number} minTabWidth The minimum width for a tab in the {@link #tabBar}. Defaults to <code>30</code>.
+     */
+
+    /**
+     * @cfg {Boolean} deferredRender
+     * <p><tt>true</tt> by default to defer the rendering of child <tt>{@link Ext.container.Container#items items}</tt>
+     * to the browsers DOM until a tab is activated. <tt>false</tt> will render all contained
+     * <tt>{@link Ext.container.Container#items items}</tt> as soon as the {@link Ext.layout.container.Card layout}
+     * is rendered. If there is a significant amount of content or a lot of heavy controls being
+     * rendered into panels that are not displayed by default, setting this to <tt>true</tt> might
+     * improve performance.</p>
+     * <br><p>The <tt>deferredRender</tt> property is internally passed to the layout manager for
+     * TabPanels ({@link Ext.layout.container.Card}) as its {@link Ext.layout.container.Card#deferredRender}
+     * configuration value.</p>
+     * <br><p><b>Note</b>: leaving <tt>deferredRender</tt> as <tt>true</tt> means that the content
+     * within an unactivated tab will not be available</p>
+     */
+    deferredRender : true,
+
+    //inherit docs
+    initComponent: function() {
+        var me = this,
+            dockedItems = me.dockedItems || [],
+            activeTab = me.activeTab || 0;
+
+        me.layout = Ext.create('Ext.layout.container.Card', Ext.apply({
+            owner: me,
+            deferredRender: me.deferredRender,
+            itemCls: me.itemCls
+        }, me.layout));
+
+        /**
+         * @property tabBar
+         * @type Ext.TabBar
+         * Internal reference to the docked TabBar
+         */
+        me.tabBar = Ext.create('Ext.tab.Bar', Ext.apply({}, me.tabBar, {
+            dock: me.tabPosition,
+            plain: me.plain,
+            border: me.border,
+            cardLayout: me.layout,
+            tabPanel: me
+        }));
+
+        if (dockedItems && !Ext.isArray(dockedItems)) {
+            dockedItems = [dockedItems];
+        }
+
+        dockedItems.push(me.tabBar);
+        me.dockedItems = dockedItems;
+
+        me.addEvents(
+            /**
+             * @event beforetabchange
+             * Fires before a tab change (activated by {@link #setActiveTab}). Return false in any listener to cancel
+             * the tabchange
+             * @param {Ext.tab.Panel} tabPanel The TabPanel
+             * @param {Ext.Component} newCard The card that is about to be activated
+             * @param {Ext.Component} oldCard The card that is currently active
+             */
+            'beforetabchange',
+
+            /**
+             * @event tabchange
+             * Fires when a new tab has been activated (activated by {@link #setActiveTab}).
+             * @param {Ext.tab.Panel} tabPanel The TabPanel
+             * @param {Ext.Component} newCard The newly activated item
+             * @param {Ext.Component} oldCard The previously active item
+             */
+            'tabchange'
+        );
+        me.callParent(arguments);
+
+        //set the active tab
+        me.setActiveTab(activeTab);
+        //set the active tab after initial layout
+        me.on('afterlayout', me.afterInitialLayout, me, {single: true});
+    },
+
+    /**
+     * @private
+     * We have to wait until after the initial layout to visually activate the activeTab (if set).
+     * The active tab has different margins than normal tabs, so if the initial layout happens with
+     * a tab active, its layout will be offset improperly due to the active margin style. Waiting
+     * until after the initial layout avoids this issue.
+     */
+    afterInitialLayout: function() {
+        var me = this,
+            card = me.getComponent(me.activeTab);
+            
+        if (card) {
+            me.layout.setActiveItem(card);
+        }
+    },
+
+    /**
+     * Makes the given card active (makes it the visible card in the TabPanel's CardLayout and highlights the Tab)
+     * @param {Ext.Component} card The card to make active
+     */
+    setActiveTab: function(card) {
+        var me = this,
+            previous;
+
+        card = me.getComponent(card);
+        if (card) {
+            previous = me.getActiveTab();
+            
+            if (previous && previous !== card && me.fireEvent('beforetabchange', me, card, previous) === false) {
+                return false;
+            }
+            
+            me.tabBar.setActiveTab(card.tab);
+            me.activeTab = card;
+            if (me.rendered) {
+                me.layout.setActiveItem(card);
+            }
+            
+            if (previous && previous !== card) {
+                me.fireEvent('tabchange', me, card, previous);
+            }
+        }
+    },
+
+    /**
+     * Returns the item that is currently active inside this TabPanel. Note that before the TabPanel first activates a
+     * child component this will return whatever was configured in the {@link #activeTab} config option 
+     * @return {Ext.Component/Integer} The currently active item
+     */
+    getActiveTab: function() {
+        return this.activeTab;
+    },
+
+    /**
+     * Returns the {@link Ext.tab.Bar} currently used in this TabPanel
+     * @return {Ext.TabBar} The TabBar
+     */
+    getTabBar: function() {
+        return this.tabBar;
+    },
+
+    /**
+     * @ignore
+     * Makes sure we have a Tab for each item added to the TabPanel
+     */
+    onAdd: function(item, index) {
+        var me = this;
+
+        item.tab = me.tabBar.insert(index, {
+            xtype: 'tab',
+            card: item,
+            disabled: item.disabled,
+            closable: item.closable,
+            hidden: item.hidden,
+            tabBar: me.tabBar
+        });
+        
+        item.on({
+            scope : me,
+            enable: me.onItemEnable,
+            disable: me.onItemDisable,
+            beforeshow: me.onItemBeforeShow,
+            iconchange: me.onItemIconChange,
+            titlechange: me.onItemTitleChange
+        });
+
+        if (item.isPanel) {
+            if (me.removePanelHeader) {
+                item.preventHeader = true;
+                if (item.rendered) {
+                    item.updateHeader();
+                }
+            }
+            if (item.isPanel && me.border) {
+                item.setBorder(false);
+            }
+        }
+
+        // ensure that there is at least one active tab
+        if (this.rendered && me.items.getCount() === 1) {
+            me.setActiveTab(0);
+        }
+    },
+    
+    /**
+     * @private
+     * Enable corresponding tab when item is enabled.
+     */
+    onItemEnable: function(item){
+        item.tab.enable();
+    },
+
+    /**
+     * @private
+     * Disable corresponding tab when item is enabled.
+     */    
+    onItemDisable: function(item){
+        item.tab.disable();
+    },
+    
+    /**
+     * @private
+     * Sets activeTab before item is shown.
+     */
+    onItemBeforeShow: function(item) {
+        if (item !== this.activeTab) {
+            this.setActiveTab(item);
+            return false;
+        }    
+    },
+    
+    /**
+     * @private
+     * Update the tab iconCls when panel iconCls has been set or changed.
+     */
+    onItemIconChange: function(item, newIconCls) {
+        item.tab.setIconCls(newIconCls);
+        this.getTabBar().doLayout();
+    },
+    
+    /**
+     * @private
+     * Update the tab title when panel title has been set or changed.
+     */
+    onItemTitleChange: function(item, newTitle) {
+        item.tab.setText(newTitle);
+        this.getTabBar().doLayout();
+    },
+
+
+    /**
+     * @ignore
+     * If we're removing the currently active tab, activate the nearest one. The item is removed when we call super,
+     * so we can do preprocessing before then to find the card's index
+     */
+    doRemove: function(item, autoDestroy) {
+        var me = this,
+            items = me.items,
+            /**
+             * At this point the item hasn't been removed from the items collection.
+             * As such, if we want to check if there are no more tabs left, we have to
+             * check for one, as opposed to 0.
+             */
+            hasItemsLeft = items.getCount() > 1;
+
+        if (me.destroying || !hasItemsLeft) {
+            me.activeTab = null;
+        } else if (item === me.activeTab) {
+             me.setActiveTab(item.next() || items.getAt(0)); 
+        }
+        me.callParent(arguments);
+
+        // Remove the two references
+        delete item.tab.card;
+        delete item.tab;
+    },
+
+    /**
+     * @ignore
+     * Makes sure we remove the corresponding Tab when an item is removed
+     */
+    onRemove: function(item, autoDestroy) {
+        var me = this;
+        
+        item.un({
+            scope : me,
+            enable: me.onItemEnable,
+            disable: me.onItemDisable,
+            beforeshow: me.onItemBeforeShow
+        });
+        if (!me.destroying && item.tab.ownerCt == me.tabBar) {
+            me.tabBar.remove(item.tab);
+        }
+    }
+});
+
+/**
+ * @class Ext.toolbar.Spacer
+ * @extends Ext.toolbar.Item
+ * A simple element that adds extra horizontal space between items in a toolbar.
+ * By default a 2px wide space is added via css specification:
+ * <pre><code>
+    .x-toolbar .x-toolbar-spacer {
+        width:2px;
+    }
+ * </code></pre>
+ * <p>Example usage:</p>
+ * {@img Ext.toolbar.Spacer/Ext.toolbar.Spacer.png Toolbar Spacer}
+ * <pre><code>
+    Ext.create('Ext.panel.Panel', {
+        title: 'Toolbar Spacer Example',
+        width: 300,
+        height: 200,
+        tbar : [
+            'Item 1',
+            {xtype: 'tbspacer'}, // or ' '
+            'Item 2',
+            // space width is also configurable via javascript
+            {xtype: 'tbspacer', width: 50}, // add a 50px space
+            'Item 3'
+        ],
+        renderTo: Ext.getBody()
+    });   
+</code></pre>
+ * @constructor
+ * Creates a new Spacer
+ * @xtype tbspacer
+ */
+Ext.define('Ext.toolbar.Spacer', {
+    extend: 'Ext.Component',
+    alias: 'widget.tbspacer',
+    alternateClassName: 'Ext.Toolbar.Spacer',
+    baseCls: Ext.baseCSSPrefix + 'toolbar-spacer',
+    focusable: false
+});
+/**
+ * @class Ext.tree.Column
+ * @extends Ext.grid.column.Column
+ * 
+ * Provides indentation and folder structure markup for a Tree taking into account
+ * depth and position within the tree hierarchy.
+ * 
+ * @private
+ */
+Ext.define('Ext.tree.Column', {
+    extend: 'Ext.grid.column.Column',
+    alias: 'widget.treecolumn',
+
+    initComponent: function() {
+        var origRenderer = this.renderer || this.defaultRenderer,
+            origScope    = this.scope || window;
+
+        this.renderer = function(value, metaData, record, rowIdx, colIdx, store, view) {
+            var buf   = [],
+                format = Ext.String.format,
+                depth = record.getDepth(),
+                treePrefix  = Ext.baseCSSPrefix + 'tree-',
+                elbowPrefix = treePrefix + 'elbow-',
+                expanderCls = treePrefix + 'expander',
+                imgText     = '<img src="{1}" class="{0}" />',
+                checkboxText= '<input type="button" role="checkbox" class="{0}" {1} />',
+                formattedValue = origRenderer.apply(origScope, arguments),
+                href = record.get('href'),
+                target = record.get('hrefTarget');
+
+            while (record) {
+                if (!record.isRoot() || (record.isRoot() && view.rootVisible)) {
+                    if (record.getDepth() === depth) {
+                        buf.unshift(format(imgText,
+                            treePrefix + 'icon ' + 
+                            treePrefix + 'icon' + (record.get('icon') ? '-inline ' : (record.isLeaf() ? '-leaf ' : '-parent ')) +
+                            (record.get('iconCls') || ''),
+                            record.get('icon') || Ext.BLANK_IMAGE_URL
+                        ));
+                        if (record.get('checked') !== null) {
+                            buf.unshift(format(
+                                checkboxText,
+                                (treePrefix + 'checkbox') + (record.get('checked') ? ' ' + treePrefix + 'checkbox-checked' : ''),
+                                record.get('checked') ? 'aria-checked="true"' : ''
+                            ));
+                            if (record.get('checked')) {
+                                metaData.tdCls += (' ' + Ext.baseCSSPrefix + 'tree-checked');
+                            }
+                        }
+                        if (record.isLast()) {
+                            if (record.isLeaf() || (record.isLoaded() && !record.hasChildNodes())) {
+                                buf.unshift(format(imgText, (elbowPrefix + 'end'), Ext.BLANK_IMAGE_URL));
+                            } else {
+                                buf.unshift(format(imgText, (elbowPrefix + 'end-plus ' + expanderCls), Ext.BLANK_IMAGE_URL));
+                            }
+                            
+                        } else {
+                            if (record.isLeaf() || (record.isLoaded() && !record.hasChildNodes())) {
+                                buf.unshift(format(imgText, (treePrefix + 'elbow'), Ext.BLANK_IMAGE_URL));
+                            } else {
+                                buf.unshift(format(imgText, (elbowPrefix + 'plus ' + expanderCls), Ext.BLANK_IMAGE_URL));
+                            }
+                        }
+                    } else {
+                        if (record.isLast() || record.getDepth() === 0) {
+                            buf.unshift(format(imgText, (elbowPrefix + 'empty'), Ext.BLANK_IMAGE_URL));
+                        } else if (record.getDepth() !== 0) {
+                            buf.unshift(format(imgText, (elbowPrefix + 'line'), Ext.BLANK_IMAGE_URL));
+                        }                      
+                    }
+                }
+                record = record.parentNode;
+            }
+            if (href) {
+                formattedValue = format('<a href="{0}" target="{1}">{2}</a>', href, target, formattedValue);
+            }
+            return buf.join("") + formattedValue;
+        };
+        this.callParent(arguments);
+    },
+
+    defaultRenderer: function(value) {
+        return value;
+    }
+});
+/**
+ * @class Ext.tree.View
+ * @extends Ext.view.Table
+ */
+Ext.define('Ext.tree.View', {
+    extend: 'Ext.view.Table',
+    alias: 'widget.treeview',
+
+    loadingCls: Ext.baseCSSPrefix + 'grid-tree-loading',
+    expandedCls: Ext.baseCSSPrefix + 'grid-tree-node-expanded',
+
+    expanderSelector: '.' + Ext.baseCSSPrefix + 'tree-expander',
+    checkboxSelector: '.' + Ext.baseCSSPrefix + 'tree-checkbox',
+    expanderIconOverCls: Ext.baseCSSPrefix + 'tree-expander-over',
+
+    blockRefresh: true,
+
+    /** 
+     * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)
+     */
+    rootVisible: true,
+
+    /** 
+     * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx Ext.enableFx})
+     */
+
+    expandDuration: 250,
+    collapseDuration: 250,
+    
+    toggleOnDblClick: true,
+
+    initComponent: function() {
+        var me = this;
+        
+        if (me.initialConfig.animate === undefined) {
+            me.animate = Ext.enableFx;
+        }
+        
+        me.store = Ext.create('Ext.data.NodeStore', {
+            recursive: true,
+            rootVisible: me.rootVisible,
+            listeners: {
+                beforeexpand: me.onBeforeExpand,
+                expand: me.onExpand,
+                beforecollapse: me.onBeforeCollapse,
+                collapse: me.onCollapse,
+                scope: me
+            }
+        });
+        
+        if (me.node) {
+            me.setRootNode(me.node);
+        }
+        me.animQueue = {};
+        me.callParent(arguments);
+    },
+    
+    onClear: function(){
+        this.store.removeAll();    
+    },
+
+    setRootNode: function(node) {
+        var me = this;        
+        me.store.setNode(node);
+        me.node = node;
+        if (!me.rootVisible) {
+            node.expand();
+        }
+    },
+    
+    onRender: function() {
+        var me = this,
+            opts = {delegate: me.expanderSelector},
+            el;
+
+        me.callParent(arguments);
+
+        el = me.el;
+        el.on({
+            scope: me,
+            delegate: me.expanderSelector,
+            mouseover: me.onExpanderMouseOver,
+            mouseout: me.onExpanderMouseOut
+        });
+        el.on({
+            scope: me,
+            delegate: me.checkboxSelector,
+            click: me.onCheckboxChange
+        });
+    },
+
+    onCheckboxChange: function(e, t) {
+        var item = e.getTarget(this.getItemSelector(), this.getTargetEl()),
+            record, value;
+            
+        if (item) {
+            record = this.getRecord(item);
+            value = !record.get('checked');
+            record.set('checked', value);
+            this.fireEvent('checkchange', record, value);
+        }
+    },
+
+    getChecked: function() {
+        var checked = [];
+        this.node.cascadeBy(function(rec){
+            if (rec.get('checked')) {
+                checked.push(rec);
+            }
+        });
+        return checked;
+    },
+    
+    isItemChecked: function(rec){
+        return rec.get('checked');
+    },
+
+    createAnimWrap: function(record, index) {
+        var thHtml = '',
+            headerCt = this.panel.headerCt,
+            headers = headerCt.getGridColumns(),
+            i = 0, len = headers.length, item,
+            node = this.getNode(record),
+            tmpEl, nodeEl;
+
+        for (; i < len; i++) {
+            item = headers[i];
+            thHtml += '<th style="width: ' + (item.hidden ? 0 : item.getDesiredWidth()) + 'px; height: 0px;"></th>';
+        }
+
+        nodeEl = Ext.get(node);        
+        tmpEl = nodeEl.insertSibling({
+            tag: 'tr',
+            html: [
+                '<td colspan="' + headerCt.getColumnCount() + '">',
+                    '<div class="' + Ext.baseCSSPrefix + 'tree-animator-wrap' + '">',
+                        '<table class="' + Ext.baseCSSPrefix + 'grid-table" style="width: ' + headerCt.getFullWidth() + 'px;"><tbody>',
+                            thHtml,
+                        '</tbody></table>',
+                    '</div>',
+                '</td>'
+            ].join('')
+        }, 'after');
+
+        return {
+            record: record,
+            node: node,
+            el: tmpEl,
+            expanding: false,
+            collapsing: false,
+            animating: false,
+            animateEl: tmpEl.down('div'),
+            targetEl: tmpEl.down('tbody')
+        };
+    },
+
+    getAnimWrap: function(parent) {
+        if (!this.animate) {
+            return null;
+        }
+
+        // We are checking to see which parent is having the animation wrap
+        while (parent) {
+            if (parent.animWrap) {
+                return parent.animWrap;
+            }
+            parent = parent.parentNode;
+        }
+        return null;
+    },
+
+    doAdd: function(nodes, records, index) {
+        // If we are adding records which have a parent that is currently expanding
+        // lets add them to the animation wrap
+        var me = this,
+            record = records[0],
+            parent = record.parentNode,
+            a = me.all.elements,
+            relativeIndex = 0,
+            animWrap = me.getAnimWrap(parent),
+            targetEl, children, len;
+
+        if (!animWrap || !animWrap.expanding) {
+            me.resetScrollers();
+            return me.callParent(arguments);
+        }
+
+        // We need the parent that has the animWrap, not the nodes parent
+        parent = animWrap.record;
+        
+        // If there is an anim wrap we do our special magic logic
+        targetEl = animWrap.targetEl;
+        children = targetEl.dom.childNodes;
+        
+        // We subtract 1 from the childrens length because we have a tr in there with the th'es
+        len = children.length - 1;
+        
+        // The relative index is the index in the full flat collection minus the index of the wraps parent
+        relativeIndex = index - me.indexOf(parent) - 1;
+        
+        // If we are adding records to the wrap that have a higher relative index then there are currently children
+        // it means we have to append the nodes to the wrap
+        if (!len || relativeIndex >= len) {
+            targetEl.appendChild(nodes);
+        }
+        // If there are already more children then the relative index it means we are adding child nodes of
+        // some expanded node in the anim wrap. In this case we have to insert the nodes in the right location
+        else {
+            // +1 because of the tr with th'es that is already there
+            Ext.fly(children[relativeIndex + 1]).insertSibling(nodes, 'before', true);
+        }
+        
+        // We also have to update the CompositeElementLite collection of the DataView
+        if (index < a.length) {
+            a.splice.apply(a, [index, 0].concat(nodes));
+        }
+        else {            
+            a.push.apply(a, nodes);
+        }
+        
+        // If we were in an animation we need to now change the animation
+        // because the targetEl just got higher.
+        if (animWrap.isAnimating) {
+            me.onExpand(parent);
+        }
+    },
+    
+    doRemove: function(record, index) {
+        // If we are adding records which have a parent that is currently expanding
+        // lets add them to the animation wrap
+        var me = this,
+            parent = record.parentNode,
+            all = me.all,
+            animWrap = me.getAnimWrap(record),
+            node = all.item(index).dom;
+
+        if (!animWrap || !animWrap.collapsing) {
+            me.resetScrollers();
+            return me.callParent(arguments);
+        }
+
+        animWrap.targetEl.appendChild(node);
+        all.removeElement(index);
+    },
+
+    onBeforeExpand: function(parent, records, index) {
+        var me = this,
+            animWrap;
+            
+        if (!me.animate) {
+            return;
+        }
+
+        if (me.getNode(parent)) {
+            animWrap = me.getAnimWrap(parent);
+            if (!animWrap) {
+                animWrap = parent.animWrap = me.createAnimWrap(parent);
+                animWrap.animateEl.setHeight(0);
+            }
+            else if (animWrap.collapsing) {
+                // If we expand this node while it is still expanding then we
+                // have to remove the nodes from the animWrap.
+                animWrap.targetEl.select(me.itemSelector).remove();
+            } 
+            animWrap.expanding = true;
+            animWrap.collapsing = false;
+        }
+    },
+
+    onExpand: function(parent) {
+        var me = this,
+            queue = me.animQueue,
+            id = parent.getId(),
+            animWrap,
+            animateEl, 
+            targetEl,
+            queueItem;        
+        
+        if (me.singleExpand) {
+            me.ensureSingleExpand(parent);
+        }
+        
+        animWrap = me.getAnimWrap(parent);
+
+        if (!animWrap) {
+            me.resetScrollers();
+            return;
+        }
+        
+        animateEl = animWrap.animateEl;
+        targetEl = animWrap.targetEl;
+
+        animateEl.stopAnimation();
+        // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
+        queue[id] = true;
+        animateEl.slideIn('t', {
+            duration: me.expandDuration,
+            listeners: {
+                scope: me,
+                lastframe: function() {
+                    // Move all the nodes out of the anim wrap to their proper location
+                    animWrap.el.insertSibling(targetEl.query(me.itemSelector), 'before');
+                    animWrap.el.remove();
+                    me.resetScrollers();
+                    delete animWrap.record.animWrap;
+                    delete queue[id];
+                }
+            }
+        });
+        
+        animWrap.isAnimating = true;
+    },
+    
+    resetScrollers: function(){
+        var panel = this.panel;
+        
+        panel.determineScrollbars();
+        panel.invalidateScroller();
+    },
+
+    onBeforeCollapse: function(parent, records, index) {
+        var me = this,
+            animWrap;
+            
+        if (!me.animate) {
+            return;
+        }
+
+        if (me.getNode(parent)) {
+            animWrap = me.getAnimWrap(parent);
+            if (!animWrap) {
+                animWrap = parent.animWrap = me.createAnimWrap(parent, index);
+            }
+            else if (animWrap.expanding) {
+                // If we collapse this node while it is still expanding then we
+                // have to remove the nodes from the animWrap.
+                animWrap.targetEl.select(this.itemSelector).remove();
+            }
+            animWrap.expanding = false;
+            animWrap.collapsing = true;
+        }
+    },
+    
+    onCollapse: function(parent) {
+        var me = this,
+            queue = me.animQueue,
+            id = parent.getId(),
+            animWrap = me.getAnimWrap(parent),
+            animateEl, targetEl;
+
+        if (!animWrap) {
+            me.resetScrollers();
+            return;
+        }
+        
+        animateEl = animWrap.animateEl;
+        targetEl = animWrap.targetEl;
+
+        queue[id] = true;
+        
+        // @TODO: we are setting it to 1 because quirks mode on IE seems to have issues with 0
+        animateEl.stopAnimation();
+        animateEl.slideOut('t', {
+            duration: me.collapseDuration,
+            listeners: {
+                scope: me,
+                lastframe: function() {
+                    animWrap.el.remove();
+                    delete animWrap.record.animWrap;
+                    me.resetScrollers();
+                    delete queue[id];
+                }             
+            }
+        });
+        animWrap.isAnimating = true;
+    },
+    
+    /**
+     * Checks if a node is currently undergoing animation
+     * @private
+     * @param {Ext.data.Model} node The node
+     * @return {Boolean} True if the node is animating
+     */
+    isAnimating: function(node) {
+        return !!this.animQueue[node.getId()];    
+    },
+    
+    collectData: function(records) {
+        var data = this.callParent(arguments),
+            rows = data.rows,
+            len = rows.length,
+            i = 0,
+            row, record;
+            
+        for (; i < len; i++) {
+            row = rows[i];
+            record = records[i];
+            if (record.get('qtip')) {
+                row.rowAttr = 'data-qtip="' + record.get('qtip') + '"';
+                if (record.get('qtitle')) {
+                    row.rowAttr += ' ' + 'data-qtitle="' + record.get('qtitle') + '"';
+                }
+            }
+            if (record.isExpanded()) {
+                row.rowCls = (row.rowCls || '') + ' ' + this.expandedCls;
+            }
+            if (record.isLoading()) {
+                row.rowCls = (row.rowCls || '') + ' ' + this.loadingCls;
+            }
+        }
+        
+        return data;
+    },
+    
+    /**
+     * Expand a record that is loaded in the view.
+     * @param {Ext.data.Model} record The record to expand
+     * @param {Boolean} deep (optional) True to expand nodes all the way down the tree hierarchy.
+     * @param {Function} callback (optional) The function to run after the expand is completed
+     * @param {Object} scope (optional) The scope of the callback function.
+     */
+    expand: function(record, deep, callback, scope) {
+        return record.expand(deep, callback, scope);
+    },
+    
+    /**
+     * Collapse a record that is loaded in the view.
+     * @param {Ext.data.Model} record The record to collapse
+     * @param {Boolean} deep (optional) True to collapse nodes all the way up the tree hierarchy.
+     * @param {Function} callback (optional) The function to run after the collapse is completed
+     * @param {Object} scope (optional) The scope of the callback function.
+     */
+    collapse: function(record, deep, callback, scope) {
+        return record.collapse(deep, callback, scope);
+    },
+    
+    /**
+     * Toggle a record between expanded and collapsed.
+     * @param {Ext.data.Record} recordInstance
+     */
+    toggle: function(record) {
+        this[record.isExpanded() ? 'collapse' : 'expand'](record);
+    },
+    
+    onItemDblClick: function(record, item, index) {
+        this.callParent(arguments);
+        if (this.toggleOnDblClick) {
+            this.toggle(record);
+        }
+    },
+    
+    onBeforeItemMouseDown: function(record, item, index, e) {
+        if (e.getTarget(this.expanderSelector, item)) {
+            return false;
+        }
+        return this.callParent(arguments);
+    },
+    
+    onItemClick: function(record, item, index, e) {
+        if (e.getTarget(this.expanderSelector, item)) {
+            this.toggle(record);
+            return false;
+        }
+        return this.callParent(arguments);
+    },
+    
+    onExpanderMouseOver: function(e, t) {
+        e.getTarget(this.cellSelector, 10, true).addCls(this.expanderIconOverCls);
+    },
+    
+    onExpanderMouseOut: function(e, t) {
+        e.getTarget(this.cellSelector, 10, true).removeCls(this.expanderIconOverCls);
+    },
+    
+    /**
+     * Gets the base TreeStore from the bound TreePanel.
+     */
+    getTreeStore: function() {
+        return this.panel.store;
+    },    
+    
+    ensureSingleExpand: function(node) {
+        var parent = node.parentNode;
+        if (parent) {
+            parent.eachChild(function(child) {
+                if (child !== node && child.isExpanded()) {
+                    child.collapse();
+                }
+            });
+        }
+    }
+});
+/**
+ * @class Ext.tree.Panel
+ * @extends Ext.panel.Table
+ * 
+ * The TreePanel provides tree-structured UI representation of tree-structured data.
+ * A TreePanel must be bound to a {@link Ext.data.TreeStore}. TreePanel's support
+ * multiple columns through the {@link columns} configuration. 
+ * 
+ * Simple TreePanel using inline data.
+ *
+ * {@img Ext.tree.Panel/Ext.tree.Panel1.png Ext.tree.Panel component}
+ * 
+ * ## Simple Tree Panel (no columns)
+ *
+ *     var store = Ext.create('Ext.data.TreeStore', {
+ *         root: {
+ *             expanded: true, 
+ *             text:"",
+ *             user:"",
+ *             status:"", 
+ *             children: [
+ *                 { text:"detention", leaf: true },
+ *                 { text:"homework", expanded: true, 
+ *                     children: [
+ *                         { text:"book report", leaf: true },
+ *                         { text:"alegrbra", leaf: true}
+ *                     ]
+ *                 },
+ *                 { text: "buy lottery tickets", leaf:true }
+ *             ]
+ *         }
+ *     });     
+ *             
+ *     Ext.create('Ext.tree.Panel', {
+ *         title: 'Simple Tree',
+ *         width: 200,
+ *         height: 150,
+ *         store: store,
+ *         rootVisible: false,        
+ *         renderTo: Ext.getBody()
+ *     });
+ *
+ * @xtype treepanel
+ */
+Ext.define('Ext.tree.Panel', {
+    extend: 'Ext.panel.Table',
+    alias: 'widget.treepanel',
+    alternateClassName: ['Ext.tree.TreePanel', 'Ext.TreePanel'],
+    requires: ['Ext.tree.View', 'Ext.selection.TreeModel', 'Ext.tree.Column'],
+    viewType: 'treeview',
+    selType: 'treemodel',
+    
+    treeCls: Ext.baseCSSPrefix + 'tree-panel',
+    
+    /**
+     * @cfg {Boolean} lines false to disable tree lines (defaults to true)
+     */
+    lines: true,
+    
+    /**
+     * @cfg {Boolean} useArrows true to use Vista-style arrows in the tree (defaults to false)
+     */
+    useArrows: false,
+    
+    /**
+     * @cfg {Boolean} singleExpand <tt>true</tt> if only 1 node per branch may be expanded
+     */
+    singleExpand: false,
+    
+    ddConfig: {
+        enableDrag: true,
+        enableDrop: true
+    },
+    
+    /** 
+     * @cfg {Boolean} animate <tt>true</tt> to enable animated expand/collapse (defaults to the value of {@link Ext#enableFx Ext.enableFx})
+     */
+            
+    /** 
+     * @cfg {Boolean} rootVisible <tt>false</tt> to hide the root node (defaults to <tt>true</tt>)
+     */
+    rootVisible: true,
+    
+    /** 
+     * @cfg {Boolean} displayField The field inside the model that will be used as the node's text. (defaults to <tt>text</tt>)
+     */    
+    displayField: 'text',
+
+    /** 
+     * @cfg {Boolean} root Allows you to not specify a store on this TreePanel. This is useful for creating a simple
+     * tree with preloaded data without having to specify a TreeStore and Model. A store and model will be created and
+     * root will be passed to that store.
+     */
+    root: null,
+    
+    // Required for the Lockable Mixin. These are the configurations which will be copied to the
+    // normal and locked sub tablepanels
+    normalCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible', 'scroll'],
+    lockedCfgCopy: ['displayField', 'root', 'singleExpand', 'useArrows', 'lines', 'rootVisible'],
+
+    /**
+     * @cfg {Boolean} hideHeaders
+     * Specify as <code>true</code> to hide the headers.
+     */
+    
+    /**
+     * @cfg {Boolean} folderSort Set to true to automatically prepend a leaf sorter to the store (defaults to <tt>undefined</tt>)
+     */ 
+    
+    constructor: function(config) {
+        config = config || {};
+        if (config.animate === undefined) {
+            config.animate = Ext.enableFx;
+        }
+        this.enableAnimations = config.animate;
+        delete config.animate;
+        
+        this.callParent([config]);
+    },
+    
+    initComponent: function() {
+        var me = this,
+            cls = [me.treeCls];
+
+        if (me.useArrows) {
+            cls.push(Ext.baseCSSPrefix + 'tree-arrows');
+            me.lines = false;
+        }
+        
+        if (me.lines) {
+            cls.push(Ext.baseCSSPrefix + 'tree-lines');
+        } else if (!me.useArrows) {
+            cls.push(Ext.baseCSSPrefix + 'tree-no-lines');
+        }
+
+        if (!me.store || Ext.isObject(me.store) && !me.store.isStore) {
+            me.store = Ext.create('Ext.data.TreeStore', Ext.apply({}, me.store || {}, {
+                root: me.root,
+                fields: me.fields,
+                model: me.model,
+                folderSort: me.folderSort
+            }));
+        }
+        else if (me.root) {
+            me.store = Ext.data.StoreManager.lookup(me.store);
+            me.store.setRootNode(me.root);
+            if (me.folderSort !== undefined) {
+                me.store.folderSort = me.folderSort;
+                me.store.sort();
+            }            
+        }
+        
+        // I'm not sure if we want to this. It might be confusing
+        // if (me.initialConfig.rootVisible === undefined && !me.getRootNode()) {
+        //     me.rootVisible = false;
+        // }
+        
+        me.viewConfig = Ext.applyIf(me.viewConfig || {}, {
+            rootVisible: me.rootVisible,
+            animate: me.enableAnimations,
+            singleExpand: me.singleExpand,
+            node: me.store.getRootNode(),
+            hideHeaders: me.hideHeaders
+        });
+        
+        me.mon(me.store, {
+            scope: me,
+            rootchange: me.onRootChange,
+            clear: me.onClear
+        });
+    
+        me.relayEvents(me.store, [
+            /**
+             * @event beforeload
+             * Event description
+             * @param {Ext.data.Store} store This Store
+             * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to load the Store
+             */
+            'beforeload',
+
+            /**
+             * @event load
+             * Fires whenever the store reads data from a remote data source.
+             * @param {Ext.data.store} this
+             * @param {Array} records An array of records
+             * @param {Boolean} successful True if the operation was successful.
+             */
+            'load'   
+        ]);
+        
+        me.store.on({
+            /**
+             * @event itemappend
+             * Fires when a new child node is appended to a node in the tree.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The newly appended node
+             * @param {Number} index The index of the newly appended node
+             */
+            append: me.createRelayer('itemappend'),
+            
+            /**
+             * @event itemremove
+             * Fires when a child node is removed from a node in the tree
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node removed
+             */
+            remove: me.createRelayer('itemremove'),
+            
+            /**
+             * @event itemmove
+             * Fires when a node is moved to a new location in the tree
+             * @param {Tree} tree The owner tree
+             * @param {Node} node The node moved
+             * @param {Node} oldParent The old parent of this node
+             * @param {Node} newParent The new parent of this node
+             * @param {Number} index The index it was moved to
+             */
+            move: me.createRelayer('itemmove'),
+            
+            /**
+             * @event iteminsert
+             * Fires when a new child node is inserted in a node in tree
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node inserted
+             * @param {Node} refNode The child node the node was inserted before
+             */
+            insert: me.createRelayer('iteminsert'),
+            
+            /**
+             * @event beforeitemappend
+             * Fires before a new child is appended to a node in this tree, return false to cancel the append.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be appended
+             */
+            beforeappend: me.createRelayer('beforeitemappend'),
+            
+            /**
+             * @event beforeitemremove
+             * Fires before a child is removed from a node in this tree, return false to cancel the remove.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be removed
+             */
+            beforeremove: me.createRelayer('beforeitemremove'),
+            
+            /**
+             * @event beforeitemmove
+             * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
+             * @param {Tree} tree The owner tree
+             * @param {Node} node The node being moved
+             * @param {Node} oldParent The parent of the node
+             * @param {Node} newParent The new parent the node is moving to
+             * @param {Number} index The index it is being moved to
+             */
+            beforemove: me.createRelayer('beforeitemmove'),
+            
+            /**
+             * @event beforeiteminsert
+             * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
+             * @param {Tree} tree The owner tree
+             * @param {Node} parent The parent node
+             * @param {Node} node The child node to be inserted
+             * @param {Node} refNode The child node the node is being inserted before
+             */
+            beforeinsert: me.createRelayer('beforeiteminsert'),
+             
+            /**
+             * @event itemexpand
+             * Fires when a node is expanded.
+             * @param {Node} this The expanding node
+             */
+            expand: me.createRelayer('itemexpand'),
+             
+            /**
+             * @event itemcollapse
+             * Fires when a node is collapsed.
+             * @param {Node} this The collapsing node
+             */
+            collapse: me.createRelayer('itemcollapse'),
+             
+            /**
+             * @event beforeitemexpand
+             * Fires before a node is expanded.
+             * @param {Node} this The expanding node
+             */
+            beforeexpand: me.createRelayer('beforeitemexpand'),
+             
+            /**
+             * @event beforeitemcollapse
+             * Fires before a node is collapsed.
+             * @param {Node} this The collapsing node
+             */
+            beforecollapse: me.createRelayer('beforeitemcollapse')
+        });
+        
+        // If the user specifies the headers collection manually then dont inject our own
+        if (!me.columns) {
+            if (me.initialConfig.hideHeaders === undefined) {
+                me.hideHeaders = true;
+            }
+            me.columns = [{
+                xtype    : 'treecolumn',
+                text     : 'Name',
+                flex     : 1,
+                dataIndex: me.displayField         
+            }];
+        }
+        
+        if (me.cls) {
+            cls.push(me.cls);
+        }
+        me.cls = cls.join(' ');
+        me.callParent();
+        
+        me.relayEvents(me.getView(), [
+            /**
+             * @event checkchange
+             * Fires when a node with a checkbox's checked property changes
+             * @param {Ext.data.Model} node The node who's checked property was changed
+             * @param {Boolean} checked The node's new checked state
+             */
+            'checkchange'
+        ]);
+            
+        // If the root is not visible and there is no rootnode defined, then just lets load the store
+        if (!me.getView().rootVisible && !me.getRootNode()) {
+            me.setRootNode({
+                expanded: true
+            });
+        }
+    },
+    
+    onClear: function(){
+        this.view.onClear();
+    },
+    
+    setRootNode: function() {
+        return this.store.setRootNode.apply(this.store, arguments);
+    },
+    
+    getRootNode: function() {
+        return this.store.getRootNode();
+    },
+    
+    onRootChange: function(root) {
+        this.view.setRootNode(root);
+    },
+
+    /**
+     * Retrieve an array of checked records.
+     * @return {Array} An array containing the checked records
+     */
+    getChecked: function() {
+        return this.getView().getChecked();
+    },
+    
+    isItemChecked: function(rec) {
+        return rec.get('checked');
+    },
+        
+    /**
+     * Expand all nodes
+     * @param {Function} callback (optional) A function to execute when the expand finishes.
+     * @param {Object} scope (optional) The scope of the callback function
+     */
+    expandAll : function(callback, scope) {
+        var root = this.getRootNode();
+        if (root) {
+            root.expand(true, callback, scope);
+        }
+    },
+
+    /**
+     * Collapse all nodes
+     * @param {Function} callback (optional) A function to execute when the collapse finishes.
+     * @param {Object} scope (optional) The scope of the callback function
+     */
+    collapseAll : function(callback, scope) {
+        var root = this.getRootNode();
+        if (root) {
+            if (this.getView().rootVisible) {
+                root.collapse(true, callback, scope);
+            }
+            else {
+                root.collapseChildren(true, callback, scope);
+            }
+        }
+    },
+
+    /**
+     * Expand the tree to the path of a particular node.
+     * @param {String} path The path to expand
+     * @param {String} field (optional) The field to get the data from. Defaults to the model idProperty.
+     * @param {String} separator (optional) A separator to use. Defaults to <tt>'/'</tt>.
+     * @param {Function} callback (optional) A function to execute when the expand finishes. The callback will be called with
+     * (success, lastNode) where success is if the expand was successful and lastNode is the last node that was expanded.
+     * @param {Object} scope (optional) The scope of the callback function
+     */
+    expandPath: function(path, field, separator, callback, scope) {
+        var me = this,
+            current = me.getRootNode(),
+            index = 1,
+            view = me.getView(),
+            keys,
+            expander;
+        
+        field = field || me.getRootNode().idProperty;
+        separator = separator || '/';
+        
+        if (Ext.isEmpty(path)) {
+            Ext.callback(callback, scope || me, [false, null]);
+            return;
+        }
+        
+        keys = path.split(separator);
+        if (current.get(field) != keys[1]) {
+            // invalid root
+            Ext.callback(callback, scope || me, [false, current]);
+            return;
+        }
+        
+        expander = function(){
+            if (++index === keys.length) {
+                Ext.callback(callback, scope || me, [true, current]);
+                return;
+            }
+            var node = current.findChild(field, keys[index]);
+            if (!node) {
+                Ext.callback(callback, scope || me, [false, current]);
+                return;
+            }
+            current = node;
+            current.expand(false, expander);
+        };
+        current.expand(false, expander);
+    },
+    
+    /**
+     * Expand the tree to the path of a particular node, then selecti t.
+     * @param {String} path The path to select
+     * @param {String} field (optional) The field to get the data from. Defaults to the model idProperty.
+     * @param {String} separator (optional) A separator to use. Defaults to <tt>'/'</tt>.
+     * @param {Function} callback (optional) A function to execute when the select finishes. The callback will be called with
+     * (bSuccess, oLastNode) where bSuccess is if the select was successful and oLastNode is the last node that was expanded.
+     * @param {Object} scope (optional) The scope of the callback function
+     */
+    selectPath: function(path, field, separator, callback, scope) {
+        var me = this,
+            keys,
+            last;
+        
+        field = field || me.getRootNode().idProperty;
+        separator = separator || '/';
+        
+        keys = path.split(separator);
+        last = keys.pop();
+        
+        me.expandPath(keys.join('/'), field, separator, function(success, node){
+            var doSuccess = false;
+            if (success && node) {
+                node = node.findChild(field, last);
+                if (node) {
+                    me.getSelectionModel().select(node);
+                    Ext.callback(callback, scope || me, [true, node]);
+                    doSuccess = true;
+                }
+            } else if (node === me.getRootNode()) {
+                doSuccess = true;
+            }
+            Ext.callback(callback, scope || me, [doSuccess, node]);
+        }, me);
+    }
+});
+/**
+ * @class Ext.view.DragZone
+ * @extends Ext.dd.DragZone
+ * @private
+ */
+Ext.define('Ext.view.DragZone', {
+    extend: 'Ext.dd.DragZone',
+    containerScroll: false,
+
+    constructor: function(config) {
+        var me = this;
+
+        Ext.apply(me, config);
+
+        // Create a ddGroup unless one has been configured.
+        // User configuration of ddGroups allows users to specify which
+        // DD instances can interact with each other. Using one
+        // based on the id of the View would isolate it and mean it can only
+        // interact with a DropZone on the same View also using a generated ID.
+        if (!me.ddGroup) {
+            me.ddGroup = 'view-dd-zone-' + me.view.id;
+        }
+
+        // Ext.dd.DragDrop instances are keyed by the ID of their encapsulating element.
+        // So a View's DragZone cannot use the View's main element because the DropZone must use that
+        // because the DropZone may need to scroll on hover at a scrolling boundary, and it is the View's
+        // main element which handles scrolling.
+        // We use the View's parent element to drag from. Ideally, we would use the internal structure, but that 
+        // is transient; DataView's recreate the internal structure dynamically as data changes.
+        // TODO: Ext 5.0 DragDrop must allow multiple DD objects to share the same element.
+        me.callParent([me.view.el.dom.parentNode]);
+
+        me.ddel = Ext.get(document.createElement('div'));
+        me.ddel.addCls(Ext.baseCSSPrefix + 'grid-dd-wrap');
+    },
+
+    init: function(id, sGroup, config) {
+        this.initTarget(id, sGroup, config);
+        this.view.mon(this.view, {
+            itemmousedown: this.onItemMouseDown,
+            scope: this
+        });
+    },
+
+    onItemMouseDown: function(view, record, item, index, e) {
+        if (!this.isPreventDrag(e, record, item, index)) {
+            this.handleMouseDown(e);
+        }
+    },
+
+    // private template method
+    isPreventDrag: function(e) {
+        return false;
+    },
+
+    getDragData: function(e) {
+        var view = this.view,
+            item = e.getTarget(view.getItemSelector()),
+            record, selectionModel, records;
+
+        if (item) {
+            record = view.getRecord(item);
+            selectionModel = view.getSelectionModel();
+            records = selectionModel.getSelection();
+            return {
+                copy: this.view.copy || (this.view.allowCopy && e.ctrlKey),
+                event: new Ext.EventObjectImpl(e),
+                view: view,
+                ddel: this.ddel,
+                item: item,
+                records: records,
+                fromPosition: Ext.fly(item).getXY()
+            };
+        }
+    },
+
+    onInitDrag: function(x, y) {
+        var me = this,
+            data = me.dragData,
+            view = data.view,
+            selectionModel = view.getSelectionModel(),
+            record = view.getRecord(data.item),
+            e = data.event;
+
+        // Update the selection to match what would have been selected if the user had
+        // done a full click on the target node rather than starting a drag from it
+        if (!selectionModel.isSelected(record) || e.hasModifier()) {
+            selectionModel.selectWithEvent(record, e);
+        }
+        data.records = selectionModel.getSelection();
+
+        me.ddel.update(me.getDragText());
+        me.proxy.update(me.ddel.dom);
+        me.onStartDrag(x, y);
+        return true;
+    },
+
+    getDragText: function() {
+        var count = this.dragData.records.length;
+        return Ext.String.format(this.dragText, count, count == 1 ? '' : 's');
+    },
+
+    getRepairXY : function(e, data){
+        return data ? data.fromPosition : false;
+    }
+});
+Ext.define('Ext.tree.ViewDragZone', {
+    extend: 'Ext.view.DragZone',
+
+    isPreventDrag: function(e, record) {
+        return (record.get('allowDrag') === false) || !!e.getTarget(this.view.expanderSelector);
+    },
+    
+    afterRepair: function() {
+        var me = this,
+            view = me.view,
+            selectedRowCls = view.selectedItemCls,
+            records = me.dragData.records,
+            fly = Ext.fly;
+        
+        if (Ext.enableFx && me.repairHighlight) {
+            // Roll through all records and highlight all the ones we attempted to drag.
+            Ext.Array.forEach(records, function(record) {
+                // anonymous fns below, don't hoist up unless below is wrapped in
+                // a self-executing function passing in item.
+                var item = view.getNode(record);
+                
+                // We must remove the selected row class before animating, because
+                // the selected row class declares !important on its background-color.
+                fly(item.firstChild).highlight(me.repairHighlightColor, {
+                    listeners: {
+                        beforeanimate: function() {
+                            if (view.isSelected(item)) {
+                                fly(item).removeCls(selectedRowCls);
+                            }
+                        },
+                        afteranimate: function() {
+                            if (view.isSelected(item)) {
+                                fly(item).addCls(selectedRowCls);
+                            }
+                        }
+                    }
+                });
+            });
+        }
+        me.dragging = false;
+    }
+});
+/**
+ * @class Ext.tree.ViewDropZone
+ * @extends Ext.view.DropZone
+ * @private
+ */
+Ext.define('Ext.tree.ViewDropZone', {
+    extend: 'Ext.view.DropZone',
+
+    /**
+     * @cfg {Boolean} allowParentInsert
+     * Allow inserting a dragged node between an expanded parent node and its first child that will become a
+     * sibling of the parent when dropped (defaults to false)
+     */
+    allowParentInserts: false,
+    /**
+     * @cfg {String} allowContainerDrop
+     * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false)
+     */
+    allowContainerDrops: false,
+
+    /**
+     * @cfg {String} appendOnly
+     * True if the tree should only allow append drops (use for trees which are sorted, defaults to false)
+     */
+    appendOnly: false,
+
+    /**
+     * @cfg {String} expandDelay
+     * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node
+     * over the target (defaults to 500)
+     */
+    expandDelay : 500,
+
+    indicatorCls: 'x-tree-ddindicator',
+
+    // private
+    expandNode : function(node) {
+        var view = this.view;
+        if (!node.isLeaf() && !node.isExpanded()) {
+            view.expand(node);
+            this.expandProcId = false;
+        }
+    },
+
+    // private
+    queueExpand : function(node) {
+        this.expandProcId = Ext.Function.defer(this.expandNode, this.expandDelay, this, [node]);
+    },
+
+    // private
+    cancelExpand : function() {
+        if (this.expandProcId) {
+            clearTimeout(this.expandProcId);
+            this.expandProcId = false;
+        }
+    },
+
+    getPosition: function(e, node) {
+        var view = this.view,
+            record = view.getRecord(node),
+            y = e.getPageY(),
+            noAppend = record.isLeaf(),
+            noBelow = false,
+            region = Ext.fly(node).getRegion(),
+            fragment;
+
+        // If we are dragging on top of the root node of the tree, we always want to append.
+        if (record.isRoot()) {
+            return 'append';
+        }
+
+        // Return 'append' if the node we are dragging on top of is not a leaf else return false.
+        if (this.appendOnly) {
+            return noAppend ? false : 'append';
+        }
+
+        if (!this.allowParentInsert) {
+            noBelow = record.hasChildNodes() && record.isExpanded();
+        }
+
+        fragment = (region.bottom - region.top) / (noAppend ? 2 : 3);
+        if (y >= region.top && y < (region.top + fragment)) {
+            return 'before';
+        }
+        else if (!noBelow && (noAppend || (y >= (region.bottom - fragment) && y <= region.bottom))) {
+            return 'after';
+        }
+        else {
+            return 'append';
+        }
+    },
+
+    isValidDropPoint : function(node, position, dragZone, e, data) {
+        if (!node || !data.item) {
+            return false;
+        }
+
+        var view = this.view,
+            targetNode = view.getRecord(node),
+            draggedRecords = data.records,
+            dataLength = draggedRecords.length,
+            ln = draggedRecords.length,
+            i, record;
+
+        // No drop position, or dragged records: invalid drop point
+        if (!(targetNode && position && dataLength)) {
+            return false;
+        }
+
+        // If the targetNode is within the folder we are dragging
+        for (i = 0; i < ln; i++) {
+            record = draggedRecords[i];
+            if (record.isNode && record.contains(targetNode)) {
+                return false;
+            }
+        }
+        
+        // Respect the allowDrop field on Tree nodes
+        if (position === 'append' && targetNode.get('allowDrop') == false) {
+            return false;
+        }
+        else if (position != 'append' && targetNode.parentNode.get('allowDrop') == false) {
+            return false;
+        }
+
+        // If the target record is in the dragged dataset, then invalid drop
+        if (Ext.Array.contains(draggedRecords, targetNode)) {
+             return false;
+        }
+
+        // @TODO: fire some event to notify that there is a valid drop possible for the node you're dragging
+        // Yes: this.fireViewEvent(blah....) fires an event through the owning View.
+        return true;
+    },
+
+    onNodeOver : function(node, dragZone, e, data) {
+        var position = this.getPosition(e, node),
+            returnCls = this.dropNotAllowed,
+            view = this.view,
+            targetNode = view.getRecord(node),
+            indicator = this.getIndicator(),
+            indicatorX = 0,
+            indicatorY = 0;
+
+        // auto node expand check
+        this.cancelExpand();
+        if (position == 'append' && !this.expandProcId && !Ext.Array.contains(data.records, targetNode) && !targetNode.isLeaf() && !targetNode.isExpanded()) {
+            this.queueExpand(targetNode);
+        }
+            
+        if (this.isValidDropPoint(node, position, dragZone, e, data)) {
+            this.valid = true;
+            this.currentPosition = position;
+            this.overRecord = targetNode;
+
+            indicator.setWidth(Ext.fly(node).getWidth());
+            indicatorY = Ext.fly(node).getY() - Ext.fly(view.el).getY() - 1;
+
+            if (position == 'before') {
+                returnCls = targetNode.isFirst() ? Ext.baseCSSPrefix + 'tree-drop-ok-above' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
+                indicator.showAt(0, indicatorY);
+                indicator.toFront();
+            }
+            else if (position == 'after') {
+                returnCls = targetNode.isLast() ? Ext.baseCSSPrefix + 'tree-drop-ok-below' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
+                indicatorY += Ext.fly(node).getHeight();
+                indicator.showAt(0, indicatorY);
+                indicator.toFront();
+            }
+            else {
+                returnCls = Ext.baseCSSPrefix + 'tree-drop-ok-append';
+                // @TODO: set a class on the parent folder node to be able to style it
+                indicator.hide();
+            }
+        }
+        else {
+            this.valid = false;
+        }
+
+        this.currentCls = returnCls;
+        return returnCls;
+    },
+
+    onContainerOver : function(dd, e, data) {
+        return e.getTarget('.' + this.indicatorCls) ? this.currentCls : this.dropNotAllowed;
+    },
+    
+    notifyOut: function() {
+        this.callParent(arguments);
+        this.cancelExpand();
+    },
+
+    handleNodeDrop : function(data, targetNode, position) {
+        var me = this,
+            view = me.view,
+            parentNode = targetNode.parentNode,
+            store = view.getStore(),
+            recordDomNodes = [],
+            records, i, len,
+            insertionMethod, argList,
+            needTargetExpand,
+            transferData,
+            processDrop;
+
+        // If the copy flag is set, create a copy of the Models with the same IDs
+        if (data.copy) {
+            records = data.records;
+            data.records = [];
+            for (i = 0, len = records.length; i < len; i++) {
+                data.records.push(Ext.apply({}, records[i].data));
+            }
+        }
+
+        // Cancel any pending expand operation
+        me.cancelExpand();
+
+        // Grab a reference to the correct node insertion method.
+        // Create an arg list array intended for the apply method of the
+        // chosen node insertion method.
+        // Ensure the target object for the method is referenced by 'targetNode'
+        if (position == 'before') {
+            insertionMethod = parentNode.insertBefore;
+            argList = [null, targetNode];
+            targetNode = parentNode;
+        }
+        else if (position == 'after') {
+            if (targetNode.nextSibling) {
+                insertionMethod = parentNode.insertBefore;
+                argList = [null, targetNode.nextSibling];
+            }
+            else {
+                insertionMethod = parentNode.appendChild;
+                argList = [null];
+            }
+            targetNode = parentNode;
+        }
+        else {
+            if (!targetNode.isExpanded()) {
+                needTargetExpand = true;
+            }
+            insertionMethod = targetNode.appendChild;
+            argList = [null];
+        }
+
+        // A function to transfer the data into the destination tree
+        transferData = function() {
+            var node;
+            for (i = 0, len = data.records.length; i < len; i++) {
+                argList[0] = data.records[i];
+                node = insertionMethod.apply(targetNode, argList);
+                
+                if (Ext.enableFx && me.dropHighlight) {
+                    recordDomNodes.push(view.getNode(node));
+                }
+            }
+            
+            // Kick off highlights after everything's been inserted, so they are
+            // more in sync without insertion/render overhead.
+            if (Ext.enableFx && me.dropHighlight) {
+                //FIXME: the check for n.firstChild is not a great solution here. Ideally the line should simply read 
+                //Ext.fly(n.firstChild) but this yields errors in IE6 and 7. See ticket EXTJSIV-1705 for more details
+                Ext.Array.forEach(recordDomNodes, function(n) {
+                    Ext.fly(n.firstChild ? n.firstChild : n).highlight(me.dropHighlightColor);
+                });
+            }
+        };
+
+        // If dropping right on an unexpanded node, transfer the data after it is expanded.
+        if (needTargetExpand) {
+            targetNode.expand(false, transferData);
+        }
+        // Otherwise, call the data transfer function immediately
+        else {
+            transferData();
+        }
+    }
+});
+/**
+ * @class Ext.tree.ViewDDPlugin
+ * @extends Ext.AbstractPlugin
+ * <p>This plugin provides drag and/or drop functionality for a TreeView.</p>
+ * <p>It creates a specialized instance of {@link Ext.dd.DragZone DragZone} which knows how to drag out of a {@link Ext.tree.View TreeView}
+ * and loads the data object which is passed to a cooperating {@link Ext.dd.DragZone DragZone}'s methods with the following properties:<ul>
+ * <li>copy : Boolean
+ *  <div class="sub-desc">The value of the TreeView's <code>copy</code> property, or <code>true</code> if the TreeView was configured
+ *  with <code>allowCopy: true</code> <u>and</u> the control key was pressed when the drag operation was begun.</div></li>
+ * <li>view : TreeView
+ *  <div class="sub-desc">The source TreeView from which the drag originated.</div></li>
+ * <li>ddel : HtmlElement
+ *  <div class="sub-desc">The drag proxy element which moves with the mouse</div></li>
+ * <li>item : HtmlElement
+ *  <div class="sub-desc">The TreeView node upon which the mousedown event was registered.</div></li>
+ * <li>records : Array
+ *  <div class="sub-desc">An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source TreeView.</div></li>
+ * </ul></p>
+ * <p>It also creates a specialized instance of {@link Ext.dd.DropZone} which cooperates with other DropZones which are members of the same
+ * ddGroup which processes such data objects.</p>
+ * <p>Adding this plugin to a view means that two new events may be fired from the client TreeView, <code>{@link #event-beforedrop beforedrop}</code> and
+ * <code>{@link #event-drop drop}</code></p>
+ */
+Ext.define('Ext.tree.plugin.TreeViewDragDrop', {
+    extend: 'Ext.AbstractPlugin',
+    alias: 'plugin.treeviewdragdrop',
+
+    uses: [
+        'Ext.tree.ViewDragZone',
+        'Ext.tree.ViewDropZone'
+    ],
+
+    /**
+     * @event beforedrop
+     * <p><b>This event is fired through the TreeView. Add listeners to the TreeView object</b></p>
+     * <p>Fired when a drop gesture has been triggered by a mouseup event in a valid drop position in the TreeView.
+     * @param {HtmlElement} node The TreeView node <b>if any</b> over which the mouse was positioned.</p>
+     * <p>Returning <code>false</code> to this event signals that the drop gesture was invalid, and if the drag proxy
+     * will animate back to the point from which the drag began.</p>
+     * <p>Returning <code>0</code> To this event signals that the data transfer operation should not take place, but
+     * that the gesture was valid, and that the repair operation should not take place.</p>
+     * <p>Any other return value continues with the data transfer operation.</p>
+     * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone DragZone}'s
+     * {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties:<ul>
+     * <li>copy : Boolean
+     *  <div class="sub-desc">The value of the TreeView's <code>copy</code> property, or <code>true</code> if the TreeView was configured
+     *  with <code>allowCopy: true</code> and the control key was pressed when the drag operation was begun</div></li>
+     * <li>view : TreeView
+     *  <div class="sub-desc">The source TreeView from which the drag originated.</div></li>
+     * <li>ddel : HtmlElement
+     *  <div class="sub-desc">The drag proxy element which moves with the mouse</div></li>
+     * <li>item : HtmlElement
+     *  <div class="sub-desc">The TreeView node upon which the mousedown event was registered.</div></li>
+     * <li>records : Array
+     *  <div class="sub-desc">An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source TreeView.</div></li>
+     * </ul>
+     * @param {Ext.data.Model} overModel The Model over which the drop gesture took place.
+     * @param {String} dropPosition <code>"before"</code>, <code>"after"</code> or <code>"append"</code> depending on whether the mouse is above or below the midline of the node,
+     * or the node is a branch node which accepts new child nodes.
+     * @param {Function} dropFunction <p>A function to call to complete the data transfer operation and either move or copy Model instances from the source
+     * View's Store to the destination View's Store.</p>
+     * <p>This is useful when you want to perform some kind of asynchronous processing before confirming
+     * the drop, such as an {@link Ext.window.MessageBox#confirm confirm} call, or an Ajax request.</p>
+     * <p>Return <code>0</code> from this event handler, and call the <code>dropFunction</code> at any time to perform the data transfer.</p>
+     */
+
+    /**
+     * @event drop
+     * <b>This event is fired through the TreeView. Add listeners to the TreeView object</b>
+     * Fired when a drop operation has been completed and the data has been moved or copied.
+     * @param {HtmlElement} node The TreeView node <b>if any</b> over which the mouse was positioned.
+     * @param {Object} data The data object gathered at mousedown time by the cooperating {@link Ext.dd.DragZone DragZone}'s
+     * {@link Ext.dd.DragZone#getDragData getDragData} method it contains the following properties:<ul>
+     * <li>copy : Boolean
+     *  <div class="sub-desc">The value of the TreeView's <code>copy</code> property, or <code>true</code> if the TreeView was configured
+     *  with <code>allowCopy: true</code> and the control key was pressed when the drag operation was begun</div></li>
+     * <li>view : TreeView
+     *  <div class="sub-desc">The source TreeView from which the drag originated.</div></li>
+     * <li>ddel : HtmlElement
+     *  <div class="sub-desc">The drag proxy element which moves with the mouse</div></li>
+     * <li>item : HtmlElement
+     *  <div class="sub-desc">The TreeView node upon which the mousedown event was registered.</div></li>
+     * <li>records : Array
+     *  <div class="sub-desc">An Array of {@link Ext.data.Model Model}s representing the selected data being dragged from the source TreeView.</div></li>
+     * </ul>
+     * @param {Ext.data.Model} overModel The Model over which the drop gesture took place.
+     * @param {String} dropPosition <code>"before"</code>, <code>"after"</code> or <code>"append"</code> depending on whether the mouse is above or below the midline of the node,
+     * or the node is a branch node which accepts new child nodes.
+     */
+
+    dragText : '{0} selected node{1}',
+
+    /**
+     * @cfg {Boolean} allowParentInsert
+     * Allow inserting a dragged node between an expanded parent node and its first child that will become a
+     * sibling of the parent when dropped (defaults to false)
+     */
+    allowParentInserts: false,
+
+    /**
+     * @cfg {String} allowContainerDrop
+     * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false)
+     */
+    allowContainerDrops: false,
+
+    /**
+     * @cfg {String} appendOnly
+     * True if the tree should only allow append drops (use for trees which are sorted, defaults to false)
+     */
+    appendOnly: false,
+
+    /**
+     * @cfg {String} ddGroup
+     * A named drag drop group to which this object belongs.  If a group is specified, then both the DragZones and DropZone
+     * used by this plugin will only interact with other drag drop objects in the same group (defaults to 'TreeDD').
+     */
+    ddGroup : "TreeDD",
+
+    /**
+     * @cfg {String} dragGroup
+     * <p>The ddGroup to which the DragZone will belong.</p>
+     * <p>This defines which other DropZones the DragZone will interact with. Drag/DropZones only interact with other Drag/DropZones
+     * which are members of the same ddGroup.</p>
+     */
+
+    /**
+     * @cfg {String} dropGroup
+     * <p>The ddGroup to which the DropZone will belong.</p>
+     * <p>This defines which other DragZones the DropZone will interact with. Drag/DropZones only interact with other Drag/DropZones
+     * which are members of the same ddGroup.</p>
+     */
+
+    /**
+     * @cfg {String} expandDelay
+     * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node
+     * over the target (defaults to 1000)
+     */
+    expandDelay : 1000,
+
+    /**
+     * @cfg {Boolean} enableDrop
+     * <p>Defaults to <code>true</code></p>
+     * <p>Set to <code>false</code> to disallow the View from accepting drop gestures</p>
+     */
+    enableDrop: true,
+
+    /**
+     * @cfg {Boolean} enableDrag
+     * <p>Defaults to <code>true</code></p>
+     * <p>Set to <code>false</code> to disallow dragging items from the View </p>
+     */
+    enableDrag: true,
+    
+    /**
+     * @cfg {String} nodeHighlightColor The color to use when visually highlighting the dragged
+     * or dropped node (defaults to 'c3daf9' - light blue). The color must be a 6 digit hex value, without
+     * a preceding '#'. See also {@link #nodeHighlightOnDrop} and {@link #nodeHighlightOnRepair}.
+     */
+    nodeHighlightColor: 'c3daf9',
+    
+    /**
+     * @cfg {Boolean} nodeHighlightOnDrop Whether or not to highlight any nodes after they are
+     * successfully dropped on their target. Defaults to the value of `Ext.enableFx`.
+     * See also {@link #nodeHighlightColor} and {@link #nodeHighlightOnRepair}.
+     * @markdown
+     */
+    nodeHighlightOnDrop: Ext.enableFx,
+    
+    /**
+     * @cfg {Boolean} nodeHighlightOnRepair Whether or not to highlight any nodes after they are
+     * repaired from an unsuccessful drag/drop. Defaults to the value of `Ext.enableFx`.
+     * See also {@link #nodeHighlightColor} and {@link #nodeHighlightOnDrop}.
+     * @markdown
+     */
+    nodeHighlightOnRepair: Ext.enableFx,
+
+    init : function(view) {
+        view.on('render', this.onViewRender, this, {single: true});
+    },
+
+    /**
+     * @private
+     * AbstractComponent calls destroy on all its plugins at destroy time.
+     */
+    destroy: function() {
+        Ext.destroy(this.dragZone, this.dropZone);
+    },
+
+    onViewRender : function(view) {
+        var me = this;
+
+        if (me.enableDrag) {
+            me.dragZone = Ext.create('Ext.tree.ViewDragZone', {
+                view: view,
+                ddGroup: me.dragGroup || me.ddGroup,
+                dragText: me.dragText,
+                repairHighlightColor: me.nodeHighlightColor,
+                repairHighlight: me.nodeHighlightOnRepair
+            });
+        }
+
+        if (me.enableDrop) {
+            me.dropZone = Ext.create('Ext.tree.ViewDropZone', {
+                view: view,
+                ddGroup: me.dropGroup || me.ddGroup,
+                allowContainerDrops: me.allowContainerDrops,
+                appendOnly: me.appendOnly,
+                allowParentInserts: me.allowParentInserts,
+                expandDelay: me.expandDelay,
+                dropHighlightColor: me.nodeHighlightColor,
+                dropHighlight: me.nodeHighlightOnDrop
+            });
+        }
+    }
+});
+/**
+ * @class Ext.util.Cookies
+
+Utility class for setting/reading values from browser cookies.
+Values can be written using the {@link #set} method.
+Values can be read using the {@link #get} method.
+A cookie can be invalidated on the client machine using the {@link #clear} method.
+
+ * @markdown
+ * @singleton
+ */
+Ext.define('Ext.util.Cookies', {
+    singleton: true,
+    
+    /**
+     * Create a cookie with the specified name and value. Additional settings
+     * for the cookie may be optionally specified (for example: expiration,
+     * access restriction, SSL).
+     * @param {String} name The name of the cookie to set. 
+     * @param {Mixed} value The value to set for the cookie.
+     * @param {Object} expires (Optional) Specify an expiration date the
+     * cookie is to persist until.  Note that the specified Date object will
+     * be converted to Greenwich Mean Time (GMT). 
+     * @param {String} path (Optional) Setting a path on the cookie restricts
+     * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>). 
+     * @param {String} domain (Optional) Setting a domain restricts access to
+     * pages on a given domain (typically used to allow cookie access across
+     * subdomains). For example, "sencha.com" will create a cookie that can be
+     * accessed from any subdomain of sencha.com, including www.sencha.com,
+     * support.sencha.com, etc.
+     * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
+     * should only be accessible via SSL on a page using the HTTPS protocol.
+     * Defaults to <tt>false</tt>. Note that this will only work if the page
+     * calling this code uses the HTTPS protocol, otherwise the cookie will be
+     * created with default options.
+     */
+    set : function(name, value){
+        var argv = arguments,
+            argc = arguments.length,
+            expires = (argc > 2) ? argv[2] : null,
+            path = (argc > 3) ? argv[3] : '/',
+            domain = (argc > 4) ? argv[4] : null,
+            secure = (argc > 5) ? argv[5] : false;
+            
+        document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
+    },
+
+    /**
+     * Retrieves cookies that are accessible by the current page. If a cookie
+     * does not exist, <code>get()</code> returns <tt>null</tt>.  The following
+     * example retrieves the cookie called "valid" and stores the String value
+     * in the variable <tt>validStatus</tt>.
+     * <pre><code>
+     * var validStatus = Ext.util.Cookies.get("valid");
+     * </code></pre>
+     * @param {String} name The name of the cookie to get
+     * @return {Mixed} Returns the cookie value for the specified name;
+     * null if the cookie name does not exist.
+     */
+    get : function(name){
+        var arg = name + "=",
+            alen = arg.length,
+            clen = document.cookie.length,
+            i = 0,
+            j = 0;
+            
+        while(i < clen){
+            j = i + alen;
+            if(document.cookie.substring(i, j) == arg){
+                return this.getCookieVal(j);
+            }
+            i = document.cookie.indexOf(" ", i) + 1;
+            if(i === 0){
+                break;
+            }
+        }
+        return null;
+    },
+
+    /**
+     * Removes a cookie with the provided name from the browser
+     * if found by setting its expiration date to sometime in the past. 
+     * @param {String} name The name of the cookie to remove
+     * @param {String} path (optional) The path for the cookie. This must be included if you included a path while setting the cookie.
+     */
+    clear : function(name, path){
+        if(this.get(name)){
+            path = path || '/';
+            document.cookie = name + '=' + '; expires=Thu, 01-Jan-70 00:00:01 GMT; path=' + path;
+        }
+    },
+    
+    /**
+     * @private
+     */
+    getCookieVal : function(offset){
+        var endstr = document.cookie.indexOf(";", offset);
+        if(endstr == -1){
+            endstr = document.cookie.length;
+        }
+        return unescape(document.cookie.substring(offset, endstr));
+    }
+});
+
+/**
+ * @class Ext.util.CSS
+ * Utility class for manipulating CSS rules
+ * @singleton
+ */
+Ext.define('Ext.util.CSS', function() {
+    var rules = null;
+    var doc = document;
+
+    var camelRe = /(-[a-z])/gi;
+    var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
+
+    return {
+
+        singleton: true,
+
+        constructor: function() {
+            this.rules = {};
+            this.initialized = false;
+        },
+        /**
+         * Creates a stylesheet from a text blob of rules.
+         * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
+         * @param {String} cssText The text containing the css rules
+         * @param {String} id An id to add to the stylesheet for later removal
+         * @return {StyleSheet}
+         */
+        createStyleSheet : function(cssText, id) {
+            var ss,
+                head = doc.getElementsByTagName("head")[0],
+                styleEl = doc.createElement("style");
+
+            styleEl.setAttribute("type", "text/css");
+            if (id) {
+               styleEl.setAttribute("id", id);
+            }
+
+            if (Ext.isIE) {
+               head.appendChild(styleEl);
+               ss = styleEl.styleSheet;
+               ss.cssText = cssText;
+            } else {
+                try{
+                    styleEl.appendChild(doc.createTextNode(cssText));
+                } catch(e) {
+                   styleEl.cssText = cssText;
+                }
+                head.appendChild(styleEl);
+                ss = styleEl.styleSheet ? styleEl.styleSheet : (styleEl.sheet || doc.styleSheets[doc.styleSheets.length-1]);
+            }
+            this.cacheStyleSheet(ss);
+            return ss;
+        },
+
+        /**
+         * Removes a style or link tag by id
+         * @param {String} id The id of the tag
+         */
+        removeStyleSheet : function(id) {
+            var existing = document.getElementById(id);
+            if (existing) {
+                existing.parentNode.removeChild(existing);
+            }
+        },
+
+        /**
+         * Dynamically swaps an existing stylesheet reference for a new one
+         * @param {String} id The id of an existing link tag to remove
+         * @param {String} url The href of the new stylesheet to include
+         */
+        swapStyleSheet : function(id, url) {
+            var doc = document;
+            this.removeStyleSheet(id);
+            var ss = doc.createElement("link");
+            ss.setAttribute("rel", "stylesheet");
+            ss.setAttribute("type", "text/css");
+            ss.setAttribute("id", id);
+            ss.setAttribute("href", url);
+            doc.getElementsByTagName("head")[0].appendChild(ss);
+        },
+
+        /**
+         * Refresh the rule cache if you have dynamically added stylesheets
+         * @return {Object} An object (hash) of rules indexed by selector
+         */
+        refreshCache : function() {
+            return this.getRules(true);
+        },
+
+        // private
+        cacheStyleSheet : function(ss) {
+            if(!rules){
+                rules = {};
+            }
+            try {// try catch for cross domain access issue
+                var ssRules = ss.cssRules || ss.rules,
+                    selectorText,
+                    i = ssRules.length - 1,
+                    j,
+                    selectors;
+
+                for (; i >= 0; --i) {
+                    selectorText = ssRules[i].selectorText;
+                    if (selectorText) {
+                        // Split in case there are multiple, comma-delimited selectors
+                        selectorText = selectorText.split(',');
+                        selectors = selectorText.length;
+                        for (j = 0; j < selectors; j++) {
+                            rules[Ext.String.trim(selectorText[j]).toLowerCase()] = ssRules[i];
+                        }
+                    }
+                }
+            } catch(e) {}
+        },
+
+        /**
+        * Gets all css rules for the document
+        * @param {Boolean} refreshCache true to refresh the internal cache
+        * @return {Object} An object (hash) of rules indexed by selector
+        */
+        getRules : function(refreshCache) {
+            if (rules === null || refreshCache) {
+                rules = {};
+                var ds = doc.styleSheets,
+                    i = 0,
+                    len = ds.length;
+
+                for (; i < len; i++) {
+                    try {
+                        if (!ds[i].disabled) {
+                            this.cacheStyleSheet(ds[i]);
+                        }
+                    } catch(e) {} 
+                }
+            }
+            return rules;
+        },
+
+        /**
+         * Gets an an individual CSS rule by selector(s)
+         * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
+         * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
+         * @return {CSSRule} The CSS rule or null if one is not found
+         */
+        getRule: function(selector, refreshCache) {
+            var rs = this.getRules(refreshCache);
+            if (!Ext.isArray(selector)) {
+                return rs[selector.toLowerCase()];
+            }
+            for (var i = 0; i < selector.length; i++) {
+                if (rs[selector[i]]) {
+                    return rs[selector[i].toLowerCase()];
+                }
+            }
+            return null;
+        },
+
+        /**
+         * Updates a rule property
+         * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
+         * @param {String} property The css property
+         * @param {String} value The new value for the property
+         * @return {Boolean} true If a rule was found and updated
+         */
+        updateRule : function(selector, property, value){
+            if (!Ext.isArray(selector)) {
+                var rule = this.getRule(selector);
+                if (rule) {
+                    rule.style[property.replace(camelRe, camelFn)] = value;
+                    return true;
+                }
+            } else {
+                for (var i = 0; i < selector.length; i++) {
+                    if (this.updateRule(selector[i], property, value)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    };
+}());
+/**
+ * @class Ext.util.History
+ * History management component that allows you to register arbitrary tokens that signify application
+ * history state on navigation actions.  You can then handle the history {@link #change} event in order
+ * to reset your application UI to the appropriate state when the user navigates forward or backward through
+ * the browser history stack.
+ * @singleton
+ */
+Ext.define('Ext.util.History', {
+    singleton: true,
+    alternateClassName: 'Ext.History',
+    mixins: {
+        observable: 'Ext.util.Observable'
+    },
+    
+    constructor: function() {
+        var me = this;
+        me.oldIEMode = Ext.isIE6 || Ext.isIE7 || !Ext.isStrict && Ext.isIE8;
+        me.iframe = null;
+        me.hiddenField = null;
+        me.ready = false;
+        me.currentToken = null;
+    },
+    
+    getHash: function() {
+        var href = window.location.href,
+            i = href.indexOf("#");
+            
+        return i >= 0 ? href.substr(i + 1) : null;
+    },
+
+    doSave: function() {
+        this.hiddenField.value = this.currentToken;
+    },
+    
+
+    handleStateChange: function(token) {
+        this.currentToken = token;
+        this.fireEvent('change', token);
+    },
+
+    updateIFrame: function(token) {
+        var html = '<html><body><div id="state">' + 
+                    Ext.util.Format.htmlEncode(token) + 
+                    '</div></body></html>';
+
+        try {
+            var doc = this.iframe.contentWindow.document;
+            doc.open();
+            doc.write(html);
+            doc.close();
+            return true;
+        } catch (e) {
+            return false;
+        }
+    },
+
+    checkIFrame: function () {
+        var me = this,
+            contentWindow = me.iframe.contentWindow;
+            
+        if (!contentWindow || !contentWindow.document) {
+            Ext.Function.defer(this.checkIFrame, 10, this);
+            return;
+        }
+       
+        var doc = contentWindow.document,
+            elem = doc.getElementById("state"),
+            oldToken = elem ? elem.innerText : null,
+            oldHash = me.getHash();
+           
+        Ext.TaskManager.start({
+            run: function () {
+                var doc = contentWindow.document,
+                    elem = doc.getElementById("state"),
+                    newToken = elem ? elem.innerText : null,
+                    newHash = me.getHash();
+
+                if (newToken !== oldToken) {
+                    oldToken = newToken;
+                    me.handleStateChange(newToken);
+                    window.top.location.hash = newToken;
+                    oldHash = newToken;
+                    me.doSave();
+                } else if (newHash !== oldHash) {
+                    oldHash = newHash;
+                    me.updateIFrame(newHash);
+                }
+            }, 
+            interval: 50,
+            scope: me
+        });
+        me.ready = true;
+        me.fireEvent('ready', me);            
+    },
+
+    startUp: function () {
+        var me = this;
+        
+        me.currentToken = me.hiddenField.value || this.getHash();
+
+        if (me.oldIEMode) {
+            me.checkIFrame();
+        } else {
+            var hash = me.getHash();
+            Ext.TaskManager.start({
+                run: function () {
+                    var newHash = me.getHash();
+                    if (newHash !== hash) {
+                        hash = newHash;
+                        me.handleStateChange(hash);
+                        me.doSave();
+                    }
+                },
+                interval: 50,
+                scope: me
+            });
+            me.ready = true;
+            me.fireEvent('ready', me);
+        }
+        
+    },
+
+    /**
+     * The id of the hidden field required for storing the current history token.
+     * @type String
+     * @property
+     */
+    fieldId: Ext.baseCSSPrefix + 'history-field',
+    /**
+     * The id of the iframe required by IE to manage the history stack.
+     * @type String
+     * @property
+     */
+    iframeId: Ext.baseCSSPrefix + 'history-frame',
+
+    /**
+     * Initialize the global History instance.
+     * @param {Boolean} onReady (optional) A callback function that will be called once the history
+     * component is fully initialized.
+     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser window.
+     */
+    init: function (onReady, scope) {
+        var me = this;
+        
+        if (me.ready) {
+            Ext.callback(onReady, scope, [me]);
+            return;
+        }
+        
+        if (!Ext.isReady) {
+            Ext.onReady(function() {
+                me.init(onReady, scope);
+            });
+            return;
+        }
+        
+        me.hiddenField = Ext.getDom(me.fieldId);
+        
+        if (me.oldIEMode) {
+            me.iframe = Ext.getDom(me.iframeId);
+        }
+        
+        me.addEvents(
+            /**
+             * @event ready
+             * Fires when the Ext.util.History singleton has been initialized and is ready for use.
+             * @param {Ext.util.History} The Ext.util.History singleton.
+             */
+            'ready',
+            /**
+             * @event change
+             * Fires when navigation back or forwards within the local page's history occurs.
+             * @param {String} token An identifier associated with the page state at that point in its history.
+             */
+            'change'
+        );
+        
+        if (onReady) {
+            me.on('ready', onReady, scope, {single: true});
+        }
+        me.startUp();
+    },
+
+    /**
+     * Add a new token to the history stack. This can be any arbitrary value, although it would
+     * commonly be the concatenation of a component id and another id marking the specifc history
+     * state of that component.  Example usage:
+     * <pre><code>
+// Handle tab changes on a TabPanel
+tabPanel.on('tabchange', function(tabPanel, tab){
+Ext.History.add(tabPanel.id + ':' + tab.id);
+});
+</code></pre>
+     * @param {String} token The value that defines a particular application-specific history state
+     * @param {Boolean} preventDuplicates When true, if the passed token matches the current token
+     * it will not save a new history step. Set to false if the same state can be saved more than once
+     * at the same history stack location (defaults to true).
+     */
+    add: function (token, preventDup) {
+        var me = this;
+        
+        if (preventDup !== false) {
+            if (me.getToken() === token) {
+                return true;
+            }
+        }
+        
+        if (me.oldIEMode) {
+            return me.updateIFrame(token);
+        } else {
+            window.top.location.hash = token;
+            return true;
+        }
+    },
+
+    /**
+     * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).
+     */
+    back: function() {
+        window.history.go(-1);
+    },
+
+    /**
+     * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).
+     */
+    forward: function(){
+        window.history.go(1);
+    },
+
+    /**
+     * Retrieves the currently-active history token.
+     * @return {String} The token
+     */
+    getToken: function() {
+        return this.ready ? this.currentToken : this.getHash();
+    }
+});
+/**
+ * @class Ext.view.TableChunker
+ * 
+ * Produces optimized XTemplates for chunks of tables to be
+ * used in grids, trees and other table based widgets.
+ *
+ * @singleton
+ */
+Ext.define('Ext.view.TableChunker', {
+    singleton: true,
+    requires: ['Ext.XTemplate'],
+    metaTableTpl: [
+        '{[this.openTableWrap()]}',
+        '<table class="' + Ext.baseCSSPrefix + 'grid-table ' + Ext.baseCSSPrefix + 'grid-table-resizer" border="0" cellspacing="0" cellpadding="0" {[this.embedFullWidth()]}>',
+            '<tbody>',
+            '<tr>',
+            '<tpl for="columns">',
+                '<th class="' + Ext.baseCSSPrefix + 'grid-col-resizer-{id}" style="width: {width}px; height: 0px;"></th>',
+            '</tpl>',
+            '</tr>',
+            '{[this.openRows()]}',
+                '{row}',
+                '<tpl for="features">',
+                    '{[this.embedFeature(values, parent, xindex, xcount)]}',
+                '</tpl>',
+            '{[this.closeRows()]}',
+            '</tbody>',
+        '</table>',
+        '{[this.closeTableWrap()]}'
+    ],
+
+    constructor: function() {
+        Ext.XTemplate.prototype.recurse = function(values, reference) {
+            return this.apply(reference ? values[reference] : values);
+        };
+    },
+
+    embedFeature: function(values, parent, x, xcount) {
+        var tpl = '';
+        if (!values.disabled) {
+            tpl = values.getFeatureTpl(values, parent, x, xcount);
+        }
+        return tpl;
+    },
+
+    embedFullWidth: function() {
+        return 'style="width: {fullWidth}px;"';
+    },
+
+    openRows: function() {
+        return '<tpl for="rows">';
+    },
+
+    closeRows: function() {
+        return '</tpl>';
+    },
+
+    metaRowTpl: [
+        '<tr class="' + Ext.baseCSSPrefix + 'grid-row {addlSelector} {[this.embedRowCls()]}" {[this.embedRowAttr()]}>',
+            '<tpl for="columns">',
+                '<td class="{cls} ' + Ext.baseCSSPrefix + 'grid-cell ' + Ext.baseCSSPrefix + 'grid-cell-{columnId} {{id}-modified} {{id}-tdCls} {[this.firstOrLastCls(xindex, xcount)]}" {{id}-tdAttr}><div unselectable="on" class="' + Ext.baseCSSPrefix + 'grid-cell-inner ' + Ext.baseCSSPrefix + 'unselectable" style="{{id}-style}; text-align: {align};">{{id}}</div></td>',
+            '</tpl>',
+        '</tr>'
+    ],
+    
+    firstOrLastCls: function(xindex, xcount) {
+        var cssCls = '';
+        if (xindex === 1) {
+            cssCls = Ext.baseCSSPrefix + 'grid-cell-first';
+        } else if (xindex === xcount) {
+            cssCls = Ext.baseCSSPrefix + 'grid-cell-last';
+        }
+        return cssCls;
+    },
+    
+    embedRowCls: function() {
+        return '{rowCls}';
+    },
+    
+    embedRowAttr: function() {
+        return '{rowAttr}';
+    },
+    
+    openTableWrap: function() {
+        return '';
+    },
+    
+    closeTableWrap: function() {
+        return '';
+    },
+
+    getTableTpl: function(cfg, textOnly) {
+        var tpl,
+            tableTplMemberFns = {
+                openRows: this.openRows,
+                closeRows: this.closeRows,
+                embedFeature: this.embedFeature,
+                embedFullWidth: this.embedFullWidth,
+                openTableWrap: this.openTableWrap,
+                closeTableWrap: this.closeTableWrap
+            },
+            tplMemberFns = {},
+            features = cfg.features || [],
+            ln = features.length,
+            i  = 0,
+            memberFns = {
+                embedRowCls: this.embedRowCls,
+                embedRowAttr: this.embedRowAttr,
+                firstOrLastCls: this.firstOrLastCls
+            },
+            // copy the default
+            metaRowTpl = Array.prototype.slice.call(this.metaRowTpl, 0),
+            metaTableTpl;
+            
+        for (; i < ln; i++) {
+            if (!features[i].disabled) {
+                features[i].mutateMetaRowTpl(metaRowTpl);
+                Ext.apply(memberFns, features[i].getMetaRowTplFragments());
+                Ext.apply(tplMemberFns, features[i].getFragmentTpl());
+                Ext.apply(tableTplMemberFns, features[i].getTableFragments());
+            }
+        }
+        
+        metaRowTpl = Ext.create('Ext.XTemplate', metaRowTpl.join(''), memberFns);
+        cfg.row = metaRowTpl.applyTemplate(cfg);
+        
+        metaTableTpl = Ext.create('Ext.XTemplate', this.metaTableTpl.join(''), tableTplMemberFns);
+        
+        tpl = metaTableTpl.applyTemplate(cfg);
+        
+        // TODO: Investigate eliminating.
+        if (!textOnly) {
+            tpl = Ext.create('Ext.XTemplate', tpl, tplMemberFns);
+        }
+        return tpl;
+        
+    }
+});
+
+