/** * @ignore * component hierarchy management * @author yiminghe@gmail.com */ KISSY.add('component/container', function (S, Control, ContainerRender) { function defAddChild(e) { var self = this; if (e.target !== self) { return; } var c = e.component, children = self.get('children'), index = e.index; children.splice(index, 0, c); // construct children = self.get('children'); c = children[index]; c.setInternal('parent', self); if (self.get('rendered')) { self.renderChild(index); } self.fire('afterAddChild', { component: c, index: index }); } function defRemoveChild(e) { var self = this; if (e.target !== self) { return; } var c = e.component, cDOMParentEl, cDOMEl, destroy = e.destroy, children = self.get('children'), index = e.index; if (index != -1) { children.splice(index, 1); } c.setInternal('parent', null); if (destroy) { // c is still json if (c.destroy) c.destroy(); } else { if (c.get && (cDOMEl = c.el)) { if (cDOMParentEl = cDOMEl.parentNode) { cDOMParentEl.removeChild(cDOMEl); } } } self.fire('afterRemoveChild', { component: c, index: index }); } /** * Base Container class for KISSY Component. * @extends KISSY.Component.Control * @class KISSY.Component.Container */ return Control.extend({ isContainer: true, initializer: function () { var self = this, prefixCls = self.get('prefixCls'), defaultChildCfg = self.get('defaultChildCfg'); self.publish('beforeAddChild', { defaultFn: defAddChild }); self.publish('beforeRemoveChild', { defaultFn: defRemoveChild }); defaultChildCfg.prefixCls = defaultChildCfg.prefixCls || prefixCls; }, createDom: function () { this.createChildren(); }, renderUI: function () { this.renderChildren(); }, renderChildren: function () { var i, self = this, children = self.get("children"); for (i = 0; i < children.length; i++) { self.renderChild(i); } }, createChildren: function () { var i, self = this, children = self.get("children"); for (i = 0; i < children.length; i++) { self.createChild(i); } }, /** * Add the specified component as a child of current component * at the given 0-based index. * @param {KISSY.Component.Control|Object} c * Child component instance to be added * or * Object describe child component * @param {String} [c.xclass] When c is a object, specify its child class. * @param {Number} [index] 0-based index at which * the new child component is to be inserted; * If not specified , the new child component will be inserted at last position. */ addChild: function (c, index) { var self = this, children = self.get("children"); if (index === undefined) { index = children.length; } self.fire('beforeAddChild', { component: c, index: index }); }, renderChild: function (childIndex) { var self = this, children = self.get('children'); self.createChild(childIndex).render(); self.fire('afterRenderChild', { component: children[childIndex], index: childIndex }); }, createChild: function (childIndex) { var self = this, c, elBefore, domContentEl, children = self.get('children'), cEl, contentEl; c = children[childIndex]; contentEl = self.view.getChildrenContainerEl(); domContentEl = contentEl[0]; elBefore = domContentEl.children[childIndex] || null; if (c.get('rendered')) { cEl = c.el; if (cEl.parentNode != domContentEl) { domContentEl.insertBefore(cEl, elBefore); } } else { if (elBefore) { c.set("elBefore", elBefore); } else { c.set("render", contentEl); } c.create(); } self.fire('afterCreateChild', { component: c, index: childIndex }); return c; }, /** * Removed the given child from this component,and returns it. * * If destroy is true, calls ``destroy()`` on the removed child component, * and subsequently detaches the child's Dom from the document. * Otherwise it is the caller's responsibility to * clean up the child component's Dom. * * @param {KISSY.Component.Control} c The child component to be removed. * @param {Boolean} [destroy=true] If true, * calls ``destroy()`` on the removed child component. */ removeChild: function (c, destroy) { if (destroy === undefined) { destroy = true; } this.fire('beforeRemoveChild', { component: c, index: S.indexOf(c, this.get('children')), destroy: destroy }); }, /** * Removes every child component attached to current component. * see {@link KISSY.Component.Container#removeChild} * @param {Boolean} [destroy] If true, * calls ``destroy()`` on the removed child component. * @chainable */ removeChildren: function (destroy) { var self = this, i, t = [].concat(self.get("children")); for (i = 0; i < t.length; i++) { self.removeChild(t[i], destroy); } return self; }, /** * Returns the child at the given index, or null if the index is out of bounds. * @param {Number} index 0-based index. * @return {KISSY.Component.Control} The child at the given index; null if none. */ getChildAt: function (index) { var children = this.get("children"); return children[index] || null; }, /** * destroy children * @protected */ destructor: function () { var i, children = this.get("children"); for (i = 0; i < children.length; i++) { children[i].destroy && children[i].destroy(); } } }, { ATTRS: { /** * Array of child components * @cfg {KISSY.Component.Control[]} children */ /** * @ignore */ children: { value: [], getter: function (v) { var defaultChildCfg = null, i, c, self = this; for (i = 0; i < v.length; i++) { c = v[i]; if (!c.isControl) { defaultChildCfg = defaultChildCfg || self.get('defaultChildCfg'); S.mix(c, defaultChildCfg, false); v[i] = this.createComponent(c); } } return v; }, setter: function (v) { var i, c; for (i = 0; i < v.length; i++) { c = v[i]; if (c.isControl) { c.setInternal('parent', this); } } } }, /** * default child config * @protected * @cfg {String} defaultChildCfg */ /** * @ignore */ defaultChildCfg: { value: {} }, xrender: { value: ContainerRender } }, name: 'container' }); }, { requires: ['component/control', './container/render'] });