(function(clsPrefix) {

/**
 * Most of the visual classes you interact with in Sencha Touch are Components. Every Component in Sencha Touch is a
 * subclass of Ext.Component, which means they can all:
 *
 * * Render themselves onto the page using a template
 * * Show and hide themselves at any time
 * * Center themselves on the screen
 * * Enable and disable themselves
 *
 * They can also do a few more advanced things:
 *
 * * Float above other components (windows, message boxes and overlays)
 * * Change size and position on the screen with animation
 * * Dock other Components inside itself (useful for toolbars)
 * * Align to other components, allow themselves to be dragged around, make their content scrollable & more
 *
 * ## Available Components
 *
 * There are many components available in Sencha Touch, separated into 4 main groups:
 *
 * ### Navigation components
 * * {@link Ext.Toolbar}
 * * {@link Ext.Button}
 * * {@link Ext.NavigationBar}
 * * {@link Ext.SegmentedButton}
 * * {@link Ext.Title}
 * * {@link Ext.Spacer}
 *
 * ### Store-bound components
 * * {@link Ext.dataview.DataView}
 * * {@link Ext.dataview.ComponentView}
 * * {@link Ext.Carousel}
 * * {@link Ext.List}
 * * {@link Ext.NestedList}
 *
 * ### Form components
 * * {@link Ext.form.Panel}
 * * {@link Ext.form.FieldSet}
 * * {@link Ext.field.Checkbox}
 * * {@link Ext.field.Hidden}
 * * {@link Ext.field.Slider}
 * * {@link Ext.field.Text}
 * * {@link Ext.picker.Picker}
 * * {@link Ext.picker.Date}
 *
 * ### General components
 * * {@link Ext.Panel}
 * * {@link Ext.tab.Panel}
 * * {@link Ext.viewport.Viewport Ext.Viewport}
 * * {@link Ext.Img}
 * * {@link Ext.Map}
 * * {@link Ext.Audio}
 * * {@link Ext.Video}
 * * {@link Ext.Sheet}
 * * {@link Ext.ActionSheet}
 * * {@link Ext.MessageBox}
 *
 *
 * ## Instantiating Components
 *
 * Components are created the same way as all other classes in Sencha Touch - using Ext.create. Here's how we can
 * create a Text field:
 *
 *     var panel = Ext.create('Ext.Panel', {
 *         html: 'This is my panel'
 *     });
 *
 * This will create a {@link Ext.Panel Panel} instance, configured with some basic HTML content. A Panel is just a
 * simple Component that can render HTML and also contain other items. In this case we've created a Panel instance but
 * it won't show up on the screen yet because items are not rendered immediately after being instantiated. This allows
 * us to create some components and move them around before rendering and laying them out, which is a good deal faster
 * than moving them after rendering.
 *
 * To show this panel on the screen now we can simply add it to the global Viewport:
 *
 *     Ext.Viewport.add(panel);
 *
 * Panels are also Containers, which means they can contain other Components, arranged by a layout. Let's revisit the
 * above example now, this time creating a panel with two child Components and a hbox layout:
 *
 *     @example
 *     var panel = Ext.create('Ext.Panel', {
 *         layout: 'hbox',
 *
 *         items: [
 *             {
 *                 xtype: 'panel',
 *                 flex: 1,
 *                 html: 'Left Panel, 1/3rd of total size',
 *                  style: 'background-color: #5E99CC;'
 *             },
 *             {
 *                 xtype: 'panel',
 *                 flex: 2,
 *                 html: 'Right Panel, 2/3rds of total size',
 *                  style: 'background-color: #759E60;'
 *             }
 *         ]
 *     });
 *
 *     Ext.Viewport.add(panel);
 *
 * This time we created 3 Panels - the first one is created just as before but the inner two are declared inline using
 * an xtype. Xtype is a convenient way of creating Components without having to go through the process of using
 * Ext.create and specifying the full class name, instead you can just provide the xtype for the class inside an object
 * and the framework will create the components for you.
 *
 * We also specified a layout for the top level panel - in this case hbox, which splits the horizontal width of the
 * parent panel based on the 'flex' of each child. For example, if the parent Panel above is 300px wide then the first
 * child will be flexed to 100px wide and the second to 200px because the first one was given flex: 1 and the second
 * flex: 2.
 *
 * ## Configuring Components
 *
 * Whenever you create a new Component you can pass in configuration options. All of the configurations for a given
 * Component are listed in the "Config options" section of its class docs page. You can pass in any number of
 * configuration options when you instantiate the Component, and modify any of them at any point later. For example, we
 *  can easily modify the {@link Ext.Panel#html html content} of a Panel after creating it:
 *
 *     @example miniphone
 *     //we can configure the HTML when we instantiate the Component
 *     var panel = Ext.create('Ext.Panel', {
 *         fullscreen: true,
 *         html: 'This is a Panel'
 *     });
 *
 *     //we can update the HTML later using the setHtml method:
 *     panel.setHtml('Some new HTML');
 *
 *     //we can retrieve the current HTML using the getHtml method:
 *     alert(panel.getHtml()); //alerts "Some new HTML"
 *
 * Every config has a getter method and a setter method - these are automatically generated and always follow the same
 * pattern. For example, a config called 'html' will receive getHtml and setHtml methods, a config called defaultType
 * will receive getDefaultType and setDefaultType methods, and so on.
 *
 * ## Further Reading
 *
 * See the [Component & Container Guide](#!/guide/components) for more information, and check out the
 * {@link Ext.Container} class docs also.
 *
 */
Ext.define('Ext.Component', {

    extend: 'Ext.AbstractComponent',

    alternateClassName: 'Ext.lib.Component',

    mixins: ['Ext.mixin.Traversable'],

    requires: [
        'Ext.ComponentManager',
        'Ext.XTemplate',
        'Ext.dom.Element',
        'Ext.behavior.Draggable'
    ],

    xtype: 'component',

    observableType: 'component',

    cachedConfig: {
        /**
         * @cfg {Number} flex
         */

        /**
         * @cfg {String} baseCls
         * The base CSS class to apply to this components's element. This will also be prepended to
         * other elements within this component. To add specific styling for sub-classes, use the `cls` config.
         * @accessor
         */
        baseCls: null,

        /**
         * @cfg {String} cls the CSS class to add to this component's element, in addition to the `baseCls`
         * @accessor
         */
        cls: null,

        /**
         * @cfg {String} ui The ui to be used on this Component
         */
        ui: null,

        /**
         * @cfg {Number/String} margin The margin to use on this Component. Can be specified as a number (in which case
         * all edges get the same margin) or a CSS string like '5 10 10 10'
         * @accessor
         */
        margin: null,

        /**
         * @cfg {Number/String} padding The padding to use on this Component. Can be specified as a number (in which
         * case all edges get the same padding) or a CSS string like '5 10 10 10'
         * @accessor
         */
        padding: null,

        /**
         * @cfg {Number/String} border The border to use on this Component. Can be specified as a number (in which
         * case all edges get the same border width) or a CSS string like '5 10 10 10'
         * @accessor
         */
        border: null,

        /**
         * @cfg {String} styleHtmlCls
         * The class that is added to the content target when you set styleHtmlContent to true.
         * @accessor
         */
        styleHtmlCls: clsPrefix + 'html',

        /**
         * @cfg {Boolean} styleHtmlContent
         * True to automatically style the html inside the content target of this component (body for panels).
         * @accessor
         */
        styleHtmlContent: null,

        /**
         * @cfg {Boolean} masked
         * True to mask this component.
         * @accessor
         */
        masked: null,

        /**
         * @cfg {Boolean} hidden
         * True to hide this component
         * @accessor
         */
        hidden: false
    },

    eventedConfig: {
        /**
         * @cfg {Number/Boolean} left
         * @accessor
         * @evented
         */
        left: null,

        /**
         * @cfg {Number/Boolean} top
         * @accessor
         * @evented
         */
        top: null,

        /**
         * @cfg {Number/Boolean} right
         * @accessor
         * @evented
         */
        right: null,

        /**
         * @cfg {Number/Boolean} bottom
         * @accessor
         * @evented
         */
        bottom: null,

        /**
         * @cfg {Number} width
         * The width of this component in pixels.
         * @accessor
         * @evented
         */
        width: null,

        /**
         * @cfg {Number} height
         * The height of this component in pixels.
         * @accessor
         * @evented
         */
        height: null,

        /**
         * @cfg {String} docked
         * The dock position of this component in its container. Can be 'left', 'top', 'right' or 'bottom'.
         * @accessor
         * @evented
         */
        docked: null,

        /**
         * @cfg {Boolean} centered
         * Whether or not this component is absolutely centered inside its container
         * @accessor
         * @evented
         */
        centered: null,

        /**
         * @cfg {Boolean} hidden
         * Whether or not this component is hidden
         * @accessor
         * @evented
         */
        hidden: null,

        /**
         * @cfg {Boolean} disabled
         * Whether or not this component is disabled
         * @accessor
         * @evented
         */
        disabled: null
    },

    config: {
        /**
         * @cfg {String/Object} style Optional CSS styles that will be rendered into an inline style attribute when the
         * Component is rendered
         * @accessor
         */
        style: null,

        /**
         * @cfg {String/Ext.Element/HTMLElement} html Optional HTML content to render inside this Component, or a reference
         * to an existing element on the page.
         * @accessor
         */
        html: null,

        /**
         * @cfg {Object} draggable Configuration options to make this Component draggable
         * @accessor
         */
        draggable: null,

        /**
         * @cfg {Object} droppable Configuration options to make this Component droppable
         * @accessor
         */
        droppable: null,

        /**
         * @cfg {Ext.Element} renderTo Optional element to render this Component to. Usually this is not needed because
         * a Component is normally full screen or automatically rendered inside another {@link Ext.Container Container}
         * @accessor
         */
        renderTo: null,

        /**
         * @cfg {Number} zIndex The z-index to give this Component when it is rendered
         * @accessor
         */
        zIndex: null,

        /**
         * @cfg {Mixed} tpl
         * An <bold>{@link Ext.Template}</bold>, <bold>{@link Ext.XTemplate}</bold>
         * or an array of strings to form an Ext.XTemplate.
         * Used in conjunction with the <code>{@link #data}</code> and
         * <code>{@link #tplWriteMode}</code> configurations.
         * @accessor
         */
        tpl: null,

        /**
         * @cfg {Mixed} renderTpl
         * <p>An {@link Ext.XTemplate XTemplate} used to create the internal structure inside this Component's
         * encapsulating Element.</p>
         * <p>You do not normally need to specify this. For the base classes {@link Ext.Component}
         * and {@link Ext.Container}, this defaults to <b><code>null</code></b> which means that they will be initially rendered
         * with no internal structure; they render their {@link #getEl Element} empty. The more specialized ExtJS and Touch classes
         * which use a more complex DOM structure, provide their own template definitions.</p>
         * <p>This is intended to allow the developer to create application-specific utility Components with customized
         * internal structure.</p>
         * @accessor
         */

        /**
         * @cfg {String} tplWriteMode The Ext.(X)Template method to use when
         * updating the content area of the Component. Defaults to <code>'overwrite'</code>
         * (see <code>{@link Ext.XTemplate#overwrite}</code>).
         * @accessor
         */
        tplWriteMode: 'overwrite',

        /**
         * @cfg {Mixed} data
         * The initial set of data to apply to the <code>{@link #tpl}</code> to
         * update the content area of the Component.
         * @accessor
         */
        data: null,

        /**
         * @cfg {String} disabledCls The CSS class to add to the component when it is disabled
         * @accessor
         */
        disabledCls: clsPrefix + 'item-disabled',

        /**
         * @cfg {Boolean} modal True to make this Component modal. This will create a mask underneath the Component
         * that covers the whole page and does not allow the user to interact with any other Components until this
         * Component is dismissed
         * @accessor
         */
        modal: null,

        /**
         * @cfg {Boolean} hideOnMaskTap When using a {@link #modal} Component, setting this to true (the default) will
         * hide the modal mask and the Component when the mask is tapped on
         * @accessor
         */
        hideOnMaskTap: true,

        /**
         * @cfg {Ext.Element/HTMLElement/String} contentEl The configured element will automatically be added as the content of this
         * component. When you pass a string, we expect it to be an element id. If the content element is hidden, we will automatically
         * show it.
         * @accessor
         */
        contentEl: null,

        /**
         * @private
         * @cfg {String} itemId
         * @accessor
         */
        itemId: undefined
    },

    /**
     * @event painted
     * Fires whenever the Component is moved into to the DOM body
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event erased
     * Fires whenever the Component is moved out of the DOM body
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event show
     * Fires whenever the Component is shown
     * @param {Ext.Component} this The component instance
     */

    /**
     * @event hide
     * Fires whenever the Component is hidden
     * @param {Ext.Component} this The component instance
     */

    /**
     * @private
     */
    alignPositionMap: [
        'tl-bl',
        't-b',
        'tr-br',
        'l-r',
        'l-r',
        'r-l',
        'bl-tl',
        'b-t',
        'br-tr'
    ],

    /**
     * @private
     */
    isComponent: true,

    /**
     * @private
     */
    floating: false,

    /**
     * @private
     */
    rendered: false,

    /**
     * @private
     */
    maskText: null,

    /**
     * @private
     */
    maskTextCls: clsPrefix + 'mask-msg',

    /**
     * @private
     */
    loadingMask: false,

    /**
     * @readonly
     */
    dockPositions: {
        top: true,
        right: true,
        bottom: true,
        left: true
    },

    innerElement: null,

    element: null,

    template: [],

    /**
     * Creates new Component.
     * @param {Object} config The standard configuration object.
     */
    constructor: function(config) {
        if (config && config.id) {
            this.id = config.id;
            delete config.id;
        }
        else {
            this.getId();
        }

        Ext.ComponentManager.register(this);

        this.callParent(arguments);

        /**
         * Force the component to take up 100% width and height available, by adding it to {@link Ext.viewport.Viewport}.
         * @cfg {Boolean} fullscren
         * @deprecated 2.0.0 Please use Ext.Viewport.add instead
         */
        if ('fullscreen' in this.config) {
            this.fireEvent('fullscreen', this);
        }
    },

    getTemplate: function() {
        return this.template;
    },

    getElementConfig: function() {
        return {
            reference: 'element',
            children: this.getTemplate()
        };
    },

    /**
     * Retrieves the top level element representing this component.
     * @return {Ext.dom.Element}
     */
    getEl: function() {
        return this.renderElement;
    },

    renderTo: function(container, insertBeforeElement) {
        var dom = this.renderElement.dom,
            containerDom = Ext.getDom(container),
            insertBeforeChildDom = Ext.getDom(insertBeforeElement);

        if (containerDom) {
            if (insertBeforeChildDom) {
                containerDom.insertBefore(dom, insertBeforeChildDom);
            }
            else {
                containerDom.appendChild(dom);
            }

            this.setRendered(Boolean(dom.offsetParent));
        }
    },

    setParent: function(parent) {
        var currentParent = this.parent;

        if (parent && currentParent && currentParent !== parent) {
            currentParent.remove(this, false);
        }

        this.parent = parent;

        return this;
    },

    updateRenderTo: function(newContainer) {
        this.renderTo(newContainer);
    },

    updateStyle: function(style) {
        this.element.dom.setAttribute('style', style);
    },

    updateBorder: function(border) {
        this.element.setBorder(border);
    },

    updatePadding: function(padding) {
       this.innerElement.setPadding(padding);
    },

    updateMargin: function(margin) {
        this.element.setMargin(margin);
    },

    updateUi: function(newUi, oldUi) {
        var baseCls = this.getBaseCls();

        if (baseCls) {
            if (newUi) {
                this.addCls(newUi, baseCls);
            }

            if (oldUi) {
                this.removeCls(oldUi, baseCls);
            }
        }
    },

    applyBaseCls: function(baseCls) {
        return baseCls || clsPrefix + this.xtype;
    },

    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this,
            ui = me.getUi();

        if (newBaseCls) {
            this.addCls(newBaseCls);

            if (ui) {
                this.addCls(newBaseCls, null, ui);
            }
        }

        if (oldBaseCls) {
            this.removeCls(oldBaseCls);

            if (ui) {
                this.removeCls(oldBaseCls, null, ui);
            }
        }
    },

    updateCls: function(cls, oldCls) {
        this.replaceCls(oldCls, cls);
    },

    /**
     * Updates the {@link #styleHtmlCls} configuration
     */
    updateStyleHtmlCls: function(newHtmlCls, oldHtmlCls) {
        var innerHtmlElement = this.innerHtmlElement,
            innerElement = this.innerElement;

        if (this.getStyleHtmlContent() && oldHtmlCls) {
            if (innerHtmlElement) {
                innerHtmlElement.replaceCls(oldHtmlCls, newHtmlCls);
            } else {
                innerElement.replaceCls(oldHtmlCls, newHtmlCls);
            }
        }
    },

    updateStyleHtmlContent: function(styleHtmlContent) {
        var htmlCls = this.getStyleHtmlCls(),
            innerElement = this.innerElement,
            innerHtmlElement = this.innerHtmlElement;

        if (styleHtmlContent) {
            if (innerHtmlElement) {
                innerHtmlElement.addCls(htmlCls);
            } else {
                innerElement.addCls(htmlCls);
            }
        } else {
            if (innerHtmlElement) {
                innerHtmlElement.removeCls(htmlCls);
            } else {
                innerElement.addCls(htmlCls);
            }
        }
    },

    applyContentEl: function(contentEl) {
        if (contentEl) {
            return Ext.get(contentEl);
        }
    },

    updateContentEl: function(newContentEl, oldContentEl) {
        if (oldContentEl) {
            oldContentEl.hide();
            Ext.getBody().append(oldContentEl);
        }
        if (newContentEl) {
            this.setHtml(newContentEl);
            newContentEl.show();
        }
    },

    /**
     * Returns the height and width of the Component
     * @return {Object} The current height and width of the Component
     */
    getSize: function() {
        return {
            width: this.getWidth(),
            height: this.getHeight()
        };
    },

    isCentered: function() {
        return Boolean(this.getCentered());
    },

    isFloating: function() {
        return this.floating;
    },

    isDocked: function() {
        return Boolean(this.getDocked());
    },

    isInnerItem: function() {
        var me = this;
        return !me.isCentered() && !me.isFloating() && !me.isDocked();
    },

    filterPositionValue: function(value) {
        if (value === '' || value === 'auto') {
            value = null;
        }

        return value;
    },

    applyTop: function(top) {
        return this.filterPositionValue(top);
    },

    applyRight: function(right) {
        return this.filterPositionValue(right);
    },

    applyBottom: function(bottom) {
        return this.filterPositionValue(bottom);
    },

    applyLeft: function(left) {
        return this.filterPositionValue(left);
    },

    doSetTop: function(top) {
        this.updateFloating();
        this.element.setTop(top);
    },

    doSetRight: function(right) {
        this.updateFloating();
        this.element.setRight(right);
    },

    doSetBottom: function(bottom) {
        this.updateFloating();
        this.element.setBottom(bottom);
    },

    doSetLeft: function(left) {
        this.updateFloating();
        this.element.setLeft(left);
    },

    doSetWidth: function(width) {
        this.element.setWidth(width);
    },

    doSetHeight: function(height) {
        this.element.setHeight(height);
    },

    //TODO Implement animations here
//    animatePosition: function(property, value, animation) {
//        var me = this,
//            config = {};
//
//        animation = Ext.factory(animation || true, Ext.fx.Animation);
//        animation.setElement(this.element);
//        animation.setBefore({
//            position: 'absolute'
//        });
//        animation.getFrom().set(property, this.getConfig(property));
//        animation.getTo().set(property, value);
//        animation.setAfter({
//            position: null
//        });
//        animation.setOnEnd(function() {
//            me.setConfig(config);
//        });
//
//        config[property] = value;
//
//        Ext.Animator.run(animation);
//    },
//
//    animateSize: function(property, value, animation) {
//
//    },
//
//    animateVisibility: function(value, animation) {
//
//    },

    applyCentered: function(centered) {
        centered = Boolean(centered);

        if (centered) {
            if (this.isFloating()) {
                this.resetFloating();
            }

            if (this.isDocked()) {
                this.setDocked(false);
            }
        }

        return centered;
    },

    doSetCentered: Ext.emptyFn,

    applyDocked: function(docked) {
        if (docked) {
            if (!this.dockPositions[docked]) {
                //<debug error>
                Ext.Logger.error("Invalid docking position of '" + docked + "', must be either 'top', 'right', 'bottom', " +
                    "'left' or `null` (for no docking)", this);
                //</debug>
                return;
            }

            if (this.isFloating()) {
                this.resetFloating();
            }

            if (this.isCentered()) {
                this.setCentered(false);
            }
        }

        return docked;
    },

    doSetDocked: Ext.emptyFn,

    resetFloating: function() {
        this.setTop(null);
        this.setRight(null);
        this.setBottom(null);
        this.setLeft(null);
    },

    updateFloating: function() {
        var floating = true;

        if (this.getTop() === null && this.getBottom() === null && this.getRight() === null && this.getLeft() === null) {
            floating = false;
        }

        if (floating !== this.floating) {
            if (floating) {
                if (this.isCentered()) {
                    this.setCentered(false);
                }

                if (this.isDocked()) {
                    this.setDocked(false);
                }
            }

            this.floating = floating;
            this.fireEvent('floatingchange', this, floating);
        }
    },

    applyDisabled: function(disabled) {
        return Boolean(disabled);
    },

    doSetDisabled: function(disabled) {
        this.element[disabled ? 'addCls' : 'removeCls'](this.getDisabledCls());
    },

    /**
     * Disables this Component
     */
    disable: function() {
       this.setDisabled(true);
    },

    /**
     * Enables this Component
     */
    enable: function() {
        this.setDisabled(false);
    },

    /**
     * Returns true if this Component is currently disabled
     * @return {Boolean} True if currently disabled
     */
    isDisabled: function() {
        return this.getDisabled();
    },

    updateZIndex: function(zIndex) {
        this.element.dom.style.zIndex = zIndex;
    },

    getInnerHtmlElement: function() {
        var innerHtmlElement = this.innerHtmlElement,
            styleHtmlCls = this.getStyleHtmlCls();

        if (!innerHtmlElement || !innerHtmlElement.dom || !innerHtmlElement.dom.parentNode) {
            this.innerHtmlElement = innerHtmlElement = this.innerElement.createChild({ cls: 'x-innerhtml ' });

            if (this.getStyleHtmlContent()) {
                this.innerHtmlElement.addCls(styleHtmlCls);
                this.innerElement.removeCls(styleHtmlCls);
            }
        }

        return innerHtmlElement;
    },

    updateHtml: function(html) {
        var innerHtmlElement = this.getInnerHtmlElement();
        if (typeof html === 'string') {
            innerHtmlElement.setHtml(html);
        } else {
            innerHtmlElement.setHtml('');
            innerHtmlElement.append(html);
        }
    },

    applyHidden: function(hidden) {
        return Boolean(hidden);
    },

    doSetHidden: function(hidden) {
        var element = this.renderElement;

        if (hidden) {
            element.hide();
        }
        else {
            element.show();
        }
    },

    /**
     * Returns true if this Component is currently hidden
     * @return {Boolean} True if currently hidden
     */
    isHidden: function() {
        return this.getHidden();
    },

    /**
     * Hides this Component
     */
    hide: function() {
        this.setHidden(true);
    },

    /**
     * Shows this component
     */
    show: function() {
        this.setHidden(false);
    },

    /**
     * @private
     */
    isRendered: function() {
        return this.rendered;
    },

    /**
     * @private
     */
    isPainted: function() {
        return this.renderElement.isPainted();
    },

    /**
     * @private
     */
    applyTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },

    /**
     * @private
     */
    updateData: function(newData) {
        var me = this;
        if (newData) {
            var tpl = me.getTpl(),
                tplWriteMode = me.getTplWriteMode();

            if (tpl) {
                tpl[tplWriteMode](me.element, newData);
            }
        }
    },

    /**
     * Adds a CSS class (or classes) to this Component's rendered element
     * @param {String} cls The CSS class to add
     * @param {String} prefix Optional prefix to add to each class
     * @param {String} suffix Optional suffix to add to each class
     */
    addCls: function(cls, prefix, suffix) {
        this.element.addCls(cls, prefix, suffix);
    },

    /**
     * Removes the given CSS class(es) from this Component's rendered element
     * @param {String} cls The class(es) to remove
     * @param {String} prefix Optional prefix to prepend before each class
     * @param {String} suffix Optional suffix to append to each class
     */
    removeCls: function(cls, prefix, suffix) {
        this.element.removeCls(cls, prefix, suffix);
    },

    replaceCls: function(oldCls, newCls, prefix, suffix) {
        this.element.replaceCls(oldCls, newCls, prefix, suffix);
    },

    applyItemId: function(itemId) {
        return itemId || this.getId();
    },

    //TODO Deprecate this method name, should have been 'isXtype' (lowercased 't')
    /**
     * <p>Tests whether or not this Component is of a specific xtype. This can test whether this Component is descended
     * from the xtype (default) or whether it is directly of the xtype specified (shallow = true).</p>
     * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
     * to participate in determination of inherited xtypes.</b></p>
     * <p>For a list of all available xtypes, see the {@link Ext.Component} header.</p>
     * <p>Example usage:</p>
     * <pre><code>
var t = new Ext.field.Text();
var isText = t.isXType('textfield');        // true
var isBoxSubclass = t.isXType('field');       // true, descended from Ext.field.Field
var isBoxInstance = t.isXType('field', true); // false, not a direct Ext.field.Field instance
</code></pre>
     * @param {String} xtype The xtype to check for this Component
     * @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
     * the default), or true to check whether this Component is directly of the specified xtype.
     * @return {Boolean} True if this component descends from the specified xtype, false otherwise.
     */
    isXType: function(xtype, shallow) {
        if (shallow) {
            return this.xtypes.indexOf(xtype) != -1;
        }

        return Boolean(this.xtypesMap[xtype]);
    },

    //TODO Correct this method's signature should return the array of xtypes instead of a string. Also
    // its name should be getXtypes (lowercased 't')
    /**
     * <p>Returns this Component's xtype hierarchy as a slash-delimited string. For a list of all
     * available xtypes, see the {@link Ext.Component} header.</p>
     * <p><b>If using your own subclasses, be aware that a Component must register its own xtype
     * to participate in determination of inherited xtypes.</b></p>
     * <p>Example usage:</p>
     * <pre><code>
var t = new Ext.field.Text();
alert(t.getXTypes());  // alerts 'component/field/textfield'
</code></pre>
     * @return {String} The xtype hierarchy string
     */
    getXTypes: function() {
        return this.xtypesChain.join('/');
    },

    getDraggableBehavior: function() {
        var behavior = this.draggableBehavior;

        if (!behavior) {
            behavior = this.draggableBehavior = new Ext.behavior.Draggable(this);
        }

        return behavior;
    },

    applyDraggable: function(config) {
        this.getDraggableBehavior().setConfig(config);
    },

    getDraggable: function() {
        return this.getDraggableBehavior().getDraggable();
    },

    setRendered: function(rendered) {
        var wasRendered = this.rendered;

        if (rendered !== wasRendered) {
            this.rendered = rendered;

            return true;
        }

        return false;
    },

    /**
     * Sets the size of the Component
     * @param {Number} width The new width for the Component
     * @param {Number} height The new height for the Component
     */
    setSize: function(width, height) {
        if (width != undefined) {
            this.setWidth(width);
        }
        if (height != undefined) {
            this.setHeight(height);
        }
    },

    //TODO Need serious rewrites from here on or move them to the proper places
    /**
     * Shows this Component next to another Component or Element
     * @param {Ext.Component/Ext.Element} alignTo The Element or Component to align to
     */
    showBy: function(alignTo, animation, anchor) {
        // @todo animation + allowOnSide not implemented
        //<debug warn>
        if (animation) {
            Ext.Logger.warn('showBy: animation argument not implemented.');
        }
        //</debug>

        var parent = this.getParent();
        if (parent) {
            parent.remove(this, false);
        }

        Ext.Viewport.add(this);

        this.setTop(-10000);
        this.setLeft(-10000);
        this.show();

        this.alignTo(alignTo, anchor || 'auto');

        // If we don't do a repaint here, on iOS5 iPhone/iPod, the element will not be positioned correctly
        // on the screen
        this.element.repaint();
    },

    anchorRe: /^([a-z]+)-([a-z]+)(\?)?$/,

    doAnchorXY: function(anchor, box, toBox, constrainBox) {
        var matches = anchor.match(this.anchorRe),
            fromAnchor = matches[1].split(''),
            toAnchor = matches[2].split(''),
            offsetBox = {top: toBox.top, left: toBox.left},
            constrain = (matches[3] === '?'),

            fromVertical = fromAnchor[0],
            fromHorizontal = fromAnchor[1] || fromVertical,

            toVertical = toAnchor[0],
            toHorizontal = toAnchor[1] || toVertical,

            maxLeft, maxTop;

        switch (fromVertical) {
            case 't':
                switch (toVertical) {
                    case 'c':
                        offsetBox.top += toBox.height / 2;
                        break;
                    case 'b':
                        offsetBox.top += toBox.height;
                }
                break;

            case 'b':
                switch (toVertical) {
                    case 'c':
                        offsetBox.top -= (box.height - (toBox.height / 2));
                        break;
                    case 't':
                        offsetBox.top -= box.height;
                }
                break;

            case 'c':
                 switch (toVertical) {
                    case 't':
                        offsetBox.top -= (box.height / 2);
                        break;
                    case 'c':
                        offsetBox.top -= ((box.height / 2) - (toBox.height / 2));
                        break;
                    case 'b':
                        offsetBox.top -= ((box.height / 2) - toBox.height);
                }
                break;
        }

        switch (fromHorizontal) {
            case 'l':
                switch (toHorizontal) {
                    case 'c':
                        offsetBox.left += toBox.width / 2;
                        break;
                    case 'r':
                        offsetBox.left += toBox.width;
                }
                break;

            case 'r':
                switch (toHorizontal) {
                    case 'r':
                        offsetBox.left -= (box.width - toBox.width);
                        break;
                    case 'c':
                        offsetBox.left -= (box.width - (toBox.width / 2));
                        break;
                    case 'l':
                        offsetBox.left -= box.width;
                }
                break;

            case 'c':
                 switch (toHorizontal) {
                    case 'l':
                        offsetBox.left -= (box.width / 2);
                        break;
                    case 'c':
                        offsetBox.left -= ((box.width / 2) - (toBox.width / 2));
                        break;
                    case 'r':
                        offsetBox.left -= ((box.width / 2) - toBox.width);
                }
                break;
        }

        if (constrain) {
            maxLeft = (constrainBox.left + constrainBox.width) - box.width;
            maxTop = (constrainBox.top + constrainBox.height) - box.height;

            offsetBox.left = Math.max(constrainBox.left, Math.min(maxLeft, offsetBox.left));
            offsetBox.top = Math.max(constrainBox.top, Math.min(maxTop, offsetBox.top));
        }

        return offsetBox;
    },

    alignTo : function(alignTo, anchor, offset) {
        offset = offset || 0;

        var alignElement = alignTo.element,
            alignXY = webkitConvertPointFromNodeToPage(alignElement.dom, new WebKitPoint()),
            alignSize = alignElement.getSize(),
            size = this.element.getSize(),
            parent = this.getParent(),
            constrainBox = (parent) ? parent.element.getBox() : Ext.getBody().getBox(),
            box = {
                left: 0,
                top: 0,
                width: size.width,
                height: size.height
            },
            relativeToBox = {
                left: alignXY.x,
                top: alignXY.y,
                width: alignSize.width,
                height: alignSize.height
            }, anchorBox, tmpBox;

        if (anchor == 'auto') {
            anchor = 'tc-bc';
        }

        anchorBox = this.doAnchorXY(anchor, box, relativeToBox, constrainBox);
        if (anchorBox.top + box.height > constrainBox.top + constrainBox.height) {
            tmpBox = this.doAnchorXY('bc-tc?', box, relativeToBox, constrainBox);
            anchorBox.top = tmpBox.top;
        }
        if (anchorBox.left + box.width > constrainBox.left + constrainBox.width) {
            tmpBox = this.doAnchorXY('br-tr?', box, relativeToBox, constrainBox);
            anchorBox.left = tmpBox.left;
        } else if (anchorBox.left < constrainBox.left) {
            tmpBox = this.doAnchorXY('bl-tl?', box, relativeToBox, constrainBox);
            anchorBox.left = tmpBox.left;
        }

        this.setTop(anchorBox.top);
        this.setLeft(anchorBox.left);
    },

    /**
     * <p>Walks up the <code>ownerCt</code> axis looking for an ancestor Container which matches
     * the passed simple selector.</p>
     * <p>Example:<pre><code>
var owningTabPanel = grid.up('tabpanel');
</code></pre>
     * @param {String} selector Optional. The simple selector to test.
     * @return {Ext.Container} The matching ancestor Container (or <code>undefined</code> if no match was found).
     */
    up: function(selector) {
        var result = this.parent;

        if (selector) {
            for (; result; result = result.parent) {
                if (Ext.ComponentQuery.is(result, selector)) {
                    return result;
                }
            }
        }
        return result;
    },

    updateMasked: function(newMasked) {
        var me          = this,
            element     = me.element,
            maskEl      = me.maskEl,
            loadingMask = me.loadingMask,
            maskText    = me.maskText,
            maskTextCls = me.maskTextCls,
            prefix      = Ext.baseCSSPrefix,
            cls         = [prefix + 'mask'],
            children    = [];

        if (newMasked) {
            if (!maskEl) {
                //create the maskEl in this components element
                me.maskEl = element.createChild({
                    cls: cls.join(' '),
                    children: [
                        {
                            cls: 'x-mask-inner',
                            children: [
                                {
                                    cls: prefix + 'loading-spinner-outer',
                                    html: Ext.LoadingSpinner
                                },
                                {
                                    cls : prefix + 'mask-msg',
                                    html: maskText
                                }
                            ]
                        }
                    ]
                });

                //check if it is a loadingMask
                if (loadingMask) {
                    me.maskEl.addCls(prefix + 'mask-loading');
                }

                //check if there is maskText, if so, add it as a child
                if (maskText) {
                    me.maskEl.addCls(prefix + 'mask-text');
                }
            }

            //add the masked cls to this component
            me.addCls('masked', prefix);

            //show the mask
            me.maskEl.show();
        } else if (maskEl) {
            me.removeCls('masked', prefix);
            maskEl.hide();
        }
    },

    updateMaskText: function(newMaskText) {
        var me = this,
            maskEl      = me.maskEl,
            maskTextCls = me.maskTextCls,
            el;

        if (newMaskText && maskEl) {
            el = maskEl.down('.x-mask-msg');
            el.update(newMaskText);

            maskEl.addCls('x-mask-text');
        } else if (maskEl) {
            maskEl.removeCls('x-mask-text');
        }

        this.maskText = newMaskText;
    },

    updateLoadingMask: function(newLoadingMask) {
        var maskText = this.maskText,
            maskEl = this.maskEl;

        if (newLoadingMask && maskEl) {
            maskEl.addCls('x-mask-loading');
        } else if (maskEl) {
            maskEl.removeCls('x-mask-loading');
        }

        this.loadingMask = newLoadingMask;
    },

    mask: function(msg, msgCls, loadingMask) {
        this.updateLoadingMask(loadingMask);
        this.updateMaskText(msg);
        this.updateMasked(true);
    },

    unmask: function() {
        this.updateMasked(false);
    },

    /**
     * Destroys this Component. If it is currently added to a Container it will first be removed from that Container.
     * All Ext.Element references are also deleted and the Component is de-registered from Ext.ComponentManager
     */
    destroy: function() {
        this.callParent();

        var parent = this.getParent(),
            referenceList = this.referenceList,
            i, ln, reference;

        // Remove this component itself from the container if it's currently contained
        if (parent) {
            parent.remove(this, false);
        }

        // Destroy all element references
        for (i = 0,ln = referenceList.length; i < ln; i++) {
            reference = referenceList[i];
            this[reference].destroy();
            delete this[reference];
        }

        Ext.ComponentManager.unregister(this);
    }

}, function() {
    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>';

   //<deprecated product=touch since=2.0>
    var emptyFn = Ext.emptyFn;

    this.override({
        constructor: function(config) {
            var name;

            if (config) {
                if (config.enabled) {
                    //<debug warn>
                    Ext.Logger.deprecate("'enabled' config is deprecated, please use 'disabled' config instead", this);
                    //</debug>
                    config.disabled = !config.enabled;
                }

                if (config.scroll || this.config.scroll) {
                    //<debug warn>
                    Ext.Logger.deprecate("'scroll' config is deprecated, please use 'scrollable' config instead", this);
                    //</debug>
                    config.scrollable = config.scroll || this.config.scroll;
                }

                /**
                 * @member Ext.Component
                 * @cfg {String} componentCls CSS class to add to this Component. Deprecated, please use {@link #cls} instead
                 * @deprecated 2.0.0
                 */
                if (config.componentCls) {
                    //<debug warn>
                    Ext.Logger.deprecate("'componentCls' config is deprecated, please use 'cls' config instead", this);
                    //</debug>
                    config.cls = config.componentCls;
                }

                for (name in config) {
                    if (config.hasOwnProperty(name) && name !== 'xtype' && name !== 'xclass' && !this.hasConfig(name)) {
                        this[name] = config[name];
                    }
                }
            }

            this.callParent(arguments);

            if (this.onRender !== emptyFn) {
                //<debug warn>
                Ext.Logger.deprecate("onRender() is deprecated, please put your code inside initialize() instead", this);
                //</debug>
                this.onRender();
            }

            if (this.afterRender !== emptyFn) {
                //<debug warn>
                Ext.Logger.deprecate("afterRender() is deprecated, please put your code inside initialize() instead", this);
                //</debug>
                this.afterRender();
            }

            if (this.initEvents !== emptyFn) {
                //<debug warn>
                Ext.Logger.deprecate("initEvents() is deprecated, please put your code inside initialize() instead", this);
                //</debug>
                this.initEvents();
            }

            if (this.initComponent !== emptyFn) {
                //<debug warn>
                Ext.Logger.deprecate("initComponent() is deprecated, please put your code inside initialize() instead", this);
                //</debug>
                this.initComponent();
            }
        },

        onRender: emptyFn,

        afterRender: emptyFn,

        initEvents: emptyFn,

        initComponent: emptyFn,

        show: function() {
            if (this.renderElement.dom) {
                var containerDom = this.renderElement.dom.parentNode;

                if (containerDom && containerDom.nodeType == 11) {
                    //<debug warn>
                    Ext.Logger.deprecate("Showing a component that currently doesn't have any container, " +
                        "please use Ext.Viewport.add() to add this component to the viewport", this);
                    //</debug>
                    Ext.Viewport.add(this);
                }
            }

            return this.callParent(arguments);
        },

        doSetHidden: function(hidden) {
            this.callParent(arguments);

            this.fireEvent(hidden ? 'hide' : 'show', this);
        }
    });

    /**
     * @member Ext.Component
     * @method update
     * @deprecated 2.0.0
     * Updates the HTML content of the Component. Deprecated, please use {@link #setHtml} instead
     */

    Ext.deprecateClassMembers(this, {
        el: 'element',
        body: 'element',
        outer: 'renderElement',
        ownerCt: 'parent',
        update: 'setHtml'
    });
    //</deprecated>
});

})(Ext.baseCSSPrefix);