Context.html 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  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-layout-Context'>/**
  19. </span> * Manages context information during a layout.
  20. *
  21. * # Algorithm
  22. *
  23. * This class performs the following jobs:
  24. *
  25. * - Cache DOM reads to avoid reading the same values repeatedly.
  26. * - Buffer DOM writes and flush them as a block to avoid read/write interleaving.
  27. * - Track layout dependencies so each layout does not have to figure out the source of
  28. * its dependent values.
  29. * - Intelligently run layouts when the values on which they depend change (a trigger).
  30. * - Allow layouts to avoid processing when required values are unavailable (a block).
  31. *
  32. * Work done during layout falls into either a &quot;read phase&quot; or a &quot;write phase&quot; and it is
  33. * essential to always be aware of the current phase. Most methods in
  34. * {@link Ext.layout.Layout Layout} are called during a read phase:
  35. * {@link Ext.layout.Layout#calculate calculate},
  36. * {@link Ext.layout.Layout#completeLayout completeLayout} and
  37. * {@link Ext.layout.Layout#finalizeLayout finalizeLayout}. The exceptions to this are
  38. * {@link Ext.layout.Layout#beginLayout beginLayout},
  39. * {@link Ext.layout.Layout#beginLayoutCycle beginLayoutCycle} and
  40. * {@link Ext.layout.Layout#finishedLayout finishedLayout} which are called during
  41. * a write phase. While {@link Ext.layout.Layout#finishedLayout finishedLayout} is called
  42. * a write phase, it is really intended to be a catch-all for post-processing after a
  43. * layout run.
  44. *
  45. * In a read phase, it is OK to read the DOM but this should be done using the appropriate
  46. * {@link Ext.layout.ContextItem ContextItem} where possible since that provides a cache
  47. * to avoid redundant reads. No writes should be made to the DOM in a read phase! Instead,
  48. * the values should be written to the proper ContextItem for later write-back.
  49. *
  50. * The rules flip-flop in a write phase. The only difference is that ContextItem methods
  51. * like {@link Ext.layout.ContextItem#getStyle getStyle} will still read the DOM unless the
  52. * value was previously read. This detail is unknowable from the outside of ContextItem, so
  53. * read calls to ContextItem should also be avoided in a write phase.
  54. *
  55. * Calculating interdependent layouts requires a certain amount of iteration. In a given
  56. * cycle, some layouts will contribute results that allow other layouts to proceed. The
  57. * general flow then is to gather all of the layouts (both component and container) in a
  58. * component tree and queue them all for processing. The initial queue order is bottom-up
  59. * and component layout first, then container layout (if applicable) for each component.
  60. *
  61. * This initial step also calls the beginLayout method on all layouts to clear any values
  62. * from the DOM that might interfere with calculations and measurements. In other words,
  63. * this is a &quot;write phase&quot; and reads from the DOM should be strictly avoided.
  64. *
  65. * Next the layout enters into its iterations or &quot;cycles&quot;. Each cycle consists of calling
  66. * the {@link Ext.layout.Layout#calculate calculate} method on all layouts in the
  67. * {@link #layoutQueue}. These calls are part of a &quot;read phase&quot; and writes to the DOM should
  68. * be strictly avoided.
  69. *
  70. * # Considerations
  71. *
  72. * **RULE 1**: Respect the read/write cycles. Always use the {@link Ext.layout.ContextItem#getProp getProp}
  73. * or {@link Ext.layout.ContextItem#getDomProp getDomProp} methods to get calculated values;
  74. * only use the {@link Ext.layout.ContextItem#getStyle getStyle} method to read styles; use
  75. * {@link Ext.layout.ContextItem#setProp setProp} to set DOM values. Some reads will, of
  76. * course, still go directly to the DOM, but if there is a method in
  77. * {@link Ext.layout.ContextItem ContextItem} to do a certain job, it should be used instead
  78. * of a lower-level equivalent.
  79. *
  80. * The basic logic flow in {@link Ext.layout.Layout#calculate calculate} consists of gathering
  81. * values by calling {@link Ext.layout.ContextItem#getProp getProp} or
  82. * {@link Ext.layout.ContextItem#getDomProp getDomProp}, calculating results and publishing
  83. * them by calling {@link Ext.layout.ContextItem#setProp setProp}. It is important to realize
  84. * that {@link Ext.layout.ContextItem#getProp getProp} will return `undefined` if the value
  85. * is not yet known. But the act of calling the method is enough to track the fact that the
  86. * calling layout depends (in some way) on this value. In other words, the calling layout is
  87. * &quot;triggered&quot; by the properties it requests.
  88. *
  89. * **RULE 2**: Avoid calling {@link Ext.layout.ContextItem#getProp getProp} unless the value
  90. * is needed. Gratuitous calls cause inefficiency because the layout will appear to depend on
  91. * values that it never actually uses. This applies equally to
  92. * {@link Ext.layout.ContextItem#getDomProp getDomProp} and the test-only methods
  93. * {@link Ext.layout.ContextItem#hasProp hasProp} and {@link Ext.layout.ContextItem#hasDomProp hasDomProp}.
  94. *
  95. * Because {@link Ext.layout.ContextItem#getProp getProp} can return `undefined`, it is often
  96. * the case that subsequent math will produce NaN's. This is usually not a problem as the
  97. * NaN's simply propagate along and result in final results that are NaN. Both `undefined`
  98. * and NaN are ignored by {@link Ext.layout.ContextItem#setProp}, so it is often not necessary
  99. * to even know that this is happening. It does become important for determining if a layout
  100. * is not done or if it might lead to publishing an incorrect (but not NaN or `undefined`)
  101. * value.
  102. *
  103. * **RULE 3**: If a layout has not calculated all the values it is required to calculate, it
  104. * must set {@link Ext.layout.Layout#done done} to `false` before returning from
  105. * {@link Ext.layout.Layout#calculate calculate}. This value is always `true` on entry because
  106. * it is simpler to detect the incomplete state rather than the complete state (especially up
  107. * and down a class hierarchy).
  108. *
  109. * **RULE 4**: A layout must never publish an incomplete (wrong) result. Doing so would cause
  110. * dependent layouts to run their calculations on those wrong values, producing more wrong
  111. * values and some layouts may even incorrectly flag themselves as {@link Ext.layout.Layout#done done}
  112. * before the correct values are determined and republished. Doing this will poison the
  113. * calculations.
  114. *
  115. * **RULE 5**: Each value should only be published by one layout. If multiple layouts attempt
  116. * to publish the same values, it would be nearly impossible to avoid breaking **RULE 4**. To
  117. * help detect this problem, the layout diagnostics will trap on an attempt to set a value
  118. * from different layouts.
  119. *
  120. * Complex layouts can produce many results as part of their calculations. These values are
  121. * important for other layouts to proceed and need to be published by the earliest possible
  122. * call to {@link Ext.layout.Layout#calculate} to avoid unnecessary cycles and poor performance. It is
  123. * also possible, however, for some results to be related in a way such that publishing them
  124. * may be an all-or-none proposition (typically to avoid breaking *RULE 4*).
  125. *
  126. * **RULE 6**: Publish results as soon as they are known to be correct rather than wait for
  127. * all values to be calculated. Waiting for everything to be complete can lead to deadlock.
  128. * The key here is not to forget **RULE 4** in the process.
  129. *
  130. * Some layouts depend on certain critical values as part of their calculations. For example,
  131. * HBox depends on width and cannot do anything until the width is known. In these cases, it
  132. * is best to use {@link Ext.layout.ContextItem#block block} or
  133. * {@link Ext.layout.ContextItem#domBlock domBlock} and thereby avoid processing the layout
  134. * until the needed value is available.
  135. *
  136. * **RULE 7**: Use {@link Ext.layout.ContextItem#block block} or
  137. * {@link Ext.layout.ContextItem#domBlock domBlock} when values are required to make progress.
  138. * This will mimize wasted recalculations.
  139. *
  140. * **RULE 8**: Blocks should only be used when no forward progress can be made. If even one
  141. * value could still be calculated, a block could result in a deadlock.
  142. *
  143. * Historically, layouts have been invoked directly by component code, sometimes in places
  144. * like an `afterLayout` method for a child component. With the flexibility now available
  145. * to solve complex, iterative issues, such things should be done in a responsible layout
  146. * (be it component or container).
  147. *
  148. * **RULE 9**: Use layouts to solve layout issues and don't wait for the layout to finish to
  149. * perform further layouts. This is especially important now that layouts process entire
  150. * component trees and not each layout in isolation.
  151. *
  152. * # Sequence Diagram
  153. *
  154. * The simplest sequence diagram for a layout run looks roughly like this:
  155. *
  156. * Context Layout 1 Item 1 Layout 2 Item 2
  157. * | | | | |
  158. * ----&gt;X--------------&gt;X | | |
  159. * run X---------------|-----------|----------&gt;X |
  160. * X beginLayout | | | |
  161. * X | | | |
  162. * A X--------------&gt;X | | |
  163. * X calculate X----------&gt;X | |
  164. * X C X getProp | | |
  165. * B X X----------&gt;X | |
  166. * X | setProp | | |
  167. * X | | | |
  168. * D X---------------|-----------|----------&gt;X |
  169. * X calculate | | X----------&gt;X
  170. * X | | | setProp |
  171. * E X | | | |
  172. * X---------------|-----------|----------&gt;X |
  173. * X completeLayout| | F | |
  174. * X | | | |
  175. * G X | | | |
  176. * H X--------------&gt;X | | |
  177. * X calculate X----------&gt;X | |
  178. * X I X getProp | | |
  179. * X X----------&gt;X | |
  180. * X | setProp | | |
  181. * J X--------------&gt;X | | |
  182. * X completeLayout| | | |
  183. * X | | | |
  184. * K X--------------&gt;X | | |
  185. * X---------------|-----------|----------&gt;X |
  186. * X finalizeLayout| | | |
  187. * X | | | |
  188. * L X--------------&gt;X | | |
  189. * X---------------|-----------|----------&gt;X |
  190. * X finishedLayout| | | |
  191. * X | | | |
  192. * M X--------------&gt;X | | |
  193. * X---------------|-----------|----------&gt;X |
  194. * X notifyOwner | | | |
  195. * N | | | | |
  196. * - - - - -
  197. *
  198. *
  199. * Notes:
  200. *
  201. * **A.** This is a call from the {@link #run} method to the {@link #runCycle} method.
  202. * Each layout in the queue will have its {@link Ext.layout.Layout#calculate calculate}
  203. * method called.
  204. *
  205. * **B.** After each {@link Ext.layout.Layout#calculate calculate} method is called the
  206. * {@link Ext.layout.Layout#done done} flag is checked to see if the Layout has completed.
  207. * If it has completed and that layout object implements a
  208. * {@link Ext.layout.Layout#completeLayout completeLayout} method, this layout is queued to
  209. * receive its call. Otherwise, the layout will be queued again unless there are blocks or
  210. * triggers that govern its requeueing.
  211. *
  212. * **C.** The call to {@link Ext.layout.ContextItem#getProp getProp} is made to the Item
  213. * and that will be tracked as a trigger (keyed by the name of the property being requested).
  214. * Changes to this property will cause this layout to be requeued. The call to
  215. * {@link Ext.layout.ContextItem#setProp setProp} will place a value in the item and not
  216. * directly into the DOM.
  217. *
  218. * **D.** Call the other layouts now in the first cycle (repeat **B** and **C** for each
  219. * layout).
  220. *
  221. * **E.** After completing a cycle, if progress was made (new properties were written to
  222. * the context) and if the {@link #layoutQueue} is not empty, the next cycle is run. If no
  223. * progress was made or no layouts are ready to run, all buffered values are written to
  224. * the DOM (a flush).
  225. *
  226. * **F.** After flushing, any layouts that were marked as {@link Ext.layout.Layout#done done}
  227. * that also have a {@link Ext.layout.Layout#completeLayout completeLayout} method are called.
  228. * This can cause them to become no longer done (see {@link #invalidate}). As with
  229. * {@link Ext.layout.Layout#calculate calculate}, this is considered a &quot;read phase&quot; and
  230. * direct DOM writes should be avoided.
  231. *
  232. * **G.** Flushing and calling any pending {@link Ext.layout.Layout#completeLayout completeLayout}
  233. * methods will likely trigger layouts that called {@link Ext.layout.ContextItem#getDomProp getDomProp}
  234. * and unblock layouts that have called {@link Ext.layout.ContextItem#domBlock domBlock}.
  235. * These variants are used when a layout needs the value to be correct in the DOM and not
  236. * simply known. If this does not cause at least one layout to enter the queue, we have a
  237. * layout FAILURE. Otherwise, we continue with the next cycle.
  238. *
  239. * **H.** Call {@link Ext.layout.Layout#calculate calculate} on any layouts in the queue
  240. * at the start of this cycle. Just a repeat of **B** through **G**.
  241. *
  242. * **I.** Once the layout has calculated all that it is resposible for, it can leave itself
  243. * in the {@link Ext.layout.Layout#done done} state. This is the value on entry to
  244. * {@link Ext.layout.Layout#calculate calculate} and must be cleared in that call if the
  245. * layout has more work to do.
  246. *
  247. * **J.** Now that all layouts are done, flush any DOM values and
  248. * {@link Ext.layout.Layout#completeLayout completeLayout} calls. This can again cause
  249. * layouts to become not done, and so we will be back on another cycle if that happens.
  250. *
  251. * **K.** After all layouts are done, call the {@link Ext.layout.Layout#finalizeLayout finalizeLayout}
  252. * method on any layouts that have one. As with {@link Ext.layout.Layout#completeLayout completeLayout},
  253. * this can cause layouts to become no longer done. This is less desirable than using
  254. * {@link Ext.layout.Layout#completeLayout completeLayout} because it will cause all
  255. * {@link Ext.layout.Layout#finalizeLayout finalizeLayout} methods to be called again
  256. * when we think things are all wrapped up.
  257. *
  258. * **L.** After finishing the last iteration, layouts that have a
  259. * {@link Ext.layout.Layout#finishedLayout finishedLayout} method will be called. This
  260. * call will only happen once per run and cannot cause layouts to be run further.
  261. *
  262. * **M.** After calling finahedLayout, layouts that have a
  263. * {@link Ext.layout.Layout#notifyOwner notifyOwner} method will be called. This
  264. * call will only happen once per run and cannot cause layouts to be run further.
  265. *
  266. * **N.** One last flush to make sure everything has been written to the DOM.
  267. *
  268. * # Inter-Layout Collaboration
  269. *
  270. * Many layout problems require collaboration between multiple layouts. In some cases, this
  271. * is as simple as a component's container layout providing results used by its component
  272. * layout or vise-versa. A slightly more distant collaboration occurs in a box layout when
  273. * stretchmax is used: the child item's component layout provides results that are consumed
  274. * by the ownerCt's box layout to determine the size of the children.
  275. *
  276. * The various forms of interdependence between a container and its children are described by
  277. * each components' {@link Ext.AbstractComponent#getSizeModel size model}.
  278. *
  279. * To facilitate this collaboration, the following pairs of properties are published to the
  280. * component's {@link Ext.layout.ContextItem ContextItem}:
  281. *
  282. * - width/height: These hold the final size of the component. The layout indicated by the
  283. * {@link Ext.AbstractComponent#getSizeModel size model} is responsible for setting these.
  284. * - contentWidth/contentHeight: These hold size information published by the container
  285. * layout or from DOM measurement. These describe the content only. These values are
  286. * used by the component layout to determine the outer width/height when that component
  287. * is {@link Ext.AbstractComponent#shrinkWrap shrink-wrapped}. They are also used to
  288. * determine overflow. All container layouts must publish these values for dimensions
  289. * that are shrink-wrapped. If a component has raw content (not container items), the
  290. * componentLayout must publish these values instead.
  291. *
  292. * @protected
  293. */
  294. Ext.define('Ext.layout.Context', {
  295. requires: [
  296. 'Ext.util.Queue',
  297. 'Ext.layout.ContextItem',
  298. 'Ext.layout.Layout',
  299. 'Ext.fx.Anim',
  300. 'Ext.fx.Manager'
  301. ],
  302. remainingLayouts: 0,
  303. <span id='Ext-layout-Context-property-state'> /**
  304. </span> * @property {Number} state One of these values:
  305. *
  306. * - 0 - Before run
  307. * - 1 - Running
  308. * - 2 - Run complete
  309. */
  310. state: 0,
  311. constructor: function (config) {
  312. var me = this;
  313. Ext.apply(me, config);
  314. // holds the ContextItem collection, keyed by element id
  315. me.items = {};
  316. // a collection of layouts keyed by layout id
  317. me.layouts = {};
  318. // the number of blocks of any kind:
  319. me.blockCount = 0;
  320. // the number of cycles that have been run:
  321. me.cycleCount = 0;
  322. // the number of flushes to the DOM:
  323. me.flushCount = 0;
  324. // the number of layout calculate calls:
  325. me.calcCount = 0;
  326. me.animateQueue = me.newQueue();
  327. me.completionQueue = me.newQueue();
  328. me.finalizeQueue = me.newQueue();
  329. me.finishQueue = me.newQueue();
  330. me.flushQueue = me.newQueue();
  331. me.invalidateData = {};
  332. <span id='Ext-layout-Context-property-layoutQueue'> /**
  333. </span> * @property {Ext.util.Queue} layoutQueue
  334. * List of layouts to perform.
  335. */
  336. me.layoutQueue = me.newQueue();
  337. // this collection is special because we ensure that there are no parent/child pairs
  338. // present, only distinct top-level components
  339. me.invalidQueue = [];
  340. me.triggers = {
  341. data: {
  342. /*
  343. layoutId: [
  344. { item: contextItem, prop: propertyName }
  345. ]
  346. */
  347. },
  348. dom: {}
  349. };
  350. },
  351. callLayout: function (layout, methodName) {
  352. this.currentLayout = layout;
  353. layout[methodName](this.getCmp(layout.owner));
  354. },
  355. cancelComponent: function (comp, isChild, isDestroying) {
  356. var me = this,
  357. components = comp,
  358. isArray = !comp.isComponent,
  359. length = isArray ? components.length : 1,
  360. i, k, klen, items, layout, newQueue, oldQueue, entry, temp,
  361. ownerCtContext;
  362. for (i = 0; i &lt; length; ++i) {
  363. if (isArray) {
  364. comp = components[i];
  365. }
  366. // If the component is being destroyed, remove the component's ContextItem from its parent's contextItem.childItems array
  367. if (isDestroying &amp;&amp; comp.ownerCt) {
  368. ownerCtContext = this.items[comp.ownerCt.el.id];
  369. if (ownerCtContext) {
  370. Ext.Array.remove(ownerCtContext.childItems, me.getCmp(comp));
  371. }
  372. }
  373. if (!isChild) {
  374. oldQueue = me.invalidQueue;
  375. klen = oldQueue.length;
  376. if (klen) {
  377. me.invalidQueue = newQueue = [];
  378. for (k = 0; k &lt; klen; ++k) {
  379. entry = oldQueue[k];
  380. temp = entry.item.target;
  381. if (temp != comp &amp;&amp; !temp.isDescendant(comp)) {
  382. newQueue.push(entry);
  383. }
  384. }
  385. }
  386. }
  387. layout = comp.componentLayout;
  388. me.cancelLayout(layout);
  389. if (layout.getLayoutItems) {
  390. items = layout.getLayoutItems();
  391. if (items.length) {
  392. me.cancelComponent(items, true);
  393. }
  394. }
  395. if (comp.isContainer &amp;&amp; !comp.collapsed) {
  396. layout = comp.layout;
  397. me.cancelLayout(layout);
  398. items = layout.getVisibleItems();
  399. if (items.length) {
  400. me.cancelComponent(items, true);
  401. }
  402. }
  403. }
  404. },
  405. cancelLayout: function (layout) {
  406. var me = this;
  407. me.completionQueue.remove(layout);
  408. me.finalizeQueue.remove(layout);
  409. me.finishQueue.remove(layout);
  410. me.layoutQueue.remove(layout);
  411. if (layout.running) {
  412. me.layoutDone(layout);
  413. }
  414. layout.ownerContext = null;
  415. },
  416. clearTriggers: function (layout, inDom) {
  417. var id = layout.id,
  418. collection = this.triggers[inDom ? 'dom' : 'data'],
  419. triggers = collection &amp;&amp; collection[id],
  420. length = (triggers &amp;&amp; triggers.length) || 0,
  421. collection, i, item, trigger;
  422. for (i = 0; i &lt; length; ++i) {
  423. trigger = triggers[i];
  424. item = trigger.item;
  425. collection = inDom ? item.domTriggers : item.triggers;
  426. delete collection[trigger.prop][id];
  427. }
  428. },
  429. <span id='Ext-layout-Context-method-flush'> /**
  430. </span> * Flushes any pending writes to the DOM by calling each ContextItem in the flushQueue.
  431. */
  432. flush: function () {
  433. var me = this,
  434. items = me.flushQueue.clear(),
  435. length = items.length, i;
  436. if (length) {
  437. ++me.flushCount;
  438. for (i = 0; i &lt; length; ++i) {
  439. items[i].flush();
  440. }
  441. }
  442. },
  443. flushAnimations: function() {
  444. var me = this,
  445. items = me.animateQueue.clear(),
  446. len = items.length,
  447. i;
  448. if (len) {
  449. for (i = 0; i &lt; len; i++) {
  450. // Each Component may refuse to participate in animations.
  451. // This is used by the BoxReorder plugin which drags a Component,
  452. // during which that Component must be exempted from layout positioning.
  453. if (items[i].target.animate !== false) {
  454. items[i].flushAnimations();
  455. }
  456. }
  457. // Ensure the first frame fires now to avoid a browser repaint with the elements in the &quot;to&quot; state
  458. // before they are returned to their &quot;from&quot; state by the animation.
  459. Ext.fx.Manager.runner();
  460. }
  461. },
  462. flushInvalidates: function () {
  463. var me = this,
  464. queue = me.invalidQueue,
  465. length = queue &amp;&amp; queue.length,
  466. comp, components, entry, i;
  467. me.invalidQueue = [];
  468. if (length) {
  469. components = [];
  470. for (i = 0; i &lt; length; ++i) {
  471. comp = (entry = queue[i]).item.target;
  472. // we filter out-of-body components here but allow them into the queue to
  473. // ensure that their child components are coalesced out (w/no additional
  474. // cost beyond our normal effort to avoid parent/child components in the
  475. // queue)
  476. if (!comp.container.isDetachedBody) {
  477. components.push(comp);
  478. if (entry.options) {
  479. me.invalidateData[comp.id] = entry.options;
  480. }
  481. }
  482. }
  483. me.invalidate(components, null);
  484. }
  485. },
  486. flushLayouts: function (queueName, methodName, dontClear) {
  487. var me = this,
  488. layouts = dontClear ? me[queueName].items : me[queueName].clear(),
  489. length = layouts.length,
  490. i, layout;
  491. if (length) {
  492. for (i = 0; i &lt; length; ++i) {
  493. layout = layouts[i];
  494. if (!layout.running) {
  495. me.callLayout(layout, methodName);
  496. }
  497. }
  498. me.currentLayout = null;
  499. }
  500. },
  501. <span id='Ext-layout-Context-method-getCmp'> /**
  502. </span> * Returns the ContextItem for a component.
  503. * @param {Ext.Component} cmp
  504. */
  505. getCmp: function (cmp) {
  506. return this.getItem(cmp, cmp.el);
  507. },
  508. <span id='Ext-layout-Context-method-getEl'> /**
  509. </span> * Returns the ContextItem for an element.
  510. * @param {Ext.layout.ContextItem} parent
  511. * @param {Ext.dom.Element} el
  512. */
  513. getEl: function (parent, el) {
  514. var item = this.getItem(el, el);
  515. if (!item.parent) {
  516. item.parent = parent;
  517. // all items share an empty children array (to avoid null checks), so we can
  518. // only push on to the children array if there is already something there (we
  519. // copy-on-write):
  520. if (parent.children.length) {
  521. parent.children.push(item);
  522. } else {
  523. parent.children = [ item ]; // now parent has its own children[] (length=1)
  524. }
  525. }
  526. return item;
  527. },
  528. getItem: function (target, el) {
  529. var id = el.id,
  530. items = this.items,
  531. item = items[id] ||
  532. (items[id] = new Ext.layout.ContextItem({
  533. context: this,
  534. target: target,
  535. el: el
  536. }));
  537. return item;
  538. },
  539. handleFailure: function () {
  540. // This method should never be called, but is need when layouts fail (hence the
  541. // &quot;should never&quot;). We just disconnect any of the layouts from the run and return
  542. // them to the state they would be in had the layout completed properly.
  543. var layouts = this.layouts,
  544. layout, key;
  545. Ext.failedLayouts = (Ext.failedLayouts || 0) + 1;
  546. //&lt;debug&gt;
  547. Ext.log('Layout run failed');
  548. //&lt;/debug&gt;
  549. for (key in layouts) {
  550. layout = layouts[key];
  551. if (layouts.hasOwnProperty(key)) {
  552. layout.running = false;
  553. layout.ownerContext = null;
  554. }
  555. }
  556. },
  557. <span id='Ext-layout-Context-method-invalidate'> /**
  558. </span> * Invalidates one or more components' layouts (component and container). This can be
  559. * called before run to identify the components that need layout or during the run to
  560. * restart the layout of a component. This is called internally to flush any queued
  561. * invalidations at the start of a cycle. If called during a run, it is not expected
  562. * that new components will be introduced to the layout.
  563. *
  564. * @param {Ext.Component/Array} components An array of Components or a single Component.
  565. * @param {Ext.layout.ContextItem} ownerCtContext The ownerCt's ContextItem.
  566. * @param {Boolean} full True if all properties should be invalidated, otherwise only
  567. * those calculated by the component should be invalidated.
  568. */
  569. invalidate: function (components, full) {
  570. var me = this,
  571. isArray = !components.isComponent,
  572. componentChildrenDone, containerChildrenDone, containerLayoutDone,
  573. firstTime, i, comp, item, items, length, componentLayout, layout,
  574. invalidateOptions, token;
  575. for (i = 0, length = isArray ? components.length : 1; i &lt; length; ++i) {
  576. comp = isArray ? components[i] : components;
  577. if (comp.rendered &amp;&amp; !comp.hidden) {
  578. item = me.getCmp(comp);
  579. componentLayout = comp.componentLayout;
  580. firstTime = !componentLayout.ownerContext;
  581. layout = (comp.isContainer &amp;&amp; !comp.collapsed) ? comp.layout : null;
  582. // Extract any invalidate() options for this item.
  583. invalidateOptions = me.invalidateData[item.id];
  584. delete me.invalidateData[item.id];
  585. // We invalidate the contextItem's in a top-down manner so that SizeModel
  586. // info for containers is available to their children. This is a critical
  587. // optimization since sizeModel determination often requires knowing the
  588. // sizeModel of the ownerCt. If this weren't cached as we descend, this
  589. // would be an O(N^2) operation! (where N=number of components, or 300+/-
  590. // in Themes)
  591. token = item.init(full, invalidateOptions);
  592. if (invalidateOptions) {
  593. me.processInvalidate(invalidateOptions, item, 'before');
  594. }
  595. // Allow the component layout a chance to effect its size model before we
  596. // recurse down the component hierarchy (since children need to know the
  597. // size model of their ownerCt).
  598. if (componentLayout.beforeLayoutCycle) {
  599. componentLayout.beforeLayoutCycle(item);
  600. }
  601. // Finish up the item-level processing that is based on the size model of
  602. // the component.
  603. token = item.initContinue(token);
  604. // Start these state variables at true, since that is the value we want if
  605. // they do not apply (i.e., no work of this kind on which to wait).
  606. componentChildrenDone = containerChildrenDone = containerLayoutDone = true;
  607. // A ComponentLayout MUST implement getLayoutItems to allow its children
  608. // to be collected. Ext.container.Container does this, but non-Container
  609. // Components which manage Components as part of their structure (e.g.,
  610. // HtmlEditor) must still return child Components via getLayoutItems.
  611. if (componentLayout.getLayoutItems) {
  612. componentLayout.renderChildren();
  613. items = componentLayout.getLayoutItems();
  614. if (items.length) {
  615. me.invalidate(items, true);
  616. componentChildrenDone = false;
  617. }
  618. }
  619. if (layout) {
  620. containerLayoutDone = false;
  621. layout.renderChildren();
  622. items = layout.getVisibleItems();
  623. if (items.length) {
  624. me.invalidate(items, true);
  625. containerChildrenDone = false;
  626. }
  627. }
  628. // Finish the processing that requires the size models of child items to
  629. // be determined (and some misc other stuff).
  630. item.initDone(token, componentChildrenDone, containerChildrenDone,
  631. containerLayoutDone);
  632. // Inform the layouts that we are about to begin (or begin again) now that
  633. // the size models of the component and its children are setup.
  634. me.resetLayout(componentLayout, item, firstTime);
  635. if (layout) {
  636. me.resetLayout(layout, item, firstTime);
  637. }
  638. // This has to occur after the component layout has had a chance to begin
  639. // so that we can determine what kind of animation might be needed. TODO-
  640. // move this determination into the layout itself.
  641. item.initAnimation();
  642. if (invalidateOptions) {
  643. me.processInvalidate(invalidateOptions, item, 'after');
  644. }
  645. }
  646. }
  647. me.currentLayout = null;
  648. },
  649. layoutDone: function (layout) {
  650. var ownerContext = layout.ownerContext,
  651. ownerCtContext;
  652. layout.running = false;
  653. // Once a component layout completes, we can mark it as &quot;done&quot; but we can also
  654. // decrement the remainingChildLayouts property on the ownerCtContext. When that
  655. // goes to 0, we can mark the ownerCtContext as &quot;childrenDone&quot;.
  656. if (layout.isComponentLayout) {
  657. if (ownerContext.measuresBox) {
  658. ownerContext.onBoxMeasured(); // be sure to release our boxParent
  659. }
  660. ownerContext.setProp('done', true);
  661. ownerCtContext = ownerContext.ownerCtContext;
  662. if (ownerCtContext) {
  663. if (ownerContext.target.ownerLayout.isComponentLayout) {
  664. if (! --ownerCtContext.remainingComponentChildLayouts) {
  665. ownerCtContext.setProp('componentChildrenDone', true);
  666. }
  667. } else {
  668. if (! --ownerCtContext.remainingContainerChildLayouts) {
  669. ownerCtContext.setProp('containerChildrenDone', true);
  670. }
  671. }
  672. if (! --ownerCtContext.remainingChildLayouts) {
  673. ownerCtContext.setProp('childrenDone', true);
  674. }
  675. }
  676. } else {
  677. ownerContext.setProp('containerLayoutDone', true);
  678. }
  679. --this.remainingLayouts;
  680. ++this.progressCount; // a layout completion is progress
  681. },
  682. newQueue: function () {
  683. return new Ext.util.Queue();
  684. },
  685. processInvalidate: function (options, item, name) {
  686. // When calling a callback, the currentLayout needs to be adjusted so
  687. // that whichever layout caused the invalidate is the currentLayout...
  688. if (options[name]) {
  689. var me = this,
  690. currentLayout = me.currentLayout;
  691. me.currentLayout = options.layout || null;
  692. options[name](item, options);
  693. me.currentLayout = currentLayout;
  694. }
  695. },
  696. <span id='Ext-layout-Context-method-queueAnimation'> /**
  697. </span> * Queues a ContextItem to have its {@link Ext.layout.ContextItem#flushAnimations} method called.
  698. *
  699. * @param {Ext.layout.ContextItem} item
  700. * @private
  701. */
  702. queueAnimation: function (item) {
  703. this.animateQueue.add(item);
  704. },
  705. <span id='Ext-layout-Context-method-queueCompletion'> /**
  706. </span> * Queues a layout to have its {@link Ext.layout.Layout#completeLayout} method called.
  707. *
  708. * @param {Ext.layout.Layout} layout
  709. * @private
  710. */
  711. queueCompletion: function (layout) {
  712. this.completionQueue.add(layout);
  713. },
  714. <span id='Ext-layout-Context-method-queueFinalize'> /**
  715. </span> * Queues a layout to have its {@link Ext.layout.Layout#finalizeLayout} method called.
  716. *
  717. * @param {Ext.layout.Layout} layout
  718. * @private
  719. */
  720. queueFinalize: function (layout) {
  721. this.finalizeQueue.add(layout);
  722. },
  723. <span id='Ext-layout-Context-method-queueFlush'> /**
  724. </span> * Queues a ContextItem for the next flush to the DOM. This should only be called by
  725. * the {@link Ext.layout.ContextItem} class.
  726. *
  727. * @param {Ext.layout.ContextItem} item
  728. * @private
  729. */
  730. queueFlush: function (item) {
  731. this.flushQueue.add(item);
  732. },
  733. chainFns: function (oldOptions, newOptions, funcName) {
  734. var me = this,
  735. oldLayout = oldOptions.layout,
  736. newLayout = newOptions.layout,
  737. oldFn = oldOptions[funcName],
  738. newFn = newOptions[funcName];
  739. // Call newFn last so it can get the final word on things... also, the &quot;this&quot;
  740. // pointer will be passed correctly by createSequence with oldFn first.
  741. return function (contextItem) {
  742. var prev = me.currentLayout;
  743. if (oldFn) {
  744. me.currentLayout = oldLayout;
  745. oldFn.call(oldOptions.scope || oldOptions, contextItem, oldOptions);
  746. }
  747. me.currentLayout = newLayout;
  748. newFn.call(newOptions.scope || newOptions, contextItem, newOptions);
  749. me.currentLayout = prev;
  750. };
  751. },
  752. <span id='Ext-layout-Context-method-queueInvalidate'> /**
  753. </span> * Queue a component (and its tree) to be invalidated on the next cycle.
  754. *
  755. * @param {Ext.Component/Ext.layout.ContextItem} item The component or ContextItem to invalidate.
  756. * @param {Object} options An object describing how to handle the invalidation (see
  757. * {@link Ext.layout.ContextItem#invalidate} for details).
  758. * @private
  759. */
  760. queueInvalidate: function (item, options) {
  761. var me = this,
  762. newQueue = [],
  763. oldQueue = me.invalidQueue,
  764. index = oldQueue.length,
  765. comp, old, oldComp, oldOptions, oldState;
  766. if (item.isComponent) {
  767. item = me.getCmp(comp = item);
  768. } else {
  769. comp = item.target;
  770. }
  771. item.invalid = true;
  772. // See if comp is contained by any component already in the queue (ignore comp if
  773. // that is the case). Eliminate any components in the queue that are contained by
  774. // comp (by not adding them to newQueue).
  775. while (index--) {
  776. old = oldQueue[index];
  777. oldComp = old.item.target;
  778. if (comp.isDescendant(oldComp)) {
  779. return; // oldComp contains comp, so this invalidate is redundant
  780. }
  781. if (oldComp == comp) {
  782. // if already in the queue, update the options...
  783. if (!(oldOptions = old.options)) {
  784. old.options = options;
  785. } else if (options) {
  786. if (options.widthModel) {
  787. oldOptions.widthModel = options.widthModel;
  788. }
  789. if (options.heightModel) {
  790. oldOptions.heightModel = options.heightModel;
  791. }
  792. if (!(oldState = oldOptions.state)) {
  793. oldOptions.state = options.state;
  794. } else if (options.state) {
  795. Ext.apply(oldState, options.state);
  796. }
  797. if (options.before) {
  798. oldOptions.before = me.chainFns(oldOptions, options, 'before');
  799. }
  800. if (options.after) {
  801. oldOptions.after = me.chainFns(oldOptions, options, 'after');
  802. }
  803. }
  804. // leave the old queue alone now that we've update this comp's entry...
  805. return;
  806. }
  807. if (!oldComp.isDescendant(comp)) {
  808. newQueue.push(old); // comp does not contain oldComp
  809. }
  810. // else if (oldComp isDescendant of comp) skip
  811. }
  812. // newQueue contains only those components not a descendant of comp
  813. // to get here, comp must not be a child of anything already in the queue, so it
  814. // needs to be added along with its &quot;options&quot;:
  815. newQueue.push({ item: item, options: options });
  816. me.invalidQueue = newQueue;
  817. },
  818. queueItemLayouts: function (item) {
  819. var comp = item.isComponent ? item : item.target,
  820. layout = comp.componentLayout;
  821. if (!layout.pending &amp;&amp; !layout.invalid &amp;&amp; !layout.done) {
  822. this.queueLayout(layout);
  823. }
  824. layout = comp.layout;
  825. if (layout &amp;&amp; !layout.pending &amp;&amp; !layout.invalid &amp;&amp; !layout.done) {
  826. this.queueLayout(layout);
  827. }
  828. },
  829. <span id='Ext-layout-Context-method-queueLayout'> /**
  830. </span> * Queues a layout for the next calculation cycle. This should not be called if the
  831. * layout is done, blocked or already in the queue. The only classes that should call
  832. * this method are this class and {@link Ext.layout.ContextItem}.
  833. *
  834. * @param {Ext.layout.Layout} layout The layout to add to the queue.
  835. * @private
  836. */
  837. queueLayout: function (layout) {
  838. this.layoutQueue.add(layout);
  839. layout.pending = true;
  840. },
  841. <span id='Ext-layout-Context-method-resetLayout'> /**
  842. </span> * Resets the given layout object. This is called at the start of the run and can also
  843. * be called during the run by calling {@link #invalidate}.
  844. */
  845. resetLayout: function (layout, ownerContext, firstTime) {
  846. var me = this,
  847. ownerCtContext;
  848. me.currentLayout = layout;
  849. layout.done = false;
  850. layout.pending = true;
  851. layout.firedTriggers = 0;
  852. me.layoutQueue.add(layout);
  853. if (firstTime) {
  854. me.layouts[layout.id] = layout; // track the layout for this run by its id
  855. layout.running = true;
  856. if (layout.finishedLayout) {
  857. me.finishQueue.add(layout);
  858. }
  859. // reset or update per-run counters:
  860. ++me.remainingLayouts;
  861. ++layout.layoutCount; // the number of whole layouts run for the layout
  862. layout.ownerContext = ownerContext;
  863. layout.beginCount = 0; // the number of beginLayout calls
  864. layout.blockCount = 0; // the number of blocks set for the layout
  865. layout.calcCount = 0; // the number of times calculate is called
  866. layout.triggerCount = 0; // the number of triggers set for the layout
  867. // Count the children of each ownerCt so we can tell when they are all done:
  868. if (layout.isComponentLayout &amp;&amp; (ownerCtContext = ownerContext.ownerCtContext)) {
  869. // This layout's ownerCt is in this run... The component associated with
  870. // this layout (the &quot;target&quot;) could be owned by the ownerCt's container
  871. // layout or component layout (e.g. docked items)! To manage this, we keep
  872. // two counters for these and one for the combined total:
  873. if (ownerContext.target.ownerLayout.isComponentLayout) {
  874. ++ownerCtContext.remainingComponentChildLayouts;
  875. } else {
  876. ++ownerCtContext.remainingContainerChildLayouts;
  877. }
  878. ++ownerCtContext.remainingChildLayouts;
  879. }
  880. if (!layout.initialized) {
  881. layout.initLayout();
  882. }
  883. layout.beginLayout(ownerContext);
  884. } else {
  885. ++layout.beginCount;
  886. if (!layout.running) {
  887. // back into the mahem with this one:
  888. ++me.remainingLayouts;
  889. layout.running = true;
  890. if (layout.isComponentLayout) {
  891. // this one is fun... if we call setProp('done', false) that would still
  892. // trigger/unblock layouts, but what layouts are really looking for with
  893. // this property is for it to go to true, not just be set to a value...
  894. ownerContext.unsetProp('done');
  895. // On subsequent resets we increment the child layout count properties
  896. // on ownerCtContext and clear 'childrenDone' and the appropriate other
  897. // indicator as we transition to 1:
  898. ownerCtContext = ownerContext.ownerCtContext;
  899. if (ownerCtContext) {
  900. if (ownerContext.target.ownerLayout.isComponentLayout) {
  901. if (++ownerCtContext.remainingComponentChildLayouts == 1) {
  902. ownerCtContext.unsetProp('componentChildrenDone');
  903. }
  904. } else {
  905. if (++ownerCtContext.remainingContainerChildLayouts == 1) {
  906. ownerCtContext.unsetProp('containerChildrenDone');
  907. }
  908. }
  909. if (++ownerCtContext.remainingChildLayouts == 1) {
  910. ownerCtContext.unsetProp('childrenDone');
  911. }
  912. }
  913. }
  914. // and it needs to be removed from the completion and/or finalize queues...
  915. me.completionQueue.remove(layout);
  916. me.finalizeQueue.remove(layout);
  917. }
  918. }
  919. layout.beginLayoutCycle(ownerContext, firstTime);
  920. },
  921. <span id='Ext-layout-Context-method-run'> /**
  922. </span> * Runs the layout calculations. This can be called only once on this object.
  923. * @return {Boolean} True if all layouts were completed, false if not.
  924. */
  925. run: function () {
  926. var me = this,
  927. flushed = false,
  928. watchDog = 100;
  929. me.flushInvalidates();
  930. me.state = 1;
  931. me.totalCount = me.layoutQueue.getCount();
  932. // We may start with unflushed data placed by beginLayout calls. Since layouts may
  933. // use setProp as a convenience, even in a write phase, we don't want to transition
  934. // to a read phase with unflushed data since we can write it now &quot;cheaply&quot;. Also,
  935. // these value could easily be needed in the DOM in order to really get going with
  936. // the calculations. In particular, fixed (configured) dimensions fall into this
  937. // category.
  938. me.flush();
  939. // While we have layouts that have not completed...
  940. while ((me.remainingLayouts || me.invalidQueue.length) &amp;&amp; watchDog--) {
  941. if (me.invalidQueue.length) {
  942. me.flushInvalidates();
  943. }
  944. // if any of them can run right now, run them
  945. if (me.runCycle()) {
  946. flushed = false; // progress means we probably need to flush something
  947. // but not all progress appears in the flushQueue (e.g. 'contentHeight')
  948. } else if (!flushed) {
  949. // as long as we are making progress, flush updates to the DOM and see if
  950. // that triggers or unblocks any layouts...
  951. me.flush();
  952. flushed = true; // all flushed now, so more progress is required
  953. me.flushLayouts('completionQueue', 'completeLayout');
  954. } else {
  955. // after a flush, we must make progress or something is WRONG
  956. me.state = 2;
  957. break;
  958. }
  959. if (!(me.remainingLayouts || me.invalidQueue.length)) {
  960. me.flush();
  961. me.flushLayouts('completionQueue', 'completeLayout');
  962. me.flushLayouts('finalizeQueue', 'finalizeLayout');
  963. }
  964. }
  965. return me.runComplete();
  966. },
  967. runComplete: function () {
  968. var me = this;
  969. me.state = 2;
  970. if (me.remainingLayouts) {
  971. me.handleFailure();
  972. return false;
  973. }
  974. me.flush();
  975. // Call finishedLayout on all layouts, but do not clear the queue.
  976. me.flushLayouts('finishQueue', 'finishedLayout', true);
  977. // Call notifyOwner on all layouts and then clear the queue.
  978. me.flushLayouts('finishQueue', 'notifyOwner');
  979. me.flush(); // in case any setProp calls were made
  980. me.flushAnimations();
  981. return true;
  982. },
  983. <span id='Ext-layout-Context-method-runCycle'> /**
  984. </span> * Performs one layout cycle by calling each layout in the layout queue.
  985. * @return {Boolean} True if some progress was made, false if not.
  986. * @protected
  987. */
  988. runCycle: function () {
  989. var me = this,
  990. layouts = me.layoutQueue.clear(),
  991. length = layouts.length,
  992. i;
  993. ++me.cycleCount;
  994. // This value is incremented by ContextItem#setProp whenever new values are set
  995. // (thereby detecting forward progress):
  996. me.progressCount = 0;
  997. for (i = 0; i &lt; length; ++i) {
  998. me.runLayout(me.currentLayout = layouts[i]);
  999. }
  1000. me.currentLayout = null;
  1001. return me.progressCount &gt; 0;
  1002. },
  1003. <span id='Ext-layout-Context-method-runLayout'> /**
  1004. </span> * Runs one layout as part of a cycle.
  1005. * @private
  1006. */
  1007. runLayout: function (layout) {
  1008. var me = this,
  1009. ownerContext = me.getCmp(layout.owner);
  1010. layout.pending = false;
  1011. if (ownerContext.state.blocks) {
  1012. return;
  1013. }
  1014. // We start with the assumption that the layout will finish and if it does not, it
  1015. // must clear this flag. It turns out this is much simpler than knowing when a layout
  1016. // is done (100% correctly) when base classes and derived classes are collaborating.
  1017. // Knowing that some part of the layout is not done is much more obvious.
  1018. layout.done = true;
  1019. ++layout.calcCount;
  1020. ++me.calcCount;
  1021. layout.calculate(ownerContext);
  1022. if (layout.done) {
  1023. me.layoutDone(layout);
  1024. if (layout.completeLayout) {
  1025. me.queueCompletion(layout);
  1026. }
  1027. if (layout.finalizeLayout) {
  1028. me.queueFinalize(layout);
  1029. }
  1030. } else if (!layout.pending &amp;&amp; !layout.invalid &amp;&amp; !(layout.blockCount + layout.triggerCount - layout.firedTriggers)) {
  1031. // A layout that is not done and has no blocks or triggers that will queue it
  1032. // automatically, must be queued now:
  1033. me.queueLayout(layout);
  1034. }
  1035. },
  1036. <span id='Ext-layout-Context-method-setItemSize'> /**
  1037. </span> * Set the size of a component, element or composite or an array of components or elements.
  1038. * @param {Ext.Component/Ext.Component[]/Ext.dom.Element/Ext.dom.Element[]/Ext.dom.CompositeElement}
  1039. * The item(s) to size.
  1040. * @param {Number} width The new width to set (ignored if undefined or NaN).
  1041. * @param {Number} height The new height to set (ignored if undefined or NaN).
  1042. */
  1043. setItemSize: function(item, width, height) {
  1044. var items = item,
  1045. len = 1,
  1046. contextItem, i;
  1047. // NOTE: we don't pre-check for validity because:
  1048. // - maybe only one dimension is valid
  1049. // - the diagnostics layer will track the setProp call to help find who is trying
  1050. // (but failing) to set a property
  1051. // - setProp already checks this anyway
  1052. if (item.isComposite) {
  1053. items = item.elements;
  1054. len = items.length;
  1055. item = items[0];
  1056. } else if (!item.dom &amp;&amp; !item.el) { // array by process of elimination
  1057. len = items.length;
  1058. item = items[0];
  1059. }
  1060. // else len = 1 and items = item (to avoid error on &quot;items[++i]&quot;)
  1061. for (i = 0; i &lt; len; ) {
  1062. contextItem = this.get(item);
  1063. contextItem.setSize(width, height);
  1064. item = items[++i]; // this accomodation avoids making an array of 1
  1065. }
  1066. }
  1067. });
  1068. </pre>
  1069. </body>
  1070. </html>