discordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teams
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1795 lines
68 KiB
1795 lines
68 KiB
9 years ago
|
/**
|
||
|
* Manages context information during a layout.
|
||
|
*
|
||
|
* # Algorithm
|
||
|
*
|
||
|
* This class performs the following jobs:
|
||
|
*
|
||
|
* - Cache DOM reads to avoid reading the same values repeatedly.
|
||
|
* - Buffer DOM writes and flush them as a block to avoid read/write interleaving.
|
||
|
* - Track layout dependencies so each layout does not have to figure out the source of
|
||
|
* its dependent values.
|
||
|
* - Intelligently run layouts when the values on which they depend change (a trigger).
|
||
|
* - Allow layouts to avoid processing when required values are unavailable (a block).
|
||
|
*
|
||
|
* Work done during layout falls into either a "read phase" or a "write phase" and it is
|
||
|
* essential to always be aware of the current phase. Most methods in
|
||
|
* {@link Ext.layout.Layout Layout} are called during a read phase:
|
||
|
* {@link Ext.layout.Layout#calculate calculate},
|
||
|
* {@link Ext.layout.Layout#completeLayout completeLayout} and
|
||
|
* {@link Ext.layout.Layout#finalizeLayout finalizeLayout}. The exceptions to this are
|
||
|
* {@link Ext.layout.Layout#beginLayout beginLayout},
|
||
|
* {@link Ext.layout.Layout#beginLayoutCycle beginLayoutCycle} and
|
||
|
* {@link Ext.layout.Layout#finishedLayout finishedLayout} which are called during
|
||
|
* a write phase. While {@link Ext.layout.Layout#finishedLayout finishedLayout} is called
|
||
|
* a write phase, it is really intended to be a catch-all for post-processing after a
|
||
|
* layout run.
|
||
|
*
|
||
|
* In a read phase, it is OK to read the DOM but this should be done using the appropriate
|
||
|
* {@link Ext.layout.ContextItem ContextItem} where possible since that provides a cache
|
||
|
* to avoid redundant reads. No writes should be made to the DOM in a read phase! Instead,
|
||
|
* the values should be written to the proper ContextItem for later write-back.
|
||
|
*
|
||
|
* The rules flip-flop in a write phase. The only difference is that ContextItem methods
|
||
|
* like {@link Ext.layout.ContextItem#getStyle getStyle} will still read the DOM unless the
|
||
|
* value was previously read. This detail is unknowable from the outside of ContextItem, so
|
||
|
* read calls to ContextItem should also be avoided in a write phase.
|
||
|
*
|
||
|
* Calculating interdependent layouts requires a certain amount of iteration. In a given
|
||
|
* cycle, some layouts will contribute results that allow other layouts to proceed. The
|
||
|
* general flow then is to gather all of the layouts (both component and container) in a
|
||
|
* component tree and queue them all for processing. The initial queue order is bottom-up
|
||
|
* and component layout first, then container layout (if applicable) for each component.
|
||
|
*
|
||
|
* This initial step also calls the beginLayout method on all layouts to clear any values
|
||
|
* from the DOM that might interfere with calculations and measurements. In other words,
|
||
|
* this is a "write phase" and reads from the DOM should be strictly avoided.
|
||
|
*
|
||
|
* Next the layout enters into its iterations or "cycles". Each cycle consists of calling
|
||
|
* the {@link Ext.layout.Layout#calculate calculate} method on all layouts in the
|
||
|
* {@link #layoutQueue}. These calls are part of a "read phase" and writes to the DOM should
|
||
|
* be strictly avoided.
|
||
|
*
|
||
|
* # Considerations
|
||
|
*
|
||
|
* **RULE 1**: Respect the read/write cycles. Always use the {@link Ext.layout.ContextItem#getProp getProp}
|
||
|
* or {@link Ext.layout.ContextItem#getDomProp getDomProp} methods to get calculated values;
|
||
|
* only use the {@link Ext.layout.ContextItem#getStyle getStyle} method to read styles; use
|
||
|
* {@link Ext.layout.ContextItem#setProp setProp} to set DOM values. Some reads will, of
|
||
|
* course, still go directly to the DOM, but if there is a method in
|
||
|
* {@link Ext.layout.ContextItem ContextItem} to do a certain job, it should be used instead
|
||
|
* of a lower-level equivalent.
|
||
|
*
|
||
|
* The basic logic flow in {@link Ext.layout.Layout#calculate calculate} consists of gathering
|
||
|
* values by calling {@link Ext.layout.ContextItem#getProp getProp} or
|
||
|
* {@link Ext.layout.ContextItem#getDomProp getDomProp}, calculating results and publishing
|
||
|
* them by calling {@link Ext.layout.ContextItem#setProp setProp}. It is important to realize
|
||
|
* that {@link Ext.layout.ContextItem#getProp getProp} will return `undefined` if the value
|
||
|
* is not yet known. But the act of calling the method is enough to track the fact that the
|
||
|
* calling layout depends (in some way) on this value. In other words, the calling layout is
|
||
|
* "triggered" by the properties it requests.
|
||
|
*
|
||
|
* **RULE 2**: Avoid calling {@link Ext.layout.ContextItem#getProp getProp} unless the value
|
||
|
* is needed. Gratuitous calls cause inefficiency because the layout will appear to depend on
|
||
|
* values that it never actually uses. This applies equally to
|
||
|
* {@link Ext.layout.ContextItem#getDomProp getDomProp} and the test-only methods
|
||
|
* {@link Ext.layout.ContextItem#hasProp hasProp} and {@link Ext.layout.ContextItem#hasDomProp hasDomProp}.
|
||
|
*
|
||
|
* Because {@link Ext.layout.ContextItem#getProp getProp} can return `undefined`, it is often
|
||
|
* the case that subsequent math will produce NaN's. This is usually not a problem as the
|
||
|
* NaN's simply propagate along and result in final results that are NaN. Both `undefined`
|
||
|
* and NaN are ignored by {@link Ext.layout.ContextItem#setProp}, so it is often not necessary
|
||
|
* to even know that this is happening. It does become important for determining if a layout
|
||
|
* is not done or if it might lead to publishing an incorrect (but not NaN or `undefined`)
|
||
|
* value.
|
||
|
*
|
||
|
* **RULE 3**: If a layout has not calculated all the values it is required to calculate, it
|
||
|
* must set {@link Ext.layout.Layout#done done} to `false` before returning from
|
||
|
* {@link Ext.layout.Layout#calculate calculate}. This value is always `true` on entry because
|
||
|
* it is simpler to detect the incomplete state rather than the complete state (especially up
|
||
|
* and down a class hierarchy).
|
||
|
*
|
||
|
* **RULE 4**: A layout must never publish an incomplete (wrong) result. Doing so would cause
|
||
|
* dependent layouts to run their calculations on those wrong values, producing more wrong
|
||
|
* values and some layouts may even incorrectly flag themselves as {@link Ext.layout.Layout#done done}
|
||
|
* before the correct values are determined and republished. Doing this will poison the
|
||
|
* calculations.
|
||
|
*
|
||
|
* **RULE 5**: Each value should only be published by one layout. If multiple layouts attempt
|
||
|
* to publish the same values, it would be nearly impossible to avoid breaking **RULE 4**. To
|
||
|
* help detect this problem, the layout diagnostics will trap on an attempt to set a value
|
||
|
* from different layouts.
|
||
|
*
|
||
|
* Complex layouts can produce many results as part of their calculations. These values are
|
||
|
* important for other layouts to proceed and need to be published by the earliest possible
|
||
|
* call to {@link Ext.layout.Layout#calculate} to avoid unnecessary cycles and poor performance. It is
|
||
|
* also possible, however, for some results to be related in a way such that publishing them
|
||
|
* may be an all-or-none proposition (typically to avoid breaking *RULE 4*).
|
||
|
*
|
||
|
* **RULE 6**: Publish results as soon as they are known to be correct rather than wait for
|
||
|
* all values to be calculated. Waiting for everything to be complete can lead to deadlock.
|
||
|
* The key here is not to forget **RULE 4** in the process.
|
||
|
*
|
||
|
* Some layouts depend on certain critical values as part of their calculations. For example,
|
||
|
* HBox depends on width and cannot do anything until the width is known. In these cases, it
|
||
|
* is best to use {@link Ext.layout.ContextItem#block block} or
|
||
|
* {@link Ext.layout.ContextItem#domBlock domBlock} and thereby avoid processing the layout
|
||
|
* until the needed value is available.
|
||
|
*
|
||
|
* **RULE 7**: Use {@link Ext.layout.ContextItem#block block} or
|
||
|
* {@link Ext.layout.ContextItem#domBlock domBlock} when values are required to make progress.
|
||
|
* This will mimize wasted recalculations.
|
||
|
*
|
||
|
* **RULE 8**: Blocks should only be used when no forward progress can be made. If even one
|
||
|
* value could still be calculated, a block could result in a deadlock.
|
||
|
*
|
||
|
* Historically, layouts have been invoked directly by component code, sometimes in places
|
||
|
* like an `afterLayout` method for a child component. With the flexibility now available
|
||
|
* to solve complex, iterative issues, such things should be done in a responsible layout
|
||
|
* (be it component or container).
|
||
|
*
|
||
|
* **RULE 9**: Use layouts to solve layout issues and don't wait for the layout to finish to
|
||
|
* perform further layouts. This is especially important now that layouts process entire
|
||
|
* component trees and not each layout in isolation.
|
||
|
*
|
||
|
* # Sequence Diagram
|
||
|
*
|
||
|
* The simplest sequence diagram for a layout run looks roughly like this:
|
||
|
*
|
||
|
* Context Layout 1 Item 1 Layout 2 Item 2
|
||
|
* | | | | |
|
||
|
* ---->X-------------->X | | |
|
||
|
* run X---------------|-----------|---------->X |
|
||
|
* X beginLayout | | | |
|
||
|
* X | | | |
|
||
|
* A X-------------->X | | |
|
||
|
* X calculate X---------->X | |
|
||
|
* X C X getProp | | |
|
||
|
* B X X---------->X | |
|
||
|
* X | setProp | | |
|
||
|
* X | | | |
|
||
|
* D X---------------|-----------|---------->X |
|
||
|
* X calculate | | X---------->X
|
||
|
* X | | | setProp |
|
||
|
* E X | | | |
|
||
|
* X---------------|-----------|---------->X |
|
||
|
* X completeLayout| | F | |
|
||
|
* X | | | |
|
||
|
* G X | | | |
|
||
|
* H X-------------->X | | |
|
||
|
* X calculate X---------->X | |
|
||
|
* X I X getProp | | |
|
||
|
* X X---------->X | |
|
||
|
* X | setProp | | |
|
||
|
* J X-------------->X | | |
|
||
|
* X completeLayout| | | |
|
||
|
* X | | | |
|
||
|
* K X-------------->X | | |
|
||
|
* X---------------|-----------|---------->X |
|
||
|
* X finalizeLayout| | | |
|
||
|
* X | | | |
|
||
|
* L X-------------->X | | |
|
||
|
* X---------------|-----------|---------->X |
|
||
|
* X finishedLayout| | | |
|
||
|
* X | | | |
|
||
|
* M X-------------->X | | |
|
||
|
* X---------------|-----------|---------->X |
|
||
|
* X notifyOwner | | | |
|
||
|
* N | | | | |
|
||
|
* - - - - -
|
||
|
*
|
||
|
*
|
||
|
* Notes:
|
||
|
*
|
||
|
* **A.** This is a call from the {@link #run} method to the {@link #run} method.
|
||
|
* Each layout in the queue will have its {@link Ext.layout.Layout#calculate calculate}
|
||
|
* method called.
|
||
|
*
|
||
|
* **B.** After each {@link Ext.layout.Layout#calculate calculate} method is called the
|
||
|
* {@link Ext.layout.Layout#done done} flag is checked to see if the Layout has completed.
|
||
|
* If it has completed and that layout object implements a
|
||
|
* {@link Ext.layout.Layout#completeLayout completeLayout} method, this layout is queued to
|
||
|
* receive its call. Otherwise, the layout will be queued again unless there are blocks or
|
||
|
* triggers that govern its requeueing.
|
||
|
*
|
||
|
* **C.** The call to {@link Ext.layout.ContextItem#getProp getProp} is made to the Item
|
||
|
* and that will be tracked as a trigger (keyed by the name of the property being requested).
|
||
|
* Changes to this property will cause this layout to be requeued. The call to
|
||
|
* {@link Ext.layout.ContextItem#setProp setProp} will place a value in the item and not
|
||
|
* directly into the DOM.
|
||
|
*
|
||
|
* **D.** Call the other layouts now in the first cycle (repeat **B** and **C** for each
|
||
|
* layout).
|
||
|
*
|
||
|
* **E.** After completing a cycle, if progress was made (new properties were written to
|
||
|
* the context) and if the {@link #layoutQueue} is not empty, the next cycle is run. If no
|
||
|
* progress was made or no layouts are ready to run, all buffered values are written to
|
||
|
* the DOM (a flush).
|
||
|
*
|
||
|
* **F.** After flushing, any layouts that were marked as {@link Ext.layout.Layout#done done}
|
||
|
* that also have a {@link Ext.layout.Layout#completeLayout completeLayout} method are called.
|
||
|
* This can cause them to become no longer done (see {@link #invalidate}). As with
|
||
|
* {@link Ext.layout.Layout#calculate calculate}, this is considered a "read phase" and
|
||
|
* direct DOM writes should be avoided.
|
||
|
*
|
||
|
* **G.** Flushing and calling any pending {@link Ext.layout.Layout#completeLayout completeLayout}
|
||
|
* methods will likely trigger layouts that called {@link Ext.layout.ContextItem#getDomProp getDomProp}
|
||
|
* and unblock layouts that have called {@link Ext.layout.ContextItem#domBlock domBlock}.
|
||
|
* These variants are used when a layout needs the value to be correct in the DOM and not
|
||
|
* simply known. If this does not cause at least one layout to enter the queue, we have a
|
||
|
* layout FAILURE. Otherwise, we continue with the next cycle.
|
||
|
*
|
||
|
* **H.** Call {@link Ext.layout.Layout#calculate calculate} on any layouts in the queue
|
||
|
* at the start of this cycle. Just a repeat of **B** through **G**.
|
||
|
*
|
||
|
* **I.** Once the layout has calculated all that it is resposible for, it can leave itself
|
||
|
* in the {@link Ext.layout.Layout#done done} state. This is the value on entry to
|
||
|
* {@link Ext.layout.Layout#calculate calculate} and must be cleared in that call if the
|
||
|
* layout has more work to do.
|
||
|
*
|
||
|
* **J.** Now that all layouts are done, flush any DOM values and
|
||
|
* {@link Ext.layout.Layout#completeLayout completeLayout} calls. This can again cause
|
||
|
* layouts to become not done, and so we will be back on another cycle if that happens.
|
||
|
*
|
||
|
* **K.** After all layouts are done, call the {@link Ext.layout.Layout#finalizeLayout finalizeLayout}
|
||
|
* method on any layouts that have one. As with {@link Ext.layout.Layout#completeLayout completeLayout},
|
||
|
* this can cause layouts to become no longer done. This is less desirable than using
|
||
|
* {@link Ext.layout.Layout#completeLayout completeLayout} because it will cause all
|
||
|
* {@link Ext.layout.Layout#finalizeLayout finalizeLayout} methods to be called again
|
||
|
* when we think things are all wrapped up.
|
||
|
*
|
||
|
* **L.** After finishing the last iteration, layouts that have a
|
||
|
* {@link Ext.layout.Layout#finishedLayout finishedLayout} method will be called. This
|
||
|
* call will only happen once per run and cannot cause layouts to be run further.
|
||
|
*
|
||
|
* **M.** After calling finahedLayout, layouts that have a
|
||
|
* {@link Ext.layout.Layout#notifyOwner notifyOwner} method will be called. This
|
||
|
* call will only happen once per run and cannot cause layouts to be run further.
|
||
|
*
|
||
|
* **N.** One last flush to make sure everything has been written to the DOM.
|
||
|
*
|
||
|
* # Inter-Layout Collaboration
|
||
|
*
|
||
|
* Many layout problems require collaboration between multiple layouts. In some cases, this
|
||
|
* is as simple as a component's container layout providing results used by its component
|
||
|
* layout or vise-versa. A slightly more distant collaboration occurs in a box layout when
|
||
|
* stretchmax is used: the child item's component layout provides results that are consumed
|
||
|
* by the ownerCt's box layout to determine the size of the children.
|
||
|
*
|
||
|
* The various forms of interdependence between a container and its children are described by
|
||
|
* each components' {@link Ext.Component#getSizeModel size model}.
|
||
|
*
|
||
|
* To facilitate this collaboration, the following pairs of properties are published to the
|
||
|
* component's {@link Ext.layout.ContextItem ContextItem}:
|
||
|
*
|
||
|
* - width/height: These hold the final size of the component. The layout indicated by the
|
||
|
* {@link Ext.Component#getSizeModel size model} is responsible for setting these.
|
||
|
* - contentWidth/contentHeight: These hold size information published by the container
|
||
|
* layout or from DOM measurement. These describe the content only. These values are
|
||
|
* used by the component layout to determine the outer width/height when that component
|
||
|
* is {@link Ext.Component#shrinkWrap shrink-wrapped}. They are also used to
|
||
|
* determine overflow. All container layouts must publish these values for dimensions
|
||
|
* that are shrink-wrapped. If a component has raw content (not container items), the
|
||
|
* componentLayout must publish these values instead.
|
||
|
*
|
||
|
* @private
|
||
|
*/
|
||
|
Ext.define('Ext.layout.Context', {
|
||
|
requires: [
|
||
|
//<debug>
|
||
|
'Ext.perf.Monitor',
|
||
|
//</debug>
|
||
|
'Ext.util.Queue',
|
||
|
'Ext.layout.ContextItem',
|
||
|
'Ext.layout.Layout',
|
||
|
'Ext.fx.Anim',
|
||
|
'Ext.fx.Manager'
|
||
|
],
|
||
|
|
||
|
remainingLayouts: 0,
|
||
|
|
||
|
/**
|
||
|
* @property {Number} state One of these values:
|
||
|
*
|
||
|
* - 0 - Before run
|
||
|
* - 1 - Running
|
||
|
* - 2 - Run complete
|
||
|
*/
|
||
|
state: 0,
|
||
|
|
||
|
/**
|
||
|
* @property {Number} cycleWatchDog
|
||
|
* This value is used to detect layouts that cannot progress by checking the amount of
|
||
|
* cycles processed. The value should be large enough to satisfy all but exceptionally large
|
||
|
* layout structures. When the amount of cycles is reached, the layout will fail. This should
|
||
|
* only be used for debugging, layout failures should be considered as an exceptional occurrence.
|
||
|
* @private
|
||
|
* @since 5.1.1
|
||
|
*/
|
||
|
cycleWatchDog: 200,
|
||
|
|
||
|
constructor: function (config) {
|
||
|
var me = this;
|
||
|
|
||
|
Ext.apply(me, config);
|
||
|
|
||
|
// holds the ContextItem collection, keyed by element id
|
||
|
me.items = {};
|
||
|
|
||
|
// a collection of layouts keyed by layout id
|
||
|
me.layouts = {};
|
||
|
|
||
|
// the number of blocks of any kind:
|
||
|
me.blockCount = 0;
|
||
|
// the number of cycles that have been run:
|
||
|
me.cycleCount = 0;
|
||
|
// the number of flushes to the DOM:
|
||
|
me.flushCount = 0;
|
||
|
// the number of layout calculate calls:
|
||
|
me.calcCount = 0;
|
||
|
|
||
|
me.animateQueue = me.newQueue();
|
||
|
me.completionQueue = me.newQueue();
|
||
|
me.finalizeQueue = me.newQueue();
|
||
|
me.finishQueue = me.newQueue();
|
||
|
me.flushQueue = me.newQueue();
|
||
|
|
||
|
me.invalidateData = {};
|
||
|
|
||
|
/**
|
||
|
* @property {Ext.util.Queue} layoutQueue
|
||
|
* List of layouts to perform.
|
||
|
*/
|
||
|
me.layoutQueue = me.newQueue();
|
||
|
|
||
|
// this collection is special because we ensure that there are no parent/child pairs
|
||
|
// present, only distinct top-level components
|
||
|
me.invalidQueue = [];
|
||
|
|
||
|
me.triggers = {
|
||
|
data: {
|
||
|
/*
|
||
|
layoutId: [
|
||
|
{ item: contextItem, prop: propertyName }
|
||
|
]
|
||
|
*/
|
||
|
},
|
||
|
dom: {}
|
||
|
};
|
||
|
},
|
||
|
|
||
|
callLayout: function (layout, methodName) {
|
||
|
this.currentLayout = layout;
|
||
|
layout[methodName](this.getCmp(layout.owner));
|
||
|
},
|
||
|
|
||
|
cancelComponent: function (comp, isChild, isDestroying) {
|
||
|
var me = this,
|
||
|
components = comp,
|
||
|
isArray = !comp.isComponent,
|
||
|
length = isArray ? components.length : 1,
|
||
|
i, k, klen, items, layout, newQueue, oldQueue, entry, temp,
|
||
|
ownerCtContext;
|
||
|
|
||
|
for (i = 0; i < length; ++i) {
|
||
|
if (isArray) {
|
||
|
comp = components[i];
|
||
|
}
|
||
|
|
||
|
// If the component is being destroyed, remove the component's ContextItem from its parent's contextItem.childItems array
|
||
|
if (isDestroying && comp.ownerCt) {
|
||
|
ownerCtContext = this.items[comp.ownerCt.el.id];
|
||
|
if (ownerCtContext) {
|
||
|
Ext.Array.remove(ownerCtContext.childItems, me.getCmp(comp));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!isChild) {
|
||
|
oldQueue = me.invalidQueue;
|
||
|
klen = oldQueue.length;
|
||
|
|
||
|
if (klen) {
|
||
|
me.invalidQueue = newQueue = [];
|
||
|
for (k = 0; k < klen; ++k) {
|
||
|
entry = oldQueue[k];
|
||
|
temp = entry.item.target;
|
||
|
if (temp !== comp && !temp.up(comp)) {
|
||
|
newQueue.push(entry);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
layout = comp.componentLayout;
|
||
|
me.cancelLayout(layout);
|
||
|
|
||
|
if (layout.getLayoutItems) {
|
||
|
items = layout.getLayoutItems();
|
||
|
if (items.length) {
|
||
|
me.cancelComponent(items, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (comp.isContainer && !comp.collapsed) {
|
||
|
layout = comp.layout;
|
||
|
me.cancelLayout(layout);
|
||
|
|
||
|
items = layout.getVisibleItems();
|
||
|
if (items.length) {
|
||
|
me.cancelComponent(items, true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
cancelLayout: function (layout) {
|
||
|
var me = this;
|
||
|
|
||
|
me.completionQueue.remove(layout);
|
||
|
me.finalizeQueue.remove(layout);
|
||
|
me.finishQueue.remove(layout);
|
||
|
me.layoutQueue.remove(layout);
|
||
|
|
||
|
if (layout.running) {
|
||
|
me.layoutDone(layout);
|
||
|
}
|
||
|
|
||
|
layout.ownerContext = null;
|
||
|
},
|
||
|
|
||
|
clearTriggers: function (layout, inDom) {
|
||
|
var id = layout.id,
|
||
|
collection = this.triggers[inDom ? 'dom' : 'data'],
|
||
|
triggers = collection && collection[id],
|
||
|
length = (triggers && triggers.length) || 0,
|
||
|
i, item, trigger;
|
||
|
|
||
|
for (i = 0; i < length; ++i) {
|
||
|
trigger = triggers[i];
|
||
|
item = trigger.item;
|
||
|
|
||
|
collection = inDom ? item.domTriggers : item.triggers;
|
||
|
delete collection[trigger.prop][id];
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Flushes any pending writes to the DOM by calling each ContextItem in the flushQueue.
|
||
|
*/
|
||
|
flush: function () {
|
||
|
var me = this,
|
||
|
items = me.flushQueue.clear(),
|
||
|
length = items.length, i;
|
||
|
|
||
|
if (length) {
|
||
|
++me.flushCount;
|
||
|
|
||
|
for (i = 0; i < length; ++i) {
|
||
|
items[i].flush();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
flushAnimations: function() {
|
||
|
var me = this,
|
||
|
items = me.animateQueue.clear(),
|
||
|
len = items.length,
|
||
|
i;
|
||
|
|
||
|
if (len) {
|
||
|
for (i = 0; i < len; i++) {
|
||
|
// Each Component may refuse to participate in animations.
|
||
|
// This is used by the BoxReorder plugin which drags a Component,
|
||
|
// during which that Component must be exempted from layout positioning.
|
||
|
if (items[i].target.animate !== false) {
|
||
|
items[i].flushAnimations();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ensure the first frame fires now to avoid a browser repaint with the elements in the "to" state
|
||
|
// before they are returned to their "from" state by the animation.
|
||
|
Ext.fx.Manager.runner();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
flushInvalidates: function () {
|
||
|
var me = this,
|
||
|
queue = me.invalidQueue,
|
||
|
length = queue && queue.length,
|
||
|
comp, components, entry, i;
|
||
|
|
||
|
me.invalidQueue = [];
|
||
|
|
||
|
if (length) {
|
||
|
components = [];
|
||
|
for (i = 0; i < length; ++i) {
|
||
|
comp = (entry = queue[i]).item.target;
|
||
|
// we filter out-of-body components here but allow them into the queue to
|
||
|
// ensure that their child components are coalesced out (w/no additional
|
||
|
// cost beyond our normal effort to avoid parent/child components in the
|
||
|
// queue)
|
||
|
if (!comp.container.isDetachedBody) {
|
||
|
components.push(comp);
|
||
|
|
||
|
if (entry.options) {
|
||
|
me.invalidateData[comp.id] = entry.options;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
me.invalidate(components, null);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
flushLayouts: function (queueName, methodName, dontClear) {
|
||
|
var me = this,
|
||
|
layouts = dontClear ? me[queueName].items : me[queueName].clear(),
|
||
|
length = layouts.length,
|
||
|
i, layout;
|
||
|
|
||
|
if (length) {
|
||
|
for (i = 0; i < length; ++i) {
|
||
|
layout = layouts[i];
|
||
|
if (!layout.running) {
|
||
|
me.callLayout(layout, methodName);
|
||
|
}
|
||
|
}
|
||
|
me.currentLayout = null;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Returns the ContextItem for a component.
|
||
|
* @param {Ext.Component} cmp
|
||
|
*/
|
||
|
getCmp: function (cmp) {
|
||
|
return this.getItem(cmp, cmp.el);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Returns the ContextItem for an element.
|
||
|
* @param {Ext.layout.ContextItem} parent
|
||
|
* @param {Ext.dom.Element} el
|
||
|
*/
|
||
|
getEl: function (parent, el) {
|
||
|
var item = this.getItem(el, el);
|
||
|
|
||
|
if (!item.parent) {
|
||
|
item.parent = parent;
|
||
|
|
||
|
// all items share an empty children array (to avoid null checks), so we can
|
||
|
// only push on to the children array if there is already something there (we
|
||
|
// copy-on-write):
|
||
|
if (parent.children.length) {
|
||
|
parent.children.push(item);
|
||
|
} else {
|
||
|
parent.children = [ item ]; // now parent has its own children[] (length=1)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return item;
|
||
|
},
|
||
|
|
||
|
getItem: function (target, el) {
|
||
|
var id = el.id,
|
||
|
items = this.items,
|
||
|
item = items[id] ||
|
||
|
(items[id] = new Ext.layout.ContextItem({
|
||
|
context: this,
|
||
|
target: target,
|
||
|
el: el
|
||
|
}));
|
||
|
|
||
|
return item;
|
||
|
},
|
||
|
|
||
|
handleFailure: function () {
|
||
|
// This method should never be called, but is need when layouts fail (hence the
|
||
|
// "should never"). We just disconnect any of the layouts from the run and return
|
||
|
// them to the state they would be in had the layout completed properly.
|
||
|
var layouts = this.layouts,
|
||
|
layout, key;
|
||
|
|
||
|
Ext.failedLayouts = (Ext.failedLayouts || 0) + 1;
|
||
|
|
||
|
for (key in layouts) {
|
||
|
layout = layouts[key];
|
||
|
|
||
|
if (layouts.hasOwnProperty(key)) {
|
||
|
layout.running = false;
|
||
|
layout.ownerContext = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//<debug>
|
||
|
if (Ext.repoDevMode && !this.pageAnalyzerMode) {
|
||
|
Ext.Error.raise('Layout run failed');
|
||
|
} else {
|
||
|
Ext.log.error('Layout run failed');
|
||
|
}
|
||
|
//</debug>
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Invalidates one or more components' layouts (component and container). This can be
|
||
|
* called before run to identify the components that need layout or during the run to
|
||
|
* restart the layout of a component. This is called internally to flush any queued
|
||
|
* invalidations at the start of a cycle. If called during a run, it is not expected
|
||
|
* that new components will be introduced to the layout.
|
||
|
*
|
||
|
* @param {Ext.Component/Array} components An array of Components or a single Component.
|
||
|
* @param {Boolean} full True if all properties should be invalidated, otherwise only
|
||
|
* those calculated by the component should be invalidated.
|
||
|
*/
|
||
|
invalidate: function (components, full) {
|
||
|
var me = this,
|
||
|
isArray = !components.isComponent,
|
||
|
containerLayoutDone, ownerLayout,
|
||
|
firstTime, i, comp, item, items, length, componentLayout, layout,
|
||
|
invalidateOptions, token, skipLayout;
|
||
|
|
||
|
for (i = 0, length = isArray ? components.length : 1; i < length; ++i) {
|
||
|
comp = isArray ? components[i] : components;
|
||
|
|
||
|
if (comp.rendered && !comp.hidden) {
|
||
|
ownerLayout = comp.ownerLayout;
|
||
|
componentLayout = comp.componentLayout;
|
||
|
firstTime = !componentLayout.ownerContext;
|
||
|
skipLayout = false;
|
||
|
|
||
|
if ((!ownerLayout || !ownerLayout.needsItemSize) && comp.liquidLayout) {
|
||
|
// our owning layout doesn't need us to run, and our componentLayout
|
||
|
// wants to opt out because it uses liquid CSS layout.
|
||
|
// We can skip invalidation for this component.
|
||
|
skipLayout = true;
|
||
|
}
|
||
|
|
||
|
// if we are skipping layout, we can also skip creation of the context
|
||
|
// item, unless our owner layout needs it to set our size.
|
||
|
if (!skipLayout || (ownerLayout && ownerLayout.setsItemSize)) {
|
||
|
item = me.getCmp(comp);
|
||
|
|
||
|
// If the component has had no changes which necessitate a layout, do not lay it out.
|
||
|
// Temporarily disabled because this breaks dock layout (see EXTJSIV-10251)
|
||
|
// if (item.optOut) {
|
||
|
// skipLayout = true;
|
||
|
// }
|
||
|
|
||
|
layout = (comp.isContainer && !comp.collapsed) ? comp.layout : null;
|
||
|
|
||
|
// Extract any invalidate() options for this item.
|
||
|
invalidateOptions = me.invalidateData[item.id];
|
||
|
delete me.invalidateData[item.id];
|
||
|
|
||
|
// We invalidate the contextItem's in a top-down manner so that SizeModel
|
||
|
// info for containers is available to their children. This is a critical
|
||
|
// optimization since sizeModel determination often requires knowing the
|
||
|
// sizeModel of the ownerCt. If this weren't cached as we descend, this
|
||
|
// would be an O(N^2) operation! (where N=number of components, or 300+/-
|
||
|
// in Themes)
|
||
|
token = item.init(full, invalidateOptions);
|
||
|
}
|
||
|
|
||
|
if (skipLayout) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (invalidateOptions) {
|
||
|
me.processInvalidate(invalidateOptions, item, 'before');
|
||
|
}
|
||
|
|
||
|
// Allow the component layout a chance to effect its size model before we
|
||
|
// recurse down the component hierarchy (since children need to know the
|
||
|
// size model of their ownerCt).
|
||
|
if (componentLayout.beforeLayoutCycle) {
|
||
|
componentLayout.beforeLayoutCycle(item);
|
||
|
}
|
||
|
|
||
|
if (layout && layout.beforeLayoutCycle) {
|
||
|
// allow the container layout take a peek as well. Table layout can
|
||
|
// influence its children's styling due to the interaction of nesting
|
||
|
// table-layout:fixed and auto inside each other without intervening
|
||
|
// elements of known size.
|
||
|
layout.beforeLayoutCycle(item);
|
||
|
}
|
||
|
|
||
|
// Finish up the item-level processing that is based on the size model of
|
||
|
// the component.
|
||
|
token = item.initContinue(token);
|
||
|
|
||
|
// Start this state variable at true, since that is the value we want if
|
||
|
// they do not apply (i.e., no work of this kind on which to wait).
|
||
|
containerLayoutDone = true;
|
||
|
|
||
|
// A ComponentLayout MUST implement getLayoutItems to allow its children
|
||
|
// to be collected. Ext.container.Container does this, but non-Container
|
||
|
// Components which manage Components as part of their structure (e.g.,
|
||
|
// HtmlEditor) must still return child Components via getLayoutItems.
|
||
|
if (componentLayout.getLayoutItems) {
|
||
|
componentLayout.renderChildren();
|
||
|
|
||
|
items = componentLayout.getLayoutItems();
|
||
|
if (items.length) {
|
||
|
me.invalidate(items, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (layout) {
|
||
|
containerLayoutDone = false;
|
||
|
layout.renderChildren();
|
||
|
|
||
|
if (layout.needsItemSize || layout.activeItemCount) {
|
||
|
// if the layout specified that it needs the layouts of its children
|
||
|
// to run, or if the number of "liquid" child layouts is greater
|
||
|
// than 0, we need to recurse into the children, since some or
|
||
|
// all of them may need their layouts to run.
|
||
|
items = layout.getVisibleItems();
|
||
|
if (items.length) {
|
||
|
me.invalidate(items, true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Finish the processing that requires the size models of child items to
|
||
|
// be determined (and some misc other stuff).
|
||
|
item.initDone(containerLayoutDone);
|
||
|
|
||
|
// Inform the layouts that we are about to begin (or begin again) now that
|
||
|
// the size models of the component and its children are setup.
|
||
|
me.resetLayout(componentLayout, item, firstTime);
|
||
|
if (layout) {
|
||
|
me.resetLayout(layout, item, firstTime);
|
||
|
}
|
||
|
|
||
|
// This has to occur after the component layout has had a chance to begin
|
||
|
// so that we can determine what kind of animation might be needed. TODO-
|
||
|
// move this determination into the layout itself.
|
||
|
item.initAnimation();
|
||
|
|
||
|
if (invalidateOptions) {
|
||
|
me.processInvalidate(invalidateOptions, item, 'after');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
me.currentLayout = null;
|
||
|
},
|
||
|
|
||
|
// Returns true is descendant is a descendant of ancestor
|
||
|
isDescendant: function(ancestor, descendant) {
|
||
|
if (ancestor.isContainer) {
|
||
|
for (var c = descendant.ownerCt; c; c = c.ownerCt) {
|
||
|
if (c === ancestor) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
layoutDone: function (layout) {
|
||
|
var ownerContext = layout.ownerContext;
|
||
|
|
||
|
layout.running = false;
|
||
|
|
||
|
// Once a component layout completes, we can mark it as "done".
|
||
|
if (layout.isComponentLayout) {
|
||
|
if (ownerContext.measuresBox) {
|
||
|
ownerContext.onBoxMeasured(); // be sure to release our boxParent
|
||
|
}
|
||
|
|
||
|
ownerContext.setProp('done', true);
|
||
|
} else {
|
||
|
ownerContext.setProp('containerLayoutDone', true);
|
||
|
}
|
||
|
|
||
|
--this.remainingLayouts;
|
||
|
++this.progressCount; // a layout completion is progress
|
||
|
},
|
||
|
|
||
|
newQueue: function () {
|
||
|
return new Ext.util.Queue();
|
||
|
},
|
||
|
|
||
|
processInvalidate: function (options, item, name) {
|
||
|
// When calling a callback, the currentLayout needs to be adjusted so
|
||
|
// that whichever layout caused the invalidate is the currentLayout...
|
||
|
if (options[name]) {
|
||
|
var me = this,
|
||
|
currentLayout = me.currentLayout;
|
||
|
|
||
|
me.currentLayout = options.layout || null;
|
||
|
|
||
|
options[name](item, options);
|
||
|
|
||
|
me.currentLayout = currentLayout;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Queues a ContextItem to have its {@link Ext.layout.ContextItem#flushAnimations} method called.
|
||
|
*
|
||
|
* @param {Ext.layout.ContextItem} item
|
||
|
* @private
|
||
|
*/
|
||
|
queueAnimation: function (item) {
|
||
|
this.animateQueue.add(item);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Queues a layout to have its {@link Ext.layout.Layout#completeLayout} method called.
|
||
|
*
|
||
|
* @param {Ext.layout.Layout} layout
|
||
|
* @private
|
||
|
*/
|
||
|
queueCompletion: function (layout) {
|
||
|
this.completionQueue.add(layout);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Queues a layout to have its {@link Ext.layout.Layout#finalizeLayout} method called.
|
||
|
*
|
||
|
* @param {Ext.layout.Layout} layout
|
||
|
* @private
|
||
|
*/
|
||
|
queueFinalize: function (layout) {
|
||
|
this.finalizeQueue.add(layout);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Queues a ContextItem for the next flush to the DOM. This should only be called by
|
||
|
* the {@link Ext.layout.ContextItem} class.
|
||
|
*
|
||
|
* @param {Ext.layout.ContextItem} item
|
||
|
* @private
|
||
|
*/
|
||
|
queueFlush: function (item) {
|
||
|
this.flushQueue.add(item);
|
||
|
},
|
||
|
|
||
|
chainFns: function (oldOptions, newOptions, funcName) {
|
||
|
var me = this,
|
||
|
oldLayout = oldOptions.layout,
|
||
|
newLayout = newOptions.layout,
|
||
|
oldFn = oldOptions[funcName],
|
||
|
newFn = newOptions[funcName];
|
||
|
|
||
|
// Call newFn last so it can get the final word on things... also, the "this"
|
||
|
// pointer will be passed correctly by createSequence with oldFn first.
|
||
|
return function (contextItem) {
|
||
|
var prev = me.currentLayout;
|
||
|
if (oldFn) {
|
||
|
me.currentLayout = oldLayout;
|
||
|
oldFn.call(oldOptions.scope || oldOptions, contextItem, oldOptions);
|
||
|
}
|
||
|
me.currentLayout = newLayout;
|
||
|
newFn.call(newOptions.scope || newOptions, contextItem, newOptions);
|
||
|
me.currentLayout = prev;
|
||
|
};
|
||
|
},
|
||
|
|
||
|
purgeInvalidates: function () {
|
||
|
var me = this,
|
||
|
newQueue = [],
|
||
|
oldQueue = me.invalidQueue,
|
||
|
oldLength = oldQueue.length,
|
||
|
oldIndex, newIndex, newEntry, newComp, oldEntry, oldComp, keep;
|
||
|
|
||
|
for (oldIndex = 0; oldIndex < oldLength; ++oldIndex) {
|
||
|
oldEntry = oldQueue[oldIndex];
|
||
|
oldComp = oldEntry.item.target;
|
||
|
|
||
|
keep = true;
|
||
|
for (newIndex = newQueue.length; newIndex--;) {
|
||
|
newEntry = newQueue[newIndex];
|
||
|
newComp = newEntry.item.target;
|
||
|
|
||
|
if (oldComp.isLayoutChild(newComp)) {
|
||
|
keep = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (newComp.isLayoutChild(oldComp)) {
|
||
|
Ext.Array.erase(newQueue, newIndex, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (keep) {
|
||
|
newQueue.push(oldEntry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
me.invalidQueue = newQueue;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Queue a component (and its tree) to be invalidated on the next cycle.
|
||
|
*
|
||
|
* @param {Ext.Component/Ext.layout.ContextItem} item The component or ContextItem to invalidate.
|
||
|
* @param {Object} options An object describing how to handle the invalidation (see
|
||
|
* {@link Ext.layout.ContextItem#invalidate} for details).
|
||
|
* @private
|
||
|
*/
|
||
|
queueInvalidate: function (item, options) {
|
||
|
var me = this,
|
||
|
newQueue = [],
|
||
|
oldQueue = me.invalidQueue,
|
||
|
index = oldQueue.length,
|
||
|
comp, old, oldComp, oldOptions, oldState;
|
||
|
|
||
|
if (item.isComponent) {
|
||
|
item = me.getCmp(comp = item);
|
||
|
} else {
|
||
|
comp = item.target;
|
||
|
}
|
||
|
|
||
|
item.invalid = true;
|
||
|
|
||
|
// See if comp is contained by any component already in the queue (ignore comp if
|
||
|
// that is the case). Eliminate any components in the queue that are contained by
|
||
|
// comp (by not adding them to newQueue).
|
||
|
while (index--) {
|
||
|
old = oldQueue[index];
|
||
|
oldComp = old.item.target;
|
||
|
|
||
|
if (!comp.isFloating && comp.up(oldComp)) {
|
||
|
return; // oldComp contains comp, so this invalidate is redundant
|
||
|
}
|
||
|
|
||
|
if (oldComp === comp) {
|
||
|
// if already in the queue, update the options...
|
||
|
if (!(oldOptions = old.options)) {
|
||
|
old.options = options;
|
||
|
} else if (options) {
|
||
|
if (options.widthModel) {
|
||
|
oldOptions.widthModel = options.widthModel;
|
||
|
}
|
||
|
if (options.heightModel) {
|
||
|
oldOptions.heightModel = options.heightModel;
|
||
|
}
|
||
|
if (!(oldState = oldOptions.state)) {
|
||
|
oldOptions.state = options.state;
|
||
|
} else if (options.state) {
|
||
|
Ext.apply(oldState, options.state);
|
||
|
}
|
||
|
|
||
|
if (options.before) {
|
||
|
oldOptions.before = me.chainFns(oldOptions, options, 'before');
|
||
|
}
|
||
|
if (options.after) {
|
||
|
oldOptions.after = me.chainFns(oldOptions, options, 'after');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// leave the old queue alone now that we've update this comp's entry...
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!oldComp.isLayoutChild(comp)) {
|
||
|
newQueue.push(old); // comp does not contain oldComp
|
||
|
}
|
||
|
// else if (oldComp isDescendant of comp) skip
|
||
|
}
|
||
|
// newQueue contains only those components not a descendant of comp
|
||
|
|
||
|
// to get here, comp must not be a child of anything already in the queue, so it
|
||
|
// needs to be added along with its "options":
|
||
|
newQueue.push({ item: item, options: options });
|
||
|
|
||
|
me.invalidQueue = newQueue;
|
||
|
},
|
||
|
|
||
|
queueItemLayouts: function (item) {
|
||
|
var comp = item.isComponent ? item : item.target,
|
||
|
layout = comp.componentLayout;
|
||
|
|
||
|
if (!layout.pending && !layout.invalid && !layout.done) {
|
||
|
this.queueLayout(layout);
|
||
|
}
|
||
|
|
||
|
layout = comp.layout;
|
||
|
if (layout && !layout.pending && !layout.invalid && !layout.done && !comp.collapsed) {
|
||
|
this.queueLayout(layout);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Queues a layout for the next calculation cycle. This should not be called if the
|
||
|
* layout is done, blocked or already in the queue. The only classes that should call
|
||
|
* this method are this class and {@link Ext.layout.ContextItem}.
|
||
|
*
|
||
|
* @param {Ext.layout.Layout} layout The layout to add to the queue.
|
||
|
* @private
|
||
|
*/
|
||
|
queueLayout: function (layout) {
|
||
|
this.layoutQueue.add(layout);
|
||
|
layout.pending = true;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Removes the ContextItem for an element from the cache and from the parent's
|
||
|
* "children" array.
|
||
|
* @param {Ext.layout.ContextItem} parent
|
||
|
* @param {Ext.dom.Element} el
|
||
|
*/
|
||
|
removeEl: function (parent, el) {
|
||
|
var id = el.id,
|
||
|
children = parent.children,
|
||
|
items = this.items;
|
||
|
|
||
|
if(children) {
|
||
|
Ext.Array.remove(children, items[id]);
|
||
|
}
|
||
|
delete items[id];
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Resets the given layout object. This is called at the start of the run and can also
|
||
|
* be called during the run by calling {@link #invalidate}.
|
||
|
*/
|
||
|
resetLayout: function (layout, ownerContext, firstTime) {
|
||
|
var me = this;
|
||
|
|
||
|
me.currentLayout = layout;
|
||
|
|
||
|
layout.done = false;
|
||
|
layout.pending = true;
|
||
|
layout.firedTriggers = 0;
|
||
|
|
||
|
me.layoutQueue.add(layout);
|
||
|
|
||
|
if (firstTime) {
|
||
|
me.layouts[layout.id] = layout; // track the layout for this run by its id
|
||
|
layout.running = true;
|
||
|
|
||
|
if (layout.finishedLayout) {
|
||
|
me.finishQueue.add(layout);
|
||
|
}
|
||
|
|
||
|
// reset or update per-run counters:
|
||
|
|
||
|
++me.remainingLayouts;
|
||
|
++layout.layoutCount; // the number of whole layouts run for the layout
|
||
|
|
||
|
layout.ownerContext = ownerContext;
|
||
|
layout.beginCount = 0; // the number of beginLayout calls
|
||
|
layout.blockCount = 0; // the number of blocks set for the layout
|
||
|
layout.calcCount = 0; // the number of times calculate is called
|
||
|
layout.triggerCount = 0; // the number of triggers set for the layout
|
||
|
|
||
|
if (!layout.initialized) {
|
||
|
layout.initLayout();
|
||
|
}
|
||
|
|
||
|
layout.beginLayout(ownerContext);
|
||
|
} else {
|
||
|
++layout.beginCount;
|
||
|
|
||
|
if (!layout.running) {
|
||
|
// back into the mahem with this one:
|
||
|
++me.remainingLayouts;
|
||
|
layout.running = true;
|
||
|
|
||
|
if (layout.isComponentLayout) {
|
||
|
// this one is fun... if we call setProp('done', false) that would still
|
||
|
// trigger/unblock layouts, but what layouts are really looking for with
|
||
|
// this property is for it to go to true, not just be set to a value...
|
||
|
ownerContext.unsetProp('done');
|
||
|
}
|
||
|
|
||
|
// and it needs to be removed from the completion and/or finalize queues...
|
||
|
me.completionQueue.remove(layout);
|
||
|
me.finalizeQueue.remove(layout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
layout.beginLayoutCycle(ownerContext, firstTime);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Runs the layout calculations. This can be called only once on this object.
|
||
|
* @return {Boolean} True if all layouts were completed, false if not.
|
||
|
*/
|
||
|
run: function () {
|
||
|
var me = this,
|
||
|
flushed = false,
|
||
|
watchDog = me.cycleWatchDog;
|
||
|
|
||
|
me.purgeInvalidates();
|
||
|
me.flushInvalidates();
|
||
|
|
||
|
me.state = 1;
|
||
|
me.totalCount = me.layoutQueue.getCount();
|
||
|
|
||
|
// We may start with unflushed data placed by beginLayout calls. Since layouts may
|
||
|
// use setProp as a convenience, even in a write phase, we don't want to transition
|
||
|
// to a read phase with unflushed data since we can write it now "cheaply". Also,
|
||
|
// these value could easily be needed in the DOM in order to really get going with
|
||
|
// the calculations. In particular, fixed (configured) dimensions fall into this
|
||
|
// category.
|
||
|
me.flush();
|
||
|
|
||
|
// While we have layouts that have not completed...
|
||
|
while ((me.remainingLayouts || me.invalidQueue.length) && watchDog--) {
|
||
|
if (me.invalidQueue.length) {
|
||
|
me.flushInvalidates();
|
||
|
}
|
||
|
|
||
|
// if any of them can run right now, run them
|
||
|
if (me.runCycle()) {
|
||
|
flushed = false; // progress means we probably need to flush something
|
||
|
// but not all progress appears in the flushQueue (e.g. 'contentHeight')
|
||
|
} else if (!flushed) {
|
||
|
// as long as we are making progress, flush updates to the DOM and see if
|
||
|
// that triggers or unblocks any layouts...
|
||
|
me.flush();
|
||
|
flushed = true; // all flushed now, so more progress is required
|
||
|
|
||
|
me.flushLayouts('completionQueue', 'completeLayout');
|
||
|
} else if (!me.invalidQueue.length) {
|
||
|
// after a flush, we must make progress or something is WRONG
|
||
|
me.state = 2;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!(me.remainingLayouts || me.invalidQueue.length)) {
|
||
|
me.flush();
|
||
|
me.flushLayouts('completionQueue', 'completeLayout');
|
||
|
me.flushLayouts('finalizeQueue', 'finalizeLayout');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return me.runComplete();
|
||
|
},
|
||
|
|
||
|
runComplete: function () {
|
||
|
var me = this;
|
||
|
|
||
|
me.state = 2;
|
||
|
|
||
|
if (me.remainingLayouts) {
|
||
|
me.handleFailure();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
me.flush();
|
||
|
|
||
|
// Call finishedLayout on all layouts, but do not clear the queue.
|
||
|
me.flushLayouts('finishQueue', 'finishedLayout', true);
|
||
|
|
||
|
// Call notifyOwner on all layouts and then clear the queue.
|
||
|
me.flushLayouts('finishQueue', 'notifyOwner');
|
||
|
|
||
|
me.flush(); // in case any setProp calls were made
|
||
|
|
||
|
me.flushAnimations();
|
||
|
|
||
|
return true;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Performs one layout cycle by calling each layout in the layout queue.
|
||
|
* @return {Boolean} True if some progress was made, false if not.
|
||
|
* @protected
|
||
|
*/
|
||
|
runCycle: function () {
|
||
|
var me = this,
|
||
|
layouts = me.layoutQueue.clear(),
|
||
|
length = layouts.length,
|
||
|
i;
|
||
|
|
||
|
++me.cycleCount;
|
||
|
|
||
|
// This value is incremented by ContextItem#setProp whenever new values are set
|
||
|
// (thereby detecting forward progress):
|
||
|
me.progressCount = 0;
|
||
|
|
||
|
for (i = 0; i < length; ++i) {
|
||
|
me.runLayout(me.currentLayout = layouts[i]);
|
||
|
}
|
||
|
|
||
|
me.currentLayout = null;
|
||
|
|
||
|
return me.progressCount > 0;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Runs one layout as part of a cycle.
|
||
|
* @private
|
||
|
*/
|
||
|
runLayout: function (layout) {
|
||
|
var me = this,
|
||
|
ownerContext = me.getCmp(layout.owner);
|
||
|
|
||
|
layout.pending = false;
|
||
|
|
||
|
if (ownerContext.state.blocks) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// We start with the assumption that the layout will finish and if it does not, it
|
||
|
// must clear this flag. It turns out this is much simpler than knowing when a layout
|
||
|
// is done (100% correctly) when base classes and derived classes are collaborating.
|
||
|
// Knowing that some part of the layout is not done is much more obvious.
|
||
|
layout.done = true;
|
||
|
|
||
|
++layout.calcCount;
|
||
|
++me.calcCount;
|
||
|
|
||
|
layout.calculate(ownerContext);
|
||
|
|
||
|
if (layout.done) {
|
||
|
me.layoutDone(layout);
|
||
|
|
||
|
if (layout.completeLayout) {
|
||
|
me.queueCompletion(layout);
|
||
|
}
|
||
|
if (layout.finalizeLayout) {
|
||
|
me.queueFinalize(layout);
|
||
|
}
|
||
|
} else if (!layout.pending && !layout.invalid &&
|
||
|
!(layout.blockCount + layout.triggerCount - layout.firedTriggers)) { // jshint ignore:line
|
||
|
// A layout that is not done and has no blocks or triggers that will queue it
|
||
|
// automatically, must be queued now:
|
||
|
me.queueLayout(layout);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Set the size of a component, element or composite or an array of components or elements.
|
||
|
* @param {Ext.Component/Ext.Component[]/Ext.dom.Element/Ext.dom.Element[]/Ext.dom.CompositeElement} item
|
||
|
* The item(s) to size.
|
||
|
* @param {Number} width The new width to set (ignored if undefined or NaN).
|
||
|
* @param {Number} height The new height to set (ignored if undefined or NaN).
|
||
|
*/
|
||
|
setItemSize: function(item, width, height) {
|
||
|
var items = item,
|
||
|
len = 1,
|
||
|
contextItem, i;
|
||
|
|
||
|
// NOTE: we don't pre-check for validity because:
|
||
|
// - maybe only one dimension is valid
|
||
|
// - the diagnostics layer will track the setProp call to help find who is trying
|
||
|
// (but failing) to set a property
|
||
|
// - setProp already checks this anyway
|
||
|
|
||
|
if (item.isComposite) {
|
||
|
items = item.elements;
|
||
|
len = items.length;
|
||
|
item = items[0];
|
||
|
} else if (!item.dom && !item.el) { // array by process of elimination
|
||
|
len = items.length;
|
||
|
item = items[0];
|
||
|
}
|
||
|
// else len = 1 and items = item (to avoid error on "items[++i]")
|
||
|
|
||
|
for (i = 0; i < len; ) {
|
||
|
contextItem = this.get(item);
|
||
|
contextItem.setSize(width, height);
|
||
|
|
||
|
item = items[++i]; // this accomodation avoids making an array of 1
|
||
|
}
|
||
|
},
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
// Diagnostics
|
||
|
|
||
|
debugHooks: {
|
||
|
$enabled: false, // off by default
|
||
|
|
||
|
pageAnalyzerMode: true,
|
||
|
|
||
|
logOn: {
|
||
|
//boxParent: true,
|
||
|
//calculate: true,
|
||
|
//cancelComponent: true,
|
||
|
//cancelLayout: true,
|
||
|
//doInvalidate: true,
|
||
|
//flush: true,
|
||
|
//flushInvalidate: true,
|
||
|
//invalidate: true,
|
||
|
//initItem: true,
|
||
|
//layoutDone: true,
|
||
|
//queueLayout: true,
|
||
|
//resetLayout: true,
|
||
|
//runCycle: true,
|
||
|
//setProp: true,
|
||
|
0:0
|
||
|
},
|
||
|
|
||
|
//profileLayoutsByType: true,
|
||
|
|
||
|
//reportOnSuccess: true,
|
||
|
|
||
|
cancelComponent: function (comp) {
|
||
|
if (this.logOn.cancelComponent) {
|
||
|
Ext.log('cancelCmp: ', comp.id);
|
||
|
}
|
||
|
this.callParent(arguments);
|
||
|
},
|
||
|
|
||
|
cancelLayout: function (layout) {
|
||
|
if (this.logOn.cancelLayout) {
|
||
|
Ext.log('cancelLayout: ', this.getLayoutName(layout));
|
||
|
}
|
||
|
this.callParent(arguments);
|
||
|
},
|
||
|
|
||
|
callLayout: function (layout, methodName) {
|
||
|
var accum = this.accumByType[layout.type],
|
||
|
frame = accum && accum.enter();
|
||
|
|
||
|
this.callParent(arguments);
|
||
|
|
||
|
if (accum) {
|
||
|
frame.leave();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
checkRemainingLayouts: function () {
|
||
|
var me = this,
|
||
|
expected = 0,
|
||
|
key, layout;
|
||
|
|
||
|
for (key in me.layouts) {
|
||
|
layout = me.layouts[key];
|
||
|
|
||
|
if (me.layouts.hasOwnProperty(key) && layout.running) {
|
||
|
++expected;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (me.remainingLayouts !== expected) {
|
||
|
Ext.Error.raise({
|
||
|
msg: 'Bookkeeping error me.remainingLayouts'
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
|
||
|
flush: function () {
|
||
|
if (this.logOn.flush) {
|
||
|
var items = this.flushQueue;
|
||
|
Ext.log('--- Flush ', items && items.getCount());
|
||
|
}
|
||
|
|
||
|
return this.callParent(arguments);
|
||
|
},
|
||
|
|
||
|
flushInvalidates: function () {
|
||
|
if (this.logOn.flushInvalidate) {
|
||
|
Ext.log('>> flushInvalidates');
|
||
|
}
|
||
|
|
||
|
var ret = this.callParent(arguments);
|
||
|
|
||
|
if (this.logOn.flushInvalidate) {
|
||
|
Ext.log('<< flushInvalidates');
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
},
|
||
|
|
||
|
getCmp: function (target) {
|
||
|
var ret = this.callParent(arguments);
|
||
|
if (!ret.wrapsComponent) {
|
||
|
Ext.Error.raise({
|
||
|
msg: target.id + ' is not a component'
|
||
|
});
|
||
|
}
|
||
|
return ret;
|
||
|
},
|
||
|
|
||
|
getEl: function (parent, target) {
|
||
|
var ret = this.callParent(arguments);
|
||
|
if (ret && ret.wrapsComponent) {
|
||
|
Ext.Error.raise({
|
||
|
msg: parent.id + '/' + target.id + ' is a component (expected element)'
|
||
|
});
|
||
|
}
|
||
|
return ret;
|
||
|
},
|
||
|
|
||
|
getLayoutName: function (layout) {
|
||
|
return layout.owner.id + '<' + layout.type + '>';
|
||
|
},
|
||
|
|
||
|
layoutDone: function (layout) {
|
||
|
var me = this,
|
||
|
name = me.getLayoutName(layout);
|
||
|
|
||
|
if (me.logOn.layoutDone) {
|
||
|
Ext.log('layoutDone: ', name, ' ( ', me.remainingLayouts, ' running)');
|
||
|
}
|
||
|
|
||
|
if (!layout.running) {
|
||
|
Ext.Error.raise({
|
||
|
msg: name + ' is already done'
|
||
|
});
|
||
|
}
|
||
|
if (!me.remainingLayouts) {
|
||
|
Ext.Error.raise({
|
||
|
msg: name + ' finished but no layouts are running'
|
||
|
});
|
||
|
}
|
||
|
|
||
|
me.callParent(arguments);
|
||
|
},
|
||
|
|
||
|
layoutTreeHasFailures: function (layout, reported) {
|
||
|
var me = this;
|
||
|
|
||
|
function hasFailure (lo) {
|
||
|
var failure = !lo.done,
|
||
|
key, childLayout;
|
||
|
|
||
|
if (lo.done) {
|
||
|
for (key in me.layouts) {
|
||
|
if (me.layouts.hasOwnProperty(key)) {
|
||
|
childLayout = me.layouts[key];
|
||
|
|
||
|
if (childLayout.owner.ownerLayout === lo) {
|
||
|
if (hasFailure(childLayout)) {
|
||
|
failure = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return failure;
|
||
|
}
|
||
|
|
||
|
if (hasFailure(layout)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
function markReported (lo) {
|
||
|
var key, childLayout;
|
||
|
|
||
|
reported[lo.id] = 1;
|
||
|
|
||
|
for (key in me.layouts) {
|
||
|
if (me.layouts.hasOwnProperty(key)) {
|
||
|
childLayout = me.layouts[key];
|
||
|
|
||
|
if (childLayout.owner.ownerLayout === lo) {
|
||
|
markReported(childLayout);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
markReported(layout);
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
queueLayout: function (layout) {
|
||
|
if (layout.done || layout.blockCount || layout.pending) {
|
||
|
Ext.Error.raise({
|
||
|
msg: this.getLayoutName(layout) + ' should not be queued for layout'
|
||
|
});
|
||
|
}
|
||
|
if (this.logOn.queueLayout) {
|
||
|
Ext.log('Queue ', this.getLayoutName(layout));
|
||
|
}
|
||
|
return this.callParent(arguments);
|
||
|
},
|
||
|
|
||
|
reportLayoutResult: function (layout, reported) {
|
||
|
var me = this,
|
||
|
owner = layout.owner,
|
||
|
ownerContext = me.getCmp(owner),
|
||
|
blockedBy = [],
|
||
|
triggeredBy = [],
|
||
|
key, value, i, length, childLayout,
|
||
|
item, setBy, info;
|
||
|
|
||
|
reported[layout.id] = 1;
|
||
|
|
||
|
for (key in layout.blockedBy) {
|
||
|
if (layout.blockedBy.hasOwnProperty(key)) {
|
||
|
blockedBy.push(layout.blockedBy[key]);
|
||
|
}
|
||
|
}
|
||
|
blockedBy.sort();
|
||
|
|
||
|
for (key in me.triggersByLayoutId[layout.id]) {
|
||
|
if (me.triggersByLayoutId[layout.id].hasOwnProperty(key)) {
|
||
|
value = me.triggersByLayoutId[layout.id][key];
|
||
|
triggeredBy.push({ name: key, info: value });
|
||
|
}
|
||
|
}
|
||
|
triggeredBy.sort(function (a, b) {
|
||
|
return a.name < b.name ? -1 : (b.name < a.name ? 1 : 0);
|
||
|
});
|
||
|
|
||
|
Ext.log({indent: 1}, (layout.done ? '++' : '--'), me.getLayoutName(layout),
|
||
|
(ownerContext.isBoxParent ? ' [isBoxParent]' : ''),
|
||
|
(ownerContext.boxChildren ? ' - boxChildren: ' + ownerContext.state.boxesMeasured + '/' + ownerContext.boxChildren.length : ''),
|
||
|
ownerContext.boxParent ? (' - boxParent: ' + ownerContext.boxParent.id) : '',
|
||
|
' - size: ', ownerContext.widthModel.name, '/', ownerContext.heightModel.name);
|
||
|
|
||
|
if (!layout.done || me.reportOnSuccess) {
|
||
|
if (blockedBy.length) {
|
||
|
++Ext.log.indent;
|
||
|
Ext.log({indent: 1}, 'blockedBy: count=',layout.blockCount);
|
||
|
|
||
|
length = blockedBy.length;
|
||
|
for (i = 0; i < length; i++) {
|
||
|
Ext.log(blockedBy[i]);
|
||
|
}
|
||
|
|
||
|
Ext.log.indent -= 2;
|
||
|
}
|
||
|
if (triggeredBy.length) {
|
||
|
++Ext.log.indent;
|
||
|
Ext.log({indent: 1}, 'triggeredBy: count='+layout.triggerCount);
|
||
|
|
||
|
length = triggeredBy.length;
|
||
|
for (i = 0; i < length; i++) {
|
||
|
info = value.info || value;
|
||
|
item = info.item;
|
||
|
setBy = (item.setBy && item.setBy[info.name]) || '?';
|
||
|
|
||
|
value = triggeredBy[i];
|
||
|
|
||
|
Ext.log(
|
||
|
value.name,
|
||
|
' (',
|
||
|
item.props[info.name],
|
||
|
') dirty: ',
|
||
|
(item.dirty ? !!item.dirty[info.name] : false),
|
||
|
', setBy: ',
|
||
|
setBy
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Ext.log.indent -= 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (key in me.layouts) {
|
||
|
if (me.layouts.hasOwnProperty(key)) {
|
||
|
childLayout = me.layouts[key];
|
||
|
|
||
|
if (!childLayout.done && childLayout.owner.ownerLayout === layout) {
|
||
|
me.reportLayoutResult(childLayout, reported);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (key in me.layouts) {
|
||
|
if (me.layouts.hasOwnProperty(key)) {
|
||
|
childLayout = me.layouts[key];
|
||
|
|
||
|
if (childLayout.done && childLayout.owner.ownerLayout === layout) {
|
||
|
me.reportLayoutResult(childLayout, reported);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
--Ext.log.indent;
|
||
|
},
|
||
|
|
||
|
resetLayout: function (layout) {
|
||
|
var me = this,
|
||
|
type = layout.type,
|
||
|
name = me.getLayoutName(layout),
|
||
|
accum = me.accumByType[type],
|
||
|
frame;
|
||
|
|
||
|
if (me.logOn.resetLayout) {
|
||
|
Ext.log('resetLayout: ', name, ' ( ', me.remainingLayouts, ' running)');
|
||
|
}
|
||
|
|
||
|
if (!me.state) { // if (first time ... before run)
|
||
|
if (!accum && me.profileLayoutsByType) {
|
||
|
me.accumByType[type] = accum = Ext.Perf.get('layout_' + layout.type);
|
||
|
}
|
||
|
me.numByType[type] = (me.numByType[type] || 0) + 1;
|
||
|
}
|
||
|
|
||
|
frame = accum && accum.enter();
|
||
|
me.callParent(arguments);
|
||
|
if (accum) {
|
||
|
frame.leave();
|
||
|
}
|
||
|
|
||
|
me.checkRemainingLayouts();
|
||
|
},
|
||
|
|
||
|
round: function (t) {
|
||
|
return Math.round(t * 1000) / 1000;
|
||
|
},
|
||
|
|
||
|
run: function () {
|
||
|
var me = this,
|
||
|
ret, time, key, i, layout,
|
||
|
boxParent, children, n,
|
||
|
reported, unreported,
|
||
|
calcs, total,
|
||
|
calcsLength, calc;
|
||
|
|
||
|
me.accumByType = {};
|
||
|
me.calcsByType = {};
|
||
|
me.numByType = {};
|
||
|
me.timesByType = {};
|
||
|
me.triggersByLayoutId = {};
|
||
|
|
||
|
Ext.log.indentSize = 3;
|
||
|
Ext.log('==================== LAYOUT ====================');
|
||
|
|
||
|
time = Ext.perf.getTimestamp();
|
||
|
ret = me.callParent(arguments);
|
||
|
time = Ext.perf.getTimestamp() - time;
|
||
|
|
||
|
if (me.logOn.boxParent && me.boxParents) {
|
||
|
for (key in me.boxParents) {
|
||
|
if (me.boxParents.hasOwnProperty(key)) {
|
||
|
boxParent = me.boxParents[key];
|
||
|
children = boxParent.boxChildren;
|
||
|
n = children.length;
|
||
|
|
||
|
Ext.log('boxParent: ', boxParent.id);
|
||
|
for (i = 0; i < n; ++i) {
|
||
|
Ext.log(' --> ', children[i].id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ret) {
|
||
|
Ext.log('----------------- SUCCESS -----------------');
|
||
|
} else {
|
||
|
Ext.log(
|
||
|
{level: 'error' },
|
||
|
'----------------- FAILURE -----------------'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
for (key in me.layouts) {
|
||
|
if (me.layouts.hasOwnProperty(key)) {
|
||
|
layout = me.layouts[key];
|
||
|
|
||
|
if (layout.running) {
|
||
|
Ext.log.error('Layout left running: ', me.getLayoutName(layout));
|
||
|
}
|
||
|
if (layout.ownerContext) {
|
||
|
Ext.log.error('Layout left connected: ', me.getLayoutName(layout));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!ret || me.reportOnSuccess) {
|
||
|
reported = {};
|
||
|
unreported = 0;
|
||
|
|
||
|
for (key in me.layouts) {
|
||
|
if (me.layouts.hasOwnProperty(key)) {
|
||
|
layout = me.layouts[key];
|
||
|
|
||
|
if (me.items[layout.owner.el.id].isTopLevel) {
|
||
|
if (me.reportOnSuccess || me.layoutTreeHasFailures(layout, reported)) {
|
||
|
me.reportLayoutResult(layout, reported);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Just in case we missed any layouts...
|
||
|
for (key in me.layouts) {
|
||
|
if (me.layouts.hasOwnProperty(key)) {
|
||
|
layout = me.layouts[key];
|
||
|
|
||
|
if (!reported[layout.id]) {
|
||
|
if (!unreported) {
|
||
|
Ext.log('----- Unreported!! -----');
|
||
|
}
|
||
|
++unreported;
|
||
|
me.reportLayoutResult(layout, reported);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ext.log('Cycles: ', me.cycleCount, ', Flushes: ', me.flushCount,
|
||
|
', Calculates: ', me.calcCount, ' in ', me.round(time), ' msec');
|
||
|
|
||
|
Ext.log('Calculates by type:');
|
||
|
/*Ext.Object.each(me.numByType, function (type, total) {
|
||
|
Ext.log(type, ': ', total, ' in ', me.calcsByType[type], ' tries (',
|
||
|
Math.round(me.calcsByType[type] / total * 10) / 10, 'x) at ',
|
||
|
me.round(me.timesByType[type]), ' msec (avg ',
|
||
|
me.round(me.timesByType[type] / me.calcsByType[type]), ' msec)');
|
||
|
});*/
|
||
|
calcs = [];
|
||
|
for (key in me.numByType) {
|
||
|
if (me.numByType.hasOwnProperty(key)) {
|
||
|
total = me.numByType[key];
|
||
|
|
||
|
calcs.push({
|
||
|
type : key,
|
||
|
total : total,
|
||
|
calcs : me.calcsByType[key],
|
||
|
multiple : Math.round(me.calcsByType[key] / total * 10) / 10,
|
||
|
calcTime : me.round(me.timesByType[key]),
|
||
|
avgCalcTime: me.round(me.timesByType[key] / me.calcsByType[key])
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
calcs.sort(function (a,b) {
|
||
|
return b.calcTime - a.calcTime;
|
||
|
});
|
||
|
|
||
|
calcsLength = calcs.length;
|
||
|
for (i=0; i<calcsLength; i++) {
|
||
|
calc = calcs[i];
|
||
|
|
||
|
Ext.log(
|
||
|
calc.type,
|
||
|
': ',
|
||
|
calc.total,
|
||
|
' in ',
|
||
|
calc.calcs,
|
||
|
' tries (',
|
||
|
calc.multiple,
|
||
|
'x) at ',
|
||
|
calc.calcTime,
|
||
|
' msec (avg ',
|
||
|
calc.avgCalcTime,
|
||
|
' msec)'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
},
|
||
|
|
||
|
runCycle: function () {
|
||
|
if (this.logOn.runCycle) {
|
||
|
Ext.log('>>> Cycle ', this.cycleCount, ' (queue length: ', this.layoutQueue.length, ')');
|
||
|
}
|
||
|
|
||
|
return this.callParent(arguments);
|
||
|
},
|
||
|
|
||
|
runLayout: function (layout) {
|
||
|
var me = this,
|
||
|
type = layout.type,
|
||
|
accum = me.accumByType[type],
|
||
|
frame, ret, time;
|
||
|
|
||
|
if (me.logOn.calculate) {
|
||
|
Ext.log('-- calculate ', this.getLayoutName(layout));
|
||
|
}
|
||
|
|
||
|
frame = accum && accum.enter();
|
||
|
|
||
|
time = Ext.perf.getTimestamp();
|
||
|
ret = me.callParent(arguments);
|
||
|
time = Ext.perf.getTimestamp() - time;
|
||
|
if (accum) {
|
||
|
frame.leave();
|
||
|
}
|
||
|
|
||
|
me.calcsByType[type] = (me.calcsByType[type] || 0) + 1;
|
||
|
me.timesByType[type] = (me.timesByType[type] || 0) + time;
|
||
|
|
||
|
/* add a / to the front of this line to enable layout completion logging
|
||
|
if (layout.done) {
|
||
|
var ownerContext = me.getCmp(layout.owner),
|
||
|
props = ownerContext.props;
|
||
|
|
||
|
if (layout.isComponentLayout) {
|
||
|
Ext.log('complete ', layout.owner.id, ':', type, ' w=',props.width, ' h=', props.height);
|
||
|
} else {
|
||
|
Ext.log('complete ', layout.owner.id, ':', type, ' cw=',props.contentWidth, ' ch=', props.contentHeight);
|
||
|
}
|
||
|
}**/
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
} // End Diagnostics
|
||
|
});
|