/**
 * @class Ext.panel.AbstractPanel
 * @extends Ext.container.Container
 * <p>A base class which provides methods common to Panel classes across the Sencha product range.</p>
 * <p>Please refer to sub class's documentation</p>
 * @constructor
 * @param {Object} config The config object
 */

Ext.define('Ext.panel.AbstractPanel', {

   
/* Begin Definitions */

    extend
: 'Ext.container.Container',

    requires
: ['Ext.util.MixedCollection', 'Ext.core.Element', 'Ext.toolbar.Toolbar'],

   
/* End Definitions */

    /**
     * @cfg {String} baseCls
     * The base CSS class to apply to this panel's element (defaults to <code>'x-panel'</code>).
     */

    baseCls
: Ext.baseCSSPrefix + 'panel',

    /**
     * @cfg {Number/String} bodyPadding
     * A shortcut for setting a padding style on the body element. The value can either be
     * a number to be applied to all sides, or a normal css string describing padding.
     * Defaults to <code>undefined</code>.
     */


    /**
     * @cfg {Boolean} bodyBorder
     * A shortcut to add or remove the border on the body of a panel. This only applies to a panel which has the {@link #frame} configuration set to `true`.
     * Defaults to <code>undefined</code>.
     */

   
    /**
     * @cfg {String/Object/Function} bodyStyle
     * Custom CSS styles to be applied to the panel's body element, which can be supplied as a valid CSS style string,
     * an object containing style property name/value pairs or a function that returns such a string or object.
     * For example, these two formats are interpreted to be equivalent:<pre><code>
bodyStyle: 'background:#ffc; padding:10px;'

bodyStyle: {
    background: '#ffc',
    padding: '10px'
}
     * </code></pre>
     */

   
    /**
     * @cfg {String/Array} bodyCls
     * A CSS class, space-delimited string of classes, or array of classes to be applied to the panel's body element.
     * The following examples are all valid:<pre><code>
bodyCls: 'foo'
bodyCls: 'foo bar'
bodyCls: ['foo', 'bar']
     * </code></pre>
     */


    isPanel
: true,

    componentLayout
: 'dock',

    renderTpl
: ['<div class="{baseCls}-body<tpl if="bodyCls"> {bodyCls}</tpl> {baseCls}-body-{ui}<tpl if="uiCls"><tpl for="uiCls"> {parent.baseCls}-body-{parent.ui}-{.}</tpl></tpl>"<tpl if="bodyStyle"> style="{bodyStyle}"</tpl>></div>'],

   
// TODO: Move code examples into product-specific files. The code snippet below is Touch only.
    /**
     * @cfg {Object/Array} dockedItems
     * A component or series of components to be added as docked items to this panel.
     * The docked items can be docked to either the top, right, left or bottom of a panel.
     * This is typically used for things like toolbars or tab bars:
     * <pre><code>
var panel = new Ext.panel.Panel({
    fullscreen: true,
    dockedItems: [{
        xtype: 'toolbar',
        dock: 'top',
        items: [{
            text: 'Docked to the top'
        }]
    }]
});</code></pre>
     */

     
    border
: true,

    initComponent
: function() {
       
var me = this;
       
        me
.addEvents(
            /**
             * @event bodyresize
             * Fires after the Panel has been resized.
             * @param {Ext.panel.Panel} p the Panel which has been resized.
             * @param {Number} width The Panel body's new width.
             * @param {Number} height The Panel body's new height.
             */

           
'bodyresize'
           
// // inherited
           
// 'activate',
           
// // inherited
           
// 'deactivate'
       
);

       
Ext.applyIf(me.renderSelectors, {
            body
: '.' + me.baseCls + '-body'
       
});
       
       
//!frame
       
//!border
       
       
if (me.frame && me.border && me.bodyBorder === undefined) {
            me
.bodyBorder = false;
       
}
       
if (me.frame && me.border && (me.bodyBorder === false || me.bodyBorder === 0)) {
            me
.manageBodyBorders = true;
       
}
       
        me
.callParent();
   
},

   
// @private
    initItems
: function() {
       
var me = this,
            items
= me.dockedItems;
           
        me
.callParent();
        me
.dockedItems = Ext.create('Ext.util.MixedCollection', false, me.getComponentId);
       
if (items) {
            me
.addDocked(items);
       
}
   
},

    /**
     * Finds a docked component by id, itemId or position. Also see {@link #getDockedItems}
     * @param {String/Number} comp The id, itemId or position of the docked component (see {@link #getComponent} for details)
     * @return {Ext.Component} The docked component (if found)
     */

    getDockedComponent
: function(comp) {
       
if (Ext.isObject(comp)) {
            comp
= comp.getItemId();
       
}
       
return this.dockedItems.get(comp);
   
},

    /**
     * Attempts a default component lookup (see {@link Ext.container.Container#getComponent}). If the component is not found in the normal
     * items, the dockedItems are searched and the matched component (if any) returned (see {@loink #getDockedComponent}). Note that docked
     * items will only be matched by component id or itemId -- if you pass a numeric index only non-docked child components will be searched.
     * @param {String/Number} comp The component id, itemId or position to find
     * @return {Ext.Component} The component (if found)
     */

    getComponent
: function(comp) {
       
var component = this.callParent(arguments);
       
if (component === undefined && !Ext.isNumber(comp)) {
           
// If the arg is a numeric index skip docked items
            component
= this.getDockedComponent(comp);
       
}
       
return component;
   
},

    /**
     * Parses the {@link bodyStyle} config if available to create a style string that will be applied to the body element.
     * This also includes {@link bodyPadding} and {@link bodyBorder} if available.
     * @return {String} A CSS style string with body styles, padding and border.
     * @private
     */

    initBodyStyles
: function() {
       
var me = this,
            bodyStyle
= me.bodyStyle,
            styles
= [],
           
Element = Ext.core.Element,
            prop
;

       
if (Ext.isFunction(bodyStyle)) {
            bodyStyle
= bodyStyle();
       
}
       
if (Ext.isString(bodyStyle)) {
            styles
= bodyStyle.split(';');
       
} else {
           
for (prop in bodyStyle) {
               
if (bodyStyle.hasOwnProperty(prop)) {
                    styles
.push(prop + ':' + bodyStyle[prop]);
               
}
           
}
       
}

       
if (me.bodyPadding !== undefined) {
            styles
.push('padding: ' + Element.unitizeBox((me.bodyPadding === true) ? 5 : me.bodyPadding));
       
}
       
if (me.frame && me.bodyBorder) {
           
if (!Ext.isNumber(me.bodyBorder)) {
                me
.bodyBorder = 1;
           
}
            styles
.push('border-width: ' + Element.unitizeBox(me.bodyBorder));
       
}
       
delete me.bodyStyle;
       
return styles.length ? styles.join(';') : undefined;
   
},
   
    /**
     * Parse the {@link bodyCls} config if available to create a comma-delimited string of
     * CSS classes to be applied to the body element.
     * @return {String} The CSS class(es)
     * @private
     */

    initBodyCls
: function() {
       
var me = this,
            cls
= '',
            bodyCls
= me.bodyCls;
       
       
if (bodyCls) {
           
Ext.each(bodyCls, function(v) {
                cls
+= " " + v;
           
});
           
delete me.bodyCls;
       
}
       
return cls.length > 0 ? cls : undefined;
   
},
   
    /**
     * Initialized the renderData to be used when rendering the renderTpl.
     * @return {Object} Object with keys and values that are going to be applied to the renderTpl
     * @private
     */

    initRenderData
: function() {
       
return Ext.applyIf(this.callParent(), {
            bodyStyle
: this.initBodyStyles(),
            bodyCls
: this.initBodyCls()
       
});
   
},

    /**
     * Adds docked item(s) to the panel.
     * @param {Object/Array} component The Component or array of components to add. The components
     * must include a 'dock' parameter on each component to indicate where it should be docked ('top', 'right',
     * 'bottom', 'left').
     * @param {Number} pos (optional) The index at which the Component will be added
     */

    addDocked
: function(items, pos) {
       
var me = this,
            i
= 0,
            item
, length;

        items
= me.prepareItems(items);
        length
= items.length;

       
for (; i < length; i++) {
            item
= items[i];
            item
.dock = item.dock || 'top';

           
// Allow older browsers to target docked items to style without borders
           
if (me.border === false) {
               
// item.cls = item.cls || '' + ' ' + me.baseCls + '-noborder-docked-' + item.dock;
           
}

           
if (pos !== undefined) {
                me
.dockedItems.insert(pos + i, item);
           
}
           
else {
                me
.dockedItems.add(item);
           
}
            item
.onAdded(me, i);
            me
.onDockedAdd(item);
       
}
       
if (me.rendered) {
            me
.doComponentLayout();
       
}
       
return items;
   
},

   
// Placeholder empty functions
    onDockedAdd
: Ext.emptyFn,
    onDockedRemove
: Ext.emptyFn,

    /**
     * Inserts docked item(s) to the panel at the indicated position.
     * @param {Number} pos The index at which the Component will be inserted
     * @param {Object/Array} component. The Component or array of components to add. The components
     * must include a 'dock' paramater on each component to indicate where it should be docked ('top', 'right',
     * 'bottom', 'left').
     */

    insertDocked
: function(pos, items) {
       
this.addDocked(items, pos);
   
},

    /**
     * Removes the docked item from the panel.
     * @param {Ext.Component} item. The Component to remove.
     * @param {Boolean} autoDestroy (optional) Destroy the component after removal.
     */

    removeDocked
: function(item, autoDestroy) {
       
var me = this,
            layout
,
            hasLayout
;
           
       
if (!me.dockedItems.contains(item)) {
           
return item;
       
}

        layout
= me.componentLayout;
        hasLayout
= layout && me.rendered;

       
if (hasLayout) {
            layout
.onRemove(item);
       
}

        me
.dockedItems.remove(item);
        item
.onRemoved();
        me
.onDockedRemove(item);

       
if (autoDestroy === true || (autoDestroy !== false && me.autoDestroy)) {
            item
.destroy();
       
}

       
if (hasLayout && !autoDestroy) {
            layout
.afterRemove(item);
       
}
       
       
if (!this.destroying) {
            me
.doComponentLayout();
       
}

       
return item;
   
},

    /**
     * Retrieve an array of all currently docked Components.
     * @param {String} cqSelector A {@link Ext.ComponentQuery ComponentQuery} selector string to filter the returned items.
     * @return {Array} An array of components.
     */

    getDockedItems
: function(cqSelector) {
       
var me = this,
           
// Start with a weight of 1, so users can provide <= 0 to come before top items
           
// Odd numbers, so users can provide a weight to come in between if desired
            defaultWeight
= { top: 1, left: 3, right: 5, bottom: 7 },
            dockedItems
;

       
if (me.dockedItems && me.dockedItems.items.length) {
           
// Allow filtering of returned docked items by CQ selector.
           
if (cqSelector) {
                dockedItems
= Ext.ComponentQuery.query(cqSelector, me.dockedItems.items);
           
} else {
                dockedItems
= me.dockedItems.items.slice();
           
}

           
Ext.Array.sort(dockedItems, function(a, b) {
               
// Docked items are ordered by their visual representation by default (t,l,r,b)
               
// TODO: Enforce position ordering, and have weights be sub-ordering within positions?
               
var aw = a.weight || defaultWeight[a.dock],
                    bw
= b.weight || defaultWeight[b.dock];
               
if (Ext.isNumber(aw) && Ext.isNumber(bw)) {
                   
return aw - bw;
               
}
               
return 0;
           
});
           
           
return dockedItems;
       
}
       
return [];
   
},
   
   
// inherit docs
    addUIClsToElement
: function(cls, force) {
       
var me = this;
       
        me
.callParent(arguments);
       
       
if (!force && me.rendered) {
            me
.body.addCls(Ext.baseCSSPrefix + cls);
            me
.body.addCls(me.baseCls + '-body-' + cls);
            me
.body.addCls(me.baseCls + '-body-' + me.ui + '-' + cls);
       
}
   
},
   
   
// inherit docs
    removeUIClsFromElement
: function(cls, force) {
       
var me = this;
       
        me
.callParent(arguments);
       
       
if (!force && me.rendered) {
            me
.body.removeCls(Ext.baseCSSPrefix + cls);
            me
.body.removeCls(me.baseCls + '-body-' + cls);
            me
.body.removeCls(me.baseCls + '-body-' + me.ui + '-' + cls);
       
}
   
},
   
   
// inherit docs
    addUIToElement
: function(force) {
       
var me = this;
       
        me
.callParent(arguments);
       
       
if (!force && me.rendered) {
            me
.body.addCls(me.baseCls + '-body-' + me.ui);
       
}
   
},
   
   
// inherit docs
    removeUIFromElement
: function() {
       
var me = this;
       
        me
.callParent(arguments);
       
       
if (me.rendered) {
            me
.body.removeCls(me.baseCls + '-body-' + me.ui);
       
}
   
},

   
// @private
    getTargetEl
: function() {
       
return this.body;
   
},

    getRefItems
: function(deep) {
       
var items = this.callParent(arguments),
           
// deep fetches all docked items, and their descendants using '*' selector and then '* *'
            dockedItems
= this.getDockedItems(deep ? '*,* *' : undefined),
            ln
= dockedItems.length,
            i
= 0,
            item
;
       
       
// Find the index where we go from top/left docked items to right/bottom docked items
       
for (; i < ln; i++) {
            item
= dockedItems[i];
           
if (item.dock === 'right' || item.dock === 'bottom') {
               
break;
           
}
       
}
       
       
// Return docked items in the top/left position before our container items, and
       
// return right/bottom positioned items after our container items.
       
// See AbstractDock.renderItems() for more information.
       
return dockedItems.splice(0, i).concat(items).concat(dockedItems);
   
},

    beforeDestroy
: function(){
       
var docked = this.dockedItems,
            c
;

       
if (docked) {
           
while ((c = docked.first())) {
               
this.removeDocked(c, true);
           
}
       
}
       
this.callParent();
   
},
   
    setBorder
: function(border) {
       
var me = this;
        me
.border = (border !== undefined) ? border : true;
       
if (me.rendered) {
            me
.doComponentLayout();
       
}
   
}
});