| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708 | 
							- <!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-grid-PagingScroller'>/**
 
- </span> * Implements infinite scrolling of a grid, allowing users can scroll
 
-  * through thousands of records without the performance penalties of
 
-  * renderering all the records on screen at once. The grid should be
 
-  * bound to a *buffered* store with a pageSize specified.
 
-  *
 
-  * The number of rows rendered outside the visible area, and the
 
-  * buffering of pages of data from the remote server for immediate
 
-  * rendering upon scroll can be controlled by configuring the
 
-  * {@link Ext.grid.PagingScroller #verticalScroller}.
 
-  *
 
-  * You can tell it to create a larger table to provide more scrolling
 
-  * before a refresh is needed, and also to keep more pages of records
 
-  * in memory for faster refreshing when scrolling.
 
-  *
 
-  *     var myStore = Ext.create('Ext.data.Store', {
 
-  *         // ...
 
-  *         buffered: true,
 
-  *         pageSize: 100,
 
-  *         // ...
 
-  *     });
 
-  *
 
-  *     var grid = Ext.create('Ext.grid.Panel', {
 
-  *         // ...
 
-  *         autoLoad: true,
 
-  *         verticalScroller: {
 
-  *             trailingBufferZone: 200,  // Keep 200 records buffered in memory behind scroll
 
-  *             leadingBufferZone: 5000   // Keep 5000 records buffered in memory ahead of scroll
 
-  *         },
 
-  *         // ...
 
-  *     });
 
-  *
 
-  * ## Implementation notes
 
-  *
 
-  * This class monitors scrolling of the {@link Ext.view.Table
 
-  * TableView} within a {@link Ext.grid.Panel GridPanel} which is using
 
-  * a buffered store to only cache and render a small section of a very
 
-  * large dataset.
 
-  *
 
-  * **NB!** The GridPanel will instantiate this to perform monitoring,
 
-  * this class should never be instantiated by user code.  Always use the
 
-  * {@link Ext.panel.Table#verticalScroller verticalScroller} config.
 
-  *
 
-  */
 
- Ext.define('Ext.grid.PagingScroller', {
 
- <span id='Ext-grid-PagingScroller-cfg-percentageFromEdge'>    /**
 
- </span>     * @cfg
 
-      * @deprecated This config is now ignored.
 
-      */
 
-     percentageFromEdge: 0.35,
 
- <span id='Ext-grid-PagingScroller-cfg-numFromEdge'>    /**
 
- </span>     * @cfg
 
-      * The zone which causes a refresh of the rendered viewport. As soon as the edge
 
-      * of the rendered grid is this number of rows from the edge of the viewport, the view is moved.
 
-      */
 
-     numFromEdge: 2,
 
- <span id='Ext-grid-PagingScroller-cfg-trailingBufferZone'>    /**
 
- </span>     * @cfg
 
-      * The number of extra rows to render on the trailing side of scrolling
 
-      * **outside the {@link #numFromEdge}** buffer as scrolling proceeds.
 
-      */
 
-     trailingBufferZone: 5,
 
- <span id='Ext-grid-PagingScroller-cfg-leadingBufferZone'>    /**
 
- </span>     * @cfg
 
-      * The number of extra rows to render on the leading side of scrolling
 
-      * **outside the {@link #numFromEdge}** buffer as scrolling proceeds.
 
-      */
 
-     leadingBufferZone: 15,
 
- <span id='Ext-grid-PagingScroller-cfg-scrollToLoadBuffer'>    /**
 
- </span>     * @cfg
 
-      * This is the time in milliseconds to buffer load requests when scrolling the PagingScrollbar.
 
-      */
 
-     scrollToLoadBuffer: 200,
 
-     // private. Initial value of zero.
 
-     viewSize: 0,
 
-     // private. Start at default value
 
-     rowHeight: 21,
 
-     // private. Table extent at startup time
 
-     tableStart: 0,
 
-     tableEnd: 0,
 
-     constructor: function(config) {
 
-         var me = this;
 
-         me.variableRowHeight = config.variableRowHeight;
 
-         me.bindView(config.view);
 
-         Ext.apply(me, config);
 
-         me.callParent(arguments);
 
-     },
 
-     bindView: function(view) {
 
-         var me = this,
 
-             viewListeners = {
 
-                 scroll: {
 
-                     fn: me.onViewScroll,
 
-                     element: 'el',
 
-                     scope: me
 
-                 },
 
-                 render: me.onViewRender,
 
-                 resize: me.onViewResize,
 
-                 boxready: {
 
-                     fn: me.onViewResize,
 
-                     scope: me,
 
-                     single: true
 
-                 },
 
-                 // If there are variable row heights, then in beforeRefresh, we have to find a common
 
-                 // row so that we can synchronize the table's top position after the refresh.
 
-                 // Also flag whether the grid view has focus so that it can be refocused after refresh.
 
-                 beforerefresh: me.beforeViewRefresh,
 
-                 refresh: me.onViewRefresh,
 
-                 scope: me
 
-             },
 
-             storeListeners = {
 
-                 guaranteedrange: me.onGuaranteedRange,
 
-                 scope: me
 
-             },
 
-             gridListeners = {
 
-                 reconfigure: me.onGridReconfigure,
 
-                 scope: me
 
-             }, partner;
 
-         // If we need unbinding...
 
-         if (me.view) {
 
-             if (me.view.el) {
 
-                 me.view.el.un('scroll', me.onViewScroll, me); // un does not understand the element options
 
-             }
 
-             
 
-             partner = view.lockingPartner;
 
-             if (partner) {
 
-                 partner.un('refresh', me.onLockRefresh, me);
 
-             }
 
-             
 
-             me.view.un(viewListeners);
 
-             me.store.un(storeListeners);
 
-             if (me.grid) {
 
-                 me.grid.un(gridListeners);
 
-             }
 
-             delete me.view.refreshSize; // Remove the injected refreshSize implementation
 
-         }
 
-         me.view = view;
 
-         me.grid = me.view.up('tablepanel');
 
-         me.store = view.store;
 
-         if (view.rendered) {
 
-             me.viewSize = me.store.viewSize = Math.ceil(view.getHeight() / me.rowHeight) + me.trailingBufferZone + (me.numFromEdge * 2) + me.leadingBufferZone;
 
-         }
 
-         
 
-         partner = view.lockingPartner;
 
-         if (partner) {
 
-             partner.on('refresh', me.onLockRefresh, me);
 
-         }
 
-         me.view.mon(me.store.pageMap, {
 
-             scope: me,
 
-             clear: me.onCacheClear
 
-         });
 
-         // During scrolling we do not need to refresh the height - the Grid height must be set by config or layout in order to create a scrollable
 
-         // table just larger than that, so removing the layout call improves efficiency and removes the flicker when the
 
-         // HeaderContainer is reset to scrollLeft:0, and then resynced on the very next "scroll" event.
 
-         me.view.refreshSize = Ext.Function.createInterceptor(me.view.refreshSize, me.beforeViewrefreshSize, me);
 
- <span id='Ext-grid-PagingScroller-property-position'>        /**
 
- </span>         * @property {Number} position
 
-          * Current pixel scroll position of the associated {@link Ext.view.Table View}.
 
-          */
 
-         me.position = 0;
 
-         // We are created in View constructor. There won't be an ownerCt at this time.
 
-         if (me.grid) {
 
-             me.grid.on(gridListeners);
 
-         } else {
 
-             me.view.on({
 
-                 added: function() {
 
-                     me.grid = me.view.up('tablepanel');
 
-                     me.grid.on(gridListeners);
 
-                 },
 
-                 single: true
 
-             });
 
-         }
 
-         me.view.on(me.viewListeners = viewListeners);
 
-         me.store.on(storeListeners);
 
-     },
 
-     onCacheClear: function() {
 
-         var me = this;
 
-         // Do not do anything if view is not rendered, or if the reason for cache clearing is store destruction
 
-         if (me.view.rendered && !me.store.isDestroyed) {
 
-             // Temporarily disable scroll monitoring until the scroll event caused by any following *change* of scrollTop has fired.
 
-             // Otherwise it will attempt to process a scroll on a stale view
 
-             me.ignoreNextScrollEvent = me.view.el.dom.scrollTop !== 0;
 
-             me.view.el.dom.scrollTop = 0;
 
-             delete me.lastScrollDirection;
 
-             delete me.scrollOffset;
 
-             delete me.scrollProportion;
 
-         }
 
-     },
 
-     onGridReconfigure: function (grid) {
 
-         this.bindView(grid.view);
 
-     },
 
-     // Ensure that the stretcher element is inserted into the View as the first element.
 
-     onViewRender: function() {
 
-         var me = this,
 
-             view = me.view,
 
-             el = me.view.el,
 
-             stretcher;
 
-         me.stretcher = me.createStretcher(view);
 
-         
 
-         view = view.lockingPartner;
 
-         if (view) {
 
-             stretcher = me.stretcher;
 
-             me.stretcher = new Ext.CompositeElement(stretcher);
 
-             me.stretcher.add(me.createStretcher(view));
 
-         }
 
-     },
 
-     
 
-     createStretcher: function(view) {
 
-         var el = view.el;
 
-         el.setStyle('position', 'relative');
 
-         
 
-         return el.createChild({
 
-             style:{
 
-                 position: 'absolute',
 
-                 width: '1px',
 
-                 height: 0,
 
-                 top: 0,
 
-                 left: 0
 
-             }
 
-         }, el.dom.firstChild);
 
-     },
 
-     
 
-     onViewResize: function(view, width, height) {
 
-         var me = this,
 
-             newViewSize;
 
-         newViewSize = Math.ceil(height / me.rowHeight) + me.trailingBufferZone + (me.numFromEdge * 2) + me.leadingBufferZone;
 
-         if (newViewSize > me.viewSize) {
 
-             me.viewSize = me.store.viewSize = newViewSize;
 
-             me.handleViewScroll(me.lastScrollDirection || 1);
 
-         }
 
-     },
 
-     // Used for variable row heights. Try to find the offset from scrollTop of a common row
 
-     beforeViewRefresh: function() {
 
-         var me = this,
 
-             view = me.view,
 
-             rows,
 
-             direction;
 
-         // Refreshing can cause loss of focus.
 
-         me.focusOnRefresh = Ext.Element.getActiveElement === view.el.dom;
 
-         // Only need all this is variableRowHeight
 
-         if (me.variableRowHeight) {
 
-             direction = me.lastScrollDirection;
 
-             me.commonRecordIndex = undefined;
 
-             // If we are refreshing in response to a scroll,
 
-             // And we know where the previous start was,
 
-             // and we're not teleporting out of visible range
 
-             // and the view is not empty
 
-             if (direction && (me.previousStart !== undefined) && (me.scrollProportion === undefined) && (rows = view.getNodes()).length) {
 
-                 // We have scrolled downwards
 
-                 if (direction === 1) {
 
-                     // If the ranges overlap, we are going to be able to position the table exactly
 
-                     if (me.tableStart <= me.previousEnd) {
 
-                         me.commonRecordIndex = rows.length - 1;
 
-                     }
 
-                 }
 
-                 // We have scrolled upwards
 
-                 else if (direction === -1) {
 
-                     // If the ranges overlap, we are going to be able to position the table exactly
 
-                     if (me.tableEnd >= me.previousStart) {
 
-                         me.commonRecordIndex = 0;
 
-                     }
 
-                 }
 
-                 // Cache the old offset of the common row from the scrollTop
 
-                 me.scrollOffset = -view.el.getOffsetsTo(rows[me.commonRecordIndex])[1];
 
-                 // In the new table the common row is at a different index
 
-                 me.commonRecordIndex -= (me.tableStart - me.previousStart);
 
-             } else {
 
-                 me.scrollOffset = undefined;
 
-             }
 
-         }
 
-     },
 
-     onLockRefresh: function(view) {
 
-         view.table.dom.style.position = 'absolute';
 
-     },
 
-     // Used for variable row heights. Try to find the offset from scrollTop of a common row
 
-     // Ensure, upon each refresh, that the stretcher element is the correct height
 
-     onViewRefresh: function() {
 
-         var me = this,
 
-             store = me.store,
 
-             newScrollHeight,
 
-             view = me.view,
 
-             viewEl = view.el,
 
-             viewDom = viewEl.dom,
 
-             rows,
 
-             newScrollOffset,
 
-             scrollDelta,
 
-             table = view.table.dom,
 
-             tableTop,
 
-             scrollTop;
 
-         // Refresh causes loss of focus
 
-         if (me.focusOnRefresh) {
 
-             viewEl.focus();
 
-             me.focusOnRefresh = false;
 
-         }
 
-         // Scroll events caused by processing in here must be ignored, so disable for the duration
 
-         me.disabled = true;
 
-         // No scroll monitoring is needed if
 
-         //    All data is in view OR
 
-         //  Store is filtered locally.
 
-         //    - scrolling a locally filtered page is obv a local operation within the context of a huge set of pages 
 
-         //      so local scrolling is appropriate.
 
-         if (store.getCount() === store.getTotalCount() || (store.isFiltered() && !store.remoteFilter)) {
 
-             me.stretcher.setHeight(0);
 
-             me.position = viewDom.scrollTop = 0;
 
-             // Chrome's scrolling went crazy upon zeroing of the stretcher, and left the view's scrollTop stuck at -15
 
-             // This is the only thing that fixes that
 
-             me.setTablePosition('absolute');
 
-             // We remain disabled now because no scrolling is needed - we have the full dataset in the Store
 
-             return;
 
-         }
 
-         me.stretcher.setHeight(newScrollHeight = me.getScrollHeight());
 
-         scrollTop = viewDom.scrollTop;
 
-         // Flag to the refreshSize interceptor that regular refreshSize postprocessing should be vetoed.
 
-         me.isScrollRefresh = (scrollTop > 0);
 
-         // If we have had to calculate the store position from the pure scroll bar position,
 
-         // then we must calculate the table's vertical position from the scrollProportion
 
-         if (me.scrollProportion !== undefined) {
 
-             me.setTablePosition('absolute');
 
-             me.setTableTop((me.scrollProportion ? (newScrollHeight * me.scrollProportion) - (table.offsetHeight * me.scrollProportion) : 0) + 'px');
 
-         } else {
 
-             me.setTablePosition('absolute');
 
-             me.setTableTop((tableTop = (me.tableStart||0) * me.rowHeight) + 'px');
 
-             // ScrollOffset to a common row was calculated in beforeViewRefresh, so we can synch table position with how it was before
 
-             if (me.scrollOffset) {
 
-                 rows = view.getNodes();
 
-                 newScrollOffset = -viewEl.getOffsetsTo(rows[me.commonRecordIndex])[1];
 
-                 scrollDelta = newScrollOffset - me.scrollOffset;
 
-                 me.position = (viewDom.scrollTop += scrollDelta);
 
-             }
 
-             // If the table is not fully in view view, scroll to where it is in view.
 
-             // This will happen when the page goes out of view unexpectedly, outside the
 
-             // control of the PagingScroller. For example, a refresh caused by a remote sort or filter reverting
 
-             // back to page 1.
 
-             // Note that with buffered Stores, only remote sorting is allowed, otherwise the locally
 
-             // sorted page will be out of order with the whole dataset.
 
-             else if ((tableTop > scrollTop) || ((tableTop + table.offsetHeight) < scrollTop + viewDom.clientHeight)) {
 
-                 me.lastScrollDirection = -1;
 
-                 me.position = viewDom.scrollTop = tableTop;
 
-             }
 
-         }
 
-         // Re-enable upon function exit
 
-         me.disabled = false;
 
-     },
 
-     
 
-     setTablePosition: function(position) {
 
-         this.setViewTableStyle(this.view, 'position', position);
 
-     },
 
-     
 
-     setTableTop: function(top){
 
-         this.setViewTableStyle(this.view, 'top', top);
 
-     },
 
-     
 
-     setViewTableStyle: function(view, prop, value) {
 
-         view.el.child('table', true).style[prop] = value;
 
-         view = view.lockingPartner;
 
-         
 
-         if (view) {
 
-             view.el.child('table', true).style[prop] = value;
 
-         }
 
-     },
 
-     beforeViewrefreshSize: function() {
 
-         // Veto the refreshSize if the refresh is due to a scroll.
 
-         if (this.isScrollRefresh) {
 
-             // If we're vetoing refreshSize, attach the table DOM to the View's Flyweight.
 
-             this.view.table.attach(this.view.el.child('table', true));
 
-             return (this.isScrollRefresh = false);
 
-         }
 
-     },
 
-     onGuaranteedRange: function(range, start, end) {
 
-         var me = this,
 
-             ds = me.store;
 
-         // this should never happen
 
-         if (range.length && me.visibleStart < range[0].index) {
 
-             return;
 
-         }
 
-         // Cache last table position in dataset so that if we are using variableRowHeight,
 
-         // we can attempt to locate a common row to align the table on.
 
-         me.previousStart = me.tableStart;
 
-         me.previousEnd = me.tableEnd;
 
-         me.tableStart = start;
 
-         me.tableEnd = end;
 
-         ds.loadRecords(range, {
 
-             start: start
 
-         });
 
-     },
 
-     onViewScroll: function(e, t) {
 
-         var me = this,
 
-             view = me.view,
 
-             lastPosition = me.position;
 
-         me.position = view.el.dom.scrollTop;
 
-         // Flag set when the scrollTop is programatically set to zero upon cache clear.
 
-         // We must not attempt to process that as a scroll event.
 
-         if (me.ignoreNextScrollEvent) {
 
-             me.ignoreNextScrollEvent = false;
 
-             return;
 
-         }
 
-         // Only check for nearing the edge if we are enabled.
 
-         // If there is no paging to be done (Store's dataset is all in memory) we will be disabled.
 
-         if (!me.disabled) {
 
-             me.lastScrollDirection = me.position > lastPosition ? 1 : -1;
 
-             // Check the position so we ignore horizontal scrolling
 
-             if (lastPosition !== me.position) {
 
-                 me.handleViewScroll(me.lastScrollDirection);
 
-             }
 
-         }
 
-     },
 
-     handleViewScroll: function(direction) {
 
-         var me                = this,
 
-             store             = me.store,
 
-             view              = me.view,
 
-             viewSize          = me.viewSize,
 
-             totalCount        = store.getTotalCount(),
 
-             highestStartPoint = totalCount - viewSize,
 
-             visibleStart      = me.getFirstVisibleRowIndex(),
 
-             visibleEnd        = me.getLastVisibleRowIndex(),
 
-             el                = view.el.dom,
 
-             requestStart,
 
-             requestEnd;
 
-         // Only process if the total rows is larger than the visible page size
 
-         if (totalCount >= viewSize) {
 
-             // This is only set if we are using variable row height, and the thumb is dragged so that
 
-             // There are no remaining visible rows to vertically anchor the new table to.
 
-             // In this case we use the scrollProprtion to anchor the table to the correct relative
 
-             // position on the vertical axis.
 
-             me.scrollProportion = undefined;
 
-             // We're scrolling up
 
-             if (direction == -1) {
 
-                 // If table starts at record zero, we have nothing to do
 
-                 if (me.tableStart) {
 
-                     if (visibleStart !== undefined) {
 
-                         if (visibleStart < (me.tableStart + me.numFromEdge)) {
 
-                             requestStart = Math.max(0, visibleEnd + me.trailingBufferZone - viewSize);
 
-                         }
 
-                     }
 
-                     // The only way we can end up without a visible start is if, in variableRowHeight mode, the user drags
 
-                     // the thumb up out of the visible range. In this case, we have to estimate the start row index
 
-                     else {
 
-                         // If we have no visible rows to orientate with, then use the scroll proportion
 
-                         me.scrollProportion = el.scrollTop / (el.scrollHeight - el.clientHeight);
 
-                         requestStart = Math.max(0, totalCount * me.scrollProportion - (viewSize / 2) - me.numFromEdge - ((me.leadingBufferZone + me.trailingBufferZone) / 2));
 
-                     }
 
-                 }
 
-             }
 
-             // We're scrolling down
 
-             else {
 
-                 if (visibleStart !== undefined) {
 
-                     if (visibleEnd > (me.tableEnd - me.numFromEdge)) {
 
-                         requestStart = Math.max(0, visibleStart - me.trailingBufferZone);
 
-                     }
 
-                 }
 
-                 // The only way we can end up without a visible end is if, in variableRowHeight mode, the user drags
 
-                 // the thumb down out of the visible range. In this case, we have to estimate the start row index
 
-                 else {
 
-                     // If we have no visible rows to orientate with, then use the scroll proportion
 
-                     me.scrollProportion = el.scrollTop / (el.scrollHeight - el.clientHeight);
 
-                     requestStart = totalCount * me.scrollProportion - (viewSize / 2) - me.numFromEdge - ((me.leadingBufferZone + me.trailingBufferZone) / 2);
 
-                 }
 
-             }
 
-             // We scrolled close to the edge and the Store needs reloading
 
-             if (requestStart !== undefined) {
 
-                 // The calculation walked off the end; Request the highest possible chunk which starts on an even row count (Because of row striping)
 
-                 if (requestStart > highestStartPoint) {
 
-                     requestStart = highestStartPoint & ~1;
 
-                     requestEnd = totalCount - 1;
 
-                 }
 
-                 // Make sure first row is even to ensure correct even/odd row striping
 
-                 else {
 
-                     requestStart = requestStart & ~1;
 
-                     requestEnd = requestStart + viewSize - 1;
 
-                 }
 
-                 // If range is satsfied within the prefetch buffer, then just draw it from the prefetch buffer
 
-                 if (store.rangeCached(requestStart, requestEnd)) {
 
-                     me.cancelLoad();
 
-                     store.guaranteeRange(requestStart, requestEnd);
 
-                 }
 
-                 // Required range is not in the prefetch buffer. Ask the store to prefetch it.
 
-                 // We will recieve a guaranteedrange event when that is done.
 
-                 else {
 
-                     me.attemptLoad(requestStart, requestEnd);
 
-                 }
 
-             }
 
-         }
 
-     },
 
-     getFirstVisibleRowIndex: function() {
 
-         var me = this,
 
-             view = me.view,
 
-             scrollTop = view.el.dom.scrollTop,
 
-             rows,
 
-             count,
 
-             i,
 
-             rowBottom;
 
-         if (me.variableRowHeight) {
 
-             rows = view.getNodes();
 
-             count = rows.length;
 
-             if (!count) {
 
-                 return;
 
-             }
 
-             rowBottom = Ext.fly(rows[0]).getOffsetsTo(view.el)[1];
 
-             for (i = 0; i < count; i++) {
 
-                 rowBottom += rows[i].offsetHeight;
 
-                 // Searching for the first visible row, and off the bottom of the clientArea, then there's no visible first row!
 
-                 if (rowBottom > view.el.dom.clientHeight) {
 
-                     return;
 
-                 }
 
-                 // Return the index *within the total dataset* of the first visible row
 
-                 // We cannot use the loop index to offset from the table's start index because of possible intervening group headers.
 
-                 if (rowBottom > 0) {
 
-                     return view.getRecord(rows[i]).index;
 
-                 }
 
-             }
 
-         } else {
 
-             return Math.floor(scrollTop / me.rowHeight);
 
-         }
 
-     },
 
-     getLastVisibleRowIndex: function() {
 
-         var me = this,
 
-             store = me.store,
 
-             view = me.view,
 
-             clientHeight = view.el.dom.clientHeight,
 
-             rows,
 
-             count,
 
-             i,
 
-             rowTop;
 
-         if (me.variableRowHeight) {
 
-             rows = view.getNodes();
 
-             if (!rows.length) {
 
-                 return;
 
-             }
 
-             count = store.getCount() - 1;
 
-             rowTop = Ext.fly(rows[count]).getOffsetsTo(view.el)[1] + rows[count].offsetHeight;
 
-             for (i = count; i >= 0; i--) {
 
-                 rowTop -= rows[i].offsetHeight;
 
-                 // Searching for the last visible row, and off the top of the clientArea, then there's no visible last row!
 
-                 if (rowTop < 0) {
 
-                     return;
 
-                 }
 
-                 // Return the index *within the total dataset* of the last visible row.
 
-                 // We cannot use the loop index to offset from the table's start index because of possible intervening group headers.
 
-                 if (rowTop < clientHeight) {
 
-                     return view.getRecord(rows[i]).index;
 
-                 }
 
-             }
 
-         } else {
 
-             return me.getFirstVisibleRowIndex() + Math.ceil(clientHeight / me.rowHeight) + 1;
 
-         }
 
-     },
 
-     getScrollHeight: function() {
 
-         var me = this,
 
-             view   = me.view,
 
-             table,
 
-             firstRow,
 
-             store  = me.store,
 
-             deltaHeight = 0,
 
-             doCalcHeight = !me.hasOwnProperty('rowHeight');
 
-         if (me.variableRowHeight) {
 
-             table = me.view.table.dom;
 
-             if (doCalcHeight) {
 
-                 me.initialTableHeight = table.offsetHeight;
 
-                 me.rowHeight = me.initialTableHeight / me.store.getCount();
 
-             } else {
 
-                 deltaHeight = table.offsetHeight - me.initialTableHeight;
 
-                 // Store size has been bumped because of odd end row.
 
-                 if (store.getCount() > me.viewSize) {
 
-                     deltaHeight -= me.rowHeight;
 
-                 }
 
-             }
 
-         } else if (doCalcHeight) {
 
-             firstRow = view.el.down(view.getItemSelector());
 
-             if (firstRow) {
 
-                 me.rowHeight = firstRow.getHeight(false, true);
 
-             }
 
-         }
 
-         return Math.floor(store.getTotalCount() * me.rowHeight) + deltaHeight;
 
-     },
 
-     attemptLoad: function(start, end) {
 
-         var me = this;
 
-         if (me.scrollToLoadBuffer) {
 
-             if (!me.loadTask) {
 
-                 me.loadTask = new Ext.util.DelayedTask(me.doAttemptLoad, me, []);
 
-             }
 
-             me.loadTask.delay(me.scrollToLoadBuffer, me.doAttemptLoad, me, [start, end]);
 
-         } else {
 
-             me.store.guaranteeRange(start, end);
 
-         }
 
-     },
 
-     cancelLoad: function() {
 
-         if (this.loadTask) {
 
-             this.loadTask.cancel();
 
-         }
 
-     },
 
-     doAttemptLoad:  function(start, end) {
 
-         this.store.guaranteeRange(start, end);
 
-     },
 
-     destroy: function() {
 
-         var me = this,
 
-             scrollListener = me.viewListeners.scroll;
 
-         me.store.un({
 
-             guaranteedrange: me.onGuaranteedRange,
 
-             scope: me
 
-         });
 
-         me.view.un(me.viewListeners);
 
-         if (me.view.rendered) {
 
-             me.stretcher.remove();
 
-             me.view.el.un('scroll', scrollListener.fn, scrollListener.scope);
 
-         }
 
-     }
 
- });
 
- </pre>
 
- </body>
 
- </html>
 
 
  |