ElementContainer.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-util-ElementContainer'>/**
  19. </span> * This mixin enables classes to declare relationships to child elements and provides the
  20. * mechanics for acquiring the {@link Ext.Element elements} and storing them on an object
  21. * instance as properties.
  22. *
  23. * This class is used by {@link Ext.Component components} and {@link Ext.layout.container.Container container layouts} to
  24. * manage their child elements.
  25. *
  26. * A typical component that uses these features might look something like this:
  27. *
  28. * Ext.define('Ext.ux.SomeComponent', {
  29. * extend: 'Ext.Component',
  30. *
  31. * childEls: [
  32. * 'bodyEl'
  33. * ],
  34. *
  35. * renderTpl: [
  36. * '&lt;div id=&quot;{id}-bodyEl&quot;&gt;&lt;/div&gt;'
  37. * ],
  38. *
  39. * // ...
  40. * });
  41. *
  42. * The `childEls` array lists one or more relationships to child elements managed by the
  43. * component. The items in this array can be either of the following types:
  44. *
  45. * - String: the id suffix and property name in one. For example, &quot;bodyEl&quot; in the above
  46. * example means a &quot;bodyEl&quot; property will be added to the instance with the result of
  47. * {@link Ext#get} given &quot;componentId-bodyEl&quot; where &quot;componentId&quot; is the component instance's
  48. * id.
  49. * - Object: with a `name` property that names the instance property for the element, and
  50. * one of the following additional properties:
  51. * - `id`: The full id of the child element.
  52. * - `itemId`: The suffix part of the id to which &quot;componentId-&quot; is prepended.
  53. * - `select`: A selector that will be passed to {@link Ext#select}.
  54. * - `selectNode`: A selector that will be passed to {@link Ext.DomQuery#selectNode}.
  55. *
  56. * The example above could have used this instead to achieve the same result:
  57. *
  58. * childEls: [
  59. * { name: 'bodyEl', itemId: 'bodyEl' }
  60. * ]
  61. *
  62. * When using `select`, the property will be an instance of {@link Ext.CompositeElement}. In
  63. * all other cases, the property will be an {@link Ext.Element} or `null` if not found.
  64. *
  65. * Care should be taken when using `select` or `selectNode` to find child elements. The
  66. * following issues should be considered:
  67. *
  68. * - Performance: using selectors can be slower than id lookup by a factor 10x or more.
  69. * - Over-selecting: selectors are applied after the DOM elements for all children have
  70. * been rendered, so selectors can match elements from child components (including nested
  71. * versions of the same component) accidentally.
  72. *
  73. * This above issues are most important when using `select` since it returns multiple
  74. * elements.
  75. *
  76. * **IMPORTANT**
  77. * Unlike a `renderTpl` where there is a single value for an instance, `childEls` are aggregated
  78. * up the class hierarchy so that they are effectively inherited. In other words, if a
  79. * class where to derive from `Ext.ux.SomeComponent` in the example above, it could also
  80. * have a `childEls` property in the same way as `Ext.ux.SomeComponent`.
  81. *
  82. * Ext.define('Ext.ux.AnotherComponent', {
  83. * extend: 'Ext.ux.SomeComponent',
  84. *
  85. * childEls: [
  86. * // 'bodyEl' is inherited
  87. * 'innerEl'
  88. * ],
  89. *
  90. * renderTpl: [
  91. * '&lt;div id=&quot;{id}-bodyEl&quot;&gt;'
  92. * '&lt;div id=&quot;{id}-innerEl&quot;&gt;&lt;/div&gt;'
  93. * '&lt;/div&gt;'
  94. * ],
  95. *
  96. * // ...
  97. * });
  98. *
  99. * The `renderTpl` contains both child elements and unites them in the desired markup, but
  100. * the `childEls` only contains the new child element. The {@link #applyChildEls} method
  101. * takes care of looking up all `childEls` for an instance and considers `childEls`
  102. * properties on all the super classes and mixins.
  103. *
  104. * @private
  105. */
  106. Ext.define('Ext.util.ElementContainer', {
  107. childEls: [
  108. // empty - this solves a couple problems:
  109. // 1. It ensures that all classes have a childEls (avoid null ptr)
  110. // 2. It prevents mixins from smashing on their own childEls (these are gathered
  111. // specifically)
  112. ],
  113. constructor: function () {
  114. var me = this,
  115. childEls;
  116. // if we have configured childEls, we need to merge them with those from this
  117. // class, its bases and the set of mixins...
  118. if (me.hasOwnProperty('childEls')) {
  119. childEls = me.childEls;
  120. delete me.childEls;
  121. me.addChildEls.apply(me, childEls);
  122. }
  123. },
  124. destroy: function () {
  125. var me = this,
  126. childEls = me.getChildEls(),
  127. child, childName, i, k;
  128. for (i = childEls.length; i--; ) {
  129. childName = childEls[i];
  130. if (typeof childName != 'string') {
  131. childName = childName.name;
  132. }
  133. child = me[childName];
  134. if (child) {
  135. me[childName] = null; // better than delete since that changes the &quot;shape&quot;
  136. child.remove();
  137. }
  138. }
  139. },
  140. <span id='Ext-util-ElementContainer-method-addChildEls'> /**
  141. </span> * Adds each argument passed to this method to the {@link Ext.AbstractComponent#cfg-childEls childEls} array.
  142. */
  143. addChildEls: function () {
  144. var me = this,
  145. args = arguments;
  146. if (me.hasOwnProperty('childEls')) {
  147. me.childEls.push.apply(me.childEls, args);
  148. } else {
  149. me.childEls = me.getChildEls().concat(Array.prototype.slice.call(args));
  150. }
  151. me.prune(me.childEls, false);
  152. },
  153. <span id='Ext-util-ElementContainer-method-applyChildEls'> /**
  154. </span> * Sets references to elements inside the component.
  155. * @private
  156. */
  157. applyChildEls: function(el, id) {
  158. var me = this,
  159. childEls = me.getChildEls(),
  160. baseId, childName, i, selector, value;
  161. baseId = (id || me.id) + '-';
  162. for (i = childEls.length; i--; ) {
  163. childName = childEls[i];
  164. if (typeof childName == 'string') {
  165. // We don't use Ext.get because that is 3x (or more) slower on IE6-8. Since
  166. // we know the el's are children of our el we use getById instead:
  167. value = el.getById(baseId + childName);
  168. } else {
  169. if ((selector = childName.select)) {
  170. value = Ext.select(selector, true, el.dom); // a CompositeElement
  171. } else if ((selector = childName.selectNode)) {
  172. value = Ext.get(Ext.DomQuery.selectNode(selector, el.dom));
  173. } else {
  174. // see above re:getById...
  175. value = el.getById(childName.id || (baseId + childName.itemId));
  176. }
  177. childName = childName.name;
  178. }
  179. me[childName] = value;
  180. }
  181. },
  182. getChildEls: function () {
  183. var me = this,
  184. self;
  185. // If an instance has its own childEls, that is the complete set:
  186. if (me.hasOwnProperty('childEls')) {
  187. return me.childEls;
  188. }
  189. // Typically, however, the childEls is a class-level concept, so check to see if
  190. // we have cached the complete set on the class:
  191. self = me.self;
  192. return self.$childEls || me.getClassChildEls(self);
  193. },
  194. getClassChildEls: function (cls) {
  195. var me = this,
  196. result = cls.$childEls,
  197. childEls, i, length, forked, mixin, mixins, name, parts, proto, supr, superMixins;
  198. if (!result) {
  199. // We put the various childEls arrays into parts in the order of superclass,
  200. // new mixins and finally from cls. These parts can be null or undefined and
  201. // we will skip them later.
  202. supr = cls.superclass;
  203. if (supr) {
  204. supr = supr.self;
  205. parts = [supr.$childEls || me.getClassChildEls(supr)]; // super+mixins
  206. superMixins = supr.prototype.mixins || {};
  207. } else {
  208. parts = [];
  209. superMixins = {};
  210. }
  211. proto = cls.prototype;
  212. mixins = proto.mixins; // since we are a mixin, there will be at least us
  213. for (name in mixins) {
  214. if (mixins.hasOwnProperty(name) &amp;&amp; !superMixins.hasOwnProperty(name)) {
  215. mixin = mixins[name].self;
  216. parts.push(mixin.$childEls || me.getClassChildEls(mixin));
  217. }
  218. }
  219. parts.push(proto.hasOwnProperty('childEls') &amp;&amp; proto.childEls);
  220. for (i = 0, length = parts.length; i &lt; length; ++i) {
  221. childEls = parts[i];
  222. if (childEls &amp;&amp; childEls.length) {
  223. if (!result) {
  224. result = childEls;
  225. } else {
  226. if (!forked) {
  227. forked = true;
  228. result = result.slice(0);
  229. }
  230. result.push.apply(result, childEls);
  231. }
  232. }
  233. }
  234. cls.$childEls = result = (result ? me.prune(result, !forked) : []);
  235. }
  236. return result;
  237. },
  238. prune: function (childEls, shared) {
  239. var index = childEls.length,
  240. map = {},
  241. name;
  242. while (index--) {
  243. name = childEls[index];
  244. if (typeof name != 'string') {
  245. name = name.name;
  246. }
  247. if (!map[name]) {
  248. map[name] = 1;
  249. } else {
  250. if (shared) {
  251. shared = false;
  252. childEls = childEls.slice(0);
  253. }
  254. Ext.Array.erase(childEls, index, 1);
  255. }
  256. }
  257. return childEls;
  258. },
  259. <span id='Ext-util-ElementContainer-method-removeChildEls'> /**
  260. </span> * Removes items in the childEls array based on the return value of a supplied test
  261. * function. The function is called with a entry in childEls and if the test function
  262. * return true, that entry is removed. If false, that entry is kept.
  263. *
  264. * @param {Function} testFn The test function.
  265. */
  266. removeChildEls: function (testFn) {
  267. var me = this,
  268. old = me.getChildEls(),
  269. keepers = (me.childEls = []),
  270. n, i, cel;
  271. for (i = 0, n = old.length; i &lt; n; ++i) {
  272. cel = old[i];
  273. if (!testFn(cel)) {
  274. keepers.push(cel);
  275. }
  276. }
  277. }
  278. });
  279. </pre>
  280. </body>
  281. </html>