]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/layout/ContextItem.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / layout / ContextItem.js
CommitLineData
6527f429
DM
1/**\r
2 * This class manages state information for a component or element during a layout.\r
3 * \r
4 * # Blocks\r
5 *\r
6 * A "block" is a required value that is preventing further calculation. When a layout has\r
7 * encountered a situation where it cannot possibly calculate results, it can associate\r
8 * itself with the context item and missing property so that it will not be rescheduled\r
9 * until that property is set.\r
10 * \r
11 * Blocks are a one-shot registration. Once the property changes, the block is removed.\r
12 * \r
13 * Be careful with blocks. If *any* further calculations can be made, a block is not the\r
14 * right choice.\r
15 * \r
16 * # Triggers\r
17 *\r
18 * Whenever any call to {@link #getProp}, {@link #getDomProp}, {@link #hasProp} or\r
19 * {@link #hasDomProp} is made, the current layout is automatically registered as being\r
20 * dependent on that property in the appropriate state. Any changes to the property will\r
21 * trigger the layout and it will be queued in the {@link Ext.layout.Context}.\r
22 *\r
23 * Triggers, once added, remain for the entire layout. Any changes to the property will\r
24 * reschedule all unfinished layouts in their trigger set.\r
25 *\r
26 * @private\r
27 */\r
28Ext.define('Ext.layout.ContextItem', {\r
29\r
30 heightModel: null,\r
31 widthModel: null,\r
32 sizeModel: null,\r
33\r
34 /**\r
35 * There are several cases that allow us to skip (opt out) of laying out a component\r
36 * and its children as long as its `lastBox` is not marked as `invalid`. If anything\r
37 * happens to change things, the `lastBox` is marked as `invalid` by `updateLayout`\r
38 * as it ascends the component hierarchy.\r
39 * \r
40 * @property {Boolean} optOut\r
41 * @private\r
42 * @readonly\r
43 */\r
44 optOut: false,\r
45\r
46 ownerSizePolicy: null, // plaed here by Component.getSizeModel\r
47\r
48 boxChildren: null,\r
49\r
50 boxParent: null,\r
51\r
52 children: [],\r
53\r
54 dirty: null,\r
55\r
56 // The number of dirty properties\r
57 dirtyCount: 0,\r
58\r
59 hasRawContent: true,\r
60\r
61 isContextItem: true,\r
62\r
63 isTopLevel: false,\r
64\r
65 consumersContentHeight: 0,\r
66 consumersContentWidth: 0,\r
67 consumersContainerHeight: 0,\r
68 consumersContainerWidth: 0,\r
69 consumersHeight: 0,\r
70 consumersWidth: 0,\r
71\r
72 ownerCtContext: null,\r
73\r
74 remainingChildDimensions: 0,\r
75\r
76 // the current set of property values:\r
77 props: null,\r
78\r
79 /**\r
80 * @property {Object} state\r
81 * State variables that are cleared when invalidated. Only applies to component items.\r
82 */\r
83 state: null,\r
84\r
85 /**\r
86 * @property {Boolean} wrapsComponent\r
87 * True if this item wraps a Component (rather than an Element).\r
88 * @readonly\r
89 */\r
90 wrapsComponent: false,\r
91\r
92 constructor: function (config) {\r
93 var me = this,\r
94 sizeModels = Ext.layout.SizeModel.sizeModels,\r
95 configured = sizeModels.configured,\r
96 shrinkWrap = sizeModels.shrinkWrap,\r
97 el, lastBox, ownerCt, ownerCtContext, props, sizeModel, target,\r
98 lastWidth, lastHeight, sameWidth, sameHeight, widthModel, heightModel, optOut;\r
99\r
100 Ext.apply(me, config);\r
101\r
102 target = me.target;\r
103 el = me.el;\r
104 me.id = target.id;\r
105\r
106 // These hold collections of layouts that are either blocked or triggered by sets\r
107 // to our properties (either ASAP or after flushing to the DOM). All of them have\r
108 // the same structure:\r
109 //\r
110 // me.blocks = {\r
111 // width: {\r
112 // 'layout-1001': layout1001\r
113 // }\r
114 // }\r
115 //\r
116 // The property name is the primary key which yields an object keyed by layout id\r
117 // with the layout instance as the value. This prevents duplicate entries for one\r
118 // layout and gives O(1) access to the layout instance when we need to iterate and\r
119 // process them.\r
120 // \r
121 // me.blocks = {};\r
122 // me.domBlocks = {};\r
123 // me.domTriggers = {};\r
124 // me.triggers = {};\r
125\r
126 me.flushedProps = {};\r
127 me.props = props = {};\r
128\r
129 // the set of cached styles for the element:\r
130 me.styles = {};\r
131\r
132 if (!target.isComponent) {\r
133 lastBox = el.lastBox;\r
134 } else {\r
135 me.wrapsComponent = true;\r
136 me.framing = target.frameSize || null;\r
137 me.isComponentChild = target.ownerLayout && target.ownerLayout.isComponentLayout;\r
138\r
139 lastBox = target.lastBox;\r
140\r
141 // These items are created top-down, so the ContextItem of our ownerCt should\r
142 // be available (if it is part of this layout run).\r
143 ownerCt = target.ownerCt;\r
144 if (ownerCt && (ownerCtContext = ownerCt.el && me.context.items[ownerCt.el.id])) {\r
145 me.ownerCtContext = ownerCtContext;\r
146 }\r
147\r
148 // If our ownerCtContext is in the run, it will have a SizeModel that we use to\r
149 // optimize the determination of our sizeModel. Also see recalculateSizeModel, similar\r
150 // logic exists there.\r
151 me.sizeModel = sizeModel = target.getSizeModel(ownerCtContext &&\r
152 ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);\r
153\r
154 // NOTE: The initial determination of sizeModel is valid (thankfully) and is\r
155 // needed to cope with adding components to a layout run on-the-fly (e.g., in\r
156 // the menu overflow handler of a box layout). Since this is the case, we do\r
157 // not need to recompute the sizeModel in init unless it is a "full" init (as\r
158 // our ownerCt's sizeModel could have changed in that case).\r
159\r
160 me.widthModel = widthModel = sizeModel.width;\r
161 me.heightModel = heightModel = sizeModel.height;\r
162\r
163 // The lastBox is populated early but does not get an "invalid" property\r
164 // until layout has occurred. The "false" value is placed in the lastBox\r
165 // by Component.finishedLayout.\r
166 if (lastBox && lastBox.invalid === false) {\r
167 sameWidth = (target.width === (lastWidth = lastBox.width));\r
168 sameHeight = (target.height === (lastHeight = lastBox.height));\r
169\r
170 if (widthModel === shrinkWrap && heightModel === shrinkWrap) {\r
171 optOut = true;\r
172 } else if (widthModel === configured && sameWidth) {\r
173 optOut = heightModel === shrinkWrap ||\r
174 (heightModel === configured && sameHeight);\r
175 }\r
176\r
177 if (optOut) {\r
178 // Flag this component and capture its last size...\r
179 me.optOut = true;\r
180 props.width = lastWidth;\r
181 props.height = lastHeight;\r
182 }\r
183 }\r
184 }\r
185\r
186 me.lastBox = lastBox;\r
187 },\r
188\r
189 /**\r
190 * Clears all properties on this object except (perhaps) those not calculated by this\r
191 * component. This is more complex than it would seem because a layout can decide to\r
192 * invalidate its results and run the component's layouts again, but since some of the\r
193 * values may be calculated by the container, care must be taken to preserve those\r
194 * values.\r
195 *\r
196 * @param {Boolean} full True if all properties are to be invalidated, false to keep\r
197 * those calculated by the ownerCt.\r
198 * @return {Mixed} A value to pass as the first argument to {@link #initContinue}.\r
199 * @private\r
200 */\r
201 init: function (full, options) {\r
202 var me = this,\r
203 oldProps = me.props,\r
204 oldDirty = me.dirty,\r
205 ownerCtContext = me.ownerCtContext,\r
206 ownerLayout = me.target.ownerLayout,\r
207 firstTime = !me.state,\r
208 ret = full || firstTime,\r
209 children, i, n, ownerCt, sizeModel, target,\r
210 oldHeightModel = me.heightModel,\r
211 oldWidthModel = me.widthModel,\r
212 newHeightModel, newWidthModel,\r
213 remainingCount = 0;\r
214\r
215 me.dirty = me.invalid = false;\r
216 me.props = {};\r
217\r
218 // Reset the number of child dimensions since the children will add their part:\r
219 me.remainingChildDimensions = 0;\r
220\r
221 if (me.boxChildren) {\r
222 me.boxChildren.length = 0; // keep array (more GC friendly)\r
223 }\r
224\r
225 if (!firstTime) {\r
226 me.clearAllBlocks('blocks');\r
227 me.clearAllBlocks('domBlocks');\r
228 }\r
229\r
230 // For Element wrappers, we are done...\r
231 if (!me.wrapsComponent) {\r
232 return ret;\r
233 }\r
234\r
235 // From here on, we are only concerned with Component wrappers...\r
236 target = me.target;\r
237 me.state = {}; // only Component wrappers need a "state"\r
238\r
239 if (firstTime) {\r
240 // This must occur before we proceed since it can do many things (like add\r
241 // child items perhaps):\r
242 if (target.beforeLayout && target.beforeLayout !== Ext.emptyFn) {\r
243 target.beforeLayout();\r
244 }\r
245\r
246 // Determine the ownerCtContext if we aren't given one. Normally the firstTime\r
247 // we meet a component is before the context is run, but it is possible for\r
248 // components to be added to a run that is already in progress. If so, we have\r
249 // to lookup the ownerCtContext since the odds are very high that the new\r
250 // component is a child of something already in the run. It is currently\r
251 // unsupported to drag in the owner of a running component (needs testing).\r
252 if (!ownerCtContext && (ownerCt = target.ownerCt)) {\r
253 ownerCtContext = me.context.items[ownerCt.el.id];\r
254 }\r
255\r
256 if (ownerCtContext) {\r
257 me.ownerCtContext = ownerCtContext;\r
258 me.isBoxParent = ownerLayout && ownerLayout.isItemBoxParent(me);\r
259 } else {\r
260 me.isTopLevel = true; // this is used by initAnimation...\r
261 }\r
262\r
263 me.frameBodyContext = me.getEl('frameBody');\r
264 } else {\r
265 ownerCtContext = me.ownerCtContext;\r
266\r
267 // In theory (though untested), this flag can change on-the-fly...\r
268 me.isTopLevel = !ownerCtContext;\r
269\r
270 // Init the children element items since they may have dirty state (no need to\r
271 // do this the firstTime).\r
272 children = me.children;\r
273 for (i = 0, n = children.length; i < n; ++i) {\r
274 children[i].init(true);\r
275 }\r
276 }\r
277\r
278 // We need to know how we will determine content size: containers can look at the\r
279 // results of their items but non-containers or item-less containers with just raw\r
280 // markup need to be measured in the DOM:\r
281 me.hasRawContent = !(target.isContainer && target.items.items.length > 0);\r
282\r
283 if (full) {\r
284 // We must null these out or getSizeModel will assume they are the correct,\r
285 // dynamic size model and return them (the previous dynamic sizeModel).\r
286 me.widthModel = me.heightModel = null;\r
287 sizeModel = target.getSizeModel(ownerCtContext && \r
288 ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);\r
289\r
290 if (firstTime) {\r
291 me.sizeModel = sizeModel;\r
292 }\r
293\r
294 me.widthModel = sizeModel.width;\r
295 me.heightModel = sizeModel.height;\r
296\r
297 // if we are a container child (e.g., not a docked item), and this is a full\r
298 // init, that means our parent was invalidated, and therefore we must initialize\r
299 // our remainingChildDimensions to ensure that containerChildrenSizeDone\r
300 // gets set properly once all dimensions have had their sizes determined.\r
301 // There are 3 possible scenarios here:\r
302 //\r
303 // 1. Layouts that both read and set sizes of their items (e.g. box). These\r
304 // layouts must always add both dimensions to remainingChildDimensions.\r
305 //\r
306 // 2. Layouts that neither read nor set the size of their items (e.g.\r
307 // autocontainer, form). These layouts will not create context items for their\r
308 // children, and so we will never end up here.\r
309 //\r
310 // 3. Layouts that may set the size of their items, but will never read them\r
311 // because they measure an outer containing element in the shrink-wrapping\r
312 // dimension(s) (e.g. anchor, column). There are 2 possible outcomes:\r
313 // a. The child item uses liquid CSS layout. In this case, the only dimensions\r
314 // that affect containerChildrenSizeDone are the dimensions that the owner\r
315 // layout is responsible for calculating, and so these are the dimensions\r
316 // that are added to remainingChildDimensions. Non-calculated dimensions will\r
317 // never be published because the child's component layout does not run.\r
318 //\r
319 // b. The child item does not use liquid CSS layout. In this case, the\r
320 // component layout will run like normal, and any non-calculated dimensions\r
321 // will be published, therefore, we need to add both dimensions to\r
322 // remainingChildDimensions\r
323 if (ownerCtContext && !me.isComponentChild) {\r
324 if (ownerLayout.needsItemSize || !target.liquidLayout) {\r
325 ownerCtContext.remainingChildDimensions += 2;\r
326 } else {\r
327 if (me.widthModel.calculated) {\r
328 ++ownerCtContext.remainingChildDimensions;\r
329 }\r
330 if (me.heightModel.calculated) {\r
331 ++ownerCtContext.remainingChildDimensions;\r
332 }\r
333 }\r
334 }\r
335 } else if (oldProps) {\r
336 // these are almost always calculated by the ownerCt (we might need to track\r
337 // this at some point more carefully):\r
338 me.recoverProp('x', oldProps, oldDirty);\r
339 me.recoverProp('y', oldProps, oldDirty);\r
340 \r
341 // if these are calculated by the ownerCt, don't trash them:\r
342 if (me.widthModel.calculated) {\r
343 me.recoverProp('width', oldProps, oldDirty);\r
344 } else if ('width' in oldProps) {\r
345 ++remainingCount;\r
346 }\r
347 if (me.heightModel.calculated) {\r
348 me.recoverProp('height', oldProps, oldDirty);\r
349 } else if ('height' in oldProps) {\r
350 ++remainingCount;\r
351 }\r
352 \r
353 // if we are a container child and this is not a full init, that means our\r
354 // parent was not invalidated and therefore only the dimensions that were\r
355 // set last time and removed from remainingChildDimensions last time, need to\r
356 // be added back to remainingChildDimensions. This only needs to happen for\r
357 // properties that we don't recover above (model=calculated)\r
358 if (ownerCtContext && !me.isComponentChild) {\r
359 ownerCtContext.remainingChildDimensions += remainingCount;\r
360 }\r
361 }\r
362\r
363 if (oldProps && ownerLayout && ownerLayout.manageMargins) {\r
364 me.recoverProp('margin-top', oldProps, oldDirty);\r
365 me.recoverProp('margin-right', oldProps, oldDirty);\r
366 me.recoverProp('margin-bottom', oldProps, oldDirty);\r
367 me.recoverProp('margin-left', oldProps, oldDirty);\r
368 }\r
369\r
370 // Process any invalidate options present. These can only come from explicit calls\r
371 // to the invalidate() method.\r
372 if (options) {\r
373 // Consider a container box with wrapping text. If the box is made wider, the\r
374 // text will take up less height (until there is no more wrapping). Conversely,\r
375 // if the box is made narrower, the height starts to increase due to wrapping.\r
376 //\r
377 // Imposing a minWidth constraint would increase the width. This may decrease\r
378 // the height. If the box is shrinkWrap, however, the width will already be\r
379 // such that there is no wrapping, so the height will not further decrease.\r
380 // Since the height will also not increase if we widen the box, there is no\r
381 // problem simultaneously imposing a minHeight or maxHeight constraint.\r
382 //\r
383 // When we impose as maxWidth constraint, however, we are shrinking the box\r
384 // which may increase the height. If we are imposing a maxHeight constraint,\r
385 // that is fine because a further increased height will still need to be\r
386 // constrained. But if we are imposing a minHeight constraint, we cannot know\r
387 // whether the increase in height due to wrapping will be greater than the\r
388 // minHeight. If we impose a minHeight constraint at the same time, then, we\r
389 // could easily be locking in the wrong height.\r
390 //\r
391 // It is important to note that this logic applies to simultaneously *adding*\r
392 // both a maxWidth and a minHeight constraint. It is perfectly fine to have\r
393 // a state with both constraints, but we cannot add them both at once.\r
394 newHeightModel = options.heightModel;\r
395 newWidthModel = options.widthModel;\r
396 if (newWidthModel && newHeightModel && oldWidthModel && oldHeightModel) {\r
397 if (oldWidthModel.shrinkWrap && oldHeightModel.shrinkWrap) {\r
398 if (newWidthModel.constrainedMax && newHeightModel.constrainedMin) {\r
399 newHeightModel = null;\r
400 }\r
401 }\r
402 }\r
403\r
404 // Apply size model updates (if any) and state updates (if any).\r
405 if (newWidthModel) {\r
406 me.widthModel = newWidthModel;\r
407 }\r
408 if (newHeightModel) {\r
409 me.heightModel = newHeightModel;\r
410 }\r
411\r
412 if (options.state) {\r
413 Ext.apply(me.state, options.state);\r
414 }\r
415 }\r
416\r
417 return ret;\r
418 },\r
419\r
420 /**\r
421 * @private\r
422 */\r
423 initContinue: function (full) {\r
424 var me = this,\r
425 ownerCtContext = me.ownerCtContext,\r
426 comp = me.target,\r
427 widthModel = me.widthModel,\r
428 inheritedState = comp.getInherited(),\r
429 boxParent;\r
430\r
431 if (widthModel.fixed) { // calculated or configured\r
432 inheritedState.inShrinkWrapTable = false;\r
433 } else {\r
434 delete inheritedState.inShrinkWrapTable;\r
435 }\r
436\r
437 if (full) {\r
438 if (ownerCtContext && widthModel.shrinkWrap) {\r
439 boxParent = ownerCtContext.isBoxParent ? ownerCtContext : ownerCtContext.boxParent;\r
440 if (boxParent) {\r
441 boxParent.addBoxChild(me);\r
442 }\r
443 } else if (widthModel.natural) {\r
444 me.boxParent = ownerCtContext;\r
445 }\r
446 }\r
447\r
448 return full;\r
449 },\r
450\r
451 /**\r
452 * @private\r
453 */\r
454 initDone: function(containerLayoutDone) {\r
455 var me = this,\r
456 props = me.props,\r
457 state = me.state;\r
458\r
459 // These properties are only set when they are true:\r
460 if (me.remainingChildDimensions === 0) {\r
461 props.containerChildrenSizeDone = true;\r
462 }\r
463 if (containerLayoutDone) {\r
464 props.containerLayoutDone = true;\r
465 }\r
466\r
467 if (me.boxChildren && me.boxChildren.length && me.widthModel.shrinkWrap) {\r
468 // set a very large width to allow the children to measure their natural\r
469 // widths (this is cleared once all children have been measured):\r
470 me.el.setWidth(10000);\r
471\r
472 // don't run layouts for this component until we clear this width...\r
473 state.blocks = (state.blocks || 0) + 1;\r
474 }\r
475 },\r
476\r
477 /**\r
478 * @private\r
479 */\r
480 initAnimation: function() {\r
481 var me = this,\r
482 target = me.target,\r
483 ownerCtContext = me.ownerCtContext;\r
484\r
485 if (ownerCtContext && ownerCtContext.isTopLevel) {\r
486 // See which properties we are supposed to animate to their new state.\r
487 // If there are any, queue ourself to be animated by the owning Context\r
488 me.animatePolicy = target.ownerLayout.getAnimatePolicy(me);\r
489 } else if (!ownerCtContext && target.isCollapsingOrExpanding && target.animCollapse) {\r
490 // Collapsing/expnding a top level Panel with animation. We need to fabricate\r
491 // an animatePolicy depending on which dimension the collapse is using,\r
492 // isCollapsingOrExpanding is set during the collapse/expand process.\r
493 me.animatePolicy = target.componentLayout.getAnimatePolicy(me);\r
494 }\r
495\r
496 if (me.animatePolicy) {\r
497 me.context.queueAnimation(me);\r
498 }\r
499 },\r
500\r
501 /**\r
502 * Adds a block.\r
503 * \r
504 * @param {String} name The name of the block list ('blocks' or 'domBlocks').\r
505 * @param {Ext.layout.Layout} layout The layout that is blocked.\r
506 * @param {String} propName The property name that blocked the layout (e.g., 'width').\r
507 * @private\r
508 */\r
509 addBlock: function (name, layout, propName) {\r
510 var me = this,\r
511 collection = me[name] || (me[name] = {}),\r
512 blockedLayouts = collection[propName] || (collection[propName] = {});\r
513\r
514 if (!blockedLayouts[layout.id]) {\r
515 blockedLayouts[layout.id] = layout;\r
516 ++layout.blockCount;\r
517 ++me.context.blockCount;\r
518 }\r
519 },\r
520\r
521 addBoxChild: function (boxChildItem) {\r
522 var me = this,\r
523 children,\r
524 widthModel = boxChildItem.widthModel;\r
525\r
526 boxChildItem.boxParent = this;\r
527\r
528 // Children that are widthModel.auto (regardless of heightModel) that measure the\r
529 // DOM (by virtue of hasRawContent), need to wait for their "box parent" to be sized.\r
530 // If they measure too early, they will be wrong results. In the widthModel.shrinkWrap\r
531 // case, the boxParent "crushes" the child. In the case of widthModel.natural, the\r
532 // boxParent's width is likely a key part of the child's width (e.g., "50%" or just\r
533 // normal block-level behavior of 100% width)\r
534 boxChildItem.measuresBox = widthModel.shrinkWrap ? boxChildItem.hasRawContent : widthModel.natural;\r
535\r
536 if (boxChildItem.measuresBox) {\r
537 children = me.boxChildren;\r
538\r
539 if (children) {\r
540 children.push(boxChildItem);\r
541 } else {\r
542 me.boxChildren = [ boxChildItem ];\r
543 }\r
544 }\r
545 },\r
546\r
547 /**\r
548 * Adds x and y values from a props object to a styles object as "left" and "top" values.\r
549 * Overridden to add the x property as "right" in rtl mode.\r
550 * @property {Object} styles A styles object for an Element\r
551 * @property {Object} props A ContextItem props object\r
552 * @return {Number} count The number of styles that were set.\r
553 * @private\r
554 */\r
555 addPositionStyles: function(styles, props) {\r
556 var x = props.x,\r
557 y = props.y,\r
558 count = 0;\r
559\r
560 if (x !== undefined) {\r
561 styles.left = x + 'px';\r
562 ++count;\r
563 }\r
564 if (y !== undefined) {\r
565 styles.top = y + 'px';\r
566 ++count;\r
567 }\r
568 return count;\r
569 },\r
570\r
571 /**\r
572 * Adds a trigger.\r
573 * \r
574 * @param {String} propName The property name that triggers the layout (e.g., 'width').\r
575 * @param {Boolean} inDom True if the trigger list is `domTriggers`, false if `triggers`.\r
576 * @private\r
577 */\r
578 addTrigger: function (propName, inDom) {\r
579 var me = this,\r
580 name = inDom ? 'domTriggers' : 'triggers',\r
581 collection = me[name] || (me[name] = {}),\r
582 context = me.context,\r
583 layout = context.currentLayout,\r
584 triggers = collection[propName] || (collection[propName] = {});\r
585\r
586 if (!triggers[layout.id]) {\r
587 triggers[layout.id] = layout;\r
588 ++layout.triggerCount;\r
589\r
590 triggers = context.triggers[inDom ? 'dom' : 'data'];\r
591 (triggers[layout.id] || (triggers[layout.id] = [])).push({\r
592 item: this,\r
593 prop: propName\r
594 });\r
595\r
596 if (me.props[propName] !== undefined) {\r
597 if (!inDom || !(me.dirty && (propName in me.dirty))) {\r
598 ++layout.firedTriggers;\r
599 }\r
600 }\r
601 }\r
602 },\r
603\r
604 boxChildMeasured: function () {\r
605 var me = this,\r
606 state = me.state,\r
607 count = (state.boxesMeasured = (state.boxesMeasured || 0) + 1);\r
608\r
609 if (count === me.boxChildren.length) {\r
610 // all of our children have measured themselves, so we can clear the width\r
611 // and resume layouts for this component...\r
612 state.clearBoxWidth = 1;\r
613 ++me.context.progressCount;\r
614 me.markDirty();\r
615 }\r
616 },\r
617\r
618 borderNames: [ 'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'],\r
619 marginNames: [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ],\r
620 paddingNames: [ 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' ],\r
621 trblNames: [ 'top', 'right', 'bottom', 'left' ],\r
622\r
623 cacheMissHandlers: {\r
624 borderInfo: function (me) {\r
625 var info = me.getStyles(me.borderNames, me.trblNames);\r
626\r
627 info.width = info.left + info.right;\r
628 info.height = info.top + info.bottom;\r
629\r
630 return info;\r
631 },\r
632\r
633 marginInfo: function (me) {\r
634 var info = me.getStyles(me.marginNames, me.trblNames);\r
635\r
636 info.width = info.left + info.right;\r
637 info.height = info.top + info.bottom;\r
638\r
639 return info;\r
640 },\r
641\r
642 paddingInfo: function (me) {\r
643 // if this context item's target is a framed component the padding is on the frameBody, not on the main el\r
644 var item = me.frameBodyContext || me,\r
645 info = item.getStyles(me.paddingNames, me.trblNames);\r
646\r
647 info.width = info.left + info.right;\r
648 info.height = info.top + info.bottom;\r
649\r
650 return info;\r
651 }\r
652 },\r
653\r
654 checkCache: function (entry) {\r
655 return this.cacheMissHandlers[entry](this);\r
656 },\r
657\r
658 clearAllBlocks: function (name) {\r
659 var collection = this[name],\r
660 propName;\r
661\r
662 if (collection) {\r
663 for (propName in collection) {\r
664 this.clearBlocks(name, propName);\r
665 }\r
666 }\r
667 },\r
668\r
669 /**\r
670 * Removes any blocks on a property in the specified set. Any layouts that were blocked\r
671 * by this property and are not still blocked (by other properties) will be rescheduled.\r
672 * \r
673 * @param {String} name The name of the block list ('blocks' or 'domBlocks').\r
674 * @param {String} propName The property name that blocked the layout (e.g., 'width').\r
675 * @private\r
676 */\r
677 clearBlocks: function (name, propName) {\r
678 var collection = this[name],\r
679 blockedLayouts = collection && collection[propName],\r
680 context, layout, layoutId;\r
681\r
682 if (blockedLayouts) {\r
683 delete collection[propName];\r
684\r
685 context = this.context;\r
686\r
687 for (layoutId in blockedLayouts) {\r
688 layout = blockedLayouts[layoutId];\r
689\r
690 --context.blockCount;\r
691 if (! --layout.blockCount && !layout.pending && !layout.done) {\r
692 context.queueLayout(layout);\r
693 }\r
694 }\r
695 }\r
696 },\r
697\r
698 /**\r
699 * Registers a layout in the block list for the given property. Once the property is\r
700 * set in the {@link Ext.layout.Context}, the layout is unblocked.\r
701 * \r
702 * @param {Ext.layout.Layout} layout\r
703 * @param {String} propName The property name that blocked the layout (e.g., 'width').\r
704 */\r
705 block: function (layout, propName) {\r
706 this.addBlock('blocks', layout, propName);\r
707 },\r
708\r
709 /**\r
710 * Registers a layout in the DOM block list for the given property. Once the property\r
711 * flushed to the DOM by the {@link Ext.layout.Context}, the layout is unblocked.\r
712 * \r
713 * @param {Ext.layout.Layout} layout\r
714 * @param {String} propName The property name that blocked the layout (e.g., 'width').\r
715 */\r
716 domBlock: function (layout, propName) {\r
717 this.addBlock('domBlocks', layout, propName);\r
718 },\r
719\r
720 /**\r
721 * Reschedules any layouts associated with a given trigger.\r
722 * \r
723 * @param {String} name The name of the trigger list ('triggers' or 'domTriggers').\r
724 * @param {String} propName The property name that triggers the layout (e.g., 'width').\r
725 * @private\r
726 */\r
727 fireTriggers: function (name, propName) {\r
728 var collection = this[name],\r
729 triggers = collection && collection[propName],\r
730 context = this.context,\r
731 layout, layoutId;\r
732\r
733 if (triggers) {\r
734 for (layoutId in triggers) {\r
735 layout = triggers[layoutId];\r
736 ++layout.firedTriggers;\r
737 if (!layout.done && !layout.blockCount && !layout.pending) {\r
738 context.queueLayout(layout);\r
739 }\r
740 }\r
741 }\r
742 },\r
743\r
744 /**\r
745 * Flushes any updates in the dirty collection to the DOM. This is only called if there\r
746 * are dirty entries because this object is only added to the flushQueue of the\r
747 * {@link Ext.layout.Context} when entries become dirty.\r
748 */\r
749 flush: function () {\r
750 var me = this,\r
751 dirty = me.dirty,\r
752 state = me.state,\r
753 targetEl = me.el;\r
754\r
755 me.dirtyCount = 0;\r
756\r
757 // Set any queued DOM attributes\r
758 if ('attributes' in me) {\r
759 targetEl.set(me.attributes);\r
760 delete me.attributes;\r
761 }\r
762\r
763 // Set any queued DOM HTML content\r
764 if ('innerHTML' in me) {\r
765 targetEl.innerHTML = me.innerHTML;\r
766 delete me.innerHTML;\r
767 }\r
768\r
769 if (state && state.clearBoxWidth) {\r
770 state.clearBoxWidth = 0;\r
771 me.el.setStyle('width', null);\r
772\r
773 if (! --state.blocks) {\r
774 me.context.queueItemLayouts(me);\r
775 }\r
776 }\r
777\r
778 if (dirty) {\r
779 delete me.dirty;\r
780 me.writeProps(dirty, true);\r
781 }\r
782 },\r
783\r
784 /**\r
785 * @private\r
786 */\r
787 flushAnimations: function() {\r
788 var me = this,\r
789 animateFrom = me.previousSize,\r
790 target, targetAnim, duration, animateProps, anim,\r
791 changeCount, j, propsLen, propName, oldValue, newValue;\r
792\r
793 // Only animate if the Component has been previously layed out: first layout should not animate\r
794 if (animateFrom) {\r
795 target = me.target;\r
796 targetAnim = target.getAnimationProps();\r
797 duration = targetAnim.duration;\r
798 animateProps = Ext.Object.getKeys(me.animatePolicy);\r
799\r
800 // Create an animation block using the targetAnim configuration to provide defaults.\r
801 // They may want custom duration, or easing, or listeners.\r
802 anim = Ext.apply({}, {\r
803 from: {},\r
804 to: {},\r
805 duration: duration || Ext.fx.Anim.prototype.duration\r
806 }, targetAnim);\r
807\r
808 for (changeCount = 0, j = 0, propsLen = animateProps.length; j < propsLen; j++) {\r
809 propName = animateProps[j];\r
810 oldValue = animateFrom[propName];\r
811 newValue = me.peek(propName);\r
812 if (oldValue !== newValue) {\r
813 propName = me.translateProps[propName]||propName;\r
814 anim.from[propName] = oldValue;\r
815 anim.to[propName] = newValue;\r
816 ++changeCount;\r
817 }\r
818 }\r
819\r
820 // If any values have changed, kick off animation from the cached old values to the new values\r
821 if (changeCount) {\r
822 // It'a Panel being collapsed. rollback, and then fix the class name string\r
823 if (me.isCollapsingOrExpanding === 1) {\r
824 target.componentLayout.undoLayout(me);\r
825 }\r
826\r
827 // Otherwise, undo just the animated properties so the animation can proceed from the old layout.\r
828 else {\r
829 me.writeProps(anim.from);\r
830 }\r
831 me.el.animate(anim);\r
832 anim = Ext.fx.Manager.getFxQueue(me.el.id)[0];\r
833 target.$layoutAnim = anim;\r
834\r
835 anim.on({\r
836 afteranimate: function() {\r
837 delete target.$layoutAnim;\r
838 \r
839 // afteranimate can fire when the target is being destroyed\r
840 // and the animation queue is being stopped.\r
841 if (target.destroying || target.destroyed) {\r
842 return;\r
843 }\r
844 \r
845 if (me.isCollapsingOrExpanding === 1) {\r
846 target.componentLayout.redoLayout(me);\r
847 target.afterCollapse(true);\r
848 } else if (me.isCollapsingOrExpanding === 2) {\r
849 target.afterExpand(true);\r
850 }\r
851\r
852 if (target.hasListeners.afterlayoutanimation) {\r
853 target.fireEvent('afterlayoutanimation', target);\r
854 }\r
855 }\r
856 });\r
857 }\r
858 }\r
859 },\r
860\r
861 /**\r
862 * Gets the border information for the element as an object with left, top, right and\r
863 * bottom properties holding border size in pixels. This object is only read from the\r
864 * DOM on first request and is cached.\r
865 * @return {Object}\r
866 */\r
867 getBorderInfo: function () {\r
868 var me = this,\r
869 info = me.borderInfo;\r
870\r
871 if (!info) {\r
872 me.borderInfo = info = me.checkCache('borderInfo');\r
873 }\r
874\r
875 return info;\r
876 },\r
877\r
878 /**\r
879 * @member Ext.layout.ContextItem\r
880 * Returns the context item for an owned element. This should only be called on a\r
881 * component's item. The list of child items is used to manage invalidating calculated\r
882 * results.\r
883 * @param {String/Ext.dom.Element} nameOrEl The element or the name of an owned element\r
884 * @param {Ext.layout.container.Container/Ext.Component} [owner] The owner of the\r
885 * named element if the passed "nameOrEl" parameter is a String. Defaults to this\r
886 * ContextItem's "target" property. For more details on owned elements see\r
887 * {@link Ext.Component#cfg-childEls childEls} and\r
888 * {@link Ext.Component#renderSelectors renderSelectors}\r
889 * @return {Ext.layout.ContextItem}\r
890 */\r
891 getEl: function (nameOrEl, owner) {\r
892 var me = this,\r
893 src, el, elContext;\r
894\r
895 if (nameOrEl) {\r
896 if (nameOrEl.dom) {\r
897 el = nameOrEl;\r
898 } else {\r
899 src = me.target;\r
900 if (owner) {\r
901 src = owner;\r
902 }\r
903\r
904 el = src[nameOrEl];\r
905 if (typeof el === 'function') { // ex 'getTarget'\r
906 el = el.call(src);\r
907 if (el === me.el) {\r
908 return this; // comp.getTarget() often returns comp.el\r
909 }\r
910 }\r
911 }\r
912\r
913 if (el) {\r
914 elContext = me.context.getEl(me, el);\r
915 }\r
916 }\r
917\r
918 return elContext || null;\r
919 },\r
920\r
921 /**\r
922 * Gets the "frame" information for the element as an object with left, top, right and\r
923 * bottom properties holding border+framing size in pixels. This object is calculated\r
924 * on first request and is cached.\r
925 * @return {Object}\r
926 */\r
927 getFrameInfo: function () {\r
928 var me = this,\r
929 info = me.frameInfo,\r
930 framing, border;\r
931\r
932 if (!info) {\r
933 framing = me.framing;\r
934 border = me.getBorderInfo();\r
935\r
936 me.frameInfo = info = \r
937 framing ? {\r
938 top : framing.top + border.top,\r
939 right : framing.right + border.right,\r
940 bottom: framing.bottom + border.bottom,\r
941 left : framing.left + border.left,\r
942 width : framing.width + border.width,\r
943 height: framing.height + border.height\r
944 } : border;\r
945 }\r
946\r
947 return info;\r
948 },\r
949\r
950 /**\r
951 * Gets the margin information for the element as an object with left, top, right and\r
952 * bottom properties holding margin size in pixels. This object is only read from the\r
953 * DOM on first request and is cached.\r
954 * @return {Object}\r
955 */\r
956 getMarginInfo: function () {\r
957 var me = this,\r
958 info = me.marginInfo,\r
959 comp, manageMargins, ownerLayout, ownerLayoutId;\r
960\r
961 if (!info) {\r
962 if (!me.wrapsComponent) {\r
963 info = me.checkCache('marginInfo');\r
964 } else {\r
965 comp = me.target;\r
966 ownerLayout = comp.ownerLayout;\r
967 ownerLayoutId = ownerLayout ? ownerLayout.id : null;\r
968 manageMargins = ownerLayout && ownerLayout.manageMargins;\r
969\r
970 // TODO: stop caching margin$ on the component EXTJS-13359\r
971 info = comp.margin$;\r
972 if (info && info.ownerId !== ownerLayoutId) {\r
973 // got one but from the wrong owner\r
974 info = null;\r
975 }\r
976\r
977 if (!info) { // if (no cache)\r
978 // CSS margins are only checked if there isn't a margin property on the component\r
979 info = me.parseMargins(comp, comp.margin) || me.checkCache('marginInfo');\r
980\r
981 if (manageMargins) {\r
982 // TODO: Stop zeroing out the margins EXTJS-13359\r
983 me.setProp('margin-top', 0);\r
984 me.setProp('margin-right', 0);\r
985 me.setProp('margin-bottom', 0);\r
986 me.setProp('margin-left', 0);\r
987 }\r
988\r
989 // cache the layout margins and tag them with the layout id:\r
990 info.ownerId = ownerLayoutId;\r
991 comp.margin$ = info;\r
992 }\r
993\r
994 info.width = info.left + info.right;\r
995 info.height = info.top + info.bottom;\r
996 }\r
997\r
998 me.marginInfo = info;\r
999 }\r
1000\r
1001 return info;\r
1002 },\r
1003\r
1004 /**\r
1005 * clears the margin cache so that marginInfo get re-read from the dom on the next call to getMarginInfo()\r
1006 * This is needed in some special cases where the margins have changed since the last layout, making the cached\r
1007 * values invalid. For example collapsed window headers have different margin than expanded ones.\r
1008 */\r
1009 clearMarginCache: function() {\r
1010 delete this.marginInfo;\r
1011 delete this.target.margin$;\r
1012 },\r
1013\r
1014 /**\r
1015 * Gets the padding information for the element as an object with left, top, right and\r
1016 * bottom properties holding padding size in pixels. This object is only read from the\r
1017 * DOM on first request and is cached.\r
1018 * @return {Object}\r
1019 */\r
1020 getPaddingInfo: function () {\r
1021 var me = this,\r
1022 info = me.paddingInfo;\r
1023\r
1024 if (!info) {\r
1025 me.paddingInfo = info = me.checkCache('paddingInfo');\r
1026 }\r
1027\r
1028 return info;\r
1029 },\r
1030\r
1031 /**\r
1032 * Gets a property of this object. Also tracks the current layout as dependent on this\r
1033 * property so that changes to it will trigger the layout to be recalculated.\r
1034 * @param {String} propName The property name that blocked the layout (e.g., 'width').\r
1035 * @return {Object} The property value or undefined if not yet set.\r
1036 */\r
1037 getProp: function (propName) {\r
1038 var me = this,\r
1039 result = me.props[propName];\r
1040\r
1041 me.addTrigger(propName);\r
1042 return result;\r
1043 },\r
1044\r
1045 /**\r
1046 * Gets a property of this object if it is correct in the DOM. Also tracks the current\r
1047 * layout as dependent on this property so that DOM writes of it will trigger the\r
1048 * layout to be recalculated.\r
1049 * @param {String} propName The property name (e.g., 'width').\r
1050 * @return {Object} The property value or undefined if not yet set or is dirty.\r
1051 */\r
1052 getDomProp: function (propName) {\r
1053 var me = this,\r
1054 result = (me.dirty && (propName in me.dirty)) ? undefined : me.props[propName];\r
1055\r
1056 me.addTrigger(propName, true);\r
1057 return result;\r
1058 },\r
1059\r
1060 /**\r
1061 * Returns a style for this item. Each style is read from the DOM only once on first\r
1062 * request and is then cached. If the value is an integer, it is parsed automatically\r
1063 * (so '5px' is not returned, but rather 5).\r
1064 *\r
1065 * @param {String} styleName The CSS style name.\r
1066 * @return {Object} The value of the DOM style (parsed as necessary).\r
1067 */\r
1068 getStyle: function (styleName) {\r
1069 var me = this,\r
1070 styles = me.styles,\r
1071 info, value;\r
1072\r
1073 if (styleName in styles) {\r
1074 value = styles[styleName];\r
1075 } else {\r
1076 info = me.styleInfo[styleName];\r
1077 value = me.el.getStyle(styleName);\r
1078\r
1079 if (info && info.parseInt) {\r
1080 value = parseInt(value, 10) || 0;\r
1081 }\r
1082\r
1083 styles[styleName] = value;\r
1084 }\r
1085\r
1086 return value;\r
1087 },\r
1088\r
1089 /**\r
1090 * Returns styles for this item. Each style is read from the DOM only once on first\r
1091 * request and is then cached. If the value is an integer, it is parsed automatically\r
1092 * (so '5px' is not returned, but rather 5).\r
1093 *\r
1094 * @param {String[]} styleNames The CSS style names.\r
1095 * @param {String[]} [altNames] The alternate names for the returned styles. If given,\r
1096 * these names must correspond one-for-one to the `styleNames`.\r
1097 * @return {Object} The values of the DOM styles (parsed as necessary).\r
1098 */\r
1099 getStyles: function (styleNames, altNames) {\r
1100 var me = this,\r
1101 styleCache = me.styles,\r
1102 values = {},\r
1103 hits = 0,\r
1104 n = styleNames.length,\r
1105 i, missing, missingAltNames, name, info, styleInfo, styles, value;\r
1106\r
1107 altNames = altNames || styleNames;\r
1108\r
1109 // We are optimizing this for all hits or all misses. If we hit on all styles, we\r
1110 // don't create a missing[]. If we miss on all styles, we also don't create one.\r
1111 for (i = 0; i < n; ++i) {\r
1112 name = styleNames[i];\r
1113\r
1114 if (name in styleCache) {\r
1115 values[altNames[i]] = styleCache[name];\r
1116 ++hits;\r
1117\r
1118 if (i && hits === 1) { // if (first hit was after some misses)\r
1119 missing = styleNames.slice(0, i);\r
1120 missingAltNames = altNames.slice(0, i);\r
1121 }\r
1122 } else if (hits) {\r
1123 (missing || (missing = [])).push(name);\r
1124 (missingAltNames || (missingAltNames = [])).push(altNames[i]);\r
1125 }\r
1126 }\r
1127\r
1128 if (hits < n) {\r
1129 missing = missing || styleNames;\r
1130 missingAltNames = missingAltNames || altNames;\r
1131 styleInfo = me.styleInfo;\r
1132\r
1133 styles = me.el.getStyle(missing);\r
1134\r
1135 for (i = missing.length; i--; ) {\r
1136 name = missing[i];\r
1137 info = styleInfo[name];\r
1138 value = styles[name];\r
1139\r
1140 if (info && info.parseInt) {\r
1141 value = parseInt(value, 10) || 0;\r
1142 }\r
1143\r
1144 values[missingAltNames[i]] = value;\r
1145 styleCache[name] = value;\r
1146 }\r
1147 }\r
1148\r
1149 return values;\r
1150 },\r
1151\r
1152 /**\r
1153 * Returns true if the given property has been set. This is equivalent to calling\r
1154 * {@link #getProp} and not getting an undefined result. In particular, this call\r
1155 * registers the current layout to be triggered by changes to this property.\r
1156 * \r
1157 * @param {String} propName The property name (e.g., 'width').\r
1158 * @return {Boolean}\r
1159 */\r
1160 hasProp: function (propName) {\r
1161 return this.getProp(propName) != null;\r
1162 },\r
1163\r
1164 /**\r
1165 * Returns true if the given property is correct in the DOM. This is equivalent to\r
1166 * calling {@link #getDomProp} and not getting an undefined result. In particular,\r
1167 * this call registers the current layout to be triggered by flushes of this property.\r
1168 * \r
1169 * @param {String} propName The property name (e.g., 'width').\r
1170 * @return {Boolean}\r
1171 */\r
1172 hasDomProp: function (propName) {\r
1173 return this.getDomProp(propName) != null;\r
1174 },\r
1175\r
1176 /**\r
1177 * Invalidates the component associated with this item. The layouts for this component\r
1178 * and all of its contained items will be re-run after first clearing any computed\r
1179 * values.\r
1180 * \r
1181 * If state needs to be carried forward beyond the invalidation, the `options` parameter\r
1182 * can be used.\r
1183 *\r
1184 * @param {Object} options An object describing how to handle the invalidation.\r
1185 * @param {Object} options.state An object to {@link Ext#apply} to the {@link #state}\r
1186 * of this item after invalidation clears all other properties.\r
1187 * @param {Function} options.before A function to call after the context data is cleared\r
1188 * and before the {@link Ext.layout.Layout#beginLayoutCycle} methods are called.\r
1189 * @param {Ext.layout.ContextItem} options.before.item This ContextItem.\r
1190 * @param {Object} options.before.options The options object passed to {@link #invalidate}.\r
1191 * @param {Function} options.after A function to call after the context data is cleared\r
1192 * and after the {@link Ext.layout.Layout#beginLayoutCycle} methods are called.\r
1193 * @param {Ext.layout.ContextItem} options.after.item This ContextItem.\r
1194 * @param {Object} options.after.options The options object passed to {@link #invalidate}.\r
1195 * @param {Object} options.scope The scope to use when calling the callback functions.\r
1196 */\r
1197 invalidate: function (options) {\r
1198 this.context.queueInvalidate(this, options);\r
1199 },\r
1200\r
1201 markDirty: function () {\r
1202 if (++this.dirtyCount === 1) {\r
1203 // our first dirty property... queue us for flush\r
1204 this.context.queueFlush(this);\r
1205 }\r
1206 },\r
1207\r
1208 onBoxMeasured: function () {\r
1209 var boxParent = this.boxParent,\r
1210 state = this.state;\r
1211\r
1212 if (boxParent && boxParent.widthModel.shrinkWrap && !state.boxMeasured && this.measuresBox) {\r
1213 // since an autoWidth boxParent is holding a width on itself to allow each\r
1214 // child to measure\r
1215 state.boxMeasured = 1; // best to only call once per child\r
1216 boxParent.boxChildMeasured();\r
1217 }\r
1218 },\r
1219\r
1220 parseMargins: function (comp, margins) {\r
1221 if (margins === true) {\r
1222 margins = 5;\r
1223 }\r
1224\r
1225 var type = typeof margins,\r
1226 ret;\r
1227\r
1228 if (type === 'string' || type === 'number') {\r
1229 ret = comp.parseBox(margins);\r
1230 } else if (margins) {\r
1231 ret = { top: 0, right: 0, bottom: 0, left: 0 }; // base defaults\r
1232\r
1233 if (margins) {\r
1234 margins = Ext.apply(ret, comp.parseBox(margins)); // + config\r
1235 }\r
1236 }\r
1237\r
1238 return ret;\r
1239 },\r
1240\r
1241 peek: function (propName) {\r
1242 return this.props[propName];\r
1243 },\r
1244\r
1245 recalculateSizeModel: function() {\r
1246 // See the constructor, this logic is very similar. Not broken out into\r
1247 // a separate method for performance reasons\r
1248 var me = this,\r
1249 target = me.target,\r
1250 componentLayout = target.componentLayout,\r
1251 ownerCtContext = me.ownerCtContext,\r
1252 oldContext = componentLayout.ownerContext,\r
1253 sizeModel;\r
1254\r
1255 // If the componentLayout has an ownerContext, it will just use the sizeModel that\r
1256 // exists on the context. Instead, force it to recalculate\r
1257 componentLayout.ownerContext = null;\r
1258\r
1259 me.sizeModel = sizeModel = target.getSizeModel(ownerCtContext &&\r
1260 ownerCtContext.widthModel.pairsByHeightOrdinal[ownerCtContext.heightModel.ordinal]);\r
1261\r
1262 me.widthModel = sizeModel.width;\r
1263 me.heightModel = sizeModel.height;\r
1264\r
1265 if (oldContext) {\r
1266 componentLayout.ownerContext = me;\r
1267 }\r
1268 },\r
1269\r
1270 /**\r
1271 * Recovers a property value from the last computation and restores its value and\r
1272 * dirty state.\r
1273 * \r
1274 * @param {String} propName The name of the property to recover.\r
1275 * @param {Object} oldProps The old "props" object from which to recover values.\r
1276 * @param {Object} oldDirty The old "dirty" object from which to recover state.\r
1277 */\r
1278 recoverProp: function (propName, oldProps, oldDirty) {\r
1279 var me = this,\r
1280 props = me.props,\r
1281 dirty;\r
1282\r
1283 if (propName in oldProps) {\r
1284 props[propName] = oldProps[propName];\r
1285\r
1286 if (oldDirty && propName in oldDirty) {\r
1287 dirty = me.dirty || (me.dirty = {});\r
1288 dirty[propName] = oldDirty[propName];\r
1289 }\r
1290 }\r
1291 },\r
1292\r
1293 redo: function(deep) {\r
1294 var me = this,\r
1295 items, len, i;\r
1296\r
1297 me.revertProps(me.props);\r
1298\r
1299 if (deep && me.wrapsComponent) {\r
1300 // Rollback the state of child Components\r
1301 if (me.childItems) {\r
1302 for (i = 0, items = me.childItems, len = items.length; i < len; i++) {\r
1303 items[i].redo(deep);\r
1304 }\r
1305 }\r
1306\r
1307 // Rollback the state of child Elements\r
1308 for (i = 0, items = me.children, len = items.length; i < len; i++) {\r
1309 items[i].redo();\r
1310 }\r
1311 }\r
1312 },\r
1313\r
1314 /**\r
1315 * Removes a cached ContextItem that was created using {@link #getEl}. It may be\r
1316 * necessary to call this method if the dom reference for owned element changes so \r
1317 * that {@link #getEl} can be called again to reinitialize the ContextItem with the\r
1318 * new element.\r
1319 * @param {String/Ext.dom.Element} nameOrEl The element or the name of an owned element\r
1320 * @param {Ext.layout.container.Container/Ext.Component} [owner] The owner of the\r
1321 * named element if the passed "nameOrEl" parameter is a String. Defaults to this\r
1322 * ContextItem's "target" property.\r
1323 */\r
1324 removeEl: function(nameOrEl, owner) {\r
1325 var me = this,\r
1326 src, el;\r
1327\r
1328 if (nameOrEl) {\r
1329 if (nameOrEl.dom) {\r
1330 el = nameOrEl;\r
1331 } else {\r
1332 src = me.target;\r
1333 if (owner) {\r
1334 src = owner;\r
1335 }\r
1336\r
1337 el = src[nameOrEl];\r
1338 if (typeof el === 'function') { // ex 'getTarget'\r
1339 el = el.call(src);\r
1340 if (el === me.el) {\r
1341 return this; // comp.getTarget() often returns comp.el\r
1342 }\r
1343 }\r
1344 }\r
1345\r
1346 if (el) {\r
1347 me.context.removeEl(el, me);\r
1348 }\r
1349 }\r
1350 },\r
1351\r
1352 revertProps: function (props) {\r
1353 var name,\r
1354 flushed = this.flushedProps,\r
1355 reverted = {};\r
1356\r
1357 for (name in props) {\r
1358 if (flushed.hasOwnProperty(name)) {\r
1359 reverted[name] = props[name];\r
1360 }\r
1361 }\r
1362\r
1363 this.writeProps(reverted);\r
1364 },\r
1365\r
1366 /**\r
1367 * Queue the setting of a DOM attribute on this ContextItem's target when next flushed.\r
1368 */\r
1369 setAttribute: function(name, value) {\r
1370 var me = this;\r
1371 if (!me.attributes) {\r
1372 me.attributes = {};\r
1373 }\r
1374 me.attributes[name] = value;\r
1375 me.markDirty();\r
1376 },\r
1377\r
1378 setBox: function (box) {\r
1379 var me = this;\r
1380\r
1381 if ('left' in box) {\r
1382 me.setProp('x', box.left);\r
1383 }\r
1384 if ('top' in box) {\r
1385 me.setProp('y', box.top);\r
1386 }\r
1387\r
1388 // if sizeModel says we should not be setting these, the appropriate calls will be\r
1389 // null operations... otherwise, we must set these values, so what we have in box\r
1390 // is what we go with (undefined, NaN and no change are handled at a lower level):\r
1391 me.setSize(box.width, box.height);\r
1392 },\r
1393\r
1394 /**\r
1395 * Sets the contentHeight property. If the component uses raw content, then only the\r
1396 * measured height is acceptable.\r
1397 *\r
1398 * Calculated values can sometimes be NaN or undefined, which generally mean the\r
1399 * calculation is not done. To indicate that such as value was passed, 0 is returned.\r
1400 * Otherwise, 1 is returned.\r
1401 *\r
1402 * If the caller is not measuring (i.e., they are calculating) and the component has raw\r
1403 * content, 1 is returned indicating that the caller is done.\r
1404 */\r
1405 setContentHeight: function (height, measured) {\r
1406 if (!measured && this.hasRawContent) {\r
1407 return 1;\r
1408 }\r
1409\r
1410 return this.setProp('contentHeight', height);\r
1411 },\r
1412\r
1413 /**\r
1414 * Sets the contentWidth property. If the component uses raw content, then only the\r
1415 * measured width is acceptable.\r
1416 * \r
1417 * Calculated values can sometimes be NaN or undefined, which generally means that the\r
1418 * calculation is not done. To indicate that such as value was passed, 0 is returned.\r
1419 * Otherwise, 1 is returned.\r
1420 *\r
1421 * If the caller is not measuring (i.e., they are calculating) and the component has raw\r
1422 * content, 1 is returned indicating that the caller is done.\r
1423 */\r
1424 setContentWidth: function (width, measured) {\r
1425 if (!measured && this.hasRawContent) {\r
1426 return 1;\r
1427 }\r
1428\r
1429 return this.setProp('contentWidth', width);\r
1430 },\r
1431\r
1432 /**\r
1433 * Sets the contentWidth and contentHeight properties. If the component uses raw content,\r
1434 * then only the measured values are acceptable.\r
1435 * \r
1436 * Calculated values can sometimes be NaN or undefined, which generally means that the\r
1437 * calculation is not done. To indicate that either passed value was such a value, false\r
1438 * returned. Otherwise, true is returned.\r
1439 *\r
1440 * If the caller is not measuring (i.e., they are calculating) and the component has raw\r
1441 * content, true is returned indicating that the caller is done.\r
1442 */\r
1443 setContentSize: function (width, height, measured) {\r
1444 return this.setContentWidth(width, measured) +\r
1445 this.setContentHeight(height, measured) === 2;\r
1446 },\r
1447\r
1448 /**\r
1449 * Sets a property value. This will unblock and/or trigger dependent layouts if the\r
1450 * property value is being changed. Values of NaN and undefined are not accepted by\r
1451 * this method.\r
1452 * \r
1453 * @param {String} propName The property name (e.g., 'width').\r
1454 * @param {Object} value The new value of the property.\r
1455 * @param {Boolean} dirty Optionally specifies if the value is currently in the DOM\r
1456 * (default is `true` which indicates the value is not in the DOM and must be flushed\r
1457 * at some point).\r
1458 * @return {Number} 1 if this call specified the property value, 0 if not.\r
1459 */\r
1460 setProp: function (propName, value, dirty) {\r
1461 var me = this,\r
1462 valueType = typeof value,\r
1463 info;\r
1464\r
1465 if (valueType === 'undefined' || (valueType === 'number' && isNaN(value))) {\r
1466 return 0;\r
1467 }\r
1468 if (me.props[propName] === value) {\r
1469 return 1;\r
1470 }\r
1471\r
1472 me.props[propName] = value;\r
1473 ++me.context.progressCount;\r
1474\r
1475 if (dirty === false) {\r
1476 // if the prop is equivalent to what is in the DOM (we won't be writing it),\r
1477 // we need to clear hard blocks (domBlocks) on that property.\r
1478 me.fireTriggers('domTriggers', propName);\r
1479 me.clearBlocks('domBlocks', propName);\r
1480 } else {\r
1481 info = me.styleInfo[propName];\r
1482 if (info) {\r
1483 if (!me.dirty) {\r
1484 me.dirty = {};\r
1485 }\r
1486\r
1487 me.dirty[propName] = value;\r
1488 me.markDirty();\r
1489 }\r
1490 }\r
1491\r
1492 // we always clear soft blocks on set\r
1493 me.fireTriggers('triggers', propName);\r
1494 me.clearBlocks('blocks', propName);\r
1495 return 1;\r
1496 },\r
1497\r
1498 /**\r
1499 * Sets the height and constrains the height to min/maxHeight range.\r
1500 * \r
1501 * @param {Number} height The height.\r
1502 * @param {Boolean} [dirty=true] Specifies if the value is currently in the DOM. A\r
1503 * value of `false` indicates that the value is already in the DOM.\r
1504 * @return {Number} The actual height after constraining.\r
1505 */\r
1506 setHeight: function (height, dirty /*, private {Boolean} force */) {\r
1507 var me = this,\r
1508 comp = me.target,\r
1509 ownerCtContext = me.ownerCtContext,\r
1510 frameBody, frameInfo, min, oldHeight, rem;\r
1511\r
1512 if (height < 0) {\r
1513 height = 0;\r
1514 }\r
1515 if (!me.wrapsComponent) {\r
1516 if (!me.setProp('height', height, dirty)) {\r
1517 return NaN;\r
1518 }\r
1519 } else {\r
1520 min = me.collapsedVert ? 0 : (comp.minHeight || 0);\r
1521 height = Ext.Number.constrain(height, min, comp.maxHeight);\r
1522 oldHeight = me.props.height;\r
1523 if (!me.setProp('height', height, dirty)) {\r
1524 return NaN;\r
1525 }\r
1526\r
1527 // if we are a container child, since the height is now known we can decrement\r
1528 // the number of remainingChildDimensions that the ownerCtContext is waiting on.\r
1529 if (ownerCtContext && !me.isComponentChild && isNaN(oldHeight)) {\r
1530 rem = --ownerCtContext.remainingChildDimensions;\r
1531 if (!rem) {\r
1532 // if there are 0 remainingChildDimensions set containerChildrenSizeDone\r
1533 // on the ownerCtContext to indicate that all of its children's dimensions\r
1534 // are known\r
1535 ownerCtContext.setProp('containerChildrenSizeDone', true);\r
1536 }\r
1537 }\r
1538\r
1539 frameBody = me.frameBodyContext;\r
1540 if (frameBody){\r
1541 frameInfo = me.getFrameInfo();\r
1542 frameBody[me.el.vertical ? 'setWidth' : 'setHeight'](height - frameInfo.height, dirty);\r
1543 }\r
1544 }\r
1545\r
1546 return height;\r
1547 },\r
1548\r
1549 /**\r
1550 * Sets the height and constrains the width to min/maxWidth range.\r
1551 * \r
1552 * @param {Number} width The width.\r
1553 * @param {Boolean} [dirty=true] Specifies if the value is currently in the DOM. A\r
1554 * value of `false` indicates that the value is already in the DOM.\r
1555 * @return {Number} The actual width after constraining.\r
1556 */\r
1557 setWidth: function (width, dirty /*, private {Boolean} force */) {\r
1558 var me = this,\r
1559 comp = me.target,\r
1560 ownerCtContext = me.ownerCtContext,\r
1561 frameBody, frameInfo, min, oldWidth, rem;\r
1562\r
1563 if (width < 0) {\r
1564 width = 0;\r
1565 }\r
1566 if (!me.wrapsComponent) {\r
1567 if (!me.setProp('width', width, dirty)) {\r
1568 return NaN;\r
1569 }\r
1570 } else {\r
1571 min = me.collapsedHorz ? 0 : (comp.minWidth || 0);\r
1572 width = Ext.Number.constrain(width, min, comp.maxWidth);\r
1573 oldWidth = me.props.width;\r
1574 if (!me.setProp('width', width, dirty)) {\r
1575 return NaN;\r
1576 }\r
1577\r
1578 // if we are a container child, since the width is now known we can decrement\r
1579 // the number of remainingChildDimensions that the ownerCtContext is waiting on.\r
1580 if (ownerCtContext && !me.isComponentChild && isNaN(oldWidth)) {\r
1581 rem = --ownerCtContext.remainingChildDimensions;\r
1582 if (!rem) {\r
1583 // if there are 0 remainingChildDimensions set containerChildrenSizeDone\r
1584 // on the ownerCtContext to indicate that all of its children's dimensions\r
1585 // are known\r
1586 ownerCtContext.setProp('containerChildrenSizeDone', true);\r
1587 }\r
1588 }\r
1589\r
1590 //if ((frameBody = me.target.frameBody) && (frameBody = me.getEl(frameBody))){\r
1591 frameBody = me.frameBodyContext;\r
1592 if (frameBody) {\r
1593 frameInfo = me.getFrameInfo();\r
1594 frameBody.setWidth(width - frameInfo.width, dirty);\r
1595 }\r
1596\r
1597 /*if (owner.frameBody) {\r
1598 frameContext = ownerContext.frameContext ||\r
1599 (ownerContext.frameContext = ownerContext.getEl('frameBody'));\r
1600 width += (frameContext.paddingInfo || frameContext.getPaddingInfo()).width;\r
1601 }*/\r
1602 }\r
1603\r
1604 return width;\r
1605 },\r
1606\r
1607 setSize: function (width, height, dirty) {\r
1608 this.setWidth(width, dirty);\r
1609 this.setHeight(height, dirty);\r
1610 },\r
1611\r
1612 translateProps: {\r
1613 x: 'left',\r
1614 y: 'top'\r
1615 },\r
1616\r
1617 undo: function(deep) {\r
1618 var me = this,\r
1619 items, len, i;\r
1620\r
1621 me.revertProps(me.lastBox);\r
1622\r
1623 if (deep && me.wrapsComponent) {\r
1624 // Rollback the state of child Components\r
1625 if (me.childItems) {\r
1626 for (i = 0, items = me.childItems, len = items.length; i < len; i++) {\r
1627 items[i].undo(deep);\r
1628 }\r
1629 }\r
1630\r
1631 // Rollback the state of child Elements\r
1632 for (i = 0, items = me.children, len = items.length; i < len; i++) {\r
1633 items[i].undo();\r
1634 }\r
1635 }\r
1636 },\r
1637\r
1638 unsetProp: function (propName) {\r
1639 var dirty = this.dirty;\r
1640\r
1641 delete this.props[propName];\r
1642 if (dirty) {\r
1643 delete dirty[propName];\r
1644 }\r
1645 },\r
1646\r
1647 writeProps: function(dirtyProps, flushing) {\r
1648 if (!(dirtyProps && typeof dirtyProps === 'object')) {\r
1649 //<debug>\r
1650 Ext.Logger.warn('writeProps expected dirtyProps to be an object');\r
1651 //</debug>\r
1652 return;\r
1653 }\r
1654\r
1655 var me = this,\r
1656 el = me.el,\r
1657 styles = {},\r
1658 styleCount = 0, // used as a boolean, the exact count doesn't matter\r
1659 styleInfo = me.styleInfo,\r
1660\r
1661 info,\r
1662 propName,\r
1663 numericValue,\r
1664 width = dirtyProps.width,\r
1665 height = dirtyProps.height,\r
1666 target = me.target,\r
1667 hasWidth, hasHeight, isAbsolute, scrollbarSize, style, targetEl;\r
1668\r
1669 // Process non-style properties:\r
1670 if ('displayed' in dirtyProps) {\r
1671 el.setDisplayed(dirtyProps.displayed);\r
1672 }\r
1673\r
1674 // Unblock any hard blocks (domBlocks) and copy dom styles into 'styles'\r
1675 for (propName in dirtyProps) {\r
1676 if (flushing) {\r
1677 me.fireTriggers('domTriggers', propName);\r
1678 me.clearBlocks('domBlocks', propName);\r
1679 me.flushedProps[propName] = 1;\r
1680 }\r
1681\r
1682 info = styleInfo[propName];\r
1683 if (info && info.dom) {\r
1684 // Numeric dirty values should have their associated suffix added\r
1685 if (info.suffix && (numericValue = parseInt(dirtyProps[propName], 10))) {\r
1686 styles[propName] = numericValue + info.suffix;\r
1687 }\r
1688 // Non-numeric (eg "auto") go in unchanged.\r
1689 else {\r
1690 styles[propName] = dirtyProps[propName];\r
1691 }\r
1692 ++styleCount;\r
1693 }\r
1694 }\r
1695\r
1696 // convert x/y into setPosition (for a component) or left/top styles (for an el)\r
1697 if ('x' in dirtyProps || 'y' in dirtyProps) {\r
1698 if (target.isComponent) {\r
1699 target.setPosition(dirtyProps.x, dirtyProps.y);\r
1700 } else {\r
1701 // we wrap an element, so convert x/y to styles:\r
1702 styleCount += me.addPositionStyles(styles, dirtyProps);\r
1703 }\r
1704 }\r
1705\r
1706 // IE9 subtracts the scrollbar size from the element size when the element\r
1707 // is absolutely positioned and uses box-sizing: border-box. To workaround this\r
1708 // issue we have to add the the scrollbar size.\r
1709 // \r
1710 // See http://social.msdn.microsoft.com/Forums/da-DK/iewebdevelopment/thread/47c5148f-a142-4a99-9542-5f230c78cb3b\r
1711 //\r
1712 if (me.wrapsComponent && Ext.isIE9) {\r
1713 // when we set a width and we have a vertical scrollbar (overflowY), we need\r
1714 // to add the scrollbar width... conversely for the height and overflowX\r
1715 if ((hasWidth = width !== undefined && me.hasOverflowY) ||\r
1716 (hasHeight = height !== undefined && me.hasOverflowX)) {\r
1717 // check that the component is absolute positioned.\r
1718 isAbsolute = me.isAbsolute;\r
1719 if (isAbsolute === undefined) {\r
1720 isAbsolute = false;\r
1721 targetEl = me.target.getTargetEl();\r
1722 style = targetEl.getStyle('position');\r
1723 me.isAbsolute = isAbsolute = (style === 'absolute'); // cache it\r
1724 }\r
1725\r
1726 if (isAbsolute) {\r
1727 scrollbarSize = Ext.getScrollbarSize();\r
1728\r
1729 if (hasWidth) {\r
1730 width = parseInt(width, 10) + scrollbarSize.width;\r
1731 styles.width = width + 'px';\r
1732 ++styleCount;\r
1733 }\r
1734 if (hasHeight) {\r
1735 height = parseInt(height, 10) + scrollbarSize.height;\r
1736 styles.height = height + 'px';\r
1737 ++styleCount;\r
1738 }\r
1739 }\r
1740 }\r
1741 }\r
1742\r
1743 // we make only one call to setStyle to allow it to optimize itself:\r
1744 if (styleCount) {\r
1745 el.setStyle(styles);\r
1746 }\r
1747 },\r
1748\r
1749 //-------------------------------------------------------------------------\r
1750 // Diagnostics\r
1751\r
1752 debugHooks: {\r
1753 $enabled: false, // Disable by default\r
1754\r
1755 addBlock: function (name, layout, propName) {\r
1756 //Ext.log(this.id,'.',propName,' ',name,': ',this.context.getLayoutName(layout));\r
1757 (layout.blockedBy || (layout.blockedBy = {}))[\r
1758 this.id+'.'+propName+(name.substring(0,3)==='dom' ? ':dom' : '')] = 1;\r
1759\r
1760 return this.callParent(arguments);\r
1761 },\r
1762\r
1763 addBoxChild: function (boxChildItem) {\r
1764 var ret = this.callParent(arguments),\r
1765 boxChildren = this.boxChildren,\r
1766 boxParents;\r
1767\r
1768 if (boxChildren && boxChildren.length === 1) {\r
1769 // the boxParent collection is created by the run override found in\r
1770 // Ext.diag.layout.Context, but IE sometimes does not load that override, so\r
1771 // we work around it for now\r
1772 boxParents = this.context.boxParents ||\r
1773 (this.context.boxParents = new Ext.util.MixedCollection());\r
1774 boxParents.add(this);\r
1775 }\r
1776\r
1777 return ret;\r
1778 },\r
1779\r
1780 addTrigger: function (propName, inDom) {\r
1781 var layout = this.context.currentLayout,\r
1782 triggers;\r
1783\r
1784 //Ext.log(this.id,'.',propName,' ',inDom ? ':dom' : '',' ',this.context.getLayoutName(layout));\r
1785 this.callParent(arguments);\r
1786\r
1787 triggers = this.context.triggersByLayoutId;\r
1788 (triggers[layout.id] || (triggers[layout.id] = {}))[\r
1789 this.id+'.'+propName+(inDom ? ':dom' : '')] = {\r
1790 item: this,\r
1791 name: propName\r
1792 };\r
1793 },\r
1794\r
1795 checkAuthority: function (prop) {\r
1796 var me = this,\r
1797 model = me[prop + 'Model'], // not me.sizeModel[prop] since it is immutable\r
1798 layout = me.context.currentLayout,\r
1799 ok,\r
1800 setBy;\r
1801\r
1802 if (layout === me.target.ownerLayout) {\r
1803 // the ownerLayout is only allowed to set calculated dimensions\r
1804 ok = model.calculated;\r
1805 } else if (layout.isComponentLayout) {\r
1806 // the component's componentLayout (normally) is only allowed to set auto or\r
1807 // configured dimensions. The exception is when a component is run w/o its\r
1808 // ownerLayout in the picture (isTopLevel), someone must publish the lastBox\r
1809 // values and that lucky layout is the componentLayout (kinda had to be since\r
1810 // the ownerLayout is not running)\r
1811 ok = me.isTopLevel || model.auto || model.configured;\r
1812 }\r
1813\r
1814 if (!ok) {\r
1815 setBy = me.context.getLayoutName(layout);\r
1816\r
1817 Ext.log(setBy + ' cannot set ' + prop);\r
1818 }\r
1819 },\r
1820\r
1821 clearBlocks: function (name, propName) {\r
1822 var collection = this[name],\r
1823 blockedLayouts = collection && collection[propName],\r
1824 key = this.id + '.' + propName + (name.substring(0,3)==='dom' ? ':dom' : ''),\r
1825 layout, layoutId;\r
1826\r
1827 if (blockedLayouts) {\r
1828 for (layoutId in blockedLayouts) {\r
1829 layout = blockedLayouts[layoutId];\r
1830 delete layout.blockedBy[key];\r
1831 }\r
1832 }\r
1833 return this.callParent(arguments);\r
1834 },\r
1835\r
1836 getEl: function (el) {\r
1837 var child = this.callParent(arguments);\r
1838 if (child && child !== this && child.parent !== this) {\r
1839 Ext.raise({\r
1840 msg: 'Got element from wrong component'\r
1841 });\r
1842 }\r
1843 return child;\r
1844 },\r
1845\r
1846 init: function () {\r
1847 var me = this,\r
1848 ret;\r
1849\r
1850 ret = me.callParent(arguments);\r
1851\r
1852 if (me.context.logOn.initItem) {\r
1853 Ext.log(me.id, ' consumers: content=', me.consumersContentWidth,'/',me.consumersContentHeight,\r
1854 ', container=', me.consumersContainerWidth,'/',me.consumersContainerHeight,\r
1855 ', size=', me.consumersWidth,'/',me.consumersHeight);\r
1856 }\r
1857\r
1858 return ret;\r
1859 },\r
1860\r
1861 invalidate: function () {\r
1862 if (this.wrapsComponent) {\r
1863 if (this.context.logOn.invalidate) {\r
1864 Ext.log('invalidate: ', this.id);\r
1865 }\r
1866 } else {\r
1867 Ext.raise({\r
1868 msg: 'Cannot invalidate an element contextItem'\r
1869 });\r
1870 }\r
1871 return this.callParent(arguments);\r
1872 },\r
1873\r
1874 setProp: function (propName, value, dirty) {\r
1875 var me = this,\r
1876 layout = me.context.currentLayout,\r
1877 setBy = me.context.getLayoutName(layout),\r
1878 fullName = me.id + '.' + propName,\r
1879 setByProps;\r
1880\r
1881 if (value !== null) {\r
1882 setByProps = me.setBy || (me.setBy = {});\r
1883 if (!setByProps[propName]) {\r
1884 setByProps[propName] = setBy;\r
1885 } else if (setByProps[propName] !== setBy) {\r
1886 Ext.log({level: 'warn'}, 'BAD! ', fullName, ' set by ', setByProps[propName], ' and ', setBy);\r
1887 }\r
1888 }\r
1889\r
1890 if (me.context.logOn.setProp) {\r
1891 if (typeof value !== 'undefined' && !isNaN(value) && me.props[propName] !== value) {\r
1892 Ext.log('set ', fullName, ' = ', value, ' (', dirty, ')');\r
1893 }\r
1894 }\r
1895\r
1896 return this.callParent(arguments);\r
1897 },\r
1898\r
1899 setHeight: function (height, dirty, /* private */force) {\r
1900 if (!force && this.wrapsComponent) {\r
1901 this.checkAuthority('height');\r
1902 }\r
1903\r
1904 return this.callParent(arguments);\r
1905 },\r
1906\r
1907 setWidth: function (width, dirty, /* private */force) {\r
1908 if (!force && this.wrapsComponent) {\r
1909 this.checkAuthority('width');\r
1910 }\r
1911\r
1912 return this.callParent(arguments);\r
1913 }\r
1914 } // End Diagnostics\r
1915 //-------------------------------------------------------------------------\r
1916}, function () {\r
1917 var px = { dom: true, parseInt: true, suffix: 'px' },\r
1918 isDom = { dom: true },\r
1919 faux = { dom: false };\r
1920\r
1921 // If a property exists in styleInfo, it participates in some way with the DOM. It may\r
1922 // be virtualized (like 'x' and y') and be indirect, but still requires a flush cycle\r
1923 // to reach the DOM. Properties (like 'contentWidth' and 'contentHeight') have no real\r
1924 // presence in the DOM and hence have no flush intanglements.\r
1925 // \r
1926 // For simple styles, the object value on the right contains properties that help in\r
1927 // decoding values read by getStyle and preparing values to pass to setStyle.\r
1928 //\r
1929 this.prototype.styleInfo = {\r
1930 containerChildrenSizeDone: faux,\r
1931 containerLayoutDone: faux,\r
1932 displayed: faux,\r
1933 done: faux,\r
1934 x: faux,\r
1935 y: faux,\r
1936\r
1937 // For Ext.grid.ColumnLayout\r
1938 columnsChanged: faux,\r
1939 rowHeights: faux,\r
1940 viewOverflowY: faux,\r
1941\r
1942 left: px,\r
1943 top: px,\r
1944 right: px,\r
1945 bottom: px,\r
1946 width: px,\r
1947 height: px,\r
1948\r
1949 'border-top-width': px,\r
1950 'border-right-width': px,\r
1951 'border-bottom-width': px,\r
1952 'border-left-width': px,\r
1953\r
1954 'margin-top': px,\r
1955 'margin-right': px,\r
1956 'margin-bottom': px,\r
1957 'margin-left': px,\r
1958\r
1959 'padding-top': px,\r
1960 'padding-right': px,\r
1961 'padding-bottom': px,\r
1962 'padding-left': px,\r
1963\r
1964 'line-height': isDom,\r
1965 display: isDom,\r
1966 clear: isDom\r
1967 };\r
1968});\r