| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-layout-Context'>/**</span> * 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 #runCycle} 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.AbstractComponent#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.AbstractComponent#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.AbstractComponent#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. *  * @protected */Ext.define('Ext.layout.Context', {    requires: [        'Ext.util.Queue',        'Ext.layout.ContextItem',        'Ext.layout.Layout',        'Ext.fx.Anim',        'Ext.fx.Manager'    ],    remainingLayouts: 0,<span id='Ext-layout-Context-property-state'>    /**</span>     * @property {Number} state One of these values:     *     *  - 0 - Before run     *  - 1 - Running     *  - 2 - Run complete     */    state: 0,    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 = {};<span id='Ext-layout-Context-property-layoutQueue'>        /**</span>         * @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.isDescendant(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,            collection, 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];        }    },<span id='Ext-layout-Context-method-flush'>    /**</span>     * 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;        }    },<span id='Ext-layout-Context-method-getCmp'>    /**</span>     * Returns the ContextItem for a component.     * @param {Ext.Component} cmp     */    getCmp: function (cmp) {        return this.getItem(cmp, cmp.el);    },<span id='Ext-layout-Context-method-getEl'>    /**</span>     * 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;        //<debug>        Ext.log('Layout run failed');        //</debug>        for (key in layouts) {            layout = layouts[key];            if (layouts.hasOwnProperty(key)) {                layout.running      = false;                layout.ownerContext = null;            }        }    },<span id='Ext-layout-Context-method-invalidate'>    /**</span>     * 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 {Ext.layout.ContextItem} ownerCtContext The ownerCt's ContextItem.     * @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,            componentChildrenDone, containerChildrenDone, containerLayoutDone,            firstTime, i, comp, item, items, length, componentLayout, layout,            invalidateOptions, token;        for (i = 0, length = isArray ? components.length : 1; i < length; ++i) {            comp = isArray ? components[i] : components;            if (comp.rendered && !comp.hidden) {                item = me.getCmp(comp);                componentLayout = comp.componentLayout;                firstTime = !componentLayout.ownerContext;                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 (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);                }                // Finish up the item-level processing that is based on the size model of                // the component.                token = item.initContinue(token);                // Start these state variables 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).                componentChildrenDone = containerChildrenDone = 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);                        componentChildrenDone = false;                    }                }                if (layout) {                    containerLayoutDone = false;                    layout.renderChildren();                    items = layout.getVisibleItems();                    if (items.length) {                        me.invalidate(items, true);                        containerChildrenDone = false;                    }                }                // Finish the processing that requires the size models of child items to                // be determined (and some misc other stuff).                item.initDone(token, componentChildrenDone, containerChildrenDone,                              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;    },    layoutDone: function (layout) {        var ownerContext = layout.ownerContext,            ownerCtContext;        layout.running = false;        // Once a component layout completes, we can mark it as "done" but we can also        // decrement the remainingChildLayouts property on the ownerCtContext. When that        // goes to 0, we can mark the ownerCtContext as "childrenDone".        if (layout.isComponentLayout) {            if (ownerContext.measuresBox) {                ownerContext.onBoxMeasured(); // be sure to release our boxParent            }            ownerContext.setProp('done', true);            ownerCtContext = ownerContext.ownerCtContext;            if (ownerCtContext) {                if (ownerContext.target.ownerLayout.isComponentLayout) {                    if (! --ownerCtContext.remainingComponentChildLayouts) {                        ownerCtContext.setProp('componentChildrenDone', true);                    }                } else {                    if (! --ownerCtContext.remainingContainerChildLayouts) {                        ownerCtContext.setProp('containerChildrenDone', true);                    }                }                if (! --ownerCtContext.remainingChildLayouts) {                    ownerCtContext.setProp('childrenDone', 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;        }    },<span id='Ext-layout-Context-method-queueAnimation'>    /**</span>     * 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);    },<span id='Ext-layout-Context-method-queueCompletion'>    /**</span>     * 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);    },<span id='Ext-layout-Context-method-queueFinalize'>    /**</span>     * 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);    },<span id='Ext-layout-Context-method-queueFlush'>    /**</span>     * 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;        };    },<span id='Ext-layout-Context-method-queueInvalidate'>    /**</span>     * 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.isDescendant(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.isDescendant(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) {            this.queueLayout(layout);        }    },<span id='Ext-layout-Context-method-queueLayout'>    /**</span>     * 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;    },<span id='Ext-layout-Context-method-resetLayout'>    /**</span>     * 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,            ownerCtContext;        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            // Count the children of each ownerCt so we can tell when they are all done:            if (layout.isComponentLayout && (ownerCtContext = ownerContext.ownerCtContext)) {                // This layout's ownerCt is in this run... The component associated with                // this layout (the "target") could be owned by the ownerCt's container                // layout or component layout (e.g. docked items)! To manage this, we keep                // two counters for these and one for the combined total:                if (ownerContext.target.ownerLayout.isComponentLayout) {                    ++ownerCtContext.remainingComponentChildLayouts;                } else {                    ++ownerCtContext.remainingContainerChildLayouts;                }                ++ownerCtContext.remainingChildLayouts;            }            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');                    // On subsequent resets we increment the child layout count properties                    // on ownerCtContext and clear 'childrenDone' and the appropriate other                    // indicator as we transition to 1:                    ownerCtContext = ownerContext.ownerCtContext;                    if (ownerCtContext) {                        if (ownerContext.target.ownerLayout.isComponentLayout) {                            if (++ownerCtContext.remainingComponentChildLayouts == 1) {                                ownerCtContext.unsetProp('componentChildrenDone');                            }                        } else {                            if (++ownerCtContext.remainingContainerChildLayouts == 1) {                                ownerCtContext.unsetProp('containerChildrenDone');                            }                        }                        if (++ownerCtContext.remainingChildLayouts == 1) {                            ownerCtContext.unsetProp('childrenDone');                        }                    }                }                // 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);    },<span id='Ext-layout-Context-method-run'>    /**</span>     * 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 = 100;        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 {                // 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;    },<span id='Ext-layout-Context-method-runCycle'>    /**</span>     * 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;    },<span id='Ext-layout-Context-method-runLayout'>    /**</span>     * 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)) {            // A layout that is not done and has no blocks or triggers that will queue it            // automatically, must be queued now:            me.queueLayout(layout);        }    },<span id='Ext-layout-Context-method-setItemSize'>    /**</span>     * 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}     * 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        }    }});</pre></body></html>
 |