78b0888ca29478c5c8f0869dbb3d83cb7951460f.svn-base 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. /*
  2. * Ext JS Library 2.0 Copyright(c) 2006-2007, Ext JS, LLC. licensing@extjs.com
  3. *
  4. * http://extjs.com/license
  5. */
  6. /**
  7. * @class Ext.Container
  8. * @extends Ext.BoxComponent Base class for any {@link Ext.BoxComponent} that
  9. * can contain other components. Containers handle the basic behavior
  10. * of containing items, namely adding, inserting and removing them. The
  11. * specific layout logic required to visually render contained items is
  12. * delegated to any one of the different {@link #layout} classes
  13. * available. This class is intended to be extended and should
  14. * generally not need to be created directly via the new keyword.
  15. */
  16. Ext.Container = Ext.extend(Ext.BoxComponent, {
  17. /**
  18. * @cfg {Boolean} monitorResize True to automatically monitor window
  19. * resize events to handle anything that is sensitive to the
  20. * current size of the viewport. This value is typically
  21. * managed by the chosen {@link #layout} and should not need to
  22. * be set manually.
  23. */
  24. /**
  25. * @cfg {String} layout The layout type to be used in this
  26. * container. If not specified, a default
  27. * {@link Ext.layout.ContainerLayout} will be created and used.
  28. * Valid values are: accordion, anchor, border, card, column,
  29. * fit, form and table. Specific config values for the chosen
  30. * layout type can be specified using {@link #layoutConfig}.
  31. */
  32. /**
  33. * @cfg {Object} layoutConfig This is a config object containing
  34. * properties specific to the chosen layout (to be used in
  35. * conjunction with the {@link #layout} config value). For
  36. * complete details regarding the valid config options for each
  37. * layout type, see the layout class corresponding to the type
  38. * specified:
  39. * <ul class="mdetail-params">
  40. * <li>{@link Ext.layout.Accordion}</li>
  41. * <li>{@link Ext.layout.AnchorLayout}</li>
  42. * <li>{@link Ext.layout.BorderLayout}</li>
  43. * <li>{@link Ext.layout.CardLayout}</li>
  44. * <li>{@link Ext.layout.ColumnLayout}</li>
  45. * <li>{@link Ext.layout.FitLayout}</li>
  46. * <li>{@link Ext.layout.FormLayout}</li>
  47. * <li>{@link Ext.layout.TableLayout}</li>
  48. * </ul>
  49. */
  50. /**
  51. * @cfg {Boolean/Number} bufferResize When set to true (100
  52. * milliseconds) or a number of milliseconds, the layout
  53. * assigned for this container will buffer the frequency it
  54. * calculates and does a re-layout of components. This is
  55. * useful for heavy containers or containers with a large
  56. * amount of sub components that frequent calls to layout are
  57. * expensive.
  58. */
  59. /**
  60. * @cfg {String/Number} activeItem A string component id or the
  61. * numeric index of the component that should be initially
  62. * activated within the container's layout on render. For
  63. * example, activeItem: 'item-1' or activeItem: 0 (index 0 =
  64. * the first item in the container's collection). activeItem
  65. * only applies to layout styles that can display items one at
  66. * a time (like {@link Ext.layout.Accordion},
  67. * {@link Ext.layout.CardLayout} and
  68. * {@link Ext.layout.FitLayout}). Related to
  69. * {@link Ext.layout.ContainerLayout#activeItem}.
  70. */
  71. /**
  72. * @cfg {Mixed} items A single item, or an array of child Components
  73. * to be added to this container. Each item can be any type of
  74. * object based on {@link Ext.Component}.<br>
  75. * <br>
  76. * Component config objects may also be specified in order to
  77. * avoid the overhead of constructing a real Component object
  78. * if lazy rendering might mean that the added Component will
  79. * not be rendered immediately. To take advantage of this "lazy
  80. * instantiation", set the {@link Ext.Component#xtype} config
  81. * property to the registered type of the Component wanted.<br>
  82. * <br>
  83. * For a list of all available xtypes, see
  84. * {@link Ext.Component}. If a single item is being passed, it
  85. * should be passed directly as an object reference (e.g.,
  86. * items: {...}). Multiple items should be passed as an array
  87. * of objects (e.g., items: [{...}, {...}]).
  88. */
  89. /**
  90. * @cfg {Object} defaults A config object that will be applied to
  91. * all components added to this container either via the
  92. * {@link #items} config or via the {@link #add} or
  93. * {@link #insert} methods. The defaults config can contain any
  94. * number of name/value property pairs to be added to each
  95. * item, and should be valid for the types of items being added
  96. * to the container. For example, to automatically apply
  97. * padding to the body of each of a set of contained
  98. * {@link Ext.Panel} items, you could pass: defaults:
  99. * {bodyStyle:'padding:15px'}.
  100. */
  101. /**
  102. * @cfg {Boolean} autoDestroy If true the container will
  103. * automatically destroy any contained component that is
  104. * removed from it, else destruction must be handled manually
  105. * (defaults to true).
  106. */
  107. autoDestroy : true,
  108. /**
  109. * @cfg {Boolean} hideBorders True to hide the borders of each
  110. * contained component, false to defer to the component's
  111. * existing border settings (defaults to false).
  112. */
  113. /**
  114. * @cfg {String} defaultType The default type of container
  115. * represented by this object as registered in
  116. * {@link Ext.ComponentMgr} (defaults to 'panel').
  117. */
  118. defaultType : 'panel',
  119. // private
  120. initComponent : function() {
  121. Ext.Container.superclass.initComponent.call(this);
  122. this.addEvents(
  123. /**
  124. * @event afterlayout Fires when the components in this
  125. * container are arranged by the associated
  126. * layout manager.
  127. * @param {Ext.Container}
  128. * this
  129. * @param {ContainerLayout}
  130. * layout The ContainerLayout implementation
  131. * for this container
  132. */
  133. 'afterlayout',
  134. /**
  135. * @event beforeadd Fires before any
  136. * {@link Ext.Component} is added or inserted
  137. * into the container. A handler can return false
  138. * to cancel the add.
  139. * @param {Ext.Container}
  140. * this
  141. * @param {Ext.Component}
  142. * component The component being added
  143. * @param {Number}
  144. * index The index at which the component
  145. * will be added to the container's items
  146. * collection
  147. */
  148. 'beforeadd',
  149. /**
  150. * @event beforeremove Fires before any
  151. * {@link Ext.Component} is removed from the
  152. * container. A handler can return false to
  153. * cancel the remove.
  154. * @param {Ext.Container}
  155. * this
  156. * @param {Ext.Component}
  157. * component The component being removed
  158. */
  159. 'beforeremove',
  160. /**
  161. * @event add Fires after any {@link Ext.Component} is
  162. * added or inserted into the container.
  163. * @param {Ext.Container}
  164. * this
  165. * @param {Ext.Component}
  166. * component The component that was added
  167. * @param {Number}
  168. * index The index at which the component was
  169. * added to the container's items collection
  170. */
  171. 'add',
  172. /**
  173. * @event remove Fires after any {@link Ext.Component}
  174. * is removed from the container.
  175. * @param {Ext.Container}
  176. * this
  177. * @param {Ext.Component}
  178. * component The component that was removed
  179. */
  180. 'remove');
  181. /**
  182. * The collection of components in this container as a
  183. * {@link Ext.util.MixedCollection}
  184. *
  185. * @type MixedCollection
  186. * @property items
  187. */
  188. var items = this.items;
  189. if (items) {
  190. delete this.items;
  191. if (items instanceof Array) {
  192. this.add.apply(this, items);
  193. } else {
  194. this.add(items);
  195. }
  196. }
  197. },
  198. // private
  199. initItems : function() {
  200. if (!this.items) {
  201. this.items = new Ext.util.MixedCollection(false,
  202. this.getComponentId);
  203. this.getLayout(); // initialize the layout
  204. }
  205. },
  206. // private
  207. setLayout : function(layout) {
  208. if (this.layout && this.layout != layout) {
  209. this.layout.setContainer(null);
  210. }
  211. this.initItems();
  212. this.layout = layout;
  213. layout.setContainer(this);
  214. },
  215. // private
  216. render : function() {
  217. Ext.Container.superclass.render.apply(this, arguments);
  218. if (this.layout) {
  219. if (typeof this.layout == 'string') {
  220. this.layout = new Ext.Container.LAYOUTS[this.layout
  221. .toLowerCase()](this.layoutConfig);
  222. }
  223. this.setLayout(this.layout);
  224. if (this.activeItem !== undefined) {
  225. var item = this.activeItem;
  226. delete this.activeItem;
  227. this.layout.setActiveItem(item);
  228. return;
  229. }
  230. }
  231. if (!this.ownerCt) {
  232. this.doLayout();
  233. }
  234. if (this.monitorResize === true) {
  235. Ext.EventManager.onWindowResize(this.doLayout, this);
  236. }
  237. },
  238. // protected - should only be called by layouts
  239. getLayoutTarget : function() {
  240. return this.el;
  241. },
  242. // private - used as the key lookup function for the items
  243. // collection
  244. getComponentId : function(comp) {
  245. return comp.itemId || comp.id;
  246. },
  247. /**
  248. * Adds a Component to this Container. Fires the beforeadd event
  249. * before adding, then fires the add event after the component has
  250. * been added.
  251. *
  252. * @param {Ext.Component/Object}
  253. * component The component to add.<br>
  254. * <br>
  255. * Ext uses lazy rendering, and will only render the
  256. * added Component should it become necessary.<br>
  257. * <br>
  258. * A Component config object may be passed in order to
  259. * avoid the overhead of constructing a real Component
  260. * object if lazy rendering might mean that the added
  261. * Component will not be rendered immediately. To take
  262. * advantage of this "lazy instantiation", set the
  263. * {@link Ext.Component#xtype} config property to the
  264. * registered type of the Component wanted.<br>
  265. * <br>
  266. * For a list of all available xtypes, see
  267. * {@link Ext.Component}.
  268. * @return {Ext.Component} component The Component (or config
  269. * object) that was added with the Container's default
  270. * config values applied.
  271. */
  272. add : function(comp) {
  273. if (!this.items) {
  274. this.initItems();
  275. }
  276. var a = arguments, len = a.length;
  277. if (len > 1) {
  278. for (var i = 0; i < len; i++) {
  279. this.add(a[i]);
  280. }
  281. return;
  282. }
  283. var c = this.lookupComponent(this.applyDefaults(comp));
  284. var pos = this.items.length;
  285. if (this.fireEvent('beforeadd', this, c, pos) !== false
  286. && this.onBeforeAdd(c) !== false) {
  287. this.items.add(c);
  288. c.ownerCt = this;
  289. this.fireEvent('add', this, c, pos);
  290. }
  291. return c;
  292. },
  293. /**
  294. * Inserts a Component into this Container at a specified index.
  295. * Fires the beforeadd event before inserting, then fires the add
  296. * event after the Component has been inserted.
  297. *
  298. * @param {Number}
  299. * index The index at which the Component will be
  300. * inserted into the Container's items collection
  301. * @param {Ext.Component}
  302. * component The child Component to insert.<br>
  303. * <br>
  304. * Ext uses lazy rendering, and will only render the
  305. * inserted Component should it become necessary.<br>
  306. * <br>
  307. * A Component config object may be passed in order to
  308. * avoid the overhead of constructing a real Component
  309. * object if lazy rendering might mean that the inserted
  310. * Component will not be rendered immediately. To take
  311. * advantage of this "lazy instantiation", set the
  312. * {@link Ext.Component#xtype} config property to the
  313. * registered type of the Component wanted.<br>
  314. * <br>
  315. * For a list of all available xtypes, see
  316. * {@link Ext.Component}.
  317. * @return {Ext.Component} component The Component (or config
  318. * object) that was inserted with the Container's default
  319. * config values applied.
  320. */
  321. insert : function(index, comp) {
  322. if (!this.items) {
  323. this.initItems();
  324. }
  325. var a = arguments, len = a.length;
  326. if (len > 2) {
  327. for (var i = len - 1; i >= 1; --i) {
  328. this.insert(index, a[i]);
  329. }
  330. return;
  331. }
  332. var c = this.lookupComponent(this.applyDefaults(comp));
  333. if (c.ownerCt == this && this.items.indexOf(c) < index) {
  334. --index;
  335. }
  336. if (this.fireEvent('beforeadd', this, c, index) !== false
  337. && this.onBeforeAdd(c) !== false) {
  338. this.items.insert(index, c);
  339. c.ownerCt = this;
  340. this.fireEvent('add', this, c, index);
  341. }
  342. return c;
  343. },
  344. // private
  345. applyDefaults : function(c) {
  346. if (this.defaults) {
  347. if (typeof c == 'string') {
  348. c = Ext.ComponentMgr.get(c);
  349. Ext.apply(c, this.defaults);
  350. } else if (!c.events) {
  351. Ext.applyIf(c, this.defaults);
  352. } else {
  353. Ext.apply(c, this.defaults);
  354. }
  355. }
  356. return c;
  357. },
  358. // private
  359. onBeforeAdd : function(item) {
  360. if (item.ownerCt) {
  361. item.ownerCt.remove(item, false);
  362. }
  363. if (this.hideBorders === true) {
  364. item.border = (item.border === true);
  365. }
  366. },
  367. /**
  368. * Removes a component from this container. Fires the beforeremove
  369. * event before removing, then fires the remove event after the
  370. * component has been removed.
  371. *
  372. * @param {Component/String}
  373. * component The component reference or id to remove
  374. * @param {Boolean}
  375. * autoDestroy (optional) True to automatically invoke
  376. * the component's {@link Ext.Component#destroy} function
  377. */
  378. remove : function(comp, autoDestroy) {
  379. var c = this.getComponent(comp);
  380. if (c && this.fireEvent('beforeremove', this, c) !== false) {
  381. this.items.remove(c);
  382. delete c.ownerCt;
  383. if (autoDestroy === true
  384. || (autoDestroy !== false && this.autoDestroy)) {
  385. c.destroy();
  386. }
  387. if (this.layout && this.layout.activeItem == c) {
  388. delete this.layout.activeItem;
  389. }
  390. this.fireEvent('remove', this, c);
  391. }
  392. return c;
  393. },
  394. /**
  395. * Gets a direct child Component by id, or by index.
  396. *
  397. * @param {String/Number}
  398. * id or index of child Component to return.
  399. * @return Ext.Component
  400. */
  401. getComponent : function(comp) {
  402. if (typeof comp == 'object') {
  403. return comp;
  404. }
  405. return this.items.get(comp);
  406. },
  407. // private
  408. lookupComponent : function(comp) {
  409. if (typeof comp == 'string') {
  410. return Ext.ComponentMgr.get(comp);
  411. } else if (!comp.events) {
  412. return this.createComponent(comp);
  413. }
  414. return comp;
  415. },
  416. // private
  417. createComponent : function(config) {
  418. return Ext.ComponentMgr.create(config, this.defaultType);
  419. },
  420. /**
  421. * Force this container's layout to be recalculated. A call to this
  422. * function is required after adding a new component to an already
  423. * rendered container. If you are not dynamically adding and
  424. * removing components after render, this function will generally
  425. * not need to be called.
  426. */
  427. doLayout : function() {
  428. if (this.rendered && this.layout) {
  429. this.layout.layout();
  430. }
  431. if (this.items) {
  432. var cs = this.items.items;
  433. for (var i = 0, len = cs.length; i < len; i++) {
  434. var c = cs[i];
  435. if (c.doLayout) {
  436. c.doLayout();
  437. }
  438. }
  439. }
  440. },
  441. /**
  442. * Returns the layout currently in use by the container. If the
  443. * container does not currently have a layout set, a default
  444. * {@link Ext.layout.ContainerLayout} will be created and set as the
  445. * container's layout.
  446. *
  447. * @return {ContainerLayout} layout The container's layout
  448. */
  449. getLayout : function() {
  450. if (!this.layout) {
  451. var layout = new Ext.layout.ContainerLayout(this.layoutConfig);
  452. this.setLayout(layout);
  453. }
  454. return this.layout;
  455. },
  456. // private
  457. onDestroy : function() {
  458. if (this.items) {
  459. var cs = this.items.items;
  460. for (var i = 0, len = cs.length; i < len; i++) {
  461. Ext.destroy(cs[i]);
  462. }
  463. }
  464. if (this.monitorResize) {
  465. Ext.EventManager.removeResizeListener(this.doLayout, this);
  466. }
  467. Ext.Container.superclass.onDestroy.call(this);
  468. },
  469. /**
  470. * Bubbles up the component/container heirarchy, calling the
  471. * specified function with each component. The scope (<i>this</i>)
  472. * of function call will be the scope provided or the current
  473. * component. The arguments to the function will be the args
  474. * provided or the current component. If the function returns false
  475. * at any point, the bubble is stopped.
  476. *
  477. * @param {Function}
  478. * fn The function to call
  479. * @param {Object}
  480. * scope (optional) The scope of the function (defaults
  481. * to current node)
  482. * @param {Array}
  483. * args (optional) The args to call the function with
  484. * (default to passing the current component)
  485. */
  486. bubble : function(fn, scope, args) {
  487. var p = this;
  488. while (p) {
  489. if (fn.apply(scope || p, args || [p]) === false) {
  490. break;
  491. }
  492. p = p.ownerCt;
  493. }
  494. },
  495. /**
  496. * Cascades down the component/container heirarchy from this
  497. * component (called first), calling the specified function with
  498. * each component. The scope (<i>this</i>) of function call will
  499. * be the scope provided or the current component. The arguments to
  500. * the function will be the args provided or the current component.
  501. * If the function returns false at any point, the cascade is
  502. * stopped on that branch.
  503. *
  504. * @param {Function}
  505. * fn The function to call
  506. * @param {Object}
  507. * scope (optional) The scope of the function (defaults
  508. * to current component)
  509. * @param {Array}
  510. * args (optional) The args to call the function with
  511. * (defaults to passing the current component)
  512. */
  513. cascade : function(fn, scope, args) {
  514. if (fn.apply(scope || this, args || [this]) !== false) {
  515. if (this.items) {
  516. var cs = this.items.items;
  517. for (var i = 0, len = cs.length; i < len; i++) {
  518. if (cs[i].cascade) {
  519. cs[i].cascade(fn, scope, args);
  520. } else {
  521. fn.apply(scope || this, args || [cs[i]]);
  522. }
  523. }
  524. }
  525. }
  526. },
  527. /**
  528. * Find a component under this container at any level by id
  529. *
  530. * @param {String}
  531. * id
  532. * @return Ext.Component
  533. */
  534. findById : function(id) {
  535. var m, ct = this;
  536. this.cascade(function(c) {
  537. if (ct != c && c.id === id) {
  538. m = c;
  539. return false;
  540. }
  541. });
  542. return m || null;
  543. },
  544. /**
  545. * Find a component under this container at any level by xtype or
  546. * class
  547. *
  548. * @param {String/Class}
  549. * xtype The xtype string for a component, or the class
  550. * of the component directly
  551. * @return {Array} Array of Ext.Components
  552. */
  553. findByType : function(xtype) {
  554. return typeof xtype == 'function' ? this.findBy(function(c) {
  555. return c.constructor === xtype;
  556. }) : this.findBy(function(c) {
  557. return c.constructor.xtype === xtype;
  558. });
  559. },
  560. /**
  561. * Find a component under this container at any level by property
  562. *
  563. * @param {String}
  564. * prop
  565. * @param {String}
  566. * value
  567. * @return {Array} Array of Ext.Components
  568. */
  569. find : function(prop, value) {
  570. return this.findBy(function(c) {
  571. return c[prop] === value;
  572. });
  573. },
  574. /**
  575. * Find a component under this container at any level by a custom
  576. * function. If the passed function returns true, the component will
  577. * be included in the results. The passed function is called with
  578. * the arguments (component, this container).
  579. *
  580. * @param {Function}
  581. * fcn
  582. * @param {Object}
  583. * scope (optional)
  584. * @return {Array} Array of Ext.Components
  585. */
  586. findBy : function(fn, scope) {
  587. var m = [], ct = this;
  588. this.cascade(function(c) {
  589. if (ct != c && fn.call(scope || c, c, ct) === true) {
  590. m.push(c);
  591. }
  592. });
  593. return m;
  594. }
  595. });
  596. Ext.Container.LAYOUTS = {};
  597. Ext.reg('container', Ext.Container);