/**
* @class Ext.layout.AbstractLayout
* @extends Object
* Base Layout class - extended by ComponentLayout and ContainerLayout
*/
Ext.define('Ext.layout.AbstractLayout', {
/* Begin Definitions */
/* End Definitions */
isLayout: true,
initialized: false,
statics: {
factory: function(config, defaultType) {
if (config instanceof Ext.layout.AbstractLayout) {
return config;
}
var type, options = {};
if (Ext.isString(config)) {
type = config;
}
else {
type = config.type || defaultType;
options = config;
}
return Ext.create('layout.' + type, options);
}
},
constructor : function(config) {
this.id = Ext.id(null, 'ext-layout-' + this.type + '-');
Ext.apply(this, config);
},
/**
* @private
*/
layout : function() {
var me = this;
me.layoutBusy = true;
me.initLayout();
if (me.beforeLayout.apply(me, arguments) !== false) {
me.onLayout.apply(me, arguments);
me.afterLayout();
me.owner.needsLayout = false;
}
me.layoutBusy = false;
},
beforeLayout : function() {
this.renderItems(this.getLayoutItems(), this.getRenderTarget());
return true;
},
/**
* @private
* Iterates over all passed items, ensuring they are rendered. If the items are already rendered,
* also determines if the items are in the proper place dom.
*/
renderItems : function(items, target) {
var ln = items.length,
i = 0,
item;
for (; i < ln; i++) {
item = items[i];
if (item && !item.rendered) {
this.renderItem(item, target, i);
}
else if (!this.isValidParent(item, target, i)) {
this.moveItem(item, target, i);
}
}
},
// @private - Validates item is in the proper place in the dom.
isValidParent : function(item, target, position) {
var dom = item.el ? item.el.dom : Ext.getDom(item);
if (dom && target && target.dom) {
if (Ext.isNumber(position) && dom !== target.dom.childNodes[position]) {
return false;
}
return (dom.parentNode == (target.dom || target));
}
return false;
},
/**
* @private
* Renders the given Component into the target Element.
* @param {Ext.Component} item The Component to render
* @param {Ext.core.Element} target The target Element
* @param {Number} position The position within the target to render the item to
*/
renderItem : function(item, target, position) {
if (!item.rendered) {
item.render(target, position);
this.configureItem(item);
this.childrenChanged = true;
}
},
/**
* @private
* Moved Component to the provided target instead.
*/
moveItem : function(item, target, position) {
if (typeof position == 'number') {
position = target.dom.childNodes[position];
}
// Make sure target is a dom element
target = target.dom || target;
target.insertBefore(item.el.dom, position || null);
item.container = Ext.get(target);
this.configureItem(item);
this.childrenChanged = true;
},
/**
* @private
* Adds the layout's targetCls if necessary and sets
* initialized flag when complete.
*/
initLayout : function() {
if (!this.initialized && !Ext.isEmpty(this.targetCls)) {
this.getTarget().addCls(this.targetCls);
}
this.initialized = true;
},
// @private Sets the layout owner
setOwner : function(owner) {
this.owner = owner;
},
// @private - Returns empty array
getLayoutItems : function() {
return [];
},
/**
* @private
* Applies itemCls
*/
configureItem: function(item) {
if (this.itemCls) {
item.el.addCls(this.itemCls);
}
},
// Placeholder empty functions for subclasses to extend
onLayout : Ext.emptyFn,
afterLayout : Ext.emptyFn,
onRemove : Ext.emptyFn,
onDestroy : Ext.emptyFn,
/**
* @private
* Removes itemCls
*/
afterRemove : function(item) {
if (this.itemCls && item.rendered) {
item.el.removeCls(this.itemCls);
}
},
/*
* Destroys this layout. This is a template method that is empty by default, but should be implemented
* by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
* @protected
*/
destroy : function() {
if (!Ext.isEmpty(this.targetCls)) {
var target = this.getTarget();
if (target) {
target.removeCls(this.targetCls);
}
}
this.onDestroy();
}
});