/* Minification failed. Returning unminified contents.
(32938,3,32940,4): run-time error JS1314: Implicit property name must be identifier: _formatPrice(prices) {
    return !this.showPrices || prices[0] === 0 ? '' : '$' + parseFloat(prices[0].value).toFixed(2);
  }
(32943,5,32965,6): run-time error JS1314: Implicit property name must be identifier: _formatMenu(menus) {
        for (var m = 0; m < menus.length; m++) {
            var menu = menus[m];
            // update featured items
            for (var i = 0; i < menu.featuredItems.length; i++) {
              var item = menu.featuredItems[i];
              item.price = this._formatPrice(item.prices);
                item.imageHtml = this.showImages && item.imagePath
                    ? '<div class="item-img"><img src="' + item.imagePath + '" /></div>' : '';
            }
            for (var s = 0; s < menu.sections.length; s++) {
                var section = menu.sections[s];
                // update menu items
                for (var i = 0; i < section.menuItems.length; i++) {
                    var item = section.menuItems[i];
                   item.price = this._formatPrice(item.prices);
                    item.imageHtml = this.showImages && item.thumbPath
                        ? '<div class="item-img"><img src="' + item.thumbPath + '" /></div>' : '';
                }
            }
        }
        return menus;
    }
(32967,5,32973,6): run-time error JS1314: Implicit property name must be identifier: _findMenu(menuId) {
        for (var m = 0; m < this.menus.length; m++) {
            if (this.menus[m].id === menuId) {
                return this.menus[m];
            }
        }
    }
(32975,5,32984,6): run-time error JS1314: Implicit property name must be identifier: _findSection(sectionId) {
        for (var m = 0; m < this.menus.length; m++) {
            var menu = this.menus[m];
            for (var s = 0; s < menu.sections.length; s++) {
                if (menu.sections[s].id === sectionId) {
                    return menu.sections[s];
                }
            }
        }
    }
 */
/*
    Copyright(c) 2011 Sencha Inc.
    licensing@sencha.com
    http://www.sencha.com/touchlicense
*/

if (typeof Ext === "undefined") {
    Ext = {};
}


Ext.apply = (function() {
    
    for(var key in {valueOf:1}) {
        return function(object, config, defaults) {
            
            if (defaults) {
                Ext.apply(object, defaults);
            }
            if (object && config && typeof config === 'object') {
                for (var key in config) {
                    object[key] = config[key];
                }
            }
            return object;
        };
    }
    return function(object, config, defaults) {
        
        if (defaults) {
            Ext.apply(object, defaults);
        }
        if (object && config && typeof config === 'object') {
            for (var key in config) {
                object[key] = config[key];
            }
            if (config.toString !== Object.prototype.toString) {
                object.toString = config.toString;
            }
            if (config.valueOf !== Object.prototype.valueOf) {
                object.valueOf = config.valueOf;
            }
        }
        return object;
    };
})();

Ext.apply(Ext, {
    platformVersion: '1.0',
    platformVersionDetail: {
        major: 1,
        minor: 0,
        patch: 3
    },
    userAgent: navigator.userAgent.toLowerCase(),
    cache: {},
    idSeed: 1000,
    BLANK_IMAGE_URL : 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
    isStrict: document.compatMode == "CSS1Compat",

    windowId: 'ext-window',
    documentId: 'ext-document',

    
    emptyFn : function() {},

    
    isSecure : /^https/i.test(window.location.protocol),

    
    isReady : false,

    
    enableGarbageCollector : true,

    
    enableListenerCollection : true,

    
    applyIf : function(object, config) {
        var property, undefined;

        if (object) {
            for (property in config) {
                if (object[property] === undefined) {
                    object[property] = config[property];
                }
            }
        }

        return object;
    },

    
    repaint : function() {
        var mask = Ext.getBody().createChild({
            cls: 'x-mask x-mask-transparent'
        });
        setTimeout(function() {
            mask.remove();
        }, 0);
    },

    
    id : function(el, prefix) {
        el = Ext.getDom(el) || {};
        if (el === document) {
            el.id = this.documentId;
        }
        else if (el === window) {
            el.id = this.windowId;
        }
        el.id = el.id || ((prefix || 'ext-gen') + (++Ext.idSeed));
        return el.id;
    },

    
    extend : function() {
        
        var inlineOverrides = function(o){
            for (var m in o) {
                if (!o.hasOwnProperty(m)) {
                    continue;
                }
                this[m] = o[m];
            }
        };

        var objectConstructor = Object.prototype.constructor;

        return function(subclass, superclass, overrides){
            
            if (Ext.isObject(superclass)) {
                overrides = superclass;
                superclass = subclass;
                subclass = overrides.constructor != objectConstructor
                    ? overrides.constructor
                    : function(){ superclass.apply(this, arguments); };
            }

            if (!superclass) {
                throw "Attempting to extend from a class which has not been loaded on the page.";
            }

            
            var F = function(){},
                subclassProto,
                superclassProto = superclass.prototype;

            F.prototype = superclassProto;
            subclassProto = subclass.prototype = new F();
            subclassProto.constructor = subclass;
            subclass.superclass = superclassProto;

            if(superclassProto.constructor == objectConstructor){
                superclassProto.constructor = superclass;
            }

            subclass.override = function(overrides){
                Ext.override(subclass, overrides);
            };

            subclassProto.superclass = subclassProto.supr = (function(){
                return superclassProto;
            });

            subclassProto.override = inlineOverrides;
            subclassProto.proto = subclassProto;

            subclass.override(overrides);
            subclass.extend = function(o) {
                return Ext.extend(subclass, o);
            };

            return subclass;
        };
    }(),

    
    override : function(origclass, overrides) {
        Ext.apply(origclass.prototype, overrides);
    },

    
    namespace : function() {
        var ln = arguments.length,
            i, value, split, x, xln, parts, object;

        for (i = 0; i < ln; i++) {
            value = arguments[i];
            parts = value.split(".");
            if (window.Ext) {
                object = window[parts[0]] = Object(window[parts[0]]);
            } else {
                object = arguments.callee.caller.arguments[0];
            }
            for (x = 1, xln = parts.length; x < xln; x++) {
                object = object[parts[x]] = Object(object[parts[x]]);
            }
        }
        return object;
    },

    
    urlEncode : function(o, pre) {
        var empty,
            buf = [],
            e = encodeURIComponent;

        Ext.iterate(o, function(key, item){
            empty = Ext.isEmpty(item);
            Ext.each(empty ? key : item, function(val){
                buf.push('&', e(key), '=', (!Ext.isEmpty(val) && (val != key || !empty)) ? (Ext.isDate(val) ? Ext.encode(val).replace(/"/g, '') : e(val)) : '');
            });
        });

        if(!pre){
            buf.shift();
            pre = '';
        }

        return pre + buf.join('');
    },

    
    urlDecode : function(string, overwrite) {
        if (Ext.isEmpty(string)) {
            return {};
        }

        var obj = {},
            pairs = string.split('&'),
            d = decodeURIComponent,
            name,
            value;

        Ext.each(pairs, function(pair) {
            pair = pair.split('=');
            name = d(pair[0]);
            value = d(pair[1]);
            obj[name] = overwrite || !obj[name] ? value : [].concat(obj[name]).concat(value);
        });

        return obj;
    },

    
    htmlEncode : function(value) {
        return Ext.util.Format.htmlEncode(value);
    },

    
    htmlDecode : function(value) {
         return Ext.util.Format.htmlDecode(value);
    },

    
    urlAppend : function(url, s) {
        if (!Ext.isEmpty(s)) {
            return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
        }
        return url;
    },

    
     toArray : function(array, start, end) {
        return Array.prototype.slice.call(array, start || 0, end || array.length);
     },

     
     each : function(array, fn, scope) {
         if (Ext.isEmpty(array, true)) {
             return 0;
         }
         if (!Ext.isIterable(array) || Ext.isPrimitive(array)) {
             array = [array];
         }
         for (var i = 0, len = array.length; i < len; i++) {
             if (fn.call(scope || array[i], array[i], i, array) === false) {
                 return i;
             }
         }
         return true;
     },

     
     iterate : function(obj, fn, scope) {
         if (Ext.isEmpty(obj)) {
             return;
         }
         if (Ext.isIterable(obj)) {
             Ext.each(obj, fn, scope);
             return;
         }
         else if (Ext.isObject(obj)) {
             for (var prop in obj) {
                 if (obj.hasOwnProperty(prop)) {
                     if (fn.call(scope || obj, prop, obj[prop], obj) === false) {
                         return;
                     }
                 }
             }
         }
     },

    
    pluck : function(arr, prop) {
        var ret = [];
        Ext.each(arr, function(v) {
            ret.push(v[prop]);
        });
        return ret;
    },

    
    getBody : function() {
        return Ext.get(document.body || false);
    },

    
    getHead : function() {
        var head;

        return function() {
            if (head == undefined) {
                head = Ext.get(DOC.getElementsByTagName("head")[0]);
            }

            return head;
        };
    }(),

    
    getDoc : function() {
        return Ext.get(document);
    },

    
    getCmp : function(id) {
        return Ext.ComponentMgr.get(id);
    },

    
    getOrientation: function() {
        return window.innerHeight > window.innerWidth ? 'portrait' : 'landscape';
    },

    isIterable : function(v) {
        if (!v) {
            return false;
        }
        
        if (Ext.isArray(v) || v.callee) {
            return true;
        }
        
        if (/NodeList|HTMLCollection/.test(Object.prototype.toString.call(v))) {
            return true;
        }

        
        
        return ((typeof v.nextNode != 'undefined' || v.item) && Ext.isNumber(v.length)) || false;
    },

    
    num : function(v, defaultValue) {
        v = Number(Ext.isEmpty(v) || Ext.isArray(v) || typeof v == 'boolean' || (typeof v == 'string' && Ext.util.Format.trim(v).length == 0) ? NaN : v);
        return isNaN(v) ? defaultValue : v;
    },

    
    isEmpty : function(value, allowBlank) {
        var isNull       = value == null,
            emptyArray   = (Ext.isArray(value) && !value.length),
            blankAllowed = !allowBlank ? value === '' : false;

        return isNull || emptyArray || blankAllowed;
    },

    
    isArray : function(v) {
        return Object.prototype.toString.apply(v) === '[object Array]';
    },

    
    isDate : function(v) {
        return Object.prototype.toString.apply(v) === '[object Date]';
    },

    
    isObject : function(v) {
        return !!v && !v.tagName && Object.prototype.toString.call(v) === '[object Object]';
    },

    
    isPrimitive : function(v) {
        return Ext.isString(v) || Ext.isNumber(v) || Ext.isBoolean(v);
    },

    
    isFunction : function(v) {
        return Object.prototype.toString.apply(v) === '[object Function]';
    },

    
    isNumber : function(v) {
        return Object.prototype.toString.apply(v) === '[object Number]' && isFinite(v);
    },

    
    isString : function(v) {
        return typeof v === 'string';
    },

    
    isBoolean : function(v) {
        return Object.prototype.toString.apply(v) === '[object Boolean]';
    },

    
    isElement : function(v) {
        return v ? !!v.tagName : false;
    },

    
    isDefined : function(v){
        return typeof v !== 'undefined';
    },

    
    destroy : function() {
        var ln = arguments.length,
            i, arg;

        for (i = 0; i < ln; i++) {
            arg = arguments[i];
            if (arg) {
                if (Ext.isArray(arg)) {
                    this.destroy.apply(this, arg);
                }
                else if (Ext.isFunction(arg.destroy)) {
                    arg.destroy();
                }
                else if (arg.dom) {
                    arg.remove();
                }
            }
        }
    }
});


Ext.SSL_SECURE_URL = Ext.isSecure && 'about:blank';

Ext.ns = Ext.namespace;

Ext.ns(
    'Ext.util',
    'Ext.data',
    'Ext.list',
    'Ext.form',
    'Ext.menu',
    'Ext.state',
    'Ext.layout',
    'Ext.app',
    'Ext.ux',
    'Ext.plugins',
    'Ext.direct',
    'Ext.lib',
    'Ext.gesture'
);



Ext.util.Observable = Ext.extend(Object, {
    
    
    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 (this.bubbleEvents) {
            this.enableBubble(this.bubbleEvents);
        }
    },

    
    eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal)$/,

    
    addManagedListener : function(item, ename, fn, scope, options) {
        var me = this,
            managedListeners = me.managedListeners = me.managedListeners || [],
            config;

        if (Ext.isObject(ename)) {
            options = ename;
            for (ename in options) {
                if (!options.hasOwnProperty(ename)) {
                    continue;
                }
                config = options[ename];
                if (!me.eventOptionsRe.test(ename)) {
                    me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
                }
            }
        }
        else {
            managedListeners.push({
                item: item,
                ename: ename,
                fn: fn,
                scope: scope,
                options: options
            });

            item.on(ename, fn, scope, options);
        }
    },

    
     removeManagedListener : function(item, ename, fn, scope) {
        var me = this,
            o,
            config,
            managedListeners,
            managedListener,
            length,
            i;

        if (Ext.isObject(ename)) {
            o = ename;
            for (ename in o) {
                if (!o.hasOwnProperty(ename)) {
                    continue;
                }
                config = o[ename];
                if (!me.eventOptionsRe.test(ename)) {
                    me.removeManagedListener(item, ename, config.fn || config, config.scope || o.scope);
                }
            }
        }

        managedListeners = this.managedListeners ? this.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)) {
                this.managedListeners.remove(managedListener);
                item.un(managedListener.ename, managedListener.fn, managedListener.scope);
            }
        }
    },

    
    fireEvent: function() {
        var me = this,
            a = Ext.toArray(arguments),
            ename = a[0].toLowerCase(),
            ret = true,
            ev = me.events[ename],
            queue = me.eventQueue,
            parent;

        if (me.eventsSuspended === true) {
            if (queue) {
                queue.push(a);
            }
            return false;
        }
        else if (ev && Ext.isObject(ev) && ev.bubble) {
            if (ev.fire.apply(ev, a.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, a);
            }
        }
        else if (ev && Ext.isObject(ev)) {
            a.shift();
            ret = ev.fire.apply(ev, a);
        }
        return ret;
    },

    
    addListener: function(ename, fn, scope, o) {
        var me = this,
            config,
            ev;

        if (Ext.isObject(ename)) {
            o = ename;
            for (ename in o) {
                if (!o.hasOwnProperty(ename)) {
                    continue;
                }
                config = o[ename];
                if (!me.eventOptionsRe.test(ename)) {
                    me.addListener(ename, config.fn || config, config.scope || o.scope, config.fn ? config : o);
                }
            }
        }
        else {
            ename = ename.toLowerCase();
            me.events[ename] = me.events[ename] || true;
            ev = me.events[ename] || true;
            if (Ext.isBoolean(ev)) {
                me.events[ename] = ev = new Ext.util.Event(me, ename);
            }
            ev.addListener(fn, scope, Ext.isObject(o) ? o: {});
        }
    },

    
    removeListener: function(ename, fn, scope) {
        var me = this,
            config,
            ev;

        if (Ext.isObject(ename)) {
            var o = ename;
            for (ename in o) {
                if (!o.hasOwnProperty(ename)) {
                    continue;
                }
                config = o[ename];
                if (!me.eventOptionsRe.test(ename)) {
                    me.removeListener(ename, config.fn || config, config.scope || o.scope);
                }
            }
        }
        else {
            ename = ename.toLowerCase();
            ev = me.events[ename];
            if (ev.isEvent) {
                ev.removeListener(fn, scope);
            }
        }
    },

    
    clearListeners: function() {
        var events = this.events,
            ev,
            key;

        for (key in events) {
            if (!events.hasOwnProperty(key)) {
                continue;
            }
            ev = events[key];
            if (ev.isEvent) {
                ev.clearListeners();
            }
        }

        this.clearManagedListeners();
    },

    purgeListeners : function() {
        console.warn('MixedCollection: purgeListeners has been deprecated. Please use clearListeners.');
        return this.clearListeners.apply(this, arguments);
    },

    
    clearManagedListeners : function() {
        var managedListeners = this.managedListeners || [],
            ln = managedListeners.length,
            i, managedListener;

        for (i = 0; i < ln; i++) {
            managedListener = managedListeners[i];
            managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
        }

        this.managedListener = [];
    },

    purgeManagedListeners : function() {
        console.warn('MixedCollection: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
        return this.clearManagedListeners.apply(this, arguments);
    },

    
    addEvents: function(o) {
        var me = this;
            me.events = me.events || {};
        if (Ext.isString(o)) {
            var a = arguments,
            i = a.length;
            while (i--) {
                me.events[a[i]] = me.events[a[i]] || true;
            }
        } else {
            Ext.applyIf(me.events, o);
        }
    },

    
    hasListener: function(ename) {
        var e = this.events[ename];
        return e.isEvent === true && e.listeners.length > 0;
    },

    
    suspendEvents: function(queueSuspended) {
        this.eventsSuspended = true;
        if (queueSuspended && !this.eventQueue) {
            this.eventQueue = [];
        }
    },

    
    resumeEvents: function() {
        var me = this,
            queued = me.eventQueue || [];

        me.eventsSuspended = false;
        delete me.eventQueue;

        Ext.each(queued,
        function(e) {
            me.fireEvent.apply(me, e);
        });
    },

    
    relayEvents : function(origin, events, prefix) {
        prefix = prefix || '';
        var me = this,
            len = events.length,
            i, ename;

        function createHandler(ename){
            return function(){
                return me.fireEvent.apply(me, [prefix + ename].concat(Array.prototype.slice.call(arguments, 0, -1)));
            };
        }

        for(i = 0, len = events.length; i < len; i++){
            ename = events[i].substr(prefix.length);
            me.events[ename] = me.events[ename] || true;
            origin.on(ename, createHandler(ename), me);
        }
    },

    
    enableBubble: function(events) {
        var me = this;
        if (!Ext.isEmpty(events)) {
            events = Ext.isArray(events) ? events: Ext.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;
            });
        }
    }
});

Ext.override(Ext.util.Observable, {
    
    on: Ext.util.Observable.prototype.addListener,
    
    un: Ext.util.Observable.prototype.removeListener,

    mon: Ext.util.Observable.prototype.addManagedListener,
    mun: Ext.util.Observable.prototype.removeManagedListener
});


Ext.util.Observable.releaseCapture = function(o) {
    o.fireEvent = Ext.util.Observable.prototype.fireEvent;
};


Ext.util.Observable.capture = function(o, fn, scope) {
    o.fireEvent = Ext.createInterceptor(o.fireEvent, fn, scope);
};


Ext.util.Observable.observe = function(cls, listeners) {
    if (cls) {
        if (!cls.isObservable) {
            Ext.applyIf(cls, new Ext.util.Observable());
            Ext.util.Observable.capture(cls.prototype, cls.fireEvent, cls);
        }
        if (typeof listeners == 'object') {
            cls.on(listeners);
        }
        return cls;
    }
};


Ext.util.Observable.observeClass = Ext.util.Observable.observe;

Ext.util.Event = Ext.extend(Object, (function() {
    function createBuffered(handler, listener, o, scope) {
        listener.task = new Ext.util.DelayedTask();
        return function() {
            listener.task.delay(o.buffer, handler, scope, Ext.toArray(arguments));
        };
    };

    function createDelayed(handler, listener, o, scope) {
        return function() {
            var task = new Ext.util.DelayedTask();
            if (!listener.tasks) {
                listener.tasks = [];
            }
            listener.tasks.push(task);
            task.delay(o.delay || 10, handler, scope, Ext.toArray(arguments));
        };
    };

    function createSingle(handler, listener, o, scope) {
        return function() {
            listener.ev.removeListener(listener.fn, scope);
            return handler.apply(scope, arguments);
        };
    };

    return {
        isEvent: true,

        constructor: function(observable, name) {
            this.name = name;
            this.observable = observable;
            this.listeners = [];
        },

        addListener: function(fn, scope, options) {
            var me = this,
                listener;
                scope = scope || me.observable;

            if (!me.isListening(fn, scope)) {
                listener = me.createListener(fn, scope, options);
                if (me.firing) {
                    
                    me.listeners = me.listeners.slice(0);
                }
                me.listeners.push(listener);
            }
        },

        createListener: function(fn, scope, o) {
            o = o || {};
            scope = scope || this.observable;

            var listener = {
                    fn: fn,
                    scope: scope,
                    o: o,
                    ev: this
                },
                handler = fn;

            if (o.delay) {
                handler = createDelayed(handler, listener, o, scope);
            }
            if (o.buffer) {
                handler = createBuffered(handler, listener, o, scope);
            }
            if (o.single) {
                handler = createSingle(handler, listener, o, scope);
            }

            listener.fireFn = handler;
            return listener;
        },

        findListener: function(fn, scope) {
            var listeners = this.listeners,
            i = listeners.length,
            listener,
            s;

            while (i--) {
                listener = listeners[i];
                if (listener) {
                    s = listener.scope;
                    if (listener.fn == fn && (s == scope || s == this.observable)) {
                        return i;
                    }
                }
            }

            return - 1;
        },

        isListening: function(fn, scope) {
            return this.findListener(fn, scope) !== -1;
        },

        removeListener: function(fn, scope) {
            var me = this,
                index,
                listener,
                k;
            index = me.findListener(fn, scope);
            if (index != -1) {
                listener = me.listeners[index];

                if (me.firing) {
                    me.listeners = me.listeners.slice(0);
                }

                
                if (listener.task) {
                    listener.task.cancel();
                    delete listener.task;
                }

                
                k = listener.tasks && listener.tasks.length;
                if (k) {
                    while (k--) {
                        listener.tasks[k].cancel();
                    }
                    delete listener.tasks;
                }

                
                me.listeners.splice(index, 1);
                return true;
            }

            return false;
        },

        
        clearListeners: function() {
            var listeners = this.listeners,
                i = listeners.length;

            while (i--) {
                this.removeListener(listeners[i].fn, listeners[i].scope);
            }
        },

        fire: function() {
            var me = this,
                listeners = me.listeners,
                count = listeners.length,
                i,
                args,
                listener;

            if (count > 0) {
                me.firing = true;
                for (i = 0; i < count; i++) {
                    listener = listeners[i];
                    args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
                    if (listener.o) {
                        args.push(listener.o);
                    }
                    if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
                        return (me.firing = false);
                    }
                }
            }
            me.firing = false;
            return true;
        }
    };
})());


Ext.util.Stateful = Ext.extend(Ext.util.Observable, {
    
    
    editing : false,
    
    
    dirty : false,
    
    
    persistanceProperty: 'data',
    
    constructor: function(config) {
        Ext.applyIf(this, {
            data: {}
        });        
        
        
        this.modified = {};
        
        this[this.persistanceProperty] = {};
        
        Ext.util.Stateful.superclass.constructor.call(this, config);
    },
    
    
    get: function(field) {
        return this[this.persistanceProperty][field];
    },
    
    
    set: function(fieldName, value) {
        var me = this,
            fields = me.fields,
            modified = me.modified,
            convertFields = [],
            field, key, i;
        
        
        if (arguments.length == 1 && Ext.isObject(fieldName)) {
            for (key in fieldName) {
                if (!fieldName.hasOwnProperty(key)) {
                    continue;
                }
                
                
                
                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);
                }
            }
            
            me[me.persistanceProperty][fieldName] = value;

            if (field && field.persist && !me.isEqual(currentValue, value)) {
                if (me.isModified(fieldName)) {
                    if (me.isEqual(modified[fieldName], value)) {
                        
                        
                        delete modified[fieldName];
                        
                        
                        me.dirty = false;
                        for (key in modified) {
                            if (modified.hasOwnProperty(key)){
                                me.dirty = true;
                                break;
                            }
                        }
                    }
                } else {
                    me.dirty = true;
                    modified[fieldName] = currentValue;
                }
            }

            if (!me.editing) {
                me.afterEdit();
            }
        }
    },
    
    
    getChanges : function(){
        var modified = this.modified,
            changes  = {},
            field;
            
        for (field in modified) {
            if (modified.hasOwnProperty(field)){
                changes[field] = this[this.persistanceProperty][field];
            }
        }
        
        return changes;
    },
    
    
    isModified : function(fieldName) {
        return !!(this.modified && this.modified.hasOwnProperty(fieldName));
    },
    
    
    setDirty : function() {
        this.dirty = true;
        
        if (!this.modified) {
            this.modified = {};
        }
        
        this.fields.each(function(field) {
            this.modified[field.name] = this[this.persistanceProperty][field.name];
        }, this);
    },
    
    markDirty : function() {
        throw new Error("Stateful: markDirty has been deprecated. Please use setDirty.");
    },
    
    
    reject : function(silent) {
        var modified = this.modified,
            field;
            
        for (field in modified) {
            if (!modified.hasOwnProperty(field)) {
                continue;
            }
            if (typeof modified[field] != "function") {
                this[this.persistanceProperty][field] = modified[field];
            }
        }
        
        this.dirty = false;
        this.editing = false;
        delete this.modified;
        
        if (silent !== true) {
            this.afterReject();
        }
    },
    
    
    commit : function(silent) {
        this.dirty = false;
        this.editing = false;
        
        delete this.modified;
        
        if (silent !== true) {
            this.afterCommit();
        }
    },
    
    
    copy : function(newId) {
        return new this.constructor(Ext.apply({}, this[this.persistanceProperty]), newId || this.internalId);
    }
});

Ext.util.HashMap = Ext.extend(Ext.util.Observable, {
    constructor: function(config) {
        this.addEvents(
            
            'add',
            
            'clear',
            
            'remove',
            
            'replace'
        );
        
        Ext.util.HashMap.superclass.constructor.call(this, config);
        
        this.clear(true);
    },

    
    getCount: function() {
        return this.length;
    },
    
    
    getData: function(key, value) {
        
        if (value === undefined) {
            value = key;
            key = this.getKey(value);
        }
        
        return [key, value];
    },
    
    
    getKey: function(o) {
        return o.id;    
    },

    
    add: function(key, value) {
        var me = this,
            data;
            
        if (me.containsKey(key)) {
            throw new Error('This key already exists in the HashMap');
        }
        
        data = this.getData(key, value);
        key = data[0];
        value = data[1];
        me.map[key] = value;
        ++me.length;
        me.fireEvent('add', me, key, value);
        return value;
    },

    
    replace: function(key, value) {
        var me = this,
            map = me.map,
            old;
            
        if (!me.containsKey(key)) {
            me.add(key, value);
        }
        old = map[key];
        map[key] = value;
        me.fireEvent('replace', me, key, value, old);
        return value;
    },

    
    remove: function(o) {
        var key = this.findKey(o);
        if (key !== undefined) {
            return this.removeByKey(key);
        }
        return false;
    },

    
    removeByKey: function(key) {
        var me = this,
            value;
            
        if (me.containsKey(key)) {
            value = me.map[key];
            delete me.map[key];
            --me.length;
            me.fireEvent('remove', me, key, value);
            return true;
        }
        return false;
    },

    
    get: function(key) {
        return this.map[key];
    },

    
    clear: function( initial) {
        var me = this;
        me.map = {};
        me.length = 0;
        if (initial !== true) {
            me.fireEvent('clear', me);
        }
        return me;
    },

    
    containsKey: function(key) {
        return this.map[key] !== undefined;
    },

    
    contains: function(value) {
        return this.containsKey(this.findKey(value));
    },

    
    getKeys: function() {
        return this.getArray(true);
    },

    
    getValues: function() {
        return this.getArray(false);
    },

    
    getArray: function(isKey) {
        var arr = [],
            key,
            map = this.map;
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                arr.push(isKey ? key: map[key]);
            }
        }
        return arr;
    },

    
    each: function(fn, scope) { 
        
        var items = Ext.apply({}, this.map),
            key,
            length = this.length;

        scope = scope || this;
        for (key in items) {
            if (items.hasOwnProperty(key)) {
                if (fn.call(scope, key, items[key], length) === false) {
                    break;
                }
            }
        }
        return this;
    },

    
    clone: function() {
        var hash = new Ext.util.HashMap(),
            map = this.map,
            key;
            
        hash.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                hash.add(key, map[key]);
            }
        }
        hash.resumeEvents();
        return hash;
    },

    
    findKey: function(value) {
        var key,
            map = this.map;

        for (key in map) {
            if (map.hasOwnProperty(key) && map[key] === value) {
                return key;
            }
        }
        return undefined;
    }
});

Ext.util.MixedCollection = function(allowFunctions, keyFn) {
    this.items = [];
    this.map = {};
    this.keys = [];
    this.length = 0;
    this.addEvents(
        
        'clear',
        
        'add',
        
        'replace',
        
        'remove',
        'sort'
    );
    this.allowFunctions = allowFunctions === true;
    if (keyFn) {
        this.getKey = keyFn;
    }
    Ext.util.MixedCollection.superclass.constructor.call(this);
};

Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {

    
    allowFunctions : false,

    
    add : function(key, obj){
        var myObj = obj, myKey = key;
        if(arguments.length == 1){
            myObj = myKey;
            myKey = this.getKey(myObj);
        }
        if(typeof myKey != 'undefined' && myKey !== null){
            var old = this.map[myKey];
            if(typeof old != 'undefined'){
                return this.replace(myKey, myObj);
            }
            this.map[myKey] = myObj;
        }
        this.length++;
        this.items.push(myObj);
        this.keys.push(myKey);
        this.fireEvent('add', this.length-1, myObj, myKey);
        return myObj;
    },

    
    getKey : function(o){
         return o.id;
    },

    
    replace : function(key, o){
        if(arguments.length == 1){
            o = arguments[0];
            key = this.getKey(o);
        }
        var old = this.map[key];
        if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
             return this.add(key, o);
        }
        var index = this.indexOfKey(key);
        this.items[index] = o;
        this.map[key] = o;
        this.fireEvent('replace', key, old, o);
        return o;
    },

    
    addAll : function(objs){
        if(arguments.length > 1 || Ext.isArray(objs)){
            var args = arguments.length > 1 ? arguments : objs;
            for(var i = 0, len = args.length; i < len; i++){
                this.add(args[i]);
            }
        }else{
            for(var key in objs){
                if (!objs.hasOwnProperty(key)) {
                    continue;
                }
                if(this.allowFunctions || typeof objs[key] != 'function'){
                    this.add(key, objs[key]);
                }
            }
        }
    },

    
    each : function(fn, scope){
        var items = [].concat(this.items); 
        for(var i = 0, len = items.length; i < len; i++){
            if(fn.call(scope || items[i], items[i], i, len) === false){
                break;
            }
        }
    },

    
    eachKey : function(fn, scope){
        for(var i = 0, len = this.keys.length; i < len; i++){
            fn.call(scope || window, this.keys[i], this.items[i], i, len);
        }
    },

    
    findBy : function(fn, scope) {
        for(var i = 0, len = this.items.length; i < len; i++){
            if(fn.call(scope || window, this.items[i], this.keys[i])){
                return this.items[i];
            }
        }
        return null;
    },
    

    
    insert : function(index, key, obj){
        var myKey = key, myObj = obj;
        if(arguments.length == 2){
            myObj = myKey;
            myKey = this.getKey(myObj);
        }
        if(this.containsKey(myKey)){
            this.suspendEvents();
            this.removeByKey(myKey);
            this.resumeEvents();
        }
        if(index >= this.length){
            return this.add(myKey, myObj);
        }
        this.length++;
        this.items.splice(index, 0, myObj);
        if(typeof myKey != 'undefined' && myKey !== null){
            this.map[myKey] = myObj;
        }
        this.keys.splice(index, 0, myKey);
        this.fireEvent('add', index, myObj, myKey);
        return myObj;
    },

    
    remove : function(o){
        return this.removeAt(this.indexOf(o));
    },

    
    removeAll : function(items){
        Ext.each(items || [], function(item) {
            this.remove(item);
        }, this);

        return this;
    },

    
    removeAt : function(index){
        if(index < this.length && index >= 0){
            this.length--;
            var o = this.items[index];
            this.items.splice(index, 1);
            var key = this.keys[index];
            if(typeof key != 'undefined'){
                delete this.map[key];
            }
            this.keys.splice(index, 1);
            this.fireEvent('remove', o, key);
            return o;
        }
        return false;
    },

    
    removeByKey : function(key){
        return this.removeAt(this.indexOfKey(key));
    },
    
    removeKey : function() {
        console.warn('MixedCollection: removeKey has been deprecated. Please use removeByKey.');
        return this.removeByKey.apply(this, arguments);
    },

    
    getCount : function(){
        return this.length;
    },

    
    indexOf : function(o){
        return this.items.indexOf(o);
    },

    
    indexOfKey : function(key){
        return this.keys.indexOf(key);
    },

    
    get : function(key) {
        var mk = this.map[key],
            item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
        return typeof item != 'function' || this.allowFunctions ? item : null; 
    },
    
    item : function() {
        console.warn('MixedCollection: item has been deprecated. Please use get.');
        return this.get.apply(this, arguments);
    },

    
    getAt : function(index) {
        return this.items[index];
    },

    itemAt : function() {
        console.warn('MixedCollection: itemAt has been deprecated. Please use getAt.');
        return this.getAt.apply(this, arguments);
    },
    
    
    getByKey : function(key) {
        return this.map[key];
    },

    key : function() {
        console.warn('MixedCollection: key has been deprecated. Please use getByKey.');
        return this.getByKey.apply(this, arguments);
    },
    
    
    contains : function(o){
        return this.indexOf(o) != -1;
    },

    
    containsKey : function(key){
        return typeof this.map[key] != 'undefined';
    },

    
    clear : function(){
        this.length = 0;
        this.items = [];
        this.keys = [];
        this.map = {};
        this.fireEvent('clear');
    },

    
    first : function() {
        return this.items[0];
    },

    
    last : function() {
        return this.items[this.length-1];
    },

    
    _sort : function(property, dir, fn){
        var i, len,
            dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,

            
            c     = [],
            keys  = this.keys,
            items = this.items;

        
        fn = fn || function(a, b) {
            return a - b;
        };

        
        for(i = 0, len = items.length; i < len; i++){
            c[c.length] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        
        c.sort(function(a, b){
            var v = fn(a[property], b[property]) * dsc;
            if(v === 0){
                v = (a.index < b.index ? -1 : 1);
            }
            return v;
        });

        
        for(i = 0, len = c.length; i < len; i++){
            items[i] = c[i].value;
            keys[i]  = c[i].key;
        }

        this.fireEvent('sort', this);
    },

    
    sort : function(property, direction) {
        
        var sorters = property;
        
        
        if (Ext.isString(property)) {
            sorters = [new Ext.util.Sorter({
                property : property,
                direction: direction || "ASC"
            })];
        } else if (property instanceof Ext.util.Sorter) {
            sorters = [property];
        } else if (Ext.isObject(property)) {
            sorters = [new Ext.util.Sorter(property)];
        }
        
        var length = sorters.length;
        
        if (length == 0) {
            return;
        }
                
        
        var sorterFn = function(r1, r2) {
            var result = sorters[0].sort(r1, r2),
                length = sorters.length,
                i;
            
                
                for (i = 1; i < length; i++) {
                    result = result || sorters[i].sort.call(this, r1, r2);
                }                
           
            return result;
        };
        
        this.sortBy(sorterFn);
    },
    
    
    sortBy: function(sorterFn) {
        var items  = this.items,
            keys   = this.keys,
            length = items.length,
            temp   = [],
            i;
        
        
        for (i = 0; i < length; i++) {
            temp[i] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }
        
        temp.sort(function(a, b) {
            var v = sorterFn(a.value, b.value);
            if (v === 0) {
                v = (a.index < b.index ? -1 : 1);
            }
            
            return v;
        });
        
        
        for (i = 0; i < length; i++) {
            items[i] = temp[i].value;
            keys[i]  = temp[i].key;
        }
        
        this.fireEvent('sort', this);
    },

    
    reorder: function(mapping) {
        this.suspendEvents();

        var items = this.items,
            index = 0,
            length = items.length,
            order = [],
            remaining = [],
            oldIndex;

        
        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();
            }
        }

        this.clear();
        this.addAll(order);

        this.resumeEvents();
        this.fireEvent('sort', this);
    },

    
    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);
        });
    },
    
    keySort : function() {
        console.warn('MixedCollection: keySort has been deprecated. Please use sortByKey.');
        return this.sortByKey.apply(this, arguments);
    },
    

    
    getRange : function(start, end){
        var items = this.items;
        if(items.length < 1){
            return [];
        }
        start = start || 0;
        end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
        var i, r = [];
        if(start <= end){
            for(i = start; i <= end; i++) {
                r[r.length] = items[i];
            }
        }else{
            for(i = start; i >= end; i--) {
                r[r.length] = items[i];
            }
        }
        return r;
    },

    
    filter : function(property, value, anyMatch, caseSensitive) {
        var filters = [];
        
        
        if (Ext.isString(property)) {
            filters.push(new 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);
        }
        
        
        
        var filterFn = function(record) {
            var isMatch = true,
                length = filters.length,
                i;

            for (i = 0; i < length; i++) {
                var filter = filters[i],
                    fn     = filter.filterFn,
                    scope  = filter.scope;

                isMatch = isMatch && fn.call(scope, record);
            }

            return isMatch;
        };
        
        return this.filterBy(filterFn);
    },

    
    filterBy : function(fn, scope) {
        var newMC  = new Ext.util.MixedCollection(),
            keys   = this.keys, 
            items  = this.items,
            length = items.length,
            i;
            
        newMC.getKey = this.getKey;
        
        for (i = 0; i < length; i++) {
            if (fn.call(scope||this, items[i], keys[i])) {
                newMC.add(keys[i], items[i]);
            }
        }
        
        return newMC;
    },

    
    findIndex : function(property, value, start, anyMatch, caseSensitive){
        if(Ext.isEmpty(value, false)){
            return -1;
        }
        value = this.createValueMatcher(value, anyMatch, caseSensitive);
        return this.findIndexBy(function(o){
            return o && value.test(o[property]);
        }, null, start);
    },

    
    findIndexBy : function(fn, scope, start){
        var k = this.keys, it = this.items;
        for(var i = (start||0), len = it.length; i < len; i++){
            if(fn.call(scope||this, it[i], k[i])){
                return i;
            }
        }
        return -1;
    },

    
    createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
        if (!value.exec) { 
            var er = Ext.util.Format.escapeRegex;
            value = String(value);

            if (anyMatch === true) {
                value = er(value);
            } else {
                value = '^' + er(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
         }
         return value;
    },

    
    clone : function(){
        var r = new Ext.util.MixedCollection();
        var k = this.keys, it = this.items;
        for(var i = 0, len = it.length; i < len; i++){
            r.add(k[i], it[i]);
        }
        r.getKey = this.getKey;
        return r;
    }
});




Ext.AbstractManager = Ext.extend(Object, {
    typeName: 'type',

    constructor: function(config) {
        Ext.apply(this, config || {});

        
        this.all = new Ext.util.HashMap();

        this.types = {};
    },

    
    get : function(id) {
        return this.all.get(id);
    },

    
    register: function(item) {
        this.all.add(item);
    },

    
    unregister: function(item) {
        this.all.remove(item);
    },

    
    registerType : function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
    },

    
    isRegistered : function(type){
        return this.types[type] !== undefined;
    },

    
    create: function(config, defaultType) {
        var type        = config[this.typeName] || config.type || defaultType,
            Constructor = this.types[type];

        if (Constructor == undefined) {
            throw new Error(Ext.util.Format.format("The '{0}' type has not been registered with this manager", type));
        }

        return new Constructor(config);
    },

    
    onAvailable : function(id, fn, scope){
        var all = this.all;

        all.on("add", function(index, o){
            if (o.id == id) {
                fn.call(scope || o, o);
                all.un("add", fn, scope);
            }
        });
    },
    
    
    each: function(fn, scope){
        this.all.each(fn, scope || this);    
    },
    
    
    getCount: function(){
        return this.all.getCount();
    }
});


Ext.util.DelayedTask = function(fn, scope, args) {
    var me = this,
        id,
        call = function() {
            clearInterval(id);
            id = null;
            fn.apply(scope, args || []);
        };

    
    this.delay = function(delay, newFn, newScope, newArgs) {
        me.cancel();
        fn = newFn || fn;
        scope = newScope || scope;
        args = newArgs || args;
        id = setInterval(call, delay);
    };

    
    this.cancel = function(){
        if (id) {
            clearInterval(id);
            id = null;
        }
    };
};

Ext.util.GeoLocation = Ext.extend(Ext.util.Observable, {
    
    autoUpdate: true,

    
    
    latitude: null,
    
    longitude: null,
    
    accuracy: null,
    
    altitude: null,
    
    altitudeAccuracy: null,
    
    heading: null,
    
    speed: null,
    
    timestamp: null,

    
    
    allowHighAccuracy: false,
    
    
    timeout: Infinity,
    
    maximumAge: 0,
    
    setMaximumAge: function(maximumAge) {
        this.maximumAge = maximumAge;
        this.setAutoUpdate(this.autoUpdate);
    },
    
    setTimeout: function(timeout) {
        this.timeout = timeout;
        this.setAutoUpdate(this.autoUpdate);
    },
    
    setAllowHighAccuracy: function(allowHighAccuracy) {
        this.allowHighAccuracy = allowHighAccuracy;
        this.setAutoUpdate(this.autoUpdate);
    },
    

    
    provider : null,
    
    watchOperation : null,

    constructor : function(config) {
        Ext.apply(this, config);
        

        this.coords = this; //@deprecated


        if (Ext.supports.GeoLocation) {
            this.provider = this.provider || 
                (navigator.geolocation ? navigator.geolocation : 
                (window.google || {}).gears ? google.gears.factory.create('beta.geolocation') : null);           
        }
        
        this.addEvents(
            
            'update',
            
            'locationerror',
            
            'locationupdate'
        );

        Ext.util.GeoLocation.superclass.constructor.call(this);

        if(this.autoUpdate){
            var me = this;
            setTimeout(function(){
                me.setAutoUpdate(me.autoUpdate);
            }, 0);
        }
    },

    
    setAutoUpdate : function(autoUpdate) {
        if (this.watchOperation !== null) {
            this.provider.clearWatch(this.watchOperation);
            this.watchOperation = null;
        }
        if (!autoUpdate) {
            return true;
        }
        if (!Ext.supports.GeoLocation) {
            this.fireEvent('locationerror', this, false, false, true, null);
            return false;
        }
        try{
            this.watchOperation = this.provider.watchPosition(
                Ext.createDelegate(this.fireUpdate, this), 
                Ext.createDelegate(this.fireError, this), 
                this.parseOptions());
        }
        catch(e){
            this.autoUpdate = false;
            this.fireEvent('locationerror', this, false, false, true, e.message);
            return false;
        }
        return true;
    },

    
    updateLocation : function(callback, scope, positionOptions) {
        var me = this;

        var failFunction = function(message, error){
            if(error){
                me.fireError(error);
            }
            else{
                me.fireEvent('locationerror', me, false, false, true, message);
            }
            if(callback){
                callback.call(scope || me, null, me); 
            }
            me.fireEvent('update', false, me); 
        };

        if (!Ext.supports.GeoLocation) {
            setTimeout(function() {
                failFunction(null);
            }, 0);
            return;
        }

        try{
            this.provider.getCurrentPosition(
                
                function(position){
                    me.fireUpdate(position);
                    if(callback){
                        callback.call(scope || me, me, me); 
                    }
                    me.fireEvent('update', me, me); 
                },
                
                function(error){
                    failFunction(null, error);
                },
                positionOptions ? positionOptions : this.parseOptions());
        }
        catch(e){
            setTimeout(function(){
                failFunction(e.message);
            }, 0);
        }
    },

    
    fireUpdate: function(position){
        this.timestamp = position.timestamp;
        this.latitude = position.coords.latitude;
        this.longitude = position.coords.longitude;
        this.accuracy = position.coords.accuracy;
        this.altitude = position.coords.altitude;
        this.altitudeAccuracy = position.coords.altitudeAccuracy;
        
        
        this.heading = typeof position.coords.heading == 'undefined' ? null : position.coords.heading;
        this.speed = typeof position.coords.speed == 'undefined' ? null : position.coords.speed;
        this.fireEvent('locationupdate', this);
    },
    fireError: function(error){
        this.fireEvent('locationerror', this,
            error.code == error.TIMEOUT, 
            error.code == error.PERMISSION_DENIED, 
            error.code == error.POSITION_UNAVAILABLE,
            error.message == undefined ? null : error.message);
    },
    parseOptions: function(){
        var ret = { 
            maximumAge: this.maximumAge, 
            allowHighAccuracy: this.allowHighAccuracy
        };
        
        if(this.timeout !== Infinity){
            ret.timeout = this.timeout;
        }
        return ret;
    },

    
    getLocation : function(callback, scope) {
        var me = this;
        if(this.latitude !== null){
            callback.call(scope || me, me, me);
        }
        else {
            me.updateLocation(callback, scope);
        }
    }
});

Ext.util.Region = Ext.extend(Object, {
    
    constructor : function(t, r, b, l) {
        var me = this;
        me.top = t;
        me[1] = t;
        me.right = r;
        me.bottom = b;
        me.left = l;
        me[0] = l;
    },

    
    contains : function(region) {
        var me = this;
        return (region.left >= me.left &&
                region.right <= me.right &&
                region.top >= me.top &&
                region.bottom <= me.bottom);

    },

    
    intersect : function(region) {
        var me = this,
            t = Math.max(me.top, region.top),
            r = Math.min(me.right, region.right),
            b = Math.min(me.bottom, region.bottom),
            l = Math.max(me.left, region.left);

        if (b > t && r > l) {
            return new Ext.util.Region(t, r, b, l);
        }
        else {
            return false;
        }
    },

    
    union : function(region) {
        var me = this,
            t = Math.min(me.top, region.top),
            r = Math.max(me.right, region.right),
            b = Math.max(me.bottom, region.bottom),
            l = Math.min(me.left, region.left);

        return new Ext.util.Region(t, r, b, l);
    },

    
    constrainTo : function(r) {
        var me = this,
            constrain = Ext.util.Numbers.constrain;
        me.top = constrain(me.top, r.top, r.bottom);
        me.bottom = constrain(me.bottom, r.top, r.bottom);
        me.left = constrain(me.left, r.left, r.right);
        me.right = constrain(me.right, r.left, r.right);
        return me;
    },

    
    adjust : function(t, r, b, l) {
        var me = this;
        me.top += t;
        me.left += l;
        me.right += r;
        me.bottom += b;
        return me;
    },

    
    getOutOfBoundOffset: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.getOutOfBoundOffsetX(p);
            } else {
                return this.getOutOfBoundOffsetY(p);
            }
        } else {
            p = axis;
            var d = new Ext.util.Offset();
                d.x = this.getOutOfBoundOffsetX(p.x);
                d.y = this.getOutOfBoundOffsetY(p.y);
            return d;
        }

    },

    
    getOutOfBoundOffsetX: function(p) {
        if (p <= this.left) {
            return this.left - p;
        } else if (p >= this.right) {
            return this.right - p;
        }

        return 0;
    },

    
    getOutOfBoundOffsetY: function(p) {
        if (p <= this.top) {
            return this.top - p;
        } else if (p >= this.bottom) {
            return this.bottom - p;
        }

        return 0;
    },

    
    isOutOfBound: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.isOutOfBoundX(p);
            } else {
                return this.isOutOfBoundY(p);
            }
        } else {
            p = axis;
            return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
        }
    },

    
    isOutOfBoundX: function(p) {
        return (p < this.left || p > this.right);
    },

    
    isOutOfBoundY: function(p) {
        return (p < this.top || p > this.bottom);
    },

    
    restrict: function(axis, p, factor) {
        if (Ext.isObject(axis)) {
            var newP;

            factor = p;
            p = axis;

            if (p.copy) {
                newP = p.copy();
            }
            else {
                newP = {
                    x: p.x,
                    y: p.y
                };
            }

            newP.x = this.restrictX(p.x, factor);
            newP.y = this.restrictY(p.y, factor);
            return newP;
        } else {
            if (axis == 'x') {
                return this.restrictX(p, factor);
            } else {
                return this.restrictY(p, factor);
            }
        }
    },

    
    restrictX : function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.left) {
            p -= (p - this.left) * factor;
        }
        else if (p >= this.right) {
            p -= (p - this.right) * factor;
        }
        return p;
    },

    
    restrictY : function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.top) {
            p -= (p - this.top) * factor;
        }
        else if (p >= this.bottom) {
            p -= (p - this.bottom) * factor;
        }
        return p;
    },

    
    getSize: function() {
        return {
            width: this.right - this.left,
            height: this.bottom - this.top
        };
    },

    
    copy: function() {
        return new Ext.util.Region(this.top, this.right, this.bottom, this.left);
    },

    
    toString: function() {
        return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
    },


    
    translateBy: function(offset) {
        this.left += offset.x;
        this.right += offset.x;
        this.top += offset.y;
        this.bottom += offset.y;

        return this;
    },

    
    round: function() {
        this.top = Math.round(this.top);
        this.right = Math.round(this.right);
        this.bottom = Math.round(this.bottom);
        this.left = Math.round(this.left);

        return this;
    },

    
    equals: function(region) {
        return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left)
    }
});


Ext.util.Region.getRegion = function(el) {
    return Ext.fly(el).getPageBox(true);
};


Ext.util.Region.from = function(o) {
    return new Ext.util.Region(o.top, o.right, o.bottom, o.left);
};


Ext.util.Point = Ext.extend(Object, {
    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.Point(this.x, this.y);
    },

    
    copyFrom: function(p) {
        this.x = p.x;
        this.y = p.y;

        return this;
    },

    
    toString: function() {
        return "Point[" + this.x + "," + this.y + "]";
    },

    
    equals: function(p) {
        return (this.x == p.x && this.y == p.y);
    },

    
    isWithin: function(p, threshold) {
        if (!Ext.isObject(threshold)) {
            threshold = {x: threshold};
            threshold.y = threshold.x;
        }

        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);
    },

    
    translate: function(x, y) {
        if (x != null && !isNaN(x))
            this.x += x;

        if (y != null && !isNaN(y))
            this.y += y;
    },

    
    roundedEquals: function(p) {
        return (Math.round(this.x) == Math.round(p.x) && Math.round(this.y) == Math.round(p.y));
    }
});


Ext.util.Point.fromEvent = function(e) {
    var a = (e.changedTouches && e.changedTouches.length > 0) ? e.changedTouches[0] : e;
    return new Ext.util.Point(a.pageX, a.pageY);
};
Ext.util.Offset = Ext.extend(Object, {
    constructor: function(x, y) {
        this.x = (x != null && !isNaN(x)) ? x : 0;
        this.y = (y != null && !isNaN(y)) ? y : 0;

        return this;
    },

    copy: function() {
        return new Ext.util.Offset(this.x, this.y);
    },

    copyFrom: function(p) {
        this.x = p.x;
        this.y = p.y;
    },

    toString: function() {
        return "Offset[" + this.x + "," + this.y + "]";
    },

    equals: function(offset) {
        if(!(offset instanceof Ext.util.Offset))
            throw new Error('offset must be an instance of Ext.util.Offset');

        return (this.x == offset.x && this.y == offset.y);
    },

    round: function(to) {
        if (!isNaN(to)) {
            var factor = Math.pow(10, to);
            this.x = Math.round(this.x * factor) / factor;
            this.y = Math.round(this.y * factor) / factor;
        } else {
            this.x = Math.round(this.x);
            this.y = Math.round(this.y);
        }
    },

    isZero: function() {
        return this.x == 0 && this.y == 0;
    }
});

Ext.util.Offset.fromObject = function(obj) {
    return new Ext.util.Offset(obj.x, obj.y);
};

Ext.Template = Ext.extend(Object, {
    constructor: function(html) {
        var me = this,
            args = arguments,
            buffer = [],
            value, i, length;
            
        me.initialConfig = {};

        if (Ext.isArray(html)) {
            html = html.join("");
        }
        else if (args.length > 1) {
            for (i = 0, length = args.length; 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('');
        }

        
        me.html = html;
        
        if (me.compiled) {
            me.compile();
        }
    },
    isTemplate: true,  
    
    disableFormats: false,
    
    re: /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,      
    
    applyTemplate: function(values) {
        var me = this,
            useFormat = me.disableFormats !== true,
            fm = Ext.util.Format,
            tpl = me;

        if (me.compiled) {
            return me.compiled(values);
        }
        function fn(m, name, format, args) {
            if (format && useFormat) {
                if (args) {
                    args = [values[name]].concat(new Function('return ['+ args +'];')());
                } else {
                    args = [values[name]];
                }
                if (format.substr(0, 5) == "this.") {
                    return tpl[format.substr(5)].apply(tpl, args);
                }
                else {
                    return fm[format].apply(fm, args);
                }
            }
            else {
                return values[name] !== undefined ? values[name] : "";
            }
        }
        return me.html.replace(me.re, fn);
    },

    
    set: function(html, compile) {
        var me = this;
        me.html = html;
        me.compiled = null;
        return compile ? me.compile() : me;
    },
    
    compileARe: /\\/g,
    compileBRe: /(\r\n|\n)/g,
    compileCRe: /'/g,
    /**
     * Compiles the template into an internal function, eliminating the RegEx overhead.
     * @return {Ext.Template} this
     * @hide repeat doc
     */
    compile: function() {
        var me = this,
            fm = Ext.util.Format,
            useFormat = me.disableFormats !== true,
            body, bodyReturn;

        function fn(m, name, format, args) {
            if (format && useFormat) {
                args = args ? ',' + args: "";
                if (format.substr(0, 5) != "this.") {
                    format = "fm." + format + '(';
                }
                else {
                    format = 'this.' + format.substr(5) + '(';
                }
            }
            else {
                args = '';
                format = "(values['" + name + "'] == undefined ? '' : ";
            }
            return "'," + format + "values['" + name + "']" + args + ") ,'";
        }

        bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
        body = "this.compiled = function(values){ return ['" + bodyReturn + "'].join('');};";
        eval(body);
        return me;
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
     * @param {Mixed} el The context element
     * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
     * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
     * @return {HTMLElement/Ext.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.Element (defaults to undefined)
     * @return {HTMLElement/Ext.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.Element (defaults to undefined)
     * @return {HTMLElement/Ext.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.Element (defaults to undefined)
     * @return {HTMLElement/Ext.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.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.Element (defaults to undefined)
     * @return {HTMLElement/Ext.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;
    }
});
/**
 * 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
 */
Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;

/**
 * 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
 */
Ext.Template.from = function(el, config) {
    el = Ext.getDom(el);
    return new Ext.Template(el.value || el.innerHTML, config || '');
};

/**
 * @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.DataView}</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: 'Ext JS, Inc',
email: 'tommy@extjs.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.XTemplate = Ext.extend(Ext.Template, {
    argsRe: /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
    nameRe: /^<tpl\b[^>]*?for="(.*?)"/,
    ifRe: /^<tpl\b[^>]*?if="(.*?)"/,
    execRe: /^<tpl\b[^>]*?exec="(.*?)"/,
    constructor: function() {
        Ext.XTemplate.superclass.constructor.apply(this, 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 = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + 'try{' + RETURN + Ext.util.Format.htmlDecode(exp) + ';}catch(e){return;}}');
            }

            exp = matchExec ? matchExec[1] : null;
            if (exp) {
                exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + Ext.util.Format.htmlDecode(exp) + ';}');
            }
            
            name = matchName ? matchName[1] : null;
            if (name) {
                if (name === '.') {
                    name = VALUES;
                } else if (name === '..') {
                    name = PARENT;
                }
                name = new Function(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 == '.') {
                v = 'typeof values == "string" ? 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;
    }
});
/**
 * 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
 */
Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;

/**
 * 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
 */
Ext.XTemplate.from = function(el, config) {
    el = Ext.getDom(el);
    return new Ext.XTemplate(el.value || el.innerHTML, config || {});
};



Ext.util.Sorter = Ext.extend(Object, {
    
    
    
    
    
    
    
    direction: "ASC",
    
    constructor: function(config) {
        Ext.apply(this, config);
        
        if (this.property == undefined && this.sorterFn == undefined) {
            throw "A Sorter requires either a property or a sorter function";
        }
        
        this.sort = this.createSortFunction(this.sorterFn || this.defaultSorterFn);
    },
    
    
    createSortFunction: function(sorterFn) {
        var me        = this,
            property  = me.property,
            direction = me.direction,
            modifier  = direction.toUpperCase() == "DESC" ? -1 : 1;
        
        
        
        return function(o1, o2) {
            return modifier * sorterFn.call(me, o1, o2);
        };
    },
    
    
    defaultSorterFn: function(o1, o2) {
        var v1 = this.getRoot(o1)[this.property],
            v2 = this.getRoot(o2)[this.property];

        return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
    },
    
    
    getRoot: function(item) {
        return this.root == undefined ? item : item[this.root];
    }
});

Ext.util.Filter = Ext.extend(Object, {
    
    
    
    
    
    anyMatch: false,
    
    
    exactMatch: false,
    
    
    caseSensitive: false,
    
    
    
    constructor: function(config) {
        Ext.apply(this, config);
        
        
        
        this.filter = this.filter || this.filterFn;
        
        if (this.filter == undefined) {
            if (this.property == undefined || this.value == undefined) {
                
                
                
                
            } else {
                this.filter = this.createFilterFn();
            }
            
            this.filterFn = 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]);
        };
    },
    
    
    getRoot: function(item) {
        return this.root == undefined ? item : item[this.root];
    },
    
    
    createValueMatcher : function() {
        var me            = this,
            value         = me.value,
            anyMatch      = me.anyMatch,
            exactMatch    = me.exactMatch,
            caseSensitive = me.caseSensitive,
            escapeRe      = Ext.util.Format.escapeRegex;
        
        if (!value.exec) { 
            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;
    }
});

Ext.util.Functions = {
    
    createInterceptor: function(origFn, newFn, scope, returnValue) { 
        var method = origFn;
        if (!Ext.isFunction(newFn)) {
            return origFn;
        }
        else {
            return function() {
                var me = this,
                    args = arguments;
                newFn.target = me;
                newFn.method = origFn;
                return (newFn.apply(scope || me || window, args) !== false) ?
                        origFn.apply(me || window, args) :
                        returnValue || null;
            };
        }
    },

    
    createDelegate: function(fn, obj, args, appendArgs) {
        if (!Ext.isFunction(fn)) {
            return fn;
        }
        return function() {
            var callArgs = args || arguments;
            if (appendArgs === true) {
                callArgs = Array.prototype.slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }
            else if (Ext.isNumber(appendArgs)) {
                callArgs = Array.prototype.slice.call(arguments, 0);
                
                var applyArgs = [appendArgs, 0].concat(args);
                
                Array.prototype.splice.apply(callArgs, applyArgs);
                
            }
            return fn.apply(obj || window, callArgs);
        };
    },

    
    defer: function(fn, millis, obj, args, appendArgs) {
        fn = Ext.util.Functions.createDelegate(fn, obj, args, appendArgs);
        if (millis > 0) {
            return setTimeout(fn, millis);
        }
        fn();
        return 0;
    },


    
    createSequence: function(origFn, newFn, scope) {
        if (!Ext.isFunction(newFn)) {
            return origFn;
        }
        else {
            return function() {
                var retval = origFn.apply(this || window, arguments);
                newFn.apply(scope || this || window, arguments);
                return retval;
            };
        }
    }
};



Ext.defer = Ext.util.Functions.defer;



Ext.createInterceptor = Ext.util.Functions.createInterceptor;



Ext.createSequence = Ext.util.Functions.createSequence;


Ext.createDelegate = Ext.util.Functions.createDelegate;



Ext.util.Date = {
    
    getElapsed: function(dateA, dateB) {
        return Math.abs(dateA - (dateB || new Date));
    }
};


Ext.util.Numbers = {
    
    
    toFixedBroken: (0.9).toFixed() != 1,
    
    
    constrain : function(number, min, max) {
        number = parseFloat(number);
        if (!isNaN(min)) {
            number = Math.max(number, min);
        }
        if (!isNaN(max)) {
            number = Math.min(number, max);
        }
        return number;
    },
    
    
    toFixed : function(value, precision) {
        if(Ext.util.Numbers.toFixedBroken) {
            precision = precision || 0;
            var pow = Math.pow(10, precision);
            return Math.round(value * pow) / pow;
        }
        return value.toFixed(precision);
    }
};


Ext.util.Format = {
    defaultDateFormat: 'm/d/Y',
    escapeRe: /('|\\)/g,
    trimRe: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
    formatRe: /\{(\d+)\}/g,
    escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,
    
    /**
     * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
     * @param {String} value The string to truncate
     * @param {Number} length The maximum length to allow before truncating
     * @param {Boolean} word True to try to find a common word break
     * @return {String} The converted text
     */
    ellipsis: function(value, len, word) {
        if (value && value.length > len) {
            if (word) {
                var vs = value.substr(0, len - 2),
                index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                if (index != -1 && index >= (len - 15)) {
                    return vs.substr(0, index) + "...";
                }
            } 
            return value.substr(0, len - 3) + "...";
        }
        return value;
    },

    /**
     * Escapes the passed string for use in a regular expression
     * @param {String} str
     * @return {String}
     */
    escapeRegex : function(s) {
        return s.replace(Ext.util.Format.escapeRegexRe, "\\$1");
    },

    /**
     * Escapes the passed string for ' and \
     * @param {String} string The string to escape
     * @return {String} The escaped string
     */
    escape : function(string) {
        return string.replace(Ext.util.Format.escapeRe, "\\$1");
    },

    
    toggle : function(string, value, other) {
        return string == value ? other : value;
    },

    
    trim : function(string) {
        return string.replace(Ext.util.Format.trimRe, "");
    },

    
    leftPad : function (val, size, ch) {
        var result = String(val);
        ch = ch || " ";
        while (result.length < size) {
            result = ch + result;
        }
        return result;
    },

    
    format : function (format) {
        var args = Ext.toArray(arguments, 1);
        return format.replace(Ext.util.Format.formatRe, function(m, i) {
            return args[i];
        });
    },

    
    htmlEncode: function(value) {
        return ! value ? value: String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
    },

    
    htmlDecode: function(value) {
        return ! value ? value: String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
    },

    
    date: function(v, format) {
        if (!v) {
            return "";
        }
        if (!Ext.isDate(v)) {
            v = new Date(Date.parse(v));
        }
        return v.dateFormat(format || Ext.util.Format.defaultDateFormat);
    }
};


Ext.LoadMask = Ext.extend(Ext.util.Observable, {
    

    
    msg : 'Loading...',
    
    msgCls : 'x-mask-loading',

    
    disabled: false,

    constructor : function(el, config) {
        this.el = Ext.get(el);
        Ext.apply(this, config);

        this.addEvents('show', 'hide');
        if (this.store) {
            this.bindStore(this.store, true);
        }
        Ext.LoadMask.superclass.constructor.call(this);
    },

    
    bindStore : function(store, initial) {
        if (!initial && this.store) {
            this.mun(this.store, {
                scope: this,
                beforeload: this.onBeforeLoad,
                load: this.onLoad,
                exception: this.onLoad
            });
            if(!store) {
                this.store = null;
            }
        }
        if (store) {
            store = Ext.StoreMgr.lookup(store);
            this.mon(store, {
                scope: this,
                beforeload: this.onBeforeLoad,
                load: this.onLoad,
                exception: this.onLoad
            });

        }
        this.store = store;
        if (store && store.isLoading()) {
            this.onBeforeLoad();
        }
    },

    
    disable : function() {
       this.disabled = true;
    },

    
    enable : function() {
        this.disabled = false;
    },

    
    isDisabled : function() {
        return this.disabled;
    },

    
    onLoad : function() {
        this.el.unmask();
        this.fireEvent('hide', this, this.el, this.store);
    },

    
    onBeforeLoad : function() {
        if (!this.disabled) {
            this.el.mask(Ext.LoadingSpinner + '<div class="x-loading-msg">' + this.msg + '</div>', this.msgCls, false);
            this.fireEvent('show', this, this.el, this.store);
        }
    },

    
    show: function() {
        this.onBeforeLoad();
    },

    
    hide: function() {
        this.onLoad();
    },

    
    destroy : function() {
        this.hide();
        this.clearListeners();
    }
});

Ext.LoadingSpinner = '<div class="x-loading-spinner"><span class="x-loading-top"></span><span class="x-loading-right"></span><span class="x-loading-bottom"></span><span class="x-loading-left"></span></div>';



Ext.applyIf(Array.prototype, {
    
    indexOf: function(o, from) {
        var len = this.length;
        from = from || 0;
        from += (from < 0) ? len: 0;
        for (; from < len; ++from) {
            if (this[from] === o) {
                return from;
            }
        }
        return - 1;
    },

    
    remove: function(o) {
        var index = this.indexOf(o);
        if (index != -1) {
            this.splice(index, 1);
        }
        return this;
    },

    contains: function(o) {
        return this.indexOf(o) !== -1;
    }
});


Ext.ComponentMgr = new Ext.AbstractManager({
    typeName: 'xtype',

    
    create : function(config, defaultType){
        if (config.isComponent) {
            return config;
        } else {
            var type = config.xtype || defaultType,
                Class = this.types[type];
            if (!Class) {
                throw "Attempting to create a component with an xtype that has not been registered: " + type
            }
            return new Class(config);
        }
        return config.render ? config : new (config);
    },

    registerType : function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
        cls.prototype[this.typeName] = type;
    }
});


Ext.reg = function() {
    return Ext.ComponentMgr.registerType.apply(Ext.ComponentMgr, arguments);
}; 


Ext.create = function() {
    return Ext.ComponentMgr.create.apply(Ext.ComponentMgr, arguments);
};


Ext.ComponentQuery = new function() {
    var cq = this,

        
        
        filterFnPattern = [
            'var r = [],',
                'i = 0,',
                'it = arguments[0],',
                'l = it.length,',
                'c;',
            'for (; i < l; i++) {',
                'c = it[i].{0};',
                'if (c) {',
                   'r.push(c);',
                '}',
            '}',
            'return r;'
        ].join(''),

        filterItems = function(items, operation) {
            
            
            
            return operation.method.apply(this, [ items ].concat(operation.args));
        },

        getItems = function(items, mode) {
            var result = [],
                i,
                ln = items.length,
                candidate,
                deep = mode != '>';
            for (i = 0; i < ln; i++) {
                candidate = items[i];
                if (candidate.getRefItems) {
                    result = result.concat(candidate.getRefItems(deep));
                }
            }
            return result;
        },

        getAncestors = function(items) {
            var result = [],
                i,
                ln = items.length,
                candidate;
            for (i = 0; i < ln; i++) {
                candidate = items[i];
                while (!!(candidate = candidate.ownerCt)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByXType = function(items, xtype, shallow) {
            if (xtype == '*') {
                return items.slice();
            }
            else {
                var result = [],
                    i,
                    ln = items.length,
                    candidate;
                for (i = 0; i < ln; i++) {
                    candidate = items[i];
                    if (candidate.isXType(xtype, shallow)) {
                        result.push(candidate);
                    }
                }
                return result;
            }
        },

        
        filterByClassName = function(items, className) {
            var result = [],
                i,
                ln = items.length,
                candidate;
            for (i = 0; i < ln; i++) {
                candidate = items[i];
                if (candidate.el ? candidate.el.hasCls(className) : candidate.initCls().contains(className)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByAttribute = function(items, property, operator, value) {
            var result = [],
                i,
                ln = items.length,
                candidate;
            for (i = 0; i < ln; i++) {
                candidate = items[i];
                if ((value === undefined) ? !!candidate[property] : (candidate[property] == value)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterById = function(items, id) {
            var result = [],
                i,
                ln = items.length,
                candidate;
            for (i = 0; i < ln; i++) {
                candidate = items[i];
                if (candidate.getItemId() == id) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByPseudo = function(items, name, value) {
            return cq.pseudos[name](items, value);
        },

        
        
        modeRe = /^(\s?([>\^])\s?|\s|$)/,

        
        
        tokenRe = /^(?:(#)?([\w-]+|\*)(?:\((true|false)\))?)|(?:\{([^\}]+)\})/,

        matchers = [{
            
            re: /^\.([\w-]+)(?:\((true|false)\))?/,
            method: filterByXType
        },{
            
            re: /^(?:[\[\{](?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
            method: filterByAttribute
        }, {
            
            re: /^#([\w-]+)/,
            method: filterById
        }, {
            re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
            method: filterByPseudo
        }];

    
    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },

        execute : function(root) {
            var operations = this.operations,
                ln = operations.length,
                operation, i,
                workingItems;

            
            if (!root) {
		workingItems = Ext.ComponentMgr.all.getArray();
            }

            
            
            for (i = 0; i < ln; i++) {
                operation = operations[i];

                
                
                
                
                
                
                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 (i == ln -1) {
                    return workingItems;
                }
            }
            return [];
        },

        is: function(component) {
            var operations = this.operations,
                ln = operations.length,
                i,
                workingItems = Ext.isArray(component) ? component : [ component ];

            
            
            for (i = 0; i < ln && workingItems.length; i++) {
                workingItems = filterItems(workingItems, operations[i]);
            }
            return workingItems.length != 0;
        }
    });

    Ext.apply(this, {

        
        cache: {},

        
        pseudos: {},

        
        query: function(selector, root) {
            var selectors = selector.split(','),
                ln = selectors.length,
                i, query, results = [],
                noDupResults = [], dupMatcher = {}, resultsLn, cmp;

            for (i = 0; i < ln; i++) {
                selector = Ext.util.Format.trim(selectors[i]);
                query = this.cache[selector];
                if (!query) {
                    this.cache[selector] = query = this.parse(selector);
                }
                results = results.concat(query.execute(root));
            }

            
            
            if (ln > 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;
        },

        
        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 = [],
                ln = matchers.length,
                lastSelector,
                tokenMatch,
                matchedChar,
                modeMatch,
                selectorMatch,
                args,
                i, matcher;

            
            
            
            while (selector && lastSelector != selector) {
                lastSelector = selector;

                
                tokenMatch = selector.match(tokenRe);

                if (tokenMatch) {
                    matchedChar = tokenMatch[1];

                    
                    if (matchedChar == '#') {
                        operations.push({
                            method: filterById,
                            args: [Ext.util.Format.trim(tokenMatch[2])]
                        });
                    }
                    
                    
                    else if (matchedChar == '.') {
                        operations.push({
                            method: filterByClassName,
                            args: [Ext.util.Format.trim(tokenMatch[2])]
                        });
                    }
                    
                    else if (tokenMatch[4]) {
                        operations.push({
                            method: new Function(Ext.util.Format.format(filterFnPattern, tokenMatch[4])),
                            args: []
                        });
                    }
                    
                    
                    else {
                        operations.push({
                            method: filterByXType,
                            args: [Ext.util.Format.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
                        });
                    }

                    
                    selector = selector.replace(tokenMatch[0], '');
                }

                
                
                
                while (!(modeMatch = selector.match(modeRe))) {
                    
                    
                    for (i = 0; selector && i < ln; i++) {
                        matcher = matchers[i];
                        selectorMatch = selector.match(matcher.re);

                        
                        
                        
                        if (selectorMatch) {
                            operations.push({
                                method: matcher.method,
                                args: selectorMatch.splice(1)
                            });
                            selector = selector.replace(selectorMatch[0], '');
                            break; 
                        }
                        
                        if (i == (ln - 1)) {
                            throw "Invalid ComponentQuery selector: \"" + arguments[0] + "\"";
                        }
                    }
                }

                
                
                
                
                if (modeMatch[1]) { 
                    operations.push({
                        mode: modeMatch[2]||modeMatch[1]
                    });
                    selector = selector.replace(modeMatch[0], '');
                }
            }

            
            
            return new cq.Query({
                operations: operations
            });
        }
    });
};


Ext.PluginMgr = new Ext.AbstractManager({
    typeName: 'ptype',

    
    create : function(config, defaultType){
        var PluginCls = this.types[config.ptype || defaultType];
        if (PluginCls.init) {
            return PluginCls;
        } else {
            return new PluginCls(config);
        }
    },

    
    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;
    }
});


Ext.preg = function() {
    return Ext.PluginMgr.registerType.apply(Ext.PluginMgr, arguments);
};


Ext.EventManager = {
    optionsRe: /^(?:capture|scope|delay|buffer|single|stopEvent|disableLocking|preventDefault|stopPropagation|normalized|args|delegate|horizontal|vertical|dragThreshold|holdThreshold|doubleTapThreshold|cancelThreshold|singleTapThreshold|fireClickEvent)$/,
    touchRe: /^(?:pinch|pinchstart|pinchend|tap|singletap|doubletap|swipe|swipeleft|swiperight|drag|dragstart|dragend|touchdown|touchstart|touchmove|touchend|taphold|tapstart|tapcancel)$/i,

    
    addListener : function(element, eventName, fn, scope, o){
        
        if (Ext.isObject(eventName)) {
            this.handleListenerConfig(element, eventName);
            return;
        }

        var dom = Ext.getDom(element);

        
        if (!dom){
            throw "Error listening for \"" + eventName + '\". Element "' + element + '" doesn\'t exist.';
        }

        if (!fn) {
            throw 'Error listening for "' + eventName + '". No handler function specified';
        }

        var touch = this.touchRe.test(eventName);

        
        var wrap = this.createListenerWrap(dom, eventName, fn, scope, o, touch);

        
        this.getEventListenerCache(dom, eventName).push({
            fn: fn,
            wrap: wrap,
            scope: scope
        });

        if (touch) {
            Ext.gesture.Manager.addEventListener(dom, eventName, wrap, o);
        }
        else {
            
            o = o || {};
            dom.addEventListener(eventName, wrap, o.capture || false);
        }
    },

    
    removeListener : function(element, eventName, fn, scope) {
        
        if (Ext.isObject(eventName)) {
            this.handleListenerConfig(element, eventName, true);
            return;
        }

        var dom = Ext.getDom(element),
            cache = this.getEventListenerCache(dom, eventName),
            i = cache.length, j,
            listener, wrap, tasks;

        while (i--) {
            listener = cache[i];

            if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
                wrap = listener.wrap;

                
                if (wrap.task) {
                    clearTimeout(wrap.task);
                    delete wrap.task;
                }

                
                j = wrap.tasks && wrap.tasks.length;
                if (j) {
                    while (j--) {
                        clearTimeout(wrap.tasks[j]);
                    }
                    delete wrap.tasks;
                }

                if (this.touchRe.test(eventName)) {
                    Ext.gesture.Manager.removeEventListener(dom, eventName, wrap);
                }
                else {
                    
                    dom.removeEventListener(eventName, wrap, false);
                }

                
                cache.splice(i, 1);
            }
        }
    },

    
    removeAll : function(element){
        var dom = Ext.getDom(element),
            cache = this.getElementEventCache(dom),
            ev;

        for (ev in cache) {
            if (!cache.hasOwnProperty(ev)) {
                continue;
            }
            this.removeListener(dom, ev);
        }
        Ext.cache[dom.id].events = {};
    },

    purgeElement : function(element, recurse, eventName) {
        var dom = Ext.getDom(element),
            i = 0, len;

        if(eventName) {
            this.removeListener(dom, eventName);
        }
        else {
            this.removeAll(dom);
        }

        if(recurse && dom && dom.childNodes) {
            for(len = element.childNodes.length; i < len; i++) {
                this.purgeElement(element.childNodes[i], recurse, eventName);
            }
        }
    },

    handleListenerConfig : function(element, config, remove) {
        var key, value;

        
        for (key in config) {
            if (!config.hasOwnProperty(key)) {
                continue;
            }
            
            if (!this.optionsRe.test(key)) {
                value = config[key];
                
                
                if (Ext.isFunction(value)) {
                    
                    this[(remove ? 'remove' : 'add') + 'Listener'](element, key, value, config.scope, config);
                }
                
                else {
                    
                    this[(remove ? 'remove' : 'add') + 'Listener'](element, key, config.fn, config.scope, config);
                }
            }
        }
    },

    getId : function(element) {
        
        
        
        var skip = false,
            id;

        element = Ext.getDom(element);

        if (element === document || element === window) {
            skip = true;
        }

        id = Ext.id(element);

        if (!Ext.cache[id]){
            Ext.Element.addToCache(new Ext.Element(element), id);
            if(skip){
                Ext.cache[id].skipGarbageCollection = true;
            }
        }
        return id;
    },

    
    createListenerWrap : function(dom, ename, fn, scope, o, touch) {
        o = !Ext.isObject(o) ? {} : o;

        var f = ["if(!window.Ext) {return;}"];
        
        if (touch) {
            f.push('e = new Ext.TouchEventObjectImpl(e, args);');
        }
        else {
            if(o.buffer || o.delay) {
                f.push('e = new Ext.EventObjectImpl(e);');
            } else {
                f.push('e = Ext.EventObject.setEvent(e);');
            }
        }

        if (o.delegate) {
            f.push('var t = e.getTarget("' + o.delegate + '", this);');
            f.push('if(!t) {return;}');
        } else {
            f.push('var t = e.target;');
        }

        if (o.target) {
            f.push('if(e.target !== o.target) {return;}');
        }

        if(o.stopEvent) {
            f.push('e.stopEvent();');
        } else {
            if(o.preventDefault) {
                f.push('e.preventDefault();');
            }
            if(o.stopPropagation) {
                f.push('e.stopPropagation();');
            }
        }

        if(o.normalized === false) {
            f.push('e = e.browserEvent;');
        }

        if(o.buffer) {
            f.push('(wrap.task && clearTimeout(wrap.task));');
            f.push('wrap.task = setTimeout(function(){');
        }

        if(o.delay) {
            f.push('wrap.tasks = wrap.tasks || [];');
            f.push('wrap.tasks.push(setTimeout(function(){');
        }

        
        f.push('fn.call(scope || dom, e, t, o);');

        if(o.single) {
            f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
        }

        if(o.delay) {
            f.push('}, ' + o.delay + '));');
        }

        if(o.buffer) {
            f.push('}, ' + o.buffer + ');');
        }

        var gen = new Function('e', 'o', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join("\n"));

        return function(e, args) {
            gen.call(dom, e, o, fn, scope, ename, dom, arguments.callee, args);
        };
    },

    getEventListenerCache : function(element, eventName) {
        var eventCache = this.getElementEventCache(element);
        return eventCache[eventName] || (eventCache[eventName] = []);
    },

    getElementEventCache : function(element) {
        var elementCache = Ext.cache[this.getId(element)];
        return elementCache.events || (elementCache.events = {});
    },

    
    onDocumentReady : function(fn, scope, options){
        var me = this,
            readyEvent = me.readyEvent,
            intervalId;

        if(Ext.isReady){ 
            readyEvent || (readyEvent = new Ext.util.Event());
            readyEvent.addListener(fn, scope, options);
            readyEvent.fire();
            readyEvent.listeners = []; 
        }
        else {
            if(!readyEvent) {
                readyEvent = me.readyEvent = new Ext.util.Event();

                
                var fireReady = function() {
                    Ext.isReady = true;

                    
                    window.removeEventListener('load', arguments.callee, false);

                    
                    if (intervalId) {
                        clearInterval(intervalId);
                    }
                    
                    
                    
                    setTimeout(function() {
                        Ext.supports.init();
                        
                        Ext.gesture.Manager.init();
                        Ext.orientation = Ext.Element.getOrientation();
                                                
                        
                        readyEvent.fire({
                            orientation: Ext.orientation,
                            width: Ext.Element.getViewportWidth(),
                            height: Ext.Element.getViewportHeight()
                        });
                        readyEvent.listeners = [];                        
                    }, 50);
                };

                
                

                
                intervalId = setInterval(function(){
                    if(/loaded|complete/.test(document.readyState)) {
                        clearInterval(intervalId);
                        intervalId = null;
                        fireReady();
                    }
                }, 10);

                
                window.addEventListener('load', fireReady, false);
            }

            options = options || {};
            options.delay = options.delay || 1;
            readyEvent.addListener(fn, scope, options);
        }
    },

    
    onWindowResize : function(fn, scope, options) {
        var me = this,
            resizeEvent = me.resizeEvent;

        if(!resizeEvent){
            me.resizeEvent = resizeEvent = new Ext.util.Event();
            var onResize = function() {
                resizeEvent.fire(Ext.Element.getViewportWidth(), Ext.Element.getViewportHeight());
            };
            this.addListener(window, 'resize', onResize, this);
        }

        resizeEvent.addListener(fn, scope, options);
    },

    onOrientationChange : function(fn, scope, options) {
        var me = this,
            orientationEvent = me.orientationEvent;

        if (!orientationEvent) {
            me.orientationEvent = orientationEvent = new Ext.util.Event();
            
            var onOrientationChange = function(viewport, size) {
                Ext.orientation = Ext.Viewport.getOrientation();

                orientationEvent.fire(Ext.orientation, size.width, size.height);
            };

            Ext.Viewport.on('resize', onOrientationChange, this);
        }

        orientationEvent.addListener(fn, scope, options);
    },
    
    unOrientationChange : function(fn, scope, options) {
        var me = this,
            orientationEvent = me.orientationEvent;
        
        if (orientationEvent) {
            orientationEvent.removeListener(fn, scope, options);
        }
    }
};


Ext.EventManager.on = Ext.EventManager.addListener;


Ext.EventManager.un = Ext.EventManager.removeListener;


Ext.onReady = Ext.EventManager.onDocumentReady;

Ext.EventObjectImpl = Ext.extend(Object, {
    constructor : function(e) {
        if (e) {
            this.setEvent(e.browserEvent || e);
        }
    },

    
    setEvent : function(e){
        var me = this;
        if (e == me || (e && e.browserEvent)){ 
            return e;
        }
        me.browserEvent = e;
        if(e) {
            me.type = e.type;

            
            var node = e.target;
            me.target = node && node.nodeType == 3 ? node.parentNode : node;

            
            me.xy = [e.pageX, e.pageY];
            me.timestamp = e.timeStamp;
        } else {
            me.target = null;
            me.xy = [0, 0];
        }
        return me;
    },

    
    stopEvent : function(){
        this.stopPropagation();
        this.preventDefault();
    },

    
    preventDefault : function(){
        if(this.browserEvent) {
            this.browserEvent.preventDefault();
        }
    },

    
    stopPropagation : function() {
        if(this.browserEvent) {
            this.browserEvent.stopPropagation();
        }
    },

    
    getPageX : function(){
        return this.xy[0];
    },

    
    getPageY : function(){
        return this.xy[1];
    },

    
    getXY : function(){
        return this.xy;
    },

    
    getTarget : function(selector, maxDepth, returnEl) {
        return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
    },

    getTime : function() {
        return this.timestamp;
    }
});


Ext.EventObject = new Ext.EventObjectImpl();

Ext.is = {

    init: function(navigator) {
        var me = this,
            platforms = me.platforms,
            ln = platforms.length,
            i, platform;

        navigator = navigator || window.navigator;

        for (i = 0; i < ln; i++) {
            platform = platforms[i];
            me[platform.identity] = platform.regex.test(navigator[platform.property]);
        }

        
        me.Desktop = me.Mac || me.Windows || (me.Linux && !me.Android);
        
        me.iOS = me.iPhone || me.iPad || me.iPod;

        
        me.Standalone = !!navigator.standalone;

        
        i = me.Android && (/Android\s(\d+\.\d+)/.exec(navigator.userAgent));
        if (i) {
            me.AndroidVersion = i[1];
            me.AndroidMajorVersion = parseInt(i[1], 10);
        }
        
        me.Tablet = me.iPad || (me.Android && me.AndroidMajorVersion === 3);

        
        me.Phone = !me.Desktop && !me.Tablet;

        
        me.MultiTouch = !me.Blackberry && !me.Desktop && !(me.Android && me.AndroidVersion < 3);
    },

    
    platforms: [{
        property: 'platform',
        regex: /iPhone/i,
        identity: 'iPhone'
    },

    
    {
        property: 'platform',
        regex: /iPod/i,
        identity: 'iPod'
    },

    
    {
        property: 'userAgent',
        regex: /iPad/i,
        identity: 'iPad'
    },

    
    {
        property: 'userAgent',
        regex: /Blackberry/i,
        identity: 'Blackberry'
    },

    
    {
        property: 'userAgent',
        regex: /Android/i,
        identity: 'Android'
    },

    
    {
        property: 'platform',
        regex: /Mac/i,
        identity: 'Mac'
    },

    
    {
        property: 'platform',
        regex: /Win/i,
        identity: 'Windows'
    },

    
    {
        property: 'platform',
        regex: /Linux/i,
        identity: 'Linux'
    }]
};

Ext.is.init();


Ext.supports = {
    init: function() {
        var doc = document,
            div = doc.createElement('div'),
            tests = this.tests,
            ln = tests.length,
            i, test;

        div.innerHTML = ['<div style="height:30px;width:50px;">', '<div style="height:20px;width:20px;"></div>', '</div>', '<div style="float:left; background-color:transparent;"></div>'].join('');

        doc.body.appendChild(div);

        for (i = 0; i < ln; i++) {
            test = tests[i];
            this[test.identity] = test.fn.call(this, doc, div);
        }

        doc.body.removeChild(div);
    },

    
    OrientationChange: ((typeof window.orientation != 'undefined') && ('onorientationchange' in window)),

    
    DeviceMotion: ('ondevicemotion' in window),

    
    
    
    Touch: ('ontouchstart' in window) && (!Ext.is.Desktop),

    tests: [
    
    {
        identity: 'Transitions',
        fn: function(doc, div) {
            var prefix = ['webkit', 'Moz', 'o', 'ms', 'khtml'],
                TE = 'TransitionEnd',
                transitionEndName = [
            prefix[0] + TE, 'transitionend', 
            prefix[2] + TE, prefix[3] + TE, prefix[4] + TE],
                ln = prefix.length,
                i = 0,
                out = false;
            div = Ext.get(div);
            for (; i < ln; i++) {
                if (div.getStyle(prefix[i] + "TransitionProperty")) {
                    Ext.supports.CSS3Prefix = prefix[i];
                    Ext.supports.CSS3TransitionEnd = transitionEndName[i];
                    out = true;
                    break;
                }
            }
            return out;
        }
    },

    
    {
        identity: 'RightMargin',
        fn: function(doc, div, view) {
            view = doc.defaultView;
            return ! (view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight != '0px');
        }
    },

    
    {
        identity: 'TransparentColor',
        fn: function(doc, div, view) {
            view = doc.defaultView;
            return ! (view && view.getComputedStyle(div.lastChild, null).backgroundColor != 'transparent');
        }
    },

    
    {
        identity: 'SVG',
        fn: function(doc) {
            return !!doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect;
        }
    },

    
    {
        identity: 'Canvas',
        fn: function(doc) {
            return !!doc.createElement('canvas').getContext;
        }
    },

    
    {
        identity: 'VML',
        fn: function(doc) {
            var d = doc.createElement("div");
            d.innerHTML = "<!--[if vml]><br><br><![endif]-->";
            return (d.childNodes.length == 2);
        }
    },

    
    {
        identity: 'Float',
        fn: function(doc, div) {
            return !!div.lastChild.style.cssFloat;
        }
    },

    
    {
        identity: 'AudioTag',
        fn: function(doc) {
            return !!doc.createElement('audio').canPlayType;
        }
    },

    
    {
        identity: 'History',
        fn: function() {
            return !! (window.history && history.pushState);
        }
    },

    
    {
        identity: 'CSS3DTransform',
        fn: function() {
            return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
        }
    },

    
    {
        identity: 'CSS3LinearGradient',
        fn: function(doc, div) {
            var property = 'background-image:',
                webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
                w3c = 'linear-gradient(left top, black, white)',
                moz = '-moz-' + w3c,
                options = [property + webkit, property + w3c, property + moz];

            div.style.cssText = options.join(';');

            return ("" + div.style.backgroundImage).indexOf('gradient') !== -1;
        }
    },

    
    {
        identity: 'CSS3BorderRadius',
        fn: function(doc, div) {
            var domPrefixes = ['borderRadius', 'BorderRadius', 'MozBorderRadius', 'WebkitBorderRadius', 'OBorderRadius', 'KhtmlBorderRadius'],
                pass = false,
                i;

            for (i = 0; i < domPrefixes.length; i++) {
                if (document.body.style[domPrefixes[i]] !== undefined) {
                    return pass = true;
                }
            }

            return pass;
        }
    },

    
    {
        identity: 'GeoLocation',
        fn: function() {
            return (typeof navigator != 'undefined' && typeof navigator.geolocation != 'undefined') || (typeof google != 'undefined' && typeof google.gears != 'undefined');
        }
    }]
};



Ext.data.Batch = Ext.extend(Ext.util.Observable, {
    
    autoStart: false,
    
    
    current: -1,
    
    
    total: 0,
    
    
    isRunning: false,
    
    
    isComplete: false,
    
    
    hasException: false,
    
    
    pauseOnException: true,
    
    constructor: function(config) {                
        this.addEvents(
          
          'complete',
          
          
          'exception',
          
          
          'operationcomplete',
          
          
          'operation-complete'
        );
        
        Ext.data.Batch.superclass.constructor.call(this, config);
        
        
        this.operations = [];
    },
    
    
    add: function(operation) {
        this.total++;
        
        operation.setBatch(this);
        
        this.operations.push(operation);
    },
    
    
    start: function() {
        this.hasException = false;
        this.isRunning = true;
        
        this.runNextOperation();
    },
    
    
    runNextOperation: function() {
        this.runOperation(this.current + 1);
    },
    
    
    pause: function() {
        this.isRunning = false;
    },
    
    
    runOperation: function(index) {
        var operations = this.operations,
            operation  = operations[index];
        
        if (operation == undefined) {
            this.isRunning  = false;
            this.isComplete = true;
            this.fireEvent('complete', this, operations[operations.length - 1]);
        } else {
            this.current = index;
            
            var onProxyReturn = function(operation) {
                var hasException = operation.hasException();
                
                if (hasException) {
                    this.hasException = true;
                    this.fireEvent('exception', this, operation);
                } else {
                    
                    
                    this.fireEvent('operation-complete', this, operation);
                    
                    this.fireEvent('operationcomplete', this, operation);
                }

                if (hasException && this.pauseOnException) {
                    this.pause();
                } else {
                    operation.setCompleted();
                    
                    this.runNextOperation();
                }
            };
            
            operation.setStarted();
            
            this.proxy[operation.action](operation, onProxyReturn, this);
        }
    }
});

Ext.data.Model = Ext.extend(Ext.util.Stateful, {
    evented: false,
    isModel: true,
    
    
    phantom : false,
    
    
    idProperty: 'id',
    
    constructor: function(data, id) {
        data = data || {};
        
        
        this.internalId = (id || id === 0) ? id : Ext.data.Model.id(this);
        
        Ext.data.Model.superclass.constructor.apply(this);
        
        
        var fields = this.fields.items,
            length = fields.length,
            field, name, i;
        
        for (i = 0; i < length; i++) {
            field = fields[i];
            name  = field.name;
            
            if (data[name] == undefined) {
                data[name] = field.defaultValue;
            }
        }
        
        this.set(data);
        this.dirty = false;
        
        if (this.getId()) {
            this.phantom = false;
        }
        
        if (typeof this.init == 'function') {
            this.init();
        }
    },
    
    
    validate: function() {
        var errors      = new 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;
    },
    
    
    getProxy: function() {
        return this.constructor.proxy;
    },
    
    
    save: function(options) {
        var me     = this,
            action = me.phantom ? 'create' : 'update';
        
        options = options || {};
        
        Ext.apply(options, {
            records: [me],
            action : action
        });
        
        var operation  = new Ext.data.Operation(options),
            successFn  = options.success,
            failureFn  = options.failure,
            callbackFn = options.callback,
            scope      = options.scope,
            record;
        
        var callback = function(operation) {
            record = operation.getRecords()[0];
            
            if (operation.wasSuccessful()) {
                
                
                me.set(record.data);
                record.dirty = false;

                if (typeof successFn == 'function') {
                    successFn.call(scope, record, operation);
                }
            } else {
                if (typeof failureFn == 'function') {
                    failureFn.call(scope, record, operation);
                }
            }
            
            if (typeof callbackFn == 'function') {
                callbackFn.call(scope, record, operation);
            }
        };
        
        me.getProxy()[action](operation, callback, me);
        
        return me;
    },
    
    
    getId: function() {
        return this.get(this.idProperty);
    },
    
    
    setId: function(id) {
        this.set(this.idProperty, id);
    },
    
    
    join : function(store) {
        
        this.store = store;
    },
    
    
    unjoin: function(store) {
        delete this.store;
    },
    
    
    afterEdit : function() {
        this.callStore('afterEdit');
    },
    
    
    afterReject : function() {
        this.callStore("afterReject");
    },
    
    
    afterCommit: function() {
        this.callStore('afterCommit');
    },
    
    
    callStore: function(fn) {
        var store = this.store;
        
        if (store != undefined && typeof store[fn] == "function") {
            store[fn](this);
        }
    }
});

Ext.apply(Ext.data.Model, {
    
    setProxy: function(proxy) {
        
        proxy = Ext.data.ProxyMgr.create(proxy);
        
        proxy.setModel(this);
        this.proxy = proxy;
        
        return proxy;
    },
    
    
    load: function(id, config) {
        config = Ext.applyIf(config || {}, {
            action: 'read',
            id    : id
        });
        
        var operation  = new Ext.data.Operation(config),
            callbackFn = config.callback,
            successFn  = config.success,
            failureFn  = config.failure,
            scope      = config.scope,
            record, callback;
        
        callback = function(operation) {
            record = operation.getRecords()[0];
            
            if (operation.wasSuccessful()) {
                if (typeof successFn == 'function') {
                    successFn.call(scope, record, operation);
                }
            } else {
                if (typeof failureFn == 'function') {
                    failureFn.call(scope, record, operation);
                }
            }
            
            if (typeof callbackFn == 'function') {
                callbackFn.call(scope, record, operation);
            }
        };
        
        this.proxy.read(operation, callback, this);
    }
});


Ext.data.Model.id = function(rec) {
    rec.phantom = true;
    return [Ext.data.Model.PREFIX, '-', Ext.data.Model.AUTO_ID++].join('');
};



Ext.ns('Ext.data.Record');


Ext.data.Record.id = Ext.data.Model.id;


Ext.data.Model.PREFIX = 'ext-record';
Ext.data.Model.AUTO_ID = 1;
Ext.data.Model.EDIT = 'edit';
Ext.data.Model.REJECT = 'reject';
Ext.data.Model.COMMIT = 'commit';


Ext.data.Association = Ext.extend(Object, {
    
    
    
    
    
    primaryKey: 'id',
    
    constructor: function(config) {
        Ext.apply(this, config);
        
        var types           = Ext.ModelMgr.types,
            ownerName       = config.ownerModel,
            associatedName  = config.associatedModel,
            ownerModel      = types[ownerName],
            associatedModel = types[associatedName],
            ownerProto;
        
        if (ownerModel == undefined) {
            throw new Error("The configured ownerModel was not valid (you tried " + ownerName + ")");
        }
        
        if (associatedModel == undefined) {
            throw new Error("The configured associatedModel was not valid (you tried " + associatedName + ")");
        }
        
        this.ownerModel = ownerModel;
        this.associatedModel = associatedModel;
        
        
        
        
        
        Ext.applyIf(this, {
            ownerName : ownerName,
            associatedName: associatedName
        });
    }
});

Ext.data.HasManyAssociation = Ext.extend(Ext.data.Association, {
    
    
    
    
    
    
    
    
    constructor: function(config) {
        Ext.data.HasManyAssociation.superclass.constructor.apply(this, arguments);
        
        var ownerProto = this.ownerModel.prototype,
            name       = this.name;
        
        Ext.applyIf(this, {
            storeName : name + "Store",
            foreignKey: this.ownerName.toLowerCase() + "_id"
        });
        
        ownerProto[name] = this.createStore();
    },
    
    
    createStore: function() {
        var associatedModel = this.associatedModel,
            storeName       = this.storeName,
            foreignKey      = this.foreignKey,
            primaryKey      = this.primaryKey,
            filterProperty  = this.filterProperty,
            storeConfig     = this.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] = new Ext.data.Store(config);
            }
            
            return me[storeName];
        };
    }
});

Ext.data.BelongsToAssociation = Ext.extend(Ext.data.Association, {
    
    
    

    
    
    constructor: function(config) {
        Ext.data.BelongsToAssociation.superclass.constructor.apply(this, 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'
        });
        
        ownerProto[getterName] = me.createGetter();
        ownerProto[setterName] = me.createSetter();
    },
    
    
    createSetter: function() {
        var me              = this,
            ownerModel      = me.ownerModel,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey;
        
        
        return function(value, options, scope) {
            this.set(foreignKey, value);
            
            if (typeof options == 'function') {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }
            
            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },
    
    
    createGetter: function() {
        var me              = this,
            ownerModel      = me.ownerModel,
            associatedName  = me.associatedName,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey,
            instanceName    = me.instanceName;
        
        
        return function(options, scope) {
            options = options || {};
            
            var foreignKeyId = this.get(foreignKey),
                instance, callbackFn;
                
            if (this[instanceName] == undefined) {
                instance = Ext.ModelMgr.create({}, associatedName);
                instance.set(primaryKey, foreignKeyId);

                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || this
                    };
                }
                
                associatedModel.load(foreignKeyId, options);
            } else {
                instance = this[instanceName];
                
                
                
                
                if (typeof options == 'function') {
                    options.call(scope || this, instance);
                }
                
                if (options.success) {
                    options.success.call(scope || this, instance);
                }
                
                if (options.callback) {
                    options.callback.call(scope || this, instance);
                }
                
                return instance;
            }
        };
    }
});

Ext.data.PolymorphicAssociation = Ext.extend(Ext.data.Association, {

    constructor: function(config) {
        Ext.data.PolymorphicAssociation.superclass.constructor.call(this, config);
        
        var ownerProto = this.ownerModel.prototype,
            name       = this.name;
        
        Ext.applyIf(this, {
            associationIdField: this.ownerName.toLowerCase() + "_id"
        });
        
        ownerProto[name] = this.createStore();
    },

    
    createStore: function() {
        var association           = this,
            ownerName             = this.ownerName,
            storeName             = this.name + "Store",
            associatedModel       = this.associatedModel,
            primaryKey            = this.primaryKey,
            associationIdField    = 'associated_id',
            associationModelField = 'associated_model';
        
        return function() {
            var me = this,
                modelDefaults = {},
                config, filters;
                
            if (me[storeName] == undefined) {
                filters = [
                    {
                        property  : associationIdField,
                        value     : me.get(primaryKey),
                        exactMatch: true
                    },
                    {
                        property  : associationModelField,
                        value     : ownerName,
                        exactMatch: true
                    }
                ];
                
                modelDefaults[associationIdField] = me.get(primaryKey);
                modelDefaults[associationModelField] = ownerName;
                
                config = Ext.apply({}, association.storeConfig || {}, {
                    model        : associatedModel,
                    filters      : filters,
                    remoteFilter : false,
                    modelDefaults: modelDefaults
                });
                
                me[storeName] = new Ext.data.Store(config);
            }
            
            return me[storeName];
        };
    }
});

Ext.data.validations = {
    
    
    presenceMessage: 'must be present',
    
    
    lengthMessage: 'is the wrong length',
    
    
    formatMessage: 'is the wrong format',
    
    
    inclusionMessage: 'is not included in the list of acceptable values',
    
    
    exclusionMessage: 'is not an acceptable value',
    
    
    presence: function(config, value) {
        if (value == undefined) {
            value = config;
        }
        
        return !!value;
    },
    
    
    length: function(config, value) {
        if (value == undefined) {
            return false;
        }
        
        var length = value.length,
            min    = config.min,
            max    = config.max;
        
        if ((min && length < min) || (max && length > max)) {
            return false;
        } else {
            return true;
        }
    },
    
    
    format: function(config, value) {
        return !!(config.matcher && config.matcher.test(value));
    },
    
    
    inclusion: function(config, value) {
        return config.list && config.list.indexOf(value) != -1;
    },
    
    
    exclusion: function(config, value) {
        return config.list && config.list.indexOf(value) == -1;
    }
};

Ext.data.Errors = Ext.extend(Ext.util.MixedCollection, {
    
    isValid: function() {
        return this.length == 0;
    },
    
    
    getByField: function(fieldName) {
        var errors = [],
            error, field, i;
            
        for (i = 0; i < this.length; i++) {
            error = this.items[i];
            
            if (error.field == fieldName) {
                errors.push(error);
            }
        }
        
        return errors;
    }
});


Ext.data.Field = Ext.extend(Object, {
    
    constructor : function(config) {
        if (Ext.isString(config)) {
            config = {name: config};
        }
        Ext.apply(this, config);
        
        var types = Ext.data.Types,
            st = this.sortType,
            t;

        if (this.type) {
            if (Ext.isString(this.type)) {
                this.type = types[this.type.toUpperCase()] || types.AUTO;
            }
        } else {
            this.type = types.AUTO;
        }

        
        if (Ext.isString(st)) {
            this.sortType = Ext.data.SortTypes[st];
        } else if(Ext.isEmpty(st)) {
            this.sortType = this.type.sortType;
        }

        if (!this.convert) {
            this.convert = this.type.convert;
        }
    },
    
    
    
    
    
    
    
    dateFormat: null,
    
    
    useNull: false,
    
    
    defaultValue: "",
    
    mapping: null,
    
    sortType : null,
    
    sortDir : "ASC",
    
    allowBlank : true
});


Ext.data.SortTypes = {
    
    none : function(s) {
        return s;
    },

    
    stripTagsRE : /<\/?[^>]+>/gi,

    
    asText : function(s) {
        return String(s).replace(this.stripTagsRE, "");
    },

    
    asUCText : function(s) {
        return String(s).toUpperCase().replace(this.stripTagsRE, "");
    },

    
    asUCString : function(s) {
        return String(s).toUpperCase();
    },

    
    asDate : function(s) {
        if(!s){
            return 0;
        }
        if(Ext.isDate(s)){
            return s.getTime();
        }
        return Date.parse(String(s));
    },

    
    asFloat : function(s) {
        var val = parseFloat(String(s).replace(/,/g, ""));
        return isNaN(val) ? 0 : val;
    },

    
    asInt : function(s) {
        var val = parseInt(String(s).replace(/,/g, ""), 10);
        return isNaN(val) ? 0 : val;
    }
};

Ext.data.Types = new function() {
    var st = Ext.data.SortTypes;
    
    Ext.apply(this, {
        
        stripRe: /[\$,%]/g,
        
        
        AUTO: {
            convert: function(v) {
                return v;
            },
            sortType: st.none,
            type: 'auto'
        },

        
        STRING: {
            convert: function(v) {
                return (v === undefined || v === null) ? '' : String(v);
            },
            sortType: st.asUCString,
            type: 'string'
        },

        
        INT: {
            convert: function(v) {
                return v !== undefined && v !== null && v !== '' ?
                    parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'int'
        },
        
        
        FLOAT: {
            convert: function(v) {
                return v !== undefined && v !== null && v !== '' ?
                    parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'float'
        },
        
        
        BOOL: {
            convert: function(v) {
                return v === true || v === 'true' || v == 1;
            },
            sortType: st.none,
            type: 'bool'
        },
        
        
        DATE: {
            convert: function(v) {
                var df = this.dateFormat;
                if (!v) {
                    return null;
                }
                if (Ext.isDate(v)) {
                    return v;
                }
                if (df) {
                    if (df == 'timestamp') {
                        return new Date(v*1000);
                    }
                    if (df == 'time') {
                        return new Date(parseInt(v, 10));
                    }
                    return Date.parseDate(v, df);
                }
                
                var parsed = Date.parse(v);
                return parsed ? new Date(parsed) : null;
            },
            sortType: st.asDate,
            type: 'date'
        }
    });
    
    Ext.apply(this, {
        
        BOOLEAN: this.BOOL,
        
        
        INTEGER: this.INT,
        
        
        NUMBER: this.FLOAT    
    });
};

Ext.ModelMgr = new Ext.AbstractManager({
    typeName: 'mtype',
    
    
    defaultProxyType: 'ajax',
    
    
    associationStack: [],
    
    
    registerType: function(name, config) {
        
        
        var PluginMgr    = Ext.PluginMgr,
            plugins      = PluginMgr.findByType('model', true),
            fields       = config.fields || [],
            associations = config.associations || [],
            belongsTo    = config.belongsTo,
            hasMany      = config.hasMany,
            extendName   = config.extend,
            modelPlugins = config.plugins || [],
            association, model, length, i,
            extendModel, extendModelProto, extendValidations, proxy;
        
        
        
        if (belongsTo) {
            if (!Ext.isArray(belongsTo)) {
                belongsTo = [belongsTo];
            }
            
            for (i = 0; i < belongsTo.length; i++) {
                association = belongsTo[i];
                
                if (!Ext.isObject(association)) {
                    association = {model: association};
                }
                Ext.apply(association, {type: 'belongsTo'});
                
                associations.push(association);
            }
            
            delete config.belongsTo;
        }
        
        if (hasMany) {
            if (!Ext.isArray(hasMany)) {
                hasMany = [hasMany];
            }
            
            for (i = 0; i < hasMany.length; i++) {
                association = hasMany[i];
                
                if (!Ext.isObject(association)) {
                    association = {model: association};
                }
                
                Ext.apply(association, {type: 'hasMany'});
                
                associations.push(association);
            }
            
            delete config.hasMany;
        }
        
        
        if (extendName) {
            extendModel       = this.types[extendName];
            extendModelProto  = extendModel.prototype;
            extendValidations = extendModelProto.validations;
            
            proxy              = extendModel.proxy;
            fields             = extendModelProto.fields.items.concat(fields);
            associations       = extendModelProto.associations.items.concat(associations);
            config.validations = extendValidations ? extendValidations.concat(config.validations) : config.validations;
        } else {
            extendModel = Ext.data.Model;
            proxy = config.proxy;
        }
        
        model = Ext.extend(extendModel, config);
        
        for (i = 0, length = modelPlugins.length; i < length; i++) {
            plugins.push(PluginMgr.create(modelPlugins[i]));
        }
        
        this.types[name] = model;
        
        Ext.override(model, {
            plugins     : plugins,
            fields      : this.createFields(fields),
            associations: this.createAssociations(associations, name)
        });
        
        model.modelName = name;
        Ext.data.Model.setProxy.call(model, proxy || this.defaultProxyType);
        model.getProxy = model.prototype.getProxy;
        
        model.load = function() {
            Ext.data.Model.load.apply(this, arguments);
        };
        
        for (i = 0, length = plugins.length; i < length; i++) {
            plugins[i].bootstrap(model, config);
        }
        
        model.defined = true;
        this.onModelDefined(model);
        
        return model;
    },
    
    
    onModelDefined: function(model) {
        var stack  = this.associationStack,
            length = stack.length,
            create = [],
            association, i;
        
        for (i = 0; i < length; i++) {
            association = stack[i];
            
            if (association.associatedModel == model.modelName) {
                create.push(association);
            }
        }
        
        length = create.length;
        for (i = 0; i < length; i++) {
            this.addAssociation(create[i], this.types[create[i].ownerModel].prototype.associations);
            stack.remove(create[i]);
        }
    },
    
    
    createAssociations: function(associations, name) {
        var length = associations.length,
            i, associationsMC, association;
        
        associationsMC = new Ext.util.MixedCollection(false, function(association) {
            return association.name;
        });
        
        for (i = 0; i < length; i++) {
            association = associations[i];
            Ext.apply(association, {
                ownerModel: name,
                associatedModel: association.model
            });
            
            if (this.types[association.model] == undefined) {
                this.associationStack.push(association);
            } else {
                this.addAssociation(association, associationsMC);
            }
        }
        
        return associationsMC;
    },
    
    
    addAssociation: function(association, associationsMC) {
        var type = association.type;
        
        if (type == 'belongsTo') {
            associationsMC.add(new Ext.data.BelongsToAssociation(association));
        }
        
        if (type == 'hasMany') {
            associationsMC.add(new Ext.data.HasManyAssociation(association));
        }
        
        if (type == 'polymorphic') {
            associationsMC.add(new Ext.data.PolymorphicAssociation(association));
        }
    },
    
    
    createFields: function(fields) {
        var length = fields.length,
            i, fieldsMC;
        
        fieldsMC = new Ext.util.MixedCollection(false, function(field) {
            return field.name;
        });
        
        for (i = 0; i < length; i++) {
            fieldsMC.add(new Ext.data.Field(fields[i]));
        }
        
        return fieldsMC;
    },
    
    
    getModel: function(id) {
        var model = id;
        if (typeof model == 'string') {
            model = this.types[model];
        }
        return model;
    },
    
    
    create: function(config, name, id) {
        var con = typeof name == 'function' ? name : this.types[name || config.name];
        
        return new con(config, id);
    }
});


Ext.regModel = function() {
    return Ext.ModelMgr.registerType.apply(Ext.ModelMgr, arguments);
};

Ext.data.Operation = Ext.extend(Object, {
    
    synchronous: true,
    
    
    action: undefined,
    
    
    filters: undefined,
    
    
    sorters: undefined,
    
    
    group: undefined,
    
    
    start: undefined,
    
    
    limit: undefined,
    
    
    batch: undefined,
        
    
    started: false,
    
    
    running: false,
    
    
    complete: false,
    
    
    success: undefined,
    
    
    exception: false,
    
    
    error: undefined,
    
    constructor: function(config) {
        Ext.apply(this, config || {});
    },
    
    
    setStarted: function() {
        this.started = true;
        this.running = true;
    },
    
    
    setCompleted: function() {
        this.complete = true;
        this.running  = false;
    },
    
    
    setSuccessful: function() {
        this.success = true;
    },
    
    
    setException: function(error) {
        this.exception = true;
        this.success = false;
        this.running = false;
        this.error = error;
    },
    
    
    markStarted: function() {
        console.warn("Operation: markStarted has been deprecated. Please use setStarted");
        return this.setStarted();
    },
    
    
    markCompleted: function() {
        console.warn("Operation: markCompleted has been deprecated. Please use setCompleted");
        return this.setCompleted();
    },
    
    
    markSuccessful: function() {
        console.warn("Operation: markSuccessful has been deprecated. Please use setSuccessful");
        return this.setSuccessful();
    },
    
    
    markException: function() {
        console.warn("Operation: markException has been deprecated. Please use setException");
        return this.setException();
    },
    
    
    hasException: function() {
        return this.exception === true;
    },
    
    
    getError: function() {
        return this.error;
    },
    
    
    getRecords: function() {
        var resultSet = this.getResultSet();
        
        return (resultSet == undefined ? this.records : resultSet.records);
    },
    
    
    getResultSet: function() {
        return this.resultSet;
    },
    
    
    isStarted: function() {
        return this.started === true;
    },
    
    
    isRunning: function() {
        return this.running === true;
    },
    
    
    isComplete: function() {
        return this.complete === true;
    },
    
    
    wasSuccessful: function() {
        return this.isComplete() && this.success === true;
    },
    
    
    setBatch: function(batch) {
        this.batch = batch;
    },
    
    
    allowWrite: function() {
        return this.action != 'read';
    }
});

Ext.data.ProxyMgr = new Ext.AbstractManager({
    create: function(config) {
        if (config == undefined || typeof config == 'string') {
            config = {
                type: config
            };
        }

        if (!(config instanceof Ext.data.Proxy)) {
            Ext.applyIf(config, {
                type : this.defaultProxyType,
                model: this.model
            });

            var type = config[this.typeName] || config.type,
                Constructor = this.types[type];

            if (Constructor == undefined) {
                throw new Error(Ext.util.Format.format("The '{0}' type has not been registered with this manager", type));
            }

            return new Constructor(config);
        } else {
            return config;
        }
    }
});

Ext.data.ReaderMgr = new Ext.AbstractManager({
    typeName: 'rtype'
});

Ext.data.Request = Ext.extend(Object, {
    
    action: undefined,
    
    
    params: undefined,
    
    
    method: 'GET',
    
    
    url: undefined,

    constructor: function(config) {
        Ext.apply(this, config);
    }
});

Ext.data.ResultSet = Ext.extend(Object, {
    
    loaded: true,
    
    
    count: 0,
    
    
    total: 0,
    
    
    success: false,
    
    

    constructor: function(config) {
        Ext.apply(this, config);
        
        
        this.totalRecords = this.total;
        
        if (config.count == undefined) {
            this.count = this.records.length;
        }
    }
});

Ext.data.AbstractStore = Ext.extend(Ext.util.Observable, {
    remoteSort  : false,
    remoteFilter: false,

    

    
    autoLoad: false,

    
    autoSave: false,

    
    batchUpdateMode: 'operation',

    
    filterOnLoad: true,

    
    sortOnLoad: true,

    
    defaultSortDirection: "ASC",

    
    implicitModel: false,

    
    defaultProxyType: 'memory',

    
    isDestroyed: false,

    isStore: true,

    

    
    constructor: function(config) {
        this.addEvents(
            
            'add',

            
            'remove',
            
            
            'update',

            
            'datachanged',

            
            'beforeload',

            
            'load',

            
            'beforesync'
        );
        
        Ext.apply(this, config);

        
        this.removed = [];

        
        this.sortToggle = {};

        Ext.data.AbstractStore.superclass.constructor.apply(this, arguments);

        this.model = Ext.ModelMgr.getModel(config.model);
        
        
        Ext.applyIf(this, {
            modelDefaults: {}
        });

        
        if (!this.model && config.fields) {
            this.model = Ext.regModel('ImplicitModel-' + this.storeId || Ext.id(), {
                fields: config.fields
            });

            delete this.fields;

            this.implicitModel = true;
        }

        
        this.setProxy(config.proxy || this.model.proxy);

        if (this.id && !this.storeId) {
            this.storeId = this.id;
            delete this.id;
        }

        if (this.storeId) {
            Ext.StoreMgr.register(this);
        }
        
        
        this.sorters = new Ext.util.MixedCollection();
        this.sorters.addAll(this.decodeSorters(config.sorters));
        
        
        this.filters = new Ext.util.MixedCollection();
        this.filters.addAll(this.decodeFilters(config.filters));
    },


    
    setProxy: function(proxy) {
        if (proxy instanceof Ext.data.Proxy) {
            proxy.setModel(this.model);
        } else {
            Ext.applyIf(proxy, {
                model: this.model
            });
            
            proxy = Ext.data.ProxyMgr.create(proxy);
        }
        
        this.proxy = proxy;
        
        return this.proxy;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    create: function(data, options) {
        var instance = Ext.ModelMgr.create(Ext.applyIf(data, this.modelDefaults), this.model.modelName),
            operation;
        
        options = options || {};

        Ext.applyIf(options, {
            action : 'create',
            records: [instance]
        });

        operation = new Ext.data.Operation(options);

        this.proxy.create(operation, this.onProxyWrite, this);
        
        return instance;
    },

    read: function() {
        return this.load.apply(this, arguments);
    },

    onProxyRead: Ext.emptyFn,

    update: function(options) {
        options = options || {};

        Ext.applyIf(options, {
            action : 'update',
            records: this.getUpdatedRecords()
        });

        var operation = new Ext.data.Operation(options);

        return this.proxy.update(operation, this.onProxyWrite, this);
    },

    onProxyWrite: Ext.emptyFn,


    
    destroy: function(options) {
        options = options || {};

        Ext.applyIf(options, {
            action : 'destroy',
            records: this.getRemovedRecords()
        });

        var operation = new Ext.data.Operation(options);

        return this.proxy.destroy(operation, this.onProxyWrite, this);
    },

    
    onBatchOperationComplete: function(batch, operation) {
        return this.onProxyWrite(operation);
    },

    
    onBatchComplete: function(batch, operation) {
        var operations = batch.operations,
            length = operations.length,
            i;

        this.suspendEvents();

        for (i = 0; i < length; i++) {
            this.onProxyWrite(operations[i]);
        }

        this.resumeEvents();

        this.fireEvent('datachanged', this);
    },

    onBatchException: function(batch, operation) {
        
        
        
        
        
    },

    
    filterNew: function(item) {
        return item.phantom == true || item.needsAdd == true;
    },

    
    getNewRecords: function() {
        return [];
    },

    
    getUpdatedRecords: function() {
        return [];
    },

    
    filterDirty: function(item) {
        return item.dirty == true;
    },

    
    getRemovedRecords: function() {
        return this.removed;
    },


    sort: function(sorters, direction) {

    },

    
    decodeSorters: function(sorters) {
        if (!Ext.isArray(sorters)) {
            if (sorters == undefined) {
                sorters = [];
            } else {
                sorters = [sorters];
            }
        }

        var length = sorters.length,
            Sorter = Ext.util.Sorter,
            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     : 'data',
                    direction: "ASC"
                });

                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                sorters[i] = new Sorter(config);
            }
        }

        return sorters;
    },

    filter: function(filters, value) {

    },

    
    createSortFunction: function(field, direction) {
        direction = direction || "ASC";
        var directionModifier = direction.toUpperCase() == "DESC" ? -1 : 1;

        var fields   = this.model.prototype.fields,
            sortType = fields.get(field).sortType;

        
        
        return function(r1, r2) {
            var v1 = sortType(r1.data[field]),
                v2 = sortType(r2.data[field]);

            return directionModifier * (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
        };
    },

    
    decodeFilters: function(filters) {
        if (!Ext.isArray(filters)) {
            if (filters == undefined) {
                filters = [];
            } else {
                filters = [filters];
            }
        }

        var length = filters.length,
            Filter = Ext.util.Filter,
            config, i;

        for (i = 0; i < length; i++) {
            config = filters[i];

            if (!(config instanceof Filter)) {
                Ext.apply(config, {
                    root: 'data'
                });

                
                if (config.fn) {
                    config.filterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        filterFn: config
                    };
                }

                filters[i] = new Filter(config);
            }
        }

        return filters;
    },

    clearFilter: function(supressEvent) {

    },

    isFiltered: function() {

    },

    filterBy: function(fn, scope) {

    },


    
    sync: function() {
        var me        = this,
            options   = {},
            toCreate  = me.getNewRecords(),
            toUpdate  = me.getUpdatedRecords(),
            toDestroy = me.getRemovedRecords(),
            needsSync = false;

        if (toCreate.length > 0) {
            options.create = toCreate;
            needsSync = true;
        }

        if (toUpdate.length > 0) {
            options.update = toUpdate;
            needsSync = true;
        }

        if (toDestroy.length > 0) {
            options.destroy = toDestroy;
            needsSync = true;
        }

        if (needsSync && me.fireEvent('beforesync', options) !== false) {
            me.proxy.batch(options, me.getBatchListeners());
        }
    },


    
    getBatchListeners: function() {
        var listeners = {
            scope: this,
            exception: this.onBatchException
        };

        if (this.batchUpdateMode == 'operation') {
            listeners['operationcomplete'] = this.onBatchOperationComplete;
        } else {
            listeners['complete'] = this.onBatchComplete;
        }

        return listeners;
    },

    
    save: function() {
        return this.sync.apply(this, arguments);
    },

    
    load: function(options) {
        var me = this,
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'read',
            filters: me.filters.items,
            sorters: me.sorters.items
        });

        operation = new Ext.data.Operation(options);

        if (me.fireEvent('beforeload', me, operation) !== false) {
            me.loading = true;
            me.proxy.read(operation, me.onProxyLoad, me);
        }
        
        return me;
    },

    
    afterEdit : function(record) {
        this.fireEvent('update', this, record, Ext.data.Model.EDIT);
    },

    
    afterReject : function(record) {
        this.fireEvent('update', this, record, Ext.data.Model.REJECT);
    },

    
    afterCommit : function(record) {
        if (this.autoSave) {
            this.sync();
        }

        this.fireEvent('update', this, record, Ext.data.Model.COMMIT);
    },

    clearData: Ext.emptyFn,

    destroyStore: function() {
        if (!this.isDestroyed) {
            if (this.storeId) {
                Ext.StoreMgr.unregister(this);
            }
            this.clearData();
            this.data = null;
            this.tree = null;
            
            this.reader = this.writer = null;
            this.clearListeners();
            this.isDestroyed = true;

            if (this.implicitModel) {
                Ext.destroy(this.model);
            }
        }
    },

    
    getSortState : function() {
        return this.sortInfo;
    },

    getCount: function() {

    },

    getById: function(id) {

    },

    
    
    removeAll: function() {

    }
});


Ext.data.Store = Ext.extend(Ext.data.AbstractStore, {
    
    remoteSort: false,

    
    remoteFilter: false,

    

    

    
    groupField: undefined,

    
    groupDir: "ASC",

    
    pageSize: 25,

    
    currentPage: 1,
    
    
    clearOnPageLoad: true,

    
    implicitModel: false,

    
    loading: false,
    
    
    sortOnFilter: true,

    isStore: true,

    
    constructor: function(config) {
        config = config || {};
        
        
        this.data = new Ext.util.MixedCollection(false, function(record) {
            return record.internalId;
        });

        if (config.data) {
            this.inlineData = config.data;
            delete config.data;
        }

        Ext.data.Store.superclass.constructor.call(this, config);
        
        var proxy = this.proxy,
            data  = this.inlineData;
            
        if (data) {
            if (proxy instanceof Ext.data.MemoryProxy) {
                proxy.data = data;
                this.read();
            } else {
                this.add.apply(this, data);
            }
            
            this.sort();
            delete this.inlineData;
        } else if (this.autoLoad) {
            Ext.defer(this.load, 10, this, [typeof this.autoLoad == 'object' ? this.autoLoad : undefined]);
            
            
        }
    },

    
    getGroups: function() {
        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 groups;
    },

    
    getGroupString: function(instance) {
        return instance.get(this.groupField);
    },
    
    
    first: function() {
        return this.data.first();
    },
    
    
    last: function() {
        return this.data.last();
    },

    
    insert : function(index, records) {
        var i, record, len;

        records = [].concat(records);
        for (i = 0, len = records.length; i < len; i++) {
            record = this.createModel(records[i]);
            record.set(this.modelDefaults);

            this.data.insert(index + i, record);
            record.join(this);
        }

        if (this.snapshot) {
            this.snapshot.addAll(records);
        }

        this.fireEvent('add', this, records, index);
        this.fireEvent('datachanged', this);
    },

    
    add: function(records) {
        
        if (!Ext.isArray(records)) {
            records = Array.prototype.slice.apply(arguments);
        }
        
        var length  = records.length,
            record, i;

        for (i = 0; i < length; i++) {
            record = this.createModel(records[i]);

            if (record.phantom == false) {
                record.needsAdd = true;
            }
            
            records[i] = record;
        }

        this.insert(this.data.length, records);

        return records;
    },

    
    createModel: function(record) {
        if (!(record instanceof Ext.data.Model)) {
            record = Ext.ModelMgr.create(record, this.model);
        }
        
        return record;
    },

    
    each : function(fn, scope) {
        this.data.each(fn, scope);
    },

    
    remove: function(records) {
        if (!Ext.isArray(records)) {
            records = [records];
        }

        var length = records.length,
            i, index, record;

        for (i = 0; i < length; i++) {
            record = records[i];
            index = this.data.indexOf(record);

            if (index > -1) {
                this.removed.push(record);

                if (this.snapshot) {
                    this.snapshot.remove(record);
                }

                record.unjoin(this);
                this.data.remove(record);

                this.fireEvent('remove', this, record, index);
            }
        }

        this.fireEvent('datachanged', this);
    },

    
    removeAt: function(index) {
        var record = this.getAt(index);

        if (record) {
            this.remove(record);
        }
    },

    
    load: function(options) {
        options = options || {};
        
        if (Ext.isFunction(options)) {
            options = {
                callback: options
            };
        }
        
        Ext.applyIf(options, {
            group : {field: this.groupField, direction: this.groupDir},
            start : 0,
            limit : this.pageSize,
            addRecords: false
        });
        
        return Ext.data.Store.superclass.load.call(this, options);
    },
    
    
    isLoading: function() {
        return this.loading;
    },

    
    onProxyLoad: function(operation) {
        var records = operation.getRecords();
        
        this.loadRecords(records, operation.addRecords);
        this.loading = false;
        this.fireEvent('load', this, records, operation.wasSuccessful());
        
        
        
        this.fireEvent('read', this, records, operation.wasSuccessful());

        
        var callback = operation.callback;
        
        if (typeof callback == 'function') {
            callback.call(operation.scope || this, records, operation, operation.wasSuccessful());
        }
    },

    
    onProxyWrite: function(operation) {
        var data     = this.data,
            action   = operation.action,
            records  = operation.getRecords(),
            length   = records.length,
            callback = operation.callback,
            record, i;

        if (operation.wasSuccessful()) {
            if (action == 'create' || action == 'update') {
                for (i = 0; i < length; i++) {
                    record = records[i];

                    record.phantom = false;
                    record.join(this);
                    data.replace(record);
                }
            }

            else if (action == 'destroy') {
                for (i = 0; i < length; i++) {
                    record = records[i];

                    record.unjoin(this);
                    data.remove(record);
                }

                this.removed = [];
            }

            this.fireEvent('datachanged');
        }

        
        if (typeof callback == 'function') {
            callback.call(operation.scope || this, records, operation, operation.wasSuccessful());
        }
    },

    
    getNewRecords: function() {
        return this.data.filterBy(this.filterNew).items;
    },

    
    getUpdatedRecords: function() {
        return this.data.filterBy(this.filterDirty).items;
    },

    
    sort: function(sorters, direction) {
        if (Ext.isString(sorters)) {
            var property   = sorters,
                sortToggle = this.sortToggle,
                toggle     = Ext.util.Format.toggle;

            if (direction == undefined) {
                sortToggle[property] = toggle(sortToggle[property] || "", "ASC", "DESC");
                direction = sortToggle[property];
            }

            sorters = {
                property : property,
                direction: direction
            };
        }
        
        if (arguments.length != 0) {
            this.sorters.clear();
        }
        
        this.sorters.addAll(this.decodeSorters(sorters));

        if (this.remoteSort) {
            
            this.load();
        } else {
            this.data.sort(this.sorters.items);

            this.fireEvent('datachanged', this);
        }
    },


    
    filter: function(filters, value) {
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value   : value
            };
        }
        
        this.filters.addAll(this.decodeFilters(filters));

        if (this.remoteFilter) {
            
            this.load();
        } else {
            
            this.snapshot = this.snapshot || this.data.clone();

            this.data = this.data.filter(this.filters.items);
            
            if (this.sortOnFilter && !this.remoteSort) {
                this.sort();
            } else {
                this.fireEvent('datachanged', this);
            }
        }
    },

    
    clearFilter : function(suppressEvent) {
        this.filters.clear();
        
        if (this.isFiltered()) {
            this.data = this.snapshot.clone();
            delete this.snapshot;

            if (suppressEvent !== true) {
                this.fireEvent('datachanged', this);
            }
        }
    },

    
    isFiltered : function() {
        return !!this.snapshot && this.snapshot != this.data;
    },

    
    filterBy : function(fn, scope) {
        this.snapshot = this.snapshot || this.data.clone();
        this.data = this.queryBy(fn, scope || this);
        this.fireEvent('datachanged', this);
    },

    
    queryBy : function(fn, scope) {
        var data = this.snapshot || this.data;
        return data.filterBy(fn, scope||this);
    },
    
    
    loadData: function(data, append) {
        var model  = this.model,
            length = data.length,
            i, record;

        
        for (i = 0; i < length; i++) {
            record = data[i];

            if (!(record instanceof Ext.data.Model)) {
                data[i] = Ext.ModelMgr.create(record, model);
            }
        }

        this.loadRecords(data, append);
    },

    
    loadRecords: function(records, add) {
        if (!add) {
            this.data.clear();
        }
        
        this.data.addAll(records);
        
        
        for (var i = 0, length = records.length; i < length; i++) {
            records[i].needsAdd = false;
            records[i].join(this);
        }
        
        
        this.suspendEvents();

        if (this.filterOnLoad && !this.remoteFilter) {
            this.filter();
        }

        if (this.sortOnLoad && !this.remoteSort) {
            this.sort();
        }

        this.resumeEvents();
        this.fireEvent('datachanged', this, records);
    },

    

    
    loadPage: function(page) {
        this.currentPage = page;

        this.read({
            page : page,
            start: (page - 1) * this.pageSize,
            limit: this.pageSize,
            addRecords: !this.clearOnPageLoad
        });
    },

    
    nextPage: function() {
        this.loadPage(this.currentPage + 1);
    },

    
    previousPage: function() {
        this.loadPage(this.currentPage - 1);
    },

    
    clearData: function(){
        this.data.each(function(record) {
            record.unjoin();
        });

        this.data.clear();
    },

    
    find : function(property, value, start, anyMatch, caseSensitive, exactMatch) {
        var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
        return fn ? this.data.findIndexBy(fn, null, start) : -1;
    },

    
    findRecord : function() {
        var index = this.find.apply(this, arguments);
        return index != -1 ? this.getAt(index) : null;
    },

    
    createFilterFn : function(property, value, anyMatch, caseSensitive, exactMatch) {
        if(Ext.isEmpty(value)){
            return false;
        }
        value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
        return function(r) {
            return value.test(r.data[property]);
        };
    },

    
    findExact: function(property, value, start) {
        return this.data.findIndexBy(function(rec){
            return rec.get(property) === value;
        }, this, start);
    },

    
    findBy : function(fn, scope, start) {
        return this.data.findIndexBy(fn, scope, start);
    },

    
    collect : function(dataIndex, allowNull, bypassFilter) {
        var values  = [],
            uniques = {},
            length, value, strValue, data, i;

        if (bypassFilter === true && this.snapshot) {
            data = this.snapshot.items;
        } else {
            data = this.data.items;
        }

        length = data.length;

        for (i = 0; i < length; i++) {
            value = data[i].data[dataIndex];
            strValue = String(value);

            if ((allowNull || !Ext.isEmpty(value)) && !uniques[strValue]) {
                uniques[strValue] = true;
                values[values.length] = value;
            }
        }

        return values;
    },

    
    sum : function(property, start, end) {
        var records = this.data.items,
            value   = 0,
            i;

        start = start || 0;
        end   = (end || end === 0) ? end : records.length - 1;

        for (i = start; i <= end; i++) {
            value += (records[i].data[property] || 0);
        }

        return value;
    },

    
    getCount : function() {
        return this.data.length || 0;
    },

    
    getAt : function(index) {
        return this.data.getAt(index);
    },

    
    getRange : function(start, end) {
        return this.data.getRange(start, end);
    },

    
    getById : function(id) {
        return (this.snapshot || this.data).findBy(function(record) {
            return record.getId() === id;
        });
    },

    
    indexOf : function(record) {
        return this.data.indexOf(record);
    },

    
    indexOfId : function(id) {
        return this.data.indexOfKey(id);
    },

    removeAll: function(silent) {
        var items = [];
        this.each(function(rec){
            items.push(rec);
        });
        this.clearData();
        if(this.snapshot){
            this.snapshot.clear();
        }
        
        
        
        if (silent !== true) {
            this.fireEvent('clear', this, items);
        }
    }
});


Ext.data.TreeStore = Ext.extend(Ext.data.AbstractStore, {
    
    clearOnLoad : true,

    
    nodeParam: 'node',

    
    defaultRootId: 'root',

    constructor: function(config) {
        config = config || {};
        var rootCfg = config.root || {};
        rootCfg.id = rootCfg.id || this.defaultRootId;

        
        var rootNode = new Ext.data.RecordNode(rootCfg);
        this.tree = new Ext.data.Tree(rootNode);
        this.tree.treeStore = this;

        Ext.data.TreeStore.superclass.constructor.call(this, config);
        

        if (config.root) {
            this.read({
                node: rootNode,
                doPreload: true
            });
        }
    },


    
    getRootNode: function() {
        return this.tree.getRootNode();
    },

    
    getNodeById: function(id) {
        return this.tree.getNodeById(id);
    },


    
    
    
    load: function(options) {
        options = options || {};
        options.params = options.params || {};

        var node = options.node || this.tree.getRootNode(),
            records,
            record,
            reader = this.proxy.reader,
            root;

        if (this.clearOnLoad) {
            while (node.firstChild){
                node.removeChild(node.firstChild);
            }
        }

        if (!options.doPreload) {
            Ext.applyIf(options, {
                node: node
            });
            record = node.getRecord();
            options.params[this.nodeParam] = record ? record.getId() : 'root';

            return Ext.data.TreeStore.superclass.load.call(this, options);
        } else {
            root = reader.getRoot(node.isRoot ? node.attributes : node.getRecord().raw);
            records = reader.extractData(root, true);
            this.fillNode(node, records);
            return true;
        }
    },

    
    
    fillNode: function(node, records) {
        node.loaded = true;
        var ln = records.length,
            recordNode,
            i = 0,
            raw,
            subStore = node.subStore;

        for (; i < ln; i++) {
            raw = records[i].raw;
            records[i].data.leaf = raw.leaf;
            recordNode = new Ext.data.RecordNode({
                id: records[i].getId(),
                leaf: raw.leaf,
                record: records[i],
                expanded: raw.expanded
            });
            node.appendChild(recordNode);
            if (records[i].doPreload) {
                this.load({
                    node: recordNode,
                    doPreload: true
                });
            }
        }

        
        if (subStore) {
            if (this.clearOnLoad) {
                subStore.removeAll();
            }
            subStore.add.apply(subStore, records);
        }
    },


    onProxyLoad: function(operation) {
        var records = operation.getRecords();

        this.fillNode(operation.node, records);

        this.fireEvent('read', this, operation.node, records, operation.wasSuccessful());
        
        var callback = operation.callback;
        if (typeof callback == 'function') {
            callback.call(operation.scope || this, records, operation, operation.wasSuccessful());
        }
    },


    
    getSubStore: function(node) {
        
        if (node && node.node) {
            node = node.node;
        }
        return node.getSubStore();
    },


    removeAll: function() {
        var rootNode = this.getRootNode();
        rootNode.destroy();
    }
});


Ext.StoreMgr = Ext.apply(new Ext.util.MixedCollection(), {
    

    
    register : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.add(s);
        }
    },

    
    unregister : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.remove(this.lookup(s));
        }
    },

    
    lookup : function(id) {
        if (Ext.isArray(id)) {
            var fields = ['field1'], expand = !Ext.isArray(id[0]);
            if(!expand){
                for(var i = 2, len = id[0].length; i <= len; ++i){
                    fields.push('field' + i);
                }
            }
            return new Ext.data.ArrayStore({
                data  : id,
                fields: fields,
                expandData : expand,
                autoDestroy: true,
                autoCreated: true
            });
        }
        return Ext.isObject(id) ? (id.events ? id : Ext.create(id, 'store')) : this.get(id);
    },

    
    getKey : function(o) {
         return o.storeId;
    }
});


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 = new Ext.data.Store(config);
    }

    return Ext.StoreMgr.register(store);
};


Ext.getStore = function(name) {
    return Ext.StoreMgr.lookup(name);
};


Ext.data.WriterMgr = new Ext.AbstractManager({
    
});

Ext.data.Tree = Ext.extend(Ext.util.Observable, {
    
    constructor: function(root) {
        this.nodeHash = {};
        
        
        this.root = null;
        
        if (root) {
            this.setRootNode(root);
        }
        
        this.addEvents(
            
            "append",
            
            
            "remove",
            
            
            "move",
            
            
            "insert",
            
            
            "beforeappend",
            
            
            "beforeremove",
            
            
            "beforemove",
            
            
            "beforeinsert"
        );
        
        Ext.data.Tree.superclass.constructor.call(this);        
    },
    
    
    pathSeparator: "/",

    
    proxyNodeEvent : function(){
        return this.fireEvent.apply(this, arguments);
    },

    
    getRootNode : function() {
        return this.root;
    },

    
    setRootNode : function(node) {
        this.root = node;
        node.ownerTree = this;
        node.isRoot = true;
        this.registerNode(node);
        return node;
    },

    
    getNodeById : function(id) {
        return this.nodeHash[id];
    },

    
    registerNode : function(node) {
        this.nodeHash[node.id] = node;
    },

    
    unregisterNode : function(node) {
        delete this.nodeHash[node.id];
    },

    toString : function() {
        return "[Tree"+(this.id?" "+this.id:"")+"]";
    }
});


Ext.data.Node = Ext.extend(Ext.util.Observable, {

    constructor: function(attributes) {
        
        this.attributes = attributes || {};

        this.leaf = !!this.attributes.leaf;

        
        this.id = this.attributes.id;

        if (!this.id) {
            this.id = Ext.id(null, "xnode-");
            this.attributes.id = this.id;
        }
        
        this.childNodes = [];

        
        this.parentNode = null;

        
        this.firstChild = null;

        
        this.lastChild = null;

        
        this.previousSibling = null;

        
        this.nextSibling = null;

        this.addEvents({
            
            "append" : true,

            
            "remove" : true,

            
            "move" : true,

            
            "insert" : true,

            
            "beforeappend" : true,

            
            "beforeremove" : true,

            
            "beforemove" : true,

             
            "beforeinsert" : true
        });

        this.listeners = this.attributes.listeners;
        Ext.data.Node.superclass.constructor.call(this);
    },

    
    fireEvent : function(evtName) {
        
        if (Ext.data.Node.superclass.fireEvent.apply(this, arguments) === false) {
            return false;
        }

        
        var ot = this.getOwnerTree();
        if (ot) {
            if (ot.proxyNodeEvent.apply(ot, arguments) === false) {
                return false;
            }
        }
        return true;
    },
    
    
    isLeaf : function() {
        return this.leaf === true;
    },

    
    setFirstChild : function(node) {
        this.firstChild = node;
    },

    
    setLastChild : function(node) {
        this.lastChild = node;
    },


    
    isLast : function() {
       return (!this.parentNode ? true : this.parentNode.lastChild == this);
    },

    
    isFirst : function() {
       return (!this.parentNode ? true : this.parentNode.firstChild == this);
    },

    
    hasChildNodes : function() {
        return !this.isLeaf() && this.childNodes.length > 0;
    },

    
    isExpandable : function() {
        return this.attributes.expandable || this.hasChildNodes();
    },

    
    appendChild : function(node) {
        var multi = false,
            i, len;

        if (Ext.isArray(node)) {
            multi = node;
        } else if (arguments.length > 1) {
            multi = arguments;
        }

        
        if (multi) {
            len = multi.length;

            for (i = 0; i < len; i++) {
                this.appendChild(multi[i]);
            }
        } else {
            if (this.fireEvent("beforeappend", this.ownerTree, this, node) === false) {
                return false;
            }

            var index = this.childNodes.length;
            var oldParent = node.parentNode;

            
            if (oldParent) {
                if (node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false) {
                    return false;
                }
                oldParent.removeChild(node);
            }

            index = this.childNodes.length;
            if (index === 0) {
                this.setFirstChild(node);
            }

            this.childNodes.push(node);
            node.parentNode = this;
            var ps = this.childNodes[index-1];
            if (ps) {
                node.previousSibling = ps;
                ps.nextSibling = node;
            } else {
                node.previousSibling = null;
            }

            node.nextSibling = null;
            this.setLastChild(node);
            node.setOwnerTree(this.getOwnerTree());
            this.fireEvent("append", this.ownerTree, this, node, index);

            if (oldParent) {
                node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
            }

            return node;
        }
    },

    
    removeChild : function(node, destroy) {
        var index = this.indexOf(node);

        if (index == -1) {
            return false;
        }
        if (this.fireEvent("beforeremove", this.ownerTree, this, node) === false) {
            return false;
        }

        
        this.childNodes.splice(index, 1);

        
        if (node.previousSibling) {
            node.previousSibling.nextSibling = node.nextSibling;
        }
        if (node.nextSibling) {
            node.nextSibling.previousSibling = node.previousSibling;
        }

        
        if (this.firstChild == node) {
            this.setFirstChild(node.nextSibling);
        }
        if (this.lastChild == node) {
            this.setLastChild(node.previousSibling);
        }

        this.fireEvent("remove", this.ownerTree, this, node);
        if (destroy) {
            node.destroy(true);
        } else {
            node.clear();
        }

        return node;
    },

    
    clear : function(destroy) {
        
        this.setOwnerTree(null, destroy);
        this.parentNode = this.previousSibling = this.nextSibling = null;
        if (destroy) {
            this.firstChild = this.lastChild = null;
        }
    },

    
    destroy : function(silent) {
        
        if (silent === true) {
            this.clearListeners();
            this.clear(true);
            Ext.each(this.childNodes, function(n) {
                n.destroy(true);
            });
            this.childNodes = null;
        } else {
            this.remove(true);
        }
    },

    
    insertBefore : function(node, refNode) {
        if (!refNode) { 
            return this.appendChild(node);
        }
        
        if (node == refNode) {
            return false;
        }

        if (this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false) {
            return false;
        }

        var index     = this.indexOf(refNode),
            oldParent = node.parentNode,
            refIndex  = index;

        
        if (oldParent == this && this.indexOf(node) < index) {
            refIndex--;
        }

        
        if (oldParent) {
            if (node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false) {
                return false;
            }
            oldParent.removeChild(node);
        }

        if (refIndex === 0) {
            this.setFirstChild(node);
        }

        this.childNodes.splice(refIndex, 0, node);
        node.parentNode = this;

        var ps = this.childNodes[refIndex-1];

        if (ps) {
            node.previousSibling = ps;
            ps.nextSibling = node;
        } else {
            node.previousSibling = null;
        }

        node.nextSibling = refNode;
        refNode.previousSibling = node;
        node.setOwnerTree(this.getOwnerTree());
        this.fireEvent("insert", this.ownerTree, this, node, refNode);

        if (oldParent) {
            node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
        }
        return node;
    },

    
    remove : function(destroy) {
        var parentNode = this.parentNode;

        if (parentNode) {
            parentNode.removeChild(this, destroy);
        }
        return this;
    },

    
    removeAll : function(destroy) {
        var cn = this.childNodes,
            n;

        while ((n = cn[0])) {
            this.removeChild(n, destroy);
        }
        return this;
    },

    
    getChildAt : function(index) {
        return this.childNodes[index];
    },

    
    replaceChild : function(newChild, oldChild) {
        var s = oldChild ? oldChild.nextSibling : null;

        this.removeChild(oldChild);
        this.insertBefore(newChild, s);
        return oldChild;
    },

    
    indexOf : function(child) {
        return this.childNodes.indexOf(child);
    },

    
    getOwnerTree : function() {
        
        if (!this.ownerTree) {
            var p = this;

            while (p) {
                if (p.ownerTree) {
                    this.ownerTree = p.ownerTree;
                    break;
                }
                p = p.parentNode;
            }
        }

        return this.ownerTree;
    },

    
    getDepth : function() {
        var depth = 0,
            p     = this;

        while (p.parentNode) {
            ++depth;
            p = p.parentNode;
        }

        return depth;
    },

    
    setOwnerTree : function(tree, destroy) {
        
        if (tree != this.ownerTree) {
            if (this.ownerTree) {
                this.ownerTree.unregisterNode(this);
            }
            this.ownerTree = tree;

            
            if (destroy !== true) {
                Ext.each(this.childNodes, function(n) {
                    n.setOwnerTree(tree);
                });
            }
            if (tree) {
                tree.registerNode(this);
            }
        }
    },

    
    setId: function(id) {
        if (id !== this.id) {
            var t = this.ownerTree;
            if (t) {
                t.unregisterNode(this);
            }
            this.id = this.attributes.id = id;
            if (t) {
                t.registerNode(this);
            }
            this.onIdChange(id);
        }
    },

    
    onIdChange: Ext.emptyFn,

    
    getPath : function(attr) {
        attr = attr || "id";
        var p = this.parentNode,
            b = [this.attributes[attr]];

        while (p) {
            b.unshift(p.attributes[attr]);
            p = p.parentNode;
        }

        var sep = this.getOwnerTree().pathSeparator;
        return sep + b.join(sep);
    },

    
    bubble : function(fn, scope, args) {
        var p = this;
        while (p) {
            if (fn.apply(scope || p, args || [p]) === false) {
                break;
            }
            p = p.parentNode;
        }
    },
    

    
    cascadeBy : function(fn, scope, args) {
        if (fn.apply(scope || this, args || [this]) !== false) {
            var childNodes = this.childNodes,
                length     = childNodes.length,
                i;

            for (i = 0; i < length; i++) {
                childNodes[i].cascadeBy(fn, scope, args);
            }
        }
    },

    
    eachChild : function(fn, scope, args) {
        var childNodes = this.childNodes,
            length     = childNodes.length,
            i;

        for (i = 0; i < length; i++) {
            if (fn.apply(scope || this, args || [childNodes[i]]) === false) {
                break;
            }
        }
    },

    
    findChild : function(attribute, value, deep) {
        return this.findChildBy(function(){
            return this.attributes[attribute] == value;
        }, null, deep);
    },

    
    findChildBy : function(fn, scope, deep) {
        var cs = this.childNodes,
            len = cs.length,
            i = 0,
            n,
            res;

        for(; i < len; i++){
            n = cs[i];
            if(fn.call(scope || n, n) === true){
                return n;
            }else if (deep){
                res = n.findChildBy(fn, scope, deep);
                if(res != null){
                    return res;
                }
            }

        }

        return null;
    },

    
    sort : function(fn, scope) {
        var cs  = this.childNodes,
            len = cs.length,
            i, n;

        if (len > 0) {
            var sortFn = scope ? function(){return fn.apply(scope, arguments);} : fn;
            cs.sort(sortFn);
            for (i = 0; i < len; i++) {
                n = cs[i];
                n.previousSibling = cs[i-1];
                n.nextSibling = cs[i+1];

                if (i === 0){
                    this.setFirstChild(n);
                }
                if (i == len - 1) {
                    this.setLastChild(n);
                }
            }
        }
    },

    
    contains : function(node) {
        return node.isAncestor(this);
    },

    
    isAncestor : function(node) {
        var p = this.parentNode;
        while (p) {
            if (p == node) {
                return true;
            }
            p = p.parentNode;
        }
        return false;
    },

    toString : function() {
        return "[Node" + (this.id ? " " + this.id : "") + "]";
    }
});


Ext.data.RecordNode = Ext.extend(Ext.data.Node, {
    constructor: function(config) {
        config = config || {};
        if (config.record) {
            
            config.record.node = this;
        }
        Ext.data.RecordNode.superclass.constructor.call(this, config);
    },

    getChildRecords: function() {
        var cn = this.childNodes,
            ln = cn.length,
            i = 0,
            rs = [],
            r;

        for (; i < ln; i++) {
            r = cn[i].attributes.record;
            
            
            
            
            r.data.leaf = cn[i].leaf;
            rs.push(r);
        }
        return rs;
    },

    getRecord: function() {
        return this.attributes.record;
    },


    getSubStore: function() {

        
        if (this.isLeaf()) {
            throw "Attempted to get a substore of a leaf node.";
        }
        

        var treeStore = this.getOwnerTree().treeStore;
        if (!this.subStore) {
            this.subStore = new Ext.data.Store({
                model: treeStore.model
            });
            
            
            
            var children = this.getChildRecords();
            this.subStore.add.apply(this.subStore, children);
        }

        if (!this.loaded) {
            treeStore.load({
                node: this
            });
        }
        return this.subStore;
    },

    destroy : function(silent) {
        if (this.subStore) {
            this.subStore.destroyStore();
        }
        var attr = this.attributes;
        if (attr.record) {
            delete attr.record.node;
            delete attr.record;
        }

        return Ext.data.RecordNode.superclass.destroy.call(this, silent);
    }
});

Ext.data.Proxy = Ext.extend(Ext.util.Observable, {
    
    batchOrder: 'create,update,destroy',
    
    
    defaultReaderType: 'json',
    
    
    defaultWriterType: 'json',
    
    
    
    constructor: function(config) {
        config = config || {};
        
        if (config.model == undefined) {
            delete config.model;
        }

        Ext.data.Proxy.superclass.constructor.call(this, config);
        
        if (this.model != undefined && !(this.model instanceof Ext.data.Model)) {
            this.setModel(this.model);
        }
    },
    
    
    setModel: function(model, setOnStore) {
        this.model = Ext.ModelMgr.getModel(model);
        
        var reader = this.reader,
            writer = this.writer;
        
        this.setReader(reader);
        this.setWriter(writer);
        
        if (setOnStore && this.store) {
            this.store.setModel(this.model);
        }
    },
    
    
    getModel: function() {
        return this.model;
    },
    
    
    setReader: function(reader) {
        if (reader == undefined || typeof reader == 'string') {
            reader = {
                type: reader
            };
        }

        if (reader instanceof Ext.data.Reader) {
            reader.setModel(this.model);
        } else {
            Ext.applyIf(reader, {
                proxy: this,
                model: this.model,
                type : this.defaultReaderType
            });

            reader = Ext.data.ReaderMgr.create(reader);
        }
        
        this.reader = reader;
        
        return this.reader;
    },
    
    
    getReader: function() {
        return this.reader;
    },
    
    
    setWriter: function(writer) {
        if (writer == undefined || typeof writer == 'string') {
            writer = {
                type: writer
            };
        }

        if (!(writer instanceof Ext.data.Writer)) {
            Ext.applyIf(writer, {
                model: this.model,
                type : this.defaultWriterType
            });

            writer = Ext.data.WriterMgr.create(writer);
        }
        
        this.writer = writer;
        
        return this.writer;
    },
    
    
    getWriter: function() {
        return this.writer;
    },
    
    
    create: Ext.emptyFn,
    
    
    read: Ext.emptyFn,
    
    
    update: Ext.emptyFn,
    
    
    destroy: Ext.emptyFn,
    
    
    batch: function(operations, listeners) {
        var batch = new Ext.data.Batch({
            proxy: this,
            listeners: listeners || {}
        });
        
        Ext.each(this.batchOrder.split(','), function(action) {
            if (operations[action]) {
                batch.add(new Ext.data.Operation({
                    action : action, 
                    records: operations[action]
                }));
            }
        }, this);
        
        batch.start();
        
        return batch;
    }
});


Ext.data.DataProxy = Ext.data.Proxy;

Ext.data.ProxyMgr.registerType('proxy', Ext.data.Proxy);

Ext.data.ServerProxy = Ext.extend(Ext.data.Proxy, {
    
    
    
    
    
    
    
    pageParam: 'page',
    
    
    startParam: 'start',

    
    limitParam: 'limit',
    
    
    groupParam: 'group',
    
    
    sortParam: 'sort',
    
    
    filterParam: 'filter',
    
    
    noCache : true,
    
    
    cacheString: "_dc",
    
    
    timeout : 30000,
    
    
    constructor: function(config) {
        config = config || {};
        
        Ext.data.ServerProxy.superclass.constructor.call(this, config);
        
        
        this.extraParams = config.extraParams || {};
        
        
        this.nocache = this.noCache;
    },
    
    
    create: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    read: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    update: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    destroy: function() {
        return this.doRequest.apply(this, arguments);
    },
    
    
    buildRequest: function(operation) {
        var params = Ext.applyIf(operation.params || {}, this.extraParams || {});
        
        
        params = Ext.applyIf(params, this.getParams(params, operation));
        
        var request = new Ext.data.Request({
            params   : params,
            action   : operation.action,
            records  : operation.records,
            operation: operation
        });
        
        request.url = this.buildUrl(request);
        
        
        operation.request = request;
        
        return request;
    },
    
    
    encodeSorters: function(sorters) {
        var min = [],
            length = sorters.length,
            i;
        
        for (i = 0; i < length; i++) {
            min[i] = {
                property : sorters[i].property,
                direction: sorters[i].direction
            };
        }
        
        return Ext.encode(min);
    },
    
    
    encodeFilters: function(filters) {
        var min = [],
            length = filters.length,
            i;
        
        for (i = 0; i < length; i++) {
            min[i] = {
                property: filters[i].property,
                value   : filters[i].value
            };
        }
        
        return Ext.encode(min);
    },
    
    
    encodeGroupers: function(group) {
        return Ext.encode(group);
    },
    
    
    getParams: function(params, operation) {
        params = params || {};
        
        var group       = operation.group,
            sorters     = operation.sorters,
            filters     = operation.filters,
            page        = operation.page,
            start       = operation.start,
            limit       = operation.limit,
            
            pageParam   = this.pageParam,
            startParam  = this.startParam,
            limitParam  = this.limitParam,
            groupParam  = this.groupParam,
            sortParam   = this.sortParam,
            filterParam = this.filterParam;
        
        if (pageParam && page) {
            params[pageParam] = page;
        }
        
        if (startParam && start) {
            params[startParam] = start;
        }
        
        if (limitParam && limit) {
            params[limitParam] = limit;
        }
        
        if (groupParam && group && group.field) {
            params[groupParam] = this.encodeGroupers(group);
        }
        
        if (sortParam && sorters && sorters.length > 0) {
            params[sortParam] = this.encodeSorters(sorters);
        }
        
        if (filterParam && filters && filters.length > 0) {
            params[filterParam] = this.encodeFilters(filters);
        }
        
        return params;
    },
    
    
    buildUrl: function(request) {
        var url = request.url || this.url;
        
        if (!url) {
            throw new Error("You are using a ServerProxy but have not supplied it with a url.");
        }
        
        if (this.noCache) {
            url = Ext.urlAppend(url, Ext.util.Format.format("{0}={1}", this.cacheString, (new Date().getTime())));
        }
        
        return url;
    },
    
    
    doRequest: function(operation, callback, scope) {
        throw new Error("The doRequest function has not been implemented on your Ext.data.ServerProxy subclass. See src/data/ServerProxy.js for details");
    },
    
    
    afterRequest: Ext.emptyFn,
    
    onDestroy: function() {
        Ext.destroy(this.reader, this.writer);
        
        Ext.data.ServerProxy.superclass.destroy.apply(this, arguments);
    }
});

Ext.data.AjaxProxy = Ext.extend(Ext.data.ServerProxy, {
    
    actionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'POST',
        destroy: 'POST'
    },
    
    
    
    constructor: function() {
        this.addEvents(
            
            'exception'
        );
        
        Ext.data.AjaxProxy.superclass.constructor.apply(this, arguments);    
    },
    
    
    doRequest: function(operation, callback, scope) {
        var writer  = this.getWriter(),
            request = this.buildRequest(operation, callback, scope);
            
        if (operation.allowWrite()) {
            request = writer.write(request);
        }
        
        Ext.apply(request, {
            headers       : this.headers,
            timeout       : this.timeout,
            scope         : this,
            callback      : this.createRequestCallback(request, operation, callback, scope),
            method        : this.getMethod(request),
            disableCaching: false 
        });
        
        Ext.Ajax.request(request);
        
        return request;
    },
    
    
    getMethod: function(request) {
        return this.actionMethods[request.action];
    },
    
    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;
        
        return function(options, success, response) {
            if (success === true) {
                var reader  = me.getReader(),
                    result  = reader.read(response),
                    records = result.records,
                    length  = records.length,
                    mc      = new Ext.util.MixedCollection(true, function(r) {return r.getId();}),
                    record, i;
                
                mc.addAll(operation.records);
                for (i = 0; i < length; i++) {
                    record = mc.get(records[i].getId());
                    
                    if (record) {
                        record.set(record.data);
                    }
                }

                
                Ext.apply(operation, {
                    response : response,
                    resultSet: result
                });
                
                operation.setCompleted();
                operation.setSuccessful();
            } else {
                me.fireEvent('exception', this, response, operation);
                
                
                operation.setException();                
            }
            
            
            if (typeof callback == 'function') {
                callback.call(scope || me, operation);
            }
            
            me.afterRequest(request, true);
        };
    }
});

Ext.data.ProxyMgr.registerType('ajax', Ext.data.AjaxProxy);


Ext.data.HttpProxy = Ext.data.AjaxProxy;

Ext.data.RestProxy = Ext.extend(Ext.data.AjaxProxy, {
    
    appendId: true,
    
    
    
    
    actionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'PUT',
        destroy: 'DELETE'
    },
    
    api: {
        create : 'create',
        read   : 'read',
        update : 'update',
        destroy: 'destroy'
    },
    
    
    buildUrl: function(request) {
        var records = request.operation.records || [],
            record  = records[0],
            format  = this.format,
            url     = request.url || this.url;
        
        if (this.appendId && record) {
            if (!url.match(/\/$/)) {
                url += '/';
            }
            
            url += record.getId();
        }
        
        if (format) {
            if (!url.match(/\.$/)) {
                url += '.';
            }
            
            url += format;
        }
        
        request.url = url;
        
        return Ext.data.RestProxy.superclass.buildUrl.apply(this, arguments);
    }
});

Ext.data.ProxyMgr.registerType('rest', Ext.data.RestProxy);
Ext.apply(Ext, {
    
    getHead : function() {
        var head;
        
        return function() {
            if (head == undefined) {
                head = Ext.get(document.getElementsByTagName("head")[0]);
            }
            
            return head;
        };
    }()
});


Ext.data.ScriptTagProxy = Ext.extend(Ext.data.ServerProxy, {
    defaultWriterType: 'base',
    
    
    callbackParam : "callback",
    
    
    scriptIdPrefix: 'stcScript',
    
    
    callbackPrefix: 'stcCallback',
    
    
    recordParam: 'records',
    
    
    lastRequest: undefined,
    
    
    autoAppendParams: true,
    
    constructor: function(){
        this.addEvents(
            
            'exception'
        );
        
        Ext.data.ScriptTagProxy.superclass.constructor.apply(this, arguments);    
    },

    
    doRequest: function(operation, callback, scope) {
        
        var format     = Ext.util.Format.format,
            transId    = ++Ext.data.ScriptTagProxy.TRANS_ID,
            scriptId   = format("{0}{1}", this.scriptIdPrefix, transId),
            stCallback = format("{0}{1}", this.callbackPrefix, transId);
        
        var writer  = this.getWriter(),
            request = this.buildRequest(operation),
            
            url     = Ext.urlAppend(request.url, format("{0}={1}", this.callbackParam, stCallback));
            
        if (operation.allowWrite()) {
            request = writer.write(request);
        }
        
        
        Ext.apply(request, {
            url       : url,
            transId   : transId,
            scriptId  : scriptId,
            stCallback: stCallback
        });
        
        
        request.timeoutId = Ext.defer(this.createTimeoutHandler, this.timeout, this, [request, operation]);
        
        
        window[stCallback] = this.createRequestCallback(request, operation, callback, scope);
        
        
        var script = document.createElement("script");
        script.setAttribute("src", url);
        script.setAttribute("async", true);
        script.setAttribute("type", "text/javascript");
        script.setAttribute("id", scriptId);
        
        Ext.getHead().appendChild(script);
        operation.setStarted();
        
        this.lastRequest = request;
        
        return request;
    },
    
    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;
        
        return function(response) {
            var reader = me.getReader(),
                result = reader.read(response);
            
            
            Ext.apply(operation, {
                response : response,
                resultSet: result
            });
            
            operation.setCompleted();
            operation.setSuccessful();
            
            
            if (typeof callback == 'function') {
                callback.call(scope || me, operation);
            }
            
            me.afterRequest(request, true);
        };
    },
    
    
    afterRequest: function() {
        var cleanup = function(functionName) {
            return function() {
                window[functionName] = undefined;
                
                try {
                    delete window[functionName];
                } catch(e) {}
            };
        };
        
        return function(request, isLoaded) {
            Ext.get(request.scriptId).remove();
            clearTimeout(request.timeoutId);
            
            var callbackName = request.stCallback;
            
            if (isLoaded) {
                cleanup(callbackName)();
                this.lastRequest.completed = true;
            } else {
                
                window[callbackName] = cleanup(callbackName);
            }
        };
    }(),
    
    
    buildUrl: function(request) {
        var url     = Ext.data.ScriptTagProxy.superclass.buildUrl.call(this, request),  
            params  = Ext.apply({}, request.params),
            filters = params.filters,
            filter, i;
            
        delete params.filters;
        
        if (this.autoAppendParams) {
            url = Ext.urlAppend(url, Ext.urlEncode(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);
                }
            }
        }
        
        
        var records = request.records;
        
        if (Ext.isArray(records) && records.length > 0) {
            url = Ext.urlAppend(url, Ext.util.Format.format("{0}={1}", this.recordParam, this.encodeRecords(records)));
        }
        
        return url;
    },
    
    
    destroy: function() {
        this.abort();
        
        Ext.data.ScriptTagProxy.superclass.destroy.apply(this, arguments);
    },
        
    
    isLoading : function(){
        var lastRequest = this.lastRequest;
        
        return (lastRequest != undefined && !lastRequest.completed);
    },
    
    
    abort: function() {
        if (this.isLoading()) {
            this.afterRequest(this.lastRequest);
        }
    },
        
    
    encodeRecords: function(records) {
        var encoded = "";
        
        for (var i = 0, length = records.length; i < length; i++) {
            encoded += Ext.urlEncode(records[i].data);
        }
        
        return encoded;
    },
    
    
    createTimeoutHandler: function(request, operation) {
        this.afterRequest(request, false);

        this.fireEvent('exception', this, request, operation);
        
        if (typeof request.callback == 'function') {
            request.callback.call(request.scope || window, null, request.options, false);
        }        
    }
});

Ext.data.ScriptTagProxy.TRANS_ID = 1000;

Ext.data.ProxyMgr.registerType('scripttag', Ext.data.ScriptTagProxy);

Ext.data.ClientProxy = Ext.extend(Ext.data.Proxy, {
    
    clear: function() {
        throw new Error("The Ext.data.ClientProxy subclass that you are using has not defined a 'clear' function. See src/data/ClientProxy.js for details.");
    }
});

Ext.data.MemoryProxy = Ext.extend(Ext.data.ClientProxy, {
    
    
    constructor: function(config) {
        Ext.data.MemoryProxy.superclass.constructor.call(this, config);
        
        
        this.setReader(this.reader);
    },
    
    
    read: function(operation, callback, scope) {
        var reader = this.getReader(),
            result = reader.read(this.data);

        Ext.apply(operation, {
            resultSet: result
        });
        
        operation.setCompleted();
        
        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },
    
    clear: Ext.emptyFn
});

Ext.data.ProxyMgr.registerType('memory', Ext.data.MemoryProxy);

Ext.data.WebStorageProxy = Ext.extend(Ext.data.ClientProxy, {
    
    id: undefined,

    
    constructor: function(config) {
        Ext.data.WebStorageProxy.superclass.constructor.call(this, config);
        
        
        this.cache = {};

        if (this.getStorageObject() == undefined) {
            throw "Local Storage is not supported in this browser, please use another type of data proxy";
        }

        
        this.id = this.id || (this.store ? this.store.storeId : undefined);

        if (this.id == undefined) {
            throw "No unique id was provided to the local storage proxy. See Ext.data.LocalStorageProxy documentation for details";
        }

        this.initialize();
    },

    
    create: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),
            id, record, i;
        
        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];

            if (record.phantom) {
                record.phantom = false;
                id = this.getNextId();
            } else {
                id = record.getId();
            }

            this.setRecord(record, id);
            ids.push(id);
        }

        this.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    read: function(operation, callback, scope) {
        

        var records = [],
            ids     = this.getIds(),
            length  = ids.length,
            i, recordData, record;
        
        
        if (operation.id) {
            record = this.getRecord(operation.id);
            
            if (record) {
                records.push(record);
                operation.setSuccessful();
            }
        } else {
            for (i = 0; i < length; i++) {
                records.push(this.getRecord(ids[i]));
            }
            operation.setSuccessful();
        }
        
        operation.setCompleted();

        operation.resultSet = new Ext.data.ResultSet({
            records: records,
            total  : records.length,
            loaded : true
        });

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    update: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),
            record, id, i;

        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];
            this.setRecord(record);
            
            
            
            id = record.getId();
            if (id !== undefined && ids.indexOf(id) == -1) {
                ids.push(id);
            }
        }
        this.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    destroy: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),

            
            newIds  = [].concat(ids),
            i;

        for (i = 0; i < length; i++) {
            newIds.remove(records[i].getId());
            this.removeRecord(records[i], false);
        }

        this.setIds(newIds);

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    getRecord: function(id) {
        if (this.cache[id] == undefined) {
            var rawData = Ext.decode(this.getStorageObject().getItem(this.getRecordKey(id))),
                data    = {},
                Model   = this.model,
                fields  = Model.prototype.fields.items,
                length  = fields.length,
                i, field, name, record;

            for (i = 0; i < length; i++) {
                field = fields[i];
                name  = field.name;

                if (typeof field.decode == 'function') {
                    data[name] = field.decode(rawData[name]);
                } else {
                    data[name] = rawData[name];
                }
            }

            record = new Model(data, id);
            record.phantom = false;

            this.cache[id] = record;
        }
        
        return this.cache[id];
    },

    
    setRecord: function(record, id) {
        if (id) {
            record.setId(id);
        } else {
            id = record.getId();
        }

        var rawData = record.data,
            data    = {},
            model   = this.model,
            fields  = model.prototype.fields.items,
            length  = fields.length,
            i, field, name;

        for (i = 0; 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];
            }
        }

        var obj = this.getStorageObject(),
            key = this.getRecordKey(id);
        
        
        this.cache[id] = record;
        
        
        obj.removeItem(key);
        obj.setItem(key, Ext.encode(data));
    },

    
    removeRecord: function(id, updateIds) {
        if (id instanceof Ext.data.Model) {
            id = id.getId();
        }

        if (updateIds !== false) {
            var ids = this.getIds();
            ids.remove(id);
            this.setIds(ids);
        }

        this.getStorageObject().removeItem(this.getRecordKey(id));
    },

    
    getRecordKey: function(id) {
        if (id instanceof Ext.data.Model) {
            id = id.getId();
        }

        return Ext.util.Format.format("{0}-{1}", this.id, id);
    },

    
    getRecordCounterKey: function() {
        return Ext.util.Format.format("{0}-counter", this.id);
    },

    
    getIds: function() {
        var ids    = (this.getStorageObject().getItem(this.id) || "").split(","),
            length = ids.length,
            i;

        if (length == 1 && ids[0] == "") {
            ids = [];
        } else {
            for (i = 0; i < length; i++) {
                ids[i] = parseInt(ids[i], 10);
            }
        }

        return ids;
    },

    
    setIds: function(ids) {
        var obj = this.getStorageObject(),
            str = ids.join(",");
        
        obj.removeItem(this.id);
        
        if (!Ext.isEmpty(str)) {
            obj.setItem(this.id, str);
        }
    },

    
    getNextId: function() {
        var obj  = this.getStorageObject(),
            key  = this.getRecordCounterKey(),
            last = obj[key],
            ids, id;
        
        if (last == undefined) {
            ids = this.getIds();
            last = ids[ids.length - 1] || 0;
        }
        
        id = parseInt(last, 10) + 1;
        obj.setItem(key, id);
        
        return id;
    },

    
    initialize: function() {
        var storageObject = this.getStorageObject();
        storageObject.setItem(this.id, storageObject.getItem(this.id) || "");
    },

    
    clear: function() {
        var obj = this.getStorageObject(),
            ids = this.getIds(),
            len = ids.length,
            i;

        
        for (i = 0; i < len; i++) {
            this.removeRecord(ids[i]);
        }

        
        obj.removeItem(this.getRecordCounterKey());
        obj.removeItem(this.id);
    },

    
    getStorageObject: function() {
        throw new Error("The getStorageObject function has not been defined in your Ext.data.WebStorageProxy subclass");
    }
});

Ext.data.LocalStorageProxy = Ext.extend(Ext.data.WebStorageProxy, {
    
    getStorageObject: function() {
        return window.localStorage;
    }
});

Ext.data.ProxyMgr.registerType('localstorage', Ext.data.LocalStorageProxy);

Ext.data.SessionStorageProxy = Ext.extend(Ext.data.WebStorageProxy, {
    
    getStorageObject: function() {
        return window.sessionStorage;
    }
});

Ext.data.ProxyMgr.registerType('sessionstorage', Ext.data.SessionStorageProxy);

Ext.data.Reader = Ext.extend(Object, {
    
    idProperty: 'id',

    
    totalProperty: 'total',

    
    successProperty: 'success',

    
    root: '',
    
    
    implicitIncludes: true,
    
    
    nullResultSet: new Ext.data.ResultSet({
        total  : 0,
        count  : 0,
        records: [],
        success: true
    }),

    constructor: function(config) {
        Ext.apply(this, config || {});

        this.model = Ext.ModelMgr.getModel(config.model);
        if (this.model) {
            this.buildExtractors();
        }
    },

    
    setModel: function(model, setOnProxy) {
        this.model = Ext.ModelMgr.getModel(model);
        this.buildExtractors(true);
        
        if (setOnProxy && this.proxy) {
            this.proxy.setModel(this.model, true);
        }
    },

    
    read: function(response) {
        var data = response;
        
        if (response && response.responseText) {
            data = this.getResponseData(response);
        }
        
        if (data) {
            return this.readRecords(data);
        } else {
            return this.nullResultSet;
        }
    },

    
    readRecords: function(data) {
        
        this.rawData = data;

        data = this.getData(data);

        var root    = this.getRoot(data),
            total   = root.length,
            success = true,
            value, records, recordCount;

        if (this.totalProperty) {
            value = parseInt(this.getTotal(data), 10);
            if (!isNaN(value)) {
                total = value;
            }
        }

        if (this.successProperty) {
            value = this.getSuccess(data);
            if (value === false || value === 'false') {
                success = false;
            }
        }

        records = this.extractData(root, true);
        recordCount = records.length;

        return new Ext.data.ResultSet({
            total  : total || recordCount,
            count  : recordCount,
            records: records,
            success: success
        });
    },

    
    extractData : function(root, returnRecords) {
        var values  = [],
            records = [],
            Model   = this.model,
            length  = root.length,
            idProp  = this.idProperty,
            node, id, record, i;

        for (i = 0; i < length; i++) {
            node   = root[i];
            values = this.extractValues(node);
            id     = this.getId(node);

            if (returnRecords === true) {
                record = new Model(values, id);
                record.raw = node;
                records.push(record);
                
                if (this.implicitIncludes) {
                    this.readAssociated(record, node);
                }
            } else {
                values[idProp] = id;
                records.push(values);
            }
        }

        return records;
    },
    
    
    readAssociated: function(record, data) {
        var associations = record.associations.items,
            length       = associations.length,
            association, associationName, associatedModel, associationData, inverseAssociation, proxy, reader, store, i;
        
        for (i = 0; i < length; i++) {
            association     = associations[i];
            associationName = association.name;
            associationData = this.getAssociatedDataRoot(data, association.associationKey || associationName);
            associatedModel = association.associatedModel;
            
            if (associationData) {
                proxy = associatedModel.proxy;
                
                
                if (proxy) {
                    reader = proxy.getReader();
                } else {
                    reader = new this.constructor({
                        model: association.associatedName
                    });
                }
                
                if (association.type == 'hasMany') {
                    store = record[associationName]();
                    
                    store.add.apply(store, reader.read(associationData).records);
                    
                    
                    
                    inverseAssociation = associatedModel.prototype.associations.findBy(function(assoc) {
                        return assoc.type == 'belongsTo' && assoc.associatedName == record.constructor.modelName;
                    });
                    
                    
                    if (inverseAssociation) {
                        store.data.each(function(associatedRecord) {
                            associatedRecord[inverseAssociation.instanceName] = record;
                        });                        
                    }

                } else if (association.type == 'belongsTo') {
                    record[association.instanceName] = reader.read([associationData]).records[0];
                }
            }
        }
    },
    
    
    getAssociatedDataRoot: function(data, associationName) {
        return data[associationName];
    },

    
    extractValues: function(data) {
        var fields = this.model.prototype.fields.items,
            length = fields.length,
            output = {},
            field, value, i;

        for (i = 0; i < length; i++) {
            field = fields[i];
            value = this.extractorFunctions[i](data) || field.defaultValue;

            output[field.name] = value;
        }

        return output;
    },

    
    getData: function(data) {
        return data;
    },

    
    getRoot: function(data) {
        return data;
    },

    
    getResponseData: function(response) {
        throw new Error("getResponseData must be implemented in the Ext.data.Reader subclass");
    },

    
    onMetaChange : function(meta) {
        var fields = meta.fields,
            newModel;
        
        Ext.apply(this, meta);
        
        if (fields) {
            newModel = Ext.regModel("JsonReader-Model" + Ext.id(), {fields: fields});
            this.setModel(newModel, true);
        } else {
            this.buildExtractors(true);
        }
    },

    
    buildExtractors: function(force) {
        if (force === true) {
            delete this.extractorFunctions;
        }
        
        if (this.extractorFunctions) {
            return;
        }

        var idProp      = this.id || this.idProperty,
            totalProp   = this.totalProperty,
            successProp = this.successProperty,
            messageProp = this.messageProperty;

        
        if (totalProp) {
            this.getTotal = this.createAccessor(totalProp);
        }

        if (successProp) {
            this.getSuccess = this.createAccessor(successProp);
        }

        if (messageProp) {
            this.getMessage = this.createAccessor(messageProp);
        }

        if (idProp) {
            var accessor = this.createAccessor(idProp);

            this.getId = function(record) {
                var id = accessor(record);

                return (id == undefined || id == '') ? null : id;
            };
        } else {
            this.getId = function() {
                return null;
            };
        }
        this.buildFieldExtractors();
    },

    
    buildFieldExtractors: function() {
        
        var fields = this.model.prototype.fields.items,
            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(this.createAccessor(map));
        }

        this.extractorFunctions = extractorFunctions;
    }
});

Ext.data.Writer = Ext.extend(Object, {

    constructor: function(config) {
        Ext.apply(this, config);
    },

    
    write: function(request) {
        var operation = request.operation,
            records   = operation.records || [],
            ln        = records.length,
            i         = 0,
            data      = [];

        for (; i < ln; i++) {
            data.push(this.getRecordData(records[i]));
        }
        return this.writeRecords(request, data);
    },

    
    getRecordData: function(record) {
        return record.data;
    }
});

Ext.data.WriterMgr.registerType('base', Ext.data.Writer);


Ext.data.JsonWriter = Ext.extend(Ext.data.Writer, {
    
    root: 'records',
    
    
    encode: false,
    
    
    writeRecords: function(request, data) {
        if (this.encode === true) {
            data = Ext.encode(data);
        }
        
        request.jsonData = request.jsonData || {};
        request.jsonData[this.root] = data;
        
        return request;
    }
});

Ext.data.WriterMgr.registerType('json', Ext.data.JsonWriter);


Ext.data.JsonReader = Ext.extend(Ext.data.Reader, {
    root: '',
    
    
    
    
    readRecords: function(data) {
        
        if (data.metaData) {
            this.onMetaChange(data.metaData);
        }

        
        this.jsonData = data;

        return Ext.data.JsonReader.superclass.readRecords.call(this, data);
    },

    
    getResponseData: function(response) {
        try {
            var data = Ext.decode(response.responseText);
        }
        catch (ex) {
            throw 'Ext.data.JsonReader.getResponseData: Unable to parse JSON returned by Server.';
        }

        if (!data) {
            throw 'Ext.data.JsonReader.getResponseData: JSON object not found';
        }

        return data;
    },

    
    buildExtractors : function() {
        Ext.data.JsonReader.superclass.buildExtractors.apply(this, arguments);

        if (this.root) {
            this.getRoot = this.createAccessor(this.root);
        } else {
            this.getRoot = function(root) {
                return root;
            };
        }
    },
    
    
    extractData: function(root, returnRecords) {
        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 Ext.data.JsonReader.superclass.extractData.call(this, data, returnRecords);
    },

    
    createAccessor: function() {
        var re = /[\[\.]/;

        return function(expr) {
            if (Ext.isEmpty(expr)) {
                return Ext.emptyFn;
            }
            if (Ext.isFunction(expr)) {
                return expr;
            }
            var i = String(expr).search(re);
            if (i >= 0) {
                return new Function('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
            }
            return function(obj) {
                return obj[expr];
            };
        };
    }()
});

Ext.data.ReaderMgr.registerType('json', Ext.data.JsonReader);
Ext.data.TreeReader = Ext.extend(Ext.data.JsonReader, {
    extractData : function(root, returnRecords) {
        var records = Ext.data.TreeReader.superclass.extractData.call(this, root, returnRecords),
            ln = records.length,
            i  = 0,
            record;

        if (returnRecords) {
            for (; i < ln; i++) {
                record = records[i];
                record.doPreload = !!this.getRoot(record.raw);
            }
        }
        return records;
    }
});
Ext.data.ReaderMgr.registerType('tree', Ext.data.TreeReader);

Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {

    
    buildExtractors: function() {
        Ext.data.ArrayReader.superclass.buildExtractors.apply(this, arguments);
        
        var fields = this.model.prototype.fields.items,
            length = fields.length,
            extractorFunctions = [],
            i;
        
        for (i = 0; i < length; i++) {
            extractorFunctions.push(function(index) {
                return function(data) {
                    return data[index];
                };
            }(fields[i].mapping || i));
        }
        
        this.extractorFunctions = extractorFunctions;
    }
});

Ext.data.ReaderMgr.registerType('array', Ext.data.ArrayReader);


Ext.data.ArrayStore = Ext.extend(Ext.data.Store, {
    
    constructor: function(config) {
        config = config || {};

        Ext.applyIf(config, {
            proxy: {
                type: 'memory',
                reader: 'array'
            }
        });

        Ext.data.ArrayStore.superclass.constructor.call(this, 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;
        }

        Ext.data.ArrayStore.superclass.loadData.call(this, data, append);
    }
});
Ext.reg('arraystore', Ext.data.ArrayStore);


Ext.data.SimpleStore = Ext.data.ArrayStore;
Ext.reg('simplestore', Ext.data.SimpleStore);

Ext.data.JsonStore = Ext.extend(Ext.data.Store, {
    
    constructor: function(config) {
        config = config || {};
              
        Ext.applyIf(config, {
            proxy: {
                type  : 'ajax',
                reader: 'json',
                writer: 'json'
            }
        });
        
        Ext.data.JsonStore.superclass.constructor.call(this, config);
    }
});

Ext.reg('jsonstore', Ext.data.JsonStore);

Ext.data.JsonPStore = Ext.extend(Ext.data.Store, {
    
    constructor: function(config) {
        Ext.data.JsonPStore.superclass.constructor.call(this, Ext.apply(config, {
            reader: new Ext.data.JsonReader(config),
            proxy : new Ext.data.ScriptTagProxy(config)
        }));
    }
});

Ext.reg('jsonpstore', Ext.data.JsonPStore);


Ext.data.XmlWriter = Ext.extend(Ext.data.Writer, {
    
    documentRoot: 'xmlData',

    
    header: '',

    
    record: 'record',

    
    writeRecords: function(request, data) {
        var tpl = this.buildTpl(request, data);

        request.xmlData = tpl.apply(data);

        return request;
    },

    buildTpl: function(request, data) {
        if (this.tpl) {
            return this.tpl;
        }

        var tpl = [],
            root = this.documentRoot,
            record = this.record,
            first,
            key;

        if (this.header) {
            tpl.push(this.header);
        }
        tpl.push('<', root, '>');
        if (data.length > 0) {
            tpl.push('<tpl for="."><', record, '>');
            first = data[0];
            for (key in first) {
                if (first.hasOwnProperty(key)) {
                    tpl.push('<', key, '>{', key, '}</', key, '>');
                }
            }
            tpl.push('</', record, '></tpl>');
        }
        tpl.push('</', root, '>');
        this.tpl = new Ext.XTemplate(tpl.join(''));
        return this.tpl;
    }
});

Ext.data.WriterMgr.registerType('xml', Ext.data.XmlWriter);

Ext.data.XmlReader = Ext.extend(Ext.data.Reader, {
    

    

    createAccessor: function() {
        var selectValue = function(key, root, defaultValue){
            var node = Ext.DomQuery.selectNode(key, root),
                val;
            if (node && node.firstChild) {
                val = node.firstChild.nodeValue;
            }
            return Ext.isEmpty(val) ? defaultValue : val;
        };

        return function(key) {
            var fn;

            if (key == this.totalProperty) {
                fn = function(root, defaultValue) {
                    var value = selectValue(key, root, defaultValue);
                    return parseFloat(value);
                };
            }

            else if (key == this.successProperty) {
                fn = function(root, defaultValue) {
                    var value = selectValue(key, root, true);
                    return (value !== false && value !== 'false');
                };
            }

            else {
                fn = function(root, defaultValue) {
                    return selectValue(key, root, defaultValue);
                };
            }

            return fn;
        };
    }(),

    
    getResponseData: function(response) {
        var xml = response.responseXML;

        if (!xml) {
            throw {message: 'Ext.data.XmlReader.read: XML data not found'};
        }

        return xml;
    },

    
    getData: function(data) {
        return data.documentElement || data;
    },

    
    getRoot: function(data) {
        var nodeName = data.nodeName,
            root     = this.root;
        
        if (!root || (nodeName && nodeName == root)) {
            return data;
        } else {
            return Ext.DomQuery.selectNode(root, data);
        }
    },


    


    

    

    

    
    constructor: function(config) {
        config = config || {};

        
        

        Ext.applyIf(config, {
            idProperty     : config.idPath || config.id,
            successProperty: config.success
        });
        
        Ext.data.XmlReader.superclass.constructor.call(this, config);
    },
    
    
    extractData: function(root, returnRecords) {
        var recordName = this.record;
        
        if (recordName != root.nodeName) {
            root = Ext.DomQuery.select(recordName, root);
        } else {
            root = [root];
        }
        
        return Ext.data.XmlReader.superclass.extractData.call(this, root, returnRecords);
    },
    
    
    getAssociatedDataRoot: function(data, associationName) {
        return Ext.DomQuery.select(associationName, data)[0];
    },

    
    readRecords: function(doc) {
        
        if (Ext.isArray(doc)) {
            doc = doc[0];
        }
        
        
        this.xmlData = doc;
        
        return Ext.data.XmlReader.superclass.readRecords.call(this, doc);
    }
});

Ext.data.ReaderMgr.registerType('xml', Ext.data.XmlReader);

Ext.data.XmlStore = Ext.extend(Ext.data.Store, {
    
    constructor: function(config){
        config = config || {};
        config = config || {};
              
        Ext.applyIf(config, {
            proxy: {
                type: 'ajax',
                reader: 'xml',
                writer: 'xml'
            }
        });
        Ext.data.XmlStore.superclass.constructor.call(this, config);
    }
});
Ext.reg('xmlstore', Ext.data.XmlStore);


Ext.History = new Ext.util.Observable({
    constructor: function() {
        Ext.History.superclass.constructor.call(this, config);
        
        this.addEvents(
            
            'change'
        );
    },
    
    
    init: function() {
        var me = this;
        
        me.setToken(window.location.hash);
        
        if (Ext.supports.History) {
            window.addEventListener('hashchange', this.onChange);
        } else {
            setInterval(function() {
                var newToken = me.cleanToken(window.location.hash),
                    oldToken = me.getToken();
                
                if (newToken != oldToken) {
                    me.onChange();
                }
            }, 50);
        }
    },
    
    
    onChange: function() {
        var me       = Ext.History,
            newToken = me.cleanToken(window.location.hash);
        
        if (me.token != newToken) {
            me.fireEvent('change', newToken);
        }
        
        me.setToken(newToken);
    },
    
    
    setToken: function(token) {
        return this.token = this.cleanToken(token);
    },
    
    
    cleanToken: function(token) {
        return token[0] == '#' ? token.substr(1) : token;
    },
    
    
    getToken: function() {
        return this.token;
    },
    
    
    add: function(token) {
        window.location.hash = this.setToken(token);
        
        if (!Ext.supports.History) {
            this.onChange();
        }
    }
});

Ext.ControllerManager = new Ext.AbstractManager({
    register: function(id, options) {
        options.id = id;
        
        Ext.applyIf(options, {
            application: Ext.ApplicationManager.currentApplication
        });
        
        var controller = new Ext.Controller(options);
        
        if (controller.init) {
            controller.init();
        }
        
        this.all.add(controller);
        
        return controller;
    }
});


Ext.regController = function() {
    return Ext.ControllerManager.register.apply(Ext.ControllerManager, arguments);
};

Ext.Controller = Ext.extend(Ext.util.Observable, {
    constructor: function(config) {
        this.addEvents(
            
            'instance-created',
            
            
            'instance-creation-failed',
            
            
            'instance-updated',
            
            
            'instance-update-failed',
            
            
            'instance-destroyed',
            
            
            'instance-destruction-failed'
        );
        
        Ext.Controller.superclass.constructor.call(this, config);
        
        Ext.apply(this, config || {});
        
        if (typeof this.model == 'string') {
            this.model = Ext.ModelMgr.getModel(this.model);
        }
    },
    
    index: function() {
        this.render('index', {
            listeners: {
                scope  : this,
                edit   : this.edit,
                build  : this.build,
                create : this.onCreateInstance,
                destroy: this.onDestroyInstance
            }
        });
    },
    
    
    edit: function(instance) {
        var view = this.render('edit', {
            listeners: this.getEditListeners()
        });
        
        view.loadModel(instance);
    },
    
    
    build: function() {
        this.render('build', {
            listeners: this.getBuildListeners()
        });
    },
    
    
    create: function(data, options) {
        options = options || {};
        
        var model     = this.getModel(),
            instance  = new model(data),
            successCb = options.success,
            failureCb = options.failure,
            scope     = options.scope || this;
        
        instance.save({
            scope  : this,
            success: function(instance) {
                if (typeof successCb == 'function') {
                    successCb.call(scope, instance);
                }
                
                this.fireEvent('instance-created', instance);
            },
            failure: function(instance, errors) {                
                if (typeof failureCb == 'function') {
                    failureCb.call(scope, instance, errors);
                }
                
                this.fireEvent('instance-creation-failed', instance, errors);
            }
        });
    },
    
    
    update: function(instance, updates, options) {
        options = options || {};
        
        var successCb = options.success,
            failureCb = options.failure,
            scope     = options.scope || this;
        
        if (Ext.isObject(updates)) {
            instance.set(updates);
        }
        
        instance.save({
            scope  : this,
            success: function(instance) {
                if (typeof successCb == 'function') {
                    successCb.call(scope, instance);
                }
                
                this.fireEvent('instance-updated', instance);
            },
            failure: function(instance, errors) {
                if (typeof failureCb == 'function') {
                    failureCb.call(scope, instance, errors);
                }
                
                this.fireEvent('instance-update-failed', instance, errors);
            }
        });
    },
    
    
    destroy: function(instance, options) {
        options = options || {};
        
        var successCb = options.success,
            failureCb = options.failure,
            scope     = options.scope || this;
        
        instance.destroy({
            scope  : this,
            success: function(instance) {
                if (typeof successCb == 'function') {
                    successCb.call(scope, instance);
                }
                
                this.fireEvent('instance-destroyed', instance);
            },
            failure: function(instance, errors) {
                if (typeof failureCb == 'function') {
                    failureCb.call(scope, instance, errors);
                }
                
                this.fireEvent('instance-destruction-failed', instance, errors);
            }
        });
    },
    
    
    getBuildListeners: function() {
        return {
            scope : this,
            save  : this.onCreateInstance,
            cancel: this.onCancelBuild
        };
    },
    
    
    getEditListeners: function() {
        return {
            scope : this,
            save  : this.onUpdateInstance,
            cancel: this.onCancelEdit
        };
    },
    
    
    onCancelEdit: function(view) {
        return this.closeView(view);
    },
    
    
    onCancelBuild: function(view) {
        return this.closeView(view);
    },
    
    
    onCreateInstance: function(view) {
        this.create(view.getValues(), {
            scope  : this,
            success: function(instance) {
                this.closeView(view);
            },
            failure: function(instance, errors) {
                console.log('fail');
            }
        });
    },
    
    
    onUpdateInstance: function(view) {
        this.update(view.getRecord(), view.getValues(), {
            scope  : this,
            success: function(instance) {
                this.closeView(view);
            },
            failure: function(instance, errors) {
                
            }
        });
    },
    
    
    onDestroyInstance: function(instance, view) {
        this.destroy(instance, {
            scope  : this,
            success: function(instance) {
                
            },
            failure: function(instance, errors) {
                
            }
        });
    },
    
    
    setRenderTarget: function(target) {
        
        Ext.Controller.renderTarget = target;
    },
    
    
    render: function(config, target) {
        var Controller  = Ext.Controller,
            application = this.application,
            profile     = application ? application.currentProfile : undefined,
            profileTarget, view;
        
        Ext.applyIf(config, {
            profile: profile
        });
        
        view = Ext.ComponentMgr.create(config);
        
        if (target !== false) {
            
            profileTarget = profile ? profile.getRenderTarget(config, application) : target;
            
            if (target == undefined) {
                target = profileTarget || (application ? application.defaultTarget : undefined);
            }

            if (typeof target == 'string') {
                target = Ext.getCmp(target);
            }

            if (target != undefined && target.add) {
                if (profile) {
                    profile.beforeLayout(view, target, application);
                }
                
                target.add(view);

                if (target.layout && target.layout.setActiveItem) {
                    target.layout.setActiveItem(view);
                }

                target.doComponentLayout();
                
                if (profile) {
                    profile.afterLayout(view, target, application);
                }
            }
        }
        
        return view;
    },
    
        
    control : function(view, actions, itemName) {
        if (!view || !view.isView) {
            throw 'Trying to control a view that doesnt exist';
        }

        var item = itemName ? view.refs[itemName] : view,
            key, value, name, child, listener;
    
        if (!item) {
            throw "No item called " + itemName + " found inside the " + view.name + " view.";
        }        
    	
        for (key in actions) {
            value = actions[key];
    	
            
            
            
            if (Ext.isObject(value) && !value.fn) {
                this.control(view, value, key);
            }
            else {
                
                
                if (item.refs) {
                    for (name in item.refs) {
                        child = item.refs[name];
                        if (child.isObservable && child.events[key]) {
                            child.enableBubble(key);
                        }
                    }
                }
    
                if (!value.fn) {
                    listener = {};
                    listener[key] = value;
                    listener.scope = this;
                }
                else {
                    listener = value;
                    if (listener.scope === undefined) {
                        listener.scope = this;
                    }
                }

                
                item.addListener(listener);
            }
        }

        return view;
    },
    
    
    getModel: function() {
        return Ext.ModelMgr.getModel(this.model);
    },
    
    
    closeView: function(view) {
        var ownerCt = view.ownerCt;
        
        if (ownerCt) {
            ownerCt.remove(view);
            ownerCt.doLayout();
            ownerCt.setActiveItem(ownerCt.items.last());
        }
    }
});

Ext.util.Dispatcher = Ext.extend(Ext.util.Observable, {
    
    constructor: function(config) {
        this.addEvents(
            
            'before-dispatch',
            
            
            'dispatch'
        );
        
        Ext.util.Dispatcher.superclass.constructor.call(this, config);
    },
    
    
    dispatch: function(options) {
        var interaction = new Ext.Interaction(options),
            controller  = interaction.controller,
            action      = interaction.action,
            History     = Ext.History;
        
        if (this.fireEvent('before-dispatch', interaction) !== false) {
            if (History && options.historyUrl) {
                History.suspendEvents(false);
                History.add(options.historyUrl);
                Ext.defer(History.resumeEvents, 100, History);
            }
            
            if (controller && action) {
                controller[action].call(controller, interaction);
                interaction.dispatched = true;
            }
            
            this.fireEvent('dispatch', interaction);
        }
    },
    
    
    redirect: function(options) {
        if (options instanceof Ext.data.Model) {
            
            
        } else if (typeof options == 'string') {
            
            var route = Ext.Router.recognize(options);
            
            if (route) {
                return this.dispatch(route);
            }
        }
        return null;
    },
    
    
    createRedirect: function(url) {
        return function() {
            Ext.Dispatcher.redirect(url);
        };
    }
});


Ext.Dispatcher = new Ext.util.Dispatcher();


Ext.dispatch = function() {
    return Ext.Dispatcher.dispatch.apply(Ext.Dispatcher, arguments);
};


Ext.redirect = function() {
    return Ext.Dispatcher.redirect.apply(Ext.Dispatcher, arguments);
};

Ext.createRedirect = Ext.Dispatcher.createRedirect;

Ext.util.Router = Ext.extend(Ext.util.Observable, {
    
    constructor: function(config) {
        config = config || {};

        Ext.apply(this, config, {
            defaults: {
                action: 'index'
            }
        });
        
        this.routes = [];

        Ext.util.Router.superclass.constructor.call(this, config);
    },
    
    
    connect: function(url, params) {
        params = Ext.apply({url: url}, params || {}, this.defaults);
        var route = new Ext.util.Route(params);
        
        this.routes.push(route);
        
        return route;
    },
    
    
    recognize: function(url) {
        var routes = this.routes,
            length = routes.length,
            i, result;
        
        for (i = 0; i < length; i++) {
            result = routes[i].recognize(url);
            
            if (result != undefined) {
                return result;
            }
        }
        return undefined;
    },
    
    
    draw: function(fn) {
        fn.call(this, this);
    }
});


Ext.Router = new Ext.util.Router();


Ext.util.Route = Ext.extend(Object, {
    
    
    constructor: function(config) {
        Ext.apply(this, config, {
            conditions: {}
        });
        
        
        this.paramMatchingRegex = new RegExp(/:([0-9A-Za-z\_]*)/g);
        
        
        this.paramsInMatchString = this.url.match(this.paramMatchingRegex) || [];
        
        this.matcherRegex = this.createMatcherRegex(this.url);
    },
    
    
    recognize: function(url) {
        if (this.recognizes(url)) {
            var matches = this.matchesFor(url);
            
            return Ext.applyIf(matches, {
                controller: this.controller,
                action    : this.action,
                historyUrl: url
            });
        }
    },
    
    
    recognizes: function(url) {
        return this.matcherRegex.test(url);
    },
    
    
    matchesFor: function(url) {
        var params = {},
            keys   = this.paramsInMatchString,
            values = url.match(this.matcherRegex),
            length = keys.length,
            i;
        
        
        values.shift();

        for (i = 0; i < length; i++) {
            params[keys[i].replace(":", "")] = values[i];
        }

        return params;
    },
    
    
    urlFor: function(config) {
        
    },
    
    
    createMatcherRegex: function(url) {
        
        var paramsInMatchString = this.paramsInMatchString,
            length = paramsInMatchString.length,
            i, cond, matcher;
        
        for (i = 0; i < length; i++) {
            cond    = this.conditions[paramsInMatchString[i]];
            matcher = Ext.util.Format.format("({0})", cond || "[%a-zA-Z0-9\\_\\s,]+");

            url = url.replace(new RegExp(paramsInMatchString[i]), matcher);
        }

        
        return new RegExp("^" + url + "$");
    }
});

Ext.Interaction = Ext.extend(Ext.util.Observable, {
    
    controller: '',
    
    
    action: '',
    
    
    
    
    
    
    dispatched: false,
    
    constructor: function(config) {
        Ext.Interaction.superclass.constructor.apply(this, arguments);
        
        config = config || {};
              
        Ext.applyIf(config, {
            scope: this
        });
        
        Ext.apply(this, config);
        
        if (typeof this.controller == 'string') {
            this.controller = Ext.ControllerManager.get(this.controller);
        }
    }
});

Ext.Application = Ext.extend(Ext.util.Observable, {
    

    
    scope: undefined,

    
    useHistory: true,

    

    
    autoUpdateComponentProfiles: true,

    
    setProfilesOnLaunch: true,

    

    constructor: function(config) {
        this.addEvents(
            
            'launch',

            
            'beforeprofilechange',

            
            'profilechange'
        );

        Ext.Application.superclass.constructor.call(this, config);

        this.bindReady();

        var name = this.name;

        if (name) {
            window[name] = this;

            Ext.ns(
                name,
                name + ".views",
                name + ".stores",
                name + ".models",
                name + ".controllers"
            );
        }

        if (Ext.addMetaTags) {
            Ext.addMetaTags(config);
        }
    },

    
    bindReady : function() {
        Ext.onReady(this.onReady, this);
    },

    
    launch: Ext.emptyFn,

    
    useLoadMask: false,

    
    loadMaskFadeDuration: 1000,

    
    loadMaskRemoveDuration: 1050,

    
    autoInitViewport: true,

    
    dispatch: function(options) {
        return Ext.dispatch(options);
    },

    
    initLoadMask: function() {
        var useLoadMask = this.useLoadMask,
            defaultId   = 'loading-mask',
            loadMaskId  = typeof useLoadMask == 'string' ? useLoadMask : defaultId;

        if (useLoadMask) {
            if (loadMaskId == defaultId) {
                Ext.getBody().createChild({id: defaultId});
            }

            var loadingMask  = Ext.get('loading-mask'),
                fadeDuration = this.loadMaskFadeDuration,
                hideDuration = this.loadMaskRemoveDuration;

            Ext.defer(function() {
                loadingMask.addCls('fadeout');

                Ext.defer(function() {
                    loadingMask.remove();
                }, hideDuration);
            }, fadeDuration);
        }
    },

    
    onBeforeLaunch: function() {
        var History    = Ext.History,
            useHistory = History && this.useHistory,
            profile    = this.determineProfile(true);

        if (useHistory) {
            this.historyForm = Ext.getBody().createChild({
                id    : 'history-form',
                cls   : 'x-hide-display',
                style : 'display: none;',
                tag   : 'form',
                action: '#',
                children: [
                    {
                        tag: 'div',
                        children: [
                            {
                                tag : 'input',
                                id  : History.fieldId,
                                type: 'hidden'
                            },
                            {
                                tag: 'iframe',
                                id : History.iframeId
                            }
                        ]
                    }
                ]
            });

            History.init();
            History.on('change', this.onHistoryChange, this);

            var token = History.getToken();

            if (this.launch.call(this.scope || this, profile) !== false) {
                Ext.redirect(token || this.defaultUrl || {controller: 'application', action: 'index'});
            }
        } else {
            this.launch.call(this.scope || this, profile);
        }

        this.launched = true;

        this.fireEvent('launch', this);

        if (this.setProfilesOnLaunch) {
            this.updateComponentProfiles(profile);
        }
    },

    
    onReady: function() {
        if (this.useLoadMask) {
            this.initLoadMask();
        }

        Ext.EventManager.onOrientationChange(this.determineProfile, this);

        if (this.autoInitViewport) {
            Ext.Viewport.init(this.onBeforeLaunch, this);
        } else {
            this.onBeforeLaunch();
        }

        return this;
    },

    
    determineProfile: function(silent) {
        var currentProfile = this.currentProfile,
            profiles       = this.profiles,
            name;

        for (name in profiles) {
            if (profiles[name]() === true) {
                if (name != currentProfile && this.fireEvent('beforeprofilechange', name, currentProfile) !== false) {
                    if (this.autoUpdateComponentProfiles) {
                        this.updateComponentProfiles(name);
                    }

                    if (silent !== true) {
                        this.fireEvent('profilechange', name, currentProfile);
                    }
                }

                this.currentProfile = name;
                break;
            }
        }

        return this.currentProfile;
    },

    
    updateComponentProfiles: function(profile) {
        Ext.ComponentMgr.each(function(key, component){
            if (component.setProfile) {
                component.setProfile(profile);
            }
        });
    },

    
    getProfile: function() {
        return this.currentProfile;
    },

    
    onHistoryChange: function(token) {
        return Ext.redirect(token);
    }
});

Ext.ApplicationManager = new Ext.AbstractManager({
    register: function(name, options) {
        if (Ext.isObject(name)) {
            options = name;
        } else {
            options.name = name;
        }
        
        var application = new Ext.Application(options);
        
        this.all.add(application);
        
        this.currentApplication = application;
        
        return application;
    }
});


Ext.regApplication = function() {
    return Ext.ApplicationManager.register.apply(Ext.ApplicationManager, arguments);
};



(function() {
var El = Ext.Element = Ext.extend(Object, {
    
    defaultUnit : "px",

    constructor : function(element, forceNew) {
        var dom = typeof element == 'string'
                ? document.getElementById(element)
                : element,
            id;

        if (!dom) {
            return null;
        }

        id = dom.id;
        if (!forceNew && id && Ext.cache[id]) {
            return Ext.cache[id].el;
        }

        
        this.dom = dom;

        
        this.id = id || Ext.id(dom);
        return this;
    },

    
    set : function(o, useSet) {
        var el = this.dom,
            attr,
            value;

        for (attr in o) {
            if (o.hasOwnProperty(attr)) {
                value = o[attr];
                if (attr == 'style') {
                    this.applyStyles(value);
                }
                else if (attr == 'cls') {
                    el.className = value;
                }
                else if (useSet !== false) {
                    el.setAttribute(attr, value);
                }
                else {
                    el[attr] = value;
                }
            }
        }
        return this;
    },

    
    is : function(simpleSelector) {
        return Ext.DomQuery.is(this.dom, simpleSelector);
    },

    
    getValue : function(asNumber){
        var val = this.dom.value;
        return asNumber ? parseInt(val, 10) : val;
    },

    
    addListener : function(eventName, fn, scope, options){
        Ext.EventManager.on(this.dom,  eventName, fn, scope || this, options);
        return this;
    },

    
    removeListener : function(eventName, fn, scope) {
        Ext.EventManager.un(this.dom, eventName, fn, scope);
        return this;
    },

    
    removeAllListeners : function(){
        Ext.EventManager.removeAll(this.dom);
        return this;
    },

    
    purgeAllListeners : function() {
        Ext.EventManager.purgeElement(this, true);
        return this;
    },

    
    remove : function() {
        var me = this,
            dom = me.dom;

        if (dom) {
            delete me.dom;
            Ext.removeNode(dom);
        }
    },

    isAncestor : function(c) {
        var p = this.dom;
        c = Ext.getDom(c);
        if (p && c) {
            return p.contains(c);
        }
        return false;
    },

    
    isDescendent : function(p) {
        return Ext.fly(p, '_internal').isAncestor(this);
    },

    
    contains : function(el) {
        return !el ? false : this.isAncestor(el);
    },

    
    getAttribute : function(name, ns) {
        var d = this.dom;
        return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
    },

    
    setHTML : function(html) {
        if(this.dom) {
            this.dom.innerHTML = html;
        }
        return this;
    },

    
    getHTML : function() {
        return this.dom ? this.dom.innerHTML : '';
    },

    
    hide : function() {
        this.setVisible(false);
        return this;
    },

    
    show : function() {
        this.setVisible(true);
        return this;
    },

    
     setVisible : function(visible, animate) {
        var me = this,
            dom = me.dom,
            mode = this.getVisibilityMode();

        switch (mode) {
            case El.VISIBILITY:
                this.removeCls(['x-hidden-display', 'x-hidden-offsets']);
                this[visible ? 'removeCls' : 'addCls']('x-hidden-visibility');
            break;

            case El.DISPLAY:
                this.removeCls(['x-hidden-visibility', 'x-hidden-offsets']);
                this[visible ? 'removeCls' : 'addCls']('x-hidden-display');
            break;

            case El.OFFSETS:
                this.removeCls(['x-hidden-visibility', 'x-hidden-display']);
                this[visible ? 'removeCls' : 'addCls']('x-hidden-offsets');
            break;
        }

        return me;
    },

    getVisibilityMode: function() {
        var dom = this.dom,
            mode = El.data(dom, 'visibilityMode');

        if (mode === undefined) {
            El.data(dom, 'visibilityMode', mode = El.DISPLAY);
        }

        return mode;
    },

    setVisibilityMode : function(mode) {
        El.data(this.dom, 'visibilityMode', mode);
        return this;
    }
});

var Elp = El.prototype;


El.VISIBILITY = 1;

El.DISPLAY = 2;

El.OFFSETS = 3;


El.addMethods = function(o){
   Ext.apply(Elp, o);
};


Elp.on = Elp.addListener;
Elp.un = Elp.removeListener;


Elp.update = Elp.setHTML;


El.get = function(el){
    var extEl,
        dom,
        id;

    if(!el){
        return null;
    }

    if (typeof el == "string") { 
        if (!(dom = document.getElementById(el))) {
            return null;
        }
        if (Ext.cache[el] && Ext.cache[el].el) {
            extEl = Ext.cache[el].el;
            extEl.dom = dom;
        } else {
            extEl = El.addToCache(new El(dom));
        }
        return extEl;
    } else if (el.tagName) { 
        if(!(id = el.id)){
            id = Ext.id(el);
        }
        if (Ext.cache[id] && Ext.cache[id].el) {
            extEl = Ext.cache[id].el;
            extEl.dom = el;
        } else {
            extEl = El.addToCache(new El(el));
        }
        return extEl;
    } else if (el instanceof El) {
        if(el != El.docEl){
            
            
            el.dom = document.getElementById(el.id) || el.dom;
        }
        return el;
    } else if(el.isComposite) {
        return el;
    } else if(Ext.isArray(el)) {
        return El.select(el);
    } else if(el == document) {
        
        if(!El.docEl){
            var F = function(){};
            F.prototype = Elp;
            El.docEl = new F();
            El.docEl.dom = document;
            El.docEl.id = Ext.id(document);
        }
        return El.docEl;
    }
    return null;
};


El.addToCache = function(el, id){
    id = id || el.id;
    Ext.cache[id] = {
        el:  el,
        data: {},
        events: {}
    };
    return el;
};


El.data = function(el, key, value) {
    el = El.get(el);
    if (!el) {
        return null;
    }
    var c = Ext.cache[el.id].data;
    if (arguments.length == 2) {
        return c[key];
    }
    else {
        return (c[key] = value);
    }
};




El.garbageCollect = function() {
    if (!Ext.enableGarbageCollector) {
        clearInterval(El.collectorThreadId);
    }
    else {
        var id,
            dom,
            EC = Ext.cache;

        for (id in EC) {
            if (!EC.hasOwnProperty(id)) {
                continue;
            }
            if(EC[id].skipGarbageCollection){
                continue;
            }
            dom = EC[id].el.dom;
            if(!dom || !dom.parentNode || (!dom.offsetParent && !document.getElementById(id))){
                if(Ext.enableListenerCollection){
                    Ext.EventManager.removeAll(dom);
                }
                delete EC[id];
            }
        }
    }
};



El.Flyweight = function(dom) {
    this.dom = dom;
};

var F = function(){};
F.prototype = Elp;

El.Flyweight.prototype = new F;
El.Flyweight.prototype.isFlyweight = true;

El._flyweights = {};


El.fly = function(el, named) {
    var ret = null;
    named = named || '_global';

    el = Ext.getDom(el);
    if (el) {
        (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
        ret = El._flyweights[named];
    }

    return ret;
};


Ext.get = El.get;


Ext.fly = El.fly;



})();


Ext.applyIf(Ext.Element, {
    unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
    camelRe: /(-[a-z])/gi,
    opacityRe: /alpha\(opacity=(.*)\)/i,
    propertyCache: {},
    defaultUnit : "px",
    borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
    paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
    margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},

    addUnits : function(size, units) {
        if (size === "" || size == "auto" || size === null || size === undefined) {
            size = size || '';
        }
        else if (!isNaN(size) || !this.unitRe.test(size)) {
            size = size + (units || this.defaultUnit || 'px');
        }
        return size;
    },

    
    parseBox : function(box) {
        if (typeof box != 'string') {
            box = box.toString();
        }
        var parts  = box.split(' '),
            ln = parts.length;

        if (ln == 1) {
            parts[1] = parts[2] = parts[3] = parts[0];
        }
        else if (ln == 2) {
            parts[2] = parts[0];
            parts[3] = parts[1];
        }
        else if (ln == 3) {
            parts[3] = parts[1];
        }

        return {
            top   :parseFloat(parts[0]) || 0,
            right :parseFloat(parts[1]) || 0,
            bottom:parseFloat(parts[2]) || 0,
            left  :parseFloat(parts[3]) || 0
        };
    },
    
    
    unitizeBox : function(box, units) {
        var A = this.addUnits,
            B = this.parseBox(box);
            
        return A(B.top, units) + ' ' +
               A(B.right, units) + ' ' +
               A(B.bottom, units) + ' ' +
               A(B.left, units);
        
    },

    
    camelReplaceFn : function(m, a) {
        return a.charAt(1).toUpperCase();
    },

    
    normalize : function(prop) {
        return this.propertyCache[prop] || (this.propertyCache[prop] = prop == 'float' ? 'cssFloat' : prop.replace(this.camelRe, this.camelReplaceFn));
    },

    
    getDocumentHeight: function() {
        return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
    },

    
    getDocumentWidth: function() {
        return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
    },

    
    getViewportHeight: function(){
        return window.innerHeight;
    },

    
    getViewportWidth : function() {
        return window.innerWidth;
    },

    
    getViewSize : function() {
        return {
            width: window.innerWidth,
            height: window.innerHeight
        };
    },

    
    getOrientation : function() {
        if (Ext.supports.OrientationChange) {
            return (window.orientation == 0) ? 'portrait' : 'landscape';
        }
        
        return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
    },

    
    fromPoint: function(x, y) {
        return Ext.get(document.elementFromPoint(x, y));
    }
});


Ext.applyIf(Ext.Element, {
    
    
    getComputedTransformOffset: function(el) {
        if (el instanceof Ext.Element)
            el = el.dom;
            
        var transform = window.getComputedStyle(el).webkitTransform,
            cssMatrix = transform != 'none' ? new WebKitCSSMatrix(transform) : new WebKitCSSMatrix();

        if (typeof cssMatrix.m41 != 'undefined') {
            return new Ext.util.Offset(cssMatrix.m41, cssMatrix.m42);
        } else if (typeof cssMatrix.d != 'undefined') {
            return new Ext.util.Offset(cssMatrix.d, cssMatrix.e);
        }

        return new Ext.util.Offset(0, 0);
    },

    
    cssTransform: function(el, transforms) {
        if (el instanceof Ext.Element)
            el = el.dom;

        var m = new WebKitCSSMatrix();

        Ext.iterate(transforms, function(n, v) {
            v = Ext.isArray(v) ? v : [v];
            m = m[n].apply(m, v);
        });

        
        
        if (Ext.supports.CSS3DTransform) {
            el.style.webkitTransform = 'matrix3d(' +
                                            m.m11+', '+m.m12+', '+m.m13+', '+m.m14+', '+
                                            m.m21+', '+m.m22+', '+m.m23+', '+m.m24+', '+
                                            m.m31+', '+m.m32+', '+m.m33+', '+m.m34+', '+
                                            m.m41+', '+m.m42+', '+m.m43+', '+m.m44+
                                       ')';
        } else {
            el.style.webkitTransform = m;
        }
    },

    
    cssTranslate: function(el, offset) {
        if (el instanceof Ext.Element)
            el = el.dom;

        if (Ext.supports.CSS3DTransform) {
            el.style.webkitTransform = 'translate3d('+offset.x+'px, '+offset.y+'px, 0px)';
        } else {
            el.style.webkitTransform = 'translate('+offset.x+'px, '+offset.y+'px)';
        }
    }

});


Ext.Element.addMethods({
    
    getY : function(el) {
        return this.getXY(el)[1];
    },

    
    getX : function(el) {
        return this.getXY(el)[0];
    },

    
    getXY : function() {
        var rect = this.dom.getBoundingClientRect(),
            round = Math.round;

        return [round(rect.left + window.pageXOffset), round(rect.top + window.pageYOffset)];
    },

    
    getOffsetsTo : function(el){
        var o = this.getXY(),
            e = Ext.fly(el, '_internal').getXY();
        return [o[0]-e[0],o[1]-e[1]];
    },

    
    setXY : function(pos) {
        var me = this;

        if(arguments.length > 1) {
            pos = [pos, arguments[1]];
        }

        
        var pts = me.translatePoints(pos),
            style = me.dom.style;

        for (pos in pts) {
            if (!pts.hasOwnProperty(pos)) {
                continue;
            }
            if(!isNaN(pts[pos])) style[pos] = pts[pos] + "px";
        }
        return me;
    },

    
    setX : function(x){
        return this.setXY([x, this.getY()]);
    },

    
    setY : function(y) {
        return this.setXY([this.getX(), y]);
    },

    
    setLeft : function(left) {
        this.setStyle('left', Ext.Element.addUnits(left));
        return this;
    },

    
    setTop : function(top) {
        this.setStyle('top', Ext.Element.addUnits(top));
        return this;
    },

    
    setTopLeft: function(top, left) {
        var addUnits = Ext.Element.addUnits;

        this.setStyle('top', addUnits(top));
        this.setStyle('left', addUnits(left));

        return this;
    },

    
    setRight : function(right) {
        this.setStyle('right', Ext.Element.addUnits(right));
        return this;
    },

    
    setBottom : function(bottom) {
        this.setStyle('bottom', Ext.Element.addUnits(bottom));
        return this;
    },

    
    getLeft : function(local) {
        return parseInt(this.getStyle('left'), 10) || 0;
    },

    
    getRight : function(local) {
        return parseInt(this.getStyle('right'), 10) || 0;
    },

    
    getTop : function(local) {
        return parseInt(this.getStyle('top'), 10) || 0;
    },

    
    getBottom : function(local) {
        return parseInt(this.getStyle('bottom'), 10) || 0;
    },

    
    setBox : function(left, top, width, height) {
        var undefined;
        if (Ext.isObject(left)) {
            width = left.width;
            height = left.height;
            top = left.top;
            left = left.left;
        }
        
        if (left !== undefined) {
            this.setLeft(left);
        }
        if (top !== undefined) {
            this.setTop(top);
        }
        if (width !== undefined) {
            this.setWidth(width);
        }
        if (height !== undefined) {
            this.setHeight(height);
        }
    
        return this;
    },

    
    getBox : function(contentBox, local) {
        var me = this,
            dom = me.dom,
            width = dom.offsetWidth,
            height = dom.offsetHeight,
            xy, box, l, r, t, b;

        if (!local) {
            xy = me.getXY();
        }
        else if (contentBox) {
            xy = [0,0];
        }
        else {
            xy = [parseInt(me.getStyle("left"), 10) || 0, parseInt(me.getStyle("top"), 10) || 0];
        }

        if (!contentBox) {
            box = {
                x: xy[0],
                y: xy[1],
                0: xy[0],
                1: xy[1],
                width: width,
                height: height
            };
        }
        else {
            l = me.getBorderWidth.call(me, "l") + me.getPadding.call(me, "l");
            r = me.getBorderWidth.call(me, "r") + me.getPadding.call(me, "r");
            t = me.getBorderWidth.call(me, "t") + me.getPadding.call(me, "t");
            b = me.getBorderWidth.call(me, "b") + me.getPadding.call(me, "b");
            box = {
                x: xy[0] + l,
                y: xy[1] + t,
                0: xy[0] + l,
                1: xy[1] + t,
                width: width - (l + r),
                height: height - (t + b)
            };
        }

        box.left = box.x;
        box.top = box.y;
        box.right = box.x + box.width;
        box.bottom = box.y + box.height;

        return box;
    },

    
    getPageBox : function(getRegion) {
        var me = this,
            el = me.dom,
            w = el.offsetWidth,
            h = el.offsetHeight,
            xy = me.getXY(),
            t = xy[1],
            r = xy[0] + w,
            b = xy[1] + h,
            l = xy[0];
        
        if (!el) {
            return new Ext.util.Region();
        }
        
        if (getRegion) {
            return new Ext.util.Region(t, r, b, l);
        }
        else {
            return {
                left: l,
                top: t,
                width: w,
                height: h,
                right: r,
                bottom: b
            };
        }
    },

    
    translatePoints : function(x, y) {
        y = isNaN(x[1]) ? y : x[1];
        x = isNaN(x[0]) ? x : x[0];
        var me = this,
            relative = me.isStyle('position', 'relative'),
            o = me.getXY(),
            l = parseInt(me.getStyle('left'), 10),
            t = parseInt(me.getStyle('top'), 10);

        l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
        t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);

        return {left: (x - o[0] + l), top: (y - o[1] + t)};
    }
});

(function() {
    
    Ext.Element.classReCache = {};
    var El = Ext.Element,
        view = document.defaultView;

    El.addMethods({
        marginRightRe: /marginRight/i,
        trimRe: /^\s+|\s+$/g,
        spacesRe: /\s+/,

        
        addCls: function(className) {
            var me = this,
                i,
                len,
                v,
                cls = [];

            if (!Ext.isArray(className)) {
                if (className && !this.hasCls(className)) {
                    me.dom.className += " " + className;
                }
            }
            else {
                for (i = 0, len = className.length; i < len; i++) {
                    v = className[i];
                    if (v && !me.hasCls(v)) {
                        cls.push(v);
                    }
                }
                if (cls.length) {
                    me.dom.className += " " + cls.join(" ");
                }
            }
            return me;
        },
        
        addClass : function() {
            throw new Error("Component: addClass has been deprecated. Please use addCls.");
        },

        
        removeCls: function(className) {
            var me = this,
                i,
                idx,
                len,
                cls,
                elClasses;
            if (!Ext.isArray(className)) {
                className = [className];
            }
            if (me.dom && me.dom.className) {
                elClasses = me.dom.className.replace(this.trimRe, '').split(this.spacesRe);
                for (i = 0, len = className.length; i < len; i++) {
                    cls = className[i];
                    if (typeof cls == 'string') {
                        cls = cls.replace(this.trimRe, '');
                        idx = elClasses.indexOf(cls);
                        if (idx != -1) {
                            elClasses.splice(idx, 1);
                        }
                    }
                }
                me.dom.className = elClasses.join(" ");
            }
            return me;
        },
        
        removeClass : function() {
            throw new Error("Component: removeClass has been deprecated. Please use removeCls.");
        },

        
        mask: function(msg, msgCls, transparent) {
            var me = this,
                dom = me.dom,
                el = Ext.Element.data(dom, 'mask'),
                mask,
                size,
                cls = '';

            me.addCls('x-masked');
            if (me.getStyle("position") == "static") {
                me.addCls('x-masked-relative');
            }
            if (el) {
                el.remove();
            }
            if (Ext.isString(msgCls) && !Ext.isEmpty(msgCls)) {
                cls = ' ' + msgCls;
            }
            else {
                if (msgCls) {
                    cls = ' x-mask-gray';
                }
            }
                        
            mask = me.createChild({
                cls: 'x-mask' + ((transparent !== false) ? '' : ' x-mask-gray'),
                html: msg ? ('<div class="' + (msgCls || 'x-mask-message') + '">' + msg + '</div>') : ''
            });

            size = me.getSize();

            Ext.Element.data(dom, 'mask', mask);

            if (dom === document.body) {
                size.height = window.innerHeight;
                if (me.orientationHandler) {
                    Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                }

                me.orientationHandler = function() {
                    size = me.getSize();
                    size.height = window.innerHeight;
                    mask.setSize(size);
                };

                Ext.EventManager.onOrientationChange(me.orientationHandler, me);
            }
            mask.setSize(size);
            if (Ext.is.iPad) {
                Ext.repaint();
            }
        },

        
        unmask: function() {
            var me = this,
                dom = me.dom,
                mask = Ext.Element.data(dom, 'mask');

            if (mask) {
                mask.remove();
                Ext.Element.data(dom, 'mask', undefined);
            }
            me.removeCls(['x-masked', 'x-masked-relative']);

            if (dom === document.body) {
                Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                delete me.orientationHandler;
            }
        },

        
        radioCls: function(className) {
            var cn = this.dom.parentNode.childNodes,
                v;
            className = Ext.isArray(className) ? className: [className];
            for (var i = 0, len = cn.length; i < len; i++) {
                v = cn[i];
                if (v && v.nodeType == 1) {
                    Ext.fly(v, '_internal').removeCls(className);
                }
            };
            return this.addCls(className);
        },
        
        radioClass : function() {
            throw new Error("Component: radioClass has been deprecated. Please use radioCls.");
        },

        
        toggleCls: function(className) {
            return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
        },
        
        toggleClass : function() {
            throw new Error("Component: toggleClass has been deprecated. Please use toggleCls.");
        },

        
        hasCls: function(className) {
            return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
        },
        
        hasClass : function() {
            throw new Error("Element: hasClass has been deprecated. Please use hasCls.");
            return this.hasCls.apply(this, arguments);
        },

        
        replaceCls: function(oldClassName, newClassName) {
            return this.removeCls(oldClassName).addCls(newClassName);
        },
        
        replaceClass : function() {
            throw new Error("Component: replaceClass has been deprecated. Please use replaceCls.");
        },

        isStyle: function(style, val) {
            return this.getStyle(style) == val;
        },

        
        getStyle: function(prop) {
            var dom = this.dom,
                result,
                display,
                cs,
                platform = Ext.is,
                style = dom.style;

            prop = El.normalize(prop);
            cs = (view) ? view.getComputedStyle(dom, '') : dom.currentStyle;
            result = (cs) ? cs[prop] : null;

            
            if (result && !platform.correctRightMargin &&
                    this.marginRightRe.test(prop) &&
                    style.position != 'absolute' &&
                    result != '0px') {
                display = style.display;
                style.display = 'inline-block';
                result = view.getComputedStyle(dom, null)[prop];
                style.display = display;
            }

            result || (result = style[prop]);

            
            if (!platform.correctTransparentColor && result == 'rgba(0, 0, 0, 0)') {
                result = 'transparent';
            }

            return result;
        },

        
        setStyle: function(prop, value) {
            var tmp,
                style;

            if (typeof prop == 'string') {
                tmp = {};
                tmp[prop] = value;
                prop = tmp;
            }

            for (style in prop) {
                if (prop.hasOwnProperty(style)) {
                    this.dom.style[El.normalize(style)] = prop[style];
                }
            }

            return this;
        },

        
        applyStyles: function(styles) {
            if (styles) {
                var i,
                    len,
                    dom = this.dom;

                if (typeof styles == 'function') {
                    styles = styles.call();
                }
                if (typeof styles == 'string') {
                    styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
                    for (i = 0, len = styles.length; i < len;) {
                        dom.style[El.normalize(styles[i++])] = styles[i++];
                    }
                }
                else if (typeof styles == 'object') {
                    this.setStyle(styles);
                }
            }
        },

        
        getHeight: function(contentHeight) {
            var dom = this.dom,
                height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight;
            return height > 0 ? height: 0;
        },

        
        getWidth: function(contentWidth) {
            var dom = this.dom,
                width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth;
            return width > 0 ? width: 0;
        },

        
        setWidth: function(width) {
            var me = this;
                me.dom.style.width = El.addUnits(width);
            return me;
        },

        
        setHeight: function(height) {
            var me = this;
                me.dom.style.height = El.addUnits(height);
            return me;
        },

        
        setSize: function(width, height) {
            var me = this,
                style = me.dom.style;

            if (Ext.isObject(width)) {
                
                height = width.height;
                width = width.width;
            }

            style.width = El.addUnits(width);
            style.height = El.addUnits(height);
            return me;
        },

        
        getBorderWidth: function(side) {
            return this.sumStyles(side, El.borders);
        },

        
        getPadding: function(side) {
            return this.sumStyles(side, El.paddings);
        },

        
        getMargin: function(side) {
            return this.sumStyles(side, El.margins);
        },

        
        getViewSize: function() {
            var doc = document,
                dom = this.dom;

            if (dom == doc || dom == doc.body) {
                return {
                    width: El.getViewportWidth(),
                    height: El.getViewportHeight()
                };
            }
            else {
                return {
                    width: dom.clientWidth,
                    height: dom.clientHeight
                };
            }
        },

        
        getSize: function(contentSize) {
            var dom = this.dom;
            return {
                width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth),
                height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight)
            };
        },

        
        repaint: function() {
            var dom = this.dom;
                this.addCls("x-repaint");
            dom.style.background = 'transparent none';
            setTimeout(function() {
                dom.style.background = null;
                Ext.get(dom).removeCls("x-repaint");
            },
            1);
            return this;
        },

        
        getOuterWidth: function() {
            return this.getWidth() + this.getMargin('lr');
        },

        
        getOuterHeight: function() {
            return this.getHeight() + this.getMargin('tb');
        },

        
        sumStyles: function(sides, styles) {
            var val = 0,
                m = sides.match(/\w/g),
                len = m.length,
                s,
                i;

            for (i = 0; i < len; i++) {
                s = m[i] && parseFloat(this.getStyle(styles[m[i]])) || 0;
                if (s) {
                    val += Math.abs(s);
                }
            }
            return val;
        }
    });
})();

Ext.Element.addMethods({
    
    findParent : function(simpleSelector, maxDepth, returnEl) {
        var p = this.dom,
            b = document.body,
            depth = 0,
            stopEl;

        maxDepth = maxDepth || 50;
        if (isNaN(maxDepth)) {
            stopEl = Ext.getDom(maxDepth);
            maxDepth = Number.MAX_VALUE;
        }
        while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
            if (Ext.DomQuery.is(p, simpleSelector)) {
                return returnEl ? Ext.get(p) : p;
            }
            depth++;
            p = p.parentNode;
        }
        return null;
    },
    
    
    findParentNode : function(simpleSelector, maxDepth, returnEl) {
        var p = Ext.fly(this.dom.parentNode, '_internal');
        return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
    },

    
    up : function(simpleSelector, maxDepth) {
        return this.findParentNode(simpleSelector, maxDepth, true);
    },

    
    select : function(selector, composite) {
        return Ext.Element.select(selector, this.dom, composite);
    },

    
    query : function(selector) {
        return Ext.DomQuery.select(selector, this.dom);
    },

    
    down : function(selector, returnDom) {
        var n = Ext.DomQuery.selectNode(selector, this.dom);
        return returnDom ? n : Ext.get(n);
    },

    
    child : function(selector, returnDom) {
        var node,
            me = this,
            id;
        id = Ext.get(me).id;
        
        id = id.replace(/[\.:]/g, "\\$0");
        node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
        return returnDom ? node : Ext.get(node);
    },

     
    parent : function(selector, returnDom) {
        return this.matchNode('parentNode', 'parentNode', selector, returnDom);
    },

     
    next : function(selector, returnDom) {
        return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
    },

    
    prev : function(selector, returnDom) {
        return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
    },


    
    first : function(selector, returnDom) {
        return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
    },

    
    last : function(selector, returnDom) {
        return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
    },

    matchNode : function(dir, start, selector, returnDom) {
        if (!this.dom)
            return null;
        
        var n = this.dom[start];
        while (n) {
            if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
                return !returnDom ? Ext.get(n) : n;
            }
            n = n[dir];
        }
        return null;
    }
});


Ext.Element.addMethods({
    
    getScrollParent : function() {
        var parent = this.dom, scroller;
        while (parent && parent != document.body) {
            if (parent.id && (scroller = Ext.ScrollManager.get(parent.id))) {
                return scroller;
            }
            parent = parent.parentNode;
        }
        return null;
    }
});


Ext.Element.addMethods({
    
    appendChild : function(el) {
        return Ext.get(el).appendTo(this);
    },

    
    appendTo : function(el) {
        Ext.getDom(el).appendChild(this.dom);
        return this;
    },

    
    insertBefore : function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el);
        return this;
    },

    
    insertAfter : function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el.nextSibling);
        return this;
    },

    
    insertFirst : function(el, returnDom) {
        el = el || {};
        if (el.nodeType || el.dom || typeof el == 'string') { 
            el = Ext.getDom(el);
            this.dom.insertBefore(el, this.dom.firstChild);
            return !returnDom ? Ext.get(el) : el;
        }
        else { 
            return this.createChild(el, this.dom.firstChild, returnDom);
        }
    },

    
    insertSibling: function(el, where, returnDom){
        var me = this, rt,
        isAfter = (where || 'before').toLowerCase() == 'after',
        insertEl;

        if(Ext.isArray(el)){
            insertEl = me;
            Ext.each(el, function(e) {
                rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
                if(isAfter){
                    insertEl = rt;
                }
            });
            return rt;
        }

        el = el || {};

        if(el.nodeType || el.dom){
            rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
            if (!returnDom) {
                rt = Ext.get(rt);
            }
        }else{
            if (isAfter && !me.dom.nextSibling) {
                rt = Ext.DomHelper.append(me.dom.parentNode, el, !returnDom);
            } else {
                rt = Ext.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
            }
        }
        return rt;
    },

    
    replace : function(el) {
        el = Ext.get(el);
        this.insertBefore(el);
        el.remove();
        return this;
    },
    
    
    replaceWith: function(el){
        var me = this;
            
        if(el.nodeType || el.dom || typeof el == 'string'){
            el = Ext.get(el);
            me.dom.parentNode.insertBefore(el, me.dom);
        }else{
            el = Ext.DomHelper.insertBefore(me.dom, el);
        }
        
        delete Ext.cache[me.id];
        Ext.removeNode(me.dom);      
        me.id = Ext.id(me.dom = el);
        Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);     
        return me;
    },
    
    
    createChild : function(config, insertBefore, returnDom) {
        config = config || {tag:'div'};
        if (insertBefore) {
            return Ext.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
        }
        else {
            return Ext.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
        }
    },

    
    wrap : function(config, returnDom) {
        var newEl = Ext.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
        newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
        return newEl;
    },

    
    insertHtml : function(where, html, returnEl) {
        var el = Ext.DomHelper.insertHtml(where, this.dom, html);
        return returnEl ? Ext.get(el) : el;
    }
});


Ext.Element.addMethods({
    
    getAnchorXY: function(anchor, local, size) {
        
        
        anchor = (anchor || "tl").toLowerCase();
        size = size || {};

        var me = this,
            vp = me.dom == document.body || me.dom == document,
            width = size.width || vp ? window.innerWidth: me.getWidth(),
            height = size.height || vp ? window.innerHeight: me.getHeight(),
            xy,
            rnd = Math.round,
            myXY = me.getXY(),
            extraX = vp ? 0: !local ? myXY[0] : 0,
            extraY = vp ? 0: !local ? myXY[1] : 0,
            hash = {
                c: [rnd(width * 0.5), rnd(height * 0.5)],
                t: [rnd(width * 0.5), 0],
                l: [0, rnd(height * 0.5)],
                r: [width, rnd(height * 0.5)],
                b: [rnd(width * 0.5), height],
                tl: [0, 0],
                bl: [0, height],
                br: [width, height],
                tr: [width, 0]
            };

        xy = hash[anchor];
        return [xy[0] + extraX, xy[1] + extraY];
    },

    
    getAlignToXY: function(el, position, offsets) {
        el = Ext.get(el);

        if (!el || !el.dom) {
            throw new Error("Element.alignToXY with an element that doesn't exist");
        }
        offsets = offsets || [0, 0];

        if (!position || position == '?') {
            position = 'tl-bl?';
        }
        else if (! (/-/).test(position) && position !== "") {
            position = 'tl-' + position;
        }
        position = position.toLowerCase();

        var me = this,
            matches = position.match(/^([a-z]+)-([a-z]+)(\?)?$/),
            dw = window.innerWidth,
            dh = window.innerHeight,
            p1 = "",
            p2 = "",
            a1,
            a2,
            x,
            y,
            swapX,
            swapY,
            p1x,
            p1y,
            p2x,
            p2y,
            width,
            height,
            region,
            constrain;

        if (!matches) {
            throw "Element.alignTo with an invalid alignment " + position;
        }

        p1 = matches[1];
        p2 = matches[2];
        constrain = !!matches[3];

        
        
        a1 = me.getAnchorXY(p1, true);
        a2 = el.getAnchorXY(p2, false);

        x = a2[0] - a1[0] + offsets[0];
        y = a2[1] - a1[1] + offsets[1];

        if (constrain) {
            width = me.getWidth();
            height = me.getHeight();

            region = el.getPageBox();

            
            
            
            p1y = p1.charAt(0);
            p1x = p1.charAt(p1.length - 1);
            p2y = p2.charAt(0);
            p2x = p2.charAt(p2.length - 1);

            swapY = ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
            swapX = ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));

            if (x + width > dw) {
                x = swapX ? region.left - width: dw - width;
            }
            if (x < 0) {
                x = swapX ? region.right: 0;
            }
            if (y + height > dh) {
                y = swapY ? region.top - height: dh - height;
            }
            if (y < 0) {
                y = swapY ? region.bottom: 0;
            }
        }

        return [x, y];
    }

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
});


Ext.CompositeElement = function(els, root) {
    
    this.elements = [];
    this.add(els, root);
    this.el = new Ext.Element.Flyweight();
};

Ext.CompositeElement.prototype = {
    isComposite: true,

    
    getElement : function(el) {
        
        var e = this.el;
        e.dom = el;
        e.id = el.id;
        return e;
    },

    
    transformElement : function(el) {
        return Ext.getDom(el);
    },

    
    getCount : function() {
        return this.elements.length;
    },

    
    add : function(els, root) {
        var me = this,
            elements = me.elements;
        if (!els) {
            return this;
        }
        if (typeof els == 'string') {
            els = Ext.Element.selectorFunction(els, root);
        }
        else if (els.isComposite) {
            els = els.elements;
        }
        else if (!Ext.isIterable(els)) {
            els = [els];
        }

        for (var i = 0, len = els.length; i < len; ++i) {
            elements.push(me.transformElement(els[i]));
        }

        return me;
    },

    invoke : function(fn, args) {
        var me = this,
            els = me.elements,
            len = els.length,
            e,
            i;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                Ext.Element.prototype[fn].apply(me.getElement(e), args);
            }
        }
        return me;
    },
    
    item : function(index) {
        var me = this,
            el = me.elements[index],
            out = null;

        if (el){
            out = me.getElement(el);
        }
        return out;
    },

    
    addListener : function(eventName, handler, scope, opt) {
        var els = this.elements,
            len = els.length,
            i, e;

        for (i = 0; i<len; i++) {
            e = els[i];
            if (e) {
                Ext.EventManager.on(e, eventName, handler, scope || e, opt);
            }
        }
        return this;
    },

    
    each : function(fn, scope) {
        var me = this,
            els = me.elements,
            len = els.length,
            i, e;

        for (i = 0; i<len; i++) {
            e = els[i];
            if (e) {
                e = this.getElement(e);
                if(fn.call(scope || e, e, me, i)){
                    break;
                }
            }
        }
        return me;
    },

    
    fill : function(els) {
        var me = this;
        me.elements = [];
        me.add(els);
        return me;
    },

    
    filter : function(selector) {
        var els = [],
            me = this,
            elements = me.elements,
            fn = Ext.isFunction(selector) ? selector
                : function(el){
                    return el.is(selector);
                };

        me.each(function(el, self, i){
            if(fn(el, i) !== false){
                els[els.length] = me.transformElement(el);
            }
        });
        me.elements = els;
        return me;
    },

    
    first : function() {
        return this.item(0);
    },

    
    last : function() {
        return this.item(this.getCount()-1);
    },

    
    contains : function(el) {
        return this.indexOf(el) != -1;
    },

    
    indexOf : function(el) {
        return this.elements.indexOf(this.transformElement(el));
    },

    
    clear : function() {
        this.elements = [];
    }
};

Ext.CompositeElement.prototype.on = Ext.CompositeElement.prototype.addListener;

(function(){
var fnName,
    ElProto = Ext.Element.prototype,
    CelProto = Ext.CompositeElement.prototype;

for (fnName in ElProto) {
    if (Ext.isFunction(ElProto[fnName])) {
        (function(fnName) {
            CelProto[fnName] = CelProto[fnName] || function(){
                return this.invoke(fnName, arguments);
            };
        }).call(CelProto, fnName);

    }
}
})();

if(Ext.DomQuery) {
    Ext.Element.selectorFunction = Ext.DomQuery.select;
}


Ext.Element.select = function(selector, root, composite) {
    var els;
    composite = (composite === false) ? false : true;
    if (typeof selector == "string") {
        els = Ext.Element.selectorFunction(selector, root);
    } else if (selector.length !== undefined) {
        els = selector;
    } else {
        throw new Error("Invalid selector");
    }
    return composite ? new Ext.CompositeElement(els) : els;
};

Ext.select = Ext.Element.select;


Ext.CompositeElementLite = Ext.CompositeElement;


Ext.apply(Ext.CompositeElementLite.prototype, {
    addElements : function(els, root){
        if(!els){
            return this;
        }
        if(typeof els == "string"){
            els = Ext.Element.selectorFunction(els, root);
        }
        var yels = this.elements;
        Ext.each(els, function(e) {
            yels.push(Ext.get(e));
        });
        return this;
    },

    
    removeElement : function(keys, removeDom){
        var me = this,
            els = this.elements,
            el;
        Ext.each(keys, function(val){
            if ((el = (els[val] || els[val = me.indexOf(val)]))) {
                if(removeDom){
                    if(el.dom){
                        el.remove();
                    }else{
                        Ext.removeNode(el);
                    }
                }
                els.splice(val, 1);
            }
        });
        return this;
    },

    
    replaceElement : function(el, replacement, domReplace){
        var index = !isNaN(el) ? el : this.indexOf(el),
            d;
        if(index > -1){
            replacement = Ext.getDom(replacement);
            if(domReplace){
                d = this.elements[index];
                d.parentNode.insertBefore(replacement, d);
                Ext.removeNode(d);
            }
            this.elements.splice(index, 1, replacement);
        }
        return this;
    }
});


Ext.DomHelper = {
    emptyTags : /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
    confRe : /tag|children|cn|html$/i,
    endRe : /end/i,

    
    markup : function(o) {
        var b = '',
            attr,
            val,
            key,
            keyVal,
            cn;

        if (typeof o == "string") {
            b = o;
        }
        else if (Ext.isArray(o)) {
            for (var i=0; i < o.length; i++) {
                if (o[i]) {
                    b += this.markup(o[i]);
                }
            };
        }
        else {
            b += '<' + (o.tag = o.tag || 'div');
            for (attr in o) {
                if (!o.hasOwnProperty(attr)) {
                    continue;
                }
                val = o[attr];
                if (!this.confRe.test(attr)) {
                    if (typeof val == "object") {
                        b += ' ' + attr + '="';
                        for (key in val) {
                            if (!val.hasOwnProperty(key)) {
                                continue;
                            }
                            b += key + ':' + val[key] + ';';
                        };
                        b += '"';
                    }
                    else {
                        b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
                    }
                }
            };

            
            if (this.emptyTags.test(o.tag)) {
                b += '/>';
            }
            else {
                b += '>';
                if ((cn = o.children || o.cn)) {
                    b += this.markup(cn);
                }
                else if (o.html) {
                    b += o.html;
                }
                b += '</' + o.tag + '>';
            }
        }
        return b;
    },

    
    applyStyles : function(el, styles) {
        if (styles) {
            var i = 0,
                len,
                style;

            el = Ext.fly(el);
            if (typeof styles == 'function') {
                styles = styles.call();
            }
            if (typeof styles == 'string'){
                styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
                for(len = styles.length; i < len;){
                    el.setStyle(styles[i++], styles[i++]);
                }
            } else if (Ext.isObject(styles)) {
                el.setStyle(styles);
            }
        }
    },

    
    insertHtml : function(where, el, html) {
        var hash = {},
            hashVal,
            setStart,
            range,
            frag,
            rangeEl,
            rs;

        where = where.toLowerCase();

        
        hash['beforebegin'] = ['BeforeBegin', 'previousSibling'];
        hash['afterend'] = ['AfterEnd', 'nextSibling'];

        range = el.ownerDocument.createRange();
        setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');
        if (hash[where]) {
            range[setStart](el);
            frag = range.createContextualFragment(html);
            el.parentNode.insertBefore(frag, where == 'beforebegin' ? el : el.nextSibling);
            return el[(where == 'beforebegin' ? 'previous' : 'next') + 'Sibling'];
        }
        else {
            rangeEl = (where == 'afterbegin' ? 'first' : 'last') + 'Child';
            if (el.firstChild) {
                range[setStart](el[rangeEl]);
                frag = range.createContextualFragment(html);
                if (where == 'afterbegin') {
                    el.insertBefore(frag, el.firstChild);
                }
                else {
                    el.appendChild(frag);
                }
            }
            else {
                el.innerHTML = html;
            }
            return el[rangeEl];
        }

        throw 'Illegal insertion point -> "' + where + '"';
    },

    
    insertBefore : function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforebegin');
    },

    
    insertAfter : function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterend', 'nextSibling');
    },

    
    insertFirst : function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterbegin', 'firstChild');
    },

    
    append : function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforeend', '', true);
    },

    
    overwrite : function(el, o, returnElement) {
        el = Ext.getDom(el);
        el.innerHTML = this.markup(o);
        return returnElement ? Ext.get(el.firstChild) : el.firstChild;
    },

    doInsert : function(el, o, returnElement, pos, sibling, append) {
        var newNode = this.insertHtml(pos, Ext.getDom(el), this.markup(o));
        return returnElement ? Ext.get(newNode, true) : newNode;
    }
};


Ext.DomQuery = {
    
    select : function(q, root) {
        var results = [],
            nodes,
            i,
            j,
            qlen,
            nlen;

        root = root || document;
        if (typeof root == 'string') {
            root = document.getElementById(root);
        }

        q = q.split(",");
        for (i = 0, qlen = q.length; i < qlen; i++) {
            if (typeof q[i] == 'string') {
                nodes = root.querySelectorAll(q[i]);

                for (j = 0, nlen = nodes.length; j < nlen; j++) {
                    results.push(nodes[j]);
                }
            }
        }

        return results;
    },

    
    selectNode : function(q, root) {
        return Ext.DomQuery.select(q, root)[0];
    },

    
    is : function(el, q) {
        if (typeof el == "string") {
            el = document.getElementById(el);
        }
        return Ext.DomQuery.select(q).indexOf(el) !== -1;
    }
};

Ext.Element.selectorFunction = Ext.DomQuery.select;
Ext.query = Ext.DomQuery.select;


Ext.Anim = Ext.extend(Object, {
    isAnim: true,
    
    
    disableAnimations: false,


    defaultConfig: {
        
        from: {},

        
        to: {},

        
        duration: 250,

        
        delay: 0,

        
        easing: 'ease-in-out',

        
        autoClear: true,

        
        out: true,

        
        direction: null,

        
        reverse: false
    },

    

    

    opposites: {
        'left': 'right',
        'right': 'left',
        'up': 'down',
        'down': 'up'
    },

    constructor: function(config) {
        config = Ext.apply({}, config || {}, this.defaultConfig);
        this.config = config;

        Ext.Anim.superclass.constructor.call(this);

        this.running = {};
    },

    initConfig : function(el, runConfig) {
        var me = this,
            runtime = {},
            config = Ext.apply({}, runConfig || {}, me.config);

        config.el = el = Ext.get(el);

        if (config.reverse && me.opposites[config.direction]) {
            config.direction = me.opposites[config.direction];
        }

        if (me.config.before) {
            me.config.before.call(config, el, config);
        }

        if (runConfig.before) {
            runConfig.before.call(config.scope || config, el, config);
        }

        return config;
    },
    
    run: function(el, config) {
        el = Ext.get(el);
        config = config || {};


        var me = this,
            style = el.dom.style,
            property,
            after = config.after;

        if (me.running[el.id]) {
            me.onTransitionEnd(null, el, {
                config: config,
                after: after
            });
        }

        config = this.initConfig(el, config);

        if (this.disableAnimations) {
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = config.to[property];
            }
            this.onTransitionEnd(null, el, {
                config: config,
                after: after
            });
            return me;
        }

        el.un('webkitTransitionEnd', me.onTransitionEnd, me);

        style.webkitTransitionDuration = '0ms';
        for (property in config.from) {
            if (!config.from.hasOwnProperty(property)) {
                continue;
            }
            style[property] = config.from[property];
        }

        setTimeout(function() {
            
            if (!el.dom) {
                return;
            }
            
            
            if (config.is3d === true) {
                el.parent().setStyle({
                    
                    '-webkit-perspective': '1200',
                    '-webkit-transform-style': 'preserve-3d'
                });
            }

            style.webkitTransitionDuration = config.duration + 'ms';
            style.webkitTransitionProperty = 'all';
            style.webkitTransitionTimingFunction = config.easing;

            
            el.on('webkitTransitionEnd', me.onTransitionEnd, me, {
                config: config,
                after: after
            });

            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = config.to[property];
            }
        }, config.delay || 5);

        me.running[el.id] = config;
        return me;
    },

    onTransitionEnd: function(ev, el, o) {
        el = Ext.get(el);

        if (this.running[el.id] === undefined) {
            return;
        }

        var style = el.dom.style,
            config = o.config,
            property,
            me = this;
            
        el.un('webkitTransitionEnd', me.onTransitionEnd, me);

        if (config.autoClear) {
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = '';
            }
        }

        style.webkitTransitionDuration = null;
        style.webkitTransitionProperty = null;
        style.webkitTransitionTimingFunction = null;

        if (config.is3d) {
            el.parent().setStyle({
                '-webkit-perspective': '',
                '-webkit-transform-style': ''
            });
        }

        if (me.config.after) {
            me.config.after.call(config, el, config);
        }

        if (o.after) {
            o.after.call(config.scope || me, el, config);
        }

        delete me.running[el.id];
    }
});

Ext.Anim.seed = 1000;


Ext.Anim.run = function(el, anim, config) {
    if (el.isComponent) {
        el = el.el;
    }

    config = config || {};

    if (anim.isAnim) {
        anim.run(el, config);
    }
    else {
        if (Ext.isObject(anim)) {
            if (config.before && anim.before) {
                config.before = Ext.createInterceptor(config.before, anim.before, anim.scope);
            }
            if (config.after && anim.after) {
                config.after = Ext.createInterceptor(config.after, anim.after, anim.scope);
            }
            config = Ext.apply({}, config, anim);
            anim = anim.type;
        }

        if (!Ext.anims[anim]) {
            throw anim + ' is not a valid animation type.';
        }
        else {
            
            if (el && el.dom) {
                Ext.anims[anim].run(el, config);
            }

        }
    }
};


Ext.anims = {
    
    fade: new Ext.Anim({
        before: function(el) {
            var fromOpacity = 1,
                toOpacity = 1,
                curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                zIndex = curZ;

            if (this.out) {
                toOpacity = 0;
            } else {
                zIndex = curZ + 1;
                fromOpacity = 0;
            }

            this.from = {
                'opacity': fromOpacity,
                'z-index': zIndex
            };
            this.to = {
                'opacity': toOpacity,
                'z-index': zIndex
            };
        }
    }),

    
    slide: new Ext.Anim({
        direction: 'left',
        cover: false,
        reveal: false,

        before: function(el) {
            var curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                zIndex = curZ + 1,
                toX = 0,
                toY = 0,
                fromX = 0,
                fromY = 0,
                elH = el.getHeight(),
                elW = el.getWidth();

            if (this.direction == 'left' || this.direction == 'right') {
                if (this.out == true) {
                    toX = -elW;
                }
                else {
                    fromX = elW;
                }
            }
            else if (this.direction == 'up' || this.direction == 'down') {
                if (this.out == true) {
                    toY = -elH;
                }
                else {
                    fromY = elH;
                }
            }

            if (this.direction == 'right' || this.direction == 'down') {
                toY *= -1;
                toX *= -1;
                fromY *= -1;
                fromX *= -1;
            }

            if (this.cover && this.out) {
                toX = 0;
                toY = 0;
                zIndex = curZ;
            }
            else if (this.reveal && !this.out) {
                fromX = 0;
                fromY = 0;
                zIndex = curZ;
            }

            this.from = {
                '-webkit-transform': 'translate3d(' + fromX + 'px, ' + fromY + 'px, 0)',
                'z-index': zIndex,
                'opacity': 0.99
            };
            this.to = {
                '-webkit-transform': 'translate3d(' + toX + 'px, ' + toY + 'px, 0)',
                'z-index': zIndex,
                'opacity': 1
            };
        }
    }),

    
    pop: new Ext.Anim({
        scaleOnExit: true,
        before: function(el) {
            var fromScale = 1,
                toScale = 1,
                fromOpacity = 1,
                toOpacity = 1,
                curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                fromZ = curZ,
                toZ = curZ;

            if (!this.out) {
                fromScale = 0.01;
                fromZ = curZ + 1;
                toZ = curZ + 1;
                fromOpacity = 0;
            }
            else {
                if (this.scaleOnExit) {
                    toScale = 0.01;
                    toOpacity = 0;
                } else {
                    toOpacity = 0.8;
                }
            }

            this.from = {
                '-webkit-transform': 'scale(' + fromScale + ')',
                '-webkit-transform-origin': '50% 50%',
                'opacity': fromOpacity,
                'z-index': fromZ
            };

            this.to = {
                '-webkit-transform': 'scale(' + toScale + ')',
                '-webkit-transform-origin': '50% 50%',
                'opacity': toOpacity,
                'z-index': toZ
            };
        }
    })
};

Ext.apply(Ext.anims, {
    
    flip: new Ext.Anim({
        is3d: true,
        direction: 'left',
        before: function(el) {
            var rotateProp = 'Y',
                fromScale = 1,
                toScale = 1,
                fromRotate = 0,
                toRotate = 0;

            if (this.out) {
                toRotate = -180;
                toScale = 0.8;
            }
            else {
                fromRotate = 180;
                fromScale = 0.8;
            }

            if (this.direction == 'up' || this.direction == 'down') {
                rotateProp = 'X';
            }

            if (this.direction == 'right' || this.direction == 'left') {
                toRotate *= -1;
                fromRotate *= -1;
            }

            this.from = {
                '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg) scale(' + fromScale + ')',
                '-webkit-backface-visibility': 'hidden'
            };
            this.to = {
                '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) scale(' + toScale + ')',
                '-webkit-backface-visibility': 'hidden'
            };
        }
    }),
    
    
    cube: new Ext.Anim({
        is3d: true,
        direction: 'left',
        style: 'outer',
        before: function(el) {
            var origin = '0% 0%',
                fromRotate = 0,
                toRotate = 0,
                rotateProp = 'Y',
                fromZ = 0,
                toZ = 0,
                fromOpacity = 1,
                toOpacity = 1,
                zDepth,
                elW = el.getWidth(),
                elH = el.getHeight(),
                showTranslateZ = true,
                fromTranslate = ' translateX(0)',
                toTranslate = '';

            if (this.direction == 'left' || this.direction == 'right') {
                if (this.out) {
                    origin = '100% 100%';
                    toZ = elW;
                    toOpacity = 0.5;
                    toRotate = -90;
                } else {
                    origin = '0% 0%';
                    fromZ = elW;
                    fromOpacity = 0.5;
                    fromRotate = 90;
                }
            } else if (this.direction == 'up' || this.direction == 'down') {
                rotateProp = 'X';
                if (this.out) {
                    origin = '100% 100%';
                    toZ = elH;
                    toRotate = 90;
                } else {
                    origin = '0% 0%';
                    fromZ = elH;
                    fromRotate = -90;
                }
            }

            if (this.direction == 'down' || this.direction == 'right') {
                fromRotate *= -1;
                toRotate *= -1;
                origin = (origin == '0% 0%') ? '100% 100%': '0% 0%';
            }

            if (this.style == 'inner') {
                fromZ *= -1;
                toZ *= -1;
                fromRotate *= -1;
                toRotate *= -1;

                if (!this.out) {
                    toTranslate = ' translateX(0px)';
                    origin = '0% 50%';
                } else {
                    toTranslate = fromTranslate;
                    origin = '100% 50%';
                }
            }

            this.from = {
                '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg)' + (showTranslateZ ? ' translateZ(' + fromZ + 'px)': '') + fromTranslate,
                '-webkit-transform-origin': origin
            };
            this.to = {
                '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg) translateZ(' + toZ + 'px)' + toTranslate,
                '-webkit-transform-origin': origin
            };
        },
        duration: 250
    }),
    
    
    
    wipe: new Ext.Anim({
        before: function(el) {
            var curZ = el.getStyle('z-index'),
                mask = '',
                toSize = '100%',
                fromSize = '100%';

            if (!this.out) {
                zIndex = curZ + 1;
                mask = '-webkit-gradient(linear, left bottom, right bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                toSize = el.getHeight() * 100 + 'px';
                fromSize = el.getHeight();

                this.from = {
                    '-webkit-mask-image': mask,
                    '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
                    'z-index': zIndex,
                    '-webkit-mask-position-x': 0
                };
                this.to = {
                    '-webkit-mask-image': mask,
                    '-webkit-mask-size': el.getWidth() * 3 + 'px ' + el.getHeight() + 'px',
                    'z-index': zIndex,
                    '-webkit-mask-position-x': -el.getWidth() * 2 + 'px'
                };
            }
        },
        duration: 500
    })
});


Ext.apply(Ext, {
    
    version : '1.1.1',
    versionDetail : {
        major : 1,
        minor : 1,
        patch : 1
    },
    
    
    setup: function(config) {
        if (config && typeof config == 'object') {
            if (config.addMetaTags !== false) {
                this.addMetaTags(config);
            }

            if (Ext.isFunction(config.onReady)) {
                var me = this;

                Ext.onReady(function() {
                    var args = arguments;

                    if (config.fullscreen !== false) {
                        Ext.Viewport.init(function() {
                            config.onReady.apply(me, args);
                        });
                    }
                    else {
                        config.onReady.apply(this, args);
                    }
                }, config.scope);
            }
        }
    },
    
     
    getDom : function(el) {
        if (!el || !document) {
            return null;
        }

        return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
    },
    
    
    removeNode : function(node) {
        if (node && node.parentNode && node.tagName != 'BODY') {
            Ext.EventManager.removeAll(node);
            node.parentNode.removeChild(node);
            delete Ext.cache[node.id];
        }
    },
    
    
    addMetaTags: function(config) {
        if (!Ext.isObject(config)) {
            return;
        }
        
        var head = Ext.get(document.getElementsByTagName('head')[0]),
            tag, precomposed;

        
        if (!Ext.is.Desktop) {
            tag = Ext.get(document.createElement('meta'));
            tag.set({
                name: 'viewport',
                content: 'width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0;'
            });
            head.appendChild(tag);                    
        }
        
        
        if (Ext.is.iOS) {
                            
            if (config.fullscreen !== false) {
                tag = Ext.get(document.createElement('meta'));
                tag.set({
                    name: 'apple-mobile-web-app-capable',
                    content: 'yes'
                });
                head.appendChild(tag);

                if (Ext.isString(config.statusBarStyle)) {
                    tag = Ext.get(document.createElement('meta'));
                    tag.set({
                        name: 'apple-mobile-web-app-status-bar-style',
                        content: config.statusBarStyle
                    });
                    head.appendChild(tag);
                }
            }
            
            
            if (config.tabletStartupScreen && Ext.is.iPad) {
                tag = Ext.get(document.createElement('link'));
                tag.set({
                    rel: 'apple-touch-startup-image',
                    href: config.tabletStartupScreen
                }); 
                head.appendChild(tag);                  
            }
            
            if (config.phoneStartupScreen && !Ext.is.iPad) {
                tag = Ext.get(document.createElement('link'));
                tag.set({
                    rel: 'apple-touch-startup-image',
                    href: config.phoneStartupScreen
                });
                head.appendChild(tag);
            }
            
            
            if (config.icon) {
                config.phoneIcon = config.tabletIcon = config.icon;
            }
            
            precomposed = (config.glossOnIcon === false) ? '-precomposed' : '';
            if (Ext.is.iPad && Ext.isString(config.tabletIcon)) {
                tag = Ext.get(document.createElement('link'));
                tag.set({
                    rel: 'apple-touch-icon' + precomposed,
                    href: config.tabletIcon
                });
                head.appendChild(tag);
            } 
            else if (!Ext.is.iPad && Ext.isString(config.phoneIcon)) {
                tag = Ext.get(document.createElement('link'));
                tag.set({
                    rel: 'apple-touch-icon' + precomposed,
                    href: config.phoneIcon
                });
                head.appendChild(tag);
            }
        }
    }
});


(function() {
    var initExt = function() {
        
        var bd = Ext.getBody(),
            cls = [];
        if (!bd) {
            return false;
        }
        var Is = Ext.is; 
        if (Is.Phone) {
            cls.push('x-phone');
        }
        else if (Is.Tablet) {
            cls.push('x-tablet');
        }
        else if (Is.Desktop) {
            cls.push('x-desktop');
        }
        if (Is.iPad) {
            cls.push('x-ipad');
        }
        if (Is.iOS) {
            cls.push('x-ios');
        }
        if (Is.Android) {
            cls.push('x-android', 'x-android-' + Is.AndroidMajorVersion);
        }
        if (Is.Blackberry) {
            cls.push('x-bb');
        }
        if (Is.Standalone) {
            cls.push('x-standalone');
        }
        if (cls.length) {
            bd.addCls(cls);
        }
        return true;
    };

    if (!initExt()) {
        Ext.onReady(initExt);
    }
})();



Ext.Viewport = new (Ext.extend(Ext.util.Observable, {
    constructor: function() {
        var me = this;

        this.addEvents(
            'orientationchange',
            'resize'
        );

        this.stretchSizes = {};

        if (Ext.supports.OrientationChange) {
            window.addEventListener('orientationchange', Ext.createDelegate(me.onOrientationChange, me), false);
        }
        else {
            window.addEventListener('resize', Ext.createDelegate(me.onResize, me), false);
        }

        if (!Ext.desktop) {
            document.addEventListener('touchstart', Ext.createDelegate(me.onTouchStartCapturing, me), true);
        }
    },

    init: function(fn, scope) {
        var me = this,
            stretchSize = Math.max(window.innerHeight, window.innerWidth) * 2,
            body = Ext.getBody();

        me.updateOrientation();

        this.initialHeight = window.innerHeight;
        this.initialOrientation = this.orientation;

        body.setHeight(stretchSize);

        Ext.gesture.Manager.freeze();

        this.scrollToTop();
        
        
        
        setTimeout(function() {
            me.scrollToTop();
            setTimeout(function() {
                me.scrollToTop();
                me.initialHeight = Math.max(me.initialHeight, window.innerHeight);

                if (fn) {
                    fn.apply(scope || window);
                }

                me.updateBodySize();

                Ext.gesture.Manager.thaw();
            }, 500);
        }, 500);

    },

    scrollToTop: function() {
        if (Ext.is.iOS) {
            if (Ext.is.Phone) {
                document.body.scrollTop = document.body.scrollHeight;
            }
        }
        else if (Ext.is.Blackberry) {
            window.scrollTo(0, 1000);
        }
        else {
            window.scrollTo(0, 1);
        }
    },

    updateBodySize: function() {
        Ext.getBody().setSize(window.innerWidth, window.innerHeight);
    },

    updateOrientation: function() {
        this.lastSize = this.getSize();
        this.orientation = this.getOrientation();
    },

    onTouchStartCapturing: function(e) {
        if (!Ext.currentlyFocusedField && Ext.is.iOS) {
            this.scrollToTop();
        }
    },

    onOrientationChange: function() {
        var me = this,
            body = Ext.getBody();

        if (!Ext.is.Phone) {
            body.setHeight(body.getWidth());

            this.updateOrientation();

            this.fireEvent('orientationchange', this, this.orientation);
            me.scrollToTop();
            me.updateBodySize();
            me.fireResizeEvent();
            Ext.repaint();

            return;
        }

        Ext.gesture.Manager.freeze();

        body.setHeight(body.getWidth());

        this.updateOrientation();

        this.fireEvent('orientationchange', this, this.orientation);

        setTimeout(function() {
            me.scrollToTop();

            setTimeout(function() {
                me.updateBodySize();
                me.fireResizeEvent();

                Ext.gesture.Manager.thaw();

                Ext.repaint();
            }, 200);
        }, 200);
    },

    fireResizeEvent: function() {
        var me = this;

        if (!Ext.is.iOS) {
            if (this.resizeEventTimer) {
                clearTimeout(this.resizeEventTimer);
            }

            this.resizeEventTimer = setTimeout(function() {
                me.fireEvent('resize', me, me.getSize());
            }, 500);
        } else {
            me.fireEvent('resize', me, me.getSize());
        }
    },

    onResize: function() {
        if (this.orientation != this.getOrientation()) {
            this.onOrientationChange();
        } else {
            var size = this.getSize();

            if (!Ext.is.iOS && !Ext.is.Desktop) {
                if ((size.width == this.lastSize.width && size.height > this.lastSize.height) ||
                    (size.height == this.lastSize.height && size.width > this.lastSize.width)) {
                    this.fireEvent('resize', this, size);
                }
            } else {
                this.fireEvent('resize', this, size);
            }
        }
    },

    getSize: function() {
        var size = {
            width: window.innerWidth,
            height: window.innerHeight
        };

        if (!Ext.is.Desktop) {
            size.height = (this.orientation == this.initialOrientation) ?
                            Math.max(this.initialHeight, size.height) :
                            size.height;
        }

        return size;
    },

    getOffset: function() {
        return {
            x: window.pageXOffset,
            y: window.pageYOffset
        };
    },

    getOrientation: function() {
        var me = this,
            size = me.getSize(),
            androidTablet, orientation;

        if (window.hasOwnProperty('orientation')) {
            orientation = window.orientation;
            
            androidTablet = Ext.is.Android && Ext.is.AndroidMajorVersion === 3;
            if (orientation % 180 === 0) {
                return androidTablet ? 'landscape' : 'portrait';
            }
            else {
                return androidTablet ? 'portrait' : 'landscape';
            }
        }
        else {
            if (!Ext.is.iOS && !Ext.is.Desktop) {
                if ((size.width == me.lastSize.width && size.height < me.lastSize.height) ||
                    (size.height == me.lastSize.height && size.width < me.lastSize.width)) {
                    return me.orientation;
                }
            }

            return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
        }

    }

}));


Ext.util.TapRepeater = Ext.extend(Ext.util.Observable, {

    constructor: function(el, config) {
        this.el = Ext.get(el);

        Ext.apply(this, config);

        this.addEvents(
        
        "touchstart",
        
        "tap",
        
        "touchend"
        );

        this.el.on({
            touchstart: this.onTouchStart,
            touchend: this.onTouchEnd,
            scope: this
        });

        if (this.preventDefault || this.stopDefault) {
            this.el.on('tap', this.eventOptions, this);
        }

        Ext.util.TapRepeater.superclass.constructor.call(this);
    },

    interval: 10,
    delay: 250,
    preventDefault: true,
    stopDefault: false,
    timer: 0,

    
    eventOptions: function(e) {
        if (this.preventDefault) {
            e.preventDefault();
        }
        if (this.stopDefault) {
            e.stopEvent();
        }
    },

    
    destroy: function() {
        Ext.destroy(this.el);
        this.clearListeners();
    },

    
    onTouchStart: function(e) {
        clearTimeout(this.timer);
        if (this.pressClass) {
            this.el.addCls(this.pressClass);
        }
        this.tapStartTime = new Date();

        this.fireEvent("touchstart", this, e);
        this.fireEvent("tap", this, e);

        
        if (this.accelerate) {
            this.delay = 400;
        }
        this.timer = Ext.defer(this.tap, this.delay || this.interval, this, [e]);
    },

    
    tap: function(e) {
        this.fireEvent("tap", this, e);
        this.timer = Ext.defer(this.tap, this.accelerate ? this.easeOutExpo(Ext.util.Date.getElapsed(this.tapStartTime),
            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;
    },

    
    onTouchEnd: function(e) {
        clearTimeout(this.timer);
        this.el.removeCls(this.pressClass);
        this.fireEvent("touchend", this, e);
    }
});











if (!this.JSON) {
    this.JSON = {};
}

(function () {

    function f(n) {
        
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf()) ?
                   this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z' : null;
        };

        String.prototype.toJSON =
        Number.prototype.toJSON =
        Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {






        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    }


    function str(key, holder) {



        var i,          
            k,          
            v,          
            length,
            mind = gap,
            partial,
            value = holder[key];



        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }




        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }



        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':



            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':





            return String(value);




        case 'object':




            if (!value) {
                return 'null';
            }



            gap += indent;
            partial = [];



            if (Object.prototype.toString.apply(value) === '[object Array]') {




                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }




                v = partial.length === 0 ? '[]' :
                    gap ? '[\n' + gap +
                            partial.join(',\n' + gap) + '\n' +
                                mind + ']' :
                          '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }



            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {



                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }




            v = partial.length === 0 ? '{}' :
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                        mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
        return v;
    }



    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {







            var i;
            gap = '';
            indent = '';




            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }



            } else if (typeof space === 'string') {
                indent = space;
            }




            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                     typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }




            return str('', {'': value});
        };
    }




    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {




            var j;

            function walk(holder, key) {




                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }






            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }














            if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {






                j = eval('(' + text + ')');




                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }



            throw new SyntaxError('JSON.parse');
        };
    }
}());


Ext.util.JSON = {
    encode : function(o) {
        return JSON.stringify(o);
    },

    decode : function(s) {
        return JSON.parse(s);
    }
};


Ext.encode = Ext.util.JSON.encode;

Ext.decode = Ext.util.JSON.decode;

Ext.util.JSONP = {

    
    queue: [],

    
    current: null,

    
    request : function(o) {
        o = o || {};
        if (!o.url) {
            return;
        }

        var me = this;
        o.params = o.params || {};
        if (o.callbackKey) {
            o.params[o.callbackKey] = 'Ext.util.JSONP.callback';
        }
        var params = Ext.urlEncode(o.params);

        var script = document.createElement('script');
        script.type = 'text/javascript';

        this.queue.push({
            url: o.url,
            script: script,
            callback: o.callback || function(){},
            scope: o.scope || window,
            params: params || null
        });

        if (!this.current) {
            this.next();
        }
    },

    
    next : function() {
        this.current = null;
        if (this.queue.length) {
            this.current = this.queue.shift();
            this.current.script.src = this.current.url + (this.current.params ? ('?' + this.current.params) : '');
            document.getElementsByTagName('head')[0].appendChild(this.current.script);
        }
    },

    
    callback: function(json) {
        this.current.callback.call(this.current.scope, json);
        document.getElementsByTagName('head')[0].removeChild(this.current.script);
        this.next();
    }
};


Ext.util.Draggable = Ext.extend(Ext.util.Observable, {
    baseCls: 'x-draggable',

    draggingCls: 'x-dragging',

    proxyCls: 'x-draggable-proxy',

    
    outOfBoundRestrictFactor: 1,

    
    direction: 'both',

    fps: Ext.is.Blackberry ? 25 : ((Ext.is.iOS || Ext.is.Desktop) ? 80 : 50),

    
    constrain: window,

    
    threshold: 0,

    
    delay: 0,

    
    cancelSelector: null,

    
    disabled: false,

    
    revert: false,

    
    group: 'base',

    

    
    useCssTransform: true,

    
    grid: null,
    snap: null,
    proxy: null,
    stack: false,

    
    animationDuration: 300,

    
    updateBoundaryOnTouchStart: true,

    
    
    offsetBoundary: null,

    
    dragging: false,

    
    vertical: false,

    
    horizontal: false,

    
    monitorOrientation: true,

    constructor: function(el, config) {
        this.el = Ext.get(el);
        this.id = el.id;

        config = config || {};

        Ext.apply(this, config);

        this.addEvents(
            
            'offsetchange',

            'offsetboundaryupdate'
        );

        Ext.util.Draggable.superclass.constructor.call(this, config);

        if (this.eventTarget === 'parent') {
            this.eventTarget = this.el.parent();
        } else {
            this.eventTarget = (this.eventTarget) ? Ext.get(this.eventTarget) : this.el;
        }

        if (this.direction == 'both') {
            this.horizontal = true;
            this.vertical = true;
        }
        else if (this.direction == 'horizontal') {
            this.horizontal = true;
        }
        else {
            this.vertical = true;
        }

        this.el.addCls(this.baseCls);

        if (this.proxy) {
            this.getProxyEl().addCls(this.proxyCls);
        }

        this.startEventName = (this.delay > 0) ? 'taphold' : 'dragstart';
        this.dragOptions = (this.delay > 0) ? {holdThreshold: this.delay} : {
            direction: this.direction,
            dragThreshold: this.threshold
        };

        this.container = window;

        if (this.constrain) {
            if (this.constrain === 'parent') {
                this.container = this.el.parent();
            }
            else if (this.constrain !== window) {
                this.container = Ext.get(this.constrain);
            }
        }

        this.offset = new Ext.util.Offset();

        this.linearAnimation = {
            x: new Ext.util.Draggable.Animation.Linear(),
            y: new Ext.util.Draggable.Animation.Linear()
        };

        this.updateBoundary(true);
        this.setDragging(false);

        if (!this.disabled) {
            this.enable();
        }

        return this;
    },

    
    enable: function() {
        return this.setEnabled(true);
    },

    
    disable: function() {
        return this.setEnabled(false);
    },

    
    setEnabled: function(enabled) {
        this.eventTarget[enabled ? 'on' : 'un'](this.startEventName, this.onStart, this, this.dragOptions);
        this.eventTarget[enabled ? 'on' : 'un']('drag', this.onDrag, this, this.dragOptions);
        this.eventTarget[enabled ? 'on' : 'un']('dragend', this.onDragEnd, this, this.dragOptions);
        this.eventTarget[enabled ? 'on' : 'un']('touchstart', this.onTouchStart, this);

        if (enabled) {
            Ext.EventManager.onOrientationChange(this.onOrientationChange, this);
        } else {
            Ext.EventManager.orientationEvent.removeListener(this.onOrientationChange, this);
        }

        this.disabled = !enabled;

        return this;
    },

    
    setUseCssTransform: function(useCssTransform) {
        if (typeof useCssTransform == 'undefined') {
            useCssTransform = true;
        }

        if (useCssTransform != this.useCssTransform) {
            this.useCssTransform = useCssTransform;

            var resetOffset = new Ext.util.Offset();

            if (useCssTransform == false) {
                this.setStyleOffset(this.offset);
                this.setTransformOffset(resetOffset, true);
            } else {
                this.setTransformOffset(this.offset);
                this.setStyleOffset(resetOffset);
            }
        }

        return this;
    },

    
    setOffset: function(offset, animate) {
        if (!this.horizontal) {
            offset.x = 0;
        }

        if (!this.vertical) {
            offset.y = 0;
        }

        if (!(offset instanceof Ext.util.Offset)) {
            offset = Ext.util.Offset.fromObject(offset);
        }

        offset.round();

        if (!this.offset.equals(offset)) {
            if (animate) {
                this.startAnimation(offset, animate);
            }
            else {
                this.offset = offset;
                this.region = new Ext.util.Region(
                    this.initialRegion.top + offset.y,
                    this.initialRegion.right + offset.x,
                    this.initialRegion.bottom + offset.y,
                    this.initialRegion.left + offset.x
                );

                if (this.useCssTransform) {
                    this.setTransformOffset(offset);
                }
                else {
                    this.setStyleOffset(offset);
                }

                this.fireEvent('offsetchange', this, this.offset);
            }
        }


        return this;
    },

    
    setTransformOffset: function(offset, clean) {

        
        if (clean) {
            this.getProxyEl().dom.style.webkitTransform = '';
        } else {
            Ext.Element.cssTranslate(this.getProxyEl(), offset);
        }

        return this;
    },

    
    setStyleOffset: function(offset) {
        var el = this.getProxyEl();

        el.dom.style.left = offset.x + 'px';
        el.dom.style.top = offset.y + 'px';

        return this;
    },

    
    startAnimation: function(offset, animate) {
        var me = this;

        this.stopAnimation();

        var currentTime = Date.now();
        animate = Ext.isNumber(animate) ? animate : this.animationDuration;

        this.linearAnimation.x.set({
            startOffset: this.offset.x,
            endOffset: offset.x,
            startTime: currentTime,
            duration: animate
        });

        this.linearAnimation.y.set({
            startOffset: this.offset.y,
            endOffset: offset.y,
            startTime: currentTime,
            duration: animate
        });

        this.isAnimating = true;

        this.animationTimer = setInterval(function(){
            me.handleAnimationFrame();
        }, this.getFrameDuration());
        return this;
    },

    
    getFrameDuration: function() {
        return 1000 / this.fps;
    },

    
    stopAnimation: function() {
        if (this.isAnimating) {
            clearInterval(this.animationTimer);
            this.isAnimating = false;
            this.setDragging(false);
        }

        return this;
    },

    
    handleAnimationFrame: function() {
        if (!this.isAnimating) {
            return;
        }

        var newOffset = new Ext.util.Offset();
        newOffset.x = this.linearAnimation.x.getOffset();
        newOffset.y = this.linearAnimation.y.getOffset();

        this.setOffset(newOffset);

        if ((newOffset.x === this.linearAnimation.x.endOffset) && (newOffset.y === this.linearAnimation.y.endOffset)) {
            this.stopAnimation();
        }
    },

    
    getOffset: function() {
        var offset = this.offset.copy();
        offset.y = -offset.y;
        offset.x = -offset.x;
        return offset;
    },

    
    updateBoundary: function(init) {
        var offsetBoundary;

        if (typeof init == 'undefined')
            init = false;

        this.size = {
            width: this.el.dom.scrollWidth,
            height: this.el.dom.scrollHeight
        };

        if (this.container === window) {
            this.containerBox = {
                left: 0,
                top: 0,
                right: this.container.innerWidth,
                bottom: this.container.innerHeight,
                width: this.container.innerWidth,
                height: this.container.innerHeight
            };
        }
        else {
            this.containerBox = this.container.getPageBox();
        }

        var elXY = this.el.getXY();

        this.elBox = {
            left: elXY[0] - this.offset.x,
            top: elXY[1] - this.offset.y,
            width: this.size.width,
            height: this.size.height
        };

        this.elBox.bottom = this.elBox.top + this.elBox.height;
        this.elBox.right = this.elBox.left + this.elBox.width;

        this.initialRegion = this.region = new Ext.util.Region(
            elXY[1], elXY[0] + this.elBox.width, elXY[1] + this.elBox.height, elXY[0]
        );

        var top = 0,
            right = 0,
            bottom = 0,
            left = 0;

        if (this.elBox.left < this.containerBox.left) {
            right += this.containerBox.left - this.elBox.left;
        }
        else {
            left -= this.elBox.left - this.containerBox.left;
        }

        if (this.elBox.right > this.containerBox.right) {
            left -= this.elBox.right - this.containerBox.right;
        }
        else {
            right += this.containerBox.right - this.elBox.right;
        }

        if (this.elBox.top < this.containerBox.top) {
            bottom += this.containerBox.top - this.elBox.top;
        }
        else {
            top -= this.elBox.top - this.containerBox.top;
        }

        if (this.elBox.bottom > this.containerBox.bottom) {
            top -= this.elBox.bottom - this.containerBox.bottom;
        }
        else {
            bottom += this.containerBox.bottom - this.elBox.bottom;
        }

        offsetBoundary = new Ext.util.Region(top, right, bottom, left).round();

        if (this.offsetBoundary && this.offsetBoundary.equals(offsetBoundary)) {
            return this;
        }

        this.offsetBoundary = offsetBoundary;

        this.fireEvent('offsetboundaryupdate', this, this.offsetBoundary);

        var currentComputedOffset;

        if (this.useCssTransform) {
            currentComputedOffset = Ext.Element.getComputedTransformOffset(this.getProxyEl());



            if (!this.offset.equals(currentComputedOffset) || init) {
                this.setOffset(currentComputedOffset);
            }
        }

        return this;
    },

    
    onTouchStart: function() {

    },

    
    onStart: function(e) {
        if (this.updateBoundaryOnTouchStart) {
            this.updateBoundary();
        }

        this.stopAnimation();

        this.setDragging(true);
        this.startTouchPoint = new Ext.util.Point(e.startX, e.startY);

        this.startOffset = this.offset.copy();

        this.fireEvent('dragstart', this, e);

        return true;
    },

    
    getNewOffsetFromTouchPoint: function(touchPoint) {
        var xDelta = touchPoint.x - this.startTouchPoint.x,
            yDelta = touchPoint.y - this.startTouchPoint.y,
            newOffset = this.offset.copy();

        if(xDelta == 0 && yDelta == 0) {
            return newOffset;
        }

        if (this.horizontal)
            newOffset.x = this.startOffset.x + xDelta;

        if (this.vertical)
            newOffset.y = this.startOffset.y + yDelta;

        return newOffset;
    },

    
    onDrag: function(e) {
        if (!this.dragging) {
            return;
        }

        this.lastTouchPoint = Ext.util.Point.fromEvent(e);
        var newOffset = this.getNewOffsetFromTouchPoint(this.lastTouchPoint);

        if (this.offsetBoundary != null) {
            newOffset = this.offsetBoundary.restrict(newOffset, this.outOfBoundRestrictFactor);
        }

        this.setOffset(newOffset);

        this.fireEvent('drag', this, e);

        
        
        return true;
    },

    
    onDragEnd: function(e) {
        if (this.dragging) {
            this.fireEvent('beforedragend', this, e);

            if (this.revert && !this.cancelRevert) {
                this.setOffset(this.startOffset, true);
            } else {
                this.setDragging(false);
            }

            this.fireEvent('dragend', this, e);
        }

        
        
        return true;
    },

    
    onOrientationChange: function() {
        this.updateBoundary();
    },

    
    setDragging: function(dragging) {
        if (dragging) {
            if (!this.dragging) {
                this.dragging = true;
                this.getProxyEl().addCls(this.draggingCls);
            }
        } else {
            if (this.dragging) {
                this.dragging = false;
                this.getProxyEl().removeCls(this.draggingCls);
            }
        }

        return this;
    },

    
    getProxyEl: function() {
        return this.proxy || this.el;
    },

    
    destroy: function() {
        this.el.removeCls(this.baseCls);
        this.getProxyEl().removeCls(this.proxyCls);
        this.clearListeners();
        this.disable();
    },

    
    reset: function() {
        this.startOffset = new Ext.util.Offset(0, 0);
        this.setOffset(this.startOffset);

        var oldInitialRegion = this.initialRegion.copy();

        this.updateBoundary();
        this.initialRegion = this.region = this.getProxyEl().getPageBox(true);
        this.startTouchPoint.x += this.initialRegion.left - oldInitialRegion.left;
        this.startTouchPoint.y += this.initialRegion.top - oldInitialRegion.top;
    },

    
    moveTo: function(x, y) {
        this.setOffset(new Ext.util.Offset(x - this.initialRegion.left, y - this.initialRegion.top));
        return this;
    },

    
    isDragging: function() {
        return this.dragging;
    },

    
    isVertical : function() {
        return this.vertical;
    },

    
    isHorizontal : function() {
        return this.horizontal;
    }
});

Ext.util.Draggable.Animation = {};


Ext.util.Draggable.Animation.Abstract = Ext.extend(Object, {
    
    startTime: null,

    
    startOffset: 0,

    
    constructor: function(config) {
        config = config || {};

        this.set(config);

        if (!this.startTime)
            this.startTime = Date.now();
    },

    
    set: function(name, value) {
        if (Ext.isObject(name)) {
            Ext.apply(this, name);
        }
        else {
            this[name] = value;
        }

        return this;
    },

    
    getOffset: Ext.emptyFn
});


Ext.util.Draggable.Animation.Linear = Ext.extend(Ext.util.Draggable.Animation.Abstract, {
    
    duration: 0,

    
    endOffset: 0,

    getOffset : function() {
        var distance = this.endOffset - this.startOffset,
            deltaTime = Date.now() - this.startTime,
            omegaTime = Math.min(1, (deltaTime / this.duration));

        return this.startOffset + (omegaTime * distance);
    }
});


Ext.util.Droppable = Ext.extend(Ext.util.Observable, {
    baseCls: 'x-droppable',
    
    activeCls: 'x-drop-active',
    
    invalidCls: 'x-drop-invalid',
    
    hoverCls: 'x-drop-hover',

    
    validDropMode: 'intersect',

    
    disabled: false,

    
    group: 'base',

    
    tolerance: null,


    
    monitoring: false,
    
    
    constructor : function(el, config) {
        config = config || {};
        Ext.apply(this, config);

        this.addEvents(
            
            'dropactivate',

            
            'dropdeactivate',

            
            'dropenter',

            
            'dropleave',

            
            'drop'
        );

        this.el = Ext.get(el);
        Ext.util.Droppable.superclass.constructor.call(this);

        if (!this.disabled) {
            this.enable();
        }

        this.el.addCls(this.baseCls);
    },

    
    onDragStart : function(draggable, e) {
        if (draggable.group === this.group) {
            this.monitoring = true;
            this.el.addCls(this.activeCls);
            this.region = this.el.getPageBox(true);

            draggable.on({
                drag: this.onDrag,
                beforedragend: this.onBeforeDragEnd,
                dragend: this.onDragEnd,
                scope: this
            });

            if (this.isDragOver(draggable)) {
                this.setCanDrop(true, draggable, e);
            }

            this.fireEvent('dropactivate', this, draggable, e);
        }
        else {
            draggable.on({
                dragend: function() {
                    this.el.removeCls(this.invalidCls);
                },
                scope: this,
                single: true
            });
            this.el.addCls(this.invalidCls);
        }
    },

    
    isDragOver : function(draggable, region) {
        return this.region[this.validDropMode](draggable.region);
    },

    
    onDrag : function(draggable, e) {
        this.setCanDrop(this.isDragOver(draggable), draggable, e);
    },

    
    setCanDrop : function(canDrop, draggable, e) {
        if (canDrop && !this.canDrop) {
            this.canDrop = true;
            this.el.addCls(this.hoverCls);
            this.fireEvent('dropenter', this, draggable, e);
        }
        else if (!canDrop && this.canDrop) {
            this.canDrop = false;
            this.el.removeCls(this.hoverCls);
            this.fireEvent('dropleave', this, draggable, e);
        }
    },

    
    onBeforeDragEnd: function(draggable, e) {
        draggable.cancelRevert = this.canDrop;
    },

    
    onDragEnd : function(draggable, e) {
        this.monitoring = false;
        this.el.removeCls(this.activeCls);

        draggable.un({
            drag: this.onDrag,
            beforedragend: this.onBeforeDragEnd,
            dragend: this.onDragEnd,
            scope: this
        });


        if (this.canDrop) {
            this.canDrop = false;
            this.el.removeCls(this.hoverCls);
            this.fireEvent('drop', this, draggable, e);
        }

        this.fireEvent('dropdeactivate', this, draggable, e);
    },

    
    enable : function() {
        if (!this.mgr) {
            this.mgr = Ext.util.Observable.observe(Ext.util.Draggable);
        }
        this.mgr.on({
            dragstart: this.onDragStart,
            scope: this
        });
        this.disabled = false;
    },

    
    disable : function() {
        this.mgr.un({
            dragstart: this.onDragStart,
            scope: this
        });
        this.disabled = true;
    },
    
    
    isDisabled : function() {
        return this.disabled;
    },
    
    
    isMonitoring : function() {
        return this.monitoring;
    }
});

(function(){

Ext.ScrollManager = new Ext.AbstractManager();


Ext.util.ScrollView = Ext.extend(Ext.util.Observable, {

    
    useIndicators: true,

    
    indicatorConfig: {},

    
    indicatorMargin: 4,

    constructor: function(el, config) {
        var indicators = [],
            directions = ['vertical', 'horizontal'];

        Ext.util.ScrollView.superclass.constructor.call(this);

        ['useIndicators', 'indicatorConfig', 'indicatorMargin'].forEach(function(c) {
            if (config.hasOwnProperty(c)) {
                this[c] = config[c];
                delete config[c];
            }
        }, this);

        config.scrollView = this;
        this.scroller = new Ext.util.Scroller(el, config);

        if (this.useIndicators === true) {
            directions.forEach(function(d) {
                if (this.scroller[d]) {
                    indicators.push(d);
                }
            }, this);
        } else if (directions.indexOf(this.useIndicators) !== -1) {
            indicators.push(this.useIndicators);
        }

        this.indicators = {};
        this.indicatorOffsetExtras = {};

        indicators.forEach(function(i) {
            this.indicators[i] = new Ext.util.Scroller.Indicator(this.scroller.container, Ext.apply({}, this.indicatorConfig, {type: i}));
        }, this);

        this.mon(this.scroller, {
            scrollstart: this.onScrollStart,
            scrollend: this.onScrollEnd,
            scroll: this.onScroll,
            scope: this
        });
    },

    
    onScrollStart: function() {
        this.showIndicators();
    },

    
    onScrollEnd: function() {
        this.hideIndicators();
    },

    
    onScroll: function(scroller) {
        if (scroller.offsetBoundary == null || (!this.indicators.vertical && !this.indicators.horizontal))
            return;

        var sizeAxis,
            offsetAxis,
            offsetMark,
            boundary = scroller.offsetBoundary,
            offset = scroller.offset;

        this.containerSize = scroller.containerBox;
        this.scrollerSize = scroller.size;
        this.outOfBoundOffset = boundary.getOutOfBoundOffset(offset);
        this.restrictedOffset = boundary.restrict(offset);
        this.boundarySize = boundary.getSize();

        if (!this.indicatorSizes) {
            this.indicatorSizes = {vertical: 0, horizontal: 0};
        }

        if (!this.indicatorOffsets) {
            this.indicatorOffsets = {vertical: 0, horizontal: 0};
        }

        Ext.iterate(this.indicators, function(axis, indicator) {
            sizeAxis = (axis == 'vertical') ? 'height' : 'width';
            offsetAxis = (axis == 'vertical') ? 'y' : 'x';
            offsetMark = (axis == 'vertical') ? 'bottom' : 'right';

            if (this.scrollerSize[sizeAxis] < this.containerSize[sizeAxis]) {
                this.indicatorSizes[axis] = this.containerSize[sizeAxis] * (this.scrollerSize[sizeAxis] / this.containerSize[sizeAxis]);
            }
            else {
                this.indicatorSizes[axis] = this.containerSize[sizeAxis] * (this.containerSize[sizeAxis] / this.scrollerSize[sizeAxis]);
            }
            this.indicatorSizes[axis] -= Math.abs(this.outOfBoundOffset[offsetAxis]);
            this.indicatorSizes[axis] = Math.max(this.indicatorMargin * 4, this.indicatorSizes[axis]);

            if (this.boundarySize[sizeAxis] != 0) {
                this.indicatorOffsets[axis] = (((boundary[offsetMark] - this.restrictedOffset[offsetAxis]) / this.boundarySize[sizeAxis])
                                              * (this.containerSize[sizeAxis] - this.indicatorSizes[axis]));
            } else if (offset[offsetAxis] < boundary[offsetMark]) {
                this.indicatorOffsets[axis] = this.containerSize[sizeAxis] - this.indicatorSizes[axis];
            } else {
                this.indicatorOffsets[axis] = 0;
            }

            indicator.setOffset(this.indicatorOffsetExtras[axis] + this.indicatorOffsets[axis] + this.indicatorMargin);
            indicator.setSize(this.indicatorSizes[axis] - (this.indicatorMargin * 2));
        }, this);
    },

    
    showIndicators : function() {
        Ext.iterate(this.indicators, function(axis, indicator) {
            indicator.show();
            this.indicatorOffsetExtras[axis] = indicator.el.dom.parentNode[axis === 'vertical' ? 'scrollTop' : 'scrollLeft'];
        }, this);

        return this;
    },

     
    hideIndicators : function() {
        Ext.iterate(this.indicators, function(axis, indicator) {
            indicator.hide();
        }, this);
    },

    
    destroy: function() {
        this.scroller.destroy();

        if (this.indicators) {
            Ext.iterate(this.indicators, function(axis, indicator) {
                indicator.destroy();
            }, this);
        }

        return Ext.util.ScrollView.superclass.destroy.apply(this, arguments);
    }
});


Ext.util.Scroller = Ext.extend(Ext.util.Draggable, {
    
    baseCls: '',

    
    draggingCls: '',

    
    direction: 'both',

    
    constrain: 'parent',

    
    outOfBoundRestrictFactor: 0.5,

    
    acceleration: 20,

    
    

    autoAdjustFps: false,

    
    friction: 0.5,

    
    startMomentumResetTime: 350,

    
    springTension: 0.3,

    
    minVelocityForAnimation: 1,

    
    bounces: true,

    
    momentum: true,

    cancelRevert: true,

    threshold: 5,

    constructor: function(el, config) {
        el = Ext.get(el);

        var scroller = Ext.ScrollManager.get(el.id);
        if (scroller) {
            return Ext.apply(scroller, config);
        }

        Ext.util.Scroller.superclass.constructor.apply(this, arguments);

        this.addEvents(
            
            'scrollstart',
            
            'scroll',
            
            'scrollend',
            
             'bouncestart',
             
             'bounceend'
        );

        this.on({
            dragstart: this.onDragStart,
            offsetchange: this.onOffsetChange,
            scope: this
        });

        Ext.ScrollManager.register(this);

        this.el.addCls('x-scroller');
        this.container.addCls('x-scroller-parent');

        if (this.bounces !== false) {
            var both = this.bounces === 'both' || this.bounces === true,
                horizontal = both || this.bounces === 'horizontal',
                vertical = both || this.bounces === 'vertical';

            this.bounces = {
                x: horizontal,
                y: vertical
            };
        }

        this.theta = Math.log(1 - (this.friction / 10));
        this.bouncingVelocityFactor = this.springTension * Math.E;
        this.bouncingTimeFactor = ((1 / this.springTension) * this.acceleration);

        if (!this.decelerationAnimation) {
            this.decelerationAnimation = {};
        }

        if (!this.bouncingAnimation) {
            this.bouncingAnimation = {};
        }

        ['x', 'y'].forEach(function(a) {
            if (!this.decelerationAnimation[a]) {
                this.decelerationAnimation[a] = new Ext.util.Scroller.Animation.Deceleration({
                    acceleration: this.acceleration,
                    theta: this.theta
                });
            }

            if (!this.bouncingAnimation[a]) {
                this.bouncingAnimation[a] = new Ext.util.Scroller.Animation.Bouncing({
                    acceleration: this.acceleration,
                    springTension: this.springTension
                });
            }
        }, this);

        return this;
    },

    
    updateBoundary: function(animate) {
        Ext.util.Scroller.superclass.updateBoundary.apply(this, arguments);

        this.snapToBoundary(animate);

        return this;
    },

    
    onOffsetChange: function(scroller, offset) {
        this.fireEvent('scroll', scroller, {
            x: -offset.x,
            y: -offset.y
        });
    },

    
    onTouchStart: function(e) {
        Ext.util.Scroller.superclass.onTouchStart.apply(this, arguments);

        this.stopMomentumAnimation();
    },

    
    onDragStart: function(e) {
        this.fireEvent('scrollstart', this, e);
    },

    
    setStartTime: function(e) {
        this.startTime = e.time;
        this.originalStartTime = (e.event.originalTimeStamp) ? e.event.originalTimeStamp : e.time;
    },

    
    onStart: function(e) {
        if (Ext.util.Scroller.superclass.onStart.apply(this, arguments) !== true)
            return;

        this.setStartTime(e);
        this.lastEventTime = e.time;
        this.startTimeOffset = this.offset.copy();
        this.isScrolling = true;

        this.momentumAnimationFramesHandled = 0;
    },

    
    onDrag: function(e) {
        if (Ext.util.Scroller.superclass.onDrag.apply(this, arguments) !== true)
            return;

        this.lastEventTime = e.time;

        if (this.lastEventTime - this.startTime > this.startMomentumResetTime) {
            this.setStartTime(e);
            this.startTimeOffset = this.offset.copy();
        }
    },

    
    onDragEnd: function(e) {
        if (Ext.util.Scroller.superclass.onDragEnd.apply(this, arguments) !== true)
            return;

        if (!this.startMomentumAnimation(e)) {
            this.fireScrollEndEvent();
        }
    },

    
    onOrientationChange: function() {
        Ext.util.Scroller.superclass.onOrientationChange.apply(this, arguments);

        this.snapToBoundary();
    },

    
    fireScrollEndEvent: function() {
        this.isScrolling = false;
        this.isMomentumAnimating = false;
        this.snapToBoundary();
        this.fireEvent('scrollend', this, this.getOffset());

        this.snapToSlot();
    },


    
    getLastActualFps: function() {
        var duration = (this.momentumAnimationEndTime - this.momentumAnimationStartTime - this.momentumAnimationProcessingTime) / 1000;
        return this.momentumAnimationFramesHandled / duration;
    },

    
    scrollTo: function(pos, animate) {
        this.stopMomentumAnimation();

        var newOffset = this.offsetBoundary.restrict(new Ext.util.Offset(-pos.x, -pos.y));

        this.setOffset(newOffset, animate);

        return this;
    },

    
    scrollBy: function(offset, animate) {
        this.stopMomentumAnimation();

        var newOffset = this.offset.copy();
        newOffset.x += offset.x;
        newOffset.y += offset.y;

        this.setOffset(newOffset, animate);

        return this;
    },

    
    setSnap: function(snap) {
        this.snap = snap;
    },

    
    snapToBoundary: function(animate) {
        var offset = this.offsetBoundary.restrict(this.offset);
        this.setOffset(offset, animate);

        return this;
    },

    snapToSlot: function() {
        var offset = this.offsetBoundary.restrict(this.offset);
        offset.round();

        if (this.snap) {
            if (this.snap === true) {
                this.snap = {
                    x: 50,
                    y: 50
                };
            }
            else if (Ext.isNumber(this.snap)) {
                this.snap = {
                    x: this.snap,
                    y: this.snap
                };
            }
            if (this.snap.y) {
                offset.y = Math.round(offset.y / this.snap.y) * this.snap.y;
            }
            if (this.snap.x) {
                offset.x = Math.round(offset.x / this.snap.x) * this.snap.x;
            }

            if (!this.offset.equals(offset)) {
                this.scrollTo({x: -offset.x, y: -offset.y}, this.snapDuration);
            }
        }
    },

    
    startMomentumAnimation: function(e) {
        var me = this,
            originalTime = (e.event.originalTimeStamp) ? e.event.originalTimeStamp : e.time,
            duration = Math.max(40, originalTime - this.originalStartTime);

        this.fireEvent('beforemomentumanimationstart');

        if (
            (!this.momentum || !(duration <= this.startMomentumResetTime)) &&
            !this.offsetBoundary.isOutOfBound(this.offset)
        ) {
            return false;
        }

        
        var minVelocity = this.minVelocityForAnimation,
            currentVelocity,
            currentOffset = this.offset.copy(),
            restrictedOffset,
            acceleration = (duration / this.acceleration);

        this.isBouncing = {x: false, y: false};
        this.isDecelerating = {x: false, y: false};
        this.momentumAnimationStartTime = e.time;
        this.momentumAnimationProcessingTime = 0;
        this.bouncingData = {x: null, y: null};

        
        this.momentumAnimationStartVelocity = {
            x: (this.offset.x - this.startTimeOffset.x) / acceleration,
            y: (this.offset.y - this.startTimeOffset.y) / acceleration
        };

        this.momentumAnimationStartOffset = currentOffset;

        ['x', 'y'].forEach(function(axis) {

            this.isDecelerating[axis] = (Math.abs(this.momentumAnimationStartVelocity[axis]) > minVelocity);

            if (this.bounces && this.bounces[axis]) {
                restrictedOffset = this.offsetBoundary.restrict(axis, currentOffset[axis]);

                if (restrictedOffset !== currentOffset[axis]) {
                    currentVelocity = (currentOffset[axis] - restrictedOffset) * this.bouncingVelocityFactor;

                    this.bouncingData[axis] = {
                        axis: axis,
                        offset: restrictedOffset,
                        time: this.momentumAnimationStartTime,
                        velocity: currentVelocity
                    };

                    this.isBouncing[axis] = true;
                    this.isDecelerating[axis] = false;

                    this.fireEvent('bouncestart', this, this.bouncingData[axis]);

                    this.bouncingAnimation[axis].set({
                        startTime: this.bouncingData[axis].time - this.bouncingTimeFactor,
                        startOffset: this.bouncingData[axis].offset,
                        startVelocity: this.bouncingData[axis].velocity
                    });
                }
            }

            if (this.isDecelerating[axis]) {
                this.decelerationAnimation[axis].set({
                    startVelocity: this.momentumAnimationStartVelocity[axis],
                    startOffset: this.momentumAnimationStartOffset[axis],
                    startTime: this.momentumAnimationStartTime
                });
            }
        }, this);

        if (this.isDecelerating.x || this.isDecelerating.y || this.isBouncing.x || this.isBouncing.y) {
            this.isMomentumAnimating = true;
            this.momentumAnimationFramesHandled = 0;
            this.fireEvent('momentumanimationstart');

            me.handleMomentumAnimationFrame();
            this.momentumAnimationTimer = setInterval(function() {
                me.handleMomentumAnimationFrame();
            }, this.getFrameDuration());
            return true;
        }

        return false;
    },

    
    stopMomentumAnimation: function() {
        if (this.isMomentumAnimating) {
            if (this.momentumAnimationTimer) {
                clearInterval(this.momentumAnimationTimer);
            }
            this.momentumAnimationEndTime = Date.now();

            var lastFps = this.getLastActualFps();

            if (!this.maxFps || lastFps > this.maxFps) {
                this.maxFps = lastFps;
            }

            if (this.autoAdjustFps) {
                this.fps = this.maxFps;
            }

            this.isDecelerating = {};
            this.isBouncing = {};

            this.fireEvent('momentumanimationend');
            this.fireScrollEndEvent();

        }

        return this;
    },

    
    handleMomentumAnimationFrame : function() {
        if (!this.isMomentumAnimating) {
            return;
        }

        var currentTime = Date.now(),
            newOffset = this.offset.copy(),
            offsetBoundary = this.offsetBoundary,
            currentVelocity,
            restrictedOffset,
            outOfBoundDistance;

        ['x', 'y'].forEach(function(axis) {
            if (this.isDecelerating[axis]) {
                newOffset[axis] = this.decelerationAnimation[axis].getOffset();
                currentVelocity = this.momentumAnimationStartVelocity[axis] * this.decelerationAnimation[axis].getFrictionFactor();
                outOfBoundDistance = offsetBoundary.getOutOfBoundOffset(axis, newOffset[axis]);

                
                if (outOfBoundDistance !== 0) {
                    restrictedOffset = offsetBoundary.restrict(axis, newOffset[axis]);

                    if (this.bounces && this.bounces[axis]) {
                        this.bouncingData[axis] = {
                            axis: axis,
                            offset: restrictedOffset,
                            time: currentTime,
                            velocity: currentVelocity
                        };

                        this.fireEvent('bouncestart', this, this.bouncingData[axis]);

                        this.bouncingAnimation[axis].set({
                            startTime: this.bouncingData[axis].time,
                            startOffset: this.bouncingData[axis].offset,
                            startVelocity: this.bouncingData[axis].velocity
                        });
                        this.isBouncing[axis] = true;
                    }

                    this.isDecelerating[axis] = false;
                }
                else if (Math.abs(currentVelocity) <= 1) {
                    this.isDecelerating[axis] = false;
                }
            }
            else if (this.isBouncing[axis]) {
                newOffset[axis] = this.bouncingAnimation[axis].getOffset();
                restrictedOffset = offsetBoundary.restrict(axis, newOffset[axis]);

                if (Math.abs(newOffset[axis] - restrictedOffset) <= 1) {
                    this.isBouncing[axis] = false;
                    this.fireEvent('bounceend', this, {axis: axis});
                    newOffset[axis] = restrictedOffset;
                }
            }
        }, this);

        if (!this.isDecelerating.x && !this.isDecelerating.y && !this.isBouncing.x && !this.isBouncing.y) {
            this.stopMomentumAnimation();
            return;
        }

        this.momentumAnimationFramesHandled++;
        this.momentumAnimationProcessingTime += Date.now() - currentTime;

        this.setOffset(newOffset);
    },

    
    destroy: function() {
        Ext.ScrollManager.unregister(this);
        return Ext.util.Scroller.superclass.destroy.apply(this, arguments);
    }
});

Ext.util.Scroller.Animation = {};

Ext.util.Scroller.Animation.Deceleration = Ext.extend(Ext.util.Draggable.Animation.Abstract, {
    acceleration: 30,
    theta: null,
    startVelocity: null,

    getOffset: function() {
        return this.startOffset - this.startVelocity * (1 - this.getFrictionFactor()) / this.theta;
    },

    getFrictionFactor : function() {
        var deltaTime = Date.now() - this.startTime;

        return Math.exp(deltaTime / this.acceleration * this.theta);
    }
});

Ext.util.Scroller.Animation.Bouncing = Ext.extend(Ext.util.Draggable.Animation.Abstract, {
    springTension: 0.3,
    acceleration: 30,
    startVelocity: null,

    getOffset: function() {
        var deltaTime = (Date.now() - this.startTime),
            powTime = (deltaTime / this.acceleration) * Math.pow(Math.E, -this.springTension * (deltaTime / this.acceleration));

        return this.startOffset + (this.startVelocity * powTime);
    }
});


Ext.util.Scroller.Indicator = Ext.extend(Object, {
    baseCls: 'x-scrollbar',

    ui: 'dark',

    
    type: 'horizontal',

    constructor: function(container, config) {
        this.container = container;

        Ext.apply(this, config);

        this.el = this.container.createChild({
            cls: [this.baseCls, this.baseCls + '-' + this.type, this.baseCls + '-' + this.ui].join(' ')
        });

        this.offset = new Ext.util.Offset();

        this.hide();
    },

    
    hide: function() {
        var me = this;

        if (this.hideTimer) {
            clearTimeout(this.hideTimer);
        }

        this.hideTimer = setTimeout(function() {
            me.el.setStyle('opacity', 0);
        }, 100);

        return this;
    },

    
    show: function() {
        if (this.hideTimer) {
            clearTimeout(this.hideTimer);
        }

        this.el.setStyle('opacity', 1);

        return this;
    },

    
    setVisibility: function(isVisible) {
        return this[isVisible ? 'show' : 'hide']();
    },

    
    setSize: function(size) {
        if (this.size && size > this.size) {
            size = Math.round(size);
        }

        
        this.el.dom.style[(this.type == 'horizontal') ? 'width' : 'height'] = size + 'px';

        this.size = size;

        return this;
    },

    
    setOffset: function(offset) {
        if (this.type == 'vertical') {
            this.offset.y = offset;
        } else {
            this.offset.x = offset;
        }

        if (!Ext.is.iOS && !Ext.is.Desktop) {
            if (this.type == 'vertical') {
                this.el.dom.style.top = this.offset.y + 'px';
            } else {
                this.el.dom.style.left = this.offset.x + 'px';
            }
        } else {
            Ext.Element.cssTranslate(this.el, this.offset);
        }

        return this;
    }

});

})();


Ext.util.Sortable = Ext.extend(Ext.util.Observable, {
    baseCls: 'x-sortable',

    
    direction: 'vertical',

    
    cancelSelector: null,

    
    
    
    

    
    constrain: window,
    
    group: 'base',

    
    revert: true,

    
    itemSelector: null,

    
    handleSelector: null,

    
    disabled: false,

    
    delay: 0,

    

    
    sorting: false,

    
    vertical: false,

    
    vertical: false,
    
    constructor : function(el, config) {
        config = config || {};
        Ext.apply(this, config);

        this.addEvents(
            
            'sortstart',
            
            'sortend',
            
            'sortchange'

            
            
            
            
            
            
            
            
        );

        this.el = Ext.get(el);
        Ext.util.Sortable.superclass.constructor.call(this);

        if (this.direction == 'horizontal') {
            this.horizontal = true;
        }
        else if (this.direction == 'vertical') {
            this.vertical = true;
        }
        else {
            this.horizontal = this.vertical = true;
        }

        this.el.addCls(this.baseCls);
        this.startEventName = (this.delay > 0) ? 'taphold' : 'tapstart';
        if (!this.disabled) {
            this.enable();
        }
    },

    
    onStart : function(e, t) {
        if (this.cancelSelector && e.getTarget(this.cancelSelector)) {
            return;
        }
        if (this.handleSelector && !e.getTarget(this.handleSelector)) {
            return;
        }
        
        if (!this.sorting) {
            this.onSortStart(e, t);
        }
    },

    
    onSortStart : function(e, t) {
        this.sorting = true;
        var draggable = new Ext.util.Draggable(t, {
            threshold: 0,
            revert: this.revert,
            direction: this.direction,
            constrain: this.constrain === true ? this.el : this.constrain,
            animationDuration: 100
        });
        draggable.on({
            drag: this.onDrag,
            dragend: this.onDragEnd,
            scope: this
        });
        
        this.dragEl = t;
        this.calculateBoxes();

        if (!draggable.dragging) {
            draggable.onStart(e);
        }
        
        this.fireEvent('sortstart', this, e);
    },

    
    calculateBoxes : function() {
        this.items = [];
        var els = this.el.select(this.itemSelector, false),
            ln = els.length, i, item, el, box;

        for (i = 0; i < ln; i++) {
            el = els[i];
            if (el != this.dragEl) {
                item = Ext.fly(el).getPageBox(true);
                item.el = el;
                this.items.push(item);
            }
        }
    },

    
    onDrag : function(draggable, e) {
        var items = this.items,
            ln = items.length,
            region = draggable.region,
            sortChange = false,
            i, intersect, overlap, item;
            
        for (i = 0; i < ln; i++) {
            item = items[i];
            intersect = region.intersect(item);
            if (intersect) {
                if (this.vertical && Math.abs(intersect.top - intersect.bottom) > (region.bottom - region.top) / 2) {
                    if (region.bottom > item.top && item.top > region.top) {
                        draggable.el.insertAfter(item.el);
                    }
                    else {
                        draggable.el.insertBefore(item.el);
                    }
                    sortChange = true;
                }
                else if (this.horizontal && Math.abs(intersect.left - intersect.right) > (region.right - region.left) / 2) {
                    if (region.right > item.left && item.left > region.left) {
                        draggable.el.insertAfter(item.el);
                    }
                    else {
                        draggable.el.insertBefore(item.el);
                    }
                    sortChange = true;
                }

                if (sortChange) {
                    
                    draggable.reset();

                    
                    
                    draggable.moveTo(region.left, region.top);

                    
                    this.calculateBoxes();
                    this.fireEvent('sortchange', this, draggable.el, this.el.select(this.itemSelector, false).indexOf(draggable.el.dom));
                    return;
                }
            }
        }
    },

    
    onDragEnd : function(draggable, e) {
        draggable.destroy();
        this.sorting = false;
        this.fireEvent('sortend', this, draggable, e);
    },

    
    enable : function() {
        this.el.on(this.startEventName, this.onStart, this, {delegate: this.itemSelector, holdThreshold: this.delay});
        this.disabled = false;
    },

    
    disable : function() {
        this.el.un(this.startEventName, this.onStart, this);
        this.disabled = true;
    },
    
    
    isDisabled : function() {
        return this.disabled;
    },
    
    
    isSorting : function() {
        return this.sorting;
    },
    
    
    isVertical : function() {
        return this.vertical;
    },
    
    
    isHorizontal : function() {
        return this.horizontal;
    }    
});




 (function() {

    
    Date.useStrict = false;


    
    
    
    function xf(format) {
        var args = Array.prototype.slice.call(arguments, 1);
        return format.replace(/\{(\d+)\}/g,
        function(m, i) {
            return args[i];
        });
    }


    
    Date.formatCodeToRegex = function(character, currentGroup) {
        
        var p = Date.parseCodes[character];

        if (p) {
            p = typeof p == 'function' ? p() : p;
            Date.parseCodes[character] = p;
            
        }

        return p ? Ext.applyIf({
            c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
        },
        p) : {
            g: 0,
            c: null,
            s: Ext.util.Format.escapeRegex(character)
            
        };
    };

    
    var $f = Date.formatCodeToRegex;

    Ext.apply(Date, {
        
        parseFunctions: {
            "M$": function(input, strict) {
                
                
                var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
                var r = (input || '').match(re);
                return r ? new Date(((r[1] || '') + r[2]) * 1) : null;
            }
        },
        parseRegexes: [],

        
        formatFunctions: {
            "M$": function() {
                
                return '\\/Date(' + this.getTime() + ')\\/';
            }
        },

        y2kYear: 50,

        
        MILLI: "ms",

        
        SECOND: "s",

        
        MINUTE: "mi",

        
        HOUR: "h",

        
        DAY: "d",

        
        MONTH: "mo",

        
        YEAR: "y",

        
        defaults: {},

        
        dayNames: [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
        ],

        
        monthNames: [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
        ],

        
        monthNumbers: {
            Jan: 0,
            Feb: 1,
            Mar: 2,
            Apr: 3,
            May: 4,
            Jun: 5,
            Jul: 6,
            Aug: 7,
            Sep: 8,
            Oct: 9,
            Nov: 10,
            Dec: 11
        },

        
        getShortMonthName: function(month) {
            return Date.monthNames[month].substring(0, 3);
        },

        
        getShortDayName: function(day) {
            return Date.dayNames[day].substring(0, 3);
        },

        
        getMonthNumber: function(name) {
            
            return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
        },

        
        formatCodes: {
            d: "Ext.util.Format.leftPad(this.getDate(), 2, '0')",
            D: "Date.getShortDayName(this.getDay())",
            
            j: "this.getDate()",
            l: "Date.dayNames[this.getDay()]",
            N: "(this.getDay() ? this.getDay() : 7)",
            S: "this.getSuffix()",
            w: "this.getDay()",
            z: "this.getDayOfYear()",
            W: "Ext.util.Format.leftPad(this.getWeekOfYear(), 2, '0')",
            F: "Date.monthNames[this.getMonth()]",
            m: "Ext.util.Format.leftPad(this.getMonth() + 1, 2, '0')",
            M: "Date.getShortMonthName(this.getMonth())",
            
            n: "(this.getMonth() + 1)",
            t: "this.getDaysInMonth()",
            L: "(this.isLeapYear() ? 1 : 0)",
            o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
            Y: "this.getFullYear()",
            y: "('' + this.getFullYear()).substring(2, 4)",
            a: "(this.getHours() < 12 ? 'am' : 'pm')",
            A: "(this.getHours() < 12 ? 'AM' : 'PM')",
            g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
            G: "this.getHours()",
            h: "Ext.util.Format.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
            H: "Ext.util.Format.leftPad(this.getHours(), 2, '0')",
            i: "Ext.util.Format.leftPad(this.getMinutes(), 2, '0')",
            s: "Ext.util.Format.leftPad(this.getSeconds(), 2, '0')",
            u: "Ext.util.Format.leftPad(this.getMilliseconds(), 3, '0')",
            O: "this.getGMTOffset()",
            P: "this.getGMTOffset(true)",
            T: "this.getTimezone()",
            Z: "(this.getTimezoneOffset() * -60)",

            c: function() {
                
                for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
                    var e = c.charAt(i);
                    code.push(e == "T" ? "'T'": Date.getFormatCode(e));
                    
                }
                return code.join(" + ");
            },
            

            U: "Math.round(this.getTime() / 1000)"
        },

        
        isValid: function(y, m, d, h, i, s, ms) {
            
            h = h || 0;
            i = i || 0;
            s = s || 0;
            ms = ms || 0;

            var dt = new Date(y, m - 1, d, h, i, s, ms);

            return y == dt.getFullYear() &&
            m == dt.getMonth() + 1 &&
            d == dt.getDate() &&
            h == dt.getHours() &&
            i == dt.getMinutes() &&
            s == dt.getSeconds() &&
            ms == dt.getMilliseconds();
        },

        
        parseDate: function(input, format, strict) {
            var p = Date.parseFunctions;
            if (p[format] == null) {
                Date.createParser(format);
            }
            return p[format](input, Ext.isDefined(strict) ? strict: Date.useStrict);
        },

        
        getFormatCode: function(character) {
            var f = Date.formatCodes[character];

            if (f) {
                f = typeof f == 'function' ? f() : f;
                Date.formatCodes[character] = f;
                
            }

            
            return f || ("'" + Ext.util.Format.escape(character) + "'");
        },

        
        createFormat: function(format) {
            var code = [],
            special = false,
            ch = '';

            for (var i = 0; i < format.length; ++i) {
                ch = format.charAt(i);
                if (!special && ch == "\\") {
                    special = true;
                } else if (special) {
                    special = false;
                    code.push("'" + Ext.util.Format.escape(ch) + "'");
                } else {
                    code.push(Date.getFormatCode(ch));
                }
            }
            Date.formatFunctions[format] = new Function("return " + code.join('+'));
        },

        
        createParser: function() {
            var code = [
            "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
            "def = Date.defaults,",
            "results = String(input).match(Date.parseRegexes[{0}]);",
            
            "if(results){",
            "{1}",

            "if(u != null){",
            
            "v = new Date(u * 1000);",
            
            "}else{",
            
            
            
            "dt = (new Date()).clearTime();",

            
            "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
            "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
            "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",

            
            "h  = Ext.num(h, Ext.num(def.h, dt.getHours()));",
            "i  = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
            "s  = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
            "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",

            "if(z >= 0 && y >= 0){",
            
            
            
            "v = new Date(y, 0, 1, h, i, s, ms);",

            
            "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
            "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){",
            
            "v = null;",
            
            "}else{",
            
            "v = new Date(y, m, d, h, i, s, ms);",
            "}",
            "}",
            "}",

            "if(v){",
            
            "if(zz != null){",
            
            "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
            "}else if(o){",
            
            "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
            "}",
            "}",

            "return v;"
            ].join('\n');

            return function(format) {
                var regexNum = Date.parseRegexes.length,
                currentGroup = 1,
                calc = [],
                regex = [],
                special = false,
                ch = "",
                i = 0,
                obj,
                last;

                for (; i < format.length; ++i) {
                    ch = format.charAt(i);
                    if (!special && ch == "\\") {
                        special = true;
                    } else if (special) {
                        special = false;
                        regex.push(Ext.util.Format.escape(ch));
                    } else {
                        obj = $f(ch, currentGroup);
                        currentGroup += obj.g;
                        regex.push(obj.s);
                        if (obj.g && obj.c) {
                            if (obj.last) {
                                last = obj;
                            } else {
                                calc.push(obj.c);
                            }
                        }
                    }
                }
                
                if (last) {
                    calc.push(last);
                }

                Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
                Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
            };
        }(),

        
        parseCodes: {
            
            d: {
                g: 1,
                c: "d = parseInt(results[{0}], 10);\n",
                s: "(\\d{2})"
                
            },
            j: {
                g: 1,
                c: "d = parseInt(results[{0}], 10);\n",
                s: "(\\d{1,2})"
                
            },
            D: function() {
                for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i);
                
                return {
                    g: 0,
                    c: null,
                    s: "(?:" + a.join("|") + ")"
                };
            },
            l: function() {
                return {
                    g: 0,
                    c: null,
                    s: "(?:" + Date.dayNames.join("|") + ")"
                };
            },
            N: {
                g: 0,
                c: null,
                s: "[1-7]"
                
            },
            S: {
                g: 0,
                c: null,
                s: "(?:st|nd|rd|th)"
            },
            w: {
                g: 0,
                c: null,
                s: "[0-6]"
                
            },
            z: {
                g: 1,
                c: "z = parseInt(results[{0}], 10);\n",
                s: "(\\d{1,3})"
                
            },
            W: {
                g: 0,
                c: null,
                s: "(?:\\d{2})"
                
            },
            F: function() {
                return {
                    g: 1,
                    c: "m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n",
                    
                    s: "(" + Date.monthNames.join("|") + ")"
                };
            },
            M: function() {
                for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i);
                
                return Ext.applyIf({
                    s: "(" + a.join("|") + ")"
                },
                $f("F"));
            },
            m: {
                g: 1,
                c: "m = parseInt(results[{0}], 10) - 1;\n",
                s: "(\\d{2})"
                
            },
            n: {
                g: 1,
                c: "m = parseInt(results[{0}], 10) - 1;\n",
                s: "(\\d{1,2})"
                
            },
            t: {
                g: 0,
                c: null,
                s: "(?:\\d{2})"
                
            },
            L: {
                g: 0,
                c: null,
                s: "(?:1|0)"
            },
            o: function() {
                return $f("Y");
            },
            Y: {
                g: 1,
                c: "y = parseInt(results[{0}], 10);\n",
                s: "(\\d{4})"
                
            },
            y: {
                g: 1,
                c: "var ty = parseInt(results[{0}], 10);\n"
                + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
                
                s: "(\\d{1,2})"
            },
            a: function(){
                return $f("A");
            },
            A: {
                
                calcLast: true,
                g: 1,
                c: "if (results[{0}] == 'AM') {\n"
                    + "if (!h || h == 12) { h = 0; }\n"
                    + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
                s: "(AM|PM)"
            },
            g: function() {
                return $f("G");
            },
            G: {
                g: 1,
                c: "h = parseInt(results[{0}], 10);\n",
                s: "(\\d{1,2})"
                
            },
            h: function() {
                return $f("H");
            },
            H: {
                g: 1,
                c: "h = parseInt(results[{0}], 10);\n",
                s: "(\\d{2})"
                
            },
            i: {
                g: 1,
                c: "i = parseInt(results[{0}], 10);\n",
                s: "(\\d{2})"
                
            },
            s: {
                g: 1,
                c: "s = parseInt(results[{0}], 10);\n",
                s: "(\\d{2})"
                
            },
            u: {
                g: 1,
                c: "ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
                s: "(\\d+)"
                
            },
            O: {
                g: 1,
                c: [
                "o = results[{0}];",
                "var sn = o.substring(0,1),",
                
                "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),",
                
                "mn = o.substring(3,5) % 60;",
                
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.util.Format.leftPad(hr, 2, '0') + Ext.util.Format.leftPad(mn, 2, '0')) : null;\n"
                
                ].join("\n"),
                s: "([+\-]\\d{4})"
                
            },
            P: {
                g: 1,
                c: [
                "o = results[{0}];",
                "var sn = o.substring(0,1),",
                
                "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),",
                
                "mn = o.substring(4,6) % 60;",
                
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.util.Format.leftPad(hr, 2, '0') + Ext.util.Format.leftPad(mn, 2, '0')) : null;\n"
                
                ].join("\n"),
                s: "([+\-]\\d{2}:\\d{2})"
                
            },
            T: {
                g: 0,
                c: null,
                s: "[A-Z]{1,4}"
                
            },
            Z: {
                g: 1,
                c: "zz = results[{0}] * 1;\n"
                
                + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
                s: "([+\-]?\\d{1,5})"
                
            },
            c: function() {
                var calc = [],
                arr = [
                $f("Y", 1),
                
                $f("m", 2),
                
                $f("d", 3),
                
                $f("h", 4),
                
                $f("i", 5),
                
                $f("s", 6),
                
                {
                    c: "ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"
                },
                
                {
                    c: [
                    
                    "if(results[8]) {",
                    
                    "if(results[8] == 'Z'){",
                    "zz = 0;",
                    
                    "}else if (results[8].indexOf(':') > -1){",
                    $f("P", 8).c,
                    
                    "}else{",
                    $f("O", 8).c,
                    
                    "}",
                    "}"
                    ].join('\n')
                }
                ];

                for (var i = 0, l = arr.length; i < l; ++i) {
                    calc.push(arr[i].c);
                }

                return {
                    g: 1,
                    c: calc.join(""),
                    s: [
                    arr[0].s,
                    
                    "(?:", "-", arr[1].s,
                    
                    "(?:", "-", arr[2].s,
                    
                    "(?:",
                    "(?:T| )?",
                    
                    arr[3].s, ":", arr[4].s,
                    
                    "(?::", arr[5].s, ")?",
                    
                    "(?:(?:\\.|,)(\\d+))?",
                    
                    "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?",
                    
                    ")?",
                    ")?",
                    ")?"
                    ].join("")
                };
            },
            U: {
                g: 1,
                c: "u = parseInt(results[{0}], 10);\n",
                s: "(-?\\d+)"
                
            }
        }
    });

} ());

Ext.apply(Date.prototype, {
    
    dateFormat: function(format) {
        if (Date.formatFunctions[format] == null) {
            Date.createFormat(format);
        }
        return Date.formatFunctions[format].call(this);
    },

    
    getTimezone: function() {
        
        
        
        
        
        
        
        
        
        
        
        
        return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
    },

    
    getGMTOffset: function(colon) {
        return (this.getTimezoneOffset() > 0 ? "-": "+")
        + Ext.util.Format.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
        + (colon ? ":": "")
        + Ext.util.Format.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
    },

    
    getDayOfYear: function() {
        var num = 0,
        d = this.clone(),
        m = this.getMonth(),
        i;

        for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
            num += d.getDaysInMonth();
        }
        return num + this.getDate() - 1;
    },

    
    getWeekOfYear: function() {
        
        var ms1d = 864e5,
        
        ms7d = 7 * ms1d;
        
        return function() {
            
            var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d,
            
            AWN = Math.floor(DC3 / 7),
            
            Wyr = new Date(AWN * ms7d).getUTCFullYear();

            return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
        };
    }(),

    
    isLeapYear: function() {
        var year = this.getFullYear();
        return !! ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
    },

    
    getFirstDayOfMonth: function() {
        var day = (this.getDay() - (this.getDate() - 1)) % 7;
        return (day < 0) ? (day + 7) : day;
    },

    
    getLastDayOfMonth: function() {
        return this.getLastDateOfMonth().getDay();
    },


    
    getFirstDateOfMonth: function() {
        return new Date(this.getFullYear(), this.getMonth(), 1);
    },

    
    getLastDateOfMonth: function() {
        return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
    },

    
    getDaysInMonth: function() {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

        return function() {
            
            var m = this.getMonth();

            return m == 1 && this.isLeapYear() ? 29: daysInMonth[m];
        };
    }(),

    
    getSuffix: function() {
        switch (this.getDate()) {
        case 1:
        case 21:
        case 31:
            return "st";
        case 2:
        case 22:
            return "nd";
        case 3:
        case 23:
            return "rd";
        default:
            return "th";
        }
    },

    
    clone: function() {
        return new Date(this.getTime());
    },

    
    isDST: function() {
        
        
        return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
    },

    
    clearTime: function(clone) {
        if (clone) {
            return this.clone().clearTime();
        }

        
        var d = this.getDate();

        
        this.setHours(0);
        this.setMinutes(0);
        this.setSeconds(0);
        this.setMilliseconds(0);

        if (this.getDate() != d) {
            
            
            
            
            for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));

            this.setDate(d);
            this.setHours(c.getHours());
        }

        return this;
    },

    
    add: function(interval, value) {
        var d = this.clone();
        if (!interval || value === 0) return d;

        switch (interval.toLowerCase()) {
        case Date.MILLI:
            d.setMilliseconds(this.getMilliseconds() + value);
            break;
        case Date.SECOND:
            d.setSeconds(this.getSeconds() + value);
            break;
        case Date.MINUTE:
            d.setMinutes(this.getMinutes() + value);
            break;
        case Date.HOUR:
            d.setHours(this.getHours() + value);
            break;
        case Date.DAY:
            d.setDate(this.getDate() + value);
            break;
        case Date.MONTH:
            var day = this.getDate();
            if (day > 28) {
                day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
            }
            d.setDate(day);
            d.setMonth(this.getMonth() + value);
            break;
        case Date.YEAR:
            d.setFullYear(this.getFullYear() + value);
            break;
        }
        return d;
    },

    
    between: function(start, end) {
        var t = this.getTime();
        return start.getTime() <= t && t <= end.getTime();
    }
});



Date.prototype.format = Date.prototype.dateFormat;




Ext.data.Connection = Ext.extend(Ext.util.Observable, {
    method: 'post',
    url: null,

    
    disableCaching: true,

    
    disableCachingParam: '_dc',

    
    timeout : 30000,

    useDefaultHeader : true,
    defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
    useDefaultXhrHeader : true,
    defaultXhrHeader : 'XMLHttpRequest',

    constructor : function(config) {
        config = config || {};
        Ext.apply(this, config);

        this.addEvents(
            
            'beforerequest',
            
            'requestcomplete',
            
            'requestexception'
        );
        this.requests = {};
        Ext.data.Connection.superclass.constructor.call(this);
    },

    
    request : function(o) {
        var me = this;
        if (me.fireEvent('beforerequest', me, o) !== false) {
            var params      = o.params,
                url         = o.url || me.url,
                urlParams   = o.urlParams,
                extraParams = me.extraParams,
                request, data, headers,
                method, key, xhr;

            
            if (Ext.isFunction(params)) {
                params = params.call(o.scope || window, o);
            }

            
            if (Ext.isFunction(url)) {
                url = url.call(o.scope || window, o);
            }

            
            data = o.rawData || o.xmlData || o.jsonData || null;
            if (o.jsonData && !Ext.isPrimitive(o.jsonData)) {
                data = Ext.encode(data);
            }
            
            
            params = Ext.urlEncode(extraParams, Ext.isObject(params) ? Ext.urlEncode(params) : params);
            
            urlParams = Ext.isObject(urlParams) ? Ext.urlEncode(urlParams) : urlParams;

            
            method = (o.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();

            
            if (method === 'GET' && o.disableCaching !== false && me.disableCaching) {
                url = Ext.urlAppend(url, o.disableCachingParam || me.disableCachingParam + '=' + (new Date().getTime()));
            }

            
            if ((method == 'GET' || data) && params){
                url = Ext.urlAppend(url, params);
                params = null;
            }

            
            if (urlParams) {
                url = Ext.urlAppend(url, urlParams);
            }

            
            if (o.autoAbort === true || me.autoAbort) {
                me.abort();
            }

            
            xhr = this.getXhrInstance();

            
            xhr.open(method.toUpperCase(), url, true);

            
            headers = Ext.apply({}, o.headers || {}, me.defaultHeaders || {});
            if (!headers['Content-Type'] && (data || params)) {
                var contentType = me.defaultPostHeader,
                    jsonData    = o.jsonData,
                    xmlData     = o.xmlData;
                
                if (data) {
                    if (o.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;
            }
            
            for (key in headers) {
                if (headers.hasOwnProperty(key)) {
                    try {
                        xhr.setRequestHeader(key, headers[key]);
                    }
                    catch(e) {
                        me.fireEvent('exception', key, headers[key]);
                    }                    
                }
            }

            
            request = {
                id: ++Ext.data.Connection.requestId,
                xhr: xhr,
                headers: headers,
                options: o,
                timeout: setTimeout(function() {
                    request.timedout = true;
                    me.abort(request);
                }, o.timeout || me.timeout)
            };
            me.requests[request.id] = request;

            
            xhr.onreadystatechange = Ext.createDelegate(me.onStateChange, me, [request]);

            
            xhr.send(data || params || null);
            return request;
        } else {
            return o.callback ? o.callback.apply(o.scope, [o, undefined, undefined]) : null;
        }
    },

    getXhrInstance : function() {
        return new XMLHttpRequest();
    },
    
    
    isLoading : function(r) {
        
        return r && !{0:true, 4:true}[r.xhr.readyState];
    },

    
    abort : function(request) {
        if (request && this.isLoading(request)) {
            if (!request.timedout) {
                request.aborted = true;
            }
            
            request.xhr.abort();
        }
        else if (!request) {
            var id;
            for(id in this.requests) {
                if (!this.requests.hasOwnProperty(id)) {
                    continue;
                }
                this.abort(this.requests[id]);
            }
        }
    },

    
    onStateChange : function(r) {
        if (r.xhr.readyState == 4) {
            clearTimeout(r.timeout);
            delete r.timeout;
            this.onComplete(r);
        }
    },

    
    onComplete : function(r) {
        var status = r.xhr.status,
            options = r.options,
            success = true,
            response;

        if ((status >= 200 && status < 300) || status == 304) {
            response = this.createResponse(r);
            this.fireEvent('requestcomplete', this, response, options);
            if (options.success) {
                if (!options.scope) {
                    options.success(response, options);
                }
                else {
                    options.success.call(options.scope, response, options);
                }
            }
        }
        else {
            success = false;
            switch (status) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    response = this.createException(r);
                    break;
                default:
                    response = this.createResponse(r);
            }
            this.fireEvent('requestexception', this, response, options);
            if (options.failure) {
                if (!options.scope) {
                    options.failure(response, options);
                }
                else {
                    options.failure.call(options.scope, response, options);
                }
            }
        }

        if (options.callback) {
            if (!options.scope) {
                options.callback(options, success, response);
            }
            else {
                options.callback.call(options.scope, options, success, response);
            }
        }
        
        delete this.requests[r.id];
    },

    
    createResponse : function(r) {
        var xhr = r.xhr,
            headers = {},
            lines = xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'),
            count = lines.length,
            line, index, key, value;

        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);
            }
        }

        delete r.xhr;

        return {
            request: r,
            requestId : r.id,
            status : xhr.status,
            statusText : xhr.statusText,
            getResponseHeader : function(header){ return headers[header.toLowerCase()]; },
            getAllResponseHeaders : function(){ return headers; },
            responseText : xhr.responseText,
            responseXML : xhr.responseXML
        };
    },

    
    createException : function(r) {
        return {
            request : r,
            requestId : r.id,
            status : r.aborted ? -1 : 0,
            statusText : r.aborted ? 'transaction aborted' : 'communication failure',
            aborted: r.aborted,
            timedout: r.timedout
        };
    }
});

Ext.data.Connection.requestId = 0;


Ext.Ajax = new Ext.data.Connection({
    
    
    
    
    
    

    

    
    
    
    
    
    

    
    autoAbort : false
});


Ext.util.EventSimulator = Ext.extend(Object, {

    supportedEvents: {
        touch: ['touchstart', 'touchmove', 'touchend', 'gesturestart', 'gesturechange', 'gestureend'],
        mouse: ['mousedown', 'mousemove', 'mouseup', 'click']
    },

    getEventTypeByName: function(name) {
        var ret = null;

        Ext.iterate(this.supportedEvents, function(type, events) {
            if (events.indexOf(name) != -1)
                ret = type;
        });

        return ret;
    },

    fire: function(type, target, options) {
        type = type.toLowerCase();

        if (arguments.length == 2) {
            options = target;
            target = document;
        }

        switch(this.getEventTypeByName(type)) {
            case 'touch':
                this.fireTouchEvent.call(this, type, target, options);
                break;

            case 'mouse':
                this.fireMouseEvent.call(this, type, target, options);
                break;

            default:
                throw new Error("Event type " + type + " is currently not supported");
        }

        return this;
    },

    createEvent: function(data, serializable) {

    },

    createEventData: function(event, serializable) {
        switch (this.getEventTypeByName(event.type)) {
            case 'touch':
                return this.createTouchEventData(event.type, event.target, event, serializable);
                break;

            case 'mouse':
                return this.createMouseEventData(event.type, event.target, event, serializable);
                break;

            default:
                throw new Error("Event type " + event.type + " is currently not supported");
        }
    },

    

    fireTouchEvent: function(type, target, options) {
        var touchEventData = this.createTouchEventData(type, target, options);

        var touchEvent = this.createTouchEvent(type, touchEventData);
        touchEvent.isSimulated = true;

        return target.dispatchEvent(touchEvent);
    },

    createTouchEventData: function(type, target, options, serializable) {
        var touchEventData = {
            type: type,
            timeStamp: Date.now(),
            bubbles: true,
            cancelable: true,
            detail: 1, 
            screenX: 0,
            screenY: 0,
            pageX: 0,
            pageY: 0,
            clientX: 0,
            clientY: 0,
            ctrlKey: false,
            altKey: false,
            shiftKey: false,
            metaKey: false,
            scale: 1,
            rotation: 0
        };

        if (!serializable) {
            touchEventData.target = target;
            touchEventData.view = document.defaultView;
        }

        if (options) {
            Ext.iterate(touchEventData, function(key, value) {
                if (options.hasOwnProperty(key)) {
                    touchEventData[key] = options[key];
                }
            });
        }
        
        ['touches', 'targetTouches', 'changedTouches'].forEach(function(touchListName) {
            if (options.hasOwnProperty(touchListName)) {
                touchEventData[touchListName] = this.createTouchList(options[touchListName], target, serializable);
            }
            else {
                touchEventData[touchListName] = this.createTouchList(touchEventData, target, serializable);
            }
        }, this);

        return touchEventData;
    },

    createTouchEvent: function(type, data) {
        if (typeof type != 'string') {
            data = type;
            type = type.type;
        }

        var touchEvent = document.createEvent('TouchEvent');

        if (touchEvent.initTouchEvent.length == 9) {
            touchEvent.initTouchEvent(data.touches, data.targetTouches, data.changedTouches,
                type, data.view, data.screenX, data.screenY, data.clientX, data.clientY);

        } else {
            touchEvent.initTouchEvent(type, data.bubbles, data.cancelable, data.view,
                data.detail, data.screenX, data.screenY, data.pageX, data.pageY, data.ctrlKey,
                data.altKey, data.shiftKey, data.metaKey, data.touches, data.targetTouches,
                data.changedTouches, data.scale, data.rotation);
        }

        return touchEvent;
    },

    createTouch: function(target, options, serializable) {
		if (!document.createTouch || serializable) {
			return {
                pageX: options.pageX,
                pageY: options.pageY,
                clientX: options.pageX,
                clientY: options.pageY,
                screenX: options.pageX,
                screenY: options.pageY,
                identifier: +options.identifier || 0
            };
		}
		
        return document.createTouch(
            document.defaultView,
            target,
            +options.identifier || 0,
            +options.pageX || 0,
            +options.pageY || 0,
            +options.screenX || 0,
            +options.screenY || 0);
    },

    createTouchList: function(data, target, serializable) {
        var touch,
            touches = [];

        if (Ext.isObject(data) && typeof data.target != 'undefined') {
            data = [data];
        }

        if (data) {
            for (var i = 0; i < data.length; i++) {
                if (!serializable && !data[i].target) {
                    data[i].target = target;
                }

                touch = this.createTouch(data[i].target, data[i], serializable);
                touches.push(touch);
            }
        }

		if (!document.createTouchList || serializable) {
			return touches;
		}
		
        return document.createTouchList.apply(document, touches);
    },

    

    fireMouseEvent: function(type, target, options) {
        var eventData = this.createMouseEventData(type, target, options);

        var event = this.createMouseEvent(type, eventData);
        event.isSimulated = true;
        event.originalTimeStamp = eventData.timeStamp;
        
        return target.dispatchEvent(event);
    },

    createMouseEventData: function(type, target, options, serializable) {
        var mouseEventData = {
            type: type,
            timeStamp: Date.now(),
            bubbles: true, 
            cancelable: (type != 'mousemove'), 
            detail: 1, 
            screenX: 0,
            screenY: 0,
            pageX: 0,
            pageY: 0,
            clientX: 0,
            clientY: 0,
            ctrlKey: false,
            altKey: false,
            shiftKey: false,
            metaKey: false,
            button: 0,
            relatedTarget: null
        }, 
        coordProperties = ['screen', 'client', 'page'],
        coords = {
            x: 0,
            y: 0
        };
        
        if (!serializable) {
            mouseEventData.target = target;
            mouseEventData.view = window;
        }

        if (options) {
            Ext.iterate(mouseEventData, function(key, value) {
                if (options.hasOwnProperty(key)) {
                    mouseEventData[key] = options[key];
                }
            });
        }

        coordProperties.forEach(function(p) {
            if (mouseEventData[p + 'X'] != 0) {
                coords.x = mouseEventData[p + 'X']
            }

            if (mouseEventData[p + 'Y'] != 0) {
                coords.y = mouseEventData[p + 'Y']
            }
        });

        coordProperties.forEach(function(p) {
            if (mouseEventData[p + 'X'] == 0 && coords.x != 0) {
                mouseEventData[p + 'X'] = coords.x;
            }

            if (mouseEventData[p + 'Y'] == 0 && coords.y != 0) {
                mouseEventData[p + 'Y'] = coords.y;
            }
        });

        return mouseEventData;
    },

    createMouseEvent: function(type, data) {
        var mouseEvent = document.createEvent('MouseEvents');

        mouseEvent.initMouseEvent(
            type, data.bubbles, data.cancelable, data.view, data.detail,
            data.screenX, data.screenY, data.clientX, data.clientY,
            data.ctrlKey, data.altKey, data.shiftKey, data.metaKey,
            data.button, data.relatedTarget);

        return mouseEvent;
    }

});

Ext.util.EventRecorder = Ext.extend(Ext.util.Observable, {

    eventCollection: null,

    currentEventSetName: null,

    constructor: function() {
        this.addEvents(
            'replaystart',
            'beforecalculatetarget',
            'beforefire',
            'afterfire',
            'aftercalculatetarget',
            'replayend',
            'interrupted'
        );

        this.eventSets = {};
        this.interruptedIndexes = {};

        return this;
    },

    getEventSet: function(name) {
        if (typeof name != 'string') {
            if (this.currentEventSetName == null)
                throw new Error('No EventSet is currently used');

            name = this.currentEventSetName;
        }

        if (typeof this.eventSets[name] == 'undefined') {
            this.eventSets[name] = [];
        }
        return this.eventSets[name];
    },

    start: function(name) {
        this.currentEventSetName = name;
    },

    record: function(name, event) {
        if (typeof name != 'string') {
            
            if (this.currentEventSetName == null)
                return;

            event = name;
            name = this.currentEventSetName;
        }

        var eventData = this.getEventSimulator().createEventData(event, true);

        this.getEventSet(name).push(eventData);
    },

    setEventSet: function(name, events) {
        this.eventSets[name] = events;
    },

    erase: function(name) {
        
        this.getEventSet(name).length = 0;
    },

    stopReplay: function() {
        this.interruptFlag = true;
    },

    resumeReplay: function(name) {
        var index = this.interruptedIndexes[name] || 0;
        this.replay(name, index);
    },

    replay: function(name, startIndex) {
        var simulator = this.getEventSimulator(),
            events = this.getEventSet(name),
            numEvents,
            delay = 0,
            index = 0,
            event,
            point,
            target,
            reserveTargetEvents = ['touchmove', 'touchend', 'mousemove', 'mouseup'],
            me = this;

        if (typeof startIndex == 'undefined') {
            startIndex = 0;
        }

        index = startIndex;
        
        numEvents = events.length;

        this.interruptFlag = false;

        if (numEvents > 0) {
            this.fireEvent('replaystart', name, startIndex);
            setTimeout(function() {
                event = events[index];
                
                if (event) {

                    if (reserveTargetEvents.indexOf(event.type) === -1) {
                        me.fireEvent('beforecalculatetarget', event.type, event);
                        point = Ext.util.Point.fromEvent(event);
                        target = document.elementFromPoint(point.x, point.y);
                        me.fireEvent('aftercalculatetarget', event.type, target, event);
                    }
                    
                    if (target) {
                        if (me.interruptFlag === true) {
                            me.interruptFlag = false;
                            me.interruptedIndexes[name] = index;
                            me.fireEvent('interrupted', index);
                            me.fireEvent('replayend', name, true);
                            return;
                        }
                        me.fireEvent('beforefire', event.type, target, event);
                        simulator.fire(event.type, target, event);
                        me.fireEvent('afterfire', event.type, target, event);
                    }

                    if (++index < numEvents) {
                        setTimeout(arguments.callee, events[index].timeStamp - event.timeStamp);
                    } else {
                        me.fireEvent('replayend', name, false);
                    }
                }
            }, delay);
        }
    },

    end: function() {
        this.currentEventSetName = null;
    },

    getEventSimulator: function() {
        if (!this._eventSimulator) {
            this._eventSimulator = new Ext.util.EventSimulator();
        }

        return this._eventSimulator;
    },

    setEventSimulator: function(eventSimulator) {
        if (!(eventSimulator instanceof Ext.util.EventSimulator))
            throw new Error('eventSimulator must be an instance of Ext.util.EventSimulator');

        this._eventSimulator = eventSimulator;
    },

    
    save: function(name) {

    }
});

Ext.gesture.Manager = new Ext.AbstractManager({
    eventNames: {
        start: 'touchstart',
        move: 'touchmove',
        end: 'touchend'
    },

    defaultPreventedMouseEvents: ['click'],

    clickMoveThreshold: 5,

    init: function() {
        this.targets = [];

        this.followTouches = [];
        this.currentGestures = [];
        this.currentTargets = [];

        if (!Ext.supports.Touch) {
            Ext.apply(this.eventNames, {
                start: 'mousedown',
                move: 'mousemove',
                end: 'mouseup'
            });
        }

        this.listenerWrappers = {
            start: Ext.createDelegate(this.onTouchStart, this),
            move: Ext.createDelegate(this.onTouchMove, this),
            end: Ext.createDelegate(this.onTouchEnd, this),
            mouse: Ext.createDelegate(this.onMouseEvent, this)
        };

        this.attachListeners();
    },

    freeze: function() {
        this.isFrozen = true;
    },

    thaw: function() {
        this.isFrozen = false;
    },

    getEventSimulator: function() {
        if (!this.eventSimulator) {
            this.eventSimulator = new Ext.util.EventSimulator();
        }

        return this.eventSimulator;
    },

    attachListeners: function() {
        Ext.iterate(this.eventNames, function(key, name) {
            document.addEventListener(name, this.listenerWrappers[key], false);
        }, this);

        if (Ext.supports.Touch) {
            this.defaultPreventedMouseEvents.forEach(function(name) {
                document.addEventListener(name, this.listenerWrappers['mouse'], true);
            }, this);
        }
    },

    detachListeners: function() {
        Ext.iterate(this.eventNames, function(key, name) {
            document.removeEventListener(name, this.listenerWrappers[key], false);
        }, this);

        if (Ext.supports.Touch) {
            this.defaultPreventedMouseEvents.forEach(function(name) {
                document.removeEventListener(name, this.listenerWrappers['mouse'], true);
            }, this);
        }
    },

    onMouseEvent: function(e) {
        if (!e.isSimulated) {
            e.preventDefault();
            e.stopPropagation();
        }
    },

    onTouchStart: function(e) {
        var targets = [],
            target = e.target;

        if (e.stopped === true) {
            return;
        }

        if (Ext.is.Android) {
            if (!(target.tagName && ['input', 'textarea', 'select'].indexOf(target.tagName.toLowerCase()) !== -1)) {
                e.preventDefault();
            }
        }

        if (this.isFrozen) {
            return;
        }

        
        
        
        if (this.startEvent) {
            this.onTouchEnd(e);
        }

        this.locks = {};

        this.currentTargets = [target];

        while (target) {
            if (this.targets.indexOf(target) !== -1) {
                targets.unshift(target);
            }

            target = target.parentNode;
            this.currentTargets.push(target);
        }

        this.startEvent = e;
        this.startPoint = Ext.util.Point.fromEvent(e);
        this.lastMovePoint = null;
        this.isClick = true;
        this.handleTargets(targets, e);
    },

    onTouchMove: function(e) {
        if (Ext.is.MultiTouch) {
            e.preventDefault();
        }

        if (!this.startEvent) {
            return;
        }

        if (Ext.is.Desktop) {
            e.target = this.startEvent.target;
        }

        if (this.isFrozen) {
            return;
        }

        var gestures = this.currentGestures,
            gesture,
            touch = e.changedTouches ? e.changedTouches[0] : e;

        this.lastMovePoint = Ext.util.Point.fromEvent(e);

        if (Ext.supports.Touch && this.isClick && !this.lastMovePoint.isWithin(this.startPoint, this.clickMoveThreshold)) {
            this.isClick = false;
        }

        for (var i = 0; i < gestures.length; i++) {
            if (e.stopped) {
                break;
            }

            gesture = gestures[i];

            if (gesture.listenForMove) {
                gesture.onTouchMove(e, touch);
            }
        }
    },

    
    onTouchEnd: function(e) {
        if (Ext.is.Blackberry) {
            e.preventDefault();
        }

        if (this.isFrozen) {
            return;
        }

        var gestures = this.currentGestures.slice(0),
            ln = gestures.length,
            i, gesture, endPoint,
            needsAnotherMove = false,
            touch = e.changedTouches ? e.changedTouches[0] : e;

        if (this.startPoint) {
            endPoint = Ext.util.Point.fromEvent(e);
            if (!(this.lastMovePoint || this.startPoint)['equals'](endPoint)) {
                needsAnotherMove = true;
            }
        }

        for (i = 0; i < ln; i++) {
            gesture = gestures[i];

            if (!e.stopped && gesture.listenForEnd) {
                
                
                
                if (needsAnotherMove) {
                    gesture.onTouchMove(e, touch);
                }

                gesture.onTouchEnd(e, touch);
            }

            this.stopGesture(gesture);
        }


        if (Ext.supports.Touch && this.isClick) {
            this.isClick = false;
            this.getEventSimulator().fire('click', this.startEvent.target, touch);
        }

        this.lastMovePoint = null;
        this.followTouches = [];
        this.startedChangedTouch = false;
        this.currentTargets = [];
        this.startEvent = null;
        this.startPoint = null;
    },

    handleTargets: function(targets, e) {
        
        
        var ln = targets.length,
            i;

        this.startedChangedTouch = false;
        this.startedTouches = Ext.supports.Touch ? e.touches : [e];

        for (i = 0; i < ln; i++) {
            if (e.stopped) {
                break;
            }

            this.handleTarget(targets[i], e, true);
        }

        for (i = ln - 1; i >= 0; i--) {
            if (e.stopped) {
                break;
            }

            this.handleTarget(targets[i], e, false);
        }

        if (this.startedChangedTouch) {
            this.followTouches = this.followTouches.concat((Ext.supports.Touch && e.targetTouches) ? Ext.toArray(e.targetTouches) : [e]);
        }
    },

    handleTarget: function(target, e, capture) {
        var gestures = Ext.Element.data(target, 'x-gestures') || [],
            ln = gestures.length,
            i, gesture;

        for (i = 0; i < ln; i++) {
            gesture = gestures[i];
            if (
                (!!gesture.capture === !!capture) &&
                (this.followTouches.length < gesture.touches) &&
                ((Ext.supports.Touch && e.targetTouches) ? (e.targetTouches.length === gesture.touches) : true)
            ) {
                this.startedChangedTouch = true;
                this.startGesture(gesture);

                if (gesture.listenForStart) {
                    gesture.onTouchStart(e, e.changedTouches ? e.changedTouches[0] : e);
                }

                if (e.stopped) {
                    break;
                }
            }
        }
    },

    startGesture: function(gesture) {
        gesture.started = true;
        this.currentGestures.push(gesture);
    },

    stopGesture: function(gesture) {
        gesture.started = false;
        this.currentGestures.remove(gesture);
    },

    addEventListener: function(target, eventName, listener, options) {
        target = Ext.getDom(target);
        options = options || {};

        var targets = this.targets,
            name = this.getGestureName(eventName),
            gestures = Ext.Element.data(target, 'x-gestures'),
            gesture;

        if (!gestures) {
            gestures = [];
            Ext.Element.data(target, 'x-gestures', gestures);
        }

        
        if (!name) {
            throw new Error('Trying to subscribe to unknown event ' + eventName);
        }
        

        if (targets.indexOf(target) === -1) {
            this.targets.push(target);
        }

        gesture = this.get(target.id + '-' + name);

        if (!gesture) {
            gesture = this.create(Ext.apply({}, options, {
                target: target,
                type: name
            }));

            gestures.push(gesture);
            
            
        }

        gesture.addListener(eventName, listener);

        
        if (this.startedChangedTouch && this.currentTargets.contains(target) && !gesture.started && !options.subsequent) {
            this.startGesture(gesture);
            if (gesture.listenForStart) {
                gesture.onTouchStart(this.startEvent, this.startedTouches[0]);
            }
        }
    },

    removeEventListener: function(target, eventName, listener) {
        target = Ext.getDom(target);

        var name = this.getGestureName(eventName),
            gestures = Ext.Element.data(target, 'x-gestures') || [],
            gesture;

        gesture = this.get(target.id + '-' + name);

        if (gesture) {
            gesture.removeListener(eventName, listener);

            for (name in gesture.listeners) {
                return;
            }

            gesture.destroy();
            gestures.remove(gesture);
            Ext.Element.data(target, 'x-gestures', gestures);
        }
    },

    getGestureName: function(ename) {
        return this.names && this.names[ename];
    },

    registerType: function(type, cls) {
        var handles = cls.prototype.handles,
            i, ln;

        this.types[type] = cls;

        cls[this.typeName] = type;

        if (!handles) {
            handles = cls.prototype.handles = [type];
        }

        this.names = this.names || {};

        for (i = 0, ln = handles.length; i < ln; i++) {
            this.names[handles[i]] = type;
        }
    }
});

Ext.regGesture = function() {
    return Ext.gesture.Manager.registerType.apply(Ext.gesture.Manager, arguments);
};
Ext.TouchEventObjectImpl = Ext.extend(Object, {
    constructor : function(e, args) {
        if (e) {
            this.setEvent(e, args);
        }
    },

    setEvent : function(e, args) {
        Ext.apply(this, {
            event: e,
            time: e.timeStamp
        });

        this.touches = e.touches || [e];
        this.changedTouches = e.changedTouches || [e];
        this.targetTouches = e.targetTouches || [e];
        
        if (args) {
            this.target = args.target;
            Ext.apply(this, args);
        }
        else {
            this.target = e.target;
        }
        return this;
    },

    stopEvent : function() {
        this.stopPropagation();
        this.preventDefault();
    },

    stopPropagation : function() {
        this.event.stopped = true;
    },

    preventDefault : function() {
        this.event.preventDefault();
    },

    getTarget : function(selector, maxDepth, returnEl) {
        if (selector) {
            return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
        }
        else {
            return returnEl ? Ext.get(this.target) : this.target;
        }
    }
});

Ext.TouchEventObject = new Ext.TouchEventObjectImpl();
Ext.gesture.Gesture = Ext.extend(Object, {    
    listenForStart: true,
    listenForEnd: true,
    listenForMove: true,
    
    disableLocking: false,
    
    touches: 1,
    
    constructor: function(config) {
        config = config || {};
        Ext.apply(this, config);
        
        this.target = Ext.getDom(this.target);
        this.listeners = {};
        
        
        if (!this.target) {
            throw new Error('Trying to bind a ' + this.type + ' event to element that does\'nt exist: ' + this.target);
        }
        
        
        this.id = this.target.id + '-' + this.type;
        
        Ext.gesture.Gesture.superclass.constructor.call(this);
        Ext.gesture.Manager.register(this);
    },
    
    addListener: function(name, listener) {
        this.listeners[name] = this.listeners[name] || [];
        this.listeners[name].push(listener);
    },
    
    removeListener: function(name, listener) {
        var listeners = this.listeners[name];
            
        if (listeners) {
            listeners.remove(listener);

            if (listeners.length == 0) {
                delete this.listeners[name];
            }

            for (name in this.listeners) {
                if (this.listeners.hasOwnProperty(name)) {
                    return;
                }
            }
            
            this.listeners = {};
        }
    },
    
    fire: function(type, e, args) {
        var listeners = this.listeners && this.listeners[type],
            ln = listeners && listeners.length,
            i;

        if (!this.disableLocking && this.isLocked(type)) {
            return false;
        }
        
        if (ln) {
            args = Ext.apply(args || {}, {
                time: e.timeStamp,
                type: type,
                gesture: this,
                target: (e.target.nodeType == 3) ? e.target.parentNode: e.target
            });
            
            for (i = 0; i < ln; i++) {
                listeners[i](e, args);
            }
        }
        
        return true;
    },
    
    stop: function() {
        Ext.gesture.Manager.stopGesture(this);
    },
    
    lock: function() {
        if (!this.disableLocking) {
            var args = arguments,
                ln = args.length,
                i;

            for (i = 0; i < ln; i++) {
                Ext.gesture.Manager.locks[args[i]] = this.id;
            }            
        }
    },
    
    unlock: function() {
        if (!this.disableLocking) {
            var args = arguments,
                ln = args.length,
                i;

            for (i = 0; i < ln; i++) {
                if (Ext.gesture.Manager.locks[args[i]] == this.id) {
                    delete Ext.gesture.Manager.locks[args[i]]; 
                }
            }            
        }
    },
    
    isLocked : function(type) {
        var lock = Ext.gesture.Manager.locks[type];
        return !!(lock && lock !== this.id);
    },
    
    getLockingGesture : function(type) {
        var lock = Ext.gesture.Manager.locks[type];
        if (lock) {
            return Ext.gesture.Manager.get(lock) || null;
        }
        return null;
    },
    
    onTouchStart: Ext.emptyFn,
    onTouchMove: Ext.emptyFn,
    onTouchEnd: Ext.emptyFn,
    
    destroy: function() {
        this.stop();
        this.listeners = null;
        Ext.gesture.Manager.unregister(this);
    }
});
Ext.gesture.Touch = Ext.extend(Ext.gesture.Gesture, {
    handles: ['touchstart', 'touchmove', 'touchend', 'touchdown'],
    
    touchDownInterval: 500,
    
    onTouchStart: function(e, touch) {
        this.startX = this.previousX = touch.pageX;
        this.startY = this.previousY = touch.pageY;
        this.startTime = this.previousTime = e.timeStamp;

        this.fire('touchstart', e);
        this.lastEvent = e;
        
        if (this.listeners && this.listeners.touchdown) {
            this.touchDownIntervalId = setInterval(Ext.createDelegate(this.touchDownHandler, this), this.touchDownInterval);
        }
    },
    
    onTouchMove: function(e, touch) {
        this.fire('touchmove', e, this.getInfo(touch));
        this.lastEvent = e;
    },
    
    onTouchEnd: function(e) {
        this.fire('touchend', e, this.lastInfo);
        clearInterval(this.touchDownIntervalId);
    },
    
    touchDownHandler : function() {
        this.fire('touchdown', this.lastEvent, this.lastInfo);
    },
    
    getInfo : function(touch) {
        var time = Date.now(),
            deltaX = touch.pageX - this.startX,
            deltaY = touch.pageY - this.startY,
            info = {
                startX: this.startX,
                startY: this.startY,
                previousX: this.previousX,
                previousY: this.previousY,
                pageX: touch.pageX,
                pageY: touch.pageY,
                deltaX: deltaX,
                deltaY: deltaY,
                absDeltaX: Math.abs(deltaX),
                absDeltaY: Math.abs(deltaY),
                previousDeltaX: touch.pageX - this.previousX,
                previousDeltaY: touch.pageY - this.previousY,
                time: time,
                startTime: this.startTime,
                previousTime: this.previousTime,
                deltaTime: time - this.startTime,
                previousDeltaTime: time - this.previousTime
            };
        
        this.previousTime = info.time;
        this.previousX = info.pageX;
        this.previousY = info.pageY;
        this.lastInfo = info;
        
        return info;
    }
});

Ext.regGesture('touch', Ext.gesture.Touch);
Ext.gesture.Tap = Ext.extend(Ext.gesture.Gesture, {
    handles: [
        'tapstart',
        'tapcancel',
        'tap', 
        'doubletap', 
        'taphold',
        'singletap'
    ],
    
    cancelThreshold: 10,
    
    doubleTapThreshold: 800,

    singleTapThreshold: 400,

    holdThreshold: 1000,

    fireClickEvent: false,
    
    onTouchStart : function(e, touch) {
        var me = this;
        
        me.startX = touch.pageX;
        me.startY = touch.pageY;
        me.fire('tapstart', e, me.getInfo(touch));
        
        if (this.listeners.taphold) {    
            me.timeout = setTimeout(function() {
                me.fire('taphold', e, me.getInfo(touch));
                delete me.timeout;
            }, me.holdThreshold);            
        }
        
        me.lastTouch = touch;
    },
    
    onTouchMove : function(e, touch) {
        var me = this;
        if (me.isCancel(touch)) {
            me.fire('tapcancel', e, me.getInfo(touch));
            if (me.timeout) {
                clearTimeout(me.timeout);
                delete me.timeout;
            }
            me.stop();
        }
        
        me.lastTouch = touch;
    },
    
    onTouchEnd : function(e) {
        var me = this,
            info = me.getInfo(me.lastTouch);
        
        this.fireTapEvent(e, info);
        
        if (me.lastTapTime && e.timeStamp - me.lastTapTime <= me.doubleTapThreshold) {
            me.lastTapTime = null;
            e.preventDefault();
            me.fire('doubletap', e, info);
        }
        else {
            me.lastTapTime = e.timeStamp;
        }

        if (me.listeners && me.listeners.singletap && me.singleTapThreshold && !me.preventSingleTap) {
            me.fire('singletap', e, info);
            me.preventSingleTap = true;
            setTimeout(function() {
                me.preventSingleTap = false;
            }, me.singleTapThreshold);
        }
        
        if (me.timeout) {
            clearTimeout(me.timeout);
            delete me.timeout;
        }
    },

    fireTapEvent: function(e, info) {
        this.fire('tap', e, info);
        
        if (e.event)
            e = e.event;

        var target = (e.changedTouches ? e.changedTouches[0] : e).target;

        if (!target.disabled && this.fireClickEvent) {
            var clickEvent = document.createEvent("MouseEvent");
                clickEvent.initMouseEvent('click', e.bubbles, e.cancelable, document.defaultView, e.detail, e.screenX, e.screenY, e.clientX,
                                         e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.metaKey, e.button, e.relatedTarget);
                clickEvent.isSimulated = true;


            target.dispatchEvent(clickEvent);
        }
    },
    
    getInfo : function(touch) {
        var x = touch.pageX,
            y = touch.pageY;
            
        return {
            pageX: x,
            pageY: y,
            startX: x,
            startY: y
        };
    },
    
    isCancel : function(touch) {
        var me = this;
        return (
            Math.abs(touch.pageX - me.startX) >= me.cancelThreshold ||
            Math.abs(touch.pageY - me.startY) >= me.cancelThreshold
        );
    }
});
Ext.regGesture('tap', Ext.gesture.Tap);
Ext.gesture.Swipe = Ext.extend(Ext.gesture.Gesture, {    
    listenForEnd: false,
   
    swipeThreshold: 35,
    swipeTime: 1000,
    
    onTouchStart : function(e, touch) {
        this.startTime = e.timeStamp;
        this.startX = touch.pageX;
        this.startY = touch.pageY;
        this.lock('scroll', 'scrollstart', 'scrollend');
    },
   
    onTouchMove : function(e, touch) {
        var deltaY = touch.pageY - this.startY,
            deltaX = touch.pageX - this.startX,
            absDeltaY = Math.abs(deltaY),
            absDeltaX = Math.abs(deltaX),
            deltaTime = e.timeStamp - this.startTime;

        
        if (absDeltaY - absDeltaX > 3 || deltaTime > this.swipeTime) {
            this.unlock('drag', 'dragstart', 'dragend');
            this.stop();
        }
        else if (absDeltaX > this.swipeThreshold && absDeltaX > absDeltaY) {
           
           this.fire('swipe', e, {
               direction: (deltaX < 0) ? 'left' : 'right',
               distance: absDeltaX,
               deltaTime: deltaTime,
               deltaX: deltaX
           });
   
           this.stop();
        }
    }
});
Ext.regGesture('swipe', Ext.gesture.Swipe);
Ext.gesture.Drag = Ext.extend(Ext.gesture.Touch, {
    handles: ['dragstart', 'drag', 'dragend'],

    dragThreshold: 5,

    direction: 'both',

    horizontal: false,
    vertical: false,

    constructor: function() {
        var me = this;
        Ext.gesture.Drag.superclass.constructor.apply(me, arguments);

        if (me.direction == 'both') {
            me.horizontal = true;
            me.vertical = true;
        } else if (me.direction == 'horizontal') {
            me.horizontal = true;
        } else {
            me.vertical = true;
        }

        return me;
    },

    onTouchStart: function(e, touch) {
        var me = this;
        me.startX = me.previousX = touch.pageX;
        me.startY = me.previousY = touch.pageY;
        me.startTime = me.previousTime = e.timeStamp;

        me.dragging = false;
    },

    onTouchMove: function(e, touch) {
        var me = this;
        if (me.isLocked('drag')) {
            return;
        }

        var info = me.getInfo(touch);

        if (!me.dragging) {
            if ((!e.touches || e.touches.length < 2) && me.isDragging(info) && me.fire('dragstart', e, info)) {
                me.dragging = true;
                me.lock('drag', 'dragstart', 'dragend');
                me.fire('drag', e, info);
            }
        } else {
            me.fire('drag', e, info);
        }
    },

    onTouchEnd: function(e) {
        var me = this;
        if (me.dragging) {
            me.fire('dragend', e, me.lastInfo);
        }

        me.dragging = false;
    },

    isDragging: function(info) {
        var me = this;
        return ((me.horizontal && info.absDeltaX >= me.dragThreshold) || (me.vertical && info.absDeltaY >= me.dragThreshold));
    },

    
    isVertical: function() {
        return this.vertical;
    },

    
    isHorizontal: function() {
        return this.horizontal;
    }
});

Ext.regGesture('drag', Ext.gesture.Drag);
Ext.gesture.Pinch = Ext.extend(Ext.gesture.Gesture, {
    handles: ['pinchstart', 'pinch', 'pinchend'],

    touches: 2,

    onTouchStart: function(e) {
        var me = this;

        if (this.isMultiTouch(e)) {
            me.lock('swipe', 'scroll', 'scrollstart', 'scrollend', 'touchmove', 'touchend', 'touchstart', 'tap', 'tapstart', 'taphold', 'tapcancel', 'doubletap');
            me.pinching = true;

            var targetTouches = e.targetTouches;

            me.startFirstTouch = targetTouches[0];
            me.startSecondTouch = targetTouches[1];

            me.previousDistance = me.startDistance = me.getDistance(me.startFirstTouch, me.startSecondTouch);
            me.previousScale = 1;

            me.fire('pinchstart', e, {
                distance: me.startDistance,
                scale: me.previousScale
            });
        } else if (me.pinching) {
            me.unlock('swipe', 'scroll', 'scrollstart', 'scrollend', 'touchmove', 'touchend', 'touchstart', 'tap', 'tapstart', 'taphold', 'tapcancel', 'doubletap');
            me.pinching = false;
        }
    },

    isMultiTouch: function(e) {
        return e && Ext.supports.Touch && e.targetTouches && e.targetTouches.length > 1;
    },

    onTouchMove: function(e) {
        if (!this.isMultiTouch(e)) {
            this.onTouchEnd(e);
            return;
        }

        if (this.pinching) {
            this.fire('pinch', e, this.getPinchInfo(e));
        }
    },

    onTouchEnd: function(e) {
        if (this.pinching) {
            this.fire('pinchend', e);
        }
    },

    getPinchInfo: function(e) {
        var me = this,
            targetTouches = e.targetTouches,
            firstTouch = targetTouches[0],
            secondTouch = targetTouches[1],
            distance = me.getDistance(firstTouch, secondTouch),
            scale = distance / me.startDistance,
            info = {
            scale: scale,
            deltaScale: scale - 1,
            previousScale: me.previousScale,
            previousDeltaScale: scale - me.previousScale,
            distance: distance,
            deltaDistance: distance - me.startDistance,
            startDistance: me.startDistance,
            previousDistance: me.previousDistance,
            previousDeltaDistance: distance - me.previousDistance,
            firstTouch: firstTouch,
            secondTouch: secondTouch,
            firstPageX: firstTouch.pageX,
            firstPageY: firstTouch.pageY,
            secondPageX: secondTouch.pageX,
            secondPageY: secondTouch.pageY,
            
            midPointX: (firstTouch.pageX + secondTouch.pageX) / 2,
            midPointY: (firstTouch.pageY + secondTouch.pageY) / 2
        };

        me.previousScale = scale;
        me.previousDistance = distance;

        return info;
    },

    getDistance: function(firstTouch, secondTouch) {
        return Math.sqrt(
        Math.pow(Math.abs(firstTouch.pageX - secondTouch.pageX), 2) + Math.pow(Math.abs(firstTouch.pageY - secondTouch.pageY), 2));
    }
});

Ext.regGesture('pinch', Ext.gesture.Pinch);



Ext.lib.Component = Ext.extend(Ext.util.Observable, {
    isComponent: true,

    
    renderTpl: null,

    

    

    

    

    

    
    tplWriteMode: 'overwrite',

    
    baseCls: 'x-component',

    

    

    
    disabledCls: 'x-item-disabled',

    

   

    

    

    

    

    

    
    hidden: false,

    
    disabled: false,

    

    
    draggable: false,

    
    floating: false,

    

    

    
    styleHtmlContent: false,

    
    styleHtmlCls: 'x-html',

    
    
    
    

     
     allowDomMove: true,
     autoShow: false,

     autoRender: false,

     needsLayout: false,

    

    
    rendered: false,

    constructor : function(config) {
        config = config || {};
        this.initialConfig = config;
        Ext.apply(this, config);

        this.addEvents(
            
             'beforeactivate',
            
             'activate',
            
             'beforedeactivate',
            
             'deactivate',
            
             'added',
            
             'disable',
            
             'enable',
            
             'beforeshow',
            
             'show',
            
             'beforehide',
            
             'hide',
            
             'removed',
            
             'beforerender',
            
             'render',
            
             'afterrender',
            
             'beforedestroy',
            
             'destroy',
            
             'resize',
            
             'move',

             'beforestaterestore',
             'staterestore',
             'beforestatesave',
             'statesave'
        );

        this.getId();

        this.mons = [];
        this.additionalCls = [];
        this.renderData = this.renderData || {};
        this.renderSelectors = this.renderSelectors || {};

        this.initComponent();

        
        Ext.ComponentMgr.register(this);

        
        Ext.lib.Component.superclass.constructor.call(this);

        
        if (this.plugins) {
            this.plugins = [].concat(this.plugins);
            for (var i = 0, len = this.plugins.length; i < len; i++) {
                this.plugins[i] = this.initPlugin(this.plugins[i]);
            }
        }

        
        if (this.applyTo) {
            this.applyToMarkup(this.applyTo);
            delete this.applyTo;
        }
        else if (this.renderTo) {
            this.render(this.renderTo);
            delete this.renderTo;
        }

        if (Ext.isDefined(this.disabledClass)) {
            throw "Component: disabledClass has been deprecated. Please use disabledCls.";
        }
    },

    initComponent: Ext.emptyFn,
    applyToMarkup: Ext.emptyFn,

    show: Ext.emptyFn,

    onShow : function() {
        
        var needsLayout = this.needsLayout;
        if (Ext.isObject(needsLayout)) {
            this.doComponentLayout(needsLayout.width, needsLayout.height, needsLayout.isSetSize);
        }
    },

    
    initPlugin : function(plugin) {
        if (plugin.ptype && typeof plugin.init != 'function') {
            plugin = Ext.PluginMgr.create(plugin);
        }
        else if (typeof plugin == 'string') {
            plugin = Ext.PluginMgr.create({
                ptype: plugin
            });
        }

        plugin.init(this);

        return plugin;
    },

    
    render : function(container, position) {
        if (!this.rendered && this.fireEvent('beforerender', this) !== false) {
            
            
            if (this.el) {
                this.el = Ext.get(this.el);
            }

            container = this.initContainer(container);

            this.onRender(container, position);
            this.fireEvent('render', this);

            this.initContent();

            this.afterRender(container);
            this.fireEvent('afterrender', this);

            this.initEvents();

            if (this.autoShow) {
                this.show();
            }

            if (this.hidden) {
                
                this.onHide(false); 
            }

            if (this.disabled) {
                
                this.disable(true);
            }
        }

        return this;
    },

    
    onRender : function(container, position) {
        var el = this.el,
            renderTpl,
            renderData;

        position = this.getInsertPosition(position);

        if (!el) {
            if (position) {
                el = Ext.DomHelper.insertBefore(position, this.getElConfig(), true);
            }
            else {
                el = Ext.DomHelper.append(container, this.getElConfig(), true);
            }
        }
        else if (this.allowDomMove !== false) {
            container.dom.insertBefore(el.dom, position);
        }

        el.addCls(this.initCls());
        el.setStyle(this.initStyles());

        
        
        
        
        
        
        
        
        
        

        renderTpl = this.initRenderTpl();
        if (renderTpl) {
            renderData = this.initRenderData();
            renderTpl.append(el, renderData);
        }

        this.el = el;
        this.applyRenderSelectors();
        this.rendered = true;
    },

    
    initCls: function() {
        var cls = [ this.baseCls ];

        if (this.componentCls) {
            cls.push(this.componentCls);
        }
        else {
            this.componentCls = this.baseCls;
        }
        if (this.cls) {
            cls.push(this.cls);
            delete this.cls;
        }
        if (this.ui) {
            cls.push(this.componentCls + '-' + this.ui);
        }
        return cls.concat(this.additionalCls);
    },

    
    afterRender : function() {
        this.getComponentLayout();

        if (this.x || this.y) {
            this.setPosition(this.x, this.y);
        }

        if (this.styleHtmlContent) {
            this.getTargetEl().addCls(this.styleHtmlCls);
        }

        
        
        if (!this.ownerCt) {
            this.setSize(this.width, this.height);
        }
    },

    getElConfig : function() {
        return {tag: 'div', id: this.id};
    },

    
    getInsertPosition: function(position) {
        
        if (position !== undefined) {
            if (Ext.isNumber(position)) {
                position = this.container.dom.childNodes[position];
            }
            else {
                position = Ext.getDom(position);
            }
        }

        return position;
    },

    
    initContainer: function(container) {
        
        
        
        if (!container && this.el) {
            container = this.el.dom.parentNode;
            this.allowDomMove = false;
        }

        this.container = Ext.get(container);

        if (this.ctCls) {
            this.container.addCls(this.ctCls);
        }

        return this.container;
    },

    
    initRenderData: function() {
        return Ext.applyIf(this.renderData, {
            baseCls: this.baseCls,
            componentCls: this.componentCls
        });
    },

    
    initRenderTpl: function() {
        var renderTpl = this.renderTpl;
        if (renderTpl) {
            if (this.proto.renderTpl !== renderTpl) {
                if (Ext.isArray(renderTpl) || typeof renderTpl === "string") {
                    renderTpl = new Ext.XTemplate(renderTpl);
                }
            }
            else if (Ext.isArray(this.proto.renderTpl)){
                renderTpl = this.proto.renderTpl = new Ext.XTemplate(renderTpl);
            }
        }
        return renderTpl;
    },

    
    initStyles: function() {
        var style = {},
            Element = Ext.Element,
            i, ln, split, prop;

        if (Ext.isString(this.style)) {
            split = this.style.split(';');
            for (i = 0, ln = split.length; i < ln; i++) {
                if (!Ext.isEmpty(split[i])) {
                    prop = split[i].split(':');
                    style[Ext.util.Format.trim(prop[0])] = Ext.util.Format.trim(prop[1]);
                }
            }
        } else {
            style = Ext.apply({}, this.style);
        }

        
        
        if (this.padding != undefined) {
            style.padding = Element.unitizeBox((this.padding === true) ? 5 : this.padding);
        }

        if (this.margin != undefined) {
            style.margin = Element.unitizeBox((this.margin === true) ? 5 : this.margin);
        }

        if (this.border != undefined) {
            style.borderWidth = Element.unitizeBox((this.border === true) ? 1 : this.border);
        }

        delete this.style;
        return style;
    },

    
    initContent: function() {
        var target = this.getTargetEl();

        if (this.html) {
            target.update(Ext.DomHelper.markup(this.html));
            delete this.html;
        }

        if (this.contentEl) {
            var contentEl = Ext.get(this.contentEl);
            contentEl.show();
            target.appendChild(contentEl.dom);
        }

        if (this.tpl) {
            
            if (!this.tpl.isTemplate) {
                this.tpl = new Ext.XTemplate(this.tpl);
            }

            if (this.data) {
                this.tpl[this.tplWriteMode](target, this.data);
                delete this.data;
            }
        }
    },

    
    initEvents : function() {
        var afterRenderEvents = this.afterRenderEvents,
            property, listeners;
        if (afterRenderEvents) {
            for (property in afterRenderEvents) {
                if (!afterRenderEvents.hasOwnProperty(property)) {
                    continue;
                }
                listeners = afterRenderEvents[property];
                if (this[property] && this[property].on) {
                    this.mon(this[property], listeners);
                }
            }
        }
    },

    
    applyRenderSelectors: function() {
        var selectors = this.renderSelectors || {},
            el = this.el.dom,
            selector;

        for (selector in selectors) {
            if (!selectors.hasOwnProperty(selector)) {
                continue;
            }
            this[selector] = Ext.get(Ext.DomQuery.selectNode(selectors[selector], el));
        }
    },

    is: function(selector) {
        return Ext.ComponentQuery.is(this, selector);
    },

    
    up: function(selector) {
        var result = this.ownerCt;
        if (selector) {
            for (; result; result = result.ownerCt) {
                if (Ext.ComponentQuery.is(result, selector)) {
                    return result;
                }
            }
        }
        return result;
    },

    
    nextSibling: function(selector) {
        var o = this.ownerCt, it, last, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this) + 1;
            if (idx) {
                if (selector) {
                    for (last = it.getCount(); idx < last; idx++) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx < it.getCount()) {
                        return it.getAt(idx);
                    }
                }
            }
        }
        return null;
    },

    
    previousSibling: function(selector) {
        var o = this.ownerCt, it, idx, c;
        if (o) {
            it = o.items;
            idx = it.indexOf(this);
            if (idx != -1) {
                if (selector) {
                    for (--idx; idx >= 0; idx--) {
                        if ((c = it.getAt(idx)).is(selector)) {
                            return c;
                        }
                    }
                } else {
                    if (idx) {
                        return it.getAt(--idx);
                    }
                }
            }
        }
        return null;
    },

    
    getId : function() {
        return this.id || (this.id = 'ext-comp-' + (++Ext.lib.Component.AUTO_ID));
    },

    getItemId : function() {
        return this.itemId || this.id;
    },

    
    getEl : function() {
        return this.el;
    },

    
    getTargetEl: function() {
        return this.el;
    },

    
    isXType: function(xtype, shallow) {
        
        if (Ext.isFunction(xtype)) {
            xtype = xtype.xtype;
            
        } else if (Ext.isObject(xtype)) {
            xtype = xtype.constructor.xtype;
            
        }

        return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1: this.constructor.xtype == xtype;
    },

    
    getXTypes: function() {
        var constructor = this.constructor,
            xtypes      = [],
            superclass  = this,
            xtype;

        if (!constructor.xtypes) {
            while (superclass) {
                xtype = superclass.constructor.xtype;

                if (xtype != undefined) {
                    xtypes.unshift(xtype);
                }

                superclass = superclass.constructor.superclass;
            }

            constructor.xtypeChain = xtypes;
            constructor.xtypes = xtypes.join('/');
        }

        return constructor.xtypes;
    },

    
    update : function(htmlOrData, loadScripts, cb) {
        if (this.tpl && !Ext.isString(htmlOrData)) {
            this.data = htmlOrData;
            if (this.rendered) {
                this.tpl[this.tplWriteMode](this.getTargetEl(), htmlOrData || {});
            }
        }
        else {
            this.html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
            if (this.rendered) {
                this.getTargetEl().update(this.html, loadScripts, cb);
            }
        }

        if (this.rendered) {
            this.doComponentLayout();
        }
    },

    
    setVisible : function(visible) {
        return this[visible ? 'show': 'hide']();
    },

    
    isVisible: function() {
        var visible = !this.hidden,
            cmp     = this.ownerCt;

        
        this.hiddenOwnerCt = false;
        if (this.destroyed) {
            return false;
        };

        if (visible && this.rendered && cmp) {
            while (cmp) {
                if (cmp.hidden || cmp.collapsed) {
                    
                    this.hiddenOwnerCt = cmp;
                    visible = false;
                    break;
                }
                cmp = cmp.ownerCt;
            }
        }
        return visible;
    },

    
    enable : function(silent) {
        if (this.rendered) {
            this.el.removeCls(this.disabledCls);
            this.el.dom.disabled = false;
            this.onEnable();
        }

        this.disabled = false;

        if (silent !== true) {
            this.fireEvent('enable', this);
        }

        return this;
    },

    
    disable : function(silent) {
        if (this.rendered) {
            this.el.addCls(this.disabledCls);
            this.el.dom.disabled = true;
            this.onDisable();
        }

        this.disabled = true;

        if (silent !== true) {
            this.fireEvent('disable', this);
        }

        return this;
    },

    
    isDisabled : function() {
        return this.disabled;
    },

    
    setDisabled : function(disabled) {
        return this[disabled ? 'disable': 'enable']();
    },

    
    isHidden : function() {
        return this.hidden;
    },

    
    addCls : function() {
        var me = this,
            args = Ext.toArray(arguments);
        if (me.rendered) {
            me.el.addCls(args);
        }
        else {
            me.additionalCls = me.additionalCls.concat(args);
        }
        return me;
    },

    addClass : function() {
        throw "Component: addClass has been deprecated. Please use addCls.";
    },

    
    removeCls : function() {
        var me = this,
            args = Ext.toArray(arguments);
        if (me.rendered) {
            me.el.removeCls(args);
        }
        else if (me.additionalCls.length) {
            Ext.each(args, function(cls) {
                me.additionalCls.remove(cls);
            });
        }
        return me;
    },

    removeClass : function() {
        throw "Component: removeClass has been deprecated. Please use removeCls.";
    },

    addListener : function(element, listeners, scope, options) {
        if (Ext.isString(element) && (Ext.isObject(listeners) || options && options.element)) {
            if (options.element) {
                var fn = listeners,
                    option;

                listeners = {};
                listeners[element] = fn;
                element = options.element;
                if (scope) {
                    listeners.scope = scope;
                }

                for (option in options) {
                    if (!options.hasOwnProperty(option)) {
                        continue;
                    }
                    if (this.eventOptionsRe.test(option)) {
                        listeners[option] = options[option];
                    }
                }
            }

            
            
            if (this[element] && this[element].on) {
                this.mon(this[element], listeners);
            }
            else {
                this.afterRenderEvents = this.afterRenderEvents || {};
                this.afterRenderEvents[element] = listeners;
            }
        }

        return Ext.lib.Component.superclass.addListener.apply(this, arguments);
    },

    

    
    getBubbleTarget : function() {
        return this.ownerCt;
    },

    
    isFloating : function() {
        return this.floating;
    },

    
    isDraggable : function() {
        return !!this.draggable;
    },

    
    isDroppable : function() {
        return !!this.droppable;
    },

    
    onAdded : function(container, pos) {
        this.ownerCt = container;
        this.fireEvent('added', this, container, pos);
    },

    
    onRemoved : function() {
        this.fireEvent('removed', this, this.ownerCt);
        delete this.ownerCt;
    },

    
    onEnable : Ext.emptyFn,
    
    onDisable : Ext.emptyFn,
    
    beforeDestroy : Ext.emptyFn,
    
    
    onResize : Ext.emptyFn,

    
    setSize : function(width, height) {
        
        if (Ext.isObject(width)) {
            height = width.height;
            width  = width.width;
        }
        if (!this.rendered || !this.isVisible()) {
            
            if (this.hiddenOwnerCt) {
                var layoutCollection = this.hiddenOwnerCt.layoutOnShow;
                layoutCollection.remove(this);
                layoutCollection.add(this);
            }
            this.needsLayout = {
                width: width,
                height: height,
                isSetSize: true
            };
            if (!this.rendered) {
                this.width  = (width !== undefined) ? width : this.width;
                this.height = (height !== undefined) ? height : this.height;
            }
            return this;
        }
        this.doComponentLayout(width, height, true);

        return this;
    },

    setCalculatedSize : function(width, height) {
        
        if (Ext.isObject(width)) {
            height = width.height;
            width  = width.width;
        }
        if (!this.rendered || !this.isVisible()) {
            
            if (this.hiddenOwnerCt) {
                var layoutCollection = this.hiddenOwnerCt.layoutOnShow;
                layoutCollection.remove(this);
                layoutCollection.add(this);
            }
            this.needsLayout = {
                width: width,
                height: height,
                isSetSize: false
            };
            return this;
        }
        this.doComponentLayout(width, height);

        return this;
    },

    
    doComponentLayout : function(width, height, isSetSize) {
        var componentLayout = this.getComponentLayout();
        if (this.rendered && componentLayout) {
            width = (width !== undefined || this.collapsed === true) ? width : this.width;
            height = (height !== undefined || this.collapsed === true) ? height : this.height;
            if (isSetSize) {
                this.width = width;
                this.height = height;
            }

            componentLayout.layout(width, height);
        }
        return this;
    },

    
    setComponentLayout : function(layout) {
        var currentLayout = this.componentLayout;
        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null);
        }
        this.componentLayout = layout;
        layout.setOwner(this);
    },

    getComponentLayout : function() {
        if (!this.componentLayout || !this.componentLayout.isLayout) {
            this.setComponentLayout(Ext.layout.LayoutManager.create(this.componentLayout, 'autocomponent'));
        }
        return this.componentLayout;
    },

    afterComponentLayout: Ext.emptyFn,

    
    setPosition : function(x, y) {
        if (Ext.isObject(x)) {
            y = x.y;
            x = x.x;
        }

        if (!this.rendered) {

            return this;
        }

        if (x !== undefined || y !== undefined) {
            this.el.setBox(x, y);
            this.onPosition(x, y);
            this.fireEvent('move', this, x, y);
        }
        return this;
    },

    
    onPosition: Ext.emptyFn,

    
    setWidth : function(width) {
        return this.setSize(width);
    },

    
    setHeight : function(height) {
        return this.setSize(undefined, height);
    },

    
    getSize : function() {
        return this.el.getSize();
    },

    
    getWidth : function() {
        return this.el.getWidth();
    },

    
    getHeight : function() {
        return this.el.getHeight();
    },

    
    setLoading : function(load, targetEl) {
        if (this.rendered) {
            if (load) {
                this.loadMask = this.loadMask || new Ext.LoadMask(targetEl ? this.getTargetEl() : this.el, Ext.applyIf(Ext.isObject(load) ? load : {}));
                this.loadMask.show();
            } else {
                Ext.destroy(this.loadMask);
                this.loadMask = null;
            }
        }

        return this.loadMask;
    },

    
    setDocked : function(dock, layoutParent) {
        this.dock = dock;
        if (layoutParent && this.ownerCt && this.rendered) {
            this.ownerCt.doComponentLayout();
        }
        return this;
    },

    onDestroy : function() {
        if (this.monitorResize && Ext.EventManager.resizeEvent) {
            Ext.EventManager.resizeEvent.removeListener(this.setSize, this);
        }
        Ext.destroy(this.componentLayout, this.loadMask);
    },

    
    destroy : function() {
        if (!this.isDestroyed) {
            if (this.fireEvent('beforedestroy', this) !== false) {
                this.destroying = true;
                this.beforeDestroy();

                if (this.ownerCt && this.ownerCt.remove) {
                    this.ownerCt.remove(this, false);
                }

                if (this.rendered) {
                    this.el.remove();
                }

                this.onDestroy();

                Ext.ComponentMgr.unregister(this);
                this.fireEvent('destroy', this);

                this.clearListeners();
                this.destroying = false;
                this.isDestroyed = true;
            }
        }
    }
});

Ext.lib.Component.prototype.on = Ext.lib.Component.prototype.addListener;
Ext.lib.Component.prototype.prev = Ext.lib.Component.prototype.previousSibling;
Ext.lib.Component.prototype.next = Ext.lib.Component.prototype.nextSibling;


Ext.lib.Component.AUTO_ID = 1000;


Ext.Component = Ext.extend(Ext.lib.Component, {});
Ext.reg('component', Ext.Component);


Ext.Component = Ext.extend(Ext.lib.Component, {
    
    showAnimation: null,

    
    monitorOrientation: false,

    
    floatingCls: 'x-floating',

    
    hideOnMaskTap: true,
    
        
    stopMaskTapEvent: true,

    
    centered: false,

    
    modal: false,

    
     
     
    fullscreen: false,

    
    layoutOnOrientationChange: null,
    
    
    initComponent : function() {
        this.addEvents(
            
            'beforeorientationchange',
            
            'orientationchange'
        );

        if (this.fullscreen || this.floating) {
            this.monitorOrientation = true;
            this.autoRender = true;
        }

        if (this.fullscreen) {
            var viewportSize = Ext.Viewport.getSize();
            this.width = viewportSize.width;
            this.height = viewportSize.height;
            this.cls = (this.cls || '') + ' x-fullscreen';
            this.renderTo = document.body;
        }
    },

    onRender : function() {
        Ext.Component.superclass.onRender.apply(this, arguments);

        if (this.monitorOrientation) {
            this.el.addCls('x-' + Ext.Viewport.orientation);
        }

        if (this.floating) {
            this.setFloating(true);
        }

        if (this.draggable) {
            this.setDraggable(this.draggable);
        }

        if (this.scroll) {
            this.setScrollable(this.scroll);
        }
    },

    afterRender : function() {
        if (this.fullscreen) {
            this.layoutOrientation(Ext.Viewport.orientation, this.width, this.height);
        }
        Ext.Component.superclass.afterRender.call(this);
    },

    initEvents : function() {
        Ext.Component.superclass.initEvents.call(this);

        if (this.monitorOrientation) {
            Ext.EventManager.onOrientationChange(this.setOrientation, this);
        }
    },

    
    
    afterComponentLayout : function() {
        var scrollEl = this.scrollEl,
            scroller = this.scroller,
            parentEl;

        if (scrollEl) {
            parentEl = scrollEl.parent();

            if (scroller.horizontal) {
                scrollEl.setStyle('min-width', parentEl.getWidth(true) + 'px');
                scrollEl.setHeight(parentEl.getHeight(true) || null);
            }
            if (scroller.vertical) {
                scrollEl.setStyle('min-height', parentEl.getHeight(true) + 'px');
                scrollEl.setWidth(parentEl.getWidth(true) || null);
            }
            scroller.updateBoundary(true);
        }

        if (this.fullscreen && Ext.is.iPad) {
            Ext.repaint();
        }
    },

    layoutOrientation: Ext.emptyFn,

    
    update: function(){
        
        Ext.Component.superclass.update.apply(this, arguments);
        var scroller = this.scroller;
        if (scroller && scroller.updateBoundary){
            scroller.updateBoundary(true);
        }
    },

    
    show : function(animation) {
        var rendered = this.rendered;
        if ((this.hidden || !rendered) && this.fireEvent('beforeshow', this) !== false) {
            if (this.anchorEl) {
                this.anchorEl.hide();
            }
            if (!rendered && this.autoRender) {
                this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
            }
            this.hidden = false;
            if (this.rendered) {
                this.onShow(animation);
                this.fireEvent('show', this);
            }
        }
        return this;
    },

    
    showBy : function(alignTo, animation, allowSides, anchor) {
        if (!this.floating) {
            return this;
        }
        
        if (alignTo.isComponent) {
            alignTo = alignTo.el;
        }
        else {
            alignTo = Ext.get(alignTo);
        }

        this.x = 0;
        this.y = 0;

        this.show(animation);

        if (anchor !== false) {
            if (!this.anchorEl) {
                this.anchorEl = this.el.createChild({
                    cls: 'x-anchor'
                });
            }
            this.anchorEl.show();            
        }
        
        this.alignTo(alignTo, allowSides, 20);
    },
    
    alignTo : function(alignTo, allowSides, offset) {
        
        var alignBox = alignTo.getPageBox(),
            constrainBox = {
                width: window.innerWidth,
                height: window.innerHeight
            },
            size = this.getSize(),
            newSize = {
                width: Math.min(size.width, constrainBox.width),
                height: Math.min(size.height, constrainBox.height)
            },
            position,
            index = 2,
            positionMap = [
                'tl-bl',
                't-b',
                'tr-br',
                'l-r',
                'l-r',
                'r-l',
                'bl-tl',
                'b-t',
                'br-tr'
            ],
            anchorEl = this.anchorEl,
            offsets = [0, offset],
            targetBox, cls,
            onSides = false,
            arrowOffsets = [0, 0],
            alignCenterX = alignBox.left + (alignBox.width / 2),
            alignCenterY = alignBox.top + (alignBox.height / 2);

        if (alignCenterX <= constrainBox.width * (1/3)) {
            index = 1;
            arrowOffsets[0] = 25;
        }
        else if (alignCenterX >= constrainBox.width * (2/3)) {
            index = 3;
            arrowOffsets[0] = -30;
        }
        
        if (alignCenterY >= constrainBox.height * (2/3)) {
            index += 6;
            offsets = [0, -offset];
            arrowOffsets[1] = -10;
        }
        
        
        else if (allowSides !== false && alignCenterY >= constrainBox.height * (1/3)) {
            index += 3;
            offsets = (index <= 5) ? [offset, 0] : [-offset, 0];
            arrowOffsets = (index <= 5) ? [10, 0] : [-10, 0];
            onSides = true;
        }
        else {
            arrowOffsets[1] = 10;
        }
        
        position = positionMap[index-1];
        targetBox = this.el.getAlignToXY(alignTo, position, offsets);

        
        
        if (onSides) {
            if (targetBox[0] < 0) {
                newSize.width = alignBox.left - offset;
            }
            else if (targetBox[0] + newSize.width > constrainBox.width) {
                newSize.width = constrainBox.width - alignBox.right - offset;
            }
        }
        else {
            if (targetBox[1] < 0) {
                newSize.height = alignBox.top - offset;
            }
            else if (targetBox[1] + newSize.height > constrainBox.height) {
                newSize.height = constrainBox.height - alignBox.bottom - offset;
            }
        }
        
        if (newSize.width != size.width) {
            this.setSize(newSize.width);
        }
        else if (newSize.height != size.height) {
            this.setSize(undefined, newSize.height);
        }

        targetBox = this.el.getAlignToXY(alignTo, position, offsets);                
        this.setPosition(targetBox[0], targetBox[1]);
        
        if (anchorEl) {
            
            anchorEl.removeCls(['x-anchor-bottom', 'x-anchor-left', 'x-anchor-right', 'x-anchor-top']);
            if (offsets[1] == offset) {
                cls = 'x-anchor-top';
            }
            else if (offsets[1] == -offset) {
                cls = 'x-anchor-bottom';
            }
            else if (offsets[0] == offset) {
                cls = 'x-anchor-left';
            }
            else {
                cls = 'x-anchor-right';
            }
            targetBox = anchorEl.getAlignToXY(alignTo, position, arrowOffsets);
            anchorEl.setXY(targetBox);
            anchorEl.addCls(cls);
        }
        
        return position;
    },

    
    setCentered : function(centered, update) {
        this.centered = centered;

        if (this.rendered && update) {
            var x, y;
            if (!this.ownerCt) {
                x = (Ext.Element.getViewportWidth() / 2) - (this.getWidth() / 2);
                y = (Ext.Element.getViewportHeight() / 2) - (this.getHeight() / 2);
            }
            else {
                x = (this.ownerCt.getTargetEl().getWidth() / 2) - (this.getWidth() / 2);
                y = (this.ownerCt.getTargetEl().getHeight() / 2) - (this.getHeight() / 2);
            }
            this.setPosition(x, y);
        }

        return this;
    },

    
    hide : function(animation) {
        if (!this.hidden && this.fireEvent('beforehide', this) !== false) {
            this.hidden = true;
            if (this.rendered) {
                this.onHide(animation, true);
            }
        }
        return this;
    },

    
    onShow : function(animation) {
        this.el.show();
        
        Ext.Component.superclass.onShow.call(this, animation);
        
        if (animation === undefined || animation === true) {
            animation = this.showAnimation;
        }

        if (this.floating) {
            this.el.dom.parentNode || this.el.appendTo(document.body);

            if (animation) {
                this.el.setStyle('opacity', 0.01);
            }

            if (this.centered) {
                this.setCentered(true, true);
            }
            else {
                this.setPosition(this.x, this.y);
            }

            if (this.modal) {
                this.el.parent().mask(null, 'x-mask-gray');
            }

            if (this.hideOnMaskTap) {
                Ext.getDoc().on('touchstart', this.onFloatingTouchStart, this, {capture: true, subsequent: true});
            }
        }
        
        if (animation) {
            

            Ext.Anim.run(this, animation, {
                out: false,
                autoClear: true
            });

            this.showAnimation = animation;
        }
    },

    
    onFloatingTouchStart: function(e) {
        if (!this.el.contains(e.target)) {
            this.hide();
            if (this.stopMaskTapEvent || Ext.fly(e.target).hasCls('x-mask')) {
                e.stopEvent();
            }
        }
    },

    
    onHide : function(animation, fireHideEvent) {
        if (animation === undefined || animation === true) {
            animation = this.showAnimation;
        }

        if (this.hideOnMaskTap && this.floating) {
            Ext.getDoc().un('touchstart', this.onFloatingTouchStart, this, {capture: true, subsequent: true});
        }

        if (animation) {
            Ext.Anim.run(this, animation, {
                out: true,
                reverse: true,
                autoClear: true,
                scope: this,
                fireHideEvent: fireHideEvent,
                after: this.doHide
            });
        } else {
            this.doHide(null, {fireHideEvent: fireHideEvent});
        }
    },

    
    doHide : function(el, options) {
        var parent = this.el.parent();

        this.el.hide();
        
        if (parent && this.floating && this.modal) {
            parent.unmask();
        }
        if (options && options.fireHideEvent) {
            this.fireEvent('hide', this);
        }
    },

    
    setScrollable : function(config) {
        var me = this,
            direction;
            
        if (!me.rendered) {
            me.scroll = config;
            return;
        }

        Ext.destroy(me.scroller);
        me.scroller = null;
        
        
        if (me.originalGetTargetEl) {
            me.getTargetEl = me.originalGetTargetEl;
        }
        
        if (config !== false) {
            direction = Ext.isObject(config) ? config.direction: config;
            config = Ext.apply({},
            Ext.isObject(config) ? config: {}, {

                direction: direction
            });

            if (!me.scrollEl) {
                me.scrollEl = me.getTargetEl().createChild();
            }
            me.originalGetTargetEl = me.getTargetEl;
            me.getTargetEl = function() {
                return me.scrollEl;
            };
            me.scroller = (new Ext.util.ScrollView(me.scrollEl, config)).scroller;
        }
    },

    
    setFloating : function(floating, autoShow) {
        this.floating = !!floating;
        this.hidden = true;
        if (this.rendered) {
            if (floating !== false) {
                this.el.addCls(this.floatingCls);
                if (autoShow) {
                    this.show();
                }
            }
            else {
                this.el.removeCls(this.floatingCls);
                Ext.getDoc().un('touchstart', this.onFloatingTouchStart, this);
            }
        }
        else if (floating !== false) {
            if (this.layoutOnOrientationChange !== false) {
                this.layoutOnOrientationChange = true;
            }
            this.autoRender = true;
        }
    },

    
    setDraggable : function(draggable, autoShow) {
        this.isDraggable = draggable;

        if (this.rendered) {
            if (draggable === false) {
                if (this.dragObj) {
                    this.dragObj.disable();
                }
            } else {
                if (autoShow) {
                    this.show();
                }
                if (this.dragObj) {
                    this.dragObj.enable();
                } else {
                    this.dragObj = new Ext.util.Draggable(this.el, Ext.apply({}, this.draggable || {}));
                    this.relayEvents(this.dragObj, ['dragstart', 'beforedragend' ,'drag', 'dragend']);
                }
            }
        }
    },

    
    setOrientation : function(orientation, w, h) {
        if (this.fireEvent('beforeorientationchange', this, orientation, w, h) !== false) {
            if (this.orientation != orientation) {
                this.el.removeCls('x-' + this.orientation);
                this.el.addCls('x-' + orientation);
            }

            this.orientation = orientation;
            this.layoutOrientation(orientation, w, h);

            if (this.fullscreen) {
                this.setSize(w, h);
            }
            else if (this.layoutOnOrientationChange) {
                this.doComponentLayout();
            }

            if (this.floating && this.centered) {
                this.setCentered(true, true);
            }

            this.onOrientationChange(orientation, w, h);
            this.fireEvent('orientationchange', this, orientation, w, h);
        }
    },

    
    onOrientationChange : Ext.emptyFn,

    beforeDestroy : function() {
        if (this.floating && this.modal && !this.hidden) {
            this.el.parent().unmask();
        }
        Ext.destroy(this.scroller);
        Ext.Component.superclass.beforeDestroy.call(this);
    },
    
    onDestroy : function() {
        if (this.monitorOrientation && Ext.EventManager.orientationEvent) {
            Ext.EventManager.orientationEvent.removeListener(this.setOrientation, this);
        }
        
        Ext.Component.superclass.onDestroy.call(this);
    }
});


Ext.BoxComponent = Ext.Component;

Ext.reg('component', Ext.Component);
Ext.reg('box', Ext.BoxComponent);

Ext.lib.Container = Ext.extend(Ext.Component, {
    
     
    
    
    


    
    autoDestroy : true,

     
    defaultType: 'panel',

    isContainer : true,

    baseCls: 'x-container',

    
    bubbleEvents: ['add', 'remove'],

    
    initComponent : function(){
        this.addEvents(
            
            'afterlayout',
            
            'beforeadd',
            
            'beforeremove',
            
            'add',
            
            'remove',
            
            'beforecardswitch',
            
            'cardswitch'
        );

        
        this.layoutOnShow = new Ext.util.MixedCollection();
        Ext.lib.Container.superclass.initComponent.call(this);
        this.initItems();
    },

    
    initItems : function() {
        var items = this.items;
        
        
        this.items = new Ext.util.MixedCollection(false, this.getComponentId);

        if (items) {
            if (!Ext.isArray(items)) {
                items = [items];
            }

            this.add(items);
        }
    },

    
    afterRender : function() {
        this.getLayout();
        Ext.lib.Container.superclass.afterRender.apply(this, arguments);
    },

    
    setLayout : function(layout) {
        var currentLayout = this.layout;
        
        if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
            currentLayout.setOwner(null);
        }
        
        this.layout = layout;
        layout.setOwner(this);
    },

    
    getLayout : function() {
        if (!this.layout || !this.layout.isLayout) {
            this.setLayout(Ext.layout.LayoutManager.create(this.layout, 'autocontainer'));
        }
        
        return this.layout;
    },

    
    doLayout : function() {
        var layout = this.getLayout();
        
        if (this.rendered && layout) {
            layout.layout();
        }
        
        return this;
    },

    
    afterLayout : function(layout) {
        this.fireEvent('afterlayout', this, layout);
    },

    
    prepareItems : function(items, applyDefaults) {
        if (!Ext.isArray(items)) {
            items = [items];
        }
        
        
        var item, i, ln;
        
        for (i = 0, ln = items.length; i < ln; i++) {
            item = items[i];
            
            if (applyDefaults) {
                item = this.applyDefaults(item);
            }
            
            items[i] = this.lookupComponent(item);
        }
        
        return items;
    },

    
    applyDefaults : function(config) {
        var defaults = this.defaults;
        
        if (defaults) {
            if (Ext.isFunction(defaults)) {
                defaults = defaults.call(this, config);
            }
            
            if (Ext.isString(config)) {
                config = Ext.ComponentMgr.get(config);
                Ext.apply(config, defaults);
            } else if (!config.isComponent) {
                Ext.applyIf(config, defaults);
            } else {
                Ext.apply(config, defaults);
            }
        }
        
        return config;
    },

    
    lookupComponent : function(comp) {
        if (Ext.isString(comp)) {
            return Ext.ComponentMgr.get(comp);
        } else {
            return this.createComponent(comp);
        }
        return comp;
    },

    
    createComponent : function(config, defaultType) {
        if (config.isComponent) {
            return config;
        }

        
        
        
        
        
        

        return Ext.create(config, defaultType || this.defaultType);
    },

    
    getComponentId : function(comp) {
        return comp.getItemId();
    },

    
    add : function() {
        var args = Array.prototype.slice.call(arguments),
            index = -1;

        if (typeof args[0] == 'number') {
            index = args.shift();
        }

        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];
                if (!item) {
                    throw "Trying to add a null item as a child of Container with itemId/id: " + this.getItemId();
                }
                
                if (index != -1) {
                    item = this.add(index + i, item);
                } else {
                    item = this.add(item);
                }
                results.push(item);
            }

            return results;
        }

        var cmp = this.prepareItems(args[0], true)[0];
        index = (index !== -1) ? index : this.items.length;

        if (this.fireEvent('beforeadd', this, cmp, index) !== false && this.onBeforeAdd(cmp) !== false) {
            this.items.insert(index, cmp);
            cmp.onAdded(this, index);
            this.onAdd(cmp, index);
            this.fireEvent('add', this, cmp, index);
        }

        return cmp;
    },

    onAdd : Ext.emptyFn,
    onRemove : Ext.emptyFn,

    
    insert : function(index, comp){
        return this.add(index, comp);
    },

    
    onBeforeAdd : function(item) {
        if (item.ownerCt) {
            item.ownerCt.remove(item, false);
        }
        
        if (this.hideBorders === true){
            item.border = (item.border === true);
        }
    },

    
    remove : function(comp, autoDestroy) {
        var c = this.getComponent(comp);
			if (!c) {
            	console.warn("Attempted to remove a component that does not exist. Ext.Container: remove takes an argument of the component to remove. cmp.remove() is incorrect usage.");				
			}
        
        if (c && this.fireEvent('beforeremove', this, c) !== false) {
            this.doRemove(c, autoDestroy);
            this.fireEvent('remove', this, c);
        }
        
        return c;
    },

    
    doRemove : function(component, autoDestroy) {
        var layout = this.layout,
            hasLayout = layout && this.rendered;

        this.items.remove(component);
        component.onRemoved();
        
        if (hasLayout) {
            layout.onRemove(component);
        }
        
        this.onRemove(component, autoDestroy);

        if (autoDestroy === true || (autoDestroy !== false && this.autoDestroy)) {
            component.destroy();
        }

        if (hasLayout && !autoDestroy) {
            layout.afterRemove(component);
        }
    },

    
    removeAll : function(autoDestroy) {
        var item,
            removeItems = this.items.items.slice(),
            items = [],
            ln = removeItems.length,
            i;
        
        for (i = 0; i < ln; i++) {
            item = removeItems[i];
            this.remove(item, autoDestroy);
            
            if (item.ownerCt !== this) {
                items.push(item);
            }
        }
        
        return items;
    },

    
    
    
    
    getRefItems : function(deep) {
        var items = this.items.items.slice(),
            ln = items.length,
            i, item;

        if (deep) {
            for (i = 0; i < ln; i++) {
                item = items[i];
                
                if (item.getRefItems) {
                    items = items.concat(item.getRefItems(true));
                }
            }
        }

        return items;
    },

    
    getComponent : function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }
        
        return this.items.get(comp);
    },

    
    query : function(selector) {
        return Ext.ComponentQuery.query(selector, this);
    },

    
    child : function(selector) {
        return this.query('> ' + selector)[0] || null;
    },
    
    
    
    down : function(selector) {
        return this.query(selector)[0] || null;
    },

    
    show : function() {
        Ext.lib.Container.superclass.show.apply(this, arguments);
        
        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);
            }
        }
        
        layoutCollection.clear();
    },

    
    beforeDestroy : function() {
        var items = this.items,
            c;
            
        if (items) {
            while ((c = items.first())) {
                this.doRemove(c, true);
            }
        }
        
        Ext.destroy(this.layout);
        Ext.lib.Container.superclass.beforeDestroy.call(this);
    }
});


Ext.Container = Ext.extend(Ext.lib.Container, {});
Ext.reg('container', Ext.Container);


Ext.Container = Ext.extend(Ext.lib.Container, {
    
    cardSwitchAnimation: null,

    initComponent: function() {
        if (this.scroll) {
            this.fields = new Ext.util.MixedCollection();

            if (!Ext.is.Blackberry) {
                this.fields.on({
                    add: this.onFieldAdd,
                    remove: this.onFieldRemove,
                    scope: this
                });
            }

            this.on({
                add: this.onItemAdd,
                remove: this.onItemRemove,
                scope: this
            });
        }


        Ext.Container.superclass.initComponent.apply(this, arguments);
    },

    afterRender: function() {
        Ext.Container.superclass.afterRender.apply(this, arguments);

        if (this.scroller) {
            if ((Ext.is.Android) && this.containsFormFields) {
                this.scroller.setUseCssTransform(false);
            }

            this.scroller.on('scrollstart', this.onFieldScrollStart, this);
        }
    },

    onFieldScrollStart: function() {
        var focusedField = this.focusedField;

        if (focusedField && Ext.is.iOS) {
            focusedField.blur();

        }
    },

    onItemAdd: function(me, item) {
        this.fields.addAll(Ext.ComponentQuery.query('[isField]', item));
    },

    onItemRemove: function(me, item) {
        this.fields.removeAll(Ext.ComponentQuery.query('[isField]', item));
    },

    onFieldAdd: function(key, field) {
        this.handleFieldEventListener(true, field);
    },

    onFieldRemove: function(key, field) {
        this.handleFieldEventListener(false, field);
    },

    handleFieldEventListener: function(isAdding, item) {
        if (!this.fieldEventWrap)
            this.fieldEventWrap = {};

        if (['textfield', 'passwordfield', 'emailfield',
             'textareafield', 'searchfield', 'urlfield',
             'numberfield', 'spinnerfield'].indexOf(item.xtype) !== -1) {
            if (isAdding) {
                this.fieldEventWrap[item.id] = {
                    beforefocus: function(e) {this.onFieldBeforeFocus(item, e);},
                    focus: function(e) {this.onFieldFocus(item, e);},
                    blur: function(e) {this.onFieldBlur(item, e);},
                    keyup: function(e) {this.onFieldKeyUp(item, e);},
                    scope: this
                };

                this.containsFormFields = true;
            }

            item[isAdding ? 'on' : 'un'](this.fieldEventWrap[item.id]);

            if (!isAdding) {
                delete this.fieldEventWrap[item.id];
            }
        }
    },

    onFieldKeyUp: function(field, e) {
        if (Ext.is.iOS || Ext.is.Desktop) {
            this.resetLastWindowScroll();
        }
    },

    onFieldBeforeFocus: function(field, e) {
        this.focusingField = field;
    },

    getLastWindowScroll: function() {
        if (!this.lastWindowScroll) {
            this.resetLastWindowScroll();
        }

        return {x: this.lastWindowScroll.x, y: this.lastWindowScroll.y};
    },

    resetLastWindowScroll: function() {
        this.lastWindowScroll = {
            x: window.pageXOffset,
            y: window.pageYOffset
        };
    },

    adjustScroller: function(offset) {
        var scroller = this.getClosestScroller(),
            windowScroll = this.getLastWindowScroll();

        scroller.setOffset(offset);

        
        if (Ext.is.iOS) {
            window.scrollTo(windowScroll.x, windowScroll.y);
        }

        this.resetLastWindowScroll();
    },

    onFieldFocus: function(field, e) {
        if (!Ext.is.iOS && !Ext.is.Desktop) {
            var dom = field.fieldEl.dom;

            if (dom.scrollIntoViewIfNeeded) {
                dom .scrollIntoViewIfNeeded(true);
            }
        }
        else {
             var scroller = this.getClosestScroller(),
                containerRegion = Ext.util.Region.from(scroller.containerBox),
                fieldRegion = field.fieldEl.getPageBox(true);

            
            if (this.focusingField == field || !Ext.is.iOS) {
                if (Ext.is.iOS && window.pageYOffset == 0) {
                    window.scrollTo(0, 0);
                }

                var adjustment = new Ext.util.Offset();

                if (fieldRegion.left < containerRegion.left) {
                    adjustment.x = containerRegion.left - fieldRegion.left;
                }

                if (fieldRegion.top < containerRegion.top) {
                    adjustment.y = containerRegion.top - fieldRegion.top;
                }

                if (!adjustment.isZero()) {
                    var windowScroll = this.getLastWindowScroll();

                    scroller.scrollBy(adjustment);

                    if (Ext.is.iOS) {
                        window.scrollTo(windowScroll.x, windowScroll.y);
                    }

                    this.resetLastWindowScroll();
                }
            }
            
            else {
                if (this.lastFocusedField) {
                    var deltaY = fieldRegion.top - this.lastFocusedField.fieldEl.getY(),
                        offsetY = scroller.offset.y - deltaY,
                        selfHandling = false;

                    if (!containerRegion.contains(fieldRegion) &&
                        (offsetY != 0 || (offsetY == 0 && scroller.offset.y != 0))) {
                        selfHandling = true;
                    }

                    if (offsetY > 0) {
                        offsetY = 0;
                    }

                    if (selfHandling) {
                        this.adjustScroller(new Ext.util.Offset(
                            scroller.offset.x, offsetY
                        ));
                    }
                }
            }

            this.resetLastWindowScroll();
        }

        this.lastFocusedField = field;
        this.focusedField = field;
        this.focusingField = null;
    },

    getClosestScroller: function() {
        if (!this.closestScroller) {
            this.closestScroller = this.scroller || this.el.getScrollParent();
        }

        return this.closestScroller;
    },

    onFieldBlur: function(field, e) {
        if (this.focusingField == field) {
            this.focusingField = null;
        }

        if (this.focusedField == field) {
            this.focusedField = null;
        }
    },

    
    afterLayout : function(layout) {
        if (this.floating && this.centered) {
            this.setCentered(true, true);
        }

        if (this.scroller) {
            this.scroller.updateBoundary();
        }
        Ext.Container.superclass.afterLayout.call(this, layout);
    },

    
    getActiveItem : function() {
        if (this.layout && this.layout.type == 'card') {
            return this.layout.activeItem;
        }
        else {
            return null;
        }
    },

    
    setActiveItem : function(card, animation) {
        this.layout.setActiveItem(card, animation);
        return this;
    },


    
    onBeforeCardSwitch : function(newCard, oldCard, newIndex, animated) {
        return this.fireEvent('beforecardswitch', this, newCard, oldCard, newIndex, animated);
    },

    
    onCardSwitch : function(newCard, oldCard, newIndex, animated) {
        return this.fireEvent('cardswitch', this, newCard, oldCard, newIndex, animated);
    },

    
    disable: function() {
        Ext.Container.superclass.disable.call(this);
        this.el.mask(null, 'x-mask-gray');
    },

    
    enable: function() {
        Ext.Container.superclass.enable.call(this);
        this.el.unmask();
    }
});

Ext.reg('container', Ext.Container);


Ext.lib.Panel = Ext.extend(Ext.Container, {
    
    baseCls : 'x-panel',

    

    

    

    isPanel: true,

    componentLayout: 'dock',

    renderTpl: ['<div class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>></div>'],

    

    initComponent : function() {
        this.addEvents(
            
            'bodyresize'
            
            
            
            
        );

        Ext.applyIf(this.renderSelectors, {
            body: '.' + this.baseCls + '-body'
        });

        Ext.lib.Panel.superclass.initComponent.call(this);
    },

    
    initItems : function() {
        Ext.lib.Panel.superclass.initItems.call(this);

        var items = this.dockedItems;
        this.dockedItems = new Ext.util.MixedCollection(false, this.getComponentId);
        if (items) {
            this.addDocked(items);
        }
    },

    
    getDockedComponent: function(comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }
        return this.dockedItems.get(comp);
    },

    
    getComponent: function(comp) {
        var component = Ext.lib.Panel.superclass.getComponent.call(this, comp);
        if (component == undefined) {
            component = this.getDockedComponent(comp);
        }
        return component;
    },

    
    initBodyStyles: function() {
        var bodyStyle = Ext.isString(this.bodyStyle) ? this.bodyStyle.split(';') : [],
            Element = Ext.Element;

        if (this.bodyPadding != undefined) {
            bodyStyle.push('padding: ' + Element.unitizeBox((this.bodyPadding === true) ? 5 : this.bodyPadding));
        }
        if (this.bodyMargin != undefined) {
            bodyStyle.push('margin: ' + Element.unitizeBox((this.bodyMargin === true) ? 5 : this.bodyMargin));
        }
        if (this.bodyBorder != undefined) {
            bodyStyle.push('border-width: ' + Element.unitizeBox((this.bodyBorder === true) ? 1 : this.bodyBorder));
        }
        delete this.bodyStyle;
        return bodyStyle.length ? bodyStyle.join(';') : undefined;
    },

    
    initRenderData: function() {
        return Ext.applyIf(Ext.lib.Panel.superclass.initRenderData.call(this), {
            bodyStyle: this.initBodyStyles(),
            bodyCls: this.bodyCls
        });
    },

    
    addDocked : function(items, pos) {
        items = this.prepareItems(items);

        var item, i, ln;
        for (i = 0, ln = items.length; i < ln; i++) {
            item = items[i];
            item.dock = item.dock || 'top';
            if (pos !== undefined) {
                this.dockedItems.insert(pos+i, item);
            }
            else {
                this.dockedItems.add(item);
            }
            item.onAdded(this, i);
            this.onDockedAdd(item);
        }
        if (this.rendered) {
            this.doComponentLayout();
        }
    },

    
    onDockedAdd : Ext.emptyFn,
    onDockedRemove : Ext.emptyFn,

    
    insertDocked : function(pos, items) {
        this.addDocked(items, pos);
    },

    
    removeDocked : function(item, autoDestroy) {
        if (!this.dockedItems.contains(item)) {
            return item;
        }

        var layout = this.componentLayout,
            hasLayout = layout && this.rendered;

        if (hasLayout) {
            layout.onRemove(item);
        }

        this.dockedItems.remove(item);
        item.onRemoved();
        this.onDockedRemove(item);

        if (autoDestroy === true || (autoDestroy !== false && this.autoDestroy)) {
            item.destroy();
        }

        if (hasLayout && !autoDestroy) {
            layout.afterRemove(item);
        }
        this.doComponentLayout();

        return item;
    },

    
    getDockedItems : function() {
        if (this.dockedItems && this.dockedItems.items.length) {
            return this.dockedItems.items.slice();
        }
        return [];
    },

    
    getTargetEl : function() {
        return this.body;
    },


    getRefItems: function(deep) {
        var refItems    = Ext.lib.Panel.superclass.getRefItems.call(this, deep),
            
            dockedItems = this.getDockedItems(),
            ln          = dockedItems.length,
            i           = 0,
            item;

        refItems = refItems.concat(dockedItems);

        if (deep) {
            for (; i < ln; i++) {
                item = dockedItems[i];
                if (item.getRefItems) {
                    refItems = refItems.concat(item.getRefItems(true));
                }
            }
        }

        return refItems;
    },
    
    beforeDestroy: function(){
        var docked = this.dockedItems,
            c;
            
        if (docked) {
            while ((c = docked.first())) {
                this.removeDocked(c, true);
            }
        }
        Ext.lib.Panel.superclass.beforeDestroy.call(this);
    }
});


Ext.Panel = Ext.extend(Ext.lib.Panel, {});
Ext.reg('panel', Ext.Panel);


Ext.Panel = Ext.extend(Ext.lib.Panel, {
    
    scroll: false
});

Ext.reg('panel', Ext.Panel);



Ext.Button = Ext.extend(Ext.Component, {
    

    initComponent: function(){
        this.addEvents(
            
            'tap',

            
            'beforetap'
        );
        Ext.Button.superclass.initComponent.call(this);

        this.createAutoHandler();
    },

    

    

    

    
    iconAlign: 'left',

    

    

    

    

    
    baseCls: 'x-button',

    
    pressedCls: 'x-button-pressed',

    
    badgeText: '',

    
    badgeCls: 'x-badge',

    hasBadgeCls: 'x-hasbadge',

    labelCls: 'x-button-label',

    
    ui: 'normal',

    isButton: true,

    

    
    pressedDelay: 0,

    
    iconMaskCls: 'x-icon-mask',

    
    iconMask: false,

    
    afterRender : function(ct, position) {
        var me = this;

        Ext.Button.superclass.afterRender.call(me, ct, position);

        var text = me.text,
            icon = me.icon,
            iconCls = me.iconCls,
            badgeText = me.badgeText;

        me.text = me.icon = me.iconCls = me.badgeText = null;

        me.setText(text);
        me.setIcon(icon);
        me.setIconClass(iconCls);

        if (me.iconMask && me.iconEl) {
            me.iconEl.addCls(me.iconMaskCls);
        }
        me.setBadge(badgeText);
    },

    
    initEvents : function() {
        var me = this;

        Ext.Button.superclass.initEvents.call(me);

        me.mon(me.el, {
            scope: me,

            tap      : me.onPress,
            tapstart : me.onTapStart,
            tapcancel: me.onTapCancel
        });
    },

    
    onTapStart : function() {
        var me = this;
        if (!me.disabled) {
            if (me.pressedDelay) {
                me.pressedTimeout = setTimeout(function() {
                    me.el.addCls(me.pressedCls);
                }, Ext.isNumber(me.pressedDelay) ? me.pressedDelay : 100);
            }
            else {
                me.el.addCls(me.pressedCls);
            }
        }
    },

    
    onTapCancel : function() {
        var me = this;
        if (me.pressedTimeout) {
            clearTimeout(me.pressedTimeout);
            delete me.pressedTimeout;
        }
        me.el.removeCls(me.pressedCls);
    },

    
    setHandler : function(handler, scope) {
        this.handler = handler;
        this.scope = scope;
        return this;
    },

    
    setText: function(text) {
        var me = this;

        if (me.rendered) {
            if (!me.textEl && text) {
                me.textEl = me.el.createChild({
                    tag: 'span',
                    html: text,
                    cls: this.labelCls
                });
            }
            else if (me.textEl && text != me.text) {
                if (text) {
                    me.textEl.setHTML(text);
                }
                else {
                    me.textEl.remove();
                    me.textEl = null;
                }
            }
        }
        me.text = text;
        return me;
    },

    
    setIcon: function(icon) {
        var me = this;

        if (me.rendered) {
            if (!me.iconEl && icon) {
                me.iconEl = me.el.createChild({
                    tag: 'img',
                    src: Ext.BLANK_IMAGE_URL,
                    style: 'background-image: ' + (icon ? 'url(' + icon + ')' : '')
                });

                me.setIconAlign(me.iconAlign);
            }
            else if (me.iconEl && icon != me.icon) {
                if (icon) {
                    me.iconEl.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
                    me.setIconAlign(me.iconAlign);
                }
                else {
                    me.setIconAlign(false);
                    me.iconEl.remove();
                    me.iconEl = null;
                }
            }
        }
        me.icon = icon;
        return me;
    },

    
    setIconClass: function(cls) {
        var me = this;

        if (me.rendered) {
            if (!me.iconEl && cls) {
                me.iconEl = me.el.createChild({
                    tag: 'img',
                    src: Ext.BLANK_IMAGE_URL,
                    cls: cls
                });

                me.setIconAlign(me.iconAlign);
            }
            else if (me.iconEl && cls != me.iconCls) {
                if (cls) {
                    if (me.iconCls) {
                        me.iconEl.removeCls(me.iconCls);
                    }
                    me.iconEl.addCls(cls);
                    me.setIconAlign(me.iconAlign);
                }
                else {
                    me.setIconAlign(false);
                    me.iconEl.remove();
                    me.iconEl = null;
                }
            }
        }
        me.iconCls = cls;
        return me;
    },

    
    setIconAlign: function(alignment) {
        var me         = this,
            alignments = ['top', 'right', 'bottom', 'left'],
            alignment  = ((alignments.indexOf(alignment) == -1 || !alignment) && alignment !== false) ? me.iconAlign : alignment,
            i;

        if (me.rendered && me.iconEl) {
            me.el.removeCls('x-iconalign-' + me.iconAlign);

            if (alignment) me.el.addCls('x-iconalign-' + alignment);
        }
        me.iconAlign = (alignment === false) ? me.iconAlign : alignment;
        return me;
    },

    
    setBadge : function(text) {
        var me = this;

        if (me.rendered) {
            if (!me.badgeEl && text) {
                me.badgeEl = me.el.createChild({
                    tag: 'span',
                    cls: me.badgeCls,
                    html: text
                });
                me.el.addCls(me.hasBadgeCls);
            }
            else if (me.badgeEl && text != me.badgeText) {
                if (text) {
                    me.badgeEl.setHTML(text);
                    me.el.addCls(me.hasBadgeCls);
                }
                else {
                    me.badgeEl.remove();
                    me.badgeEl = null;
                    me.el.removeCls(me.hasBadgeCls);
                }
            }
        }
        me.badgeText = text;
        return me;
    },

    
    getText : function() {
        return this.text;
    },

    
    getBadgeText : function() {
        return this.badgeText;
    },

    
    onDisable : function() {
        this.onDisableChange(true);
    },

    
    onEnable : function() {
        this.onDisableChange(false);
    },

    
    onDisableChange : function(disabled) {
        var me = this;
        if (me.el) {
            me.el[disabled ? 'addCls' : 'removeCls'](me.disabledCls);
            me.el.dom.disabled = disabled;
        }
        me.disabled = disabled;
    },

    
    onPress : function(e) {
        var me = this;
        if (!me.disabled && this.fireEvent('beforetap') !== false) {
            setTimeout(function() {
                if (!me.preventCancel) {
                    me.onTapCancel();
                }
                me.callHandler(e);
                me.fireEvent('tap', me, e);
            }, 10);
        }
    },

    
    callHandler: function(e) {
        var me = this;
        if (me.handler) {
            me.handler.call(me.scope || me, me, e);
        }
    },

    
    createAutoHandler: function() {
        var me = this,
            autoEvent = me.autoEvent;

        if (autoEvent) {
            if (typeof autoEvent == 'string') {
                autoEvent = {
                    name: autoEvent,
                    scope: me.scope || me
                };
            }

            me.addEvents(autoEvent.name);

            me.setHandler(function() {
                autoEvent.scope.fireEvent(autoEvent.name, autoEvent.scope, me);
            }, autoEvent.scope);
        }
    }
});

Ext.reg('button', Ext.Button);


Ext.SegmentedButton = Ext.extend(Ext.Container, {
    defaultType: 'button',
    componentCls: 'x-segmentedbutton',
    pressedCls: 'x-button-pressed',

    
    allowMultiple: false,

    
    
    
    initComponent : function() {
        this.layout = Ext.apply({}, this.layout || {}, {
            type: 'hbox',
            align: 'stretch'
        });
        
        Ext.SegmentedButton.superclass.initComponent.call(this);
        
        if (this.allowDepress === undefined) {
            this.allowDepress = this.allowMultiple;
        }
        
        this.addEvents(
            
            'toggle'
        );
    },

    
    initEvents : function() {
        Ext.SegmentedButton.superclass.initEvents.call(this);

        this.mon(this.el, {
            tap: this.onTap,
            capture: true,
            scope: this
        });
    },

    
    afterLayout : function(layout) {
        var me = this;
        
        Ext.SegmentedButton.superclass.afterLayout.call(me, layout);

        if (!me.initialized) {
            me.items.each(function(item, index) {
                me.setPressed(item, !!item.pressed, true); 
            });
            if (me.allowMultiple) {
                me.pressedButtons = me.getPressedButtons();
            }
            me.initialized = true;
        }
    },

    
    onTap : function(e, t) {
        if (!this.disabled && (t = e.getTarget('.x-button'))) {
            this.setPressed(t.id, this.allowDepress ? undefined : true);
        }
    },
    
    
    getPressed : function() {
        return this.allowMultiple ? this.getPressedButtons() : this.pressedButton;
    },

    
    setPressed : function(btn, pressed, suppressEvents) {
        var me = this;
        
        btn = me.getComponent(btn);
        if (!btn || !btn.isButton || btn.disabled) {
            if (!me.allowMultiple && me.pressedButton) {
                me.setPressed(me.pressedButton, false);
            }
            return;
        }
        
        if (!Ext.isBoolean(pressed)) {
            pressed = !btn.pressed;
        }
        
        if (pressed) {
            if (!me.allowMultiple) {
                if (me.pressedButton && me.pressedButton !== btn) {
                    me.pressedButton.el.removeCls(me.pressedCls);
                    me.pressedButton.pressed = false;
                    if (suppressEvents !== true) {
                        me.fireEvent('toggle', me, me.pressedButton, false);
                    }               
                }
                me.pressedButton = btn;
            }

            btn.el.addCls(me.pressedCls);
            btn.pressed = true;
            btn.preventCancel = true;
            if (me.initialized && suppressEvents !== true) {
                me.fireEvent('toggle', me, btn, true);
            }
        }
        else if (!pressed) {
            if (!me.allowMultiple && btn === me.pressedButton) {
                me.pressedButton = null;
            }
            
            if (btn.pressed) {
                btn.el.removeCls(me.pressedCls);
                btn.pressed = false;
                if (suppressEvents !== true) {
                    me.fireEvent('toggle', me, btn, false);
                }
            }
        }
        
        if (me.allowMultiple && me.initialized) {
            me.pressedButtons = me.getPressedButtons();
        }
    },
    
    
    getPressedButtons : function(toggleEvents) {
        var pressed = this.items.filterBy(function(item) {
            return item.isButton && !item.disabled && item.pressed;
        });
        return pressed.items;
    },

    
    disable : function() {
        this.items.each(function(item) {
            item.disable();
        });

        Ext.SegmentedButton.superclass.disable.apply(this, arguments);
    },

    
    enable : function() {
        this.items.each(function(item) {
            item.enable();
        }, this);

        Ext.SegmentedButton.superclass.enable.apply(this, arguments);
    }
});

Ext.reg('segmentedbutton', Ext.SegmentedButton);

Ext.AbstractStoreSelectionModel = Ext.extend(Ext.util.Observable, {
    

    
    
    
    allowDeselect: false,

    
    selected: null,

    constructor: function(cfg) {
        cfg = cfg || {};
        Ext.apply(this, cfg);

        this.modes = {
            SINGLE: true,
            SIMPLE: true,
            MULTI: true
        };

        
        this.setSelectionMode(cfg.mode);

        
        this.selected = new Ext.util.MixedCollection();

        Ext.AbstractStoreSelectionModel.superclass.constructor.call(this, cfg);
    },

    
    bind : function(store, initial){
        if(!initial && this.store){
            if(store !== this.store && this.store.autoDestroy){
                this.store.destroy();
            }else{
                this.store.un("add", this.onStoreAdd, this);
                this.store.un("clear", this.onStoreClear, this);
                this.store.un("remove", this.onStoreRemove, this);
                this.store.un("update", this.onStoreUpdate, this);
            }
        }
        if(store){
            store = Ext.StoreMgr.lookup(store);
            store.on({
                add: this.onStoreAdd,
                clear: this.onStoreClear,
                remove: this.onStoreRemove,
                update: this.onStoreUpdate,
                scope: this
            });
        }
        this.store = store;
        if(store && !initial) {
            this.refresh();
        }
    },

    selectAll: function(silent) {
        var selections = this.store.getRange();
        for (var i = 0, ln = selections.length; i < ln; i++) {
            this.doSelect(selections[i], true, silent);
        }
    },

    deselectAll: function() {
        var selections = this.getSelection();
        for (var i = 0, ln = selections.length; i < ln; i++) {
            this.doDeselect(selections[i]);
        }
    },

    
    
    
    selectWithEvent: function(record, e) {
        switch (this.selectionMode) {
            case 'MULTI':
                if (e.ctrlKey && this.isSelected(record)) {
                    this.doDeselect(record, false);
                } else if (e.shiftKey && this.lastFocused) {
                    this.selectRange(this.lastFocused, record, e.ctrlKey);
                } else if (e.ctrlKey) {
                    this.doSelect(record, true, false);
                } else if (this.isSelected(record) && !e.shiftKey && !e.ctrlKey && this.selected.getCount() > 1) {
                    this.doSelect(record, false, false);
                } else {
                    this.doSelect(record, false);
                }
                break;
            case 'SIMPLE':
                if (this.isSelected(record)) {
                    this.doDeselect(record);
                } else {
                    this.doSelect(record, true);
                }
                break;
            case 'SINGLE':
                
                if (this.allowDeselect && this.isSelected(record)) {
                    this.doDeselect(record);
                
                } else {
                    this.doSelect(record, false);
                }
                break;
        }
    },

    
    selectRange : function(startRecord, endRecord, keepExisting, dir){
        var i,
            startRow = this.store.indexOf(startRecord),
            endRow = this.store.indexOf(endRecord),
            tmp,
            selectedCount = 0,
            dontDeselect;

        if (this.isLocked()){
            return;
        }

        
        if (startRow > endRow){
            tmp = endRow;
            endRow = startRow;
            startRow = tmp;
        }

        for (i = startRow; i <= endRow; i++) {
            if (this.isSelected(this.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) {
                    this.doDeselect(i, true);
                }
            } else {
                this.doSelect(i, true);
            }

        }
    },
    
    
    select: function(records, keepExisting, suppressEvent) {
        this.doSelect(records, keepExisting, suppressEvent);
    },

    
    deselect: function(records, suppressEvent) {
        this.doDeselect(records, suppressEvent);
    },
    
    doSelect: function(records, keepExisting, suppressEvent) {
        if (this.locked) {
            return;
        }
        if (typeof records === "number") {
            records = [this.store.getAt(records)];
        }
        if (this.selectionMode == "SINGLE" && records) {
            var record = records.length ? records[0] : records;
            this.doSingleSelect(record, suppressEvent);
        } else {
            this.doMultiSelect(records, keepExisting, suppressEvent);
        }
    },

    doMultiSelect: function(records, keepExisting, suppressEvent) {
        if (this.locked) {
            return;
        }
        var selected = this.selected,
            change = false,
            record;

        records = !Ext.isArray(records) ? [records] : records;
        if (!keepExisting && selected.getCount() > 0) {
            change = true;
            this.doDeselect(this.getSelection(), true);
        }

        for (var i = 0, ln = records.length; i < ln; i++) {
            record = records[i];
            if (keepExisting && this.isSelected(record)) {
                continue;
            }
            change = true;
            this.lastSelected = record;
            selected.add(record);
            if (!suppressEvent) {
                this.setLastFocused(record);
            }

            this.onSelectChange(record, true, suppressEvent);
        }
        
        this.maybeFireSelectionChange(change && !suppressEvent);
    },

    
    doDeselect: function(records, suppressEvent) {
        if (this.locked) {
            return;
        }

        if (typeof records === "number") {
            records = [this.store.getAt(records)];
        }

        var change = false,
            selected = this.selected,
            record;

        records = !Ext.isArray(records) ? [records] : records;
        for (var i = 0, ln = records.length; i < ln; i++) {
            record = records[i];
            if (selected.remove(record)) {
                if (this.lastSelected == record) {
                    this.lastSelected = selected.last();
                }
                this.onSelectChange(record, false, suppressEvent);
                change = true;
            }
        }
        
        this.maybeFireSelectionChange(change && !suppressEvent);
    },

    doSingleSelect: function(record, suppressEvent) {
        if (this.locked) {
            return;
        }
        
        
        if (this.isSelected(record)) {
            return;
        }
        var selected = this.selected;
        if (selected.getCount() > 0) {
            this.doDeselect(this.lastSelected, suppressEvent);
        }
        selected.add(record);
        this.lastSelected = record;
        this.onSelectChange(record, true, suppressEvent);
        this.setLastFocused(record);
        this.maybeFireSelectionChange(!suppressEvent);
    },

    
    setLastFocused: function(record) {
        var recordBeforeLast = this.lastFocused;
        this.lastFocused = record;
        this.onLastFocusChanged(recordBeforeLast, record);
    },


    
    
    maybeFireSelectionChange: function(fireEvent) {
        if (fireEvent) {
            this.fireEvent('selectionchange', this, this.getSelection());
        }
    },

    
    getLastSelected: function() {
        return this.lastSelected;
    },
    
    getLastFocused: function() {
        return this.lastFocused;
    },

    
    getSelection: function() {
        return this.selected.getRange();
    },

    
    getSelectionMode: function() {
        return this.selectionMode;
    },

    
    setSelectionMode: function(selMode) {
        selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
        
        
        this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
    },

    
    isLocked: function() {
        return this.locked;
    },

    
    setLocked: function(locked) {
        this.locked = !!locked;
    },

    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.store.getAt(record) : record;
        return this.selected.indexOf(record) !== -1;
    },
    
    
    hasSelection: function() {
        return this.selected.getCount() > 0;
    },

    refresh: function() {
        var toBeSelected = [],
            oldSelections = this.getSelection(),
            ln = oldSelections.length,
            selection,
            change,
            i = 0;

        
        
        
        for (; i < ln; i++) {
            selection = oldSelections[i];
            if (this.store.indexOf(selection) != -1) {
                toBeSelected.push(selection);
            }
        }

        
        
        if (this.selected.getCount() != toBeSelected.length) {
            change = true;
        }

        this.clearSelections();

        if (toBeSelected.length) {
            
            this.doSelect(toBeSelected, false, true);
        }
        
        this.maybeFireSelectionChange(change);
    },

    clearSelections: function() {
        
        this.selected.clear();
        this.lastSelected = null;
        this.setLastFocused(null);
    },

    
    onStoreAdd: function() {

    },

    
    
    onStoreClear: function() {
        var selected = this.selected;
        if (selected.getCount > 0) {
            selected.clear();
            this.lastSelected = null;
            this.setLastFocused(null);
            this.maybeFireSelectionChange(true);
        }
    },

    
    
    
    onStoreRemove: function(store, record) {
        if (this.locked) {
            return;
        }
        var selected = this.selected;
        if (selected.remove(record)) {
            if (this.lastSelected == record) {
                this.lastSelected = null;
            }
            if (this.getLastFocused() == record) {
                this.setLastFocused(null);
            }
            this.maybeFireSelectionChange(true);
        }
    },

    getCount: function() {
        return this.selected.getCount();
    },

    
    destroy: function() {

    },

    
    onStoreUpdate: function() {

    },

    
    onSelectChange: function(record, isSelected, suppressEvent) {

    },

    
    onLastFocusChanged: function(oldFocused, newFocused) {

    },

    
    onEditorKey: function(field, e) {

    },

    
    bindComponent: function(cmp) {

    }
});
Ext.DataViewSelectionModel = Ext.extend(Ext.AbstractStoreSelectionModel, {
    deselectOnContainerClick: true,
    bindComponent: function(view) {
        this.view = view;
        this.bind(view.getStore());
        var eventListeners = {
            refresh: this.refresh,
            scope: this,
            el: {
                scope: this
            }
        };
        eventListeners.el[view.triggerEvent] = this.onItemClick;
        eventListeners.el[view.triggerCtEvent] = this.onContainerClick;
        
        view.on(eventListeners);
    },


    onItemClick: function(e) {
        var view   = this.view,
            node   = view.findTargetByEvent(e);
        
        if (node) {
            this.selectWithEvent(view.getRecord(node), e);
        } else {
            return false;
        }
    },

    onContainerClick: function() {
        if (this.deselectOnContainerClick) {
            this.deselectAll();
        }
    },

    
    onSelectChange: function(record, isSelected, suppressEvent) {
        var view = this.view;
        
        if (isSelected) {
            view.onItemSelect(record);
            if (!suppressEvent) {
                this.fireEvent('select', this, record);
            }
        } else {
            view.onItemDeselect(record);
            if (!suppressEvent) {
                this.fireEvent('deselect', this, record);
            }
        }
    }
});



Ext.DataView = Ext.extend(Ext.Component, {
    
    

    
    

    

    
    loadingText: 'Loading...',

    
    selectedItemCls: "x-item-selected",

    
    emptyText: "",

    
    deferEmptyText: true,

    
    trackOver: false,

    
    blockRefresh: false,

    


    
    last: false,
    
    triggerEvent: 'click',
    triggerCtEvent: 'containerclick',
    
    addCmpEvents: function() {
        
    },

    
    initComponent : function(){
        var isDef = Ext.isDefined;
        if (!isDef(this.tpl) || !isDef(this.store) || !isDef(this.itemSelector)) {
            throw "DataView requires tpl, store and itemSelector configurations to be defined.";
        }

        Ext.DataView.superclass.initComponent.call(this);
        if(Ext.isString(this.tpl) || Ext.isArray(this.tpl)){
            this.tpl = new Ext.XTemplate(this.tpl);
        }

        
        
        if (Ext.isDefined(this.overCls) || Ext.isDefined(this.overClass)) {
            this.overItemCls = this.overCls || this.overClass;
            delete this.overCls;
            delete this.overClass;
            throw "Using the deprecated overCls or overClass configuration. Use overItemCls.";
        }

        if (Ext.isDefined(this.selectedCls) || Ext.isDefined(this.selectedClass)) {
            this.selectedItemCls = this.selectedCls || this.selectedClass;
            delete this.selectedCls;
            delete this.selectedClass;
            throw "Using the deprecated selectedCls or selectedClass configuration. Use selectedItemCls.";
        }
        
        this.addEvents(
            
            'beforerefresh',
            
            'refresh'
        );
        
        this.addCmpEvents();

        this.store = Ext.StoreMgr.lookup(this.store)
        this.all = new Ext.CompositeElementLite();
        this.getSelectionModel().bindComponent(this);
    },
    
    onRender : function() {
        Ext.DataView.superclass.onRender.apply(this, arguments);
        if (this.loadingText) {
            this.loadMask = new Ext.LoadMask(this.el, {
                msg: this.loadingText
            });
        }
    },

    getSelectionModel: function(){
        if (!this.selModel) {
            this.selModel = {};
        }

        var mode;
        switch(true) {
            case this.simpleSelect:
                mode = 'SIMPLE';
            break;
            
            case this.multiSelect:
                mode = 'MULTI';
            break;
            
            case this.singleSelect:
            default:
                mode = 'SINGLE';
            break;
        }
        
        Ext.applyIf(this.selModel, {
            allowDeselect: this.allowDeselect,
            mode: mode
        });        
        
        if (!this.selModel.events) {
            this.selModel = new Ext.DataViewSelectionModel(this.selModel);
        }
        
        if (!this.selModel.hasRelaySetup) {
            this.relayEvents(this.selModel, ['selectionchange', 'select', 'deselect']);
            this.selModel.hasRelaySetup = true;
        }

        
        
        if (this.disableSelection) {
            this.selModel.locked = true;
        }
        
        return this.selModel;
    },

    
    refresh: function() {
        if (!this.rendered) {
            return;
        }
        
        this.fireEvent('beforerefresh', this);
        var el = this.getTargetEl(),
            records = this.store.getRange();

        el.update('');
        if (records.length < 1) {
            if (!this.deferEmptyText || this.hasSkippedEmptyText) {
                el.update(this.emptyText);
            }
            this.all.clear();
        } else {
            this.tpl.overwrite(el, this.collectData(records, 0));
            this.all.fill(Ext.query(this.itemSelector, el.dom));
            this.updateIndexes(0);
        }
        this.hasSkippedEmptyText = true;
        this.fireEvent('refresh', this);
    },

    
    prepareData: function(data, index, record) {
        if (record) {    
            Ext.apply(data, this.prepareAssociatedData(record));            
        }
        return data;
    },
    
    
    prepareAssociatedData: function(record, ids) {
        
        ids = ids || [];
        
        var associations     = record.associations.items,
            associationCount = associations.length,
            associationData  = {},
            associatedStore, associatedName, associatedRecords, associatedRecord,
            associatedRecordCount, association, internalId, i, j;
        
        for (i = 0; i < associationCount; i++) {
            association = associations[i];
            
            
            associatedStore = record[association.storeName];
            
            
            associationData[association.name] = [];
            
            
            if (associatedStore && associatedStore.data.length > 0) {
                associatedRecords = associatedStore.data.items;
                associatedRecordCount = associatedRecords.length;
            
                
                for (j = 0; j < associatedRecordCount; j++) {
                    associatedRecord = associatedRecords[j];
                    internalId = associatedRecord.internalId;
                    
                    
                    
                    if (ids.indexOf(internalId) == -1) {
                        ids.push(internalId);
                        
                        associationData[association.name][j] = associatedRecord.data;
                        Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids));
                    }
                }
            }
        }
        
        return associationData;
    },

    
    collectData : function(records, startIndex){
        var r = [],
            i = 0,
            len = records.length;

        for(; i < len; i++){
            r[r.length] = this.prepareData(records[i].data, startIndex + i, records[i]);
        }

        return r;
    },

    
    bufferRender : function(records, index){
        var div = document.createElement('div');
        this.tpl.overwrite(div, this.collectData(records, index));
        return Ext.query(this.itemSelector, div);
    },

    
    onUpdate : function(ds, record){
        var index = this.store.indexOf(record),
            original,
            node;

        if (index > -1){
            original = this.all.elements[index];
            node = this.bufferRender([record], index)[0];

            this.all.replaceElement(index, node, true);
            this.updateIndexes(index, index);

            
            
            this.selModel.refresh();
        }
    },

    
    onAdd : function(ds, records, index){
        if (this.all.getCount() === 0) {
            this.refresh();
            return;
        }

        var nodes = this.bufferRender(records, index), 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);
        }
        this.updateIndexes(index);
    },

    
    onRemove : function(ds, record, index){
        this.all.removeElement(index, true);
        this.updateIndexes(index);
        if (this.store.getCount() === 0){
            this.refresh();
        }
    },

    
    refreshNode : function(index){
        this.onUpdate(this.store, this.store.getAt(index));
    },

    
    updateIndexes : function(startIndex, endIndex){
        var ns = this.all.elements;
        startIndex = startIndex || 0;
        endIndex = endIndex || ((endIndex === 0) ? 0 : (ns.length - 1));
        for(var i = startIndex; i <= endIndex; i++){
            ns[i].viewIndex = i;
        }
    },

    
    getStore : function(){
        return this.store;
    },

    
    bindStore : function(store, initial) {
        if (!initial && this.store) {
            if (store !== this.store && this.store.autoDestroy) {
                this.store.destroy();
            } 
            else {
                this.mun(this.store, {
                    scope: this,
                    beforeload: this.onBeforeLoad,
                    datachanged: this.onDataChanged,
                    add: this.onAdd,
                    remove: this.onRemove,
                    update: this.onUpdate,
                    clear: this.refresh                    
                });
            }
            if (!store) {
                if (this.loadMask) {
                    this.loadMask.bindStore(null);
                }
                this.store = null;
            }
        }
        if (store) {
            store = Ext.StoreMgr.lookup(store);
            this.mon(store, {
                scope: this,
                beforeload: this.onBeforeLoad,
                datachanged: this.onDataChanged,
                add: this.onAdd,
                remove: this.onRemove,
                update: this.onUpdate,
                clear: this.refresh                    
            });
            if (this.loadMask) {
                this.loadMask.bindStore(store);
            }
        }
        
        this.store = store;
        
        this.getSelectionModel().bind(store);
        
        if (store) {
            this.refresh();
        }
    },

    
    onDataChanged: function() {
        if (this.blockRefresh !== true) {
            this.refresh.apply(this, arguments);
        }
    },

    
    findItemByChild: function(node){
        return Ext.fly(node).findParent(this.itemSelector, this.getTargetEl());
    },
    
    
    findTargetByEvent: function(e) {
        return e.getTarget(this.itemSelector, this.getTargetEl());
    },


    
    getSelectedNodes: function(){
        var nodes   = [],
            records = this.selModel.getSelection(),
            ln = records.length,
            i  = 0;

        for (; i < ln; i++) {
            nodes.push(this.getNode(records[i]));
        }

        return nodes;
    },

    
    getRecords: function(nodes) {
        var records = [],
            i = 0,
            len = nodes.length;

        for (; i < len; i++) {
            records[records.length] = this.store.getAt(nodes[i].viewIndex);
        }

        return r;
    },

    
    getRecord: function(node){
        return this.store.getAt(node.viewIndex);
    },

    
    isSelected : function(node) {
        
        var r = this.getRecord(node);
        return this.selModel.isSelected(r);
    },
    
    
    select: function(records, keepExisting, suppressEvent) {
        this.selModel.select(records, keepExisting, suppressEvent);
    },

    
    deselect: function(records, suppressEvent) {
        this.selModel.deselect(records, suppressEvent);
    },

    
    getNode : function(nodeInfo) {
        if (Ext.isString(nodeInfo)) {
            return document.getElementById(nodeInfo);
        } else if (Ext.isNumber(nodeInfo)) {
            return this.all.elements[nodeInfo];
        } else if (nodeInfo instanceof Ext.data.Model) {
            var idx = this.store.indexOf(nodeInfo);
            return this.all.elements[idx];
        }
        return nodeInfo;
    },

    
    getNodes: function(start, end) {
        var ns = this.all.elements,
            nodes = [],
            i;

        start = start || 0;
        end = !Ext.isDefined(end) ? Math.max(ns.length - 1, 0) : end;
        if (start <= end) {
            for (i = start; i <= end && ns[i]; i++) {
                nodes.push(ns[i]);
            }
        } else {
            for (i = start; i >= end && ns[i]; i--) {
                nodes.push(ns[i]);
            }
        }
        return nodes;
    },

    
    indexOf: function(node) {
        node = this.getNode(node);
        if (Ext.isNumber(node.viewIndex)) {
            return node.viewIndex;
        }
        return this.all.indexOf(node);
    },

    
    onBeforeLoad: function() {
        if (this.loadingText) {
            this.getTargetEl().update('');
            this.all.clear();
        }
    },

    onDestroy : function() {
        this.all.clear();
        Ext.DataView.superclass.onDestroy.call(this);
        this.bindStore(null);
        this.selModel.destroy();
    },

    
    onItemSelect: function(record) {
        var node = this.getNode(record);
        Ext.fly(node).addCls(this.selectedItemCls);
    },

    
    onItemDeselect: function(record) {
        var node = this.getNode(record);
        Ext.fly(node).removeCls(this.selectedItemCls);
    },
    
    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();
    }
});
Ext.reg('dataview', Ext.DataView);






Ext.DataView.override({
    
    
    

    
    getSelectionCount : function(){
        return this.selModel.getSelection().length;
    },

    
    getSelectedRecords : function(){
        return this.selModel.getSelection();
    }
});

Ext.DataView.override({
    scroll: 'vertical',

    
    pressedCls : "x-item-pressed",

    
    pressedDelay: 100,
    
    
    allowDeselect: true,

    
    triggerEvent: 'singletap',
    
    triggerCtEvent: 'containertap',
    
    
    
    addCmpEvents: function() {

        this.addEvents(
            
            'itemtap',

            
            'itemdoubletap',

            
            'itemswipe',

            
            "containertap",

            
            "selectionchange",

            
            "beforeselect"
        );

    },

    
    afterRender: function() {
        var me = this;

        Ext.DataView.superclass.afterRender.call(me);

        var eventHandlers = {
            tapstart : me.onTapStart,
            tapcancel: me.onTapCancel,
            touchend : me.onTapCancel,
            doubletap: me.onDoubleTap,
            swipe    : me.onSwipe,
            scope    : me
        };
        eventHandlers[this.triggerEvent] = me.onTap;
        me.mon(me.getTargetEl(), eventHandlers);
        
        if (this.store) {
            this.bindStore(this.store, true);
        }
    },

    
    onTap: function(e) {
        var item = this.findTargetByEvent(e);
        if (item) {
            Ext.fly(item).removeCls(this.pressedCls);
            var index = this.indexOf(item);
            if (this.onItemTap(item, index, e) !== false) {
                this.fireEvent("itemtap", this, index, item, e);
            }
        }
        else {
            if(this.fireEvent("containertap", this, e) !== false) {
                this.onContainerTap(e);
            }
        }
    },

    
    onTapStart: function(e, t) {
        var me = this,
            item = this.findTargetByEvent(e);

        if (item) {
            if (me.pressedDelay) {
                if (me.pressedTimeout) {
                    clearTimeout(me.pressedTimeout);
                }
                me.pressedTimeout = setTimeout(function() {
                    Ext.fly(item).addCls(me.pressedCls);
                }, Ext.isNumber(me.pressedDelay) ? me.pressedDelay : 100);
            }
            else {
                Ext.fly(item).addCls(me.pressedCls);
            }
        }
    },

    
    onTapCancel: function(e, t) {
        var me = this,
            item = this.findTargetByEvent(e);

        if (me.pressedTimeout) {
            clearTimeout(me.pressedTimeout);
            delete me.pressedTimeout;
        }

        if (item) {
            Ext.fly(item).removeCls(me.pressedCls);
        }
    },

    
    onContainerTap: function(e) {
        
        
        
    },

    
    onDoubleTap: function(e) {
        var item = this.findTargetByEvent(e);
        if (item) {
            this.fireEvent("itemdoubletap", this, this.indexOf(item), item, e);
        }
    },

    
    onSwipe: function(e) {
        var item = this.findTargetByEvent(e);
        if (item) {
            this.fireEvent("itemswipe", this, this.indexOf(item), item, e);
        }
    },

    
    onItemTap: function(item, index, e) {
        if (this.pressedTimeout) {
            clearTimeout(this.pressedTimeout);
            delete this.pressedTimeout;
        }
        return true;
    }
});


Ext.List = Ext.extend(Ext.DataView, {
    componentCls: 'x-list',

    
    pinHeaders: Ext.is.iOS || Ext.is.Desktop,

    
    indexBar: false,

    
    grouped: false,

    
    clearSelectionOnDeactivate: true,

    renderTpl: [
        '<tpl if="grouped"><h3 class="x-list-header x-list-header-swap x-hidden-display"></h3></tpl>'
    ],

    groupTpl : [
        '<tpl for=".">',
            '<div class="x-list-group x-group-{id}">',
                '<h3 class="x-list-header">{group}</h3>',
                '<div class="x-list-group-items">',
                    '{items}',
                '</div>',
            '</div>',
        '</tpl>'
    ],
    
    
    itemSelector: '.x-list-item',
    
    
    itemCls: '',
    
    

    
    onItemDisclosure: false,
    
    
    preventSelectionOnDisclose: true,

    
    initComponent : function() {
        
        
        
        var memberFnsCombo = {};
        
        if (Ext.isArray(this.itemTpl)) {
            this.itemTpl = this.itemTpl.join('');
        } else if (this.itemTpl && this.itemTpl.html) {
            Ext.apply(memberFnsCombo, this.itemTpl.initialConfig);
            this.itemTpl = this.itemTpl.html;
        }
        
        if (!Ext.isDefined(this.itemTpl)) {
            throw new Error("Ext.List: itemTpl is a required configuration.");
        }
        
        
        if (this.itemTpl && this.itemTpl.indexOf("\"x-list-item\"") !== -1) {
            throw new Error("Ext.List: Using a CSS class of x-list-item within your own tpl will break Ext.Lists. Remove the x-list-item from the tpl/itemTpl");
        }
        
        this.tpl = '<tpl for="."><div class="x-list-item ' + this.itemCls + '"><div class="x-list-item-body">' + this.itemTpl + '</div>';
        if (this.onItemDisclosure) {
            this.tpl += '<div class="x-list-disclosure"></div>';
        }
        this.tpl += '</div></tpl>';
        this.tpl = new Ext.XTemplate(this.tpl, memberFnsCombo);
       

        if (this.grouped) {
            
            this.listItemTpl = this.tpl;
            if (Ext.isString(this.listItemTpl) || Ext.isArray(this.listItemTpl)) {
                
                
                this.listItemTpl = new Ext.XTemplate(this.listItemTpl, memberFnsCombo);
            }
            if (Ext.isString(this.groupTpl) || Ext.isArray(this.groupTpl)) {
                this.tpl = new Ext.XTemplate(this.groupTpl);
            }
        }
        else {
            this.indexBar = false;
        }
        
        if (this.scroll !== false) {
            this.scroll = {
                direction: 'vertical',
                useIndicators: !this.indexBar
            };
        }

        
        
        
        
        Ext.List.superclass.initComponent.call(this);

        if (this.onItemDisclosure) {
            
            
            if (Ext.isFunction(this.onItemDisclosure)) {
                this.onItemDisclosure = {
                    scope: this,
                    handler: this.onItemDisclosure
                };
            }
        }

        this.on('deactivate', this.onDeactivate, this);
        
        this.addEvents(
             
             'disclose',
             
             
             'update'
         );
    },

    
    onRender : function() {
        if (this.grouped) {
            Ext.applyIf(this.renderData, {
                grouped: true
            });

            if (this.scroll) {
                Ext.applyIf(this.renderSelectors, {
                    header: '.x-list-header-swap'
                });                
            }
        }
        
        Ext.List.superclass.onRender.apply(this, arguments);
    },

    
    onDeactivate : function() {
        if (this.clearSelectionOnDeactivate) {
            this.getSelectionModel().deselectAll();
        }
    },

    
    afterRender : function() {
        if (!this.grouped) {
            this.el.addCls('x-list-flat');
        }
        this.getTargetEl().addCls('x-list-parent');

        if (this.indexBar) {
            this.indexBar = new Ext.IndexBar(Ext.apply({}, Ext.isObject(this.indexBar) ? this.indexBar : {}, {
                xtype: 'indexbar',
                alphabet: true,
                renderTo: this.el
            }));
            this.addCls('x-list-indexed');
        }
        
        Ext.List.superclass.afterRender.call(this);
        
        if (this.onItemDisclosure) {
            this.mon(this.getTargetEl(), 'singletap', this.handleItemDisclosure, this, {delegate: '.x-list-disclosure'});
        }
    },

    
    initEvents : function() {
        Ext.List.superclass.initEvents.call(this);

        if (this.grouped) {
            if (this.pinHeaders && this.scroll) {
                this.mon(this.scroller, {
                    scrollstart: this.onScrollStart,
                    scroll: this.onScroll,
                    scope: this
                });
            }

            if (this.indexBar) {
                this.mon(this.indexBar, {
                    index: this.onIndex,
                    scope: this
                });
            }
        }
    },

    //@private

    handleItemDisclosure : function(e, t) {
        var node = this.findItemByChild(t),
            record, index;
            
        if (node) {
            record = this.getRecord(node);
            index  = this.indexOf(node);
            if (this.preventSelectionOnDisclose) {
                e.stopEvent();
            }
            this.fireEvent('disclose', record, node, index, e);
     
            if (Ext.isObject(this.onItemDisclosure) && this.onItemDisclosure.handler) {
                this.onItemDisclosure.handler.call(this, record, node, index);
            }
        }
    },

    
    setActiveGroup : function(group) {
        var me = this;
        if (group) {
            if (!me.activeGroup || me.activeGroup.header != group.header) {
                me.header.setHTML(group.header.getHTML());
                me.header.show();
            }            
        }
        else {
            me.header.hide();
        }

        this.activeGroup = group;
    },

    
    getClosestGroups : function(pos) {
        
        if (!this.groupOffsets) {
            this.updateOffsets();
        }
        var groups = this.groupOffsets,
            ln = groups.length,
            group, i,
            current, next;

        for (i = 0; i < ln; i++) {
            group = groups[i];
            if (group.offset > pos.y) {
                next = group;
                break;
            }
            current = group;
        }

        return {
            current: current,
            next: next
        };
    },

    updateIndexes : function() {
        Ext.List.superclass.updateIndexes.apply(this, arguments);
        this.updateList();
    },

    afterComponentLayout : function() {
        Ext.List.superclass.afterComponentLayout.apply(this, arguments);
        this.updateList();
    },

    updateList : function() {
        this.fireEvent('update', this);
        this.updateOffsets();
    },
    
    updateOffsets : function() {
        if (this.grouped) {
            this.groupOffsets = [];

            var headers = this.getTargetEl().query('h3.x-list-header'),
                ln = headers.length,
                header, i;

            for (i = 0; i < ln; i++) {
                header = Ext.get(headers[i]);
                header.setVisibilityMode(Ext.Element.VISIBILITY);
                this.groupOffsets.push({
                    header: header,
                    offset: header.dom.offsetTop
                });
            }
        }
    },

    
    onScrollStart : function() {
        var offset = this.scroller.getOffset();
        this.closest = this.getClosestGroups(offset);
        this.setActiveGroup(this.closest.current);
    },

    
    onScroll : function(scroller, pos, options) {
        if (!this.closest) {
            this.closest = this.getClosestGroups(pos);
        }

        if (!this.headerHeight) {
            this.headerHeight = this.header.getHeight();
        }

        if (pos.y <= 0) {
            if (this.activeGroup) {
                this.setActiveGroup(false);
                this.closest.next = this.closest.current;
            }
            return;
        }
        else if (
            (this.closest.next && pos.y > this.closest.next.offset) ||
            (pos.y < this.closest.current.offset)
        ) {
            this.closest = this.getClosestGroups(pos);
            this.setActiveGroup(this.closest.current);
        }
        if (this.closest.next && pos.y > 0 && this.closest.next.offset - pos.y <= this.headerHeight) {
            var transform = this.headerHeight - (this.closest.next.offset - pos.y);
            Ext.Element.cssTranslate(this.header, {x: 0, y: -transform});
            this.transformed = true;
        }
        else if (this.transformed) {
            this.header.setStyle('-webkit-transform', null);
            this.transformed = false;
        }
    },

    
    onIndex : function(record, target, index) {
        var key = record.get('key').toLowerCase(),
            groups = this.store.getGroups(),
            ln = groups.length,
            group, i, closest, id;

        for (i = 0; i < ln; i++) {
            group = groups[i];
            id = this.getGroupId(group);

            if (id == key || id > key) {
                closest = id;
                break;
            }
            else {
                closest = id;
            }
        }

        closest = this.getTargetEl().down('.x-group-' + id);
        if (closest) {
            this.scroller.scrollTo({x: 0, y: closest.getOffsetsTo(this.scrollEl)[1]}, false, null, true);
        }
    },
    
    getGroupId : function(group) {
        return group.name.toLowerCase();
    },

    
    collectData : function(records, startIndex) {
        if (!this.grouped) {
            return Ext.List.superclass.collectData.call(this, records, startIndex);
        }

        var results = [],
            groups = this.store.getGroups(),
            ln = groups.length,
            children, cln, c,
            group, i;

        for (i = 0, ln = groups.length; i < ln; i++) {
            group = groups[i];
            children = group.children;
            for (c = 0, cln = children.length; c < cln; c++) {
                children[c] = children[c].data;
            }
            results.push({
                group: group.name,
                id: this.getGroupId(group),
                items: this.listItemTpl.apply(children)
            });
        }

        return results;
    },

    
    
    
    onUpdate : function(store, record) {
        if (this.grouped) {
            this.refresh();
        }
        else {
            Ext.List.superclass.onUpdate.apply(this, arguments);
        }
    },

    
    onAdd : function(ds, records, index) {
        if (this.grouped) {
            this.refresh();
        }
        else {
            Ext.List.superclass.onAdd.apply(this, arguments);
        }
    },

    
    onRemove : function(ds, record, index) {
        if (this.grouped) {
            this.refresh();
        }
        else {
            Ext.List.superclass.onRemove.apply(this, arguments);
        }
    }
});

Ext.reg('list', Ext.List);


Ext.IndexBar = Ext.extend(Ext.DataView, {
    
    componentCls: 'x-indexbar',

    
    direction: 'vertical',

    
    tpl: '<tpl for="."><div class="x-indexbar-item">{value}</div></tpl>',

    
    itemSelector: 'div.x-indexbar-item',

    
    letters: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],

    
    listPrefix: '',

    

    

    
    componentLayout: 'autocomponent',

    scroll: false,
    
    
    initComponent : function() {
        
        this.componentLayout = this.getComponentLayout();

        if (!this.store) {
            this.store = new Ext.data.Store({
                model: 'IndexBarModel'
            });
        }

        if (this.alphabet == true) {
            this.ui = this.ui || 'alphabet';
        }

        if (this.direction == 'horizontal') {
            this.horizontal = true;
        }
        else {
            this.vertical = true;
        }

        this.addEvents(
          
          'index'
        );

        Ext.apply(this.renderData, {
            componentCls: this.componentCls
        });
        
        Ext.apply(this.renderSelectors, {
            body: '.' + this.componentCls + '-body'
        });
        
        Ext.IndexBar.superclass.initComponent.call(this);
    },

    renderTpl : ['<div class="{componentCls}-body"></div>'],
    
    getTargetEl : function() {
        return this.body;
    },
    
    
    afterRender : function() {
        Ext.IndexBar.superclass.afterRender.call(this);

        if (this.alphabet === true) {
            this.loadAlphabet();
        }
        if (this.vertical) {
            this.el.addCls(this.componentCls + '-vertical');
        }
        else if (this.horizontal) {
            this.el.addCls(this.componentCls + '-horizontal');
        }
    },

    
    loadAlphabet : function() {
        var letters = this.letters,
            len = letters.length,
            data = [],
            i, letter;

        for (i = 0; i < len; i++) {
            letter = letters[i];
            data.push({key: letter.toLowerCase(), value: letter});
        }

        this.store.loadData(data);
    },

    
    refresh: function() {
        var el = this.getTargetEl(),
            records = this.store.getRange();

        el.update('');
        if (records.length < 1) {
            if (!this.deferEmptyText || this.hasSkippedEmptyText) {
                el.update(this.emptyText);
            }
            this.all.clear();
        } else {
            this.tpl.overwrite(el, this.collectData(records, 0));
            this.all.fill(Ext.query(this.itemSelector, el.dom));
            this.updateIndexes(0);
        }
        this.hasSkippedEmptyText = true;
        this.fireEvent('refresh');
    },
    
    collectData : function() {
        var data = Ext.IndexBar.superclass.collectData.apply(this, arguments);
        if (this.listPrefix.length > 0) {
            data.unshift({
                key: '',
                value: this.listPrefix
            });
        }
        return data;
    },

    
    initEvents : function() {
        Ext.IndexBar.superclass.initEvents.call(this);

        this.mon(this.el, {
            touchstart: this.onTouchStart,
            touchend: this.onTouchEnd,
            touchmove: this.onTouchMove,
            scope: this
        });
    },

    
    onTouchStart : function(e, t) {
        e.stopEvent();
        this.el.addCls(this.componentCls + '-pressed');
        this.pageBox = this.el.getPageBox();
        this.onTouchMove(e);
    },

    
    onTouchEnd : function(e, t) {
        e.stopEvent();
        this.el.removeCls(this.componentCls + '-pressed');
    },

    
    onTouchMove : function(e) {
        e.stopPropagation();

        var point = Ext.util.Point.fromEvent(e),
            target,
            record,
            pageBox = this.pageBox;

        if (!pageBox) {
            pageBox = this.pageBox = this.el.getPageBox();
        }

        if (this.vertical) {
            if (point.y > pageBox.bottom || point.y < pageBox.top) {
                return;
            }
            target = Ext.Element.fromPoint(pageBox.left + (pageBox.width / 2), point.y);
        }
        else if (this.horizontal) {
            if (point.x > pageBox.right || point.x < pageBox.left) {
                return;
            }
            target = Ext.Element.fromPoint(point.x, pageBox.top + (pageBox.height / 2));
        }

        if (target) {
            record = this.getRecord(target.dom);
            if (record) {
                this.fireEvent('index', record, target, this.indexOf(target));
            }
        }
    },
    
    
    isVertical : function() {
        return this.vertical;
    },
    
    
    isHorizontal : function() {
        return this.horizontal;
    }
});

Ext.reg('indexbar', Ext.IndexBar);

Ext.regModel('IndexBarModel', {
    fields: ['key', 'value']
});


Ext.Toolbar = Ext.extend(Ext.Container, {
    
    isToolbar: true,
    
    
    defaultType: 'button',

    
    baseCls: 'x-toolbar',

    
    titleCls: 'x-toolbar-title',

    
    ui: 'dark',

    
    layout: null,

    

    

    
    titleEl: null,

    initComponent : function() {
        this.layout = Ext.apply({}, this.layout || {}, {
            type: 'hbox',
            align: 'center'
        });
        Ext.Toolbar.superclass.initComponent.call(this);
    },

    afterRender : function() {
        Ext.Toolbar.superclass.afterRender.call(this);

        if (this.title) {
            this.titleEl = this.el.createChild({
                cls: this.titleCls,
                html: this.title
            });
        }
    },

    
    setTitle : function(title) {
        this.title = title;
        if (this.rendered) {
            if (!this.titleEl) {
                this.titleEl = this.el.createChild({
                    cls: this.titleCls,
                    html: this.title
                });
            }
            this.titleEl.setHTML(title);
        }
    },

    
    showTitle : function() {
        if (this.titleEl) {
            this.titleEl.show();
        }
    },

    
    hideTitle : function() {
        if (this.titleEl) {
            this.titleEl.hide();
        }
    }
});

Ext.reg('toolbar', Ext.Toolbar);



Ext.Spacer = Ext.extend(Ext.Component, {
    initComponent : function() {
        if (!this.width) {
            this.flex = 1;
        }

        Ext.Spacer.superclass.initComponent.call(this);
    },

    onRender : function() {
        Ext.Spacer.superclass.onRender.apply(this, arguments);

        if (this.flex) {
            this.el.setStyle('-webkit-box-flex', this.flex);
        }
    }
});

Ext.reg('spacer', Ext.Spacer);

Ext.Sheet = Ext.extend(Ext.Panel, {
    baseCls : 'x-sheet',
    centered : false,
    floating : true,
    modal    : true,
    draggable : false,
    monitorOrientation : true,
    
    hidden    : true,
    
    
    hideOnMaskTap : false,
    
    

    

    
    enter : 'bottom',

    
    exit : 'bottom',


    
    enterAnimation : 'slide',

    
    exitAnimation : 'slide',

    
    transitions : {
        bottom : 'up',
        top    : 'down',
        right  : 'left',
        left   : 'right'
    },

    //@private

    animSheet : function(animate) {
      var anim = null,
          me = this,
          tr = me.transitions,
          opposites = Ext.Anim.prototype.opposites || {};

      if (animate && this[animate]) {
         if (animate == 'enter') {
             anim = (typeof me.enterAnimation == 'string') ?
                 {
                     type : me.enterAnimation || 'slide',
                     direction : tr[me.enter] || 'up'
                 } : me.enterAnimation;

         }
         else if (animate == 'exit') {
            anim = (typeof me.exitAnimation == 'string') ?
                 {
                     type : me.exitAnimation || 'slide',
                     direction : tr[me.exit] || 'down'
                 } :  me.exitAnimation;
         }
      }
      return anim;
    },

    
    orient : function(orientation, w, h) {
        if(!this.container || this.centered || !this.floating){
            return this;
        }

        var me = this,
            cfg = me.initialConfig || {},
            
            size = {width : cfg.width, height : cfg.height},
            pos  = {x : cfg.x, y : cfg.y},
            box  = me.el.getPageBox(),
            pageBox, scrollTop = 0;

        if (me.container.dom == document.body) {
            pageBox = {
                width: window.innerWidth,
                height: window.innerHeight
            };
            scrollTop = document.body.scrollTop;
        }
        else {
            pageBox = me.container.getPageBox();
        }

        pageBox.centerY = pageBox.height / 2;
        pageBox.centerX = pageBox.width / 2;

        if(pos.x != undefined || pos.y != undefined){
            pos.x = pos.x || 0;
            pos.y = pos.y || 0;
        }
        else {
            if (/^(bottom|top)/i.test(me.enter)) {
                size.width  = me.stretchX ? pageBox.width : Math.min(200,Math.max(size.width || box.width || pageBox.width, pageBox.width));
                size.height = Math.min(size.height || 0, pageBox.height) || undefined;
                size = me.setSize(size).getSize();
                pos.x = pageBox.centerX - size.width / 2;
                pos.y = me.enter == 'top' ? 0 : pageBox.height - size.height + scrollTop;

            } else if (/^(left|right)/i.test(me.enter)) {
                size.height = me.stretchY ? pageBox.height : Math.min(200, Math.max(size.height || box.height || pageBox.height, pageBox.height));
                size.width  = Math.min(size.width || 0, pageBox.width) || undefined;
                size = me.setSize(size).getSize();
                pos.y = 0;
                pos.x = me.enter == 'left' ? 0 : pageBox.width - size.width;
            }
        }
        me.setPosition(pos);
        return this;
    },

     
    afterRender : function() {
        Ext.Sheet.superclass.afterRender.apply(this, arguments);
        this.el.setVisibilityMode(Ext.Element.OFFSETS);
    },

    
    onShow : function(animation) {
        this.orient();
        return Ext.Sheet.superclass.onShow.call(this, animation || this.animSheet('enter'));
    },

    
    onOrientationChange : function(orientation, w, h) {
        this.orient();
        Ext.Sheet.superclass.onOrientationChange.apply(this, arguments);
    },

    
    beforeDestroy : function() {
        delete this.showAnimation;
        this.hide(false);
        Ext.Sheet.superclass.beforeDestroy.call(this);
    }
});

Ext.reg('sheet', Ext.Sheet);



Ext.ActionSheet = Ext.extend(Ext.Sheet, {
    componentCls: 'x-sheet-action',

    stretchY: true,
    stretchX: true,

    defaultType: 'button',

    constructor : function(config) {
        config = config || {};

        Ext.ActionSheet.superclass.constructor.call(this, Ext.applyIf({
            floating : true
        }, config));
    }
});

Ext.reg('actionsheet', Ext.ActionSheet);

Ext.TabBar = Ext.extend(Ext.Panel, {
    componentCls: 'x-tabbar',

    
    activeTab: null,

    
    defaultType: 'tab',

    
    sortable: false,

    
    sortHoldThreshold: 350,

    
    initComponent : function() {
        
        this.addEvents('change');

        this.layout = Ext.apply({}, this.layout || {}, {
            type: 'hbox',
            align: 'middle'
        });

        Ext.TabBar.superclass.initComponent.call(this);

    },

    
    initEvents : function() {
        if (this.sortable) {
            this.sortable = new Ext.util.Sortable(this.el, {
                itemSelector: '.x-tab',
                direction: 'horizontal',
                delay: this.sortHoldThreshold,
                constrain: true
            });
            this.mon(this.sortable, 'sortchange', this.onSortChange, this);
        }

        this.mon(this.el, {
            touchstart: this.onTouchStart,
            scope: this
        });

        Ext.TabBar.superclass.initEvents.call(this);
    },

    
    onTouchStart : function(e, t) {
        t = e.getTarget('.x-tab');
        if (t) {
            this.onTabTap(Ext.getCmp(t.id));
        }
    },

    
    onSortChange : function(sortable, el, index) {
    },

    
    onTabTap : function(tab) {
        if (!tab.disabled) {
            if (this.cardLayout) {
                if (this.cardSwitchAnimation) {
                    var animConfig = {
                        reverse: (this.items.indexOf(tab) < this.items.indexOf(this.activeTab)) ? true : false
                    };

                    if (Ext.isObject(this.cardSwitchAnimation)) {
                        Ext.apply(animConfig, this.cardSwitchAnimation);
                    } else {
                        Ext.apply(animConfig, {
                            type: this.cardSwitchAnimation
                        });
                    }
                }
                
                this.cardLayout.setActiveItem(tab.card, animConfig || this.cardSwitchAnimation);
            }
            this.activeTab = tab;
            this.fireEvent('change', this, tab, tab.card);
        }
    },

    
    getCardLayout : function() {
        return this.cardLayout;
    }
});

Ext.reg('tabbar', Ext.TabBar);


Ext.Tab = Ext.extend(Ext.Button, {
    isTab: true,
    baseCls: 'x-tab',

    
    pressedCls: 'x-tab-pressed',

    
    activeCls: 'x-tab-active',

    
    active: false,

    
    initComponent : function() {
        this.addEvents(
            
            'activate',
            
            'deactivate'
        );

        Ext.Tab.superclass.initComponent.call(this);

        var card = this.card;
        if (card) {
            this.card = null;
            this.setCard(card);
        }
    },

    
    setCard : function(card) {
        if (this.card) {
            this.mun(this.card, {
                activate: this.activate,
                deactivate: this.deactivate,
                scope: this
            });
        }
        this.card = card;
        if (card) {
            Ext.apply(this, card.tab || {});
            this.setText(this.title || card.title || this.text );
            this.setIconClass(this.iconCls || card.iconCls);
            this.setBadge(this.badgeText || card.badgeText);

            this.mon(card, {
                beforeactivate: this.activate,
                beforedeactivate: this.deactivate,
                scope: this
            });
        }
    },

    onRender: function() {
        Ext.Tab.superclass.onRender.apply(this, arguments);
        if (this.active) {
            this.el.addCls(this.activeCls);
        }
    },

    
    getCard : function() {
        return this.card;
    },

    
    activate : function() {
        this.active = true;
        if (this.el) {
            this.el.addCls(this.activeCls);
        }
        this.fireEvent('activate', this);
    },

    
    deactivate : function() {
        this.active = false;
        if (this.el) {
            this.el.removeCls(this.activeCls);
        }
        this.fireEvent('deactivate', this);
    }
});

Ext.reg('tab', Ext.Tab);


Ext.TabPanel = Ext.extend(Ext.Panel, {
    
    cardSwitchAnimation: 'slide',

    
    tabBarDock: 'top',
    componentCls: 'x-tabpanel',

    
    ui: 'dark',

    

    

    

    
    initComponent : function() {

        
        var layout = new Ext.layout.CardLayout(this.layout || {});
        this.layout = null;
        this.setLayout(layout);

        this.tabBar = new Ext.TabBar(Ext.apply({}, this.tabBar || {}, {
            cardLayout: layout,
            cardSwitchAnimation: this.cardSwitchAnimation,
            dock: this.tabBarDock,
            ui: this.ui,
            sortable: this.sortable
        }));

        if (this.dockedItems && !Ext.isArray(this.dockedItems)) {
            this.dockedItems = [this.dockedItems];
        }
        else if (!this.dockedItems) {
            this.dockedItems = [];
        }
        this.dockedItems.push(this.tabBar);

        Ext.TabPanel.superclass.initComponent.call(this);
    },

    
    getTabBar : function() {
        return this.tabBar;
    },
    
    
    
    onAdd: function(cmp, idx) {
        var tabBar = this.tabBar;
        
        cmp.tab = tabBar.insert(idx, {
            xtype: 'tab',
            card: cmp
        });
        tabBar.doLayout();
    },
    
    
    onRemove: function(cmp, autoDestroy) {
        
        if (!this.destroying) {
            this.tabBar.remove(cmp.tab, autoDestroy);
            this.tabBar.doLayout();
        }
    }
});

Ext.reg('tabpanel', Ext.TabPanel);


Ext.Carousel = Ext.extend(Ext.Panel, {
    
    baseCls: 'x-carousel',

    
    indicator: true,

    
    ui: 'dark',

    
    direction: 'horizontal',

    
    horizontal: false,
    
    vertical: false,
    
    
    initComponent: function() {
        this.layout = {
            type: 'card',
            
            sizeAllCards: true,
            
            hideInactive: false,
            itemCls: 'x-carousel-item',
            targetCls: 'x-carousel-body',
            setOwner : function(owner) {
                Ext.layout.CardLayout.superclass.setOwner.call(this, owner);
            }
        };
         
        if (this.indicator) {
            var cfg = Ext.isObject(this.indicator) ? this.indicator : {};
            this.indicator = new Ext.Carousel.Indicator(Ext.apply({}, cfg, {
                direction: this.direction,
                carousel: this,
                ui: this.ui
            }));
        }

        if (this.direction == 'horizontal') {
            this.horizontal = true;
        }
        else {
            this.vertical = true;
        }
        
        Ext.Carousel.superclass.initComponent.call(this);
    },

    
    afterRender: function() {
        Ext.Carousel.superclass.afterRender.call(this);

        
        this.mon(this.body, {
            drag: this.onDrag,
            dragThreshold: 5,
            dragend: this.onDragEnd,
            direction: this.direction,
            scope: this
        });
        
        this.el.addCls(this.baseCls + '-' + this.direction);
    },
    
    
    onAdd: function(){
        Ext.Carousel.superclass.onAdd.apply(this, arguments);
        var indicator = this.indicator;
        if (indicator) {
            indicator.onCardAdd();
        }    
    },
    
    
    onRemove: function(){
        Ext.Carousel.superclass.onRemove.apply(this, arguments);
        var indicator = this.indicator;
        if (indicator) {
            indicator.onCardRemove();
        }
    },
    
        
    afterLayout : function() {
        Ext.Carousel.superclass.afterLayout.apply(this, arguments);
        
        this.currentSize = this.body.getSize();
        this.currentScroll = {x: 0, y: 0};
        
        this.updateCardPositions();
        
        var activeItem = this.layout.getActiveItem();        
        if (activeItem && this.indicator) {  
            this.indicator.onBeforeCardSwitch(this, activeItem, null, this.items.indexOf(activeItem));
        }
    },

        
    onDrag : function(e) {
        this.currentScroll = {
            x: e.deltaX,
            y: e.deltaY
        };
        
        
        var activeIndex = this.items.items.indexOf(this.layout.activeItem);
        
        if (this.horizontal) {
            if (
                
                (activeIndex == 0 && e.deltaX > 0) || 
                
                (activeIndex == this.items.length - 1 && e.deltaX < 0)
            ) {
                
                this.currentScroll.x = e.deltaX / 2;             
            }
        }
        
        else if (this.vertical) {
            if (
                
                (activeIndex == 0 && e.deltaY > 0) || 
                
                (activeIndex == this.items.length - 1 && e.deltaY < 0)
            ) {
                
                this.currentScroll.y = e.deltaY / 2;
            }
        }
        
        this.updateCardPositions();
    },

    
    updateCardPositions : function(animate) {
        var cards = this.items.items,
            ln = cards.length,
            cardOffset,
            i, card, el, style;
        
        
        
        
        for (i = 0; i < ln; i++) {
            card = cards[i];  
            
            
            if (this.isCardInRange(card)) {
                if (card.hidden) {
                    card.show();
                }
                
                el = card.el;
                style = el.dom.style;
                
                if (animate) {
                    if (card === this.layout.activeItem) {
                        el.on('webkitTransitionEnd', this.onTransitionEnd, this, {single: true});
                    }
                    style.webkitTransitionDuration = '300ms';
                }
                else {
                    style.webkitTransitionDuration = '0ms';
                }

                cardOffset = this.getCardOffset(card);
                if (this.horizontal) {
                    Ext.Element.cssTransform(el, {translate: [cardOffset, 0]});
                }
                else {
                    Ext.Element.cssTransform(el, {translate: [0, cardOffset]});
                }
            }
            else if (!card.hidden) {
                
                card.hide();
            }
        }
    },

        
    getCardOffset : function(card) {
        var cardOffset = this.getCardIndexOffset(card),
            currentSize = this.currentSize,
            currentScroll = this.currentScroll;
            
        return this.horizontal ?
            (cardOffset * currentSize.width) + currentScroll.x :
            (cardOffset * currentSize.height) + currentScroll.y;
    },

            
    getCardIndexOffset : function(card) {
        return this.items.items.indexOf(card) - this.getActiveIndex();
    },

        
    isCardInRange : function(card) {
        return Math.abs(this.getCardIndexOffset(card)) <= 2;
    },

        
    getActiveIndex : function() {
        return this.items.indexOf(this.layout.activeItem);
    },

            
    onDragEnd : function(e, t) {
        var previousDelta, deltaOffset; 
            
        if (this.horizontal) {
            deltaOffset = e.deltaX;
            previousDelta = e.previousDeltaX;
        }
        else {
            deltaOffset = e.deltaY;
            previousDelta = e.previousDeltaY;
        }
            
        
        if (deltaOffset < 0 && Math.abs(deltaOffset) > 3 && previousDelta <= 0 && this.layout.getNext()) {
            this.next();
        }
        
        else if (deltaOffset > 0 && Math.abs(deltaOffset) > 3 && previousDelta >= 0 && this.layout.getPrev()) {
            this.prev();
        }
        else {
            
            this.scrollToCard(this.layout.activeItem);
        }
    },

    
    onBeforeCardSwitch : function(newCard) {
        if (!this.customDrag && this.items.indexOf(newCard) != -1) {
            var style = newCard.el.dom.style;
            style.webkitTransitionDuration = null;
            style.webkitTransform = null;
        }
        return Ext.Carousel.superclass.onBeforeCardSwitch.apply(this, arguments);
    },

        
    scrollToCard : function(newCard) {
        this.currentScroll = {x: 0, y: 0};
        this.oldCard = this.layout.activeItem;
        
        if (newCard != this.oldCard && this.isCardInRange(newCard) && this.onBeforeCardSwitch(newCard, this.oldCard, this.items.indexOf(newCard), true) !== false) {
            this.layout.activeItem = newCard;
            if (this.horizontal) {
                this.currentScroll.x = -this.getCardOffset(newCard);
            }
            else {
                this.currentScroll.y = -this.getCardOffset(newCard);
            }
        }
        
        this.updateCardPositions(true);
    },    

    
    onTransitionEnd : function(e, t) {
        this.customDrag = false;
        this.currentScroll = {x: 0, y: 0};
        if (this.oldCard && this.layout.activeItem != this.oldCard) {
            this.onCardSwitch(this.layout.activeItem, this.oldCard, this.items.indexOf(this.layout.activeItem), true);
        }
        delete this.oldCard;
    },
        
    
    onCardSwitch : function(newCard, oldCard, index, animated) {
        this.currentScroll = {x: 0, y: 0};
        this.updateCardPositions();
        Ext.Carousel.superclass.onCardSwitch.apply(this, arguments);
        newCard.fireEvent('activate', newCard);
    },

    
    next: function() {
        var next = this.layout.getNext();
        if (next) {
            this.customDrag = true;
            this.scrollToCard(next);
        }
        return this;
    },

    
    prev: function() {
        var prev = this.layout.getPrev();
        if (prev) {
            this.customDrag = true;
            this.scrollToCard(prev);
        }
        return this;
    },
    
    
    isVertical : function() {
        return this.vertical;
    },
    
    
    isHorizontal : function() {
        return this.horizontal;
    },
    
    
    beforeDestroy: function(){
        Ext.destroy(this.indicator);
        Ext.Carousel.superclass.beforeDestroy.call(this);
    }
});

Ext.reg('carousel', Ext.Carousel);


Ext.Carousel.Indicator = Ext.extend(Ext.Component, {
    baseCls: 'x-carousel-indicator',

    initComponent: function() {
        if (this.carousel.rendered) {
            this.render(this.carousel.body);
            this.onBeforeCardSwitch(null, null, this.carousel.items.indexOf(this.carousel.layout.getActiveItem()));
        }
        else {
            this.carousel.on('render', function() {
                this.render(this.carousel.body);
            }, this, {single: true});
        }
        Ext.Carousel.Indicator.superclass.initComponent.call(this);
    },

    
    onRender: function() {
        Ext.Carousel.Indicator.superclass.onRender.apply(this, arguments);

        for (var i = 0, ln = this.carousel.items.length; i < ln; i++) {
            this.createIndicator();
        }

        this.mon(this.carousel, {
            beforecardswitch: this.onBeforeCardSwitch,
            scope: this
        });

        this.mon(this.el, {
            tap: this.onTap,
            scope: this
        });
        
        this.el.addCls(this.baseCls + '-' + this.direction);
    },

    
    onTap: function(e, t) {
        var box = this.el.getPageBox(),
            centerX = box.left + (box.width / 2),
            centerY = box.top + (box.height / 2),
            carousel = this.carousel;

        if ((carousel.isHorizontal() && e.pageX > centerX) || (carousel.isVertical() && e.pageY > centerY)) {
            this.carousel.next();
        } else {
            this.carousel.prev();
        }
    },

    
    createIndicator: function() {
        this.indicators = this.indicators || [];
        this.indicators.push(this.el.createChild({
            tag: 'span'
        }));
    },

    
    onBeforeCardSwitch: function(carousel, card, old, index) {
        if (Ext.isNumber(index) && index != -1 && this.indicators[index]) {
            this.indicators[index].radioCls('x-carousel-indicator-active');
        }
    },

    
    onCardAdd: function() {
        if (this.rendered) {
            this.createIndicator();
        }
    },

    
    onCardRemove: function() {
        if (this.rendered) {
            this.indicators.pop().remove();
        }
    }
});

Ext.reg('carouselindicator', Ext.Carousel.Indicator);

Ext.Map = Ext.extend(Ext.Component, {
    
    baseCls: 'x-map',

    
    useCurrentLocation: false,
    
    monitorResize : true,

    

    
    map: null,

    
    geo: null,

    
    maskMap: false,
    
    maskMapCls: 'x-mask-map',


    initComponent : function() {
        this.mapOptions = this.mapOptions || {};
        
        this.scroll = false;
        
        
        if(!(window.google || {}).maps){
            this.html = 'Google Maps API is required';   
        }
        else if (this.useCurrentLocation) {
            this.geo = this.geo || new Ext.util.GeoLocation({autoLoad: false});
            this.geo.on({
                locationupdate : this.onGeoUpdate,
                locationerror : this.onGeoError, 
                scope : this
            });
        }
        
        Ext.Map.superclass.initComponent.call(this);
                
        this.addEvents ( 
                 
            'maprender',
        
                 
            'centerchange',
            
                 
            'typechange',
            
                 
            'zoomchange'
        );
        
        if (this.geo){
            this.on({
                activate: this.onUpdate,
                scope: this,
                single: true
            });
            this.geo.updateLocation();
        }
        
    },
    
    
    onRender : function(container, position) {
        Ext.Map.superclass.onRender.apply(this, arguments);
        this.el.setVisibilityMode(Ext.Element.OFFSETS);        
    },
    
     
    afterRender : function() {
        Ext.Map.superclass.afterRender.apply(this, arguments);
        this.renderMap();
    },
    
    
    onResize : function( w, h) {
        Ext.Map.superclass.onResize.apply(this, arguments);
        if (this.map) {
            google.maps.event.trigger(this.map, 'resize');
        }
    },
    
    afterComponentLayout : function() {
        if (this.maskMap && !this.mask) {
            this.el.mask(null, this.maskMapCls);
            this.mask = true;
        }
    },
    
    renderMap : function(){
        var me = this,
            gm = (window.google || {}).maps;
        
        if (gm) {
            if (Ext.is.iPad) {
                Ext.applyIf(me.mapOptions, {
                    navigationControlOptions: {
                        style: gm.NavigationControlStyle.ZOOM_PAN
                    }
                });
            }
                
            Ext.applyIf(me.mapOptions, {
                center: new gm.LatLng(37.381592, -122.135672), 
                zoom: 12,
                mapTypeId: gm.MapTypeId.ROADMAP
            });
            
            if (me.maskMap && !me.mask) {
                me.el.mask(null, this.maskMapCls);
                me.mask = true;
            }
    
            if (me.el && me.el.dom && me.el.dom.firstChild) {
                Ext.fly(me.el.dom.firstChild).remove();
            }
        
            if (me.map) {
                gm.event.clearInstanceListeners(me.map);
            }
            
            me.map = new gm.Map(me.el.dom, me.mapOptions);
            
            var event = gm.event;
            
            event.addListener(me.map, 'zoom_changed', Ext.createDelegate(me.onZoom, me));
            event.addListener(me.map, 'maptypeid_changed', Ext.createDelegate(me.onTypeChange, me));
            event.addListener(me.map, 'center_changed', Ext.createDelegate(me.onCenterChange, me));
            
            me.fireEvent('maprender', me, me.map);
        }
        
    },

    onGeoUpdate : function(coords) {
        var center;
        if (coords) {
            center = this.mapOptions.center = new google.maps.LatLng(coords.latitude, coords.longitude);
        }
        
        if (this.rendered) {
            this.update(center);
        }
        else {
            this.on('activate', this.onUpdate, this, {single: true, data: center});
        }
    },
    
    onGeoError : function(geo){
          
    },

    onUpdate : function(map, e, options) {
        this.update((options || {}).data);
    },
    
    
    
    update : function(coordinates) {
        var me = this, 
            gm = (window.google || {}).maps;

        if (gm) {
            coordinates = coordinates || me.coords || new gm.LatLng(37.381592, -122.135672);
            
            if (coordinates && !(coordinates instanceof gm.LatLng) && 'longitude' in coordinates) {
                coordinates = new gm.LatLng(coordinates.latitude, coordinates.longitude);
            }
            
            if (!me.hidden && me.rendered) {
                me.map || me.renderMap();
                if (me.map && coordinates instanceof gm.LatLng) {
                   me.map.panTo(coordinates);
                }
            }
            else {
                me.on('activate', me.onUpdate, me, {single: true, data: coordinates});
            }
        }
    },
    
    
    onZoom  : function() {
        this.mapOptions.zoom = (this.map && this.map.getZoom 
            ? this.map.getZoom() 
            : this.mapOptions.zoom) || 10 ;
            
        this.fireEvent('zoomchange', this, this.map, this.mapOptions.zoom);
    },
    
    
    onTypeChange  : function() {
        this.mapOptions.mapTypeId = this.map && this.map.getMapTypeId 
            ? this.map.getMapTypeId() 
            : this.mapOptions.mapTypeId;
        
        this.fireEvent('typechange', this, this.map, this.mapOptions.mapTypeId);
    },

    
    onCenterChange : function(){
       this.mapOptions.center = this.map && this.map.getCenter 
            ? this.map.getCenter() 
            : this.mapOptions.center;
        
       this.fireEvent('centerchange', this, this.map, this.mapOptions.center);
       
    },
    
    getState : function(){
        return this.mapOptions;  
    },
    
    
    onDestroy : function() {
        Ext.destroy(this.geo);
        if (this.maskMap && this.mask) {
            this.el.unmask();
        }
        if (this.map && (window.google || {}).maps) {
            google.maps.event.clearInstanceListeners(this.map);
        }
        Ext.Map.superclass.onDestroy.call(this);
    }
});

Ext.reg('map', Ext.Map);

Ext.NestedList = Ext.extend(Ext.Panel, {
    componentCls: 'x-nested-list',
    
    layout: 'card',

    

    

    
    
    

    
    cardSwitchAnimation: 'slide',

    
    backButton: null,

    
    backText: 'Back',

    
    useTitleAsBackText: true,

    
    updateTitleText: true,

    
    displayField: 'text',

    
    loadingText: 'Loading...',

    
    emptyText: 'No items available.',

    
    onItemDisclosure: false,

    
    clearSelectionDelay: 200,
    
    
    
    allowDeselect: false,

    
    getItemTextTpl: function(node) {
        return '{' + this.displayField + '}';
    },

    
    getTitleTextTpl: function(node) {
        return '{' + this.displayField + '}';
    },

    
    renderTitleText: function(node) {
        
        
        
        if (!node.titleTpl) {
            node.titleTpl = new Ext.XTemplate(this.getTitleTextTpl(node));
        }
        var record = node.getRecord();
        if (record) {
            return node.titleTpl.applyTemplate(record.data);
        } else if (node.isRoot) {
            return this.title || this.backText;
        
        } else {
            throw new Error("No RecordNode passed into renderTitleText");
        }
        
    },

    
    
    useToolbar: true,

    

    

    
    getDetailCard: function(recordNode, parentNode) {
        return false;
    },

    initComponent : function() {
        
        var store    = Ext.StoreMgr.lookup(this.store),
            rootNode = store.getRootNode(),
            title    = rootNode.getRecord() ? this.renderTitleText(rootNode) : this.title || '';

        this.store = store;

        if (this.useToolbar) {
            
            this.backButton = new Ext.Button({
                text: this.backText,
                ui: 'back',
                handler: this.onBackTap,
                scope: this,
                
                hidden: true
            });
            if (!this.toolbar || !this.toolbar.isComponent) {
                
                this.toolbar = Ext.apply({}, this.toolbar || {}, {
                    dock: 'top',
                    xtype: 'toolbar',
                    ui: 'light',
                    title: title,
                    items: []
                });
                this.toolbar.items.unshift(this.backButton);
                this.toolbar = new Ext.Toolbar(this.toolbar);

                this.dockedItems = this.dockedItems || [];
                this.dockedItems.push(this.toolbar);
            } else {
                this.toolbar.insert(0, this.backButton);
            }
        }

        this.items = [this.getSubList(rootNode)];

        Ext.NestedList.superclass.initComponent.call(this);
        this.on('itemtap', this.onItemTap, this);


        this.addEvents(
            

            

            

            

            

            
            
            'listchange',

            
            'leafitemtap'
        );
    },

    
    getListConfig: function(node) {
        var itemId = node.internalId,
            emptyText = this.emptyText;

        return {
            itemId: itemId,
            xtype: 'list',
            autoDestroy: true,
            recordNode: node,
            store: this.store.getSubStore(node),
            loadingText: this.loadingText,
            onItemDisclosure: this.onItemDisclosure,
            displayField: this.displayField,
            singleSelect: true,
            clearSelectionOnDeactivate: false,
            bubbleEvents: [
                'itemtap',
                'containertap',
                'beforeselect',
                'itemdoubletap',
                'selectionchange'
            ],
            itemTpl: '<span<tpl if="leaf == true"> class="x-list-item-leaf"</tpl>>' + this.getItemTextTpl(node) + '</span>',
            deferEmptyText: true,
            allowDeselect: this.allowDeselect,
            refresh: function() {
                if (this.hasSkippedEmptyText) {
                    this.emptyText = emptyText;
                }
                Ext.List.prototype.refresh.apply(this, arguments);
            }
        };
    },

    
    getSubList: function(node) {
        var items  = this.items,
            list,
            itemId = node.internalId;

        
        
        if (items && items.get) {
            list = items.get(itemId);
        }

        if (list) {
            return list;
        } else {
            return this.getListConfig(node);
        }
    },

    addNextCard: function(recordNode, swapTo) {
        var nextList,
            parentNode   = recordNode ? recordNode.parentNode : null,
            card;

        if (recordNode.leaf) {
            card = this.getDetailCard(recordNode, parentNode);
            if (card) {
                nextList = this.add(card);
            }
        } else {
            nextList = this.getSubList(recordNode);
            nextList = this.add(nextList);
        }
        return nextList;
    },

    setActivePath: function(path) {
        
        
        
        var gotoRoot = path.substr(0, 1) === "/",
            j        = 0,
            ds       = this.store,
            tree     = ds.tree,
            node, card, lastCard,
            pathArr, pathLn;

        if (gotoRoot) {
            path = path.substr(1);
        }

        pathArr = Ext.toArray(path.split('/'));
        pathLn  = pathArr.length;


        if (gotoRoot) {
            
            var items      = this.items,
                itemsArray = this.items.items,
                i          = items.length;

            for (; i > 1; i--) {
                this.remove(itemsArray[i - 1], true);
            }

            
            
            var rootNode = itemsArray[0].recordNode;
            if (rootNode.id !== pathArr[0]) {
                throw new Error("rootNode doesn't match!");
            }
            

            
            j = 1;
        }


        
        for (; j < pathLn; j++) {
            if (pathArr[j] !== "") {
                node = tree.getNodeById(pathArr[j]);

                
                
                
                card = this.addNextCard(node);

                
                
                if (card) {
                    lastCard = card;
                }
            }
        }

        
        if (!lastCard) {
            throw new Error("Card was not found when trying to add to NestedList.");
        }
        

        this.setActiveItem(lastCard, false);
        this.fireEvent('listchange', this, lastCard);
        this.syncToolbar();
    },

    syncToolbar: function(card) {
        var list          = card || this.getActiveItem(),
            depth         = this.items.indexOf(list),
            recordNode    = list.recordNode,
            parentNode    = recordNode ? recordNode.parentNode : null,
            backBtn       = this.backButton,
            backBtnText   = this.useTitleAsBackText && parentNode ? this.renderTitleText(parentNode) : this.backText,
            backToggleMth = (depth !== 0) ? 'show' : 'hide';


            if (backBtn) {
                backBtn[backToggleMth]();
                if (parentNode) {
                    backBtn.setText(backBtnText);
                }
            }


            if (this.toolbar && this.updateTitleText) {
                this.toolbar.setTitle(recordNode && recordNode.getRecord() ? this.renderTitleText(recordNode) : this.title || '');
                this.toolbar.doLayout();
            }
    },

    
    onItemTap: function(subList, subIdx, el, e) {
        var store        = subList.getStore(),
            record       = store.getAt(subIdx),
            recordNode   = record.node,
            parentNode   = recordNode ? recordNode.parentNode : null,
            displayField = this.displayField,
            backToggleMth,
            nextDepth,
            nextList;

        nextList = this.addNextCard(recordNode);

        if (recordNode.leaf) {
            this.fireEvent("leafitemtap", subList, subIdx, el, e, nextList);
        }

        if (nextList) {
            
            
            nextDepth = this.items.indexOf(nextList);

            this.setActiveItem(nextList, {
                type: this.cardSwitchAnimation
            });
            this.syncToolbar(nextList);
        }
    },

    
    onBackTap: function() {
        var currList      = this.getActiveItem(),
            currIdx       = this.items.indexOf(currList);

        if (currIdx != 0) {
            var prevDepth     = currIdx - 1,
                prevList      = this.items.getAt(prevDepth),
                recordNode    = prevList.recordNode,
                record        = recordNode.getRecord(),
                parentNode    = recordNode ? recordNode.parentNode : null,
                backBtn       = this.backButton,
                backToggleMth = (prevDepth !== 0) ? 'show' : 'hide',
                backBtnText;

            this.on('cardswitch', function(newCard, oldCard) {
                var selModel = prevList.getSelectionModel();
                this.remove(currList);
                if (this.clearSelectionDelay) {
                    Ext.defer(selModel.deselectAll, this.clearSelectionDelay, selModel);
                }
            }, this, {single: true});
            
            this.setActiveItem(prevList, {
                type: this.cardSwitchAnimation,
                reverse: true,
                scope: this
            });
            this.syncToolbar(prevList);
        }
    }
});
Ext.reg('nestedlist', Ext.NestedList);

Ext.Picker = Ext.extend(Ext.Sheet, {
    
    componentCls: 'x-picker',
    
    stretchX: true,
    stretchY: true,
    hideOnMaskTap: false,
    
    
    doneButton: 'Done',
    
    
    cancelButton: 'Cancel',

    
    height: 220,
    
    
    useTitles: false,

    
    

    
    
    
    
    
    defaultType: 'pickerslot',
    
    
    initComponent : function() {

        this.addEvents(
            
            'pick',

            
            'change',

            
            'cancel'
        );
            
        this.layout = {
            type: 'hbox',
            align: 'stretch'
        };

        if (this.slots) {
            this.items = this.items ? (Ext.isArray(this.items) ? this.items : [this.items]) : [];
            this.items = this.items.concat(this.slots);
        }
        
        if (this.useTitles) {
            this.defaults = Ext.applyIf(this.defaults || {}, {
                title: ''
            });            
        }

        this.on('slotpick', this.onSlotPick, this);

        if (this.doneButton || this.cancelButton) {
            var toolbarItems = [];

            if (this.cancelButton) {
                toolbarItems.push(
                    Ext.apply(
                        {
                            handler: this.onCancelButtonTap,
                            scope: this
                        },
                        ((Ext.isObject(this.cancelButton) ? this.cancelButton : { text: String(this.cancelButton) }))
                    )
                );
            }

            toolbarItems.push({xtype: 'spacer'});

            if (this.doneButton) {
                toolbarItems.push(
                    Ext.apply(
                        {
                            ui: 'action',
                            handler: this.onDoneButtonTap,
                            scope: this
                        },
                        ((Ext.isObject(this.doneButton) ? this.doneButton : { text: String(this.doneButton) }))
                    )
                );
            }

            this.toolbar = new Ext.Toolbar(Ext.applyIf(this.buttonBar || {
                dock: 'top',
                items: toolbarItems,
                defaults: {
                    xtype: 'button'
                }
            }));
           
            this.dockedItems = this.dockedItems ? (Ext.isArray(this.dockedItems) ? this.dockedItems : [this.dockedItems]) : [];
            this.dockedItems.push(this.toolbar);
        }

        Ext.Picker.superclass.initComponent.call(this);
    },

    
    afterRender: function() {
        Ext.Picker.superclass.afterRender.apply(this, arguments);

        if (this.value) {
            this.setValue(this.value, false);
        }
    },

    
    onDoneButtonTap : function() {
        var anim = this.animSheet('exit');
        Ext.apply(anim, {
            after: function() {
                this.fireEvent('change', this, this.getValue());
            },
            scope: this
        });
        this.hide(anim);
    },

    
    onCancelButtonTap : function() {
        var anim = this.animSheet('exit');
        Ext.apply(anim, {
            after: function() {
                
                this.setValue(this.values);
                this.fireEvent('cancel', this);
            },
            scope: this
        });
        this.hide(anim);
    },
    
    
    onSlotPick: function(slot, value, node) {
        this.fireEvent('pick', this, this.getValue(), slot);
        return false;
    },
    
    
    setValue: function(values, animated) {
        var slot,
            items = this.items.items,
            ln = items.length;

        
        if (!values) {
            for (var i = 0; i < ln; i++) {
                items[i].setSelectedNode(0);
            }
            
            return this;
        }

        Ext.iterate(values, function(key, value) {
            slot = this.child('[name=' + key + ']');
            
            if (slot) {
                slot.setValue(value, animated);
            }
        }, this);

        this.values = values;
       
        return this;
    },
    
    
    getValue: function() {
        var values = {},
            items = this.items.items,
            ln = items.length, item, i;

        for (i = 0; i < ln; i++) {
            item = items[i];
            values[item.name] = item.getValue();
        }

        return values;
    }
});

Ext.regModel('x-textvalue', {
    fields: ['text', 'value']
});


Ext.Picker.Slot = Ext.extend(Ext.DataView, {
    isSlot: true,
    
    flex: 1,

    
    name: null,

    
    displayField: 'text',

    
    valueField: 'value',

    
    align: 'center',
    
    
    itemSelector: 'div.x-picker-item',
    
    
    componentCls: 'x-picker-slot',
    
    
    renderTpl : [
        '<div class="x-picker-mask">',
            '<div class="x-picker-bar"></div>',
        '</div>'
    ],
    
    
    selectedIndex: 0,
    
    
    getElConfig: function() {
        return {
            tag: 'div',
            id: this.id,
            cls: 'x-picker-' + this.align
        };
    },
    
    
    initComponent : function() {
        
        if (!this.name) {
            throw new Error('Each picker slot is required to have a name.');
        }
        

        Ext.apply(this.renderSelectors, {
            mask: '.x-picker-mask',
            bar: '.x-picker-bar'
        });

        this.scroll = {
            direction: 'vertical',
            useIndicators: false,
            friction: 0.7,
            acceleration: 25,
            snapDuration: 200,
            animationDuration: 200
        };

        this.tpl = new Ext.XTemplate([
            '<tpl for=".">',
                '<div class="x-picker-item {cls} <tpl if="extra">x-picker-invalid</tpl>">{' + this.displayField + '}</div>',
            '</tpl>'
        ]);

        var data = this.data,
            parsedData = [],
            ln = data && data.length,
            i, item, obj;

        if (data && Ext.isArray(data) && ln) {
            for (i = 0; i < ln; i++) {
                item = data[i];
                obj = {};
                if (Ext.isArray(item)) {
                    obj[this.valueField] = item[0];
                    obj[this.displayField] = item[1];
                }
                else if (Ext.isString(item)) {
                    obj[this.valueField] = item;
                    obj[this.displayField] = item;
                }
                else if (Ext.isObject(item)) {
                    obj = item;
                }
                parsedData.push(obj);
            }

            this.store = new Ext.data.Store({
                model: 'x-textvalue',
                data: parsedData
            });
            
            this.tempStore = true;
        }
        else if (this.store) {
            this.store = Ext.StoreMgr.lookup(this.store);
        }

        this.enableBubble('slotpick');

        if (this.title) {
            this.title = new Ext.Component({
                dock: 'top',
                componentCls: 'x-picker-slot-title',
                html: this.title
            });
            this.dockedItems = this.title;
        }

        Ext.Picker.Slot.superclass.initComponent.call(this);

        if (this.value !== undefined) {
            this.setValue(this.value, false);
        }
    },
    
    
    setupBar: function() {
        this.el.setStyle({padding: ''});

        var padding = this.bar.getY() - this.el.getY();
        this.barHeight = this.bar.getHeight();

        this.el.setStyle({
            padding: padding + 'px 0'
        });
        this.slotPadding = padding;
        this.scroller.updateBoundary();
        this.scroller.setSnap(this.barHeight);
        this.setSelectedNode(this.selectedIndex, false);
    },
    
    
    afterComponentLayout: function() {
        
        
        Ext.defer(this.setupBar, 200, this);
    },
    
    
    initEvents: function() {
        this.mon(this.scroller, {
            scrollend: this.onScrollEnd,
            scope: this
        });
    },
    
    
    onScrollEnd: function(scroller, offset) {
        this.selectedNode = this.getNode(Math.round(offset.y / this.barHeight));
        this.selectedIndex = this.indexOf(this.selectedNode);
        this.fireEvent('slotpick', this, this.getValue(), this.selectedNode);
    },
    
    
    scrollToNode: function(node, animate) {
        var offsetsToBody = Ext.fly(node).getOffsetsTo(this.scrollEl)[1];
        this.scroller.scrollTo({
            y: offsetsToBody
        }, animate !== false ? true : false);
    },
    
    
    onItemTap: function(node) {
        Ext.Picker.Slot.superclass.onItemTap.apply(this, arguments);
        this.setSelectedNode(node);

        this.selectedNode = node;
        this.selectedIndex = this.indexOf(node);
        this.fireEvent('slotpick', this, this.getValue(), this.selectedNode);
    },
    
    
    getSelectedNode: function() {
        return this.selectedNode;
    },
    
    
    setSelectedNode: function(selected, animate) {
        
        if (Ext.isNumber(selected)) {
            selected = this.getNode(selected);
        }
        else if (selected.isModel) {
            selected = this.getNode(this.store.indexOf(selected));
        }

        
        if (selected) {
            this.selectedNode = selected;
            this.selectedIndex = this.indexOf(selected);
            this.scrollToNode(selected, animate);
        }
    },
    
    
    getValue: function() {
        var record = this.store.getAt(this.selectedIndex);
        return record ? record.get(this.valueField) : null;
    },

    
    setValue: function(value, animate) {
        var index = this.store.find(this.valueField, value);
        if (index != -1) {
            if (!this.rendered) {
                this.selectedIndex = index;
                return;
            }
            this.setSelectedNode(index, animate);
        }
    },

    onDestroy: function() {
        if (this.tempStore) {
            this.store.destroyStore();
            this.store = null;
        }
        Ext.Picker.Slot.superclass.onDestroy.call(this);
    }
});

Ext.reg('pickerslot', Ext.Picker.Slot);


Ext.DatePicker = Ext.extend(Ext.Picker, {
    
    yearFrom: 1980,

    
    yearTo: new Date().getFullYear(),

    
    monthText: 'Month',

    
    dayText: 'Day',

    
    yearText: 'Year',

    

    
    slotOrder: ['month', 'day', 'year'],

    initComponent: function() {
        var yearsFrom = this.yearFrom,
            yearsTo = this.yearTo,
            years = [],
            days = [],
            months = [],
            ln, tmp, i,
            daysInMonth;

        
        if (yearsFrom > yearsTo) {
            tmp = yearsFrom;
            yearsFrom = yearsTo;
            yearsTo = tmp;
        }

        for (i = yearsFrom; i <= yearsTo; i++) {
            years.push({
                text: i,
                value: i
            });
        }

        daysInMonth = this.getDaysInMonth(1, new Date().getFullYear());

        for (i = 0; i < daysInMonth; i++) {
            days.push({
                text: i + 1,
                value: i + 1
            });
        }

        for (i = 0, ln = Date.monthNames.length; i < ln; i++) {
            months.push({
                text: Date.monthNames[i],
                value: i + 1
            });
        }

        this.slots = [];

        this.slotOrder.forEach(function(item){
            this.slots.push(this.createSlot(item, days, months, years));
        }, this);

        Ext.DatePicker.superclass.initComponent.call(this);
    },

    afterRender: function() {
        Ext.DatePicker.superclass.afterRender.apply(this, arguments);

        this.setValue(this.value);
    },

    createSlot: function(name, days, months, years){
        switch (name) {
            case 'year':
                return {
                    name: 'year',
                    align: 'center',
                    data: years,
                    title: this.useTitles ? this.yearText : false,
                    flex: 3
                };
            case 'month':
                return {
                    name: name,
                    align: 'right',
                    data: months,
                    title: this.useTitles ? this.monthText : false,
                    flex: 4
                };
            case 'day':
                return {
                    name: 'day',
                    align: 'center',
                    data: days,
                    title: this.useTitles ? this.dayText : false,
                    flex: 2
                };
        }
    },

    
    onSlotPick: function(slot, value) {
        var name = slot.name,
            date, daysInMonth, daySlot;

        if (name === "month" || name === "year") {
            daySlot = this.child('[name=day]');
            date = this.getValue();
            daysInMonth = this.getDaysInMonth(date.getMonth()+1, date.getFullYear());
            daySlot.store.clearFilter();
            daySlot.store.filter({
                fn: function(r) {
                    return r.get('extra') === true || r.get('value') <= daysInMonth;
                }
            });
            daySlot.scroller.updateBoundary(true);
        }

        Ext.DatePicker.superclass.onSlotPick.apply(this, arguments);
    },

    
    getValue: function() {
        var value = Ext.DatePicker.superclass.getValue.call(this),
            daysInMonth = this.getDaysInMonth(value.month, value.year),
            day = Math.min(value.day, daysInMonth);

        return new Date(value.year, value.month-1, day);
    },

    
    setValue: function(value, animated) {
        if (!Ext.isDate(value) && !Ext.isObject(value)) {
            value = null;
        }

        if (Ext.isDate(value)) {
            this.value = {
                day : value.getDate(),
                year: value.getFullYear(),
                month: value.getMonth() + 1
            };
        } else {
            this.value = value;
        }

        return Ext.DatePicker.superclass.setValue.call(this, this.value, animated);
    },

    
    getDaysInMonth: function(month, year) {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        return month == 2 && this.isLeapYear(year) ? 29 : daysInMonth[month-1];
    },

    
    isLeapYear: function(year) {
        return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
    }
});

Ext.reg('datepicker', Ext.DatePicker);


Ext.Media = Ext.extend(Ext.Component, {
    

    
    url: '',

    
    enableControls: true,
    
    
    autoResume: false,

    
    autoPause: true,

    
    preload: true,

    
    playing: false,

    
    afterRender : function() {
        var cfg = this.getConfiguration();
        Ext.apply(cfg, {
            src: this.url,
            preload: this.preload ? 'auto' : 'none'
        });
        if(this.enableControls){
            cfg.controls = 'controls';
        }
        if(this.loop){
            cfg.loop = 'loop';
        }
        
        this.media = this.el.createChild(cfg);
        Ext.Media.superclass.afterRender.call(this);
        
        this.on({
            scope: this,
            activate: this.onActivate,
            beforedeactivate: this.onDeactivate
        });
    },
    
    
    onActivate: function(){
        if (this.autoResume && !this.playing) {
            this.play();
        }
    },
    
    
    onDeactivate: function(){
        if (this.autoPause && this.playing) {
            this.pause();
        }
    },

    
    play : function() {
        this.media.dom.play();
        this.playing = true;
    },

    
    pause : function() {
        this.media.dom.pause();
        this.playing = false;
    },

    
    toggle : function() {
        if(this.playing){
            this.pause();    
        }
        else {
            this.play();
        }
    }
});

Ext.reg('media', Ext.Media);

Ext.Video = Ext.extend(Ext.Media, {
    

    

    
    posterUrl: '',
    
    
    componentCls: 'x-video',

    afterRender : function() {
        Ext.Video.superclass.afterRender.call(this);
        if (this.posterUrl) {
            this.media.hide();
            this.ghost = this.el.createChild({
                cls: 'x-video-ghost',
                style: 'width: 100%; height: 100%; background: #000 url(' + this.posterUrl + ') center center no-repeat; -webkit-background-size: 100% auto;'
            });
            this.ghost.on('tap', this.onGhostTap, this, {single: true});
        }
    },
    
    onGhostTap: function(){
        this.media.show();
        this.ghost.hide();
        this.play();
    },
    
    
    getConfiguration: function(){
        return {
            tag: 'video',
            width: '100%',
            height: '100%'
        };
    }    
});

Ext.reg('video', Ext.Video);

Ext.Audio = Ext.extend(Ext.Media, {
    

    

    componentCls: 'x-audio',
    
    
    onActivate: function(){
        Ext.Audio.superclass.onActivate.call(this);
        if (Ext.is.Phone) {
            this.media.show();
        }    
    },
    
    
    onDeactivate: function(){
        Ext.Audio.superclass.onDeactivate.call(this);
        if (Ext.is.Phone) {
            this.media.hide();
        }
    },
    
    
    getConfiguration: function(){
        var hidden = !this.enableControls;
        if (!Ext.supports.AudioTag) {
            return {
                tag: 'embed',
                type: 'audio/mpeg',
                target: 'myself',
                controls: 'true',
                hidden: hidden
            };
        } else {
            return {
                tag: 'audio',
                hidden: hidden
            };
        }    
    }
});

Ext.reg('audio', Ext.Audio);

Ext.MessageBox = Ext.extend(Ext.Sheet, {
    
    centered: true,

    
    renderHidden: true,

    
    ui: 'dark',

    
    componentCls: 'x-msgbox',

    
    enterAnimation: 'pop',

    
    exitAnimation: 'pop',

    autoHeight      : true,

    
    defaultTextHeight : 75,

    constructor : function(config) {

        config = config || {};

        var ui = config.ui || this.ui || '',
            baseCls = config.componentCls || this.componentCls;

        delete config.html;

        this.titleBar = Ext.create({
            xtype : 'toolbar',
            ui    : ui,
            dock  : 'top',
            cls   : baseCls + '-title',
            title : '&#160;'
        });

        this.buttonBar = Ext.create({
            xtype : 'toolbar',
            ui    : ui,
            dock  : 'bottom',
            layout: { pack: 'center' },
            cls   : baseCls + '-buttons'
        });

        config = Ext.apply({
                    ui  : ui,
            dockedItems : [this.titleBar, this.buttonBar],
        renderSelectors : {
                           body : '.' + baseCls + '-body',
                         iconEl : '.' + baseCls + '-icon',
                   msgContentEl : '.' + baseCls + '-content',
                          msgEl : '.' + baseCls + '-text',
                       inputsEl : '.' + baseCls + '-inputs',
                        inputEl : '.' + baseCls + '-input-single',
                    multiLineEl : '.' + baseCls + '-input-textarea'
           }
         }, config || {});

        Ext.MessageBox.superclass.constructor.call(this, config);
    },

    renderTpl: [
        '<div class="{componentCls}-body"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>>',
            '<div class="{componentCls}-icon x-hidden-display"></div>',
            '<div class="{componentCls}-content">',
                '<div class="{componentCls}-text"></div>',
                '<div class="{componentCls}-inputs x-hidden-display">',
                    '<input type="text" class="{componentCls}-input {componentCls}-input-single" />',
                    '<textarea class="{componentCls}-input {componentCls}-input-textarea"></textarea>',
                '</div>',
            '</div>',
        '</div>'
    ],

    
    onClick : function(button) {
        if (button) {
            var config = button.config || {};

            if (typeof config.fn == 'function') {
                config.fn.call(
                    config.scope || null,
                    button.itemId || button.text,
                    config.input ? config.input.dom.value : null,
                    config
                );
            }

            if (config.cls) {
                    this.el.removeCls(config.cls);
                }

            if (config.input) {
                config.input.dom.blur();
            }
        }

        this.hide();
    },

    
    show : function(config) {
        var attrib,
            attrName,
            attribs = {
                autocomplete : 'off',
                autocapitalize : 'off',
                autocorrect : 'off',
                maxlength : 0,
                autofocus : true,
                placeholder : '',
                type : 'text'
            },
            assert = /true|on/i;

        this.rendered || this.render(document.body);

        config = Ext.applyIf(
            config || {}, {
                multiLine : false,
                prompt  : false,
                value   : '',
                modal   : true
            }
        );

        if (config.title) {
            this.titleBar.setTitle(config.title);
            this.titleBar.show();
        } else {
            this.titleBar.hide();
        }

        if (this.inputsEl && (config.multiLine || config.prompt)) {
            this.inputsEl.show();

            if (this.multiLineEl && config.multiLine) {
                this.inputEl && this.inputEl.hide();
                this.multiLineEl.show().setHeight(Ext.isNumber(config.multiLine) ? parseFloat(config.multiLine) : this.defaultTextHeight);
                config.input = this.multiLineEl;
            } else if (this.inputEl) {
                this.inputEl.show();
                this.multiLineEl && this.multiLineEl.hide();
                config.input = this.inputEl;
            }

            
            if (Ext.isObject(config.prompt)) {
                Ext.apply(attribs, config.prompt);
            }

            for (attrName in attribs) {
                if (attribs.hasOwnProperty(attrName)) {
                    attrib = attribs[attrName];
                    config.input.dom.setAttribute(
                        attrName.toLowerCase(),
                        /^auto/i.test(attrName) ? (assert.test(attrib+'') ? 'on' : 'off' ) : attrib
                    );
                }
            }

        } else {
            this.inputsEl && this.inputsEl.hide();
        }

        this.setIcon(config.icon || '', false);
        this.updateText(config.msg, false);

        if (config.cls) {
            this.el.addCls(config.cls);
        }

        this.modal = !!config.modal;

        var bbar = this.buttonBar,
            bs = [];

        bbar.removeAll();

        Ext.each([].concat(config.buttons || Ext.MessageBox.OK), function(button) {
            if (button) {
                bs.push(
                    Ext.applyIf({
                        config  : config,
                        scope   : this,
                        handler : this.onClick
                    }, button)
                );
            }
        }, this);

        bbar.add(bs);

        if (bbar.rendered) {
            bbar.doLayout();
        }

        Ext.MessageBox.superclass.show.call(this, config.animate);

        if (config.input) {
            config.input.dom.value = config.value || '';
            
            if (assert.test(attribs.autofocus+'') && !('autofocus' in config.input.dom)) {
                config.input.dom.focus();
            }
        }

        return this;
    },

     
    onOrientationChange : function() {
        this.doComponentLayout();

        Ext.MessageBox.superclass.onOrientationChange.apply(this, arguments);
    },

    
    adjustScale : function(){
        Ext.apply(this,{
            maxWidth : window.innerWidth,
            maxHeight : window.innerHeight,
            minWidth : window.innerWidth * .5,
            minHeight : window.innerHeight * .5
        });
    },

    
    doComponentLayout : function() {
        this.adjustScale();

        return Ext.MessageBox.superclass.doComponentLayout.apply(this, arguments);
    },

    
    alert : function(title, msg, fn, scope) {
        return this.show({
            title : title,
            msg   : msg,
            buttons: Ext.MessageBox.OK,
            fn    : fn,
            scope : scope,
            icon  : Ext.MessageBox.INFO
        });
    },

    
    confirm : function(title, msg, fn, scope) {
        return this.show({
            title : title,
            msg : msg,
            buttons: Ext.MessageBox.YESNO,
            fn: function(button) {
                fn.call(scope, button);
            },
            scope : scope,
            icon: Ext.MessageBox.QUESTION
        });
     },

    
    prompt : function(title, msg, fn, scope, multiLine, value, promptConfig) {
        return this.show({
            title : title,
            msg : msg,
            buttons: Ext.MessageBox.OKCANCEL,
            fn: function(button, inputValue) {
                fn.call(scope, button, inputValue);
            },
            scope : scope,
            icon  : Ext.MessageBox.QUESTION,
            prompt: promptConfig || true,
            multiLine: multiLine,
            value: value
        });
    },

    
    updateText : function(text, doLayout) {
        if(this.msgEl) {
            this.msgEl.update(text ? String(text) : '&#160;');
            if(doLayout !== false) {
                this.doComponentLayout();
            }
        }
        return this;
    },

    
    setIcon : function(icon, doLayout) {
        if (icon) {
            this.iconEl.show();
            this.iconEl.replaceCls(this.iconCls, icon);
        } else {
            this.iconEl.replaceCls(this.iconCls, 'x-hidden-display');
        }

        if (doLayout !== false) {
            this.doComponentLayout();
        }

        this.iconCls = icon;
        return this;
    }
});

(function(){
    var B = Ext.MessageBox;

    Ext.apply(B, {
        OK     : {text : 'OK',     itemId : 'ok',  ui : 'action' },
        CANCEL : {text : 'Cancel', itemId : 'cancel'},
        YES    : {text : 'Yes',    itemId : 'yes', ui : 'action' },
        NO     : {text : 'No',     itemId : 'no'},
        

        
        INFO     : 'x-msgbox-info',
        WARNING  : 'x-msgbox-warning',
        QUESTION : 'x-msgbox-question',
        ERROR    : 'x-msgbox-error'
    });

    Ext.apply(B, {
        OKCANCEL    : [B.CANCEL, B.OK],
        YESNOCANCEL : [B.CANCEL, B.NO, B.YES],
        YESNO       : [B.NO, B.YES]
        
    });

})();

Ext.reg('messagebox', Ext.MessageBox);


Ext.reg('msgbox', Ext.MessageBox);


Ext.Msg = new Ext.MessageBox();

Ext.form.FormPanel = Ext.extend(Ext.Panel, {
    
    standardSubmit: false,

    componentCls: 'x-form',
    
    
    url: undefined,
    
    
    baseParams : undefined,
    
    
    waitTpl: new Ext.XTemplate(
        '<div class="{cls}">{message}&hellip;</div>'
    ),
    
    
    submitOnAction : true,    

    getElConfig: function() {
        return Ext.apply(Ext.form.FormPanel.superclass.getElConfig.call(this), {
            tag: 'form'
        });
    },
    
    
    initComponent : function() {
        this.addEvents(
           
            'submit', 
           
             'beforesubmit', 
           
             'exception'
        );

        Ext.form.FormPanel.superclass.initComponent.call(this);
        
        this.on('action', this.onFieldAction, this);
    },
    
    
    afterRender : function() {
        Ext.form.FormPanel.superclass.afterRender.call(this);
        this.el.on('submit', this.onSubmit, this);
    },

    
    onSubmit : function(e, t) {
        if (!this.standardSubmit || this.fireEvent('beforesubmit', this, this.getValues(true)) === false) {
            if (e) {
                e.stopEvent();
            }       
        }
    },
    
    
    onFieldAction : function(field) {
        if (this.submitOnAction) {
            field.blur();
            this.submit();
        }
    },
    
    

    submit: function(options) {
        var form = this.el.dom || {},
            formValues

            options = Ext.apply({
               url : this.url || form.action,
               submitDisabled : false,
               method : form.method || 'post',
               autoAbort : false,
               params : null,
               waitMsg : null,
               headers : null,
               success : null,
               failure : null
            }, options || {});

            formValues = this.getValues(this.standardSubmit || !options.submitDisabled);
        
        if (this.standardSubmit) {
            if (form) {
                if (options.url && Ext.isEmpty(form.action)) {
                    form.action = options.url;
                }

                form.method = (options.method || form.method).toLowerCase();

                if (this.fireEvent('beforesubmit', this, formValues, options) !== false) {
                    form.submit();
                }
            }
            return null;
        }
        
        if (this.fireEvent('beforesubmit', this, formValues, options ) !== false) {
            if (options.waitMsg) {
                this.showMask(options.waitMsg);
            }
            
            return Ext.Ajax.request({
                url     : options.url,
                method  : options.method,
                rawData : Ext.urlEncode(Ext.apply(
                    Ext.apply({}, this.baseParams || {}),
                    options.params || {},
                    formValues
                )),
                autoAbort : options.autoAbort,
                headers  : Ext.apply(
                   {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
                    options.headers || {}),
                scope    : this,
                callback : function(callbackOptions, success, response) {
                    var responseText = response.responseText;
                    this.hideMask();
                        
                    if (success) {

                        response = Ext.decode(responseText);
                        success = !!response.success;

                        if (success) {
                            if (Ext.isFunction(options.success)) {
                                options.success.call(options.scope || this, this, response, responseText);
                            }

                            this.fireEvent('submit', this, response);
                            return;
                        }
                    }

                    if (Ext.isFunction(options.failure)) {
                        options.failure.call(options.scope || this, this, response, responseText);
                    }
                    
                    this.fireEvent('exception', this, response);
                }
            });
        }
    },

    
    loadRecord: function(instance) {
        if (instance && instance.data) {
            this.setValues(instance.data);
            
            
            this.record = instance;
        }
        
        return this;
    },
    
    
    loadModel: function() {
        return this.loadRecord.apply(this, arguments);
    },
    
    
    getRecord: function() {
        return this.record;
    },
    
    
    updateRecord: function(instance, enabled) {
        var fields, values, name;
        
        if(instance && (fields = instance.fields)){
            values = this.getValues(enabled);
            for (name in values) {
                if(values.hasOwnProperty(name) && fields.containsKey(name)){
                   instance.set(name, values[name]);     
                }
            }
        }
        return this;
    },
    

    
    setValues: function(values) {
         var fields = this.getFields(),
             name,
             field,
             value;
             
        values = values || {};
        
        for (name in values) {
            if (values.hasOwnProperty(name)) {
                field = fields[name];
                value = values[name];
                if (field) {
                    if (Ext.isArray(field)) {
                        field.forEach(function(field){
                            if (Ext.isArray(values[name])) {
                                field.setChecked((value.indexOf(field.getValue()) != -1));
                            } else {
                                field.setChecked((value == field.getValue()));
                            }
                        });
                    } else {  
                        if (field.setChecked) {
                            field.setChecked(value);
                        } else {
                            field.setValue(value);
                        }
                    }
                }
            }       
        }
        
        return this;
    },

    
    getValues: function(enabled) {
        var fields = this.getFields(),
            field,
            values = {},
            name;

        for (name in fields) {
            if (fields.hasOwnProperty(name)) {
                if (Ext.isArray(fields[name])) {
                    values[name] = [];

                    fields[name].forEach(function(field) {
                        if (field.isChecked() && !(enabled && field.disabled)) {
                            if (field instanceof Ext.form.Radio) {
                                values[name] = field.getValue();
                            } else {
                                values[name].push(field.getValue());
                            }
                        }
                    });
                } else {
                    field = fields[name];
                    
                    if (!(enabled && field.disabled)) {
                        if (field instanceof Ext.form.Checkbox) {
                            values[name] = (field.isChecked()) ? field.getValue() : null;
                        } else {
                            values[name] = field.getValue();
                        }
                    }
                }
            }
        }

        return values;
    },

    
    reset: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.reset();
        });

        return this;
    },

    
    enable: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.enable();
        });

        return this;
    },

    
    disable: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.disable();
        });

        return this;
    },

    getFieldsAsArray: function() {
        var fields = [];

        var getFieldsFrom = function(item) {
            if (item.isField) {
                fields.push(item);
            }

            if (item.isContainer) {
                item.items.each(getFieldsFrom);
            }
        };

        this.items.each(getFieldsFrom);

        return fields;
    },

    
    getFields: function(byName) {
        var fields = {},
            itemName;

        var getFieldsFrom = function(item) {
            if (item.isField) {
                itemName = item.getName();

                if ((byName && itemName == byName) || typeof byName == 'undefined') {
                    if (fields.hasOwnProperty(itemName)) {
                        if (!Ext.isArray(fields[itemName])) {
                            fields[itemName] = [fields[itemName]];
                        }

                        fields[itemName].push(item);
                    } else {
                        fields[itemName] = item;
                    }
                }

            }

            if (item.isContainer) {
                item.items.each(getFieldsFrom);
            }
        };

        this.items.each(getFieldsFrom);
        
        return (byName) ? (fields[byName] || []) : fields;
    },

    getFieldsFromItem: function() {

    },
    
    showMask : function(cfg, target) {
        cfg = Ext.isString(cfg) ? {message : cfg} : cfg; 
        
        if (cfg && this.waitTpl) {
            this.maskTarget = target = Ext.get(target || cfg.target) || this.el;
            target && target.mask(this.waitTpl.apply(cfg));
        }
        return this;
    },
    
    
    hideMask : function(){
        if(this.maskTarget){
            this.maskTarget.unmask();
            delete this.maskTarget;
        }
        return this;
    }
});


Ext.form.FormPanel.prototype.load = Ext.form.FormPanel.prototype.loadModel; 

Ext.reg('formpanel', Ext.form.FormPanel);


Ext.reg('form', Ext.form.FormPanel);


Ext.form.FieldSet = Ext.extend(Ext.Panel, {
    componentCls: 'x-form-fieldset',

    
    initComponent : function() {
        this.componentLayout = this.getLayout();
        Ext.form.FieldSet.superclass.initComponent.call(this);
    },
    

    

    

    
    afterLayout : function(layout) {
        Ext.form.FieldSet.superclass.afterLayout.call(this, layout);
        
        if (this.title && !this.titleEl) {
            this.setTitle(this.title);
        } else if (this.titleEl) {
            this.el.insertFirst(this.titleEl);
        }

        if (this.instructions && !this.instructionsEl) {
            this.setInstructions(this.instructions);
        } else if (this.instructionsEl) {
            this.el.appendChild(this.instructionsEl);
        }
    },
    
    
    setTitle: function(title){
        if (this.rendered) {
            if (!this.titleEl) {
                this.titleEl = this.el.insertFirst({
                    cls: this.componentCls + '-title'
                });
            }
            this.titleEl.setHTML(title);
        } else {
            this.title = title;
        }
        return this;
    },
    
    
    setInstructions: function(instructions){
        if (this.rendered) {
            if (!this.instructionsEl) {
                this.instructionsEl = this.el.createChild({
                    cls: this.componentCls + '-instructions'
                });
            }
            this.instructionsEl.setHTML(instructions);
        } else {
            this.instructions = instructions;
        }
        return this;
    }
});

Ext.reg('fieldset', Ext.form.FieldSet);

Ext.form.Field = Ext.extend(Ext.Component,  {
    
    isField: true,

    

    

    

    

    

    
    fieldCls: 'x-form-field',

    baseCls: 'x-field',

    
    inputCls: undefined,

    
    disabled: false,

    renderTpl: [
        '<tpl if="label">',
            '<div class="x-form-label"><span>{label}</span></div>',
        '</tpl>',
        '<tpl if="fieldEl">',
            '<div class="x-form-field-container"><input id="{inputId}" type="{inputType}" name="{name}" class="{fieldCls}"',
                '<tpl if="tabIndex">tabIndex="{tabIndex}" </tpl>',
                '<tpl if="placeHolder">placeholder="{placeHolder}" </tpl>',
                '<tpl if="style">style="{style}" </tpl>',
                '<tpl if="maxlength">maxlength="{maxlength}" </tpl>',
                '<tpl if="autoComplete">autocomplete="{autoComplete}" </tpl>',
                '<tpl if="autoCapitalize">autocapitalize="{autoCapitalize}" </tpl>',
                '<tpl if="autoCorrect">autocorrect="{autoCorrect}" </tpl> />',
            '<tpl if="useMask"><div class="x-field-mask"></div></tpl>',
            '</div>',
            '<tpl if="useClearIcon"><div class="x-field-clear-container"><div class="x-field-clear x-hidden-visibility">&#215;</div></div></tpl>',
        '</tpl>'
    ],

    
    isFormField: true,

    
    autoCreateField: true,

    
    inputType: 'text',
    
    
    label: null,

    
    labelWidth: '30%',

    
    labelAlign: 'left',

    
    required: false,

    
    useMask: false,

    
    initComponent: function() {

        Ext.form.Field.superclass.initComponent.call(this);
    },

    
    getName: function() {
        return this.name || this.id || '';
    },

    
    applyRenderSelectors: function() {
        this.renderSelectors = Ext.applyIf(this.renderSelectors || {}, {
            mask: '.x-field-mask',
            labelEl: '.x-form-label',
            fieldEl: '.' + Ext.util.Format.trim(this.renderData.fieldCls).replace(/ /g, '.')
        });

        Ext.form.Field.superclass.applyRenderSelectors.call(this);
    },

    
    initRenderData: function() {
        Ext.form.Field.superclass.initRenderData.apply(this, arguments);
        
        Ext.applyIf(this.renderData, {
            disabled        :   this.disabled,
            fieldCls        :   'x-input-' + this.inputType + (this.inputCls ? ' ' + this.inputCls: ''),
            fieldEl         :   !this.fieldEl && this.autoCreateField,
            inputId         :   Ext.id(),
            label           :    this.label,
            labelAlign      :   'x-label-align-' + this.labelAlign,
            name            :   this.getName(),
            required        :   this.required,
            style           :   this.style,
            tabIndex        :   this.tabIndex,
            inputType       :   this.inputType,
            useMask         :   this.useMask
        });
        
        return this.renderData;
    },

    
    initEvents: function() {
        Ext.form.Field.superclass.initEvents.apply(this, arguments);
        
        if (this.fieldEl) {
            if (this.useMask && this.mask) {
                this.mon(this.mask, {
                    click: this.onMaskTap,
                    scope: this
                });
            }
        }
    },

    
    onRender: function() {
        Ext.form.Field.superclass.onRender.apply(this, arguments);
        
        var cls = [];
        
        if (this.required) {
            cls.push('x-field-required');
        }
        if (this.label) {
            cls.push('x-label-align-' + this.labelAlign);
        }

        this.el.addCls(cls);
    },

    
    afterRender: function() {
        Ext.form.Field.superclass.afterRender.apply(this, arguments);

        if (this.label) {
            this.setLabelWidth(this.labelWidth);
        }

        this.initValue();
    },

    isDisabled: function() {
        return this.disabled;
    },

    
    onEnable: function() {
        this.fieldEl.dom.disabled = false;
    },

    
    onDisable: function() {
        this.fieldEl.dom.disabled = true;
    },

    
    initValue: function() {
        this.setValue(this.value || '', true);

        
        this.originalValue = this.getValue();
    },

    
    isDirty: function() {
        if (this.disabled || !this.rendered) {
            return false;
        }
        
        return String(this.getValue()) !== String(this.originalValue);
    },

    
    onMaskTap: function(e) {
        if (this.disabled) {
            return false;
        }








        return true;
    },

    
    showMask: function(e) {
        if (this.mask) {
            this.mask.setStyle('display', 'block');
        }
    },

    hideMask: function(e) {
        if (this.mask) {
            this.mask.setStyle('display', 'none');
        }
    },
    
    
    reset: function() {
        this.setValue(this.originalValue);
    },

    
    getValue: function(){
        if (!this.rendered || !this.fieldEl) {
            return this.value;
        }
        
        return this.fieldEl.getValue();
    },

    
    setValue: function(value){
        this.value = value;

        if (this.rendered && this.fieldEl) {
            this.fieldEl.dom.value = (Ext.isEmpty(value) ? '' : value);
        }

        return this;
    },

    
    setLabelWidth: function(width) {
        if (this.labelEl) {
            this.labelEl.setWidth(width);
        }

        return this;
    }
});

Ext.reg('field', Ext.form.Field);


Ext.form.Slider = Ext.extend(Ext.form.Field, {
    ui: 'slider',
    

    
    inputCls: 'x-slider',

    inputType: 'slider',

    
    minValue: 0,

    
    maxValue: 100,

    
    animationDuration: 200,

    
    value: 0,

    
    trackWidth: null,

    monitorOrientation: true,

    renderTpl: [
        '<tpl if="label">',
            '<div class="x-form-label"><span>{label}</span></div>',
        '</tpl>',
        '<tpl if="fieldEl">',
            '<div id="{inputId}" name="{name}" class="{fieldCls}"',
            '<tpl if="tabIndex">tabIndex="{tabIndex}"</tpl>',
            '<tpl if="style">style="{style}" </tpl>',
        '/></tpl>'
    ],

    
    increment: 1,


    

    

    
    constructor: function(config) {
        this.addEvents(
            
            'beforechange',

            
            'change',
            
            'drag',
            
            'dragend'
        );

        Ext.form.Slider.superclass.constructor.call(this, config);
    },

    
    initComponent: function() {
        this.tabIndex = -1;

        if (this.increment == 0) {
            this.increment = 1;
        }

        this.increment = Math.abs(this.increment);

        
        
        this.values = [this.value];

        Ext.form.Slider.superclass.initComponent.apply(this, arguments);

        if (this.thumbs == undefined) {
            var thumbs = [],
                values = this.values,
                length = values.length,
                i,
                Thumb = this.getThumbClass();

            for (i = 0; i < length; i++) {
                thumbs[thumbs.length] = new Thumb({
                    value: values[i],
                    slider: this,

                    listeners: {
                        scope  : this,
                        drag   : this.onDrag,
                        dragend: this.onThumbDragEnd
                    }
                });
            }

            this.thumbs = thumbs;
        }
    },

    
    initValue: function() {
        var thumb = this.getThumb();

        if (thumb.dragObj) {
            thumb.dragObj.updateBoundary();
        }

        Ext.form.Slider.superclass.initValue.apply(this, arguments);
    },

    onOrientationChange: function() {
        Ext.form.Slider.superclass.onOrientationChange.apply(this, arguments);

        var me = this,
            thumb = this.getThumb();

        if (thumb.dragObj) {
            setTimeout(function() {
                thumb.dragObj.updateBoundary();
                me.moveThumb(thumb, me.getPixelValue(thumb.getValue(), thumb), 0);
            }, 100);
        }
    },

    getThumbClass: function() {
        return Ext.form.Slider.Thumb;
    },

    
    setValue: function(value, animationDuration, moveThumb) {
        if (typeof moveThumb == 'undefined') {
            moveThumb = true;
        }

        moveThumb = !!moveThumb;

        
        var thumb    = this.getThumb(),
            oldValue = thumb.getValue(),
            newValue = this.constrain(value);

        if (this.fireEvent('beforechange', this, thumb, newValue, oldValue) !== false) {
            if (moveThumb) {
                this.moveThumb(thumb, this.getPixelValue(newValue, thumb), animationDuration);
            }

            thumb.setValue(newValue);
            this.doComponentLayout();

            this.fireEvent('change', this, thumb, newValue, oldValue);
        }

        return this;
    },

    
    constrain: function(value) {
        var remainder = value % this.increment;

        value -= remainder;

        if (Math.abs(remainder) >= (this.increment / 2)) {
            value += (remainder > 0) ? this.increment : -this.increment;
        }

        value = Math.max(this.minValue, value);
        value = Math.min(this.maxValue, value);

        return value;
    },

    
    getValue: function() {
        
        return this.getThumb().getValue();
    },

    
    getThumb: function() {
        
        
        return this.thumbs[0];
    },

    
    getSliderValue: function(pixelValue, thumb) {
        var trackWidth = thumb.dragObj.offsetBoundary.right,
            range = this.maxValue - this.minValue,
            ratio;

        this.trackWidth = (trackWidth > 0) ? trackWidth : this.trackWidth;
        ratio = range / this.trackWidth;

        return this.minValue + (ratio * (pixelValue));
    },

    
    getPixelValue: function(value, thumb) {
        var trackWidth = thumb.dragObj.offsetBoundary.right,
            range = this.maxValue - this.minValue,
            ratio;

        this.trackWidth = (trackWidth > 0) ? trackWidth : this.trackWidth;
        ratio = this.trackWidth / range;

        return (ratio * (value - this.minValue));
    },

    
    renderThumbs: function() {
        var thumbs = this.thumbs,
            length = thumbs.length,
            i;

        for (i = 0; i < length; i++) {
            thumbs[i].render(this.fieldEl);
        }
    },

    
    onThumbDragEnd: function(draggable) {
        var value = this.getThumbValue(draggable);

        this.setValue(value);
        this.fireEvent('dragend', this, draggable.thumb, this.constrain(value));
    },

    
    getThumbValue: function(draggable) {
        var thumb = draggable.thumb;

        return this.getSliderValue(-draggable.getOffset().x, thumb);
    },

    
    onDrag: function(draggable){
        var value = this.getThumbValue(draggable);
        this.fireEvent('drag', this, draggable.thumb, this.constrain(value));
    },

    
    onTap: function(e) {
        if (!this.disabled) {
            var sliderBox = this.fieldEl.getPageBox(),
                leftOffset = e.pageX - sliderBox.left,
                thumb = this.getNearest(leftOffset),
                halfThumbWidth = thumb.dragObj.size.width / 2;

            this.setValue(this.getSliderValue(leftOffset - halfThumbWidth, thumb), this.animationDuration, true);
        }
    },

    
    moveThumb: function(thumb, pixel, animationDuration) {
        thumb.dragObj.setOffset(new Ext.util.Offset(pixel, 0), animationDuration);
    },

    
    afterRender: function(ct) {
        var me = this;

        me.renderThumbs();

        Ext.form.Slider.superclass.afterRender.apply(me, arguments);

        me.fieldEl.on({
            scope: me,
            tap  : me.onTap
        });
    },

    
    getNearest: function(value) {
        
        return this.thumbs[0];
    },

    
    setThumbsDisabled: function(disable) {
        var thumbs = this.thumbs,
            ln     = thumbs.length,
            i      = 0;

        for (; i < ln; i++) {
            thumbs[i].dragObj[disable ? 'disable' : 'enable']();
        }
    },

    
    disable: function() {
        Ext.form.Slider.superclass.disable.call(this);
        this.setThumbsDisabled(true);
    },

    
    enable: function() {
        Ext.form.Slider.superclass.enable.call(this);
        this.setThumbsDisabled(false);
    }
});

Ext.reg('sliderfield', Ext.form.Slider);


Ext.form.Slider.Thumb = Ext.extend(Ext.form.Field, {
    isField: false,
    baseCls: 'x-thumb',
    autoCreateField: false,
    draggable: true,

    
    value: 0,

    

    
    onRender: function() {
        this.draggable = {
            direction: 'horizontal',
            constrain: this.slider.fieldEl,
            revert: false,
            thumb: this
        };

        Ext.form.Slider.Thumb.superclass.onRender.apply(this, arguments);
    },

    
    setValue: function(newValue) {
        this.value = newValue;

        return this;
    },

    
    getValue: function() {
        return this.value;
    }
});

Ext.reg('sliderthumb', Ext.form.Slider.Thumb);



Ext.form.Toggle = Ext.extend(Ext.form.Slider, {
    minValue: 0,

    maxValue: 1,

    ui: 'toggle',
    
    inputType: 'toggle',

    

    
    minValueCls: 'x-toggle-off',

    
    maxValueCls: 'x-toggle-on',

    
    animationDuration: 70,
    
    
    toggle: function() {
        var thumb = this.thumbs[0],
            value = thumb.getValue();

        this.setValue(value == this.minValue ? this.maxValue : this.minValue, this.animationDuration);
    },

    
    setValue: function(value) {
        Ext.form.Toggle.superclass.setValue.call(this, value, this.animationDuration);

        var fieldEl = this.fieldEl;
        
        if (this.constrain(value) === this.minValue) {
            fieldEl.addCls(this.minValueCls);
            fieldEl.removeCls(this.maxValueCls);
        }
        else {
            fieldEl.addCls(this.maxValueCls);
            fieldEl.removeCls(this.minValueCls);
        }
    },

    
    onTap: function() {
        if (!this.disabled) {
            this.toggle();
        }
    },

    getThumbClass: function() {
        return Ext.form.Toggle.Thumb;
    }
});

Ext.reg('togglefield', Ext.form.Toggle);



Ext.form.Toggle.Thumb = Ext.extend(Ext.form.Slider.Thumb, {
    onRender: function() {
        Ext.form.Toggle.Thumb.superclass.onRender.apply(this, arguments);
        Ext.DomHelper.append(this.el, [{
            cls: 'x-toggle-thumb-off',
            html: '<span>OFF</span>'
        },{
            cls: 'x-toggle-thumb-on',
            html: '<span>ON</span>'
        },{
            cls: 'x-toggle-thumb-thumb'
        }]);
    }
});

Ext.form.Text = Ext.extend(Ext.form.Field, {
    ui: 'text',

    
    focusCls: 'x-field-focus',

    
    maxLength: 0,

    
    placeHolder: undefined,

    
    autoComplete: undefined,

    
    autoCapitalize: undefined,

    
    autoCorrect: undefined,

    

    
    isFocused: false,

    
    isClearIconVisible: false,

    useMask: Ext.is.iOS,

    initComponent: function() {
        this.addEvents(
            
            'focus',
            
            'blur',
            
            'keyup',
            
            'change',
            
            'action'
        );
        
        this.enableBubble('action');

        Ext.form.Text.superclass.initComponent.apply(this, arguments);
    },

    applyRenderSelectors: function() {
        this.renderSelectors = Ext.applyIf(this.renderSelectors || {}, {
            clearIconEl: '.x-field-clear',
            clearIconContainerEl: '.x-field-clear-container'
        });
        
        Ext.form.Text.superclass.applyRenderSelectors.call(this);
    },

    initRenderData: function() {
        var renderData     = Ext.form.Text.superclass.initRenderData.call(this),
            autoComplete   = this.autoComplete,
            autoCapitalize = this.autoCapitalize,
            autoCorrect    = this.autoCorrect;

        Ext.applyIf(renderData, {
            placeHolder : this.placeHolder,
            maxlength   : this.maxLength,
            useClearIcon   : this.useClearIcon
        });

        var testArray = [true, 'on'];

        if (autoComplete !== undefined) {
            renderData.autoComplete = (testArray.indexOf(autoComplete) !== -1)  ? 'on': 'off';
        }

        if (autoCapitalize !== undefined) {
            renderData.autoCapitalize = (testArray.indexOf(autoCapitalize) !== -1) ? 'on': 'off';
        }

        if (autoCorrect !== undefined) {
            renderData.autoCorrect = (testArray.indexOf(autoCorrect) !== -1) ? 'on': 'off';
        }

        this.renderData = renderData;
        
        return renderData;
    },

    initEvents: function() {
        Ext.form.Text.superclass.initEvents.call(this);

        if (this.fieldEl) {
            this.mon(this.fieldEl, {
                focus: this.onFocus,
                blur: this.onBlur,
                keyup: this.onKeyUp,
                paste: this.updateClearIconVisibility,
                mousedown: this.onBeforeFocus,
                scope: this
            });

            if (this.clearIconEl){
                this.mon(this.clearIconContainerEl, {
                    scope: this,
                    tap: this.onClearIconTap
                });
            }
        }
    },

    
    onEnable: function() {
        Ext.form.Text.superclass.onEnable.apply(this, arguments);

        this.disabled = false;
        
        this.updateClearIconVisibility();
    },

    
    onDisable: function() {
        Ext.form.Text.superclass.onDisable.apply(this, arguments);

        this.blur();
        
        this.hideClearIcon();
    },

    
    onClearIconTap: function() {
        if (!this.disabled) {
            this.setValue('');
        }
    },

    
    updateClearIconVisibility: function() {
        var value = this.getValue();

        if (!value) {
            value = '';
        }
        
        if (value.length < 1){
            this.hideClearIcon();
        }
        else {
            this.showClearIcon();
        }

        return this;
    },

    
    showClearIcon: function() {
        if (!this.disabled && this.fieldEl && this.clearIconEl && !this.isClearIconVisible) {
            this.isClearIconVisible = true;
            this.fieldEl.addCls('x-field-clearable');
            this.clearIconEl.removeCls('x-hidden-visibility');
        }

        return this;
    },

    
    hideClearIcon: function() {
        if (this.fieldEl && this.clearIconEl && this.isClearIconVisible) {
            this.isClearIconVisible = false;
            this.fieldEl.removeCls('x-field-clearable');
            this.clearIconEl.addCls('x-hidden-visibility');
        }

        return this;
    },

    
    afterRender: function() {
        Ext.form.Text.superclass.afterRender.call(this);
        this.updateClearIconVisibility();
    },
    
    onBeforeFocus: function(e) {
        this.fireEvent('beforefocus', e);
    },

    
    beforeFocus: Ext.emptyFn,

    
    onMaskTap: function(e) {
        if (Ext.form.Text.superclass.onMaskTap.apply(this, arguments) !== true) {
            return false;
        }
        
        this.maskCorrectionTimer = Ext.defer(this.showMask, 1000, this);
        this.hideMask();
    },

    
    onFocus: function(e) {
        if (this.mask) {
            if (this.maskCorrectionTimer) {
                clearTimeout(this.maskCorrectionTimer);
            }
            
            this.hideMask();
        }

        this.beforeFocus();

        if (this.focusCls) {
            this.el.addCls(this.focusCls);
        }

        if (!this.isFocused) {
            this.isFocused = true;
            
            this.startValue = this.getValue();
            this.fireEvent('focus', this, e);
        }

        Ext.currentlyFocusedField = this;
    },

    
    beforeBlur: Ext.emptyFn,

    
    onBlur: function(e) {
        this.beforeBlur();

        if (this.focusCls) {
            this.el.removeCls(this.focusCls);
        }

        this.isFocused = false;

        var value = this.getValue();

        if (String(value) != String(this.startValue)){
            this.fireEvent('change', this, value, this.startValue);
        }

        this.fireEvent('blur', this, e);

        this.updateClearIconVisibility();

        this.showMask();

        this.afterBlur();

        Ext.currentlyFocusedField = null;
    },

    
    afterBlur: Ext.emptyFn,

    
    focus: function(){
        if (this.rendered && this.fieldEl && this.fieldEl.dom.focus) {
            this.fieldEl.dom.focus();
        }

        return this;
    },

    
    blur: function(){
        if(this.rendered && this.fieldEl && this.fieldEl.dom.blur) {
            this.fieldEl.dom.blur();
        }
        return this;
    },

    
    setValue: function() {
        Ext.form.Text.superclass.setValue.apply(this, arguments);

        this.updateClearIconVisibility();
    },

    
    onKeyUp: function(e) {
        this.updateClearIconVisibility();
        
        this.fireEvent('keyup', this, e);

        if (e.browserEvent.keyCode === 13) {
            this.blur();
            this.fireEvent('action', this, e);
        }
    }
});

Ext.reg('textfield', Ext.form.Text);


Ext.form.TextField = Ext.extend(Ext.form.Text, {
    constructor: function() {
        console.warn("Ext.form.TextField has been deprecated and will be removed in Sencha Touch 1.0. Please use Ext.form.Text instead");
        Ext.form.TextField.superclass.constructor.apply(this, arguments);
    }
});


Ext.form.Password = Ext.extend(Ext.form.Text, {
    inputType: 'password',
    autoCapitalize : false
});

Ext.reg('passwordfield', Ext.form.Password);


Ext.form.Email = Ext.extend(Ext.form.Text, {
    inputType: 'email',
    
    autoCapitalize: false
});

Ext.reg('emailfield', Ext.form.Email);


Ext.form.Url = Ext.extend(Ext.form.Text, {
    inputType: 'url',
    
    autoCapitalize: false
});

Ext.reg('urlfield', Ext.form.Url);


Ext.form.Search = Ext.extend(Ext.form.Text, {
    inputType: 'search'
    
});

Ext.reg('searchfield', Ext.form.Search);


Ext.form.Number = Ext.extend(Ext.form.Text, {
    ui: 'number',

    inputType: 'number',
    
    
    minValue : undefined,
    
    
    maxValue : undefined,
    
    
    stepValue : undefined,

    renderTpl: [
        '<tpl if="label"><div class="x-form-label"><span>{label}</span></div></tpl>',
        '<tpl if="fieldEl"><div class="x-form-field-container">',
            '<input id="{inputId}" type="{inputType}" name="{name}" class="{fieldCls}"',
                '<tpl if="tabIndex">tabIndex="{tabIndex}" </tpl>',
                '<tpl if="placeHolder">placeholder="{placeHolder}" </tpl>',
                '<tpl if="style">style="{style}" </tpl>',
                '<tpl if="minValue != undefined">min="{minValue}" </tpl>',
                '<tpl if="maxValue != undefined">max="{maxValue}" </tpl>',
                '<tpl if="stepValue != undefined">step="{stepValue}" </tpl>',
                '<tpl if="autoComplete">autocomplete="{autoComplete}" </tpl>',
                '<tpl if="autoCapitalize">autocapitalize="{autoCapitalize}" </tpl>',
                '<tpl if="autoFocus">autofocus="{autoFocus}" </tpl>',
            '/>',
            '<tpl if="useMask"><div class="x-field-mask"></div></tpl>',
            '</div></tpl>',
        '<tpl if="useClearIcon"><div class="x-field-clear-container"><div class="x-field-clear x-hidden-visibility">&#215;</div><div></tpl>'
    ],
    
    
    onRender : function() {
        Ext.apply(this.renderData, {
            maxValue : this.maxValue,
            minValue : this.minValue,
            stepValue : this.stepValue 
        });
        
        Ext.form.Number.superclass.onRender.apply(this, arguments);
    }
});

Ext.reg('numberfield', Ext.form.Number);



Ext.form.Spinner = Ext.extend(Ext.form.Number, {

    
    componentCls: 'x-spinner',
    
    
    minValue: Number.NEGATIVE_INFINITY,
    
    maxValue: Number.MAX_VALUE,
    
    incrementValue: 1,
    
    accelerateOnTapHold: true,

    
    defaultValue: 0,

    
    cycle: false,
    
    
    disableInput: false,

    
    useClearIcon: false,

    
    autoCapitalize: false,

    renderTpl: [
        '<tpl if="label"><div class="x-form-label"><span>{label}</span></div></tpl>',
        '<tpl if="fieldEl">',
            '<div class="{componentCls}-body">',
                '<div class="{componentCls}-down"><span>-</span></div>',
                '<div class="x-form-field-container">',
                    '<input id="{inputId}" type="{type}" name="{name}" class="{fieldCls}"',
                        '<tpl if="tabIndex">tabIndex="{tabIndex}" </tpl>',
                        '<tpl if="placeHolder">placeholder="{placeHolder}" </tpl>',
                        '<tpl if="style">style="{style}" </tpl>',
                        '<tpl if="minValue != undefined">min="{minValue}" </tpl>',
                        '<tpl if="maxValue != undefined">max="{maxValue}" </tpl>',
                        '<tpl if="stepValue != undefined">step="{stepValue}" </tpl>',
                        '<tpl if="autoComplete">autocomplete="{autoComplete}" </tpl>',
                        '<tpl if="autoFocus">autofocus="{autoFocus}" </tpl>',
                    '/>',
                    '<tpl if="useMask"><div class="x-field-mask"></div></tpl>',
                '</div>',
                '<div class="{componentCls}-up"><span>+</span></div>',
            '</div>',
        '</tpl>'
    ],
    
    initComponent: function() {
        
        this.addEvents(
            
            'spin',
            
            'spindown',
            
            'spinup'
        );

        Ext.form.Spinner.superclass.initComponent.call(this);    
    },

    
    onRender: function() {
        this.renderData.disableInput = this.disableInput;

        Ext.applyIf(this.renderSelectors, {
            spinUpEl: '.x-spinner-up',
            spinDownEl: '.x-spinner-down'
        });

        Ext.form.Spinner.superclass.onRender.apply(this, arguments);
        
        this.downRepeater = this.createRepeater(this.spinDownEl, this.onSpinDown);
        this.upRepeater = this.createRepeater(this.spinUpEl, this.onSpinUp);
    },

    initValue: function() {
        if (isNaN(this.defaultValue)) {
            this.defaultValue = 0;
        }

        if (!this.value) {
            this.value = this.defaultValue;
        }

        Ext.form.Spinner.superclass.initValue.apply(this, arguments);
    },
    
    
    createRepeater: function(el, fn){
        var repeat = new Ext.util.TapRepeater(el, {
            accelerate: this.accelerateOnTapHold
        });

        this.mon(repeat, {
            tap: fn,
            touchstart: this.onTouchStart,
            touchend: this.onTouchEnd,
            preventDefault: true,
            scope: this
        });
        
        return repeat;
    },

    
    onSpinDown: function() {
        if (!this.disabled) {
            this.spin(true);
        }
    },

    
    onSpinUp: function() {
        if (!this.disabled) {
            this.spin(false);
        }
    },

    onKeyUp: function(e) {








        Ext.form.Spinner.superclass.onKeyUp.apply(this, arguments);
    },

    
    onTouchStart: function(btn) {
        if (!this.disabled) {
            btn.el.addCls('x-button-pressed');
        }
    },

    
    onTouchEnd: function(btn) {
        btn.el.removeCls('x-button-pressed');
    },

    setValue: function(value) {
        value = parseFloat(value);

        if (isNaN(value)) {
            value = this.defaultValue;
        }

        Ext.form.Spinner.superclass.setValue.call(this, value);
    },

    
    spin: function(down) {
        var value = parseFloat(this.getValue()),
            increment = this.incrementValue,
            cycle = this.cycle,
            min = this.minValue,
            max = this.maxValue,
            direction = down ? 'down': 'up';

        if (down){
            value -= increment;
        }
        else{
            value += increment;
        }

        value = (isNaN(value)) ? this.defaultValue: value;

        if (value < min) {
            value = cycle ? max: min;
        }
        else if (value > max) {
            value = cycle ? min: max;
        }

        this.setValue(value);

        this.fireEvent('spin' + direction, this, value);
        this.fireEvent('spin', this, value, direction);
    },

    
    destroy: function() {
        Ext.destroy(this.downRepeater, this.upRepeater);
        Ext.form.Spinner.superclass.destroy.call(this, arguments);
    }
});

Ext.reg('spinnerfield', Ext.form.Spinner);


Ext.form.Hidden = Ext.extend(Ext.form.Field, {
    ui: 'hidden',
    
    inputType: 'hidden',

    tabIndex: -1
});

Ext.reg('hiddenfield', Ext.form.Hidden);



Ext.form.HiddenField = Ext.extend(Ext.form.Hidden, {

    constructor: function() {
        console.warn("Ext.form.HiddenField has been deprecated and will be removed in Sencha Touch 1.0. Please use Ext.form.Hidden instead");
        Ext.form.HiddenField.superclass.constructor.apply(this, arguments);
    }
});


Ext.form.Checkbox = Ext.extend(Ext.form.Field, {
    ui: 'checkbox',
    
    inputType: 'checkbox',

    
    checked: false,
    
    
    value: '',

    
    constructor: function(config) {
        this.addEvents(
            
            'check',

            
            'uncheck'
        );

        Ext.form.Checkbox.superclass.constructor.call(this, config);
    },
    
    renderTpl: [
        '<tpl if="label"><div class="x-form-label"><span>{label}</span></div></tpl>',
        '<tpl if="fieldEl"><input id="{inputId}" type="{inputType}" name="{name}" class="{fieldCls}" tabIndex="-1" ',
            '<tpl if="checked"> checked </tpl>',
            '<tpl if="style">style="{style}" </tpl> value="{inputValue}" />',
        '</tpl>'
    ],

    
    onRender: function() {
        var isChecked = this.getBooleanIsChecked(this.checked);

        Ext.apply(this.renderData, {
            inputValue  : String(this.value),
            checked     : isChecked
        });

        Ext.form.Checkbox.superclass.onRender.apply(this, arguments);

        if (this.fieldEl) {
            this.mon(this.fieldEl, {
                click: this.onChange,
                scope: this
            });

            this.setChecked(isChecked);
            this.originalState = this.isChecked();
        }
    },
    
    
    onChange: function(e) {
        if (e) {
            if (e.browserEvent) {
                e = e.browserEvent;
            }

            if (Ext.supports.Touch && !e.isSimulated) {
                e.preventDefault();
                e.stopPropagation();
                return;
            }
        }
        
        if (this.isChecked()) {
            this.fireEvent('check', this);
        } else {
            this.fireEvent('uncheck', this);
        }
    },

    
    isChecked: function() {
        if (this.rendered) {
            return this.fieldEl.dom.checked || false;
        } else {
            return !!this.checked;
        }
    },

    
    setChecked: function(checked) {
        var newState = this.getBooleanIsChecked(checked),
            rendered = this.rendered,
            currentState,
            field;
    
        if (rendered) {
            field = this.fieldEl.dom;
            currentState = field.checked;
        } else {
            currentState = !!this.checked;
        }

        if (currentState != newState) {
            if (rendered) {
                field.checked = newState;
            } else {
                this.checked = newState;
            }
            this.onChange();
        }
        return this;
    },

    
    check: function() {
        return this.setChecked(true);
    },

    
    uncheck: function() {
        return this.setChecked(false);
    },

    
    reset: function() {
        Ext.form.Checkbox.superclass.reset.apply(this, arguments);
        
        this.setChecked(this.originalState);

        return this;
    },

    //@private

    getBooleanIsChecked: function(value) {
        return /^(true|1|on)/i.test(String(value));
    },

    getSameGroupFields: function() {
        var parent = this.el.up('form'),
            formComponent = Ext.getCmp(parent.id),
            fields = [];

        if (formComponent) {
            fields = formComponent.getFields(this.getName());
        }

        return fields;
    },

    
    getGroupValues: function() {
        var values = [];

        this.getSameGroupFields().forEach(function(field) {
            if (field.isChecked()) {
                values.push(field.getValue());
            }
        });

        return values;
    },

    
    setGroupValues: function(values) {
        this.getSameGroupFields().forEach(function(field) {
            field.setChecked((values.indexOf(field.getValue()) !== -1));
        });
        
        return this;
    },

    
    setValue: function(value) {
        value = String(value);

        Ext.form.Checkbox.superclass.setValue.call(this, value);
    }
});

Ext.reg('checkboxfield', Ext.form.Checkbox);


Ext.form.Radio = Ext.extend(Ext.form.Checkbox, {
    inputType: 'radio',
    
    ui: 'radio',
    
    

    
    getGroupValue: function() {
        var field,
            fields = this.getSameGroupFields();

        for (var i=0; i<fields.length; i++) {
            field = fields[i];

            if (field.isChecked()) {
                return field.getValue();
            }
        }

        return null;
    },

    
    setGroupValue: function(value) {
        var field,
            fields = this.getSameGroupFields(),
            i = 0,
            len = fields.length;

        for (; i < len; i++) {
            field = fields[i];

            if (field.getValue() == value) {
                field.check();
                return;
            }
        }
    }
});

Ext.reg('radiofield', Ext.form.Radio);


Ext.form.Select = Ext.extend(Ext.form.Text, {
    ui: 'select',
    

    
    valueField: 'value',

    
    displayField: 'text',

    

    
    
    
    
    
    tabIndex: -1,

    
    useMask: true,

    monitorOrientation: true,
    
    
    initComponent: function() {
        var options = this.options;

        if (this.store) {
            this.store = Ext.StoreMgr.lookup(this.store);
        }
        else {
            this.store = new Ext.data.Store({
                fields: [this.valueField, this.displayField]
            });

            if (options && Ext.isArray(options) && options.length > 0) {
                this.setOptions(this.options);
            }
        }

        Ext.form.Select.superclass.initComponent.call(this);

        this.addEvents(
            
            'change'
        );
    },

    
    onRender: function(){
        Ext.form.Select.superclass.onRender.apply(this, arguments);
        
        var name = this.hiddenName;
        if (name) {
            this.hiddenField = this.el.insertSibling({
                name: name,
                tag: 'input',
                type: 'hidden'
            }, 'after');
        }    
    },

    
    getPicker: function() {
        if (!this.picker) {
            this.picker = new Ext.Picker({
                slots: [{
                    align       : 'center',
                    name        : this.name,
                    valueField  : this.valueField,
                    displayField: this.displayField,
                    value       : this.getValue(),
                    store       : this.store
                }],
                listeners: {
                    change: this.onPickerChange,
                    scope: this
                }
            });
        }

        return this.picker;
    },

    
    getListPanel: function() {
        if (!this.listPanel) {
            this.listPanel = new Ext.Panel({
                floating         : true,
                stopMaskTapEvent : false,
                hideOnMaskTap    : true,
                cls              : 'x-select-overlay',
                scroll           : 'vertical',
                items: {
                    xtype: 'list',
                    store: this.store,
                    itemId: 'list',
                    scroll: false,
                    itemTpl : [
                        '<span class="x-list-label">{' + this.displayField + '}</span>',
                        '<span class="x-list-selected"></span>'
                    ],
                    listeners: {
                        select : this.onListSelect,
                        scope  : this
                    }
                }
            });
        }

        return this.listPanel;
    },

    
    onOrientationChange: function() {
        if (this.listPanel && !this.listPanel.hidden && !Ext.is.Phone) {
            this.listPanel.showBy(this.el, false, false);
        }
    },

    
    onMaskTap: function() {
        if (this.disabled) {
            return;
        }
        
        this.showComponent();
    },

    
    showComponent: function() {
        if (Ext.is.Phone) {
            var picker = this.getPicker(),
                name   = this.name,
                value  = {};
                
            value[name] = this.getValue();
            picker.show();
            picker.setValue(value);
        }
        else {
            var listPanel = this.getListPanel(),
                index = this.store.findExact(this.valueField, this.value);

            listPanel.showBy(this.el, 'fade', false);
            listPanel.down('#list').getSelectionModel().select(index != -1 ? index: 0, false, true);
        }
    },

    
    onListSelect: function(selModel, selected) {
        if (selected) {
            this.setValue(selected.get(this.valueField));
            this.fireEvent('change', this, this.getValue());
        }
        
        this.listPanel.hide({
            type: 'fade',
            out: true,
            scope: this
        });
    },

    
    onPickerChange: function(picker, value) {
        var currentValue = this.getValue(),
            newValue = value[this.name];

        if (newValue != currentValue) {
            this.setValue(newValue);
            this.fireEvent('change', this, newValue);
        }
    },

    
    setValue: function(value) {
        var idx = 0,
            hiddenField = this.hiddenField,
            record;

        if (value) {
            idx = this.store.findExact(this.valueField, value)
        } 
        record = this.store.getAt(idx);

        if (record && this.rendered) {
            this.fieldEl.dom.value = record.get(this.displayField);
            this.value = record.get(this.valueField);
            if (hiddenField) {
                hiddenField.dom.value = this.value;
            }
        } else {
            if (this.rendered) {
                this.fieldEl.dom.value = value;
            }
            this.value = value;
        }

        
        if (this.picker) {
            var pickerValue = {};
            pickerValue[this.name] = this.value;
            this.picker.setValue(pickerValue);
        }
        
        return this;
    },

    
    getValue: function(){
        return this.value;
    },

    
    setOptions: function(options, append) {
        if (!options) {
            this.store.clearData();
            this.setValue(null);
        }
        else {
            this.store.loadData(options, append);
        }
    },

    destroy: function() {
        Ext.form.Select.superclass.destroy.apply(this, arguments);
        Ext.destroy(this.listPanel, this.picker, this.hiddenField);
    }
});

Ext.reg('selectfield', Ext.form.Select);


Ext.form.TextArea = Ext.extend(Ext.form.Text, {
    ui: 'textarea',

    
    maxRows: undefined,
    
    autoCapitalize: false,

    renderTpl: [
        '<tpl if="label"><div class="x-form-label"><span>{label}</span></div></tpl>',
        '<tpl if="fieldEl"><div class="x-form-field-container">',
            '<textarea id="{inputId}" type="{type}" name="{name}" class="{fieldCls}"',
            '<tpl if="tabIndex">tabIndex="{tabIndex}" </tpl>',
            '<tpl if="placeHolder">placeholder="{placeHolder}" </tpl>',
            '<tpl if="style">style="{style}" </tpl>',
            '<tpl if="maxRows != undefined">rows="{maxRows}" </tpl>',
            '<tpl if="maxlength">maxlength="{maxlength}" </tpl>',
            '<tpl if="autoComplete">autocomplete="{autoComplete}" </tpl>',
            '<tpl if="autoCapitalize">autocapitalize="{autoCapitalize}" </tpl>',
            '<tpl if="autoFocus">autofocus="{autoFocus}" </tpl>',
            '></textarea>',
            '<tpl if="useMask"><div class="x-field-mask"></div></tpl>',
        '</div></tpl>'
    ],
    
    
    onRender: function() {
        this.renderData.maxRows = this.maxRows;
        
        Ext.form.TextArea.superclass.onRender.apply(this, arguments);
    },

    onKeyUp: function(e) {
        this.fireEvent('keyup', this, e);
    }
});

Ext.reg('textareafield', Ext.form.TextArea);



Ext.form.DatePicker = Ext.extend(Ext.form.Field, {
    ui: 'select',
    
    
    picker: null,

    

    
    destroyPickerOnHide: false,

    

    
    
    
    initComponent: function() {
        this.addEvents(
            
            'change'
        );

        this.tabIndex = -1;
        this.useMask = true;

        Ext.form.Text.superclass.initComponent.apply(this, arguments);
    },

    
    getDatePicker: function() {
        if (!this.datePicker) {
            if (this.picker instanceof Ext.DatePicker) {
                this.datePicker = this.picker;
            } else {
                this.datePicker = new Ext.DatePicker(Ext.apply(this.picker || {}));
            }

            this.datePicker.setValue(this.value || null);

            this.datePicker.on({
                scope : this,
                change: this.onPickerChange,
                hide  : this.onPickerHide
            });
        }

        return this.datePicker;
    },

    
    onMaskTap: function() {
        if (Ext.form.DatePicker.superclass.onMaskTap.apply(this, arguments) !== true) {
            return false;
        }
        
        this.getDatePicker().show();
    },
    
    
    onPickerChange : function(picker, value) {
        this.setValue(value);
        this.fireEvent('change', this, this.getValue());
    },
    
    
    onPickerHide: function() {
        if (this.destroyPickerOnHide && this.datePicker) {
            this.datePicker.destroy();
        }
    },

    
    setValue: function(value, animated) {
        if (this.datePicker) {
            this.datePicker.setValue(value, animated);
            this.value = (value != null) ? this.datePicker.getValue() : null;
        } else {
            if (!Ext.isDate(value) && !Ext.isObject(value)) {
                value = null;
            }

            if (Ext.isObject(value)) {
                this.value = new Date(value.year, value.month-1, value.day);
            } else {
                this.value = value;
            }
        }

        if (this.rendered) {
            this.fieldEl.dom.value = this.getValue(true);
        }
        
        return this;
    },
    
    
    getValue: function(format) {
        var value = this.value || null;
        return (format && Ext.isDate(value)) ? value.format(Ext.util.Format.defaultDateFormat) : value;
    },
    
    
    onDestroy: function() {
        if (this.datePicker) {
            this.datePicker.destroy();
        }
        
        Ext.form.DatePicker.superclass.onDestroy.call(this);
    }
});

Ext.reg('datepickerfield', Ext.form.DatePicker);



Ext.layout.LayoutManager = new Ext.AbstractManager({
    
    create : function(config, defaultType) {
        if (!config) {
            config = defaultType;
        }
        if (Ext.isString(config)) {
            return new this.types[config || defaultType];
        }
        else if (Ext.isObject(config)) {
            if (config.isLayout) {
                return config;
            }
            else {
                return new this.types[config.type || defaultType](config);
            }
        }
    }
});


Ext.regLayout = function() {
    return Ext.layout.LayoutManager.registerType.apply(Ext.layout.LayoutManager, arguments);
};


Ext.layout.Layout = Ext.extend(Object, {
    isLayout: true,
    initialized: false,

    constructor : function(config) {
        this.id = Ext.id(null, 'ext-layout-' + this.type + '-');
        Ext.apply(this, config);
    },

    
    layout : function() {
        var me = this;
        me.layoutBusy = true;
        me.initLayout();

        if (me.beforeLayout.apply(me, arguments) !== false) {
            me.onLayout.apply(me, arguments);
            me.afterLayout();
            me.owner.needsLayout = false;
            me.layoutBusy = false;
        }
    },

    beforeLayout : function() {
        this.renderItems(this.getLayoutItems(), this.getTarget());
        return true;
    },

    
    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, i, target);
            }
            else if (!this.isValidParent(item, target)) {
                this.moveItem(item, i, target);
            }
        }
    },

    
    renderItem : function(item, position, target) {
        if (!item.rendered) {
            item.render(target, position);
            this.configureItem(item, position);
            this.childrenChanged = true;
        }
    },

    
    moveItem : function(item, position, target) {
        if (typeof position == 'number') {
            position = target.dom.childNodes[position];
        }
        
        target = target.dom || target;
        target.insertBefore(item.el.dom, position || null);
        item.container = target;
        this.configureItem(item, position);
        this.childrenChanged = true;
    },

    
    initLayout : function() {
        if (!this.initialized && !Ext.isEmpty(this.targetCls)) {
            this.getTarget().addCls(this.targetCls);
        }
        this.initialized = true;
    },

    
    setOwner : function(owner) {
        this.owner = owner;
    },

    
    getLayoutItems : function() {
        return [];
    },

    
    isValidParent : function(item, target) {
        var dom = item.el ? item.el.dom : Ext.getDom(item);
        return target && (dom.parentNode == (target.dom || target));
    },

    
    configureItem: function(item, position) {
        if (this.itemCls) {
            item.el.addCls(this.itemCls);
        }
    },
    
    
    onLayout : Ext.emptyFn,
    afterLayout : Ext.emptyFn,
    onRemove : Ext.emptyFn,
    onDestroy : Ext.emptyFn,

    
    afterRemove : function(item) {
        if (this.itemCls && item.rendered) {
            item.el.removeCls(this.itemCls);
        }
    },

    
    destroy : function() {
        if (!Ext.isEmpty(this.targetCls)) {
            var target = this.getTarget();
            if (target) {
                target.removeCls(this.targetCls);
            }
        }
        this.onDestroy();
    }
});

Ext.layout.ComponentLayout = Ext.extend(Ext.layout.Layout, {
    type: 'component',

    monitorChildren: true,

    beforeLayout : function(width, height) {
        Ext.layout.ComponentLayout.superclass.beforeLayout.call(this);
        var owner = this.owner,
            isVisible = owner.isVisible(),
            layoutCollection;
        
        if (!isVisible && owner.hiddenOwnerCt) {
            layoutCollection = owner.hiddenOwnerCt.layoutOnShow;
            layoutCollection.remove(owner);
            layoutCollection.add(owner);
            owner.needsLayout = {
                width: width,
                height: height,
                isSetSize: false
            };
        }

        return isVisible && this.needsLayout(width, height);
    },

    
    needsLayout : function(width, height) {
        this.lastComponentSize = this.lastComponentSize || {
            width: -Infinity,
            height: -Infinity
        };

        var childrenChanged = this.childrenChanged;
        this.childrenChanged = false;

        return (childrenChanged || this.lastComponentSize.width !== width || this.lastComponentSize.height !== height);
    },

    
    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);
        }
    },

    
    getTarget : function() {
        return this.owner.el;
    },

    
    setTargetSize : function(width, height) {
        this.setElementSize(this.owner.el, width, height);
        this.lastComponentSize = {
            width: width,
            height: height
        };
    },

    afterLayout : function() {
        var owner = this.owner,
            layout = owner.layout,
            ownerCt = owner.ownerCt,
            ownerCtSize, activeSize, ownerSize, width, height;

        owner.afterComponentLayout(this);

        
        if (layout && layout.isLayout) {
            layout.layout();
        }

        if (ownerCt && ownerCt.componentLayout && ownerCt.componentLayout.monitorChildren && !ownerCt.componentLayout.layoutBusy) {
            ownerCt.componentLayout.childrenChanged = true;

            
            if (ownerCt.layout && !ownerCt.layout.layoutBusy) {
                if (ownerCt.layout.type == 'autocontainer') {
                    ownerCt.doComponentLayout(width, height);
                }
                else {
                    ownerCt.layout.layout();
                }
            }
        }
    }
});


Ext.layout.AutoComponentLayout = Ext.extend(Ext.layout.ComponentLayout, {
    type: 'autocomponent',

    onLayout : function(width, height) {
        this.setTargetSize(width, height);
    }
});

Ext.regLayout('autocomponent', Ext.layout.AutoComponentLayout);


Ext.layout.DockLayout = Ext.extend(Ext.layout.ComponentLayout, {
    type: 'dock',

    
    itemCls: 'x-docked',

    
    onLayout: function(width, height) {
        var me = this,
            owner = me.owner,
            body = owner.body,
            ownerCt = owner.ownerCt,
            layout = owner.layout,
            collapsed = owner.collapsed,
            contracted = owner.contracted,
            expanded = owner.expanded,
            headerItem = me.headerItem,
            target = me.getTarget(),
            autoWidth = false,
            autoHeight = false,
            animTo;

        
        var info = me.info = {
            boxes: [],
            size: {
                width: width,
                height: height
            },
            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')
            },
            bodyBox: {}
        };

        
        if (height === undefined || height === null || width === undefined || width === null || contracted) {
            
            if ((height === undefined || height === null) && (width === undefined || width === null)) {
                autoHeight = true;
                autoWidth = true;
                if (!owner.animCollapse || (!expanded && !contracted)) {
                    me.setTargetSize(null, null);
                }
                me.setBodyBox({width: null, height: null});
            }
            
            else if (height === undefined || height === null) {
                autoHeight = true;
                
                if (!owner.animCollapse || (!expanded && !contracted)) {
                    me.setTargetSize(width, null);
                }
                me.setBodyBox({width: width, height: null});
            
            }
            else {
                autoWidth = true;
                
                if (!owner.animCollapse || (!expanded && !contracted)) {
                    me.setTargetSize(null, height);
                }
                me.setBodyBox({width: null, height: height});
            }

            
            if (!collapsed && layout && layout.isLayout) {
                layout.layout();
            }

            
            
            
            
            
            me.dockItems(autoWidth, autoHeight);
            if (collapsed) {
                if (headerItem) {
                    if (headerItem.dock == 'top' || headerItem.dock == 'bottom') {
                        info.size.height = headerItem.getHeight();
                    }
                    else {
                        info.size.width = headerItem.getWidths();
                    }
                } else {
                    info.size.height = 0;
                }
            }
            if (expanded || contracted) {
                if (owner.animCollapse) {
                    Ext.createDelegate(owner.animCollapseFn, owner, [info.size.width, info.size.height])();
                }
                else {
                    Ext.createDelegate(owner['after' + (expanded ? 'Expand' : 'Collapse')], owner)();
                    me.setTargetSize(info.size.width, info.size.height);
                }
            }
            else {
                me.setTargetSize(info.size.width, info.size.height);
            }
        }
        else {
            
            
            
            if (expanded || contracted) {
                if (owner.animCollapse) {
                    Ext.createDelegate(owner.animCollapseFn, owner, [width, height])();
                }
                else {
                    Ext.createDelegate(owner['after' + (expanded ? 'Expand' : 'Collapse')], owner)();
                    me.setTargetSize(width, height);
                }
            }
            else {
                me.setTargetSize(width, height);
                me.dockItems();
            }
        }
        Ext.layout.DockLayout.superclass.onLayout.call(me, width, height);
    },

    afterLayout : function() {
        Ext.layout.DockLayout.superclass.afterLayout.call(this);
    },

    
    dockItems : function(autoWidth, autoHeight) {
        this.calculateDockBoxes(autoWidth, autoHeight);

        
        
        
        var info = this.info,
            collapsed = this.owner.collapsed,
            boxes = info.boxes,
            ln = boxes.length,
            dock, i;

        
        
        for (i = 0; i < ln; i++) {
            dock = boxes[i];
            if (collapsed === true && !dock.isHeader) {
                continue;
            }
            dock.item.setPosition(dock.x, dock.y);
        }

        
        
        if (autoWidth) {
            info.bodyBox.width = null;
        }
        if (autoHeight) {
            info.bodyBox.height = null;
        }
        this.setBodyBox(info.bodyBox);
    },

    
    calculateDockBoxes : function(autoWidth, autoHeight) {
        
        
        
        var me = this,
            target = me.getTarget(),
            items = me.getLayoutItems(),
            owner = me.owner,
            contracted = owner.contracted,
            expanded = owner.expanded,
            bodyEl = owner.body,
            info = me.info,
            size = info.size,
            ln = items.length,
            padding = info.padding,
            border = info.border,
            item, i, box, w, h, itemEl, vis;

        
        
        if (autoHeight) {
            size.height = bodyEl.getHeight() + padding.top + border.top + padding.bottom + border.bottom;
        }
        else {
            size.height = target.getHeight() - target.getMargin('tb');
        }
        
        if (autoWidth) {
            size.width = bodyEl.getWidth() + padding.left + border.left + padding.right + border.right;
        }
        else {
            size.width = target.getWidth() - target.getMargin('lr');
        }

        info.bodyBox = {
            x: border.left + padding.left,
            y: border.top + padding.top,
            width: size.width - padding.left - border.left - padding.right - border.right,
            height: size.height - border.top - padding.top - border.bottom - padding.bottom
        };

        
        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.isHeader) {
                me.headerItem = item;
            }
            
            
            
            box = me.initBox(item);

            if (autoHeight === true) {
                box = me.adjustAutoBox(box, i);
            }
            else {
                box = me.adjustSizedBox(box, i);
            }

            
            
            
            info.boxes.push(box);
        }
    },

    
    adjustSizedBox : function(box, index) {
        var bodyBox = this.info.bodyBox;
        switch (box.type) {
            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.overlay) {
            switch (box.type) {
                case 'top':
                    bodyBox.y += box.height;
                    bodyBox.height -= box.height;
                    break;

                case 'left':
                    bodyBox.x += box.width;
                    bodyBox.width -= box.width;
                    break;

                case 'bottom':
                    bodyBox.height -= box.height;
                    break;

                case 'right':
                    bodyBox.width -= box.width;
                    break;
            }
        }
        return box;
    },

    
    adjustAutoBox : function (box, index) {
        var info = this.info,
            bodyBox = info.bodyBox,
            size = info.size,
            boxes = info.boxes,
            pos = box.type,
            i, adjustBox;

        if (pos == 'top' || pos == 'bottom') {
            
            for (i = 0; i < index; 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;
                    bodyBox.width -= box.width;
                }
                break;

            case 'right':
                if (!box.overlay) {
                    bodyBox.width -= box.width;
                }
                box.x = (bodyBox.x + bodyBox.width);
                break;
        }
        return box;
    },

    
    initBox : function(item) {
        var bodyBox = this.info.bodyBox,
            horizontal = (item.dock == 'top' || item.dock == 'bottom'),
            box = {
                item: item,
                overlay: item.overlay,
                type: item.dock
            };
        
        if (item.stretch !== false) {
            box.stretched = true;
            if (horizontal) {
                box.x = bodyBox.x;
                box.width = bodyBox.width;
                item.doComponentLayout(box.width - item.el.getMargin('lr'));
            }
            else {
                box.y = bodyBox.y;
                box.height = bodyBox.height;
                item.doComponentLayout(undefined, box.height - item.el.getMargin('tb'));
            }
        }
        else {
            item.doComponentLayout();
            box.width = item.getWidth();
            box.height = item.getHeight();
            if (horizontal) {
                box.x = (item.align == 'right') ? bodyBox.width - box.width : bodyBox.x;
            }
        }

        
        
        if (box.width == undefined) {
            box.width = item.getWidth() + item.el.getMargin('lr');
        }
        if (box.height == undefined) {
            box.height = item.getHeight() + item.el.getMargin('tb');
        }

        return box;
    },

    
    getLayoutItems : function() {
        return this.owner.getDockedItems();
    },

    
    setBodyBox : function(box) {
        var me = this,
            owner = me.owner,
            body = owner.body,
            contracted = owner.contracted,
            expanded = owner.expanded,
            info = me.info,
            bodyMargin = info.bodyMargin,
            padding = info.padding,
            border = info.border;

        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);
        body.setLeft(box.x - padding.left - border.left);
        body.setTop(box.y - padding.top - border.top);
    },

    
    configureItem : function(item, pos) {
        Ext.layout.DockLayout.superclass.configureItem.call(this, item, pos);

        var el = item.el || Ext.get(item);
        if (this.itemCls) {
            el.addCls(this.itemCls + '-' + item.dock);
        }
    },

    afterRemove : function(item) {
        Ext.layout.DockLayout.superclass.afterRemove.call(this, item);
        if (this.itemCls) {
            item.el.removeCls(this.itemCls + '-' + item.dock);
        }
        var dom = item.el.dom;
        
        if (dom) {
            dom.parentNode.removeChild(dom);
        }
        
        this.childrenChanged = true;
    }
});

Ext.regLayout('dock', Ext.layout.DockLayout);

Ext.layout.FieldLayout = Ext.extend(Ext.layout.ComponentLayout, {
    type: 'field',

    
    onLayout: function(width, height) {
        Ext.layout.FieldLayout.superclass.onLayout.call(this, owner, target);

        this.setTargetSize(width, height);
        
    },

    
    handleLabel : function() {
        this.owner.labelEl.setWidth(this.owner.labelWidth);
    }
});

Ext.regLayout('field', Ext.layout.FieldLayout);


Ext.layout.ContainerLayout = Ext.extend(Ext.layout.Layout, {
    type: 'container',
        
    
     
    
    getLayoutItems : function() {
        return this.owner && this.owner.items && this.owner.items.items || [];
    },
    
    afterLayout : function() {
        this.owner.afterLayout(this);
    },    
    
    getTarget : function() {
        return this.owner.getTargetEl();
    }
});


Ext.layout.AutoContainerLayout = Ext.extend(Ext.layout.ContainerLayout, {
    type: 'autocontainer',

    
    onLayout : function(owner, target) {
        var items = this.getLayoutItems(),
            ln = items.length, i;
        for (i = 0; i < ln; i++) {
            items[i].doComponentLayout();
        }
    }
});

Ext.regLayout('auto', Ext.layout.AutoContainerLayout);
Ext.regLayout('autocontainer', Ext.layout.AutoContainerLayout);

Ext.layout.FitLayout = Ext.extend(Ext.layout.ContainerLayout, {
    itemCls: 'x-fit-item',
    targetCls: 'x-layout-fit',
    type: 'fit',
    
    
    onLayout : function() {
        Ext.layout.FitLayout.superclass.onLayout.call(this);

        if (this.owner.items.length) {
            var box = this.getTargetBox(),
                item = this.owner.items.get(0);
            
            this.setItemBox(item, box);
            item.cancelAutoSize = true;
        }
    },

    getTargetBox : function() {
        var target = this.getTarget(),
            size = target.getSize(),
            padding = {
                left: target.getPadding('l'),
                right: target.getPadding('r'),
                top: target.getPadding('t'),
                bottom: target.getPadding('b')
            }, 
            border = {
                left: target.getBorderWidth('l'),
                right: target.getBorderWidth('r'),
                top: target.getBorderWidth('t'),
                bottom: target.getBorderWidth('b')
            };
            
        return {
            width: size.width- padding.left - padding.right - border.left - border.right,
            height: size.height - padding.top - padding.bottom - border.top - border.bottom,
            x: padding.left + border.left,
            y: padding.top + border.top
        };        
    },
    
    
    setItemBox : function(item, box) {
        if (item && box.height > 0) {
            
            box = Ext.apply({}, box);
            box.width -= item.el.getMargin('lr');
            box.height -= item.el.getMargin('tb');
            item.setCalculatedSize(box);
            item.setPosition(box);
        }
    }
});

Ext.regLayout('fit', Ext.layout.FitLayout);



Ext.layout.CardLayout = Ext.extend(Ext.layout.FitLayout, {
    type: 'card',

    sizeAllCards: false,
    hideInactive: true,

    beforeLayout: function() {
        this.activeItem = this.getActiveItem();
        return Ext.layout.CardLayout.superclass.beforeLayout.apply(this, arguments);
    },

    onLayout: function() {
        Ext.layout.FitLayout.superclass.onLayout.apply(this, arguments);

        var activeItem = this.activeItem,
            items = this.getLayoutItems(),
            ln = items.length,
            targetBox = this.getTargetBox(),
            i,
            item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            this.setItemBox(item, targetBox);
        }

        if (!this.firstActivated && activeItem) {
            if (activeItem.fireEvent('beforeactivate', activeItem) !== false) {
                activeItem.fireEvent('activate', activeItem);
            }
            this.firstActivated = true;
        }
    },

    
    getActiveItem: function() {
        if (!this.activeItem && this.owner) {
            this.activeItem = this.parseActiveItem(this.owner.activeItem);
        }

        if (this.activeItem && this.owner.items.items.indexOf(this.activeItem) != -1) {
            return this.activeItem;
        }

        return null;
    },

    
    parseActiveItem: function(item) {
        if (item && item.isComponent) {
            return item;
        }
        else if (typeof item == 'number' || item == undefined) {
            return this.getLayoutItems()[item || 0];
        }
        else {
            return this.owner.getComponent(item);
        }
    },

    
    configureItem: function(item, position) {
        Ext.layout.FitLayout.superclass.configureItem.call(this, item, position);
        if (this.hideInactive && this.activeItem !== item) {
            item.hide();
        }
        else {
            item.show();
        }
    },

    onRemove: function(component) {
        if (component === this.activeItem) {
            this.activeItem = null;
            if (this.owner.items.getCount() == 0) {
                this.firstActivated = false;
            }
        }
    },

    
    getAnimation: function(newCard, owner) {
        var newAnim = (newCard || {}).cardSwitchAnimation;
        if (newAnim === false) {
            return false;
        }
        return newAnim || owner.cardSwitchAnimation;
    },

    
    setActiveItem: function(newCard, animation) {
        var me = this,
            owner = me.owner,
            doc = Ext.getDoc(),
            oldCard = me.activeItem,
            newIndex;
        
        animation = (animation == undefined) ? this.getAnimation(newCard, owner) : animation;

        newCard = me.parseActiveItem(newCard);
        newIndex = owner.items.indexOf(newCard);


        
        if (newIndex == -1) {
            owner.add(newCard);
        }

        
        if (newCard && oldCard != newCard && owner.onBeforeCardSwitch(newCard, oldCard, newIndex, !!animation) !== false) {
            
            if (!newCard.rendered) {
                this.layout();
            }

            
            if (newCard.fireEvent('beforeactivate', newCard, oldCard) === false) {
                return false;
            }
            if (oldCard && oldCard.fireEvent('beforedeactivate', oldCard, newCard) === false) {
                return false;
            }
                        
            
            if (newCard.hidden) {
                newCard.show();
            }

            me.activeItem = newCard;

            if (animation) {
                doc.on('click', Ext.emptyFn, me, {
                    single: true,
                    preventDefault: true
                });

                Ext.Anim.run(newCard, animation, {
                    out: false,
                    autoClear: true,
                    scope: me,
                    after: function() {
                        Ext.defer(function() {
                            doc.un('click', Ext.emptyFn, me);
                        },
                        50, me);

                        newCard.fireEvent('activate', newCard, oldCard);

                        if (!oldCard) {
                            
                            
                            owner.onCardSwitch(newCard, oldCard, newIndex, true);
                        }
                    }
                });

                if (oldCard) {
                    Ext.Anim.run(oldCard, animation, {
                        out: true,
                        autoClear: true,
                        after: function() {
                            oldCard.fireEvent('deactivate', oldCard, newCard);
                            if (me.hideInactive && me.activeItem != oldCard) {
                                oldCard.hide();
                            }

                            
                            
                            
                            owner.onCardSwitch(newCard, oldCard, newIndex, true);
                        }
                    });
                }
            }
            else {
                newCard.fireEvent('activate', newCard, oldCard);
                if (oldCard) {
                    oldCard.fireEvent('deactivate', oldCard, newCard);
                    if (me.hideInactive) {
                        oldCard.hide();
                    }
                }
                owner.onCardSwitch(newCard, oldCard, newIndex, false);
            }

            return newCard;
        }

        return false;
    },

    
    getNext: function(wrap) {
        var items = this.getLayoutItems(),
            index = items.indexOf(this.activeItem);
        return items[index + 1] || (wrap ? items[0] : false);
    },

    
    next: function(anim, wrap) {
        return this.setActiveItem(this.getNext(wrap), anim);
    },

    
    getPrev: function(wrap) {
        var items = this.getLayoutItems(),
            index = items.indexOf(this.activeItem);
        return items[index - 1] || (wrap ? items[items.length - 1] : false);
    },

    
    prev: function(anim, wrap) {
        return this.setActiveItem(this.getPrev(wrap), anim);
    }
});

Ext.regLayout('card', Ext.layout.CardLayout);

Ext.layout.BoxLayout = Ext.extend(Ext.layout.ContainerLayout, {
    type: 'box',

    targetCls: 'x-layout-box',
    
    innerCls: 'x-layout-box-inner',

    
    pack : 'start',
    align: 'center',

    notifyOwnerCtContainer: true,

    fixedLayout: false,

    
    direction: 'normal',

    
    onLayout: function() {
        Ext.layout.BoxLayout.superclass.onLayout.call(this);
        
        if (this.pack === 'left' || this.pack === 'top') {
            this.pack = 'start';
        } else if (this.pack === 'right' || this.pack === 'bottom') {
            this.pack = 'end';
        }

        var target = this.getTarget(),
            ct = target.parent(),
            targetWidth = (ct.getWidth() - ct.getPadding('lr') - ct.getBorderWidth('lr')) + 'px',
            targetHeight = (ct.getHeight() - ct.getPadding('tb') - ct.getBorderWidth('tb')) + 'px';
            
        target.setStyle({
            '-webkit-box-orient': this.orientation,
            '-webkit-box-direction': this.direction,
            '-webkit-box-pack': this.pack,
            '-webkit-box-align': this.align
        });
        
        if (this.orientation == 'horizontal') {
            target.setStyle({
                'min-width': targetWidth,
                'height': targetHeight
            });
        } else {
            target.setStyle({
                'min-height': targetHeight,
                'width': targetWidth
            });
        }

        this.prepareFlexedItems();
        this.setFlexedItems();
    },
    
    prepareFlexedItems : function() {        
        var items = this.getLayoutItems(),
            ln = items.length,
            item, i;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.flex != undefined) {
                item.el.setStyle('position', 'absolute');
                item.boxEl = this.createBoxEl(item);
            } else {
                item.doComponentLayout();
            }
        }
    },    
        
    setFlexedItems : function() {
        var items = this.getLayoutItems(),
            ln = items.length,
            item, i;
            
        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.flex != undefined) {
                item.boxSize = item.boxEl.getSize();
            }
        }

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item.flex != undefined) {
                item.el.setStyle('position', '');
                if (this.align == 'stretch') {
                    item.setSize(item.boxSize);
                } else {
                    if (this.orientation == 'horizontal') {
                        item.setWidth(item.boxSize.width);
                    } else {
                        item.setHeight(item.boxSize.height);
                    }
                }                
                item.boxEl.remove();
                delete item.boxEl;
                delete item.boxSize;
            }
        }
    },
    
    getTarget : function() {
        var owner = this.owner,
            innerCt = this.innerCt;
        
        if (!innerCt) {
            if (owner.scrollEl) {
                innerCt = owner.scrollEl.addCls(this.innerCls);
            } else {
                innerCt = owner.getTargetEl().createChild({cls: this.innerCls});
            }
            this.innerCt = innerCt;
        }

        return innerCt;
    },
    
    createBoxEl : function(item) {
        var el = item.el;
        return el.insertSibling({
            style: 'margin-top: ' + el.getMargin('tb') + 'px; margin-left: ' + el.getMargin('lr') + 'px; -webkit-box-flex: ' + item.flex
        });
    }
});


Ext.layout.HBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
    orientation: 'horizontal'
    
    
    
    
});

Ext.regLayout('hbox', Ext.layout.HBoxLayout);


Ext.layout.VBoxLayout = Ext.extend(Ext.layout.BoxLayout, {
    orientation: 'vertical'
    
    
    
    
    
});

Ext.regLayout('vbox', Ext.layout.VBoxLayout);



Ext.plugins.ListPagingPlugin = Ext.extend(Ext.util.Observable, {
    
    autoPaging: false,

    
    loadMoreText: 'Load More...',

    init: function(list) {
        this.list = list;

        list.onBeforeLoad = Ext.util.Functions.createInterceptor(list.onBeforeLoad, this.onBeforeLoad, this);

        
        this.mon(list, 'update', this.onListUpdate, this);
    },

    onListUpdate : function() {
        if (!this.rendered) {
            this.render();
        }

        this.el.appendTo(this.list.getTargetEl());
        if (!this.autoPaging) {
            this.el.removeCls('x-loading');
        }
        this.loading = false;
    },

    render : function() {
        var list = this.list,
            targetEl = list.getTargetEl(),
            html = '';

        if (!this.autoPaging) {
            html += '<div class="x-list-paging-msg">' + this.loadMoreText + '</div>';
        }

        this.el = targetEl.createChild({
            cls: 'x-list-paging' + (this.autoPaging ? ' x-loading' : ''),
            html: html + Ext.LoadingSpinner
        });

        if (this.autoPaging) {
            this.mon(targetEl.getScrollParent(), 'scrollend', this.onScrollEnd, this);
        }
        else {
            this.mon(this.el, 'tap', this.onPagingTap, this);
        }

        this.rendered = true;
    },

    onBeforeLoad : function() {
        if (this.loading && this.list.store.getCount() > 0) {
            this.list.loadMask.disable();
            return false;
        }
    },

    
    onPagingTap : function(e) {
        if (!this.loading) {
            this.loading = true;
            this.list.store.nextPage();
            this.el.addCls('x-loading');
        }
    },

    onScrollEnd : function(scroller, pos) {
        if (pos.y >= Math.abs(scroller.offsetBoundary.top)) {
            this.loading = true;
            this.list.store.nextPage();
        }
    }
});

Ext.preg('listpaging', Ext.plugins.ListPagingPlugin);


Ext.plugins.PullRefreshPlugin = Ext.extend(Ext.util.Observable, {
    
    pullRefreshText: 'Pull down to refresh...',

    
    releaseRefreshText: 'Release to refresh...',

    
    loadingText: 'Loading...',

    
    snappingAnimationDuration: 150,

    
    refreshFn: null,

    
    pullTpl: new Ext.XTemplate(
        '<div class="x-list-pullrefresh">',
            '<div class="x-list-pullrefresh-arrow"></div>',
            Ext.LoadingSpinner,
            '<div class="x-list-pullrefresh-wrap">',
                '<h3 class="x-list-pullrefresh-message">{message}</h3>',
                '<div class="x-list-pullrefresh-updated">Last Updated: <span>{lastUpdated:date("m/d/Y h:iA")}</span></div>',
            '</div>',
        '</div>'
    ),

    isRefreshing: false,
    isLoading: false,
    currentViewState: '',

    init: function(list) {
        this.list = list;
        this.lastUpdated = new Date();

        list.on('update', this.onListUpdate, this);

        list.onBeforeLoad = Ext.util.Functions.createInterceptor(list.onBeforeLoad, this.onBeforeLoad, this);
    },

    
    onListUpdate: function() {
        if (!this.rendered) {
            this.render();
        }

        this.list.getTargetEl().insertFirst(this.el);

        if (!this.refreshFn) {
            this.onLoadComplete.call(this);
        }
    },

    
    render : function() {
        var list = this.list,
            targetEl = list.getTargetEl(),
            scroller = targetEl.getScrollParent();

        if (!this.pullTpl.isTemplate) {
            this.pullTpl = new Ext.XTemplate(this.pullTpl);
        }

        this.el = this.pullTpl.insertFirst(targetEl, {
            message: this.pullRefreshText,
            lastUpdated: this.lastUpdated
        }, true);

        this.messageEl = this.el.down('.x-list-pullrefresh-message');
        this.updatedEl = this.el.down('.x-list-pullrefresh-updated > span');

        this.pullHeight = this.el.getHeight();

        
        this.scroller = scroller;

        scroller.on('bouncestart', this.onBounceStart, this);
        scroller.on('offsetchange', this.onOffsetChange, this);
        scroller.on('bounceend', this.onBounceEnd, this);
        scroller.on('offsetboundaryupdate', this.onOffsetBoundaryUpdate, this);

        this.rendered = true;
    },

    onOffsetBoundaryUpdate: function(scroller, offsetBoundary) {
        if (this.isRefreshing) {
            offsetBoundary.bottom += this.pullHeight;
        }
    },

    onBounceStart: function(scroller, info) {
        if (info.axis === 'y') {
            if (!this.isRefreshing && scroller.offset.y > this.pullHeight) {
                this.isRefreshing = true;

                this.onOffsetBoundaryUpdate(scroller, scroller.offsetBoundary);
            }
        }
    },

    onBounceEnd: function(scroller, info) {
        if (info.axis === 'y') {
            if (this.isRefreshing) {
                this.isRefreshing = false;

                this.setViewState('loading');
                this.isLoading = true;

                if (this.refreshFn) {
                    this.refreshFn.call(this, this.onLoadComplete, this);
                }
                else {
                    this.list.getStore().load();
                }
            }
        }
    },

    onOffsetChange: function(scroller, offset) {
        if (offset.y > 0 && !this.isRefreshing && !this.isLoading) {
            if (offset.y > this.pullHeight) {
                this.setViewState('release');
            }
            else {
                this.setViewState('pull');
            }
        }
    },

    setViewState: function(state) {
        if (state === this.currentViewState) {
            return this;
        }

        this.currentViewState = state;

        switch (state) {
            case 'pull':
                this.messageEl.setHTML(this.pullRefreshText);
                this.el.removeCls(['x-list-pullrefresh-release', 'x-list-pullrefresh-loading']);
            break;

            case 'release':
                this.messageEl.setHTML(this.releaseRefreshText);
                this.el.addCls('x-list-pullrefresh-release');
            break;

            case 'loading':
                this.messageEl.setHTML(this.loadingText);
                this.el.addCls('x-list-pullrefresh-loading');
                break;
        }

        return this;
    },

    
    onBeforeLoad: function() {
        if (this.isLoading && this.list.store.getCount() > 0) {
            this.list.loadMask.disable();
            return false;
        }
    },

    
    onLoadComplete: function() {
        var me = this;

        if (this.isLoading) {
            this.isLoading = false;
            this.lastUpdated = new Date();

            this.setViewState('pull');
            this.updatedEl.setHTML(Ext.util.Format.date(this.lastUpdated, "m/d/Y h:iA"));

            setTimeout(function() {
                me.scroller.updateBoundary(me.snappingAnimationDuration);
            }, 100);
        }
    }
});

Ext.preg('pullrefresh', Ext.plugins.PullRefreshPlugin);



;
/**
 * @class Ext.History
 * @extends Ext.util.Observable
 * @ignore
 * @private
 * 
 * Mobile-optimized port of Ext.History. Note - iPad on iOS < 4.2 does not have HTML5 History support so we still
 * have to poll for changes.
 */
Ext.History = new Ext.util.Observable({ 
    constructor: function() {
        Ext.History.superclass.constructor.call(this, config);
        
        this.addEvents(
            /**
             * @event change
             */
            'change'
        );
    },
    
    /**
     * @private
     * Initializes event listeners
     */
    init: function() {
        var me = this;
        
        me.setToken(window.location.hash);
        
        if (Ext.supports.History) {
            window.addEventListener('hashchange', this.onChange);
        } else {
            setInterval(function() {
                var newToken = me.cleanToken(window.location.hash),
                    oldToken = me.getToken();
                
                if (newToken != oldToken) {
                    me.onChange();
                }
            }, 50);
        }
    },
    
    /**
     * @private
     * Event listener for the hashchange event
     */
    onChange: function() {
        var me       = Ext.History,
            newToken = me.cleanToken(window.location.hash);
        
        if (me.token != newToken) {
            me.fireEvent('change', newToken);
        }
        
        me.setToken(newToken);
    },
    
    /**
     * Sets a new token, stripping of the leading # if present. Does not fire the 'change' event
     * @param {String} token The new token
     * @return {String} The cleaned token
     */
    setToken: function(token) {
        return this.token = this.cleanToken(token);
    },
    
    /**
     * @private
     * Cleans a token by stripping off the leading # if it is present
     * @param {String} token The unclean token
     * @return {String} The clean token
     */
    cleanToken: function(token) {
        return token[0] == '#' ? token.substr(1) : token;
    },
    
    /**
     * Returns the current history token
     * @return {String} The current token
     */
    getToken: function() {
        return this.token;
    },
    
    /**
     * Adds a token to the history stack by updation the address bar hash
     * @param {String} token The new token
     */
    add: function(token) {
        window.location.hash = this.setToken(token);
        
        if (!Ext.supports.History) {
            this.onChange();
        }
    }
});
/**
 * @author Ed Spencer
 * @class Ext.ControllerManager
 * @extends Ext.AbstractManager
 * @singleton
 * 
 * <p>Keeps track of all of the registered controllers. This should very rarely need to be used by developers. This 
 * is simply an {@link Ext.AbstractManager AbstractManager} with a custom {@link #register} function which sets up
 * the controller and its linked {@link Ext.Application application}.</p>
 */
Ext.ControllerManager = new Ext.AbstractManager({
    register: function(id, options) {
        options.id = id;
        
        Ext.applyIf(options, {
            application: Ext.ApplicationManager.currentApplication
        });
        
        var controller = new Ext.Controller(options);
        
        if (controller.init) {
            controller.init();
        }
        
        this.all.add(controller);
        
        return controller;
    }
});

/**
 * Shorthand for {@link Ext.ControllerMgr#register}
 * Creates a new Controller class from the specified config object. See {@link Ext.Controller} for full examples.
 * 
 * @param {Object} config A configuration object for the Controller you wish to create.
 * @return {Ext.Controller} The newly registered Controller
 * @member Ext
 * @method regController
 */
Ext.regController = function() {
    return Ext.ControllerManager.register.apply(Ext.ControllerManager, arguments);
};
/**
 * @author Ed Spencer
 * @class Ext.Controller
 * @extends Ext.util.Observable
 * 
 * @constructor
 */
Ext.Controller = Ext.extend(Ext.util.Observable, {
    constructor: function(config) {
        this.addEvents(
            /**
             * @event instance-created
             * Fired when a new model instance has been successfully created by this controller
             * @param {Ext.data.Model} instance The newly-created model instance
             */
            'instance-created',
            
            /**
             * @event instance-creation-failed
             * Fired when an attempt at saving a new instance failed
             * @param {Ext.data.Model} instance The instance that could not be saved
             * @param {Object} errors The set of errors (if any) that caused the failure
             */
            'instance-creation-failed',
            
            /**
             * @event instance-updated
             * Fired when an existing model instance has been successfully updated by this controller
             * @param {Ext.data.Model} instance The instance that was updated
             */
            'instance-updated',
            
            /**
             * @event instance-update-failed
             * Fired when an update to existing model instance could not be successfully completed
             * @param {Ext.data.Model} instance The instance that could not be updated
             * @param {Object} errors The set of errors (if any) that caused the failure
             */
            'instance-update-failed',
            
            /**
             * @event instance-destroyed
             * Fired when an existing instance has been successfully destroyed by this controller
             * @param {Ext.data.Model} instance The instance that was destroyed
             */
            'instance-destroyed',
            
            /**
             * @event instance-destruction-failed
             * Fired when an existing instance could not be destroyed
             * @param {Ext.data.Model} instance The instance that could not be destroyed
             * @param {Object} errors The set of errors (if any) that caused the failure
             */
            'instance-destruction-failed'
        );
        
        Ext.Controller.superclass.constructor.call(this, config);
        
        Ext.apply(this, config || {});
        
        if (typeof this.model == 'string') {
            this.model = Ext.ModelMgr.getModel(this.model);
        }
    },
    
    index: function() {
        this.render('index', {
            listeners: {
                scope  : this,
                edit   : this.edit,
                build  : this.build,
                create : this.onCreateInstance,
                destroy: this.onDestroyInstance
            }
        });
    },
    
    /**
     * Renders the edit form for a given model instance
     * @param {Ext.data.Model} instance The instance to edit
     */
    edit: function(instance) {
        var view = this.render('edit', {
            listeners: this.getEditListeners()
        });
        
        view.loadModel(instance);
    },
    
    /**
     * Callback automatically tied to the index view's 'build' event. By default this just renders the registered
     * 'build' view
     */
    build: function() {
        this.render('build', {
            listeners: this.getBuildListeners()
        });
    },
    
    /**
     * Saves a phantom Model instance via its configured Proxy. Fires the 'instance-created' event if successful,
     * the 'instance-creation-failed' event if not.
     * @param {Object} data The data to create the instance from
     * @param {Object} options Optional options object containing callbacks for success and failure plus optional scope
     */
    create: function(data, options) {
        options = options || {};
        
        var model     = this.getModel(),
            instance  = new model(data),
            successCb = options.success,
            failureCb = options.failure,
            scope     = options.scope || this;
        
        instance.save({
            scope  : this,
            success: function(instance) {
                if (typeof successCb == 'function') {
                    successCb.call(scope, instance);
                }
                
                this.fireEvent('instance-created', instance);
            },
            failure: function(instance, errors) {                
                if (typeof failureCb == 'function') {
                    failureCb.call(scope, instance, errors);
                }
                
                this.fireEvent('instance-creation-failed', instance, errors);
            }
        });
    },
    
    /**
     * Updates an existing model instance by applying optional updates to it and attempting to save
     * @param {Ext.data.Model} instance The existing instance
     * @param {Object} updates Optional additional updates to apply to the instance before saving
     * @param {Object} options success and failure callback functions
     */
    update: function(instance, updates, options) {
        options = options || {};
        
        var successCb = options.success,
            failureCb = options.failure,
            scope     = options.scope || this;
        
        if (Ext.isObject(updates)) {
            instance.set(updates);
        }
        
        instance.save({
            scope  : this,
            success: function(instance) {
                if (typeof successCb == 'function') {
                    successCb.call(scope, instance);
                }
                
                this.fireEvent('instance-updated', instance);
            },
            failure: function(instance, errors) {
                if (typeof failureCb == 'function') {
                    failureCb.call(scope, instance, errors);
                }
                
                this.fireEvent('instance-update-failed', instance, errors);
            }
        });
    },
    
    /**
     * Destroys one or more existing, previously saved model instances
     * @param {Ext.data.Model} instance The model instance to destroy
     * @param {Object} options success and failure callbacks
     */
    destroy: function(instance, options) {
        options = options || {};
        
        var successCb = options.success,
            failureCb = options.failure,
            scope     = options.scope || this;
        
        instance.destroy({
            scope  : this,
            success: function(instance) {
                if (typeof successCb == 'function') {
                    successCb.call(scope, instance);
                }
                
                this.fireEvent('instance-destroyed', instance);
            },
            failure: function(instance, errors) {
                if (typeof failureCb == 'function') {
                    failureCb.call(scope, instance, errors);
                }
                
                this.fireEvent('instance-destruction-failed', instance, errors);
            }
        });
    },
    
    /**
     * Returns the listeners to attach to the view rendered by the {@link #build} action. By default this returns listeners
     * for save and cancel, but this can be overridden
     * @return {Object} listeners
     */
    getBuildListeners: function() {
        return {
            scope : this,
            save  : this.onCreateInstance,
            cancel: this.onCancelBuild
        };
    },
    
    /**
     * Returns the listeners to attach to the view rendered by the {@link #edit} action. By default this returns listeners
     * for save and cancel, but this can be overridden
     * @return {Object} listeners
     */
    getEditListeners: function() {
        return {
            scope : this,
            save  : this.onUpdateInstance,
            cancel: this.onCancelEdit
        };
    },
    
    /**
     * Handler for the 'cancel' event fired by an {@link #edit} view. By default this just closes the view
     * @param {Ext.Component} view The edit form
     */
    onCancelEdit: function(view) {
        return this.closeView(view);
    },
    
    /**
     * Handler for the 'cancel' event fired by an {@link #build} view. By default this just closes the view
     * @param {Ext.Component} view The build form
     */
    onCancelBuild: function(view) {
        return this.closeView(view);
    },
    
    /**
     * Callback automatically tied to the index view's 'create' event. By default this just calls the controller's
     * create function with the data and some basic callbacks to handle errors or show success. Can be overridden
     * to provide custom behavior
     * @param {Ext.View} view The view instance that fired the event
     */
    onCreateInstance: function(view) {
        this.create(view.getValues(), {
            scope  : this,
            success: function(instance) {
                this.closeView(view);
            },
            failure: function(instance, errors) {
                console.log('fail');
            }
        });
    },
    
    /**
     * Callback automatically tied to the index view's 'update' event. By default this just calls the controller's
     * update function with the data and some basic callbacks to handle errors or show success. Can be overridden
     * to provide custom behavior
     * @param {Ext.Component} view The view instance that fired the event
     */
    onUpdateInstance: function(view) {
        this.update(view.getRecord(), view.getValues(), {
            scope  : this,
            success: function(instance) {
                this.closeView(view);
            },
            failure: function(instance, errors) {
                
            }
        });
    },
    
    /**
     * Callback automatically tied to the index view's 'destroy' event. By default that just calls the controller's
     * destroy function with the model instance and some basic callbacks to handle errors or show success. Can be
     * overridden to provide custom behavior.
     * @param {Ext.data.Model} instance The instance to destroy
     * @param {Ext.View} view The view instance that fired the event
     */
    onDestroyInstance: function(instance, view) {
        this.destroy(instance, {
            scope  : this,
            success: function(instance) {
                
            },
            failure: function(instance, errors) {
                
            }
        });
    },
    
    /**
     * Sets the default container that components rendered using {@link #render} will be added to.
     * In many applications there is a fixed navigation panel and a content panel - the content
     * panel would usually form the render target in this type of setup.
     * @param {Ext.Container} target The container to add rendered components to
     */
    setRenderTarget: function(target) {
        /**
         * @property renderTarget
         * @type Ext.Container
         * The current {@link #setRenderTarget render target}. Read only
         */
        Ext.Controller.renderTarget = target;
    },
    
    /**
     * Renders a given view based on a registered name
     * @param {String} viewName The name of the view to render
     * @param {Object} config Optional config object
     * @return {Ext.View} The view instance
     */
    render: function(config, target) {
        var Controller  = Ext.Controller,
            application = this.application,
            profile     = application ? application.currentProfile : undefined,
            profileTarget, view;
        
        Ext.applyIf(config, {
            profile: profile
        });
        
        view = Ext.ComponentMgr.create(config);
        
        if (target !== false) {
            //give the current Ext.Profile a chance to set the target
            profileTarget = profile ? profile.getRenderTarget(config, application) : target;
            
            if (target == undefined) {
                target = profileTarget || (application ? application.defaultTarget : undefined);
            }

            if (typeof target == 'string') {
                target = Ext.getCmp(target);
            }

            if (target != undefined && target.add) {
                if (profile) {
                    profile.beforeLayout(view, target, application);
                }
                
                target.add(view);

                if (target.layout && target.layout.setActiveItem) {
                    target.layout.setActiveItem(view);
                }

                target.doComponentLayout();
                
                if (profile) {
                    profile.afterLayout(view, target, application);
                }
            }
        }
        
        return view;
    },
    
    /**
     * This function allows you to add listeners to a view
     * in a convenient way
     */    
    control : function(view, actions, itemName) {
        if (!view || !view.isView) {
            throw 'Trying to control a view that doesnt exist';
        }

        var item = itemName ? view.refs[itemName] : view,
            key, value, name, child, listener;
    
        if (!item) {
            throw "No item called " + itemName + " found inside the " + view.name + " view.";
        }        
    	
        for (key in actions) {
            value = actions[key];
    	
            // If it is an object, it can either be a listener with a handler defined
            // in which case the key is the event name, or it can be an object containing
            // listener(s), in which case key will be the items name
            if (Ext.isObject(value) && !value.fn) {
                this.control(view, value, key);
            }
            else {
                // Now hopefully we can be sure that key is an event name. We will loop over all
                // child items and enable bubbling for this event
                if (item.refs) {
                    for (name in item.refs) {
                        child = item.refs[name];
                        if (child.isObservable && child.events[key]) {
                            child.enableBubble(key);
                        }
                    }
                }
    
                if (!value.fn) {
                    listener = {};
                    listener[key] = value;
                    listener.scope = this;
                }
                else {
                    listener = value;
                    if (listener.scope === undefined) {
                        listener.scope = this;
                    }
                }

                // Finally we bind the listener on this item
                item.addListener(listener);
            }
        }

        return view;
    },
    
    /**
     * Returns the constructor for the model type linked to this controller
     * @return {Ext.data.Model} The model constructor
     */
    getModel: function() {
        return Ext.ModelMgr.getModel(this.model);
    },
    
    /**
     * @private
     * Used internally whenever we want to remove a component from its parent container. See onCancelEdit and onCancelBuild
     * @param {Ext.Component} view The component to close
     */
    closeView: function(view) {
        var ownerCt = view.ownerCt;
        
        if (ownerCt) {
            ownerCt.remove(view);
            ownerCt.doLayout();
            ownerCt.setActiveItem(ownerCt.items.last());
        }
    }
});
/**
 * @author Ed Spencer
 * @class Ext.util.Dispatcher
 * @extends Ext.util.Observable
 * 
 * <p>The Dispatcher class is used to send requests through to a controller action. Usually, only a single Dispatcher
 * is required on the page, and by default a single instance is already created - {@link Ext.Dispatcher}. See the
 * {@link Ext.Dispatcher Dispatcher docs} for details on how this works.</p>
 * 
 * @constructor
 */
Ext.util.Dispatcher = Ext.extend(Ext.util.Observable, {
    
    constructor: function(config) {
        this.addEvents(
            /**
             * @event before-dispatch
             * Fires before an interaction is dispatched. Return false from any listen to cancel the dispatch
             * @param {Ext.Interaction} interaction The Interaction about to be dispatched
             */
            'before-dispatch',
            
            /**
             * @event dispatch
             * Fired once an Interaction has been dispatched
             * @param {Ext.Interaction} interaction The Interaction that was just dispatched
             */
            'dispatch'
        );
        
        Ext.util.Dispatcher.superclass.constructor.call(this, config);
    },
    
    /**
     * Dispatches a single interaction to a controller/action pair
     * @param {Object} options Options representing at least the controller and action to dispatch to
     */
    dispatch: function(options) {
        var interaction = new Ext.Interaction(options),
            controller  = interaction.controller,
            action      = interaction.action,
            History     = Ext.History;
        
        if (this.fireEvent('before-dispatch', interaction) !== false) {
            if (History && options.historyUrl) {
                History.suspendEvents(false);
                History.add(options.historyUrl);
                Ext.defer(History.resumeEvents, 100, History);
            }
            
            if (controller && action) {
                controller[action].call(controller, interaction);
                interaction.dispatched = true;
            }
            
            this.fireEvent('dispatch', interaction);
        }
    },
    
    /**
     * Dispatches to a controller/action pair, adding a new url to the History stack
     */
    redirect: function(options) {
        if (options instanceof Ext.data.Model) {
            //compose a route for the model
            
        } else if (typeof options == 'string') {
            //use router
            var route = Ext.Router.recognize(options);
            
            if (route) {
                return this.dispatch(route);
            }
        }
        return null;
    },
    
    /**
     * Convenience method which returns a function that calls Ext.Dispatcher.redirect. Useful when setting
     * up several listeners that should redirect, e.g.:
<pre><code>
myComponent.on({
    homeTap : Ext.Dispatcher.createRedirect('home'),
    inboxTap: Ext.Dispatcher.createRedirect('inbox'),
});
</code></pre>
     * @param {String/Object} url The url to create the redirect function for
     * @return {Function} The redirect function
     */
    createRedirect: function(url) {
        return function() {
            Ext.Dispatcher.redirect(url);
        };
    }
});

/**
 * @class Ext.Dispatcher
 * @extends Ext.util.Dispatcher
 * 
 * <p>The Dispatcher is responsible for sending requests through to a specific {@link Ext.Controller controller} 
 * action. It is usually invoked either by a UI event handler calling {@link Ext#dispatch}, or by the 
 * {@link Ext.Router Router} recognizing a change in the page url.</p>
 * 
 * <p>Ext.Dispatcher is the default instance of {@link Ext.util.Dispatcher} that is automatically created for every
 * application. Usually it is the only instance that you will need.</p>
 * 
 * <p>Let's say we have an application that manages instances of a Contact model using a contacts controller:</p>
 * 
<pre><code>
Ext.regModel('Contact', {
    fields: ['id', 'name', 'email']
});

//the controller has a single action - list - which just loads the Contacts and logs them to the console
Ext.regController('contacts', {
    list: function() {
        new Ext.data.Store({
            model: 'Contact',
            autoLoad: {
                callback: function(contacts) {
                    console.log(contacts);
                }
            }
        });
    }
});
</code></pre>
 * 
 * <p>We can easily dispatch to the contacts controller's list action from anywhere in our app:</p>
 * 
<pre><code>
Ext.dispatch({
    controller: 'contacts',
    action    : 'list',
    
    historyUrl: 'contacts/list',
    
    anotherOption: 'some value'
});
</code></pre>
 * 
 * <p>The Dispatcher finds the contacts controller and calls its list action. We also passed in a couple of additional
 * options to dispatch - historyUrl and anotherOption. 'historyUrl' is a special parameter which automatically changes
 * the browser's url when passed. For example, if your application is being served from http://yourapp.com, dispatching
 * with the options we passed above would update the url to http://yourapp.com/#contacts/list, as well as calling the 
 * controller action as before.</p>
 * 
 * <p>We also passed a second configuration into dispatch - anotherOption. We can access this inside our controller 
 * action like this:</p>
 * 
<pre><code>
Ext.regController('contacts', {
    list: function(options) {
        console.log(options.anotherOption); // 'some value'
    }
});
</code></pre>
 * 
 * <p>We can pass anything in to Ext.dispatch and have it come through to our controller action. Internally, all of the
 * options that we pass to dispatch are rolled into an {@link Ext.Interaction}. Interaction is a very simple class that
 * represents a single request into the application - typically the controller and action names plus any additional 
 * information like the Model instance that a particular action is concerned with.</p>
 * 
 * @singleton
 */
Ext.Dispatcher = new Ext.util.Dispatcher();

/**
 * Shorthand for {@link Ext.Dispatcher#dispatch}. Dispatches a request to a controller action
 * 
 * @member Ext
 * @method dispatch
 */
Ext.dispatch = function() {
    return Ext.Dispatcher.dispatch.apply(Ext.Dispatcher, arguments);
};

/**
 * Shorthand for {@link Ext.Dispatcher#redirect}. Dispatches a request to a controller action, adding to the History
 * stack and updating the page url as necessary.
 * 
 * @member Ext
 * @method redirect
 */
Ext.redirect = function() {
    return Ext.Dispatcher.redirect.apply(Ext.Dispatcher, arguments);
};

Ext.createRedirect = Ext.Dispatcher.createRedirect;
/**
 * @author Ed Spencer
 * @class Ext.util.Router
 * @extends Ext.util.Observable
 * 
 * <p>See {@link Ext.Router}.</p>
 */
Ext.util.Router = Ext.extend(Ext.util.Observable, {
    
    constructor: function(config) {
        config = config || {};

        Ext.apply(this, config, {
            defaults: {
                action: 'index'
            }
        });
        
        this.routes = [];

        Ext.util.Router.superclass.constructor.call(this, config);
    },
    
    /**
     * Connects a url-based route to a controller/action pair plus additional params
     * @param {String} url The url to recognize
     */
    connect: function(url, params) {
        params = Ext.apply({url: url}, params || {}, this.defaults);
        var route = new Ext.util.Route(params);
        
        this.routes.push(route);
        
        return route;
    },
    
    /**
     * Recognizes a url string connected to the Router, return the controller/action pair associated with it
     * @param {String} url The url to recognize
     * @return {Object/undefined} If the url was recognized, the controller and action to call, else undefined
     */
    recognize: function(url) {
        var routes = this.routes,
            length = routes.length,
            i, result;
        
        for (i = 0; i < length; i++) {
            result = routes[i].recognize(url);
            
            if (result != undefined) {
                return result;
            }
        }
        return undefined;
    },
    
    /**
     * Convenience method which just calls the supplied function with the Router instance. Example usage:
<pre><code>
Ext.Router.draw(function(map) {
    map.connect('activate/:token', {controller: 'users', action: 'activate'});
    map.connect('home',            {controller: 'index', action: 'home'});
});
</code></pre>
     * @param {Function} fn The fn to call
     */
    draw: function(fn) {
        fn.call(this, this);
    }
});

/**
 * @author Ed Spencer
 * @class Ext.Router
 * @extends Ext.util.Observable
 * <p>The Router is used to map urls to {@link Ext.Controller controller}/action pairs. It can be used whenever an 
 * application wishes to provide history and deep linking support. Every {@link Ext.Application} can set up Routes
 * using the default {@link Ext.Router} instance, supplying application-specific routes like this:</p>
 * 
<pre><code>
//Note the # in the url examples below
Ext.Router.draw(function(map) {
    //maps the url http://mydomain.com/#dashboard to the home controller's index action
    map.connect('dashboard', {controller: 'home', action: 'index'});

    //fallback route - would match routes like http://mydomain.com/#users/list to the 'users' controller's
    //'list' action
    map.connect(':controller/:action');
});
</code></pre>
 * 
 * <p>The Router is concerned only with the segment of the url after the hash (#) character. This segment is parsed
 * by the {@link Ext.Dispatcher Dispatcher} and passed to the Router's {@link #recognize} method. Most of the time you
 * will not need to modify any of the behavior of the Router - it is all handled internally by the application 
 * architecture.</p>
 * 
 * @singleton
 */
Ext.Router = new Ext.util.Router();

/**
 * @author Ed Spencer
 * @class Ext.util.Route
 * @extends Object
 * @ignore
 * <p>Represents a mapping between a url and a controller/action pair. May also contain additional params</p>
 */
Ext.util.Route = Ext.extend(Object, {
    /**
     * @cfg {String} url The url string to match. Required.
     */
    
    constructor: function(config) {
        Ext.apply(this, config, {
            conditions: {}
        });
        
        /*
         * The regular expression we use to match a segment of a route mapping
         * this will recognise segments starting with a colon,
         * e.g. on 'namespace/:controller/:action', :controller and :action will be recognised
         */
        this.paramMatchingRegex = new RegExp(/:([0-9A-Za-z\_]*)/g);
        
        /*
         * Converts a route string into an array of symbols starting with a colon. e.g.
         * ":controller/:action/:id" => [':controller', ':action', ':id']
         */
        this.paramsInMatchString = this.url.match(this.paramMatchingRegex) || [];
        
        this.matcherRegex = this.createMatcherRegex(this.url);
    },
    
    /**
     * Attempts to recognize a given url string and return controller/action pair for it
     * @param {String} url The url to recognize
     * @return {Object} The matched data, or false if no match
     */
    recognize: function(url) {
        if (this.recognizes(url)) {
            var matches = this.matchesFor(url);
            
            return Ext.applyIf(matches, {
                controller: this.controller,
                action    : this.action,
                historyUrl: url
            });
        }
    },
    
    /**
     * Returns true if this Route matches the given url string
     * @param {String} url The url to test
     * @return {Boolean} True if this Route recognizes the url
     */
    recognizes: function(url) {
        return this.matcherRegex.test(url);
    },
    
    /**
     * @private
     * Returns a hash of matching url segments for the given url.
     * @param {String} url The url to extract matches for
     * @return {Object} matching url segments
     */
    matchesFor: function(url) {
        var params = {},
            keys   = this.paramsInMatchString,
            values = url.match(this.matcherRegex),
            length = keys.length,
            i;
        
        //first value is the entire match so reject
        values.shift();

        for (i = 0; i < length; i++) {
            params[keys[i].replace(":", "")] = values[i];
        }

        return params;
    },
    
    /**
     * Constructs a url for the given config object by replacing wildcard placeholders in the Route's url
     * @param {Object} config The config object
     * @return {String} The constructed url
     */
    urlFor: function(config) {
        
    },
    
    /**
     * @private
     * Takes the configured url string including wildcards and returns a regex that can be used to match
     * against a url
     * @param {String} url The url string
     * @return {RegExp} The matcher regex
     */
    createMatcherRegex: function(url) {
        /**
         * Converts a route string into an array of symbols starting with a colon. e.g.
         * ":controller/:action/:id" => [':controller', ':action', ':id']
         */
        var paramsInMatchString = this.paramsInMatchString,
            length = paramsInMatchString.length,
            i, cond, matcher;
        
        for (i = 0; i < length; i++) {
            cond    = this.conditions[paramsInMatchString[i]];
            matcher = Ext.util.Format.format("({0})", cond || "[%a-zA-Z0-9\\_\\s,]+");

            url = url.replace(new RegExp(paramsInMatchString[i]), matcher);
        }

        //we want to match the whole string, so include the anchors
        return new RegExp("^" + url + "$");
    }
});
/**
 * @author Ed Spencer
 * @class Ext.Interaction
 * @extends Ext.util.Observable
 * 
 * <p>Interactions are very simple objects that represent an action performed by specific {@link Ext.Controller} 
 * action. The must consist of the {@link #controller} and {@link #action} to be called, but can contain any other
 * data too. See {@link Ext.Dispatcher} for more details on how Interactions fit into the application ecosystem.</p>
 * 
 * <p>Interactions are an internal representation that most developers will not have much direct use for. They 
 * help provide a normalized API for controller actions - each action should simply be set up to receive an Interaction
 * object. Because Interaction objects are always created when dispatching to a controller action, it is possible to 
 * store the Interaction objects that were created in a session to perform simple analytics on how the application 
 * is used. This is not built into the framework at the moment, but is left open for custom development if needed.</p>
 * 
 * @constructor
 * @param {Object} config Options object containing at least a controller/action pair
 */
Ext.Interaction = Ext.extend(Ext.util.Observable, {
    /**
     * @cfg {String} controller The controller to dispatch to
     */
    controller: '',
    
    /**
     * @cfg {String} action The controller action to invoke
     */
    action: '',
    
    /**
     * @cfg {Array} args Any arguments to pass to the action
     */
    
    /**
     * @cfg {Object} scope Optional scope to execute the controller action in
     */
    
    /**
     * True if this Interaction has already been dispatched
     * @property dispatched
     * @type Boolean
     */
    dispatched: false,
    
    constructor: function(config) {
        Ext.Interaction.superclass.constructor.apply(this, arguments);
        
        config = config || {};
              
        Ext.applyIf(config, {
            scope: this
        });
        
        Ext.apply(this, config);
        
        if (typeof this.controller == 'string') {
            this.controller = Ext.ControllerManager.get(this.controller);
        }
    }
});
/**
 * @author Ed Spencer
 * @class Ext.Application
 * @extends Ext.util.Observable
 *
 * <p>Represents a Sencha Application. Most Applications consist of at least the application's name and a launch
 * function:</p>
 *
<pre><code>
new Ext.Application({
    name: 'MyApp',

    launch: function() {
        this.viewport = new Ext.Panel({
            fullscreen: true,

            id    : 'mainPanel',
            layout: 'card',
            items : [
                {
                    html: 'Welcome to My App!'
                }
            ]
        });
    }
});
</code></pre>
 *
 * <p>Instantiating a new application automatically creates a global variable using the configured {@link #name}
 * property and sets up namespaces for views, stores, models and controllers within the app:</p>
 *
<pre><code>
//this code is run internally automatically when creating the app
{@link Ext.ns}('MyApp', 'MyApp.views', 'MyApp.stores', 'MyApp.models', 'MyApp.controllers');
</code></pre>
 *
 * <p>The launch function usually creates the Application's Viewport and runs any actions the Application needs to
 * perform when it boots up. The launch function is only expected to be run once.</p>
 *
 * <p><u>Routes and history support</u></p>
 *
 * <p>Sencha Applications provide in-app deep linking and history support, allowing your users both to use the back
 * button inside your application and to refresh the page and come back to the same screen even after navigating.
 * In-app history support relies on the Routing engine, which maps urls to controller/action pairs. Here's an example
 * route definition:</p>
 *
<pre><code>
//Note the # in the url examples below
Ext.Router.draw(function(map) {
    //maps the url http://mydomain.com/#dashboard to the home controller's index action
    map.connect('dashboard', {controller: 'home', action: 'index'});

    //fallback route - would match routes like http://mydomain.com/#users/list to the 'users' controller's
    //'list' action
    map.connect(':controller/:action');
});
</code></pre>
 *
 * <p>If you generated your Sencha app using the Sencha Command application generator script, you'll see this file is
 * already present in your application's app/routes.js file. History-driven apps can specify the {@link #defaultUrl}
 * configuration option, which will dispatch to that url if no url is currently set:</p>
 *
<pre><code>
new Ext.Application({
    name: 'MyApp',
    defaultUrl: 'dashboard'
});
</code></pre>
 *
 * <p><u>Application profiles</u></p>
 *
 * <p>Applications support multiple app profiles and reconfigure itself accordingly. Here we set up an Application
 * with 3 profiles - one if the device is a phone, one for tablets in landscape orientation and one for tablets in
 * portrait orientation:</p>
 *
<pre><code>
new Ext.Application({
    name: 'MyApp',

    profiles: {
        phone: function() {
            return Ext.is.Phone;
        },
        tabletPortrait: function() {
            return Ext.is.Tablet && Ext.orientation == 'portrait';
        },
        tabletLandscape: function() {
            return Ext.is.Tablet && Ext.orientation == 'landscape';
        }
    }
});
</code></pre>
 *
 * <p>When the Application checks its list of profiles, the first function that returns true becomes the current profile.
 * The Application will normally automatically detect when a profile change has occurred (e.g. if a tablet is rotated
 * from portrait to landscape mode) and fire the {@link #profilechange} event. It will also by default inform all
 * {@link Ext.Component Components} on the page that the current profile has changed by calling their
 * {@link Ext.Component#setProfile setProfile} functions. The setProfile function is left as an empty function for you
 * to implement if your component needs to react to different device/application profiles.</p>
 *
 * <p>The current profile can be found using {@link #getProfile}. If the Application does not correctly detect device
 * profile changes, calling the {@link #determineProfile} function will force it to re-check.</p>
 */
Ext.Application = Ext.extend(Ext.util.Observable, {
    /**
     * @cfg {String} name The name of the Application. This should be the same as the single global variable that the
     * application uses, and should not contain spaces
     */

    /**
     * @cfg {Object} scope The scope to execute the {@link #launch} function in. Defaults to the Application
     * instance.
     */
    scope: undefined,

    /**
     * @cfg {Boolean} useHistory True to automatically set up Ext.History support (defaults to true)
     */
    useHistory: true,

    /**
     * @cfg {String} defaultUrl When the app is first loaded, this url will be redirected to. Defaults to undefined
     */

    /**
     * @cfg {Boolean} autoUpdateComponentProfiles If true, automatically calls {@link Ext.Component#setProfile} on
     * all components whenever a application/device profile change is detected (defaults to true)
     */
    autoUpdateComponentProfiles: true,

    /**
     * @cfg {Boolean} setProfilesOnLaunch If true, determines the current application profile on launch and calls
     * {@link #updateComponentProfiles}. Defaults to true
     */
    setProfilesOnLaunch: true,

    /**
     * @cfg {Object} profiles A set of named profile specifications that this application supports. See the intro
     * docs for an example
     */

    constructor: function(config) {
        this.addEvents(
            /**
             * @event launch
             * Fires when the application is launched
             * @param {Ext.Application} app The Application instance
             */
            'launch',

            /**
             * @event beforeprofilechange
             * Fires when a change in Application profile has been detected, but before any action is taken to
             * update the application's components about the change. Return false from any listener to cancel the
             * automatic updating of application components (see {@link #autoUpdateComponentProfiles})
             * @param {String} profile The name of the new profile
             * @param {String} oldProfile The name of the old profile (may be undefined)
             */
            'beforeprofilechange',

            /**
             * @event profilechange
             * Fires when a change in Applicatino profile has been detected and the application's components have
             * already been informed. Listeners can perform additional processing here if required
             * @param {String} profile The name of the new profile
             * @param {String} oldProfile The name of the old profile (may be undefined)
             */
            'profilechange'
        );

        Ext.Application.superclass.constructor.call(this, config);

        this.bindReady();

        var name = this.name;

        if (name) {
            window[name] = this;

            Ext.ns(
                name,
                name + ".views",
                name + ".stores",
                name + ".models",
                name + ".controllers"
            );
        }

        if (Ext.addMetaTags) {
            Ext.addMetaTags(config);
        }
    },

    /**
     * @private
     * We bind this outside the constructor so that we can cancel it in the test environment
     */
    bindReady : function() {
        Ext.onReady(this.onReady, this);
    },

    /**
     * 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,

    /**
     * @cfg {Boolean/String} useLoadMask True to automatically remove an application loading mask when the
     * DOM is ready. If set to true, this expects a div called "loading-mask" to be present in the body.
     * Pass the id of some other DOM node if using a custom loading mask element. Defaults to false.
     */
    useLoadMask: false,

    /**
     * @cfg {Number} loadMaskFadeDuration The number of milliseconds the load mask takes to fade out. Defaults to 1000
     */
    loadMaskFadeDuration: 1000,

    /**
     * @cfg {Number} loadMaskRemoveDuration The number of milliseconds until the load mask is removed after starting the
     * {@link #loadMaskFadeDuration fadeout}. Defaults to 1050.
     */
    loadMaskRemoveDuration: 1050,

    /**
     * @cfg {Boolean} autoInitViewport Will automatically set up the application to work in full screen mode by calling
     * {@link Ext.Viewport#init} if true (defaults to true)
     */
    autoInitViewport: true,

    /**
     * Dispatches to a given controller/action combo with optional arguments.
     * @param {Object} options Object containing strings referencing the controller and action to dispatch
     * to, plus optional args array
     * @return {Boolean} True if the controller and action were found and dispatched to, false otherwise
     */
    dispatch: function(options) {
        return Ext.dispatch(options);
    },

    /**
     * @private
     * Initializes the loading mask, called automatically by onReady if {@link #useLoadMask} is configured
     */
    initLoadMask: function() {
        var useLoadMask = this.useLoadMask,
            defaultId   = 'loading-mask',
            loadMaskId  = typeof useLoadMask == 'string' ? useLoadMask : defaultId;

        if (useLoadMask) {
            if (loadMaskId == defaultId) {
                Ext.getBody().createChild({id: defaultId});
            }

            var loadingMask  = Ext.get('loading-mask'),
                fadeDuration = this.loadMaskFadeDuration,
                hideDuration = this.loadMaskRemoveDuration;

            Ext.defer(function() {
                loadingMask.addCls('fadeout');

                Ext.defer(function() {
                    loadingMask.remove();
                }, hideDuration);
            }, fadeDuration);
        }
    },

    /**
     * @private
     */
    onBeforeLaunch: function() {
        var History    = Ext.History,
            useHistory = History && this.useHistory,
            profile    = this.determineProfile(true);

        if (useHistory) {
            this.historyForm = Ext.getBody().createChild({
                id    : 'history-form',
                cls   : 'x-hide-display',
                style : 'display: none;',
                tag   : 'form',
                action: '#',
                children: [
                    {
                        tag: 'div',
                        children: [
                            {
                                tag : 'input',
                                id  : History.fieldId,
                                type: 'hidden'
                            },
                            {
                                tag: 'iframe',
                                id : History.iframeId
                            }
                        ]
                    }
                ]
            });

            History.init();
            History.on('change', this.onHistoryChange, this);

            var token = History.getToken();

            if (this.launch.call(this.scope || this, profile) !== false) {
                Ext.redirect(token || this.defaultUrl || {controller: 'application', action: 'index'});
            }
        } else {
            this.launch.call(this.scope || this, profile);
        }

        this.launched = true;

        this.fireEvent('launch', this);

        if (this.setProfilesOnLaunch) {
            this.updateComponentProfiles(profile);
        }
    },

    /**
     * @private
     * Called when the DOM is ready. Calls the application-specific launch function and dispatches to the
     * first controller/action combo
     */
    onReady: function() {
        if (this.useLoadMask) {
            this.initLoadMask();
        }

        Ext.EventManager.onOrientationChange(this.determineProfile, this);

        if (this.autoInitViewport) {
            Ext.Viewport.init(this.onBeforeLaunch, this);
        } else {
            this.onBeforeLaunch();
        }

        return this;
    },

    /**
     * Calls each configured {@link #profile} function, marking the first one that returns true as the current
     * application profile. Fires the 'beforeprofilechange' and 'profilechange' events if the profile has changed
     * @param {Boolean} silent If true, the events profilechange event is not fired
     */
    determineProfile: function(silent) {
        var currentProfile = this.currentProfile,
            profiles       = this.profiles,
            name;

        for (name in profiles) {
            if (profiles[name]() === true) {
                if (name != currentProfile && this.fireEvent('beforeprofilechange', name, currentProfile) !== false) {
                    if (this.autoUpdateComponentProfiles) {
                        this.updateComponentProfiles(name);
                    }

                    if (silent !== true) {
                        this.fireEvent('profilechange', name, currentProfile);
                    }
                }

                this.currentProfile = name;
                break;
            }
        }

        return this.currentProfile;
    },

    /**
     * @private
     * Sets the profile on every component on the page. Will probably refactor this to something less hacky.
     * @param {String} profile The new profile name
     */
    updateComponentProfiles: function(profile) {
        Ext.ComponentMgr.each(function(key, component){
            if (component.setProfile) {
                component.setProfile(profile);
            }
        });
    },

    /**
     * Gets the name of the currently-detected application profile
     * @return {String} The profile name
     */
    getProfile: function() {
        return this.currentProfile;
    },

    /**
     * @private
     */
    onHistoryChange: function(token) {
        return Ext.redirect(token);
    }
});
/**
 * @class Ext.ApplicationManager
 * @extends Ext.AbstractManager
 * @singleton
 * @ignore
 */
Ext.ApplicationManager = new Ext.AbstractManager({
    register: function(name, options) {
        if (Ext.isObject(name)) {
            options = name;
        } else {
            options.name = name;
        }
        
        var application = new Ext.Application(options);
        
        this.all.add(application);
        
        this.currentApplication = application;
        
        return application;
    }
});

/**
 * Shorthand for {@link Ext.ApplicationManager#register}
 * Creates a new Application class from the specified config object. See {@link Ext.Application} for full examples.
 * 
 * @param {Object} config A configuration object for the Model you wish to create.
 * @return {Ext.Application} The newly created Application
 * @member Ext
 * @method regApplication
 */
Ext.regApplication = function() {
    return Ext.ApplicationManager.register.apply(Ext.ApplicationManager, arguments);
};
;
Ext.ns('OrderEze.views');

Ext.regApplication('OrderEze', {
    //    defaultTarget: "viewport",
    defaultUrl: 'home/index',
    name: "OrderEze",
    useHistory: true,
    useLoadMask: true,

    launch: function () {
        // these values are defined globally on the Default.aspx page
        OrderEze.settings = OrderEzeSettings;
        if (typeof (OrderEzeCartSettings) !== 'undefined')
            OrderEze.cartSettings = OrderEzeCartSettings;

        Eze.log('app launch');

        Eze.bustCache();

        mapboxgl.accessToken = OrderEze.settings.mapKey;

        // Fix iOS 11.3 scrolling bug
        document.addEventListener('touchmove', function (e) {
            e.preventDefault();
        }, { passive: false });

        // Fix websites viewed with Google App on iOS
        if (!Ext.is.Android) {
            document.body.style.position = 'absolute';
            document.body.style.bottom = '0px';
        }

        this.viewport = new OrderEze.Viewport({
            application: this
        });
        Eze.log('app launched');
    }
});;
Ext.ns('Eze');

Eze = {
    history: [],
    // set in Viewport.initComponent() function
    viewport: null,

    nav: function (controller, action, options) {
        Eze.log('currPage ' + Ext.History.getToken());
        Eze.log(Eze.history);

        var config = options || {};
        config.controller = controller;
        config.action = action || 'index';
        config.historyUrl = config.controller + '/' + config.action;
        if (config.id) config.historyUrl += '/' + config.id;
        config.internalNav = true;

        Eze.log('navigating to #' + config.historyUrl);

        Ext.dispatch(config);
    },
    navToUrl: function (url, options) {
        Eze.log('navigating to ' + url);
        if (url.substr(0, 1) != '#') return;
        url = url.substr(1);
        var parts = url.split('/');
        if (parts.length < 2) return;
        var config = options || {};
        if (parts.length >= 3) config.id = parts[2];
        Eze.nav(parts[0], parts[1], config);
        Eze.log(parts);
    },
    trackPageView: function () {
        Eze.history.push('#' + Ext.History.getToken());
    },
    navBack: function (defaultPage) {
        Eze.history.pop(); // discard the current page in history
        var prevPage = Eze.history.pop(); // pop and go to the prev page
        Eze.log('prev page = ' + prevPage);
        Eze.log('defaultPage = ' + defaultPage);
        if (typeof (defaultPage) !== 'string' || defaultPage.charAt(0) !== '#') defaultPage = null;
        Eze.navToUrl(prevPage || defaultPage || '#home/index', { direction: 'right' });
    },
    showMask: function (message) {
        Eze.mask = new Ext.LoadMask(Ext.getBody(), { msg: message });
        Eze.mask.show();
        var el = Ext.Element.data(Eze.mask.el.dom, 'mask');
        if (el) {
            Eze.log('adding super-mask');
            el.addCls('super-mask');
        } else {
            Eze.log('couldn\'t add super-mask');
        }
    },
    hideMask: function () {
        if (Eze.mask) {
            Eze.mask.hide();
        }
    },
    showListMask: function (listitem) {
        Eze.currentLoadingItem = listitem;
        Ext.fly(Eze.currentLoadingItem).addCls('item-loading');
    },
    hideListMask: function () {
        if (Eze.currentLoadingItem) {
            Ext.fly(Eze.currentLoadingItem).removeCls('item-loading');
        }
    },
    alert: function (title, message) {
        Ext.Msg.show({
            cls: 'msgbox-over',
            title: title,
            msg: message,
            btns: Ext.MessageBox.OK
        });
    },
    load: function (type, action, params, callback, scope, dontShowMask) {
        if (!dontShowMask) Eze.showMask('Please wait...');
        if (Ext.isFunction(params)) {
            // params arg can be ommited, so swap vars around
            scope = callback;
            callback = params;
            params = {};
        }
        Eze.log('Eze.load ' + type + '/' + action);
        var args = Ext.isObject(params) ? Ext.encode(params) : params;
        Eze.log(args);

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: type, action: action, args: args },
            timeout: 120000,
            callback: function (opts, success, response) {
                Eze.hideMask();
                Eze.hideListMask();
                var error = 'There was a problem loading the data';
                if (success) {
                    var json = Ext.decode(response.responseText);
                    Eze.log('Eze.load callback');
                    Eze.log(json);
                    if (json.success) {
                        if (callback) callback.call(scope || this, json.Data);
                        return;
                    }
                    error = json.GeneralError;
                }
                Eze.alert('', error);
            },
            scope: this
        });
    },
    log: function (obj) {
        if (OrderEze.settings.debug) {
            console.log(obj);    
        }
    },
    bustCache: function () {
        Ext.Ajax.on('beforerequest', function (conn, options) {
            Eze.log('*** ', options);
            conn.useDefaultXhrHeader = false;
            options.url += '?_=' + (new Date().getTime());
            if (options.params) options.url += '&a=' + options.params.type + '-' + options.params.action;
        });
    }
};;
/**
 * The routes.js file connects local urls (e.g. http://mydomain.com/#someUrl) to a controller action.
 * This allows the application to reinitialize itself if it is refreshed, and provides in-application
 * history support. Sample usage:
 * 
 * Connects http://myapp.com/#home to the index controller's overview action:
 * map.connect("home", {controller: 'index', action: 'overview'});
 * 
 * Connects urls like "images/myImage.jpg" to the images controller's show action, passing
 * "myImage.jpg" as the "url" property of the options object each controller action receives:
 * map.connect("images/:url", {controller: 'images', action: 'show'});
 * 
 * Your app.js file can specify a defaultUrl config property, which will be dispatched to if there is
 * no specified url when the application first loads.
 */
Ext.Router.draw(function(map) {
    
    
    //These are default fallback routes and can be removed if not needed
    map.connect(':controller/:action');
    map.connect(':controller/:action/:id');
//    map.connect(':controller/:action/:id/:arg1');
//    map.connect(':controller/:action/:id/:arg1/:arg2');
//    map.connect(':controller/:action/:id/:arg1/:arg2/:arg3');
});;
Ext.regController("account", {

    index: function(options) {
        Eze.trackPageView();
        Eze.log('controller account/index ' + options.id);

//        if (this.indexView && !options.refresh) {
//            // used cached view
//            this.application.viewport.setActiveItem(this.indexView, options.direction ? { type: 'slide', direction: options.direction} : null);
//            return;
//        }

        Eze.load('account', 'index', options.phoneSearch, function (data) {

            this.indexView = this.render({
                    xtype: 'account-index',
                    pageData: data,
                    listeners: {
                        deactivate: function(v) {
                            if (v.destroyMe) {
                                v.destroy();
                                this.indexView = null;
                            }
                        },
                        scope: this
                    }
                });

            Eze.log('controller account/index after render');
            this.application.viewport.setActiveItem(this.indexView, options.direction ? { type: 'slide', direction: options.direction} : null);
            Eze.log('controller account/index after active');
        }, this);
    },

    order: function(options) {
        Eze.trackPageView();
        Eze.log('controller account/order ' + options.id);

        Eze.load('account', 'order', parseInt(options.id) || 0, function (data) {
            if (data.status == "ERROR") return Eze.nav('account');
            
            this.orderView = this.render({
                    xtype: 'account-order',
                    pageData: data,
                    listeners: {
                        deactivate: function(v) {
                            v.destroy();
                        },
                        scope: this
                    }
                });

            Eze.log('controller account/order after render');
            this.application.viewport.setActiveItem(this.orderView, { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller account/order after active');
        }, this, true);
    }

});
;
Ext.regController("cart", {
    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller cart/index ' + options.id);

        Eze.load('cart', 'index', function (data) {
            Eze.utils.cartItemCount = data.itemCount;
            var view = this.render({
                xtype: 'cart-index',
                pageData: data,
                listeners: {
                    deactivate: function (v) {
                        v.destroy();
                    },
                    scope: this
                }
            });

            Eze.log('controller cart/index after render');

            this.application.viewport.setActiveItem(view);
            Eze.hideListMask();

            Eze.log('controller cart/index after active');

        }, this);
    }

});
;
Ext.regController("checkout", {

    index: function (options) {
        if (!OrderEze.settings.isLoggedIn || Eze.utils.cartItemCount <= 0) return Eze.nav('cart');

        Eze.trackPageView();
        Eze.log('controller checkout/index ' + options.id);

        if (this.indexView) {
            // used cached view
            Eze.log('controller checkout/index ' + options.id + ' (cached)');
            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
            return;
        }

        Eze.load('checkout', 'index', function (data) {
            if (data.status == "ERROR") return Eze.nav('cart');

            this.indexView = this.render({
                xtype: 'checkout-index',
                pageData: data,
                listeners: {
                    deactivate: function (v) {
                        if (v.destroyMe) {
                            v.destroy();
                            this.indexView = null;
                        }
                    },
                    scope: this
                }
            });

            Eze.log('controller checkout/index after render');
            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller checkout/index after active');
        }, this);
    },

    delivery: function (options) {
        if (!OrderEze.settings.isLoggedIn || Eze.utils.cartItemCount <= 0) return Eze.nav('cart');

        Eze.trackPageView();
        Eze.log('controller checkout/delivery ' + options.id);

        if (this.deliveryView) {
            // used cached view
            Eze.log('controller checkout/delivery ' + options.id + ' (cached)');
            this.application.viewport.setActiveItem(this.deliveryView, { type: 'slide', direction: options.direction || 'left' });
            return;
        }

        Eze.load('checkout', 'delivery', function (data) {
            if (data.status == "ERROR") return Eze.nav('cart');

            this.deliveryView = this.render({
                xtype: 'checkout-delivery',
                pageData: data,
                listeners: {
                    deactivate: function (v) {
                        if (v.destroyMe) {
                            v.destroy();
                            this.deliveryView = null;
                        }
                    },
                    scope: this
                }
            });

            Eze.log('controller checkout/delivery after render');
            this.application.viewport.setActiveItem(this.deliveryView, { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller checkout/delivery after active');
        }, this);
    },

    deliverysl: function (options) {
        if (!OrderEze.settings.isLoggedIn || Eze.utils.cartItemCount <= 0) return Eze.nav('cart');

        Eze.trackPageView();
        Eze.log('controller checkout/deliverysl ' + options.id);

        if (this.deliveryView) {
            // used cached view
            Eze.log('controller checkout/deliverysl ' + options.id + ' (cached)');
            this.application.viewport.setActiveItem(this.deliveryView, { type: 'slide', direction: options.direction || 'left' });
            return;
        }

        Eze.load('checkout', 'deliverysl', function (data) {
            if (data.status == "ERROR") return Eze.nav('cart');

            this.deliveryView = this.render({
                xtype: 'checkout-deliverysl',
                pageData: data,
                listeners: {
                    deactivate: function (v) {
                        if (v.destroyMe) {
                            v.destroy();
                            this.deliveryView = null;
                        }
                    },
                    scope: this
                }
            });

            Eze.log('controller checkout/deliverysl after render');
            this.application.viewport.setActiveItem(this.deliveryView, { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller checkout/deliverysl after active');
        }, this);
    },

    payment: function (options) {
        if (!OrderEze.settings.isLoggedIn || Eze.utils.cartItemCount <= 0) return Eze.nav('cart');

        Eze.trackPageView();
        Eze.log('controller checkout/payment ' + options.id);

        if (this.paymentView) {
            // used cached view
            this.application.viewport.setActiveItem(this.paymentView, { type: 'slide', direction: options.direction || 'left' });
            return;
        }

        Eze.load('checkout', 'payment', function (data) {
            if (data.status == "ERROR") return Eze.nav('cart');

            this.paymentView = this.render({
                xtype: 'checkout-payment',
                pageData: data,
                listeners: {
                    deactivate: function (v) {
                        if (v.destroyMe) {
                            v.destroy();
                            this.paymentView = null;
                        }
                    },
                    scope: this
                }
            });

            Eze.log('controller checkout/payment after render');
            this.application.viewport.setActiveItem(this.paymentView, { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller checkout/payment after active');
        }, this);
    },

    guest: function (options) {
        if (!OrderEze.settings.isLoggedIn || Eze.utils.cartItemCount <= 0) return Eze.nav('cart');

        Eze.trackPageView();
        Eze.log('controller checkout/guest ' + options.id);

        if (this.guestView) {
            // used cached view
            Eze.log('controller checkout/guest ' + options.id + ' (cached)');
            this.application.viewport.setActiveItem(this.guestView, { type: 'slide', direction: options.direction || 'left' });
            return;
        }

        Eze.load('checkout', 'guest', function (data) {
            if (data.status == "ERROR") return Eze.nav('cart');

            this.guestView = this.render({
                xtype: 'checkout-guest',
                pageData: data,
                listeners: {
                    deactivate: function (v) {
                        if (v.destroyMe) {
                            v.destroy();
                            this.guestView = null;
                        }
                    },
                    scope: this
                }
            });

            Eze.log('controller checkout/guest after render');
            this.application.viewport.setActiveItem(this.guestView, { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller checkout/guest after active');
        }, this);
    },

    summary: function (options) {
        // cartItemCount will be 0 when we redirect from processor page
        if (!OrderEze.settings.isLoggedIn || (Eze.utils.cartItemCount <= 0 && !options.id)) return Eze.nav('cart');

        // check if we are returning to this page from a credit card processor
        // if id is defined, we are returning from a processor (see ~/AuthorizeNetReturn.aspx.cs, ~/Checkout/HeartlandReturn.aspx.cs, ~/Checkout/GdpayProcess.aspx.cs)
        // options.id will be 'true' if approved otherwise 'false'
        if (options.id) {
            if (options.id == 'true') {
                // succesfully processed, submit the order with a 'true' param signifying a external processor
                Eze.load('checkout', 'summarySubmit', true, function (data) {
                    Eze.log('checkout/summary.next done');
                    Eze.navToUrl(data.nextPage);
                }, this);
                return;
            } else if (options.id == 'false') {
                // failed processing, fetch the error message from session on the server & show an alert
                Eze.load('checkout', 'cardFailedMessage', function (data) {
                    Eze.alert('Transaction failed!', data.message);
                }, this);
            }
        }
        if (Eze.utils.cartItemCount <= 0) return Eze.nav('cart');

        Eze.trackPageView();
        Eze.log('controller checkout/summary ' + options.id);

        Eze.load('checkout', 'summary', function (data) {
            if (data.status == "ERROR") return Eze.nav('cart');

            this.summaryView = this.render({
                xtype: 'checkout-summary',
                pageData: data,
                listeners: {
                    deactivate: function (v) {
                        v.destroy();
                    },
                    scope: this
                }
            });

            Eze.log('controller checkout/summary after render');
            this.application.viewport.setActiveItem(this.summaryView, options.id ? null : { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller checkout/summary after active');
        }, this);
    },

    complete: function (options) {
        if (!OrderEze.settings.isLoggedIn) return Eze.nav('cart');

        Eze.log('controller checkout/complete ' + options.id);

        Eze.load('checkout', 'complete', parseInt(options.id), function (data) {
            if (data.status == "ERROR") return Eze.nav('cart');
            Eze.utils.cartItemCount = 0;

            var view = this.render({
                xtype: 'checkout-complete',
                pageData: data,
                listeners: {
                    deactivate: function (v) {
                        v.destroy();
                    },
                    scope: this
                }
            });

            Eze.log('controller checkout/complete after render');
            this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller checkout/complete after active');
        }, this);
    }

});
;
Ext.regController("contact", {

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller contact/index ' + options.id);

        var view = this.render({
            xtype: 'contact-index',
            pageData: { locationId: options.id },
            listeners: {
                deactivate: function (v) {
                    v.destroy();
                }
            }
        });
        Eze.log('controller contact/index after render');
        
        this.application.viewport.setActiveItem(view, {
            type: 'slide',
            direction: options.direction || 'left'
        });
        
        Eze.log('controller contact/index after active');

        Eze.hideListMask();        
    }
});
;
Ext.regController("events", {
    destroyMe: false,

    index: function (options) {
        Eze.trackPageView();
        Eze.log(options);
        if (this.indexView) {
            // used cached view
            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
            Eze.hideListMask();
            return;
        }

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'events', action: 'index', calendarId: options.id },
            callback: function (opts, success, response) {
                var items = [];
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        items = json.Data;
                    }
                }

                this.indexView = this.render({
                    xtype: 'events-index',
                    calendarId: options.id,
                    events: items,
                    listeners: {
                        deactivate: function (v) {
                            if (v.destroyMe) {
                                v.destroy();
                                this.indexView = null;
                            }
                        },
                        scope: this
                    }
                });

                this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
                Eze.hideListMask();
            },
            scope: this
        });
    },

    details: function (options) {
        Eze.trackPageView();
        Eze.log('controller Events/details ' + options.calendarId);

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'events', action: 'details', eventId: options.id },
            callback: function (opts, success, response) {
                var data = null;
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success && json.Data.length > 0) {
                        data = json.Data[0];
                    }
                }

                var view = this.render({
                    xtype: 'events-details',
                    calendarId: data.calendarId,
                    eventDetails: data,
                    listeners: {
                        deactivate: function (v) {
                            v.destroy();
                        }
                    }
                });
                Eze.log('controller Events/details after render ');
                this.application.viewport.setActiveItem(view, {
                    type: 'slide',
                    direction: 'left'
                });
                Eze.log('controller Events/details after active ');

                Eze.hideListMask();
            },
            scope: this
        });

    }
});;
Ext.regController("favs", {

    index: function(options) {
        Eze.trackPageView();
        Eze.log('controller favs/index ' + options.id);

//        if (this.indexView) {
//            // used cached view
//            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
//            return;
//        }
//
        Eze.load('favs', 'index', function(data) {
            
            this.indexView = this.render({
                    xtype: 'favs-index',
                    pageData: data,
                    listeners: {
                        deactivate: function(v) {
                            v.destroy();
                        },
                        scope: this
                    }
                });

            Eze.log('controller favs/index after render');
            this.application.viewport.setActiveItem(this.indexView);
            Eze.log('controller favs/index after active');
        }, this);
    }


});
;
Ext.regController("form", {

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller form/index ' + options.id);

        if (this.indexView) {
            // used cached view
            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
            Eze.hideListMask();
            return;
        }

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'form', action: 'index', formId: options.id },
            callback: function (opts, success, response) {
                var data = [], name = 'Contact Form', topTextData = '', bottomTextData = '';
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        name = json.Data.name;
                        data = json.Data.formFields;
                        topTextData = json.Data.topText;
                        bottomTextData = json.Data.bottomText;
                    }
                }

                this.indexView = this.render({
                    xtype: 'form-index',
                    formName: name,
                    formFields: data,
                    topText: topTextData,
                    bottomText: bottomTextData,
                    formId: options.id,
                    listeners: {
                        deactivate: function (v) {
                            v.destroy();
                            this.indexView = null;
                        },
                        click: {
                            element: 'el',
                            fn: function () {
                                var combos = Ext.ComponentQuery.query("datepickerfield");
                                for (var i = 0; i < combos.length; i++) {
                                    if (combos[i].value == null)
                                        combos[i].getDatePicker().setValue(new Date(new Date().getFullYear(), 0, 1));
                                }
                            }
                        },
                        scope: this
                    }
                });

                Eze.log('controller form/index after render');

                this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
                Eze.hideListMask();

                Eze.log('controller form/index after active');
            },
            scope: this
        });

    }

});
;
Ext.regController("home", {

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller home/index');
        if (!this.mainPanel) {
            this.mainPanel = this.render({
                xtype: 'home-index'
            });

            this.application.viewport.setActiveItem(this.mainPanel);
            Eze.log(' created mainPanel ...');
        } else {
            Eze.log(options);
            this.application.viewport.setActiveItem(this.mainPanel, { type: 'slide', direction: 'right' });
            Eze.log(' shown mainPanel ...');
        }
    }
});;
Ext.regController("locations", {

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller locations/index ' + options.id);

        if (this.indexView) {
            // used cached view
            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
            Eze.hideListMask();
            return;
        }
        
        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'locations', action: 'index' },
            callback: function (opts, success, response) {
                var data = [];
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        data = json.Data;
                    }
                }

                this.indexView = this.render({
                    xtype: 'locations-index',
                    pageData: data,
                    listeners: {
                        deactivate: function (v) {
                            if (v.destroyMe) {
                                v.destroy();
                                this.indexView = null;
                            }
                        },
                        scope: this
                    }
                });

                Eze.log('controller locations/index after render');

                this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
                Eze.hideListMask();

                Eze.log('controller locations/index after active');
            },
            scope: this
        });
    },

    details: function (options) {
        Eze.trackPageView();
        Eze.log('controller locations/details ' + options.id);
        
        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'locations', action: 'details', locationId: options.id },
            callback: function (opts, success, response) {
                var data = {};
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        data = json.Data;
                    }
                }

                if (data.latitude && data.longitude) {
                    this.showLocationDetails([data.longitude, data.latitude], data, options);
                    return;
                } 

                var that = this;

                this.mapboxClient = mapboxSdk({ accessToken: mapboxgl.accessToken });

                this.mapboxClient.geocoding.forwardGeocode({ query: data.addressString, limit: 1 }).send().then((response) => {
                    var mapCenter = response.body.features[0].center;

                    that.showLocationDetails(mapCenter, data, options);

                });
            },
            scope: this
        });
    },

    showLocationDetails: function(mapCenter, data, options) {
        var view = this.render({
            xtype: 'locations-details',
            pageData: data,
            mapCenter: mapCenter,
            listeners: {
                deactivate: function (v) {
                    v.destroy();
                },
                scope: this
            }
        });

        Eze.log('controller locations/details after render');

        this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
        Eze.hideListMask();

        Eze.log('controller locations/details after active');
    }

});
;
Ext.regController("mailing", {

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller mailing/index ' + options.id);

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'mailing', action: 'index' },
            callback: function (opts, success, response) {
                var data = { formFields: [{ html: '<div class="empty">There was a problem loading the mailing list form. <br />Please try again.</div>' }] };
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        data = json.Data;
                    }
                }

                var view = this.render({
                    xtype: 'mailing-index',
                    pageData: data,
                    listeners: {
                        deactivate: function (v) {
                            v.destroy();
                        },
                        click: {
                            element: 'el',
                            fn: function () {
                                var combos = Ext.ComponentQuery.query("datepickerfield");
                                for (var i = 0; i < combos.length; i++) {
                                    if (combos[i].value == null)
                                        combos[i].getDatePicker().setValue(new Date(new Date().getFullYear(), 0, 1));
                                }
                            }
                        },
                        scope: this
                    }
                });

                Eze.log('controller mailing/index after render');

                this.application.viewport.setActiveItem(view, {
                    type: 'slide',
                    direction: options.direction || 'left'
                });

                Eze.log('controller mailing/index after active');

                Eze.hideListMask();
            },
            scope: this
        });


    }

});
;
Ext.regController("map", {

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller map/index ' + options.id);

        if (OrderEze.settings.longitude && OrderEze.settings.latitude) {
            this.showMapPage([OrderEze.settings.longitude, OrderEze.settings.latitude], options);
            return;
        }
        
        var that = this;
        
        var mapboxClient = mapboxSdk({ accessToken: mapboxgl.accessToken });

        mapboxClient.geocoding.forwardGeocode({ query: OrderEze.settings.addressString, limit: 1 }).send().then((response) => {
            var mapCenter = response.body.features[0].center;

            that.showMapPage(mapCenter, options);
        }, (error) => { alert('There was a problem finding the address'); });
    },

    showMapPage: function (mapCenter, options) {
        var view = this.render({
            xtype: 'map-index',
            mapCenter: mapCenter
        });

        Eze.log('controller map/index after render');
        this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
        Eze.log('controller map/index after active');

        Eze.hideListMask();
    }
});;
Ext.regController("menu", {
    destroyAll: false,
    deptViews: {},
    deptViewIds: [],

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller menu/index ' + options.id);
        var deptId = options.id || 1;
        var view = this.deptViews['deptView' + deptId];

        if (view) {
            // used cached view
            Eze.log('controller menu/index ' + deptId + ' cached');
            // change order of deptViewIds so that this view is last and less likely to be removed from cache
            this.moveViewToBackOfCache('deptView' + deptId);
            this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
            Eze.hideListMask();
            return;
        }

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'menu', action: 'index', deptId: deptId },
            callback: function (opts, success, response) {
                var deptData = {};
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        deptData = json.Data;
                    }
                }

                view = this.render({
                    xtype: 'menu-index',
                    deptId: deptId,
                    deptData: deptData,
                    listeners: {
                        deactivate: this.cleanupViews,
                        scope: this
                    }
                });

                // view caching
                this.deptViews['deptView' + deptId] = view;
                this.deptViewIds.push('deptView' + deptId);

                Eze.log('controller menu/index after render');
                Eze.log(this.deptViews);

                this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
                Eze.hideListMask();

                Eze.log('controller menu/index after active');
            },
            scope: this
        });
    },
    cleanupViews: function (v) {
        if (v.destroyAll) {
            // delete all menu views when going back to home screen
            for (var i = 0; i < this.deptViewIds.length; i++) {
                var id = this.deptViewIds[i];
                Eze.log('controller menu/index ' + this.deptViews[id].deptId + ' destroying');
                this.deptViews[id].destroy();
                delete this.deptViews[id];
            }
            this.deptViewIds = [];
        } else if (this.deptViewIds.length > 10) {
            // only cache 10 menu views
            var id = this.deptViewIds.shift();
            Eze.log('controller menu/index ' + this.deptViews[id].deptId + ' destroying');
            this.deptViews[id].destroy();
            delete this.deptViews[id];
        }
        Eze.log(this.deptViews);
        Eze.log(this.deptViewIds);
    },
    moveViewToBackOfCache: function (viewId) {
        var index = -1;
        for (var i = 0; i < this.deptViewIds.length; i++) {
            if (this.deptViewIds[i] == viewId) {
                index = i;
                break;
            }
        }
        if (index != -1) {
            this.deptViewIds.splice(index, 1);
            this.deptViewIds.push(viewId);
        }
    }
});
;
Ext.regController("menupro", {
    menus: [],
    showImages: true,
    showPrices: true,

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller menupro/index');

        if (this.indexView) {
            // used cached view
            Eze.log('controller menupro/index (cached)');
            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
            return;
        }

        Eze.load('menupro', 'index', function (data) {
            Eze.log(data);
            this.showImages = data.showImages;
            this.showPrices = data.showPrices;
            this.menus = this._formatMenu(data.menus);
            Eze.log(this.menus);

            this.indexView = this.render({
                xtype: 'menupro-index',
                menus: this.menus,
                listeners: {
                    deactivate: function (v) {
                        if (v.destroyMe) {
                            v.destroy();
                            this.indexView = null;
                        }
                    },
                    scope: this
                }
            });

            Eze.log('controller menupro/index after render');
            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
            Eze.log('controller menupro/index after active');
        }, this);
        
    },

    menu: function (options) {
        Eze.trackPageView();
        Eze.hideListMask();
        Eze.log('controller menupro/menu ' + options.id);

        var menu = this._findMenu(parseInt(options.id, 10));
        if (!menu) {
            Eze.log('menu with id ' + options.id + ' not found');
            Eze.nav('menupro', 'index');
            return;
        }

        var view = this.render({
            xtype: 'menupro-menu',
            menu: menu,
            listeners: {
                deactivate: function (v) {
                    if (v.destroyMe) {
                        v.destroy();
                    }
                },
                scope: this
            }
        });

        Eze.log('controller menupro/menu after render');
        this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
        Eze.log('controller menupro/menu after active');
    },

    section: function (options) {
        Eze.trackPageView();
        Eze.hideListMask();
        Eze.log('controller menupro/section ' + options.id);

        var section = this._findSection(parseInt(options.id, 10));;
        if (!section) {
            Eze.log('section with id ' + options.id + ' not found');
            Eze.nav('menupro', 'index');
            return;
        }

        var view = this.render({
            xtype: 'menupro-section',
            section: section,
            listeners: {
                deactivate: function (v) {
                    if (v.destroyMe) {
                        v.destroy();
                    }
                },
                scope: this
            }
        });

        Eze.log('controller menupro/section after render');
        this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
        Eze.log('controller menupro/section after active');
  },

    // Return the menu item price
  _formatPrice(prices) {
    return !this.showPrices || prices[0] === 0 ? '' : '$' + parseFloat(prices[0].value).toFixed(2);
  },

    // updates the values for menu item price and image based on mobile settings
    _formatMenu(menus) {
        for (var m = 0; m < menus.length; m++) {
            var menu = menus[m];
            // update featured items
            for (var i = 0; i < menu.featuredItems.length; i++) {
              var item = menu.featuredItems[i];
              item.price = this._formatPrice(item.prices);
                item.imageHtml = this.showImages && item.imagePath
                    ? '<div class="item-img"><img src="' + item.imagePath + '" /></div>' : '';
            }
            for (var s = 0; s < menu.sections.length; s++) {
                var section = menu.sections[s];
                // update menu items
                for (var i = 0; i < section.menuItems.length; i++) {
                    var item = section.menuItems[i];
                   item.price = this._formatPrice(item.prices);
                    item.imageHtml = this.showImages && item.thumbPath
                        ? '<div class="item-img"><img src="' + item.thumbPath + '" /></div>' : '';
                }
            }
        }
        return menus;
    },

    _findMenu(menuId) {
        for (var m = 0; m < this.menus.length; m++) {
            if (this.menus[m].id === menuId) {
                return this.menus[m];
            }
        }
    },

    _findSection(sectionId) {
        for (var m = 0; m < this.menus.length; m++) {
            var menu = this.menus[m];
            for (var s = 0; s < menu.sections.length; s++) {
                if (menu.sections[s].id === sectionId) {
                    return menu.sections[s];
                }
            }
        }
    }
});;
Ext.regController("page", {

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller page/index ' + options.id);

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'page', action: 'index', pageId: options.id },
            callback: function (opts, success, response) {
                var data = [];
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        data = json.Data;
                    } else {
                        
                    }
                }

                var view = this.render({ 
                    xtype: 'page-index',
                    pageData: data,
                    listeners: {
                        deactivate: function (v) {
                            if (v.destroyMe) {
                                v.destroy();
                            }
                        },
                        scope: this
                    }
                });
                
                Eze.log('controller page/index after render');

                this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
                Eze.hideListMask();

                Eze.log('controller page/index after active');
            },
            scope: this
        });
    }

});
;
Ext.regController("photos", {
    destroyMe: false,

    index: function (options) {
        Eze.trackPageView();
        Eze.log(options);
        if (this.indexView) {
            // used cached view
            Eze.log('controller photos/index (cached)');
            this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
            Eze.hideListMask();
            return;
        }

        Eze.log('controller photos/index');
        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'photos', action: 'index' },
            callback: function (opts, success, response) {
                var items = [];
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        items = json.Data;
                    }
                }

                this.indexView = this.render({
                    xtype: 'photos-index',
                    galleries: items,
                    listeners: {
                        deactivate: function (v) {
                            if (v.destroyMe) {
                                v.destroy();
                                this.indexView = null;
                            }
                        },
                        scope: this
                    }
                });

                Eze.log('controller photos/index after render');
                this.application.viewport.setActiveItem(this.indexView, { type: 'slide', direction: options.direction || 'left' });
                Eze.hideListMask();

                Eze.log('controller photos/index after active');
            },
            scope: this
        });
    },

    show: function (options) {
        Eze.trackPageView();
        Eze.log(options);
        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'photos', action: 'images', galleryId: options.id },
            callback: function (opts, success, response) {
                var galleryName = 'Photos';
                var items = [{ cls: 'photoSlide', html: '<div class="photo"><h2>There was a problem loading the photos. Please try again.</h2></div>'}];
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        items = json.Data.photos;
                        galleryName = json.Data.galleryName;
                    }
                }

                var view = this.render({
                    xtype: 'photos-show',
                    photos: items,
                    galleryName: galleryName,
                    listeners: {
                        deactivate: function (v) {
                            v.destroy();
                        }
                    }
                });
                this.application.viewport.setActiveItem(view, { type: 'slide', direction: 'left' });

                Eze.hideListMask();
            },
            scope: this
        });
    }
});;
Ext.regController("rpage", {

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller rpage/index ' + options.id);

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'page', action: 'responsive', pageId: options.id },
            callback: function (opts, success, response) {
                var data = [];
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        data = json.Data;
                    }
                }

                var view = this.render({
                    xtype: 'rpage-index',
                    pageData: data,
                    listeners: {
                        deactivate: function (v) {
                            if (v.destroyMe) {
                                v.destroy();
                            }
                        },
                        scope: this
                    }
                });

                Eze.log('controller rpage/index after render');

                this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
                Eze.hideListMask();

                Eze.log('controller rpage/index after active');
            },
            scope: this
        });

    }

});
;
Ext.regController("takeout", {
    destroyAll: false,
    deptViews: {},
    deptViewIds: [],

    index: function (options) {
        Eze.trackPageView();
        Eze.log('controller takeout/index ' + options.id);
        Eze.log(options);
        var deptId = parseInt(options.id) || 1;
        var view = this.deptViews['deptView' + deptId];

//        if (view) {
//            // used cached view
//            Eze.log('controller takeout/index ' + deptId + ' cached');
//            // change order of deptViewIds so that this view is last and less likely to be removed from cache
//            this.moveViewToBackOfCache('deptView' + deptId);
//            if (options.fromTab) {
//                this.application.viewport.setActiveItem(view);
//            } else {
//                this.application.viewport.setActiveItem(view, { type: 'flip', direction: options.direction || 'left' });
//            }
//            Eze.hideListMask();
//            return;
//        }

        Eze.load('takeout', 'index', deptId, function (deptData) {
            Eze.utils.cartItemCount = deptData.cartItemCount;

            view = this.render({
                xtype: 'takeout-index',
                deptId: deptId,
                deptData: deptData,
                listeners: {
                    deactivate: this.cleanupViews,
                    scope: this
                }
            });

            // view caching
            this.deptViews['deptView' + deptId] = view;
            this.deptViewIds.push('deptView' + deptId);

            Eze.log('controller takeout/index after render');
            Eze.log(this.deptViews);

            if (options.animation == false) {
                this.application.viewport.setActiveItem(view);
            } else {
                this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
            }
            Eze.hideListMask();

            Eze.log('controller takeout/index after active');
        }, this, !options.fromTab);

        
//        Ext.Ajax.request({
//            url: 'json.aspx',
//            params: { type: 'takeout', action: 'index', deptId: deptId },
//            callback: function (opts, success, response) {
//                var deptData = {};
//                if (success) {
//                    var json = Ext.decode(response.responseText);
//                    if (json.success) {
//                        deptData = json.Data;
//                    }
//                }
//
//                Eze.utils.cartItemCount = deptData.cartItemCount;
//                
//                view = this.render({
//                    xtype: 'takeout-index',
//                    deptId: deptId,
//                    deptData: deptData,
//                    listeners: {
//                        deactivate: this.cleanupViews,
//                        scope: this
//                    }
//                });
//
//                // view caching
//                this.deptViews['deptView' + deptId] = view;
//                this.deptViewIds.push('deptView' + deptId);
//
//                Eze.log('controller takeout/index after render');
//                Eze.log(this.deptViews);
//
//                if (options.animation == false) {
//                    this.application.viewport.setActiveItem(view);
//                } else {
//                    this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
//                }
//                Eze.hideListMask();
//
//                Eze.log('controller takeout/index after active');
//            },
//            scope: this
//        });
    },
    cleanupViews: function (v) {
        if (v.destroyAll) {
            // delete all menu views when going back to home screen
            for (var i = 0; i < this.deptViewIds.length; i++) {
                var id = this.deptViewIds[i];
                Eze.log('controller takeout/index ' + this.deptViews[id].deptId + ' destroying');
                this.deptViews[id].destroy();
                delete this.deptViews[id];
            }
            this.deptViewIds = [];
        } else if (this.deptViewIds.length > 0) {
            // only cache 10 takeout views
            var id = this.deptViewIds.shift();
            Eze.log('controller takeout/index ' + this.deptViews[id].deptId + ' destroying');
            this.deptViews[id].destroy();
            delete this.deptViews[id];
        }
        Eze.log(this.deptViews);
        Eze.log(this.deptViewIds);
    },
    moveViewToBackOfCache: function (viewId) {
        var index = -1;
        for (var i = 0; i < this.deptViewIds.length; i++) {
            if (this.deptViewIds[i] == viewId) {
                index = i;
                break;
            }
        }
        if (index != -1) {
            this.deptViewIds.splice(index, 1);
            this.deptViewIds.push(viewId);
        }
    },
    item: function (options) {
        Eze.trackPageView();
        Eze.log('controller takeout/item ' + options.id);

        var itemId = options.id;
        if (!itemId) return;

        Ext.Ajax.request({
            url: 'json.aspx',
            params: { type: 'takeout', action: 'item', itemId: itemId },
            callback: function (opts, success, response) {
                var itemData = { name: 'Invalid Item', description: '', imageHtml: '', price: '' };
                if (success) {
                    var json = Ext.decode(response.responseText);
                    if (json.success) {
                        itemData = json.Data;
                    }
                }

                var view = this.render({
                    xtype: 'takeout-item',
                    itemData: itemData
                });

                Eze.log('controller takeout/item after render');

                this.application.viewport.setActiveItem(view, { type: 'slide', direction: options.direction || 'left' });
                Eze.hideListMask();

                Eze.log('controller takeout/item after active');
            },
            scope: this
        });
    }
});
;
Ext.regModel('Dept', {
    fields: ['deptId', 'name', 'imagePath']
});
;
Ext.regModel('Event', {
    fields: ['listId', 'name', 'description', 'dayStr', { name: 'sortOrder', type: 'int'}]
});

Ext.regModel('EventDetail', {
    fields: ['name', 'description', 'start', 'end', 'repeat', 'link', 'calendarId']
});

;
Ext.regModel('ItemOptions', {
    fields: [
        { name: 'key', type: 'string' },
        { name: 'text', type: 'string' }
    ]
});

;
Ext.regModel('Location', {
    fields: ['locationId', 'name', 'address', 'phone']
});
;
Ext.regModel('MenuItem', {
    fields: ['itemId', 'name', 'description', 'price', 'imagePath', 'availHtml']
});
;
Ext.regModel('NavItem', {
    fields: ['title', 'key', 'subkey', 'image', 'href', 'header', 'internalUrl']
});
;
Ext.regModel('Page', {
    fields: ['pageId', 'name', 'type', 'internalUrl', 'externalUrl', 'isLink']
});
;
Ext.regModel('Photo', {
    fields: [
                'name', 
                'image_url',
                { name: 'gallery_id', type: 'number' },
                { name: 'image_count', type: 'number' }
            ]
});;
Ext.regModel('ProMenu', {
    fields: ['id', 'name', 'description']
});;
Ext.regModel('ProMenuItem', {
    fields: ['id', 'name', 'description', 'price', 'imagePath']
});
;
Ext.regModel('ProSection', {
    fields: ['id', 'name', 'description']
});;
Ext.ux.EditableList = Ext.extend(Ext.List, {
    inEditMode: false,
    inSwipeMode: false,
    initComponent: function () {
        var me = this;
        var tmpTpl, tplParams;
        // define the template for our delete-able list items
        tmpTpl = '<div class="x-list-delete">' +
                    '<div class="x-list-delete-icon multi-delete"></div>' +
                    '<div class="x-list-delete-content">{listtpl}</div>' +
                    '<div class=" x-component" style="-webkit-box-flex: 1;"></div>' +
                    '<div class="x-button x-button-decline-small  x-list-delete-button x-hidden">' +
                        '<span class="x-button-label">Delete</span></span>' +
                    '</div>' +
                 '</div>'
        tplParams = {
            listtpl: (Ext.isString(this.itemTpl) ? this.itemTpl : this.itemTpl.html)
        };
        // create new xtemplate
        this.deleteTpl = new Ext.XTemplate(tmpTpl);
        // apply any existing templates to our new one
        this.itemTpl = this.deleteTpl.apply(tplParams);
        // set up listeners for our editable list
        this.listeners = {
            // activate: when component is created
            "activate": this.addEditButton,
            // beginEdit: when editing action begins
            "beginEdit": function (list, mode) {
                this.selModel.setLocked(true);
                if (mode == "multi") {
                    this.inEditMode = true;
                    this.addDeleteBar();
                }
                else {
                    this.inSwipeMode = true;
                }
            },
            // endEdit; when editing action stops
            "endEdit": function (list, mode) {
                if (mode == "multi") {
                    this.inEditMode = false;
                }
                else {
                    this.inSwipeMode = false;
                }
                this.selModel.setLocked(false);
            },
            // el: adding delegate listener to the delete button "item" in our XTemplate
            // will fire on clicks of the element
            el: {
                tap: function (e, item, delegate) {
                    if (me.inSwipeMode) {
                        var listitem = Ext.get(item).up(".x-list-delete");
                        listitem.addCls("selected");
                        me.deleteItems();
                    }
//                    else {
                        e.stopEvent();
//                    }
                },
                delegate: '.x-display'
            },
            // itemtap: when an item in our list is tapped; handles regular and in-edit-mode taps
            itemtap: function (view, index, item, e) {
                // if we're in view mode, handle delete list fn
                if (view.inEditMode) {
                    var item = Ext.get(item);
                    item.select(".x-list-delete").each(function () {
                        if (this.hasCls("selected")) {
                            this.addCls("unselected");
                            this.removeCls("selected");
                            view.setDeleteButton();
                        }
                        else {
                            this.addCls("selected");
                            this.removeCls("unselected");
                            view.setDeleteButton();
                        }
                    })
                }
                // otherwise, let regular process take over
                else {
                    if (e.target.className != "x-list-disclosure") {
                        this.getSelectionModel().select(this.getRecord(item))
                    }
                }
            },
            // swipe: when user swipes over a list itme
            itemswipe: function (view, index, item, e) {
                if (view.inEditMode) {
                    return false;
                }
                var swipedIndex = "";
                view.fireEvent("beginEdit", view, "swipe");
                // loop over all list items, removing display class from delete button
                view.el.select(".x-list-delete-button").each(function (el, c, idx) {
                    // if this delete button is displayed, save index
                    if (el.hasCls("x-display")) {
                        swipedIndex = idx;
                    }
                    el.removeCls("x-display");
                    el.addCls("x-hidden");
                });
                // if the index of the existing delete button equals the row just swiped
                // fire the endEdit event, since swiping is done
                if (swipedIndex === index) {
                    view.fireEvent("endEdit", view, "swipe");
                }
                // otherwise, add the display class to the new row's delete button
                else {
                    var el = Ext.get(item);
                    el.select(".x-list-delete-button").each(function () {
                        this.removeCls("x-hidden");
                        this.addCls("x-display");
                    })
                }
            }
        }
        Ext.ux.EditableList.superclass.initComponent.call(this);
        // add new events
        this.addEvents("beginEdit", "endEdit");
    },
    /* addEditButton: adds an editing button to the parent panel's toolbar
    * is fired when this component is originally created
    */
    addEditButton: function () {
        var me = this;
        var panel = this.up("panel");
        var ttbar = panel.dockedItems.getAt(0);
        var btn = new Ext.Button({
            ui: "action",
            text: "Edit",
            //bubbleEvents: ["endEdit"],
            handler: function () {
                if (this.getText() == "Edit") {
                    this.setText("Done");
                    me.showDeleteIcons();
                }
                else {
                    this.setText("Edit");
                    me.hideDeleteIcons();
                }
            },
            listeners: {
                "beginEdit": function (list, mode) {
                    this.setText("Done");
                },
                "endEdit": function (list, mode) {
                    this.setText("Edit");
                }
            }
        });
        // relay events to the button so we can cherry pick on them from the button as well
        btn.relayEvents(this, ['beginEdit', 'endEdit']);
        ttbar.add(btn);
        ttbar.doComponentLayout();
        panel.doComponentLayout();
    },
    /* addDeleteBar: adds a toolbar to the bottom of the parent panel
    * is fired when the mode is set to "multi" (e.g., when not in swipe editing mode)
    */
    addDeleteBar: function () {
        var me = this;
        var panel = this.up("panel");
        var bbar = panel.dockedItems.getAt(1);
        if (!bbar) {
            bbar = new Ext.Toolbar({
                dock: "bottom",
                ui: "light",
                hidden: true,
                showAnimation: "fade",
                listeners: {
                    "endEdit": function (list, mode) {
                        this.hide(this.showAnimation)
                    },
                    "hide": function () {
                        this.doHide();
                        panel.doComponentLayout();
                    }
                },
                items: [{
                    xtype: "button",
                    iconCls: "trash",
                    iconMask: true,
                    ui: "decline",
                    text: "Delete",
                    disabled: true,
                    handler: me.deleteItems,
                    scope: me
                }]
            });
            // relay event to bottom toolbar so we can cherry pick it from the toolbar as well
            bbar.relayEvents(this, ["endEdit"])
            panel.addDocked(bbar);
        }
        this.setDeleteButton();
        bbar.show();
        panel.doComponentLayout();
    },
    /* showDeleteIcons: adds/removes classes from list item's delete icons, allowing CSS3 to handle animations
    */
    showDeleteIcons: function () {
        var nodes = this.getNodes();
        this.el.select(".x-list-delete").each(function () {
            this.removeCls("hidden");
            this.removeCls("selected");
            this.addCls("unselected");
        });
        this.fireEvent("beginEdit", this, "multi");
    },
    /* hideDeleteIcons: adds/removes classes from list item's delete icons, allowing CSS3 to handle animations
    */
    hideDeleteIcons: function () {
        var nodes = this.getNodes();
        this.el.select(".x-list-delete").each(function () {
            this.addCls("hidden");
        });
        this.el.select(".x-list-delete-button").each(function () {
            this.removeCls("x-display");
            this.addCls("x-hidden");
        })
        var mode = this.inEditMode ? "multi" : "swipe";
        this.fireEvent("endEdit", this, mode)
    },
    /* onTapStart: override of regular onTapStart() method to prevent highlighting of list items during edit mode
    * is fired every time a list item is taped
    */
    onTapStart: function (e, t) {
        var me = this,
            item = this.findTargetByEvent(e);

        if (item && !this.inEditMode && !this.inSwipeMode) {
            if (me.pressedDelay) {
                if (me.pressedTimeout) {
                    clearTimeout(me.pressedTimeout);
                }
                me.pressedTimeout = setTimeout(function () {
                    Ext.fly(item).addCls(me.pressedCls);
                }, Ext.isNumber(me.pressedDelay) ? me.pressedDelay : 100);
            }
            else {
                Ext.fly(item).addCls(me.pressedCls);
            }
        }
    },
    /* setDeleteButton: enables multi-delete button and increments button "count" text
    */
    setDeleteButton: function () {
        var count = this.el.select(".selected").elements.length;
        var btn = this.up("panel").dockedItems.getAt(1).items.getAt(0);
        if (count) {
            btn.enable()
            btn.setText(Ext.util.Format.format('Delete ({0})', count));
            btn.doComponentLayout()
        }
        else {
            btn.setText("Delete");
            btn.disable();
        }
    },
    /* deleteItems: deletes selected items from list's store
    */
    deleteItems: function () {
        var me = this;
        var records = [];
        var groups = me.store.getGroups();
        var tmprecords = [];
        if (groups.length) {
            for (var i = 0; i < groups.length; i++) {
                var cr = groups[i].children;
                for (var x = 0; x < cr.length; x++) {
                    tmprecords.push(cr[x]);
                }
            }
            var cache = new Ext.util.MixedCollection();
            cache.addAll(tmprecords);
        }
        var count = this.el.select(".x-list-delete").each(function (el, c, idx) {
            if (this.hasCls("selected")) {
                if (cache) {
                    var record = cache.getAt(idx);
                }
                else {
                    var record = me.store.getAt(idx);
                }
                records.push(record);
            }
        });
        this.store.remove(records)
        this.store.sync();
        this.hideDeleteIcons();
    }
});
Ext.reg('editablelist', Ext.ux.EditableList);;
/**
* Script name:	TabBarMvc
* 
* Description:	Custom Sencha Touch TabBar that works with a MVC structured application;
* 				Full description can be found here:
* 				http://www.onlinesolutionsdevelopment.com/blog/mobile-development/javascript/sencha-touch-tabbar-in-a-mvc-structured-application
* 
* Author:		CAM
* Author URL:	http://www.onlinesolutionsdevelopment.com/blog
* 
* Last modified date: October 8, 2011
*/

TabBarMvc = Ext.extend(Ext.TabBar, {
    dock: 'bottom',
    ui: 'dark',
    layout: {
        pack: 'center'
    },

    initComponent: function () {
        var thisComponent = this;
        this.previousTabIndex = 0;

        // iterate through all the items
        Ext.each(this.items, function (item) {
            // add a handler function for the tab button
            item.handler = function () {
                thisComponent.tabButtonHandler(this);
            };

        }, this);

        // detect when the route changed
        Ext.Dispatcher.on('dispatch', function (interaction) {
            var tabs = this.query('.tab');

            var action = interaction.action;
            var controller = interaction.controller.id;

            Ext.each(tabs, function (item) {
                if (!item.route)
                    return;

                var dispatchOptions = Ext.Router.recognize(item.route);
                //
                var itemAction = dispatchOptions.action;
                var itemController = dispatchOptions.controller;

                if (itemController == controller && itemAction == action) {
                    this.setActiveTab(item);

                    this.previousTabIndex = this.items.indexOf(item);

                    return false;
                }
            }, this);

        }, this);

        // switch animation
        if (!this.switchAnimation) {
            this.switchAnimation = { type: 'slide' };
        }
        else if (Ext.isString(this.switchAnimation)) {
            this.switchAnimation = { type: this.switchAnimation };
        }

        TabBarMvc.superclass.initComponent.apply(this, arguments);
    },

    // function called on tab button tap
    tabButtonHandler: function (tab) {
        var tabIndex = this.items.indexOf(tab);

        this.setActiveTab(tab);

        if (!Ext.isEmpty(tab.route)) {
            var anim = {};
            anim = Ext.apply(anim, this.switchAnimation);

            if (tabIndex != -1 && tabIndex < this.previousTabIndex) {
                anim.reverse = true;
            }

            var dispatchOptions = Ext.Router.recognize(tab.route);
            dispatchOptions.animation = anim;
            //
            Ext.dispatch(dispatchOptions);
        }

        this.previousTabIndex = tabIndex;
    },

    setActiveTab: function (tab) {
        var tabs = this.query('.tab');

        Ext.each(tabs, function (item, index) {
            item.removeCls('x-tab-active');
        }, this);

        tab.addCls('x-tab-active');
    }
});

Ext.reg('TabBarMvc', TabBarMvc);;
Eze.TakeoutTabs = Ext.extend(Ext.TabBar, {
    dock: 'bottom',
    ui: 'dark',
    layout: {
        pack: 'justify',
        align: 'stretch'
    },
    initComponent: function () {
        Eze.log('takeout-tabs init');

        this.items = [{
            text: 'Home',
            iconCls: 'home',
            route: 'home/index'
        }, {
            text: 'Menu',
            iconCls: 'note2',
            route: 'takeout/index'
        }, {
            text: 'Favorites',
            iconCls: 'favorites',
            route: 'favs/index'
        }, {
            cls: 'cartTabIcon',
            text: OrderEze.cartSettings.ShoppingCartTitle,
            iconCls: 'shop1',
            route: 'cart/index',
            badgeText: Eze.utils.cartItemCount <= 0 ? null : Eze.utils.cartItemCount
        }, {
            text: 'Account',
            iconCls: 'user',
            route: 'account/index'
        }];

        var thisComponent = this;
        this.previousTabIndex = 0;

        // iterate through all the items
        Ext.each(this.items, function (item) {
            // add a handler function for the tab button
            item.handler = function () {
                thisComponent.tabButtonHandler(this);
            };

            var currPage = Ext.History.getToken();
            currPage = currPage.substr(0, item.route.length);
            if (item.route == currPage) {
                item.cls = 'x-tab-active';
            }

        }, this);

        Eze.TakeoutTabs.superclass.initComponent.apply(this, arguments);
    },

    // function called on tab button tap
    tabButtonHandler: function (tab) {
        var tabIndex = this.items.indexOf(tab);

        if (!Ext.isEmpty(tab.route)) {
            var dispatchOptions = Ext.Router.recognize(tab.route);
            dispatchOptions.animation = false;
            dispatchOptions.fromTab = true;

            Eze.navToUrl('#' + tab.route, dispatchOptions);

            //Ext.dispatch(dispatchOptions);
        }

        this.previousTabIndex = tabIndex;
    }
});
Ext.reg('takeout-tabs', Eze.TakeoutTabs);
;
Eze.utils = {
    cartItemCount: -1,
    updateCartItemCount: function (view, count) {
        Eze.utils.cartItemCount = count;
        Eze.log(view.dockedItems.getAt(1).items.getAt(3));
        view.dockedItems.getAt(1).items.getAt(3).setBadge(count);
        Eze.log(view.dockedItems.getAt(1).items.getAt(3));
    },
    hideKeyboard: function () {
        var activeElement = document.activeElement;
        activeElement.setAttribute('readonly', 'readonly');
        activeElement.setAttribute('disabled', 'true');
        Ext.defer(function () {
            activeElement.blur();
            activeElement.removeAttribute('readonly');
            activeElement.removeAttribute('disabled');
        }, 100);
    }
};

Number.prototype.formatMoney = function (c, d, t) {
    if (this == 0) return '';
    p = '';
    if (Object.prototype.toString.apply(c) === '[object Boolean]' && c) {
        c = 2;
        p = '+';
    }
    var n = this,
        c = isNaN(c = Math.abs(c)) ? 2 : c,
        d = d == undefined ? "." : d,
        t = t == undefined ? "," : t,
        s = n < 0 ? "-$" : "$",
        i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
        j = (j = i.length) > 3 ? j % 3 : 0;
    return p + s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};

Ext.Picker.override({
    show: function () {
        if (Ext.is.Phone) {
            // need to manually hide the keyboard and delay showing the Picker popup because the popup displays under the keyboard
            // and when the keyboard and picker are dismissed, the layout is completely broken
            // See https://ordereze.visualstudio.com/DefaultCollection/OrderEze/_workitems#_a=edit&id=3443
            Eze.utils.hideKeyboard();
            Ext.defer(Ext.Sheet.superclass.show, 500, this, arguments);
        } else {
            Ext.Sheet.superclass.show.apply(this, arguments);
        }
    }
});;
/**
* @class OrderEze.Viewport
* @extends Ext.Panel
* This is a default generated class which would usually be used to initialize your application's
* main viewport. By default this is simply a welcome screen that tells you that the app was 
* generated correctly.
*/
OrderEze.Viewport = Ext.extend(Ext.Panel, {
    id: 'viewport',
    layout: 'card',
    fullscreen: true,

    initComponent: function () {
        Eze.log('viewport init');
        Eze.viewport = this;
        Eze.log('viewport init done');

        // delay adding the event handler to prevent sending duplicate pageviews of the first 
        // page, because it is already sent by the GA script included in the page html
        var that = this;
        setTimeout(function() {
            that.on('cardswitch', that.sendPageViewToAnalytics);
        }, 500);

        OrderEze.Viewport.superclass.initComponent.call(this, arguments);
    },
    sendPageViewToAnalytics: function () {
        // send pageview to google analytics if present on the page
        if (typeof (window.ga) !== 'function') return;
        var relativeUrl = document.location.href.replace(document.location.origin, '');
        Eze.log('sending pageview to GA: ' + relativeUrl);

        var pageTitle = 'Mobile Website';
        var toolbar = Eze.viewport.getActiveItem().getDockedItems();
        if (toolbar.length > 0 && toolbar[0].title) {
            pageTitle += ' - ' + toolbar[0].title;
        } else if (document.location.hash === '#home/index') {
            pageTitle += ' - Home';
        }
        Eze.log('Page Title: ' + pageTitle);

        ga('set', {
            page: relativeUrl,
            title: pageTitle
        });
        ga('send', 'pageview');
    }
});

;
Ext.ns('OrderEze.views.account');

OrderEze.views.account.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'account-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'My Account',
                items: [{
                    xtype: 'spacer'
                }, {
                    text: 'Order Lookup',
                    ui: 'action',
                    hidden: !this.pageData.showOrderLookup,
                    handler: this.orderLookup,
                    scope: this
                }, {
                    text: 'Login',
                    ui: 'action',
                    hidden: OrderEze.settings.isLoggedIn,
                    handler: this.showLoginSheet,
                    scope: this
                }, {
                    text: 'Logout',
                    ui: 'action',
                    hidden: !OrderEze.settings.isLoggedIn || OrderEze.settings.isGuestUser,
                    handler: this.logout,
                    scope: this
                }]
            }, {
                xtype: 'takeout-tabs'
            }]
        });

        if (OrderEze.settings.isLoggedIn && !OrderEze.settings.isGuestUser) {
            this.items = [];
            this.items.push({
                cls: 'panel-text',
                html: '<div style="text-align: left;">' +
                            '<p><strong>Name:</strong> ' + this.pageData.name + "</p>" +
                            '<p><strong>Username:</strong> ' + this.pageData.username + "</p>" +
                            '<p><strong>Email:</strong> ' + this.pageData.email + "</p>" +
                        '</div>'
            });

            if (this.pageData.orders.length == 0) {
                this.items.push({
                    cls: 'panel-text',
                    html: '<p><strong>No Orders Yet!</strong><br />You have not placed any orders on the website yet. Once you place an order, the information will be displayed here.</p>'
                });
            } else {
                this.items.push({
                    xtype: 'list',
                    cls: 'orders-list',
                    scroll: false,
                    grouped: true,
                    store: new Ext.data.JsonStore({
                        fields: ['orderId', 'number', 'date', 'price', 'type'],
                        getGroupString: function (record) {
                            return 'My Orders';
                        },
                        data: this.pageData.orders
                    }),
                    itemTpl: '<div class="medium"><span class="right-text">{price}</span> <strong>{type} Order #{number}</strong></div>' +
                             '<div class="small">{date}</div>',
                    disableSelection: true,
                    onItemDisclosure: true,
                    listeners: {
                        itemtap: function (list, index, item, e) {
                            var rec = list.store.getAt(index);
                            Eze.log("tapped order #" + rec.get('orderId'));
                            Eze.showListMask(item);
                            Eze.nav('account', 'order', { id: rec.get('orderId') });
                        },
                        scope: this
                    }
                });
            }
        } else if (Ext.isDefined(this.pageData.guestOrders)) {
            this.items = [];
            if (this.pageData.guestOrders.length == 0) {
                this.items.push({
                    cls: 'panel-text',
                    html: '<p><strong>No Orders were found!</strong>'
                });
            } else {
                this.items.push({
                    xtype: 'list',
                    cls: 'orders-list',
                    scroll: false,
                    grouped: true,
                    store: new Ext.data.JsonStore({
                        fields: ['orderId', 'number', 'date', 'price', 'type'],
                        getGroupString: function (record) {
                            return 'My Orders';
                        },
                        data: this.pageData.guestOrders
                    }),
                    itemTpl: '<div class="medium"><span class="right-text">{price}</span> <strong>{type} Order #{number}</strong></div>' +
                             '<div class="small">{date}</div>',
                    disableSelection: true,
                    onItemDisclosure: true,
                    listeners: {
                        itemtap: function (list, index, item, e) {
                            var rec = list.store.getAt(index);
                            Eze.log("tapped order #" + rec.get('orderId'));
                            Eze.showListMask(item);
                            Eze.nav('account', 'order', { id: rec.get('orderId') });
                        },
                        scope: this
                    }
                });
            }
        } else {
            this.items = [
                {
                    cls: 'panel-text',
                    html: '<h2>You are not logged in!</h2><p>Please login to view your profile information and past orders. Click on the <strong>Login</strong> button above to login.</p>'
                }];
        }

        OrderEze.views.account.index.superclass.initComponent.call(this, arguments);
    },

    showLoginSheet: function () {
        Eze.log('showLoginSheet');
        var sheet = new OrderEze.views.account.LoginSheet({
            instructions: 'You must login to the website to view your account information and past orders',
            listeners: {
                loggedin: function (theSheet) {
                    Eze.log('showLoginSheet done');
                    Eze.nav('account', 'index', { refresh: true });
                },
                scope: this
            }
        });
        sheet.show();
    },

    logout: function () {
        Eze.log('logout');
        Ext.Msg.confirm('', 'Are you sure you want to logout?', function (btn) {
            if (btn == 'no') return;
            Eze.load('account', 'logout', function (data) {
                OrderEze.settings.isLoggedIn = false;
                OrderEze.settings.isGuestUser = false;
                Eze.nav('account', 'index', { refresh: true });
            }, this);
        }, this);
    },

    orderLookup: function () {
        Eze.log('orderLookup');
        var sheet = new OrderEze.views.account.OrderLookupSheet({});
        sheet.show();
    }
});

Ext.reg('account-index', OrderEze.views.account.index);;
Ext.ns('OrderEze.views.account');

OrderEze.views.account.order = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'checkout-summary',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Order #' + this.pageData.orderNumber,
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        Eze.navBack('#account/index');
                    },
                    scope: this
                }]
            }]
        });

        this.items = [{
            xtype: 'list',
            itemTpl: '<div class="medium bold">{title}</div><div class="small">{text}</div>',
            store: new Ext.data.JsonStore({
                fields: ['title', 'text'],
                getGroupString: function (record) {
                    return 'Order Information';
                },
                data: this.pageData.infos
            }),
            scroll: false,
            grouped: true,
            cls: 'innerList',
            disableSelection: true
        }, {
            xtype: 'list',
            cls: 'innerList',
            scroll: false,
            grouped: true,
            store: new Ext.data.Store({
                fields: ['Name', 'Price', 'Quantity', 'Tag', 'ExtraNames', 'Comments'],
                getGroupString: function (record) {
                    return 'Order Items';
                },
                data: this.pageData.items
            }),
            itemTpl: '<span class="right-text">{Price}</span><span class="medium">{Name}</span><div class="small">{ExtraNames}</div><div class="small">{Tag}</div>',
            itemTpl2: '<div class="medium"><span class="right-text">{Price}</span><span class="medium">{Name}</span><div class="small">{ExtraNames}</div><div class="small">{Tag}</div><div class="small">{Comments}</div></div>',
            disableSelection: true
        }, {
            xtype: 'list',
            itemTpl: '<div class="{cls}"><span class="right-text">{price}</span>{title}</div>',
            store: new Ext.data.JsonStore({
                fields: ['title', 'price'],
                getGroupString: function (record) {
                    return 'Pricing';
                },
                data: this.pageData.prices
            }),
            scroll: false,
            grouped: true,
            cls: 'innerList',
            disableSelection: true
        }];

        OrderEze.views.account.order.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('account-order', OrderEze.views.account.order);;
Ext.ns('OrderEze.views.cart');

OrderEze.views.cart.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'cart-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: OrderEze.cartSettings.ShoppingCartTitle,
                items: [{
                    text: 'Empty',
                    disabled: this.pageData.items.length == 0,
                    handler: this.emptyCart,
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Checkout',
                    ui: 'action',
                    disabled: this.pageData.items.length == 0,
                    handler: this.checkout,
                    scope: this
                }]
            }, {
                xtype: 'takeout-tabs'
            }]

        });

        this.emptyMsg = '<div class="panel-text"><h2>Your ' + OrderEze.cartSettings.ShoppingCartTitle + ' is Empty!</h2><p>Click on the Menu icon below to browse our menu and add items to your ' + OrderEze.cartSettings.ShoppingCartTitle + '</p></div>';

        if (this.pageData.itemCount == 0) {
            this.items = [{ html: this.emptyMsg}];
        } else {
            var subtotalHtml = '<table>';
            subtotalHtml += '<tr><th>Sub-total:</th><th style="width: 60px;">' + this.pageData.subtotal.formatMoney() + '</th>';
            if (this.pageData.promoResult && this.pageData.promoResult.IsValid) {
                var discounts = this.pageData.promoResult.Discounts;
                for (var i = 0; i < discounts.length; i++) {
                    var d = discounts[i];
                    var amount = (d.Value == 0) ? '-$0.00' : (-d.Value).formatMoney();
                    subtotalHtml += '<tr><td>' + d.Text + ':</td><td>' + amount + '</span>';
                }
                subtotalHtml += '<tr><th>Sub-total after Discounts:</th><th>' + (this.pageData.subtotal - this.pageData.discount).formatMoney() + '</th>';
            }
            subtotalHtml += '</table>';

            this.items = [{
                xtype: 'editablelist',
                mode: 'multi',
                cls: 'cart-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.Store({
                    fields: ['ItemID', 'Name', 'Price', 'Quantity', 'Tag', 'Info'],
                    getGroupString: function (record) {
                        return 'Cart Items';
                    },
                    data: this.pageData.items,
                    proxy: new Ext.data.MemoryProxy(),
                    listeners: {
                        remove: this.deleteItem,
                        scope: this
                    }
                }),
                itemTpl: '<span class="right-text">{Price}</span><span class="medium">{Name}</span><div class="small">{Info}</div><div class="small">{Tag}</div>',
                groupTpl: [
                    '<tpl for=".">',
                        '<div class="x-list-group x-group-{id}">',
                            '<h3 class="x-list-header">{group}<span class="right-text">swipe to remove item</span></h3>',
                            '<div class="x-list-group-items">',
                                '{items}',
                            '</div>',
                        '</div>',
                    '</tpl>'
                ],
                disableSelection: true,
                onItemDisclosure: false
            }, {
                xtype: 'form',
                cls: 'promo-form',
                items: [{
                    xtype: 'fieldset',
                    items: [{
                        xtype: 'textfield',
                        label: 'Promo Code',
                        labelWidth: '50%',
                        value: this.pageData.promoCode,
                        listeners: {
                            change: this.promoChanged,
                            scope: this
                        }
                    }]
                }]
            }, {
                cls: 'cart-subtotal',
                html: subtotalHtml
            }, {
                cls: 'cart-order-time',
                html: this.pageData.orderTime
            }, {
                layout: {
                    type: 'vbox',
                    align: 'center'
                },
                padding: 5,
                items: [{
                    xtype: 'button',
                    ui: 'action-small',
                    text: 'Change ' + this.pageData.orderType + ' Time',
                    width: 150,
                    handler: this.showPickupTimeSheet,
                    scope: this
                }]
            }];

        }

        OrderEze.views.cart.index.superclass.initComponent.call(this, arguments);
    },

    checkout: function () {
        Eze.log('checkout');
        if (this.pageData.subtotal - this.pageData.discount < this.pageData.minimumOrderPrice) {
            Eze.alert('', 'Minimum order required is ' + this.pageData.minimumOrderPrice.formatMoney());
            return;
        }

        if (!OrderEze.settings.isLoggedIn) {
            Eze.log('not logged in');
            this.showLoginSheet();
            return;
        }
        Eze.log('logged in');
        Eze.nav('checkout', 'index');
    },

    emptyCart: function () {
        Eze.log('emptyCart');
        Ext.Msg.confirm('Empty ' + OrderEze.cartSettings.ShoppingCartTitle + '?', 'Are you sure you want to empty your ' + OrderEze.cartSettings.ShoppingCartTitle + '?', function (btn) {
            if (btn == 'no') return;
            Eze.load('cart', 'emptyCart', function (data) {
                if (OrderEze.settings.isGuestUser) {
                    OrderEze.settings.isLoggedIn = false;
                    OrderEze.settings.isGuestUser = false;
                }
                Eze.log('emptyCart done');
                Eze.nav('cart', 'index');
            }, this);
        }, this);
    },

    deleteItem: function (store, record, index) {
        Eze.log('deleteItem ' + record.get('ItemID'));

        Eze.load('cart', 'deleteItem', record.get('ItemID'), function (data) {
            Eze.utils.cartItemCount = data.itemCount;
            Eze.nav('cart');
            return;
            Eze.utils.updateCartItemCount(this, data.itemCount);
            if (data.itemCount == 0) {
                this.removeAll(true);
                this.add({ html: this.emptyMsg });
                // disable top buttons
                var tb = this.dockedItems.getAt(0);
                tb.items.getAt(0).setDisabled(true);
                tb.items.getAt(2).setDisabled(true);
                this.doLayout();
            }
        }, this);

    },

    showPickupTimeSheet: function () {
        Eze.log('showPickupTimeSheet');
        var sheet = new OrderEze.views.takeout.PickupTimeSheet({
            listeners: {
                done: function (theSheet) {
                    Eze.log('showPickupTimeSheet done');
                    Eze.nav('cart', 'index');
                },
                scope: this
            }
        });
        sheet.show();
    },

    showLoginSheet: function () {
        Eze.log('showLoginSheet');
        var sheet = new OrderEze.views.account.LoginSheet({
            instructions: this.pageData.guestCheckoutEnabled ? 'Create an account for faster check out on future purchases. No time right now? No problem. You can check out as a guest.' : 'You must login to the website to place your order',
            showGuest: this.pageData.guestCheckoutEnabled,
            orderType: this.pageData.orderType,
            listeners: {
                loggedin: function (theSheet) {
                    Eze.log('showLoginSheet done');
                    this.checkout();
                },
                scope: this
            }
        });
        sheet.show();
    },

    promoChanged: function (tb, value, oldvalue) {
        Eze.load('cart', 'updatePromoCode', value, function (json) {
            Eze.nav('cart', 'index');
        }, this);
    }
});

Ext.reg('cart-index', OrderEze.views.cart.index);;
Ext.ns('OrderEze.views.checkout');

OrderEze.views.checkout.complete = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'checkout-complete',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Order Complete',
                items: [{
                    text: 'Home',
                    ui: 'action',
                    handler: function () {
                        Eze.nav('home');
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'My Orders',
                    ui: 'action',
                    handler: function () {
                        Eze.nav('account', 'index', { refresh: true });
                    },
                    scope: this
                }]
            }]
        });
        if (OrderEze.settings.isGuestUser) {
            OrderEze.settings.isLoggedIn = false;
            OrderEze.settings.isGuestUser = false;
        }
        this.items = [{
            cls: 'panel-text',
            html: this.pageData.checkoutMessage
        }];

        OrderEze.views.checkout.complete.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('checkout-complete', OrderEze.views.checkout.complete);;
Ext.ns('OrderEze.views.checkout');

OrderEze.views.checkout.delivery = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'checkout-delivery',
            scroll: false,
            layout: 'fit',
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Delivery Address',
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Next',
                    ui: 'forward',
                    handler: this.next,
                    scope: this
                }]
            }]
        });

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                xtype: 'fieldset',
                title: 'Where should we deliver?',
                defaults: { labelWidth: '35%' },
                hidden: this.pageData.addresses.length == 0,
                items: [{
                    xtype: 'selectfield',
                    name: 'addressId',
                    options: this.pageData.addresses,
                    value: this.pageData.addresses.length > 1 ? this.pageData.addresses[1].value : '-1',
                    listeners: {
                        change: this.onAddressChanged,
                        scope: this
                    }
                }, {
                    xtype: 'textareafield',
                    name: 'savedNotes',
                    label: 'Delivery Notes',
                    placeHolder: 'i.e. side entrance',
                    value: this.pageData.addresses.length > 1 ? this.pageData.addresses[1].notes : ''
                }]
            }, {
                xtype: 'fieldset',
                title: 'Address Information:',
                hidden: this.pageData.addresses.length > 0,
                defaults: { labelWidth: '35%', required: true },
                items: [{
                    xtype: 'textfield',
                    name: 'name',
                    label: 'Name'
                }, {
                    xtype: 'textfield',
                    name: 'company',
                    label: 'Company',
                    required: false
                }, {
                    xtype: 'textfield',
                    name: 'street',
                    label: 'Street'
                }, {
                    xtype: 'textfield',
                    name: 'crossStreet',
                    label: 'Cross Street',
                    hidden: !this.pageData.showCrossStreet
                }, {
                    xtype: 'textfield',
                    name: 'city',
                    label: 'City'
                }, {
                    xtype: 'selectfield',
                    name: 'state',
                    label: 'State',
                    options: this.pageData.allStates
                }, {
                    xtype: 'textfield',
                    name: 'zip',
                    label: 'Zip'
                }, {
                    xtype: 'textfield',
                    name: 'phone',
                    label: 'Phone'
                }, {
                    xtype: 'textfield',
                    name: 'fax',
                    label: 'Fax',
                    required: false
                }, {
                    xtype: 'textareafield',
                    name: 'notes',
                    label: 'Delivery Notes',
                    placeHolder: 'i.e. side entrance',
                    required: false
                }, {
                    xtype: 'checkboxfield',
                    name: 'save',
                    label: 'Save address for later?',
                    labelWidth: '80%',
                    checked: true,
                    value: true,
                    required: false,
                    hidden: OrderEze.settings.isGuestUser
                }]
            }]
        });

        this.items = [this.form];

        OrderEze.views.checkout.delivery.superclass.initComponent.call(this, arguments);
    },

    onAddressChanged: function (cb, value) {
        Eze.log('onAddressChanged ' + cb.getValue());

        var showAddressForm = (cb.getValue() == '-1');
        // address fieldset
        this.form.items.getAt(1).setVisible(showAddressForm);
        // delivery notes textarea
        var r = cb.store.findRecord('value', value);
        this.form.items.getAt(0).items.getAt(1).setVisible(!showAddressForm).setValue(r ? r.data.notes : '');

    },

    next: function () {
        Eze.log('next');
        Eze.load('checkout', 'deliverySubmit', this.form.getValues(), function (data) {
            Eze.log('checkout/delivery.next done');
            Eze.navToUrl(data.nextPage);
        }, this);
    }
});

Ext.reg('checkout-delivery', OrderEze.views.checkout.delivery);;
Ext.ns('OrderEze.views.checkout');

OrderEze.views.checkout.deliverysl = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'checkout-delivery',
            scroll: false,
            layout: 'fit',
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Delivery Address',
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Next',
                    ui: 'forward',
                    handler: this.next,
                    scope: this
                }]
            }]
        });

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                xtype: 'fieldset',
                title: 'Tip Delivery Person?',
                defaults: { labelWidth: '70%' },
                hidden: !this.pageData.showTipField,
                items: [{
                    xtype: 'numberfield',
                    name: 'tip',
                    label: 'Tip Amount ($)',
                    value: '0',
                    minValue: 0
                }]
            }, {
                xtype: 'fieldset',
                title: 'Where should we deliver?',
                defaults: { labelWidth: '35%' },
                hidden: this.pageData.addresses.length == 0,
                items: [{
                    xtype: 'selectfield',
                    name: 'addressId',
                    options: this.pageData.addresses,
                    value: this.pageData.addresses.length > 1 ? this.pageData.addresses[1].value : '-1',
                    listeners: {
                        change: this.onAddressChanged,
                        scope: this
                    }
                }, {
                    xtype: 'textareafield',
                    name: 'savedNotes',
                    label: 'Delivery Notes',
                    placeHolder: 'i.e. side entrance',
                    value: this.pageData.addresses.length > 1 ? this.pageData.addresses[1].notes : ''
                }]
            }, {
                xtype: 'fieldset',
                title: 'Address Information:',
                hidden: this.pageData.addresses.length > 0,
                defaults: { labelWidth: '35%', required: true },
                items: [{
                    xtype: 'textfield',
                    name: 'name',
                    label: 'Name'
                }, {
                    xtype: 'selectfield',
                    name: 'zip',
                    label: 'Zip',
                    options: this.pageData.allZipCodes,
                    listeners: {
                        change: this.onZipChanged,
                        scope: this
                    }
                }, {
                    id: 'streetName',
                    xtype: 'selectfield',
                    name: 'streetName',
                    label: 'Street',
                    options: this.pageData.streetNames
                }, {
                    xtype: 'textfield',
                    name: 'houseNum',
                    label: 'House #'
                }, {
                    xtype: 'textfield',
                    name: 'apt',
                    label: 'Apt/Suite',
                    required: false
                }, {
                    xtype: 'textfield',
                    name: 'phone',
                    label: 'Phone'
                }, {
                    xtype: 'textareafield',
                    name: 'notes',
                    label: 'Delivery Notes',
                    placeHolder: 'i.e. side entrance',
                    required: false
                }, {
                    xtype: 'checkboxfield',
                    name: 'save',
                    label: 'Save address for later?',
                    labelWidth: '80%',
                    checked: true,
                    value: true,
                    required: false
                }]
            }]
        });

        this.items = [this.form];

        OrderEze.views.checkout.delivery.superclass.initComponent.call(this, arguments);
        Eze.log('checkout/deliverysl.initComponent done');
    },

    onAddressChanged: function (cb, value) {
        Eze.log('onAddressChanged ' + cb.getValue());

        var showAddressForm = (cb.getValue() == '-1');
        // address fieldset
        this.form.items.getAt(2).setVisible(showAddressForm);
        // delivery notes textarea
        var r = cb.store.findRecord('value', value);
        this.form.items.getAt(1).items.getAt(1).setVisible(!showAddressForm).setValue(r ? r.data.notes : '');
        
    },

    onZipChanged: function (cb, value) {
        Eze.log('onZipChanged ' + value);

        var namesSelect = Ext.getCmp('streetName');
        namesSelect.setOptions([{ text: '', value: '' }]);
        namesSelect.setValue('');
        
        Eze.load('checkout', 'deliveryslStreetNames', value, function (data) {
            namesSelect.setOptions(data.streetNames);
            namesSelect.setValue(data.streetNames[0].value);
        }, this);

    },

    next: function () {
        Eze.log('next');
        Eze.load('checkout', 'deliveryslSubmit', this.form.getValues(), function (data) {
            Eze.log('checkout/deliverysl.next done');
            Eze.navToUrl(data.nextPage);
        }, this);
    }
});

Ext.reg('checkout-deliverysl', OrderEze.views.checkout.deliverysl);;
Ext.ns('OrderEze.views.checkout');

OrderEze.views.checkout.guest = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'checkout-guest',
            scroll: false,
            layout: 'fit',
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Order Confirmation',
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Next',
                    ui: 'forward',
                    handler: this.next,
                    scope: this
                }]
            }]
        });

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                id: 'guestFieldset',
                xtype: 'fieldset',
                title: '',
                defaults: { labelWidth: '40%', required: true },
                items: [this.pageData.fields]
            }]
        });

        this.items = [this.form];

        OrderEze.views.checkout.guest.superclass.initComponent.call(this, arguments);
    },


    next: function () {
        Eze.log('next');
        Eze.load('checkout', 'guestSubmit', this.form.getValues(), function (data) {
            Eze.log('checkout/guest.next done');
            Eze.navToUrl(data.nextPage);
        }, this);
    }
});

Ext.reg('checkout-guest', OrderEze.views.checkout.guest);;
Ext.ns('OrderEze.views.checkout');

OrderEze.views.checkout.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'checkout-index',
            scroll: true,
            layout: 'fit',
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Checkout',
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Next',
                    ui: 'forward',
                    handler: this.next,
                    scope: this
                }]
            }]
        });

        var pmtItems = [];
        if (this.pageData.payOnlineAllowed && this.pageData.paypalMethod != 'standard') {
            pmtItems.push({
                xtype: 'radiofield',
                label: 'Online with Credit Card',
                name: 'paymentType',
                value: 'Online',
                checked: this.pageData.payOnlineAllowed,
                listeners: {
                    check: this.onPaymentChanged,
                    scope: this
                }
            });
        }
        if (this.pageData.payWithPaypalAllowed) {
            pmtItems.push({
                xtype: 'radiofield',
                label: '<img src="/ordereze/images/paypal_express_co.gif" align="left" style="margin-right:7px;">',
                name: 'paymentType',
                value: 'PayPal',
                checked: !this.pageData.payOnlineAllowed && this.pageData.payWithPaypalAllowed,
                listeners: {
                    check: this.onPaymentChanged,
                    scope: this
                }
            });
        }
        if (this.pageData.payInPersonAllowed) {
            pmtItems.push({
                xtype: 'radiofield',
                label: 'In Person',
                name: 'paymentType',
                value: 'In Person',
                checked: !this.pageData.payOnlineAllowed && !this.pageData.payWithPaypalAllowed && this.pageData.payInPersonAllowed,
                listeners: {
                    check: this.onPaymentChanged,
                    scope: this
                }
            });
        }

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                xtype: 'fieldset',
                title: 'How would you like to pay?',
                instructions: this.pageData.paymentMessage,
                defaults: {
                    labelWidth: '80%'
                },
                items: pmtItems
            }, {
                xtype: 'fieldset',
                title: 'How much would you like to tip?',
                defaults: { labelWidth: '70%' },
                hidden: !this.pageData.showTipField,
                items: [{
                    xtype: 'numberfield',
                    name: 'tip',
                    label: 'Tip Amount ($)',
                    value: '0',
                    minValue: 0
                }]
            }, {
                xtype: 'fieldset',
                title: this.pageData.commentsTitle || 'Order Comments:',
                hidden: !this.pageData.commentsAllowed,
                items: [{
                    xtype: 'textareafield',
                    name: 'orderComments'
                }]
            }]
        });

        this.items = [this.form];

        OrderEze.views.checkout.index.superclass.initComponent.call(this, arguments);
    },

    onPaymentChanged: function (cb) {
        Eze.log('onPaymentChanged ' + cb.getValue());
        var showTipSection = (cb.getValue() == 'Online');
        // tip fieldset
        if (this.pageData.showTipField) this.form.items.getAt(1).setVisible(showTipSection);
    },

    next: function () {
        Eze.log('next');
        Eze.load('checkout', 'indexSubmit', this.form.getValues(), function (data) {
            Eze.log('checkout/index.next done');
            Eze.navToUrl(data.nextPage);
        }, this);
    }
});

Ext.reg('checkout-index', OrderEze.views.checkout.index);;
Ext.ns('OrderEze.views.checkout');

OrderEze.views.checkout.payment = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'checkout-payment',
            scroll: false,
            layout: 'fit',
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Payment',
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Next',
                    ui: 'forward',
                    handler: this.next,
                    scope: this
                }]
            }]
        });

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                xtype: 'fieldset',
                title: 'Payment Method',
                hidden: this.pageData.cards.length == 0,
                items: [{
                    xtype: 'selectfield',
                    name: 'cardId',
                    options: this.pageData.cards,
                    value: this.pageData.cards.length > 1 ? this.pageData.cards[1].value : '-1',
                    listeners: {
                        change: this.onCardChanged,
                        scope: this
                    }
                }]
            }, {
                id: 'pmt_info',
                xtype: 'fieldset',
                title: 'Payment Information:',
                hidden: this.pageData.cards.length > 0,
                defaults: { labelWidth: '40%', required: true },
                items: [{
                    xtype: 'textfield',
                    name: 'name',
                    label: 'Name',
                    hidden: !this.pageData.cardConfig.OwnerName
                }, {
                    xtype: 'selectfield',
                    name: 'cardType',
                    label: 'Type',
                    options: this.pageData.allTypes
                }, {
                    xtype: 'textfield',
                    name: 'cardNumber',
                    label: 'Number'
                }, {
                    xtype: 'selectfield',
                    name: 'cardExpMonth',
                    label: 'Exp. Month',
                    options: this.pageData.allMonths
                }, {
                    xtype: 'selectfield',
                    name: 'cardExpYear',
                    label: 'Exp. Year',
                    options: this.pageData.allYears
                }, {
                    xtype: 'numberfield',
                    name: 'cardCode',
                    label: 'Auth Code',
                    hidden: !this.pageData.cardConfig.SecurityCode,
                    minValue: 100,
                    maxValue: 9999
                }, {
                    xtype: 'checkboxfield',
                    name: 'save',
                    label: 'Save card info for later?',
                    labelWidth: '80%',
                    checked: true,
                    value: 'true',
                    required: false,
                    hidden: OrderEze.settings.isGuestUser
                }]
            }, {
                id: 'billing_info',
                xtype: 'fieldset',
                title: 'Billing Information:',
                hidden: this.pageData.cards.length > 0 && this.pageData.cardConfig.HasAny,
                defaults: { labelWidth: '40%', required: true },
                items: [{
                    xtype: 'textfield',
                    name: 'streeNumber',
                    label: 'House #',
                    hidden: !this.pageData.cardConfig.StreetNumber
                }, {
                    xtype: 'textfield',
                    name: 'streetName',
                    label: 'Street',
                    hidden: !this.pageData.cardConfig.StreetName
                }, {
                    xtype: 'textfield',
                    name: 'city',
                    label: 'City',
                    hidden: !this.pageData.cardConfig.City
                }, {
                    xtype: 'selectfield',
                    name: 'state',
                    label: 'State',
                    options: !this.pageData.cardConfig.State ? [] : this.pageData.allStates,
                    hidden: !this.pageData.cardConfig.State
                }, {
                    xtype: 'textfield',
                    name: 'zip',
                    label: 'Zip',
                    hidden: !this.pageData.cardConfig.ZipCode
                }]
            }]
        });

        // show ssl seal
        var seal = Ext.get('siteseal');
        if (seal) {
            this.form.insert(0, {
                cls: 'panel-text', 
                padding: 0,
                bodyPadding: 0,
                html: '<p>Payment information is transmitted securely.<br />' + seal.getHTML() + "</p>"
            });
        }

        this.items = [this.form];

        OrderEze.views.checkout.payment.superclass.initComponent.call(this, arguments);
    },

    onCardChanged: function (cb, value) {
        Eze.log('onCardChanged ' + cb.getValue());

        var showCardsForm = (cb.getValue() == '-1');
        Ext.getCmp('pmt_info').setVisible(showCardsForm);
        Ext.getCmp('billing_info').setVisible(showCardsForm);
    },

    next: function () {
        Eze.log('next');
        Eze.load('checkout', 'paymentSubmit', this.form.getValues(), function (data) {
            Eze.log('checkout/payment.next done');
            Eze.navToUrl(data.nextPage);
        }, this);
    }

});

Ext.reg('checkout-payment', OrderEze.views.checkout.payment);;
Ext.ns("OrderEze.views.checkout");

OrderEze.views.checkout.summary = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: "checkout-summary",
            scroll: true,
            dockedItems: [
                {
                    dock: "top",
                    xtype: "toolbar",
                    title: "Review Your Order",
                    items: [
                        {
                            text: "Back",
                            ui: "back",
                            handler: function () {
                                this.destroyMe = true;
                                Eze.navBack();
                            },
                            scope: this,
                        },
                        {
                            xtype: "spacer",
                        },
                        {
                            text: "Submit Order",
                            ui: "action",
                            id: 'submitOrder',
                            handler: this.next,
                            scope: this,
                            hidden: this.pageData.payNowMethod == "Paypal",
                        },
                    ],
                },
            ],
        });

        this.items = [
            {
                html:
                    '<div class="panel-text"><p>Please review your order below then click the <strong>Submit Order</strong> button to complete your order.</p></div>',
            },
            {
                xtype: "list",
                itemTpl:
                    '<div class="medium bold">{title}</div><div class="small">{text}</div>',
                store: new Ext.data.JsonStore({
                    fields: ["title", "text"],
                    getGroupString: function (record) {
                        return "Order Information";
                    },
                    data: this.pageData.infos,
                }),
                scroll: false,
                grouped: true,
                cls: "innerList",
                disableSelection: true,
            },
            {
                xtype: "list",
                cls: "innerList",
                scroll: false,
                grouped: true,
                store: new Ext.data.Store({
                    fields: [
                        "ItemID",
                        "Name",
                        "Price",
                        "Quantity",
                        "Tag",
                        "Info",
                        "Comments",
                    ],
                    getGroupString: function (record) {
                        return "Order Items";
                    },
                    data: this.pageData.items,
                }),
                itemTpl:
                    '<span class="right-text">{Price}</span><span class="medium">{Name}</span><div class="small">{Info}</div><div class="small">{Tag}</div>',
                disableSelection: true,
            },
            {
                xtype: "list",
                itemTpl:
                    '<div class="{cls}"><span class="right-text">{price}</span>{title}</div>',
                store: new Ext.data.JsonStore({
                    fields: ["title", "price"],
                    getGroupString: function (record) {
                        return "Pricing";
                    },
                    data: this.pageData.prices,
                }),
                scroll: false,
                grouped: true,
                cls: "innerList",
                disableSelection: true,
            },
        ];

        if (this.pageData.payNow && this.pageData.payNowMethod == "AuthorizeNet") {
            this.items.splice(0, 1, {
                html:
                    '<div class="panel-text"><h2>Review Your Order</h2><p>Please review your order and enter your payment information below, then click the <strong>Submit Order</strong> button to complete your order.</p></div>',
            });

            Ext.each(
                this.pageData.paymentData.fields,
                function (item) {
                    if (item.name == "x_exp_month" || item.name == "x_exp_year") {
                        item.listeners = {
                            change: this.updateExp,
                            scope: this,
                        };
                    }
                },
                this
            );

            this.form = new Ext.form.FormPanel({
                scroll: false,
                standardSubmit: true,
                url: this.pageData.paymentData.postBackUrl,
                method: "POST",
                bodyPadding: 10,
                items: [
                    {
                        xtype: "fieldset",
                        title: "Payment Method",
                        margin: 0,
                        defaults: { labelWidth: "40%", required: true },
                        items: [this.pageData.paymentData.fields],
                    },
                ],
            });

            this.items.splice(1, 0, this.form);
        }

        if (
            this.pageData.payNow &&
            this.pageData.payNowMethod == "HeartlandSecureSubmit"
        ) {
            this.items.splice(0, 1, {
                html:
                    '<div class="panel-text"><h2>Review Your Order</h2><p>Please review your order and enter your payment information below, then click the <strong>Submit Order</strong> button to complete your order.</p></div>',
            });

            this.form = new Ext.form.FormPanel({
                scroll: false,
                standardSubmit: true,
                url: this.pageData.paymentData.heartlandReturn,
                method: "POST",
                bodyPadding: 10,
                items: [
                    {
                        xtype: "fieldset",
                        title: "Payment Method",
                        margin: 0,
                        defaults: { labelWidth: "40%", required: true },
                        items: [this.pageData.paymentData.fields],
                    },
                ],
            });

            this.items.splice(1, 0, this.form);
        }
        if (this.pageData.payNow && this.pageData.payNowMethod === "Gdpay") {
            this.items.splice(0, 1, {
                html:
                    '<div class="panel-text"><h2>Review Your Order</h2><p>Please review your order and enter your payment information below, then click the <strong>Submit Order</strong> button to complete your order.</p></div>',
            });

            this.form = new Ext.form.FormPanel({
                scroll: false,
                url: this.pageData.paymentData.gdpayProcessUrl,
                method: "POST",
                bodyPadding: 10,
                items: [
                    {
                        xtype: "fieldset",
                        title: "Payment Method",
                        margin: 0,
                        defaults: { labelWidth: "40%", required: true },
                        items: [this.pageData.paymentData.fields],
                    },
                ],
                listeners: {
                    //listener if the form is submitted, successfully
                    submit: function (form, result) {
                        Ext.getCmp('submitOrder').setDisabled(false);
                        Eze.log("checkout/summary.next done");
                        Eze.navToUrl('#checkout/summary/true');
                    },
                    //listener if the form submission fails
                    exception: function (form, result) {
                        Ext.getCmp('submitOrder').setDisabled(false);
                        if (result.duplicateOrderId) {
                            Eze.log("checkout/summary.next duplicate order");
                            Eze.navToUrl('#checkout/complete/' + result.duplicateOrderId);
                        } else {

                            Eze.log("checkout/summary.next error");
                            Eze.navToUrl('#checkout/summary/false');
                        }
                    }
                }
            });
            // Register the card connect tokenizer callback.
            Ext.onReady(() => {
                window.addEventListener("message", (event) => {
                    this.form
                        .getFields()
                        .x_token.setValue(JSON.parse(event.data).message || "");
                });
            });
            this.items.splice(1, 0, this.form);
        }

        if (
            (this.pageData.payNow || this.pageData.payNowWithPaypal) &&
            this.pageData.payNowMethod == "Paypal"
        ) {
            this.items.splice(0, 1, {
                html:
                    '<div class="panel-text">' +
                    "<h2>Review Your Order</h2>" +
                    "<p>Please review your order then click the <strong>Paypal</strong> button to make your payment.</p>" +
                    '<p style="margin-top: 10px; font-weight: bold;">Note: You MUST return to this website to complete your order.</p>' +
                    '<p style="height: 42px; text-align: center; margin: 10px auto 0; width: 145px;"><a href="' +
                    this.pageData.paypalUrl +
                    '">' +
                    '<img src="/ordereze/images/paypal_express_co.gif" align="left" style="margin-right:7px; width: 145px; height: 42px;" /></a></p>' +
                    "</div>",
            });
        }

        OrderEze.views.checkout.summary.superclass.initComponent.call(
            this,
            arguments
        );
    },

    updateExp: function (select, value) {
        var monthField, yearField, expField;
        var fields = this.form.getFields();
        for (name in fields) {
            if (name == "x_exp_month") monthField = fields[name];
            else if (name == "x_exp_year") yearField = fields[name];
            else if (name == "x_exp_date") expField = fields[name];
        }
        if (!monthField || !yearField || !expField) return;

        var month = monthField.getValue(),
            year = yearField.getValue().substring(2);

        expField.setValue(month + "-" + year);
        Eze.log(month + "-" + year);
    },

    next: function () {
        Eze.log("next");
        if (this.pageData.payNow) {
            if (this.pageData.payNowMethod === "HeartLand") {
                var obj = this;
                Ext.Msg.alert(
                    "Attention",
                    "You will now be redirected to our secure payment page",
                    function () {
                        window.location = obj.pageData.paymentData.postBackUrl;
                    }
                );
            } else if (this.pageData.payNowMethod === "HeartlandSecureSubmit") {
                if (this.isValid()) {
                    this.processHeartlandSecureSubmit();
                }
            } else if (this.pageData.payNowMethod === "Gdpay") {
                if (this.isValid()) {
                    Ext.getCmp('submitOrder').setDisabled(true);
                    Ext.getCmp('submitOrder').setText('Submitting order...');
                    this.form.submit({ method: "POST" });
                }
            } else {
                if (this.isValid()) this.form.submit({ method: "POST" });
            }
        } else {
            Eze.load(
                "checkout",
                "summarySubmit",
                function (data) {
                    Eze.log("checkout/summary.next done");
                    Eze.navToUrl(data.nextPage);
                },
                this
            );
        }
    },

    isValid: function () {
        var vals = this.form.getValues();
        if (
            this.pageData.payNowMethod === "Gdpay" &&
            !this.form.getValues().x_token
        ) {
            Eze.alert("", "Card Number is not valid!");
            return false;
        }
        if (vals.x_first_name == "") {
            Eze.alert("", "Name on Card is required!");
            return false;
        }
        if (!vals.x_last_name && vals.x_last_name == "") {
            Eze.alert("", "Last Name is required!");
            return false;
        }
        if (this.pageData.payNowMethod !== "Gdpay" && vals.x_card_num == "") {
            Eze.alert("", "Card Number is required!");
            return false;
        }
        if (vals.x_card_code == "") {
            Eze.alert("", "Auth Code is required!");
            return false;
        }

        if (!vals.x_zip_code && vals.x_zip_code == "") {
            Eze.alert("", "Zip Code is required!");
            return false;
        }
        if (!vals.x_zip_codes && vals.x_zip_codes == "") {
            Eze.alert("", "Zip Code is required!");
            return false;
        }
        return true;
    },

    processHeartlandSecureSubmit: function () {
        var tokenField, cardField, expMonthField, expYearField, cardCodeField;
        var values = this.form.getValues();
        var fields = this.form.getFields();
        for (name in fields) {
            if (name == "x_token") tokenField = fields[name];
            if (name == "x_card_num") cardField = fields[name];
            if (name == "exp_month") expMonthField = fields[name];
            if (name == "exp_year") expYearField = fields[name];
            if (name == "x_card_code") cardCodeField = fields[name];
        }

        Ext.util.JSONP.request({
            url: this.pageData.paymentData.secureSubmitUrl,
            callbackKey: "callback",
            params: {
                api_key: values.x_public_key,
                object: "token",
                token_type: "supt",
                "card[number]": values.x_card_num,
                "card[exp_month]": values.exp_month,
                "card[exp_year]": values.exp_year,
                "card[cvc]": values.x_card_code,
                _method: "post",
                timeStamp: new Date().getTime(),
            },
            callback: function (response) {
                // Request failed, handle error
                Eze.log(response);
                if (typeof response.error === "object") {
                    // handle exception
                    Eze.alert("", response.error.message);
                } else if (typeof response.token_value != "") {
                    tokenField.setValue(response.token_value);
                    //disable cc fields to prevent post
                    cardField.disable();
                    expMonthField.disable();
                    expYearField.disable();
                    cardCodeField.disable();
                    this.form.submit({ method: "POST" });
                } else {
                    Eze.alert("", "Invalid Merchant Configuration!");
                }
            },
            scope: this,
        });
    },
});

Ext.reg("checkout-summary", OrderEze.views.checkout.summary);
;
Ext.ns('OrderEze.views.contact');

OrderEze.views.contact.index = Ext.extend(Ext.form.FormPanel, {
    initComponent: function () {
        Ext.regModel('ContactUs', {
            fields: [
                { name: 'name', type: 'string' },
                { name: 'email', type: 'string' },
                { name: 'phone', type: 'string' },
                { name: 'comment', type: 'string' }
            ]
        });

        var buttonText = "Back";
        if (this.pageData.locationId ==="")
        {
            buttonText = "Home";
        }

        Ext.apply(this, {
            scroll: 'vertical',
            standardSubmit: false,
            url: 'json.aspx',
            baseParams: { type: 'contact', action: 'submit' },
            items: [{
                xtype: 'fieldset',
                title: 'Contact Info',
                instructions: 'Please enter the information above',
                defaults: {
                    labelWidth: '35%'
                },
                items: [{
                    xtype: 'textfield',
                    name: 'name',
                    label: 'Name',
                    placeHolder: 'John Doe',
                    autoCapitalize: true,
                    required: true
                }, {
                    xtype: 'emailfield',
                    name: 'email',
                    label: 'Email',
                    placeHolder: 'you@domain.com',
                    required: true
                }, {
                    xtype: 'textfield',
                    name: 'phone',
                    label: 'Phone',
                    placeHolder: '111-111-1111'
                }, {
                    xtype: 'textareafield',
                    name: 'comment',
                    label: 'Comment',
                    required: true
                },{
                    xtype: 'hiddenfield',
                    name: 'locationId',
                    value: this.pageData.locationId
                }]
            }, {
                xtype: 'button',
                text: 'Submit',
                handler: function () {
                    this.submit({
                        method: 'POST',
                        waitMsg: { message: 'Submitting ', cls: 'loading-mask', target: Ext.getBody() },
                        success: function (form, result) {
                            form.reset();
                            Ext.Msg.alert('Thank You', 'Your message has been sent<br />Thank You', function () {
                                Eze.navBack();
                            });
                        },
                        failure: function (form, result) {
                            Ext.Msg.alert('Oops', 'There was a problem submitting your message. Please try again<br /><br />' + result.GeneralError);
                        }
                    });
                },
                scope: this
            }],
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: OrderEze.settings.moduleNames.contactUs,
                items: [{
                    text: buttonText,
                    ui: 'back',
                    handler: Eze.navBack
                }]
            }]
        });

        OrderEze.views.contact.index.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('contact-index', OrderEze.views.contact.index);;
Ext.ns("OrderEze.views.events");

OrderEze.views.events.details = Ext.extend(Ext.Panel, {

    initComponent: function () {
        if (this.eventDetails) {
            Ext.apply(this, {
                bodyCls: 'event-details',
                tpl: '<h2>{name}</h2>' +
                     '<p>{when}</p>' +
                     '<p>{description}</p>' +
                     '<p>{link}</p>',
                data: this.eventDetails,
                styleHtmlContent: true,
                scroll: true,
                layout: 'fit'
            });
        } else {
            this.html = '<div class="empty">Unable to load the details for this event.<br /> Please try again.</div>';
        }

        this.dockedItems = {
            dock: 'top',
            xtype: 'toolbar',
            title: this.eventDetails.name,
            items: {
                text: 'Back',
                ui: 'back',
                handler: Eze.navBack
            }
        };
        OrderEze.views.events.details.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('events-details', OrderEze.views.events.details);;
Ext.ns('OrderEze.views.events');

OrderEze.views.events.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Eze.log('view events/index');

        Ext.apply(this, {
            cls: 'events-index',
            layout: 'fit',
            scroll: false,
            items: [],
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Events',
                items: [{
                    text: 'Home',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }]
            }]
        });

        if (!this.events || this.events.length == 0) {
            this.items.push({
                html: '<div class="empty">There are no upcoming events. <br />Please check back again soon.</div>'
            });
        } else {
            this.items.push({
                xtype: 'list',
                scroll: 'vertical',
                itemTpl: '<div class="event"><h2>{name}</h2><p>{description}</p></div>',
                disableSelection: true,
                onItemDisclosure: true,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'Event',
                    sorters: 'sortOrder',
                    getGroupString: function (record) {
                        return record.get('dayStr');
                    },
                    data: this.events
                }),
                emptyText: '<div class="empty">There are no upcoming events. <br />Please check back again soon.</div>',
                listeners: {
                    itemtap: this.onItemTap,
                    afterrender: this.onEventItemRender,
                    scope: this
                }
            });
        }

        Eze.log('view events/index after apply');

        OrderEze.views.events.index.superclass.initComponent.call(this, arguments);


    },

    onEventItemRender: function (list) {
        var nodes = list.getNodes();
        for (var i = 0; i < nodes.length; i++) {
            var node = nodes[i];
            var record = list.getRecord(node);
            var color = record.data.borderColor;
            if (record.data.borderColor.length > 0) {
                Ext.fly(node).setStyle('border-left', '10px solid ' + color);
            }
        }
    },

    onItemTap: function (list, index, item, event) {
        Eze.log('onItemTap');
        Eze.showListMask(item);
        var r = list.store.getAt(index);
        var id = r.get('listId');
        Eze.nav('events', 'details', { calendarId: this.calendarId, id: id });
    }
});

Ext.reg('events-index', OrderEze.views.events.index);;
Ext.ns('OrderEze.views.favs');

OrderEze.views.favs.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'favs-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: 'Favorites',
                items: [{
                    xtype: 'spacer'
                }, {
                    text: 'Login',
                    ui: 'action',
                    hidden: OrderEze.settings.isLoggedIn,
                    handler: this.showLoginSheet,
                    scope: this
                }]
            }, {
                xtype: 'takeout-tabs'
            }]
        });

        if (OrderEze.settings.isLoggedIn && !OrderEze.settings.isGuestUser) {
            if (this.pageData.favs.length == 0) {
                this.items = [{
                    cls: 'panel-text',
                    html: '<h2>No Favorites Yet!</h2><p>You have not saved any items as favorites yet.</p><br /><p>When adding an item to your ' + OrderEze.cartSettings.ShoppingCartTitle + ', select the option to <strong>Add to My Favorites</strong>. The item will then be shown on this page for you to quickly add to your ' + OrderEze.cartSettings.ShoppingCartTitle + ' for your next order.</p>'
                }];
            } else {
                this.list = new Ext.ux.EditableList({
                    cls: 'favs-list',
                    scroll: false,
                    grouped: true,
                    store: new Ext.data.Store({
                        fields: ['favId', 'name', 'price', 'units', 'tag', 'comment', 'info'],
                        getGroupString: function (record) {
                            return 'Favorite Items';
                        },
                        data: this.pageData.favs,
                        proxy: new Ext.data.MemoryProxy(),
                        listeners: {
                            remove: this.deleteItem,
                            scope: this
                        }
                    }),
                    itemTpl: '<span class="right-text">{units} {price}</span><div class="medium">{name}</div>' +
                            '<div class="small">{info}</div>' +
                            '<div class="small">{tag}</div>' +
                            '<div class="small">{comment}</div>',
                    groupTpl: [
                        '<tpl for=".">',
                            '<div class="x-list-group x-group-{id}">',
                                '<h3 class="x-list-header">{group}<span class="right-text">swipe to delete item</span></h3>',
                                '<div class="x-list-group-items">',
                                    '{items}',
                                '</div>',
                            '</div>',
                        '</tpl>'
                    ],
                    disableSelection: true,
                    onItemDisclosure: true
                });

                this.list.on('itemtap', function (list, index, item, e) {
                    if (list.inSwipeMode) return;
                    var rec = list.store.getAt(index);
                    Eze.log('addToCart - ' + rec.get('name'));

                    Ext.Msg.confirm('', 'Add your favorite "' + rec.get('name') + '" to your ' + OrderEze.cartSettings.ShoppingCartTitle + '?', function (btn) {
                        if (btn == 'no') return;
                        this.selectedFavId = rec.get('favId');
                        this.addToCart();
                    }, this);

                }, this);

                this.items = [{
                    cls: 'panel-text',
                    html: '<p>Tap on a item to add it to your ' + OrderEze.cartSettings.ShoppingCartTitle + '</p>'
                }, this.list];
            }
        } else {
            this.items = [{
                cls: 'panel-text',
                html: '<h2>You are not logged in!</h2><p>You must login in order to view your Favorite Items. Click on the <strong>Login</strong> button above to login.</p>'
            }];
        }

        OrderEze.views.favs.index.superclass.initComponent.call(this, arguments);
    },

    deleteItem: function (store, record, index) {
        Eze.log('deleteItem ' + record.get('favId'));

        Eze.load('favs', 'deleteItem', record.get('favId'), function (data) {
            Eze.nav('favs');
        }, this);
    },

    addToCart: function () {
        Eze.load('favs', 'addToCart', this.selectedFavId, function (data) {
            if (data.status == 'TIMENOTSET') {
                this.showPickupTimeSheet();
            } else if (data.status == 'UNAVAILABLE') {
                this.showUnavailableSheet(data);
            } else if (data.status == 'OK') {
                Eze.utils.cartItemCount = data.cartItemCount;
                Eze.nav('favs');
            }
        }, this);
    },

    showPickupTimeSheet: function () {
        Eze.log('showPickupTimeSheet');
        var sheet = new OrderEze.views.takeout.PickupTimeSheet({
            listeners: {
                done: function (theSheet) {
                    this.addToCart();
                },
                scope: this
            }
        });
        sheet.show();
    },

    showUnavailableSheet: function (data) {
        Eze.log('showUnavailableSheet');
        var sheet = new OrderEze.views.takeout.UnavailableSheet({
            availData: data,
            listeners: {
                done: function (theSheet, showChangeTimeSheet) {
                    if (showChangeTimeSheet)
                        this.showPickupTimeSheet();
                },
                scope: this
            }
        });
        sheet.show();
    },

    showLoginSheet: function () {
        Eze.log('showLoginSheet');
        var sheet = new OrderEze.views.account.LoginSheet({
            instructions: 'You must login to the website to view your favorite items',
            listeners: {
                loggedin: function (theSheet) {
                    Eze.log('showLoginSheet done');
                    Eze.nav('favs');
                },
                scope: this
            }
        });
        sheet.show();
    }

});

Ext.reg('favs-index', OrderEze.views.favs.index);;
Ext.ns('OrderEze.views.form');

OrderEze.views.form.index = Ext.extend(Ext.form.FormPanel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'form-index',
            scroll: 'vertical',
            standardSubmit: false,
            url: 'json.aspx',
            baseParams: { type: 'form', action: 'submit', formId: this.formId },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.formName,
                items: [{
                    text: 'Home',
                    ui: 'back',
                    handler: Eze.navBack
                }]
            }]
        });

        this.items = [];
        if (this.topText !== '') {
            this.items.push({ html: '<div class="form-content">' + this.topText + '</div>' });
        }

        if (this.formFields.length >= 1) {
            this.defaults = { xtype: 'fieldset' };
            this.items.push([this.formFields, { // pulled from server by the form controller
                xtype: 'button',
                text: 'Submit',
                handler: function () {
                    this.submit({
                        method: 'POST',
                        waitMsg: { message: 'Submitting ', cls: 'loading-mask', target: Ext.getBody() },
                        success: function (form, result) {
                            form.reset();

                            if (result.Data.thankYouMsg === '' || result.Data.thankYouMsg === '<p></p>') {
                                Ext.Msg.alert('Thank You', 'The form has been submitted successfully', function () {
                                    Eze.nav('home');
                                });
                            } else {
                                Ext.Msg.alert('', result.Data.thankYouMsg, function () {
                                    Eze.nav('home');
                                });                                
                            }
                        },
                        failure: function (form, result) {
                            var msg = 'There was a problem submitting the form. Please try again';
                            if (result.GeneralError) msg += '<br /><br />' + result.GeneralError;
                            Ext.Msg.alert('Oops', msg);
                        }
                    });
                },
                scope: this
            }]);
        } else {
            this.items = [{ html: '<div class="empty">There was a problem loading the form. <br />Please try again.</div>' }];
        }

        if (this.bottomText !== '') {
            this.items.push({ html: '<div class="form-content">' + this.bottomText + '</div>', margin: '40px 0 0 0' });
        }

        OrderEze.views.form.index.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('form-index', OrderEze.views.form.index);;
Ext.ns('OrderEze.views.home');

OrderEze.views.home.index = Ext.extend(Ext.Panel, {

    initComponent: function () {
        Ext.apply(this, {
            cls: 'home-index',
            scroll: true
        });

        if (OrderEze.settings.showTitleBar) {
            this.dockedItems = [{
                dock: 'top',
                xtype: 'toolbar',
                title: OrderEze.settings.siteName
            }];
        }
        this.items = [];

        if (OrderEze.settings.logoUrl) {
            this.items.push({
                html: '<div style="height: 100px;"><img src="' + OrderEze.settings.logoUrl + '" style="width: 100%; height: 100px;" /></div>'
            });
        }


        var lists = {};

        if (OrderEze.settings.welcomMsg) {
            lists.welcomMsg = {
                cls: 'welcome',
                html: '<p>' + OrderEze.settings.welcomMsg + '</p>'
            };
        }

        if (OrderEze.settings.storeHours.length > 0) {
            var storeHourItems = '<div style="padding: 3px;">' + OrderEze.settings.storeHoursTitle + '</div><table style="margin: auto; text-align: left;">';
            for (var j = 0; j < OrderEze.settings.storeHours.length; j++) {
                storeHourItems += '<tr><td style="padding: 3px">' + OrderEze.settings.storeHours[j].days + ':</td><td>' + OrderEze.settings.storeHours[j].hours + '</td></tr>';
            }
            storeHourItems += '</table>';
            if (OrderEze.settings.storeHoursNotes.length > 0) {
                storeHourItems += '<div style="padding: 3px;">' + OrderEze.settings.storeHoursNotes + '</div>';
            }
            lists.hours = {
                cls: 'welcome',
                html: storeHourItems
            };
        }

        if (OrderEze.settings.photoSlider.length > 0 && OrderEze.settings.photoSlider[0] !== 0) {
            var killTimer = 0;
            var x = 0;
            function performSwipe(direction) {
                if (direction == 'right') { //Go anti-clockwise
                    if (x !== 0) {
                        x--;
                    } else {
                        x = OrderEze.settings.photoSlider.length - 1;
                    }
                } else if (direction == 'left') { //Go clockwise
                    x++;
                }
                pnlCarousel.setActiveItem(Math.abs(x) % (OrderEze.settings.photoSlider.length), { type: OrderEze.settings.photoSliderTransitionType, direction: direction });
            }

            var photoItems = [];
            for (var i = 0; i < OrderEze.settings.photoSlider.length; i++) {
                var tempObj = new Object();
                if (OrderEze.settings.photoSlider[i].imageClickURL.length > 0) {
                    tempObj.html = '<div style="height: 150px;"><a href="' + OrderEze.settings.photoSlider[i].imageClickURL + '"><img src="' + OrderEze.settings.photoSlider[i].imageURL + '" style="width: 100%; height: 150px;" /></a></div>';
                } else {
                    tempObj.html = '<div style="height: 150px;"><img src="' + OrderEze.settings.photoSlider[i].imageURL + '" style="width: 100%; height: 150px;" /></div>';
                }
                photoItems.push(tempObj);
            }
            var pnlCarousel = new Ext.Panel({
                padding: '0 0 0 0',
                listeners: {
                    el: {
                        'swipe': function (e, o) {
                            clearInterval(killTimer);
                            performSwipe(e.direction);
                        },
                        'touchstart': function (e, o) {
                            clearTimeout(this._delayScrollableTimeout);
                            // prevent vertical scrolling when swiping on the slider
                            this.setScrollable(false);
                            // delay call to this.setScrollable(true);
                            this._delayScrollableTimeout = Ext.defer(this.setScrollable, 300, this, [true]);
                        },
                        scope: this
                    },
                    afterrender: function () {
                        if (OrderEze.settings.photoSliderTransitionInterval !== 0) {
                            killTimer = setInterval(function () {
                                performSwipe('left');
                            }, OrderEze.settings.photoSliderTransitionInterval);
                        }
                    }
                },
                height: 150,
                maxHeight: 150,
                layout: 'card',
                defaults: {
                    flex: 1
                },
                items: photoItems
            });
            lists.photoSlider = pnlCarousel;
        }

        if (OrderEze.settings.modules.length > 0) {
            lists.modules = {
                xtype: 'list',
                itemTpl: '<tpl if="isUnderlined"><label style="text-decoration:underline;"></tpl>{title}<tpl if="isUnderlined"></label></tpl>',
                store: new Ext.data.JsonStore({
                    model: 'NavItem',
                    data: OrderEze.settings.modules
                }),
                scroll: false,
                cls: 'innerList',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var rec = list.store.getAt(index);
                        Eze.log('itemtap');
                        if (rec.get('internalUrl') !== '' && !rec.get('isLink')) {
                            Eze.showListMask(item);
                            Eze.navToUrl(rec.get('internalUrl'));
                        } else if (rec.get('externalUrl') !== '' && rec.get('isLink')) {
                            window.open(rec.get('externalUrl'));
                        } else if (!rec.get('isLink')) {
                            Eze.showListMask(item);
                            var r = list.store.getAt(index);
                            Eze.nav(r.get('key'), 'index', { id: r.get('subkey') });
                        }
                    },
                    afterrender: function (list) {
                        var records = list.getStore().getRange();
                        Eze.log('afterrender');
                        Eze.log(records);
                        for (var i = 0; i < records.length; i++) {
                            if (records[i].get('isLink') == true) {
                                var node = list.getNode(records[i]);
                                Ext.fly(node).addCls('item-link');
                                Eze.log(node);
                            }
                        }
                    }
                }
            };
        }

        if (OrderEze.settings.social.length > 0) {
            lists.social = {
                xtype: 'list',
                itemTpl: '<table><tr><td width="35px"><img src="../js/sencha1/images/icons/{image}.png?v9" style="width: 30px; height: 30px;" /></td><td>{title}</td></tr></table>',
                store: new Ext.data.JsonStore({
                    model: 'NavItem',
                    data: OrderEze.settings.social
                }),
                scroll: false,
                cls: 'innerList',
                disableSelection: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        Eze.log('itemtap');
                        var socialItem = list.store.getAt(index);
                        var url = socialItem.get('href');
                        if (socialItem.get('key') === 'youtube') {
                            // Fixes bug4620 where youtube links will display a blank page if the native app is installed
                            url += url.indexOf('?') > 0 ? '&' : '?';
                            url += 'o=U&noapp=1'; // append an undocumented querystring param which stops the native app from opening the link
                        }
                        window.open(url);
                    }
                }
            };
        }

        if (OrderEze.settings.siteInfo.length > 0) {
            lists.siteInfo = {
                xtype: 'list',
                scroll: false,
                cls: 'innerList',
                itemTpl: '<table><tr><td width="30px">' +
                    '<img src="../js/sencha1/images/icons/{image}.png?v5" /></td><td><h2>{header}</h2><a class="info-link {key}" href="{href}" target="_blank">{title}</a>' +
                    '</td></tr></table>',
                store: new Ext.data.JsonStore({
                    model: 'NavItem',
                    data: OrderEze.settings.siteInfo
                }),
                disableSelection: true
            };
        }

        var order = OrderEze.settings.groupsOrder;
        for (var i = 0; i < order.length; i++) {
            var group = order[i];
            if (lists[group]) this.items.push(lists[group]);
        }

        if (OrderEze.settings.showDashboardLnk) {
            var dashboardTarget = window == window.top ? ' target="_blank"' : '';
            var dashboardName = OrderEze.settings.dashboardLinkName;
            this.items.push({
                id: "dashboardLnk",
                xtype: 'list',
                scroll: false,
                disableSelection: true,
                cls: 'innerList',
                itemTpl: '<tpl"><a href="/admin/socialhub" style="text-decoration:none;"' + dashboardTarget + '> ' + dashboardName + ' </a></tpl>',
                store: new Ext.data.JsonStore({ model: 'NavItem', data: [{}], }),
            });
        }

        var portalName = OrderEze.settings.portalName;
        var portalUrl = OrderEze.settings.portalDomain;
        var desktopLink = '';
        if (OrderEze.settings.showDesktopLink) desktopLink = '<a class="desktop" href="' + OrderEze.settings.desktopWebsiteUrl + '">Desktop Website</a>';
        var btmLinks = [];
        if (OrderEze.settings.showDashboardLnk) btmLinks.push('<a id="lnkLogin" href="#">Login</a><a id="lnkLogout" href="#">Logout</a>');
        if (OrderEze.settings.showBranding) btmLinks.push('Powered by <a href="http://www.' + portalUrl + '" target="_blank"> ' + portalName + '</a>');
        var htmlFooter = '<table width="100%" cellpadding="5"><tr>' +
                         '<td align="left">' + desktopLink + '</td>' +
                         '<td align="right">' + btmLinks.join('&nbsp;|&nbsp;') + '</td>' +
                         '</tr></table>';

        this.items.push({
            cls: 'footer',
            html: htmlFooter,
            style: { paddingBottom: '15px' },
            listeners: {
                afterrender: function () {
                    if (!OrderEze.settings.showDashboardLnk) return;
                    var lnkLogin = Ext.get('lnkLogin');
                    var lnkLogout = Ext.get('lnkLogout');
                    var dashboardLnk = Ext.get('dashboardLnk');

                    if (OrderEze.settings.isAdminUser) {
                        lnkLogin.hide();
                        lnkLogout.show();
                        dashboardLnk.show();
                    } else {
                        lnkLogin.show();
                        lnkLogout.hide();
                        dashboardLnk.hide();
                    }
                    lnkLogin.on('click', function (event) {
                        event.preventDefault();
                        Eze.log('showLoginSheet');
                        var sheet = new OrderEze.views.account.LoginSheet({
                            instructions: 'You must login to the website to view your account information',
                            listeners: {
                                loggedin: function (theSheet) {
                                    Eze.log('showLoginSheet done');
                                    Ext.get('lnkLogin').hide();
                                    Ext.get('lnkLogout').show();
                                    if (OrderEze.settings.isAdminUser) Ext.get('dashboardLnk').show();
                                    Eze.nav('home', 'index', { refresh: true });
                                },
                                scope: this
                            }
                        });
                        sheet.show();
                    });
                    lnkLogout.on('click', function (event) {
                        event.preventDefault();
                        Eze.log('logout');
                        Ext.Msg.confirm('', 'Are you sure you want to logout?', function (btn) {
                            if (btn == 'no') return;
                            Eze.load('account', 'logout', function (data) {
                                OrderEze.settings.isLoggedIn = false;
                                OrderEze.settings.isAdminUser = false;
                                Ext.get('lnkLogin').show();
                                Ext.get('dashboardLnk').hide();
                                Ext.get('lnkLogout').hide();
                                Eze.nav('home', 'index', { refresh: true });
                            }, this);
                        }, this);
                    });
                }
            }
        });

        OrderEze.views.home.index.superclass.initComponent.call(this);
    }
});

Ext.reg('home-index', OrderEze.views.home.index);;
Ext.ns('OrderEze.views.locations');

OrderEze.views.locations.details = Ext.extend(Ext.Panel, {
    initComponent: function () {

        var self = this;

        // Add listener to 
        window.addEventListener("resize", function () {
            self.resizeMap();
        });
        
        Ext.apply(this, {
            cls: 'locations-details',
            items: [{
                xtype: 'component',
                id: 'map-container-location-details',
                height: '100%',
                width: '100%',
                autoEl: {
                    tag: 'div'
                }
            }],
            listeners: {
                activate: this._onActivate,
                deactivate: function (v) {
                    v.destroy();
                }
            },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.pageData.name,
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        Eze.navBack();
                    },
                    scope: this
                }]
            }, {
                dock: 'bottom',
                xtype: 'toolbar',
                items: [{
                    xtype: 'spacer'
                }, {
                    text: 'Get Directions',
                    ui: 'action',
                    handler: function () {
                        Eze.log('Directions');
                        window.location = this.pageData.directionsUrl;
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Call Now',
                    ui: 'action',
                    handler: function () {
                        Eze.log('Call Now');
                        window.location = this.pageData.callUsUrl;
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }]
            }]
        });

        OrderEze.views.locations.details.superclass.initComponent.call(this, arguments);

    },

    _onActivate: function () {

        var container = document.getElementById("map-container-location-details");

        var center = this.mapCenter;

        this.map = new mapboxgl.Map({
            container: container, // container id
            style: 'mapbox://styles/mapbox/streets-v9', // stylesheet location
            center: center, // starting position [lng, lat]
            zoom: 14 // starting zoom
        });

        var popup = new mapboxgl.Popup({ offset: 25 }).setHTML('<div class="map-info"><b>' + this.pageData.name + '</b><br />' + this.pageData.addressHtml + '<br />tel: ' + this.pageData.phone + '</div>');

        var marker = new mapboxgl.Marker()
            .setLngLat(center)
            .setPopup(popup)
            .addTo(this.map);

        marker.togglePopup();     
        // get the south west and north east map bounds
        var sw = this.map.getBounds().getSouthWest();
        var ne = this.map.getBounds().getNorthEast();
        // add padding to allow the user to pan and scroll tha map around the area
        var swPadded = new mapboxgl.LngLat(sw.lng - 0.07, sw.lat - 0.04);
        var nePadded = new mapboxgl.LngLat(ne.lng + 0.07, ne.lat + 0.04);
        this.map.setMaxBounds(new mapboxgl.LngLatBounds(swPadded, nePadded));
    },

    resizeMap: function () {

        if (!this.map) {
            return;
        }

        var that = this;

        // The time out was added because the map breaks 
        // on window resize if you resize instantly
        setTimeout(function () {
            that.map.resize();
        }, 1000);
    }
});

Ext.reg('locations-details', OrderEze.views.locations.details);;
Ext.ns('OrderEze.views.locations');

OrderEze.views.locations.index = Ext.extend(Ext.Panel, {
    destroyMe: false,

    initComponent: function () {

        var self = this;

        window.addEventListener("resize", function () {
            self.resizeMap();
        });

        Ext.apply(this, {
            cls: 'locations-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: OrderEze.settings.moduleNames.locations,
                items: [{
                    text: 'Home',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }]
            }],
            items: [{
                xtype: 'component',
                id: 'map-container-location',
                height: 200,
                width: '100%',
                autoEl: {
                    tag: 'div'
                }
            }, {
                xtype: 'list',
                cls: 'locations-list',
                scroll: false,
                store: new Ext.data.JsonStore({
                    model: 'Location',
                    data: this.pageData
                }),
                itemTpl: '<div class="location">' +
                            '<h2>{name}</h2><p>{addressHtml}</p><p>tel: {phone}</p>' +
                            '<table style="font-size: 0.7em;"><tpl for="hours"><tr><td style="padding: 1px 10px 1px 0">{days}:</td><td>{hours}</td></tr></tpl></table>' +
                            '<p>{notes}</p>' +
                         '</div>' +
                         '<div class="x-layout-box-inner x-layout-box" style="padding-top: 5px;-webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-box-pack: start; -webkit-box-align: center;">' +
                             '<div data-type="directions" class="x-button x-button-action" style=" font-size:16px;"><span data-type="directions" class="x-button-label">Directions</span></div>' +
                             '<div class=" x-component" style="-webkit-box-flex: 1;"></div>' +
                             '<div data-type="call" class="x-button x-button-action" style="font-size:16px;"><span data-type="call" class="x-button-label">Call</span></div>' +
                             '<div class=" x-component" style="-webkit-box-flex: 1;"></div>' +
                             '<div data-type="contact" class="x-button x-button-action" style="font-size:16px;"><span data-type="contact" class="x-button-label">Contact Us</span></div>' +
                             '<div class=" x-component" style="-webkit-box-flex: 1;"></div>' +
                         '</div>',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var buttonType = e.target.getAttribute("data-type");
                        if (buttonType === "directions") {
                            Eze.log('Directions');                            
                            window.location = this.pageData[index].directionsUrl;
                        }
                        else if (buttonType === "call") {
                            Eze.log('Call Now');
                            window.location = "tel:" + this.pageData[index].phone;
                        }
                        else if (buttonType === "contact") {
                            var rec = list.store.getAt(index);
                            var locId = rec.get("locationId");
                            Eze.log("Contact with location id: " + locId);
                            Eze.nav("contact", "index", { id: locId });
                        }
                        else {
                            var rec = list.store.getAt(index);
                            Eze.showListMask(item);
                            Eze.nav('locations', 'details', { id: rec.get('locationId') });
                        }
                    },
                    scope: this
                }
                }],
            listeners: {
                activate: this._onActivate,
                deactivate: function (v) {
                    v.hide();
                }
            }
        });
        Eze.log('view locations/index init');    

        this.mapboxClient = mapboxSdk({ accessToken: mapboxgl.accessToken });
        
        OrderEze.views.locations.index.superclass.initComponent.call(this, arguments);
    },

    _onActivate: function () {

        var container = document.getElementById("map-container-location");

        var center = this.mapCenter;

        this.map = new mapboxgl.Map({
            container: container, // container id
            style: 'mapbox://styles/mapbox/streets-v9', // stylesheet location
            center: center, // starting position [lng, lat]
            zoom: 14 // starting zoom
        });
        
        this.markerBounds = new mapboxgl.LngLatBounds();

        for (var i = 0; i < this.pageData.length; i++) {
            this.addMarker(this.pageData[i], i === this.pageData.length - 1);
        }
        // get the south west and north east map bounds
        var sw = this.map.getBounds().getSouthWest();
        var ne = this.map.getBounds().getNorthEast();
        // add padding to allow the user to pan and scroll tha map around the area
        var swPadded = new mapboxgl.LngLat(sw.lng - 0.07, sw.lat - 0.04);
        var nePadded = new mapboxgl.LngLat(ne.lng + 0.07, ne.lat + 0.04);
        this.map.setMaxBounds(new mapboxgl.LngLatBounds(swPadded, nePadded));
    },

    resizeMap: function () {

        if (!this.map) {
            return;
        }

        var that = this;

        // The time out was added because the map breaks 
        // on window resize if you resize instantly
        setTimeout(function () {
            that.map.resize();
        }, 1000);
    },

    addMarker: function (location, fitBounds) {

        if (location.latitude && location.longitude) {
            this.addMarkerMap([location.longitude, location.latitude], fitBounds);
            return;
        }

        var that = this;

        this.mapboxClient.geocoding.forwardGeocode({ query: location.addressString, limit: 1 }).send().then((response) => {
            var mapCenter = response.body.features[0].center;

            that.addMarkerMap(mapCenter, fitBounds);

        }, (error) => { alert('There was a problem finding the address'); });
    },

    addMarkerMap: function (mapCenter, fitBounds) {
        this.markerBounds.extend(mapCenter);

        var popup = new mapboxgl.Popup({ offset: 25 }).setHTML('<div>' + location.name + '</div>');

        new mapboxgl.Marker()
            .setLngLat(mapCenter)
            .setPopup(popup)
            .addTo(this.map);

        if (fitBounds) {
            this.map.fitBounds(this.markerBounds, { padding: { top: 15, bottom: 15, left: 50, right: 50 }, duration: 0 });
        }
    }
});

Ext.reg('locations-index', OrderEze.views.locations.index);;
Ext.ns('OrderEze.views.mailing');

OrderEze.views.mailing.index = Ext.extend(Ext.form.FormPanel, {
    initComponent: function () {
        Ext.apply(this, {
            scroll: 'vertical',
            standardSubmit: false,
            url: 'json.aspx',
            baseParams: { type: 'mailing', action: 'submit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: OrderEze.settings.moduleNames.mailingList,
                items: [{
                    text: 'Home',
                    ui: 'back',
                    handler: function () {
                        Eze.navBack();
                    },
                    scope: this
                }]
            }]
        });

        if (this.pageData.formFields.length >= 1) {
            this.defaults = { xtype: 'fieldset' };
            this.items = [{
                xtype: 'panel',
                html: '<div class="x-form-fieldset-instructions" style="margin: 0;">' + this.pageData.headerText + '</div>',
                bodyPadding: 0
            }, this.pageData.formFields, { // pulled from server by the mailing controller
                xtype: 'button',
                text: 'Submit',
                handler: function () {
                    this.submit({
                        method: 'POST',
                        waitMsg: { message: 'Submitting ', cls: 'loading-mask', target: Ext.getBody() },
                        success: function (form, result) {
                            form.removeAll();
                            form.add([{
                                xtype: 'panel',
                                html: '<div class="x-form-fieldset-instructions" style="margin: 0;">' + this.pageData.thankYouText + '</div>',
                                bodyPadding: 0
                            }]);
                            form.doLayout();
                            form.scroller.setOffset({ x: 0, y: 0 }, false);
                        },
                        failure: function (form, result) {
                            var msg = 'There was a problem submitting the form. Please try again';
                            if (result.GeneralError) msg += '<br /><br />' + result.GeneralError;
                            Ext.Msg.alert('Oops', msg);
                        }
                    });
                },
                scope: this
            }];
        } else {
            this.items = [{ html: '<div class="empty">There was a problem loading the mailing list form. <br />Please try again.</div>'}];
        }

        OrderEze.views.mailing.index.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('mailing-index', OrderEze.views.mailing.index);;
Ext.ns('OrderEze.views.map');

OrderEze.views.map.index = Ext.extend(Ext.Panel, {
    initComponent: function () {

        var self = this;

        window.addEventListener("resize", function () {
            self.resizeMap();
        });

        Eze.log('view map/index');
        
        Eze.log(this.mapCenter);

        Ext.apply(this, {
            id: 'map',
            address: OrderEze.settings.address,
            items: [{
                xtype: 'component',
                id: 'map-container',
                height: '100%',
                width: '100%',
                autoEl: {
                    tag: 'div'
                }
            }],
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: OrderEze.settings.moduleNames.map,
                items: [{
                    text: 'Home',
                    ui: 'back',
                    handler: function () {
                        Eze.navBack();
                    }
                }]
            }, {
                dock: 'bottom',
                xtype: 'toolbar',
                items: [{
                    xtype: 'spacer'
                }, {
                    text: 'Get Directions',
                    ui: 'action',
                    handler: function () {
                        window.location = OrderEze.settings.directionsUrl;
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }]
            }],
            listeners: {
                activate: this._onActivate,
                deactivate: function (v) {
                    v.destroy();
                }
            }
        });
        
        OrderEze.views.map.index.superclass.initComponent.call(this, arguments);
    },

    _onActivate: function () {        

        var container = document.getElementById("map-container");

        var center = this.mapCenter;

        this.map = new mapboxgl.Map({
            container: container, // container id
            style: 'mapbox://styles/mapbox/streets-v9', // stylesheet location
            center: center, // starting position [lng, lat]
            zoom: 14 // starting zoom
        });

        var popup = new mapboxgl.Popup({ offset: 25 }).setHTML('<div class="map-info"><b>' + OrderEze.settings.siteName + '</b><br />' + OrderEze.settings.addressHtml + '</div>');

        var marker = new mapboxgl.Marker()
            .setLngLat(center)
            .setPopup(popup)            
            .addTo(this.map);

        marker.togglePopup();
        // get the south west and north east map bounds
        var sw = this.map.getBounds().getSouthWest();
        var ne = this.map.getBounds().getNorthEast();
        // add padding to allow the user to pan and scroll tha map around the area
        var swPadded = new mapboxgl.LngLat(sw.lng - 0.07, sw.lat - 0.04);
        var nePadded = new mapboxgl.LngLat(ne.lng + 0.07, ne.lat + 0.04);
        this.map.setMaxBounds(new mapboxgl.LngLatBounds(swPadded, nePadded));
    },
    resizeMap: function () {

        if (!this.map) {
            return;
        }

        var that = this;

        // The time out was added because the map breaks 
        // on window resize if you resize instantly
        setTimeout(function () {
            that.map.resize();
        }, 1000);
    }
});

Ext.reg('map-index', OrderEze.views.map.index);;
Ext.ns('OrderEze.views.menu');

OrderEze.views.menu.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        var isTopDept = this.deptData.parentId == 0;
        Ext.apply(this, {
            cls: 'menu-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: isTopDept ? OrderEze.settings.moduleNames.menus : this.deptData.name,
                items: [{
                    text: isTopDept ? 'Home' : 'Back',
                    ui: 'back',
                    handler: function () {
                        if (isTopDept) {
                            this.destroyAll = true;
                        }
                        Eze.navBack();
                    },
                    scope: this
                }]
            }]
        });

        this.items = [];
        if (isTopDept) {
            if (this.deptData.topText !== '')
                this.items.push({ html: '<div class="dept-info">' + this.deptData.topText + '</div>' });
        } else {
            this.items.push({ html: '<div class="dept-info"><h2>' + this.deptData.name + '</h2><p>' + this.deptData.description + '</p><div class="clear"></div></div>' });
        }

        if (this.deptData.TotalCount == 0) {
            this.items.push({ html: '<div class="empty">There are no items to display in this menu. <br />Please check back again soon.</div>' })
        }
        if (this.deptData.depts && this.deptData.depts.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'depts-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'Dept',
                    getGroupString: function (record) {
                        return record.get('groupName');
                    },
                    data: this.deptData.depts
                }),
                itemTpl: '<a href="/mobile/#menu/index/{deptId}" class="o-internal-link">{name}</a>',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var rec = list.store.getAt(index);

                        Eze.showListMask(item);
                        Eze.nav('menu', 'index', { id: rec.get('deptId'), deptName: rec.get('name') });
                    },
                    scope: this
                }
            });
        }
        if (this.deptData.items && this.deptData.items.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'items-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'MenuItem',
                    getGroupString: function (record) {
                        return record.get('groupName');
                    },
                    data: this.deptData.items
                }),
                itemTpl: '<div class="item">' +
                            '{imageHtml}' +
                            '<div class="item-content"><div><span class="item-price">{price}</span><h2>{name}</h2></div><p>{description}</p></div>' +
                         '</div>',
                disableSelection: true
            });
        }

        if (isTopDept && this.deptData.bottomText !== '') {
            this.items.push({ html: '<div class="dept-info">' + this.deptData.bottomText + '</div>' });
        }

        OrderEze.views.menu.index.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('menu-index', OrderEze.views.menu.index);;
Ext.ns('OrderEze.views.menupro');

OrderEze.views.menupro.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'menu-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: OrderEze.settings.moduleNames.menuspro,
                items: [{
                    text: 'Home',
                    ui: 'back',
                    handler: function () {
                        Eze.navBack();
                    },
                    scope: this
                }]
            }]
        });

        this.items = [];

        if (this.menus.length === 0) {
            this.items.push({ html: '<div class="empty">There are no items to display in this menu. <br />Please check back again soon.</div>' })
        }
        if (this.menus && this.menus.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'depts-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'ProMenu',
                    getGroupString: function (record) {
                        return 'Menus';
                    },
                    data: this.menus
                }),
                itemTpl: '<a href="/mobile/#menupro/menu/{id}" class="o-internal-link">{name}</a>',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var rec = list.store.getAt(index);

                        Eze.showListMask(item);
                        Eze.nav('menupro', 'menu', { id: rec.get('id') });
                    },
                    scope: this
                }
            });
        }

        OrderEze.views.menupro.index.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('menupro-index', OrderEze.views.menupro.index);;
Ext.ns('OrderEze.views.menupro');

OrderEze.views.menupro.menu = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'menu-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.menu.name,
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        Eze.navBack();
                    },
                    scope: this
                }]
            }]
        });

        this.items = [];

        this.items.push({ html: '<div class="dept-info"><h2>' + this.menu.name + '</h2><p>' + this.menu.description + '</p><div class="clear"></div></div>' });

        if (this.menu.sections.length === 0) {
            this.items.push({ html: '<div class="empty">There are no sections to display in this menu. <br />Please check back again soon.</div>' })
        }

        if (this.menu.featuredItems && this.menu.featuredItems.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'items-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'ProMenuItem',
                    getGroupString: function (record) {
                        return 'Featured Items';
                    },
                    data: this.menu.featuredItems
                }),
                itemTpl: '<div class="item">' +
                           '{imageHtml}' +
                            '<div class="item-content"><div><span class="item-price">{price}</span><h2>{name}</h2></div><p>{description}</p></div>' +
                         '</div>',
                disableSelection: true
            });
        }

        if (this.menu.sections && this.menu.sections.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'depts-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'ProSection',
                    getGroupString: function (record) {
                        return 'Menu Sections';
                    },
                    data: this.menu.sections
                }),
                itemTpl: '<a href="/mobile/#menupro/section/{id}" class="o-internal-link">{name}</a>',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var rec = list.store.getAt(index);

                        Eze.showListMask(item);
                        Eze.nav('menupro', 'section', { id: rec.get('id') });
                    },
                    scope: this
                }
            });
        }

        OrderEze.views.menupro.menu.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('menupro-menu', OrderEze.views.menupro.menu);;
Ext.ns('OrderEze.views.menupro');

OrderEze.views.menupro.section = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'menu-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.section.name,
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        Eze.navBack();
                    },
                    scope: this
                }]
            }]
        });

        this.items = [];

        this.items.push({ html: '<div class="dept-info"><h2>' + this.section.name + '</h2><p>' + this.section.description + '</p><div class="clear"></div></div>' });

        if (this.section.menuItems.length === 0) {
            this.items.push({ html: '<div class="empty">There are no items to display in this section. <br />Please check back again soon.</div>' })
        }

        if (this.section.menuItems && this.section.menuItems.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'items-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'ProMenuItem',
                    getGroupString: function (record) {
                        return 'Items';
                    },
                    data: this.section.menuItems
                }),
                itemTpl: '<div class="item">' +
                           '{imageHtml}' +
                            '<div class="item-content"><div><span class="item-price">{price}</span><h2>{name}</h2></div><p>{description}</p></div>' +
                         '</div>',
                disableSelection: true
            });
        }

        OrderEze.views.menupro.section.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('menupro-section', OrderEze.views.menupro.section);;
Ext.ns('OrderEze.views.page');

OrderEze.views.page.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        Ext.apply(this, {
            cls: 'page-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.pageData.name,
                items: [{
                    text: this.pageData.parentId == 0 ? 'Home' : 'Back',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }]
            }]
        });

        this.items = [];
        if (this.pageData.html) {
            this.items.push({
                bodyCls: 'page-content',
                html: this.pageData.html
            });
        }

        

        if (this.pageData.childPages && this.pageData.childPages.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'pages-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'Page',
                    getGroupString: function (record) {
                        return '&nbsp;';
                    },
                    data: this.pageData.childPages
                }),
                itemTpl: '<tpl if="isLink"><a href="{externalUrl}" <tpl if="isUnderlined==false">style="text-decoration:none;"</tpl> target="_blank"></tpl> {name} <tpl if="isLink"></a></tpl>',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var rec = list.store.getAt(index);
                        if (rec.get('type') == 'Has Content') {
                            Eze.showListMask(item);
                            Eze.nav('page', 'index', { id: rec.get('pageId') });
                        } else if (rec.get('type') == 'Internal Link') {
                            Eze.navToUrl(rec.get('internalUrl'));
                        }
                    },
                    afterrender: function (list) {
                        var records = list.getStore().getRange();
                        Eze.log(records);
                        for (var i = 0; i < records.length; i++) {
                            if (records[i].get('isLink') == true) {
                                var node = list.getNode(records[i]);
                                Ext.fly(node).addCls('item-link');
                                Eze.log(node);
                            }
                        }
                    },
                    scope: this
                }
            });
        }

        OrderEze.views.page.index.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('page-index', OrderEze.views.page.index);;
Ext.ns('OrderEze.views.photos');

OrderEze.views.photos.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        var tpl = '<table class="gallery">' +
                    '<tr>' +
                        '<td class="gallery-img">' +
                            '<img src="{image_url}" />' +
                        '</td>' +
                        '<td class="gallery-content">' +
                            '<h2>{name}</h2>' +
                            '<p>{image_count} photos</p>' +
                        '</td>' +
                '</table>';

        Ext.apply(this, {
            id: 'photos-index',
            cls: 'photos-index',
            scroll: false,
            layout: 'fit',
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: OrderEze.settings.moduleNames.photoGallery,
                items: [{
                    text: 'Home',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }]
            }],
            items: [new Ext.List({
                id: 'galleries-list',
                cls: 'galleries',
                scroll: true,
                store: new Ext.data.JsonStore({
                    model: 'Photo',
                    data: this.galleries
                }),
                itemTpl: tpl,
                emptyText: '<div class="empty">There are no galleries to display. <br />Please check back again soon.</div>',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: this.onGalleryClick
                }
            })]
        });

        OrderEze.views.photos.index.superclass.initComponent.call(this);
    },

    onGalleryClick: function (dataview, index, item, e) {
        var id = dataview.store.getAt(index).get('gallery_id');
        var name = dataview.store.getAt(index).get('name');

        Eze.showListMask(item);
        Eze.nav('photos', 'show', { id: id });
    }
});

Ext.reg('photos-index', OrderEze.views.photos.index);;
Ext.ns("OrderEze.views.photos");

OrderEze.views.photos.show = Ext.extend(Ext.Carousel, {
    initComponent: function () {
        Ext.apply(this, {
            items: [{
                xtype: 'carousel',
                ui: 'light',
                cls: 'photos-show',
                items: this.photos
            }],
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.galleryName || 'Photos',
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: Eze.navBack
                }]
            }]
        });

        OrderEze.views.photos.show.superclass.initComponent.call(this);
    }
});

Ext.reg('photos-show', OrderEze.views.photos.show);;
Ext.ns('OrderEze.views.rpage');

OrderEze.views.rpage.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        var cacheBust = (new Date().getTime());
        var pageUrl = document.location.protocol + '//' + document.location.host + '/' + this.pageData.slug + '/mobile-view?' + cacheBust;
        var frameId = this.getId() + '-frame';
        Ext.apply(this, {
            cls: 'rpage-index',
            scroll: false,
            layout: 'fit',
            style: 'width: 100%; height: 100%;',
            html: '<iframe id="' + frameId + '" width="100%" height="100%" frameborder="0" src="' + pageUrl + '" style=""></iframe>',
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.pageData.name,
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        this.destroyMe = true;
                        Eze.navBack();
                    },
                    scope: this
                }]
            }],
            listeners: {
                deactivate: function (v) {
                    if (v.destroyMe) v.destroy();
                },
                afterrender: function () {
                    var isMobilePreview = (window.top !== window);
                    var frame = Ext.get(frameId);
                    frame.on('load', function () {
                        if (isMobilePreview) {
                            // add class to the body of the iframe document in order to style it 
                            // differently within the mobile preview iframe
                            frame.dom.contentDocument.body.className += " mobile-preview";
                        }
                    });
                }
            }
        });

        OrderEze.views.rpage.index.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('rpage-index', OrderEze.views.rpage.index);;
Ext.ns('OrderEze.views.takeout');

OrderEze.views.takeout.index = Ext.extend(Ext.Panel, {
    initComponent: function () {
        var isTopDept = this.deptData.parentId == 0;
        Ext.apply(this, {
            cls: 'menu-index',
            scroll: true,
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: isTopDept ? OrderEze.settings.moduleNames.takeout : this.deptData.name,
                items: [{
                    text: isTopDept ? 'Home' : 'Back',
                    ui: 'back',
                    hidden: isTopDept,
                    handler: function () {
                        if (isTopDept) {
                            this.destroyAll = true;
                            Eze.nav('home', 'index');
                        } else {
                            Eze.navBack();
                        }
                    },
                    scope: this
                }]
            }, {
                xtype: 'takeout-tabs'
            }]
        });

        this.items = [];
        if (isTopDept) {
            if (this.deptData.topText !== '')
                this.items.push({ html: '<div class="dept-info">' + this.deptData.topText + '</div>' });
        } else {
            this.items.push({ html: '<div class="dept-info"><h2>' + this.deptData.name + '</h2><p>' + this.deptData.description + '</p><div class="clear"></div></div>' });
        }

        if (this.deptData.TotalCount == 0) {
            this.items.push({ html: '<div class="empty">There are no items to display in this menu. <br />Please check back again soon.</div>' })
        }
        if (this.deptData.depts && this.deptData.depts.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'depts-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'Dept',
                    getGroupString: function (record) {
                        return record.get('groupName');
                    },
                    data: this.deptData.depts
                }),
                itemTpl: '{name}',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var rec = list.store.getAt(index);
                        Eze.showListMask(item);
                        Eze.nav('takeout', 'index', { id: rec.get('deptId'), deptName: rec.get('name') });
                    },
                    scope: this
                }
            });
        }
        if (this.deptData.items && this.deptData.items.length > 0) {
            this.items.push({
                xtype: 'list',
                cls: 'items-list',
                scroll: false,
                grouped: true,
                store: new Ext.data.JsonStore({
                    model: 'MenuItem',
                    getGroupString: function (record) {
                        return record.get('groupName');
                    },
                    data: this.deptData.items
                }),
                itemTpl: '<div class="item">' +
                            '{imageHtml}' +
                            '<div class="item-content"><div style="padding-top: 6px;"><span class="item-price">{price}</span><h2>{name}</h2><p>{description} {availHtml}</p></div></div>' +
                         '</div>',
                disableSelection: true,
                onItemDisclosure: true,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var rec = list.store.getAt(index);

                        Eze.showListMask(item);
                        Eze.nav('takeout', 'item', { id: rec.get('itemId') });
                    },
                    scope: this
                }
            });
        }

        if (isTopDept && this.deptData.topText !== '') {
            this.items.push({ html: '<div class="dept-info">' + this.deptData.bottomText + '</div>' });
        }
        if (!this.deptData.ccProcessorMobileReady && isTopDept) {
            Eze.alert("", "Online Ordering has been temporarily disabled. Please give us a call to place your order. We are sorry for the inconvenience.");
        }

        OrderEze.views.takeout.index.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('takeout-index', OrderEze.views.takeout.index);;
Ext.ns('OrderEze.views.takeout');

OrderEze.views.takeout.item = Ext.extend(Ext.Panel, {
    item: null,

    initComponent: function () {
        this.item = {
            itemId: this.itemData.itemId,
            quantity: '1',
            tag: '',
            comments: '',
            addToFavs: false,
            answers: {},
            weight: this.itemData.weightMeasurement,
            toppings: {}
        };
        Ext.apply(this, {
            cls: 'takeout-index',
            scroll: true,
            listeners: {
                deactivate: function (v) {
                    if (this.unitsPicker) this.unitsPicker.destroy();
                    if (this.weightPicker) this.weightPicker.destroy();
                    if (this.requests) this.requests.destroy();
                    if (this.tag) this.tag.destroy();
                    v.destroy();
                },
                scope: this
            },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.itemData.name,
                items: [{
                    text: 'Back',
                    ui: 'back',
                    handler: function () {
                        Eze.navBack();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    id: 'cartButton',
                    ui: 'action',
                    text: this.itemData.price == 0 ? 'Add' : 'Add - ' + this.itemData.price.formatMoney() + '',
                    handler: this.onAddClicked,
                    scope: this
                }]
            }]
        });

        this.items = [{
            html: '<div class="item-info">' +
                    '<span class="item-price">' + this.itemData.price.formatMoney() + this.itemData.priceUnitText + '</span><h2>' + this.itemData.name + '</h2><p>' + this.itemData.description + '</p>' + this.itemData.availHtml +
                  '</div>'
        }];

        var optionsData = [
            { key: 'quantity', group: '&nbsp;', text: this.itemData.itemUnitsTitle, value: this.item.quantity }
        ];

        if (this.itemData.allowTagSection) {
            optionsData.push({ key: 'tag', group: '&nbsp;', text: this.itemData.itemTagTitle, value: '', hidden: true });
        }

        if (this.itemData.allowItemComments) {
            optionsData.push({ key: 'requests', group: '&nbsp;', text: 'Special Requests', value: '' });
        }

        this.optionsStore = new Ext.data.Store({
            fields: ['key', 'group', 'text', 'value'],
            data: optionsData,
            groupField: 'group'
        });

        this.items.push({
            xtype: 'list',
            cls: 'takeout-options-list',
            itemTpl: '<div class="medium">{text}</div><p class="small">{value}</p>',
            grouped: true,
            scroll: false,
            disableSelection: true,
            onItemDisclosure: true,
            store: this.optionsStore,
            listeners: {
                itemtap: function (list, index, item, e) {
                    var rec = list.store.getAt(index);
                    if (rec.get('key') == 'quantity') {
                        this.showQuantityPicker();
                    } else if (rec.get('key') == 'tag') {
                        this.showTagSheet();
                    } else if (rec.get('key') == 'requests') {
                        this.showRequestsSheet();
                    }
                },
                scope: this
            }
        });

        if (this.itemData.type == 'Customize') this.addCustomizeQuestions();
        if (this.itemData.type == 'Weight') this.addWeightOption();
        if (this.itemData.type == 'Pizza') {
            this.addPizzaQuestions();
            this.addCustomizeQuestions();
        }

        this.form = new Ext.form.FormPanel({
            items: [{
                xtype: 'fieldset',
                items: [{
                    xtype: 'checkboxfield',
                    label: 'Add to My Favorites',
                    labelWidth: '85%',
                    value: 'true',
                    listeners: {
                        check: this.favChanged,
                        uncheck: this.favChanged,
                        scope: this
                    }
                }]
            }]
        });
        this.items.push(this.form);

        OrderEze.views.takeout.item.superclass.initComponent.call(this, arguments);
    },

    addPizzaQuestions: function () {
        if (!Ext.isEmpty(this.itemData.pizzaToppings)) {
            var toppingOptions;
            if (this.itemData.pizzaIsWholePie) {
                toppingOptions = [
                    { key: 'whole', group: 'Pizza Toppings', text: 'Toppings for the Whole Pie', value: '', price: '' },
                    { key: 'firsthalf', group: 'Pizza Toppings', text: 'Toppings for the First Half', value: '', price: '' },
                    { key: 'secondhalf', group: 'Pizza Toppings', text: 'Toppings for the Second Half', value: '', price: '' }
                ];
            } else {
                toppingOptions = [
                    { key: 'slice', group: 'Pizza Toppings', text: 'Toppings for the Slice', value: '', price: '' }
                ];
            }

            this.pizzaToppingsStore = new Ext.data.Store({
                fields: ['key', 'group', 'text', 'value', 'price'],
                data: toppingOptions,
                groupField: 'group'
            });

            this.items.push({
                xtype: 'list',
                cls: 'takeout-options-list',
                itemTpl: '<div class="medium">{text}</div><p class="small"><span style="float: right; margin-right: 5px;">{price}</span>{value}</p>',
                grouped: true,
                scroll: false,
                disableSelection: true,
                onItemDisclosure: true,
                store: this.pizzaToppingsStore,
                listeners: {
                    itemtap: function (list, index, item, e) {
                        var rec = list.store.getAt(index);
                        var key = rec.get('key');
                        var sheet = new OrderEze.views.takeout.PizzaSheet({
                            initialValues: this.item.toppings[key],
                            formTitle: 'Choose ' + rec.get('text'),
                            message: this.itemData.pizzaMessage,
                            toppings: this.itemData.pizzaToppings,
                            listeners: {
                                done: function (theSheet, values) {
                                    this.item.toppings[key] = values;
                                    this.updateView();
                                },
                                scope: this
                            }
                        });
                        sheet.show();
                    },
                    scope: this
                }
            });
        }
    },

    addCustomizeQuestions: function () {
        this.questionsStore = new Ext.data.Store({
            fields: ['GroupID', 'Name', 'IsRequired', 'DisplayMode', 'MinExtrasAllowed', 'MaxExtrasAllowed', 'SummaryForCustomer', 'value', 'price'],
            data: this.itemData.questions,
            groupField: 'IsRequired',
            sorters: [{
                property: 'IsRequired',
                direction: 'DESC'
            }]
        });

        this.items.push({
            xtype: 'list',
            cls: 'takeout-questions-list',
            itemTpl: '<div class="medium">{Name}</div><p class="small"><span style="float: right; margin-right: 5px;">{price}</span>{value}</p>',
            grouped: true,
            scroll: false,
            disableSelection: true,
            onItemDisclosure: true,
            store: this.questionsStore,
            listeners: {
                itemtap: function (list, index, item, e) {
                    var rec = list.store.getAt(index);
                    var extraGroup = rec.data;
                    var sheet = new OrderEze.views.takeout.CustomizeSheet({
                        initialValues: this.item.answers['group' + extraGroup.GroupID],
                        extraGroup: extraGroup,
                        listeners: {
                            done: function (theSheet, values) {
                                this.item.answers['group' + extraGroup.GroupID] = values;
                                this.updateView();
                            },
                            scope: this
                        }
                    });
                    sheet.show();
                },
                scope: this
            }
        });
    },

    addWeightOption: function () {
        this.weightStore = new Ext.data.Store({
            fields: ['key', 'group', 'text', 'value'],
            data: [
                { key: 'weight', group: 'Weight', text: this.getWeightDisplay(), value: this.itemData.price.formatMoney() }
            ],
            groupField: 'group'
        });

        this.items.push({
            xtype: 'list',
            cls: 'takeout-options-list',
            itemTpl: '<div class="right-text small">{value}</div><div class="medium">{text}</div>',
            grouped: true,
            scroll: false,
            disableSelection: true,
            onItemDisclosure: true,
            store: this.weightStore,
            listeners: {
                itemtap: function (list, index, item, e) {
                    if (!this.weightPicker) {
                        this.weightPicker = new Ext.Picker({
                            useTitles: true,
                            slots: [{
                                name: 'amount',
                                title: 'Weight',
                                data: this.itemData.questions
                            }],
                            listeners: {
                                change: function (p, selected) {
                                    this.item.weight = selected.amount;
                                    this.updateView();
                                },
                                scope: this
                            }
                        });
                    }
                    this.weightPicker.show();
                },
                scope: this
            }
        });
    },

    getWeightDisplay: function () {
        return this.item.weight + " lb";
    },

    showQuantityPicker: function () {
        var numbers = [];
        for (var i = 1; i <= 25; i++)
            numbers.push({ text: '' + i, value: '' + i });

        if (!this.unitsPicker) {
            this.unitsPicker = new Ext.Picker({
                useTitles: true,
                slots: [{
                    name: 'amount',
                    title: 'Quantity',
                    data: numbers
                }],
                listeners: {
                    change: function (p, selected) {
                        this.item.quantity = selected.amount;
                        this.updateView();
                    },
                    scope: this
                }
            });
        }
        this.unitsPicker.show();
    },

    showRequestsSheet: function () {
        if (!this.requests) {
            this.requests = new OrderEze.views.takeout.TextSheet({
                title: 'Special Requests',
                placeHolder: this.itemData.specialRequestText,
                instructions: 'Price may be adjusted based on requests',
                listeners: {
                    hide: function () {
                        this.item.comments = this.requests.getValue();
                        this.updateView();
                    },
                    scope: this
                }
            });
        }
        this.requests.show();
    },

    showTagSheet: function () {
        if (!this.tag) {
            this.tag = new OrderEze.views.takeout.TextSheet({
                title: 'Tag this item',
                placeHolder: 'John Doe',
                instructions: 'Enter a name. Your order will have prices grouped by name.',
                type: 'textbox',
                listeners: {
                    hide: function () {
                        this.item.tag = this.tag.getValue();
                        this.updateView();
                    },
                    scope: this
                }
            });
        }
        this.tag.show();
    },

    updateView: function () {
        this.optionsStore.getAt(0).set('value', this.item.quantity);
        if (this.itemData.allowTagSection) this.optionsStore.getAt(1).set('value', this.item.tag);
        if (this.itemData.allowItemComments) { var cmtIndex = this.itemData.allowTagSection ? 2 : 1; this.optionsStore.getAt(cmtIndex).set('value', this.item.comments); }

        var subtotal = this.itemData.price;

        if (this.weightStore) {
            var priceForOnePound = this.itemData.price * (1 / this.itemData.weightMeasurement);
            subtotal = priceForOnePound * this.item.weight;
            this.weightStore.getAt(0).set('text', this.getWeightDisplay());
            this.weightStore.getAt(0).set('value', subtotal.formatMoney());
        }
        if (this.questionsStore) {
            var groupRecords = this.questionsStore.getRange();
            Ext.each(groupRecords, function (r, index) {
                var groupId = r.get('GroupID');
                var vals = this.item.answers['group' + groupId];
                if (Ext.isEmpty(vals)) {
                    r.set('value', '');
                    r.set('price', '');
                } else {
                    var extraNames = '', extrasPrice = 0;
                    Ext.each(r.data.answers, function (extra, i) {
                        var extraCount = this.getExtraCount(vals, extra.ExtraID);
                        if (extraCount > 0) {
                            if (extraNames != '') extraNames += ', ';
                            extraNames += extra.Name;
                            if (extraCount > 1) extraNames += '(' + extraCount + ')';
                            var price = OrderEze.cartSettings.MenuExtraPricePerUnit ? (extra.Price * extraCount) : extra.Price;
                            extrasPrice += price;
                            subtotal += price;
                        }
                    }, this);
                    r.set('value', extraNames);
                    r.set('price', extrasPrice.formatMoney(true));
                }
            }, this);
        }
        if (this.pizzaToppingsStore) {
            var pizzaRecords = this.pizzaToppingsStore.getRange();
            var recordWithFirstOptionChosen, toppingsCount = 0;
            Ext.each(pizzaRecords, function (r, index) {
                var key = r.get('key');
                var vals = this.item.toppings[key];
                if (Ext.isEmpty(vals)) {
                    r.set('value', '');
                    r.set('price', '');
                } else {
                    r.set('value', vals.join(', '));
                    r.set('price', '');
                    toppingsCount += vals.length;
                    // put the price for all of the toppings on the first row that has a topping chosen
                    if (!recordWithFirstOptionChosen) recordWithFirstOptionChosen = r;
                }
            }, this);
            if (recordWithFirstOptionChosen) {
                Eze.load('takeout', 'getPizzaToppingsCost', this.item, function (json) {
                    recordWithFirstOptionChosen.set('price', json.cost.formatMoney(true));
                    subtotal += json.cost;
                    subtotal = subtotal * Ext.num(this.item.quantity, 1);
                    Ext.getCmp('cartButton').setText(subtotal == 0 ? 'Add' : 'Add - ' + subtotal.formatMoney());
                }, this);
            }
        }

        subtotal = subtotal * Ext.num(this.item.quantity, 1);
        Ext.getCmp('cartButton').setText(subtotal == 0 ? 'Add' : 'Add - ' + subtotal.formatMoney());
    },

    getExtraCount: function (vals, extraId) {
        // returns the count of a extraId in the values list
        // vals will look like ["72|1", "74|2"] or ["65", "67"]
        var id = '' + extraId,
            size = id.length,
            len = vals.length;

        for (var i = 0; i < len; i++) {
            var key = vals[i];
            if (key.substring(0, size) == extraId) {
                if (key.indexOf('|') != -1) {
                    return parseInt(key.substring(size + 1));
                } else {
                    return 1;
                }
            }
        }
        return 0;
    },

    onAddClicked: function () {
        if (!this.isValid()) return;
        Eze.log('onAddClicked');
        Eze.log(this.item);

        Eze.load('takeout', 'addToCart', this.item, function (json) {
            if (json.status == 'TIMENOTSET') {
                this.showPickupTimeSheet();
            } else if (json.status == 'UNAVAILABLE') {
                this.showUnavailableSheet(json);
            } else if (json.status == 'OK') {
                Eze.utils.cartItemCount = json.cartItemCount;
                Eze.navBack();
            }
        }, this);
    },

    isValid: function () {
        var groups = this.itemData.questions;
        for (var i = 0; i < groups.length; i++) {
            var g = groups[i],
                vals = this.item.answers['group' + g.GroupID] || [],
                min = g.MinExtrasAllowed,
                max = g.MinExtrasAllowed,
                len = vals.length;

            if ((max > 0 && len > max) || len < min) {
                Ext.Msg.alert('We\'re Sorry...', 'You must answer all of the required questions to continue');
                return false;
            }
        }

        return true;
    },

    showPickupTimeSheet: function () {
        Eze.log('showPickupTimeSheet');
        var sheet = new OrderEze.views.takeout.PickupTimeSheet({
            listeners: {
                done: function () {
                    this.onAddClicked();
                },
                scope: this
            }
        });
        sheet.show();
    },

    showUnavailableSheet: function (json) {
        Eze.log('showUnavailableSheet');
        var sheet = new OrderEze.views.takeout.UnavailableSheet({
            availData: json,
            listeners: {
                done: function (theSheet, showChangeTimeSheet) {
                    if (showChangeTimeSheet)
                        this.showPickupTimeSheet();
                },
                scope: this
            }
        });
        sheet.show();
    },

    favChanged: function (cb) {
        if (cb.isChecked()) {
            Eze.log('checked');
            if (!OrderEze.settings.isLoggedIn) {
                Eze.log('show login form');
                var sheet = new OrderEze.views.account.LoginSheet({
                    instructions: 'You must be logged in in order to add an item to your Favorites',
                    listeners: {
                        loggedin: function () {
                            Eze.log('showLoginSheet loggedin');
                            Eze.log('setting fav to true 2');
                            this.item.addToFavs = true;
                        },
                        cancel: function () {
                            Eze.log('showLoginSheet cancel');
                            Eze.log('cb.uncheck()');
                            cb.uncheck();
                        },
                        scope: this
                    }
                });
                sheet.show();
            } else {
                Eze.log('setting fav to true');
                this.item.addToFavs = true;
            }
        } else {
            Eze.log('unchecked');
            Eze.log('setting fav to false');
            this.item.addToFavs = false;
        }
    }
});

Ext.reg('takeout-item', OrderEze.views.takeout.item);;
Ext.ns('OrderEze.views.takeout');

OrderEze.views.takeout.CustomizeSheet = Ext.extend(Ext.Sheet, {
    extraGroup: null,
    initialValues: null,

    initComponent: function () {
        this.addEvents('done');

        Ext.apply(this, {
            padding: 0,
            stretchY: true,
            stretchX: true,
            modal: false,
            height: Eze.viewport.getHeight(),
            layout: { type: 'fit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                items: [{
                    text: 'Cancel',
                    handler: function () {
                        this.hide();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Done',
                    ui: 'action',
                    handler: function () {
                        var vals = this.getValues();
                        if (this.isValid(vals)) {
                            this.fireEvent('done', this, vals);
                            this.hide();
                        }
                    },
                    scope: this
                }]
            }]
        });

        if (Ext.isEmpty(this.initialValues)) this.initialValues = [];

        var fields;
        if (this.extraGroup.AllowMultiple) {
            fields = this.getDropdownFields();
        } else if (this.extraGroup.SingleSelection) {
            fields = this.getRadioFields();
        } else {
            fields = this.getCheckboxFields();
        }


        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                xtype: 'fieldset',
                title: this.extraGroup.Name,
                instructions: this.extraGroup.Summary,
                defaults: {
                    labelWidth: '80%'
                },
                items: fields
            }]
        });
        this.items = [this.form];

        OrderEze.views.takeout.CustomizeSheet.superclass.initComponent.call(this, arguments);

        this.on('hide', function (v) { v.destroy(); });
    },

    isValid: function (vals) {
        var min = this.extraGroup.MinExtrasAllowed,
            max = this.extraGroup.MaxExtrasAllowed;
        var len = 0;
        for (var i = 0; i < vals.length; i++) {
            var parts = vals[i].split('|');
            len += this.getExtraCount(vals, parts[0]);
        }

        if ((max > 0 && len > max) || len < min) {
            Ext.Msg.alert('Not so fast...', this.extraGroup.Summary);
            return false;
        }

        return true;
    },

    getValues: function () {
        var formVals = this.form.getValues();
        var vals;
        if (this.extraGroup.AllowMultiple) {
            vals = [];
            for (var key in formVals) {
                if (formVals[key] > 0) {
                    vals.push(key + '|' + formVals[key]);
                }
            }
        } else {
            vals = formVals.extraIds;
            if (!Ext.isArray(vals)) vals = [vals];
        }
        Eze.log(vals);
        return vals;
    },

    getRadioFields: function () {
        var fields = [],
            answers = this.extraGroup.answers,
            len = answers.length;
        for (var i = 0; i < len; i++) {
            var a = answers[i];
            fields.push({
                xtype: 'radiofield',
                label: a.Name + '</span>' + '<span style="float: right;">' + a.Price.formatMoney(),
                name: 'extraIds',
                value: a.ExtraID,
                checked: this.initialValues.indexOf('' + a.ExtraID) != -1
            });
        }
        return fields;
    },

    getCheckboxFields: function () {
        var fields = [],
            answers = this.extraGroup.answers,
            len = answers.length;
        for (var i = 0; i < len; i++) {
            var a = answers[i];
            fields.push({
                xtype: 'checkboxfield',
                label: a.Name + '</span>' + '<span style="float: right;">' + a.Price.formatMoney(),
                name: 'extraIds',
                value: a.ExtraID,
                checked: this.initialValues.indexOf('' + a.ExtraID) != -1
            });
        }
        return fields;
    },

    getDropdownFields: function () {
        var fields = [],
            answers = this.extraGroup.answers,
            len = answers.length,
            numbers = [];

        for (var i = 0; i <= 15; i++) numbers.push({ text: '' + i, value: i });

        for (var i = 0; i < len; i++) {
            var a = answers[i];
            fields.push({
                xtype: 'selectfield',
                label: a.Name + '</span>' + '<span style="float: right;">' + a.Price.formatMoney(),
                labelWidth: '70%',
                name: '' + a.ExtraID,
                options: numbers,
                value: this.getExtraCount(this.initialValues, a.ExtraID)
            });
        }
        return fields;
    },

    getExtraCount: function (vals, extraId) {
        if (Ext.isEmpty(vals)) return 0;
        
        // returns the count of a extraId in the values list
        // vals will look like ["72|1", "74|2"] or ["65", "67"]
        var id = '' + extraId,
            size = id.length,
            len = vals.length;

        for (var i = 0; i < len; i++) {
            var key = vals[i];
            if (key.substring(0, size) == extraId) {
                if (key.indexOf('|') != -1) {
                    return parseInt(key.substring(size + 1));
                } else {
                    return 1;
                }
            }
        }
        return 0;
    }
});

Ext.reg('takeout-CustomizeSheet', OrderEze.views.takeout.CustomizeSheet);;
Ext.ns('OrderEze.views.account');

OrderEze.views.account.LoginSheet = Ext.extend(Ext.Sheet, {
    instructions: '',

    initComponent: function () {
        this.addEvents('loggedin', 'cancel');

        Ext.apply(this, {
            padding: 0,
            stretchY: true,
            stretchX: true,
            modal: false,
            height: Eze.viewport.getHeight(),
            layout: { type: 'fit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.title,
                items: [{
                    text: 'Cancel',
                    handler: function () {
                        this.fireEvent('cancel', this);
                        this.hide();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Register',
                    ui: 'action',
                    handler: this.showRegisterSheet,
                    scope: this
                }]
            }]
        });

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                id: 'loginFieldset',
                xtype: 'fieldset',
                title: 'Please Login',
                defaults: {
                    labelWidth: '35%'
                },
                items: [{
                    xtype: 'textfield',
                    label: 'Username',
                    name: 'username'
                }, {
                    xtype: 'passwordfield',
                    label: 'Password',
                    name: 'password'
                }]
            }, {
                id: 'loginFieldsetBtn',
                xtype: 'button',
                ui: 'action',
                text: 'Login',
                handler: this.doLogin,
                scope: this
            }, {
                id: 'guestFieldsetBtn',
                xtype: 'button',
                cls: 'x-toolbar-dark',
                text: '<div style="color:#fff !important">Checkout as Guest</div>',
                handler: this.guestCheckout,
                hidden: !this.showGuest,
                scope: this
            }, {
                cls: 'x-form-fieldset-instructions',
                html: this.instructions
            }]
        });

        this.items = [this.form];


        OrderEze.views.account.LoginSheet.superclass.initComponent.call(this, arguments);

        this.on('hide', function (v) { v.destroy(); });
    },

    doLogin: function () {
        Eze.log('doLogin');
        Eze.load('account', 'login', this.form.getValues(), function(data) {
            Eze.log('doLogin success');
            OrderEze.settings.isLoggedIn = true;
            OrderEze.settings.isAdminUser = data.isAdminUser;
            this.fireEvent('loggedin', this);
            this.hide();
        }, this);
    },

    guestCheckout: function () {
        Eze.log('doGuestLogin');
        Eze.load('account', 'guestLogin', this.form.getValues(), function (data) {
            Eze.log('doGuestLogin success');
            OrderEze.settings.isLoggedIn = true;
            OrderEze.settings.isGuestUser = true;
            this.fireEvent('loggedin', this);
            this.hide();
        }, this);
    },

    showRegisterSheet: function () {
        Eze.log('showRegisterSheet');
        Eze.load('account', 'register', function (json) {
            var sheet = new OrderEze.views.account.RegisterSheet({
                pageData: json,
                listeners: {
                    registered: function (theSheet) {
                        Eze.log('showRegisterSheet done');
                        this.fireEvent('loggedin', this);
                        this.hide();
                    },
                    scope: this
                }
            });
            sheet.show();
        }, this);

    }

});

Ext.reg('account-LoginSheet', OrderEze.views.account.LoginSheet);;
Ext.ns('OrderEze.views.account');

OrderEze.views.account.OrderLookupSheet = Ext.extend(Ext.Sheet, {
    initComponent: function () {
        this.addEvents('cancel');

        Ext.apply(this, {
            padding: 0,
            stretchY: true,
            stretchX: true,
            modal: false,
            height: Eze.viewport.getHeight(),
            layout: { type: 'fit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.title,
                items: [{
                    text: 'Cancel',
                    handler: function () {
                        this.fireEvent('cancel', this);
                        this.hide();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }]
            }]
        });

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                id: 'orderLookupFieldset',
                xtype: 'fieldset',
                title: 'Please enter your phone number',
                items: [{
                    id: 'txtOrderLookup',
                    xtype: 'textfield',
                    name: 'phone',
                }]
            }, {
                id: 'orderLookupFieldsetBtn',
                xtype: 'button',
                ui: 'action',
                text: 'Search Orders',
                handler: this.doSearchByPhone,
                scope: this
            }, {
                cls: 'x-form-fieldset-instructions',
                html: 'Enter your phone number provided during checkout'
            }]
        });

        this.items = [this.form];


        OrderEze.views.account.OrderLookupSheet.superclass.initComponent.call(this, arguments);

        this.on('hide', function (v) { v.destroy(); });
    },

    doSearchByPhone: function () {
        Eze.log('doSearchByPhone');
        Eze.nav("account", "index", { phoneSearch: Ext.getCmp('txtOrderLookup').getValue() });
        this.hide();
    },
});

Ext.reg('account-OrderLookupSheet', OrderEze.views.account.OrderLookupSheet);;
Ext.ns('OrderEze.views.takeout');

OrderEze.views.takeout.PickupTimeSheet = Ext.extend(Ext.Sheet, {
    initComponent: function () {
        this.addEvents('done');

        this.time = {
            locationId: 0,
            type: 'Pickup',
            date: '',
            time: '',
            changed: ''
        }

        Ext.apply(this, {
            cls: 'pickup-time o-full-sheet',
            padding: 0,
            stretchY: true,
            stretchX: true,
            modal: false,
            height: Eze.viewport.getHeight(),
            layout: { type: 'fit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                items: [{
                    text: 'Cancel',
                    handler: function () {
                        this.hide();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    id: 'doneButton',
                    text: 'Save Time',
                    ui: 'action',
                    handler: function () {
                        if (this.time.locationId === '') {
                            Eze.alert('', 'Please select a location');
                            return;
                        }
                        Eze.load('takeout', 'savePickupTime', this.time, function () {
                            this.fireEvent('done', this);
                            this.hide();
                        }, this);
                    },
                    scope: this
                }]
            }]
        });

        this.locationsField = new Ext.form.Select({
            name: 'location',
            label: 'Location',
            listeners: {
                change: function (select, value) {
                    this.time.locationId = value;
                    this.time.changed = 'location';
                    Ext.defer(function () {
                        Eze.load('takeout', 'getPickupTimes', this.time, this.updateFormFields, this);
                    }, 100, this);
                },
                scope: this
            }
        });

        this.pickupField = new Ext.form.Radio({
            label: 'For Pickup',
            name: 'type',
            hidden: true,
            listeners: {
                check: function () {
                    if (this.time.type == 'Pickup') return;
                    this.time.type = 'Pickup';
                    this.time.changed = 'type';
                    if (!this.updatingForm) Eze.load('takeout', 'getPickupTimes', this.time, this.updateFormFields, this);
                },
                scope: this
            }
        });

        this.deliveryField = new Ext.form.Radio({
            label: 'For Delivery',
            name: 'type',
            hidden: true,
            listeners: {
                check: function () {
                    if (this.time.type == 'Delivery') return;
                    this.time.type = 'Delivery';
                    this.time.changed = 'type';
                    if (!this.updatingForm) Eze.load('takeout', 'getPickupTimes', this.time, this.updateFormFields, this);
                },
                scope: this
            }
        });

        this.datesField = new Ext.form.Select({
            name: 'date',
            label: 'Date',
            listeners: {
                change: function (select, value) {
                    this.time.date = value;
                    this.time.changed = 'date';
                    if (!this.updatingForm) {
                        Ext.defer(function () {
                            Eze.load('takeout', 'getPickupTimes', this.time, this.updateFormFields, this);
                        }, 100, this);
                    }
                },
                scope: this
            }
        });

        this.timesField = new Ext.form.Select({
            name: 'time',
            label: 'Time',
            listeners: {
                change: function (select, value) {
                    this.time.time = value;
                },
                scope: this
            }
        });

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                id: 'titleMsg',
                cls: 'x-form-fieldset-title',
                html: 'Choose your Pickup date and time',
                bodyPadding: 0
            }, {
                xtype: 'fieldset',
                id: 'locationSet',
                defaults: { labelWidth: '30%' },
                items: [this.locationsField]
            }, {
                xtype: 'fieldset',
                defaults: { labelWidth: '80%' },
                items: [this.pickupField, this.deliveryField]
            }, {
                id: 'instrMsg',
                xtype: 'fieldset',
                defaults: { labelWidth: '50px' },
                items: [this.datesField, this.timesField]
            }]
        });
        this.items = [this.form];

        OrderEze.views.takeout.PickupTimeSheet.superclass.initComponent.call(this, arguments);

        this.on('hide', function (v) { v.destroy(); });
        this.on('show', function () {
            Eze.load('takeout', 'getPickupTimes', this.updateFormFields, this);
        }, this);
    },

    updateFormFields: function (json) {
        this.updatingForm = true;

        if (json.allLocations) {
            if (json.allLocations.length > 1) {
                json.allLocations.splice(0, 0, { text: '- Select Location -', value: '' });
                this.time.locationId = json.locationId > 0 ? json.locationId : '';
                this.locationsField.setOptions(json.allLocations);
                this.locationsField.setValue(this.time.locationId);
            } else {
                this.time.locationId = json.locationId;
                Ext.getCmp('locationSet').setVisible(false);
            }

            Ext.getCmp('instrMsg').setInstructions(json.instructions);
        }

        if (json.allTypes) {
            this.time.type = json.type;
            var hasPickup = json.allTypes.indexOf('Pickup') != -1;
            this.pickupField.setVisible(hasPickup);
            this.pickupField.setChecked(json.type == 'Pickup');

            var hasDelivery = json.allTypes.indexOf('Delivery') != -1;
            this.deliveryField.setVisible(hasDelivery);
            this.deliveryField.setChecked(json.type == 'Delivery');
        }

        if (json.allDates) {
            this.time.date = json.date;
            this.datesField.setOptions(json.allDates.slice());
            this.datesField.setValue(json.date);

            Ext.fly('titleMsg').setHTML(json.title);
        }

        if (json.allTimes) {
            this.time.time = json.time;
            this.timesField.setOptions(json.allTimes.slice());
            this.timesField.setValue(json.time);
        }


        this.updatingForm = false;
    },

    savePickupTime: function () {
        Eze.log('savePickupTime');
        Eze.log(json);
        this.time.type = json.selectedType;

    }
});

Ext.reg('takeout-PickupTimeSheet', OrderEze.views.takeout.PickupTimeSheet);;
Ext.ns('OrderEze.views.takeout');

OrderEze.views.takeout.PizzaSheet = Ext.extend(Ext.Sheet, {
    toppings: null,
    formTitle: null,
    message: null,
    initialValues: null,

    initComponent: function () {
        this.addEvents('done');

        Ext.apply(this, {
            padding: 0,
            stretchY: true,
            stretchX: true,
            modal: false,
            height: Eze.viewport.getHeight(),
            layout: { type: 'fit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                items: [{
                    text: 'Cancel',
                    handler: function () {
                        this.hide();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Done',
                    ui: 'action',
                    handler: function () {
                        var vals = this.form.getValues().toppings;
                        if (!Ext.isArray(vals)) vals = [vals];
                        this.fireEvent('done', this, vals);
                        this.hide();
                    },
                    scope: this
                }]
            }]
        });

        if (Ext.isEmpty(this.initialValues)) this.initialValues = [];
        if (Ext.isEmpty(this.toppings)) this.toppings = [];

        var fields = [],
            len = this.toppings.length;
        for (var i = 0; i < len; i++) {
            var t = this.toppings[i];
            fields.push({
                xtype: 'checkboxfield',
                label: t,
                name: 'toppings',
                value: t,
                checked: this.initialValues.indexOf(t) != -1
            });
        }

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                xtype: 'fieldset',
                title: this.formTitle,
                instructions: this.message,
                defaults: {
                    labelWidth: '80%'
                },
                items: fields
            }]
        });
        this.items = [this.form];
        
        OrderEze.views.takeout.PizzaSheet.superclass.initComponent.call(this, arguments);
    }
});

Ext.reg('takeout-PizzaSheet', OrderEze.views.takeout.PizzaSheet);;
Ext.ns('OrderEze.views.account');

OrderEze.views.account.RegisterSheet = Ext.extend(Ext.Sheet, {
    initComponent: function () {
        this.addEvents('registered', 'cancel');

        Ext.apply(this, {
            padding: 0,
            stretchY: true,
            stretchX: true,
            modal: false,
            height: Eze.viewport.getHeight(),
            layout: { type: 'fit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.title,
                items: [{
                    text: 'Cancel',
                    handler: function () {
                        this.fireEvent('cancel', this);
                        this.hide();
                    },
                    scope: this
                }]
            }]
        });

        var accountFields = [
            {
                xtype: 'textfield',
                label: 'Username',
                name: 'username',
                listeners: {
                    change: function (tb, value, oldValue) {
                        Eze.load('account', 'validateUsername', value, Ext.emptyFn, this, true);
                    }
                }
            }, {
                xtype: 'emailfield',
                label: 'Email',
                name: 'email'
            }, {
                xtype: 'passwordfield',
                label: 'Password',
                name: 'password'
            }, {
                xtype: 'passwordfield',
                label: 'Confirm Password',
                name: 'confirmpassword'
            }, {
                xtype: 'selectfield',
                label: 'Password Question',
                name: 'question',
                options: [
                    { text: '', value: '' },
                    { text: 'What was your childhood nickname?', value: 'What was your childhood nickname?' },
                    { text: 'In what city did you meet your spouse?', value: 'In what city did you meet your spouse/significant other?' },
                    { text: 'What is your favorite color?', value: 'What is your favorite color?' },
                    { text: 'In what city was your first job?', value: 'In what city or town was your first job?' },
                    { text: 'Who is your favorite actor/actress?', value: 'Who is your favorite actor/actress?' },
                    { text: 'What was the color of your first car?', value: 'What was the color of your first car?' },
                    { text: 'In what city were you born?', value: 'In what city were you born?' }
                ]
            }, {
                xtype: 'textfield',
                label: 'Password Answer',
                name: 'answer'
            }
        ];

        if (this.pageData.showMailingListField) {
            accountFields.push({
                xtype: 'checkboxfield',
                label: 'Join our Mailing List?',
                labelWidth: '80%',
                name: 'joinmailinglist',
                value: 'true',
                required: false
            });
        }

        this.form = new Ext.form.FormPanel({
            scroll: true,
            standardSubmit: false,
            items: [{
                xtype: 'fieldset',
                title: 'Account Information',
                defaults: {
                    labelWidth: '35%',
                    required: true
                },
                items: accountFields
            }, {
                xtype: 'fieldset',
                title: 'Contact Information',
                defaults: {
                    labelWidth: '35%',
                    required: true
                },
                items: [this.pageData.contactFields]
            }]
        });

        if (this.pageData.attrFields.length > 0) {
            this.form.add(this.pageData.attrFields);
        }

        this.form.add({
            xtype: 'button',
            ui: 'action',
            text: 'Register',
            handler: this.doRegister,
            scope: this
        });

        this.items = [this.form];

        OrderEze.views.account.RegisterSheet.superclass.initComponent.call(this, arguments);

        this.on('hide', function (v) { v.destroy(); });
    },

    doRegister: function () {
        Eze.log('doRegister');
        var vals = this.form.getValues();
        var attrs = {};
        for (var key in vals) {
            if (key.substring(0, 4) == 'attr') {
                attrs[key.substring(4)] = vals[key];
                delete vals[key];
            }
        }
        vals.attributes = attrs;
        Eze.load('account', 'registerSubmit', vals, function (data) {
            Eze.log('doRegister success');
            OrderEze.settings.isLoggedIn = true;
            this.fireEvent('registered', this);
            this.hide();
        }, this)
    }
});

Ext.reg('account-RegisterSheet', OrderEze.views.account.RegisterSheet);;
Ext.ns('OrderEze.views.takeout');

OrderEze.views.takeout.TextSheet = Ext.extend(Ext.Sheet, {
    currentValue: '',
    placeHolder: '',
    title: '',
    instructions: '',
    type: 'textarea',

    initComponent: function () {
        Ext.apply(this, {
            padding: 0,
            stretchY: true,
            stretchX: true,
            modal: false,
            height: Eze.viewport.getHeight(),
            layout: { type: 'fit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                title: this.title,
                items: [{
                    text: 'Clear',
                    handler: function () {
                        this.textfield.setValue('');
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Done',
                    ui: 'action',
                    handler: function () {
                        this.currentValue = this.textfield.getValue();
                        this.textfield.blur();
                        this.hide();
                    },
                    scope: this
                }]
            }]
        });

        //this.on('show', function() { this.textfield.focus(); }, this);

        if (this.type === 'textarea') {
            this.textfield = new Ext.form.TextArea({
                name: 'comment',
                placeHolder: this.placeHolder
            });
        } else if (this.type === 'textbox') {
            this.textfield = new Ext.form.Text({
                name: 'comment',
                placeHolder: this.placeHolder
            });
        }

        this.items = [{
            xtype: 'form',
            standardSubmit: false,
            listeners: {
                deactivate: function (v) { v.destroy(); }
            },
            items: [{
                xtype: 'fieldset',
                instructions: this.instructions,
                items: [this.textfield]
            }]
        }];

        OrderEze.views.takeout.TextSheet.superclass.initComponent.call(this, arguments);
    },

    getValue: function () {
        return this.currentValue;
    }
});

Ext.reg('takeout-TextSheet', OrderEze.views.takeout.TextSheet);;
Ext.ns('OrderEze.views.takeout');

OrderEze.views.takeout.UnavailableSheet = Ext.extend(Ext.Sheet, {
    availData: {},
    
    initComponent: function () {
        this.addEvents('done');

        Ext.apply(this, {
            cls: 'unavail-item',
            padding: 0,
            stretchY: true,
            stretchX: true,
            modal: false,
            height: Eze.viewport.getHeight(),
            layout: { type: 'fit' },
            dockedItems: [{
                dock: 'top',
                xtype: 'toolbar',
                items: [{
                    text: 'Back',
                    handler: function () {
                        this.hide();
                    },
                    scope: this
                }, {
                    xtype: 'spacer'
                }, {
                    text: 'Change Time',
                    ui: 'action',
                    handler: function () {
                        this.hide();
                        this.fireEvent('done', this, true /* showChangeTimeSheet */);
                    },
                    scope: this
                }]
            }]
        });

        this.items = [{
            scroll: true,
            html: '<div class="header">This item is not available at your requested ' + this.availData.orderType + ' time.</div>' + 
                  '<div class="section">' + this.availData.itemName + '</div>' +
                  '<div class="section"><strong>Your ' + this.availData.orderType + ' Time:</strong><br />' + this.availData.chosenTime + '</div>' +
                  '<div class="section"><strong>Item Availability:</strong><br />' + this.availData.availHtml + '</div>' +
                  '<div class="footer">Press <strong>Change Time</strong> to set a different ' + this.availData.orderType + ' time or press <strong>Back</strong> to go back to the Menu and choose another item.</div>'
        }];

        OrderEze.views.takeout.UnavailableSheet.superclass.initComponent.call(this, arguments);

        this.on('hide', function (v) { v.destroy(); });
    }
});

Ext.reg('takeout-UnavailableSheet', OrderEze.views.takeout.UnavailableSheet);;
