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>
|