1. #501
    Ext User
    Join Date
    Mar 2008
    Posts
    186
    Vote Rating
    0
    nctag is on a distinguished road

      0  

    Default


    @jmaisel
    I've had this issue in past. Try to check all of your css files. I know sometimes it's hard. Check whether all files that are needed are included. Then check whether all files are loaded correctly (Firebug!!!). Then you should be able to fix it. Take a look at the demo so you can see which css files you need. Take care of the inclusion order too.

    HTH

  2. #502
    Sencha User ThorstenSuckow's Avatar
    Join Date
    Sep 2007
    Location
    Aachen, Germany
    Posts
    597
    Vote Rating
    2
    ThorstenSuckow is on a distinguished road

      0  

    Default


    Quote Originally Posted by jmaisel View Post
    Hey....

    I'm having an issue with the Live Grid. I've copied and pasted the example page code pretty much verbatim, but when I spawn the window containing the grid, the grid appears to fetch the data, then resize itself incorrectly in the windows viewport.
    As nctag mentioned, this definitely looks like the css for the Livegrid has not been included - you can find the css file in resources/css.

    HTH


    Thorsten

  3. #503
    Ext User
    Join Date
    May 2009
    Posts
    7
    Vote Rating
    0
    jmaisel is on a distinguished road

      0  

    Default livegrid rendering issues

    livegrid rendering issues


    Quote Originally Posted by MindPatterns View Post
    As nctag mentioned, this definitely looks like the css for the Livegrid has not been included - you can find the css file in resources/css.

    HTH


    Thorsten
    Hey guys.

    I appreciate the feedback, but the css is certainly there. firebug says it's downloaded properly, etc. I've even tried cutting and pasting it into the page. There are no runtime errors from the js so those files seem to be there in the right place etc. Any chance this is a firefox on linux specific issue? I'm seriously at a loss. I was 99% sure that taking the example page code and integrating it with my app would work. Perhaps it's some other odd css issue.

    Again, any insight appreciated.

    Joe.

  4. #504
    Ext User
    Join Date
    May 2009
    Posts
    7
    Vote Rating
    0
    jmaisel is on a distinguished road

      0  

    Default livegrid rendering issues: solved, but another question.

    livegrid rendering issues: solved, but another question.


    Hey everyone...

    So, it appears that if I include all the grid files in 1 large file, it errors out for some reason. I assembled the larger include based on the order in the demo page. Can anyone tell me why this would cause it to fail?

    I have narrowed it down to this as the cause 100% and can provide the files necessary to duplicate the issue if anyone is interested, though I seriously can't understand why this would cause a problem.

    thanks,

    Joe

  5. #505
    Ext User
    Join Date
    Apr 2009
    Posts
    5
    Vote Rating
    0
    adamli is on a distinguished road

      0  

    Default Adopt Columns Size to new Window Size

    Adopt Columns Size to new Window Size


    How can I realise that all the columns in the Livegrid after Window(contained the grid) resize/maximalize would be adapted to new window size (all the column proportionally) like in normal grid ?

    Many thanks for response

  6. #506
    Sencha User ThorstenSuckow's Avatar
    Join Date
    Sep 2007
    Location
    Aachen, Germany
    Posts
    597
    Vote Rating
    2
    ThorstenSuckow is on a distinguished road

      0  

    Default


    Quote Originally Posted by jmaisel View Post
    Hey everyone...

    So, it appears that if I include all the grid files in 1 large file, it errors out for some reason. I assembled the larger include based on the order in the demo page. Can anyone tell me why this would cause it to fail?

    I have narrowed it down to this as the cause 100% and can provide the files necessary to duplicate the issue if anyone is interested, though I seriously can't understand why this would cause a problem.

    thanks,

    Joe
    Can you post your setup?

  7. #507
    Ext User
    Join Date
    May 2009
    Posts
    7
    Vote Rating
    0
    jmaisel is on a distinguished road

      0  

    Default 1 large include failure test

    1 large include failure test


    Hey...

    So, to rehash I'm on FF 3 .0.10 on Ubuntu. I'm using some smart logic to assemble my js includes on the fly into 1 large file, however doing so with the grid seems to break the grids rendering (as seen in my original screen cap). Once I realized that I made 1 static include file containing all the live grid js files in the same order as the script src tags:
    1. GridPanel.js
    2. GridView.js
    3. RowSelectionModel.js
    4. Store.js
    5. Toolbar.js
    6. JsonReader.js
    trying to include these files into 1 logical include with all my other js files, or even including it seperately into 1 large file (as seen below) breaks the layout and I get the rendering problem described in my original post. I tried with both my version of ext (2.2.1) and whichever version you have on the demo page. In either case the behavior is the same. Maybe I'm being super thick and I missed something obvious, but I don't think so. If I have then'll I'll be the happy recipient of an id10t award.

    Here's my code for validating that using 1 large include fails:

    first, the jsp:

    HTML Code:
    <html>
    <head>
    
        <title>Ext.ux.Livegrid</title>
    
        <!-- your usual ext stuff --> 
        <script type="text/javascript" src="livegrid/ext/ext-base.js"></script>
        <script type="text/javascript" src="livegrid/ext/ext-all.js"></script>
        <link rel="stylesheet" type="text/css" href="livegrid/ext/resources/css/ext-all.css"/>
             
        <!-- needed files for the ux  -->
        <link rel="stylesheet" type="text/css" href="livegrid/ext-ux-livegrid.css"/>
        
        <script type="text/javascript" src="livegrid/grid-all.js"></script>
        
        <!-- uncomment this, and comment out the above <script> and it works - ->
        <script type="text/javascript" src="livegrid/GridPanel.js"></script>
        <script type="text/javascript" src="livegrid/GridView.js"></script>
        <script type="text/javascript" src="livegrid/RowSelectionModel.js"></script>
    
        <script type="text/javascript" src="livegrid/Store.js"></script>
        <script type="text/javascript" src="livegrid/Toolbar.js"></script>
        <script type="text/javascript" src="livegrid/JsonReader.js"></script>
        <!- - -->
        
    </head>
    
    <body>
        
        <div style="padding-top:40px;">
        <input type="button" onclick="showMe()" value="Open up the grid example" />
        </div>
        
        <div id="content">
        </div>
        
    <script type="text/javascript">
    
    Ext.namespace('Ext.ux');
    
    Ext.ux.Livegrid = Ext.extend(Ext.ux.grid.livegrid.GridPanel, {
    
        initComponent : function()
        {
            var bufferedReader = new Ext.ux.grid.livegrid.JsonReader({
                root            : 'response.value.items',
                versionProperty : 'response.value.version',
                totalProperty   : 'response.value.total_count',
                id              : 'id'
              }, [ {
                 name : 'number_field', sortType : 'int'
              },{
                 name : 'string_field', sortType : 'string'
              },{
                 name : 'date_field',   sortType : 'int'
            }]);
    
            this.store = new Ext.ux.grid.livegrid.Store({
                autoLoad   : true,
                bufferSize : 300,
                reader     : bufferedReader,
                sortInfo   : {field: 'number_field', direction: 'ASC'},
                url        : 'grid-data.jsp'
            });
    
            this.selModel = new Ext.ux.grid.livegrid.RowSelectionModel();
    
            this.view = new Ext.ux.grid.livegrid.GridView({
                nearLimit      : 100,
                loadMask : {
                    msg : 'Please wait...'
                }
            });
    
            this.bbar = new Ext.ux.grid.livegrid.Toolbar({
                view        : this.view,
                displayInfo : true
            });
    
            Ext.ux.Livegrid.superclass.initComponent.call(this);
        }
    
    });
    
        function showMe()
        {
            var grid = new Ext.ux.Livegrid({
                enableDragDrop : false,
                cm             : new Ext.grid.ColumnModel([
                    new Ext.grid.RowNumberer({header : '#' }),
                    {header: "Number", align : 'left',   width: 160, sortable: true, dataIndex: 'number_field'},
                    {header: "String", align : 'left',   width: 160, sortable: true, dataIndex: 'string_field'},
                    {header: "Date",   align : 'right',  width: 160, sortable: true, dataIndex: 'date_field'}
                ]),
                loadMask       : {
                    msg : 'Loading...'
                },
                title          : 'Large table'
            });
    
            var w = new Ext.Window({
                title       : 'Ext.ux.Livegrid',
                maximizable : true,
                renderTo    : 'content',
                resizable   : true,
                layout      : 'fit',
                items       : [grid],
                width       : 500,
                height      : 400,
                pageX        : 100,
                pageY        : 100,
                tbar        : new Ext.Toolbar({
                    items : [
                        new Ext.Button({
                            text : 'Button 2'
                        })
                    ]
                })
            });
    
            w.show();
            //w.center();
        }
    
    </script>
    
    </body>
    </html>
    Now, the include file (grid-all.js):

    Code:
    /**
     * Ext.ux.grid.livegrid.GridPanel
     * Copyright (c) 2007-2008, http://www.siteartwork.de
     *
     * Ext.ux.grid.livegrid.GridPanel is licensed under the terms of the
     *                  GNU Open Source GPL 3.0
     * license.
     *
     * Commercial use is prohibited. Visit <http://www.siteartwork.de/livegrid>
    
     * if you need to obtain a commercial license.
     *
     * This program is free software: you can redistribute it and/or modify it under
     * the terms of the GNU General Public License as published by the Free Software
     * Foundation, either version 3 of the License, or any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
     * details.
     *
     * You should have received a copy of the GNU General Public License along with
     * this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
     *
     */
    
    Ext.namespace('Ext.ux.grid.livegrid');
    
    /**
     * @class Ext.ux.grid.livegrid.GridPanel
     * @extends Ext.grid.GridPanel
     * @constructor
     * @param {Object} config
     *
     * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
     */
    Ext.ux.grid.livegrid.GridPanel = Ext.extend(Ext.grid.GridPanel, {
    
        /**
         * Overriden to make sure the attached store loads only when the
         * grid has been fully rendered if, and only if the store's
         * "autoLoad" property is set to true.
         *
         */
        onRender : function(ct, position)
        {
            Ext.ux.grid.livegrid.GridPanel.superclass.onRender.call(this, ct, position);
    
            var ds = this.getStore();
    
            if (ds._autoLoad === true) {
                delete ds._autoLoad;
                ds.load();
            }
        },
    
        /**
         * Overriden since the original implementation checks for
         * getCount() of the store, not getTotalCount().
         *
         */
        walkCells : function(row, col, step, fn, scope)
        {
            var ds  = this.store;
            var _oF = ds.getCount;
    
            ds.getCount = ds.getTotalCount;
    
            var ret = Ext.ux.grid.livegrid.GridPanel.superclass.walkCells.call(this, row, col, step, fn, scope);
    
            ds.getCount = _oF;
    
            return ret;
        }
    
    });
    
    
    
    /**
     * Ext.ux.grid.livegrid.GridView
     * Copyright (c) 2007-2008, http://www.siteartwork.de
     *
     * Ext.ux.grid.livegrid.GridView is licensed under the terms of the
     *                  GNU Open Source GPL 3.0
     * license.
     *
     * Commercial use is prohibited. Visit <http://www.siteartwork.de/livegrid>
     * if you need to obtain a commercial license.
     *
     * This program is free software: you can redistribute it and/or modify it under
     * the terms of the GNU General Public License as published by the Free Software
     * Foundation, either version 3 of the License, or any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
     * details.
     *
     * You should have received a copy of the GNU General Public License along with
     * this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
     *
     */
    
    Ext.namespace('Ext.ux.grid.livegrid');
    
    /**
     * @class Ext.ux.grid.livegrid.GridView
     * @extends Ext.grid.GridView
     * @constructor
     * @param {Object} config
     *
     * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
     */
    Ext.ux.grid.livegrid.GridView = function(config) {
    
        this.addEvents({
            /**
             * @event beforebuffer
             * Fires when the store is about to buffer new data.
             * @param {Ext.ux.BufferedGridView} this
             * @param {Ext.data.Store} store The store
             * @param {Number} rowIndex
             * @param {Number} visibleRows
             * @param {Number} totalCount
             * @param {Number} options The options with which the buffer request was called
             */
            'beforebuffer' : true,
            /**
             * @event buffer
             * Fires when the store is finsihed buffering new data.
             * @param {Ext.ux.BufferedGridView} this
             * @param {Ext.data.Store} store The store
             * @param {Number} rowIndex
             * @param {Number} visibleRows
             * @param {Number} totalCount
             * @param {Object} options
             */
            'buffer' : true,
            /**
             * @event bufferfailure
             * Fires when buffering failed.
             * @param {Ext.ux.BufferedGridView} this
             * @param {Ext.data.Store} store The store
             * @param {Object} options The options the buffer-request was initiated with
             */
            'bufferfailure' : true,
            /**
             * @event cursormove
             * Fires when the the user scrolls through the data.
             * @param {Ext.ux.BufferedGridView} this
             * @param {Number} rowIndex The index of the first visible row in the
             *                          grid absolute to it's position in the model.
             * @param {Number} visibleRows The number of rows visible in the grid.
             * @param {Number} totalCount
             */
            'cursormove' : true
    
        });
    
        /**
         * @cfg {Number} scrollDelay The number of microseconds a call to the
         * onLiveScroll-lisener should be delayed when the scroll event fires
         */
    
        /**
         * @cfg {Number} bufferSize The number of records that will at least always
         * be available in the store for rendering. This value will be send to the
         * server as the <tt>limit</tt> parameter and should not change during the
         * lifetime of a grid component. Note: In a paging grid, this number would
         * indicate the page size.
         * The value should be set high enough to make a userfirendly scrolling
         * possible and should be greater than the sum of {nearLimit} and
         * {visibleRows}. Usually, a value in between 150 and 200 is good enough.
         * A lesser value will more often make the store re-request new data, while
         * a larger number will make loading times higher.
         */
    
        /**
         * @cfg {Number} nearLimit This value represents a near value that is responsible
         * for deciding if a request for new data is needed. The lesser the number, the
         * more often new data will be requested. The number should be set to a value
         * that lies in between 1/4 to 1/2 of the {bufferSize}.
         */
    
        /**
         * @cfg {Number} horizontalScrollOffset The height of a horizontal aligned
         * scrollbar.  The scrollbar is shown if the total width of all visible
         * columns exceeds the width of the grid component.
         * On Windows XP (IE7, FF2), this value defaults to 17.
         */
        this.horizontalScrollOffset = 17;
    
        /**
         * @cfg {Object} loadMaskConfig The config of the load mask that will be shown
         * by the view if a request for new data is underway.
         */
        this.loadMask = false;
    
        Ext.apply(this, config);
    
        this.templates = {};
        /**
         * The master template adds an addiiotnal scrollbar to make cursoring in the
         * data possible.
         */
        this.templates.master = new Ext.Template(
    
            '<div class="x-grid3" hidefocus="true"><div class="ext.ux.grid.livegrid-liveScroller"><div></div></div>',
                '<div class="x-grid3-viewport">',
                    '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset">{header}</div></div><div class="x-clear"></div></div>',
                    '<div class="x-grid3-scroller" style="overflow-y:hidden !important;"><div class="x-grid3-body">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
                "</div>",
                '<div class="x-grid3-resize-marker"> </div>',
                '<div class="x-grid3-resize-proxy"> </div>',
            "</div>"
        );
    
        // shorthands for often used parent classes
        this._gridViewSuperclass = Ext.ux.grid.livegrid.GridView.superclass;
    
        this._gridViewSuperclass.constructor.call(this);
    
    
    };
    
    
    Ext.extend(Ext.ux.grid.livegrid.GridView, Ext.grid.GridView, {
    
    // {{{ --------------------------properties-------------------------------------
    
        /**
         * Used to store the z-index of the mask that is used to show while buffering,
         * so the scrollbar can be displayed above of it.
         * @type {Number} _maskIndex
         */
        _maskIndex : 20001,
    
        /**
         * Stores the height of the header. Needed for recalculating scroller inset height.
         * @param {Number}
         */
        hdHeight : 0,
    
        /**
         * Indicates wether the last row in the grid is clipped and thus not fully display.
         * 1 if clipped, otherwise 0.
         * @param {Number}
         */
        rowClipped : 0,
    
    
        /**
         * This is the actual y-scroller that does control sending request to the server
         * based upon the position of the scrolling cursor.
         * @param {Ext.Element}
         */
        liveScroller : null,
    
        /**
         * This is the panel that represents the amount of data in a given repository.
         * The height gets computed via the total amount of records multiplied with
         * the fixed(!) row height
         * @param {native HTMLObject}
         */
        liveScrollerInset : null,
    
        /**
         * The <b>fixed</b> row height for <b>every</b> row in the grid. The value is
         * computed once the store has been loaded for the first time and used for
         * various calculations during the lifetime of the grid component, such as
         * the height of the scroller and the number of visible rows.
         * @param {Number}
         */
        rowHeight : -1,
    
        /**
         * Stores the number of visible rows that have to be rendered.
         * @param {Number}
         */
        visibleRows : 1,
    
        /**
         * Stores the last offset relative to a previously scroll action. This is
         * needed for deciding wether the user scrolls up or down.
         * @param {Number}
         */
        lastIndex : -1,
    
        /**
         * Stores the last visible row at position "0" in the table view before
         * a new scroll event was created and fired.
         * @param {Number}
         */
        lastRowIndex : 0,
    
        /**
         * Stores the value of the <tt>liveScroller</tt>'s <tt>scrollTop</tt> DOM
         * property.
         * @param {Number}
         */
        lastScrollPos : 0,
    
        /**
         * The current index of the row in the model that is displayed as the first
         * visible row in the view.
         * @param {Number}
         */
        rowIndex : 0,
    
        /**
        * Set to <tt>true</tt> if the store is busy with loading new data.
        * @param {Boolean}
        */
        isBuffering : false,
    
        /**
         * If a request for new data was made and the user scrolls to a new position
         * that lays not within the requested range of the new data, the queue will
         * hold the latest requested position. If the buffering succeeds and the value
         * of requestQueue is not within the range of the current buffer, data may be
         * re-requested.
         *
         * @param {Number}
         */
        requestQueue : -1,
    
        /**
         * The view's own load mask that will be shown when a request to data was made
         * and there are no rows in the buffer left to render.
         * @see {loadMaskConfig}
         * @param {Ext.LoadMask}
         */
        loadMask : null,
    
        /**
         * Set to <tt>true</tt> if a request for new data has been made while there
         * are still rows in the buffer that can be rendered before the request
         * finishes.
         * @param {Boolean}
         */
        isPrebuffering : false,
    // }}}
    
    // {{{ --------------------------public API methods-----------------------------
    
        /**
         * Resets the view to display the first row in the data model. This will
         * change the scrollTop property of the scroller and may trigger a request
         * to buffer new data, if the row index "0" is not within the buffer range and
         * forceReload is set to true.
         *
         * @param {Boolean} forceReload <tt>true</tt> to reload the buffers contents,
         *                              othwerwise <tt>false</tt>
    
         *
         * @return {Boolean} Whether the store loads after reset(true); returns false
         * if any of the attached beforeload listeners cancels the load-event
         */
        reset : function(forceReload)
        {
            if (forceReload === false) {
                this.ds.modified = [];
                //this.grid.selModel.clearSelections(true);
                this.rowIndex      = 0;
                this.lastScrollPos = 0;
                this.lastRowIndex = 0;
                this.lastIndex    = 0;
                this.adjustVisibleRows();
                this.adjustScrollerPos(-this.liveScroller.dom.scrollTop, true);
                this.showLoadMask(false);
                this.refresh(true);
                //this.replaceLiveRows(0, true);
                this.fireEvent('cursormove', this, 0,
                               Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped),
                               this.ds.totalLength);
                return false;
            } else {
    
                var params = {};
                var sInfo = this.ds.sortInfo;
    
                if (sInfo) {
                    params = {
                        dir  : sInfo.direction,
                        sort : sInfo.field
                    };
                }
    
                return this.ds.load({params : params});
            }
    
        },
    
    // {{{ ------------adjusted methods for applying custom behavior----------------
        /**
         * Overwritten so the {@link Ext.ux.grid.livegrid.DragZone} can be used
         * with this view implementation.
         *
         * Since detaching a previously created DragZone from a grid panel seems to
         * be impossible, a little workaround will tell the parent implementation
         * that drad/drop is not enabled for this view's grid, and right after that
         * the custom DragZone will be created, if neccessary.
         */
        renderUI : function()
        {
            var g = this.grid;
            var dEnabled = g.enableDragDrop || g.enableDrag;
    
            g.enableDragDrop = false;
            g.enableDrag     = false;
    
            this._gridViewSuperclass.renderUI.call(this);
    
            var g = this.grid;
    
            g.enableDragDrop = dEnabled;
            g.enableDrag     = dEnabled;
    
            if(dEnabled){
                this.dragZone = new Ext.ux.grid.livegrid.DragZone(g, {
                    ddGroup : g.ddGroup || 'GridDD'
                });
            }
    
            if (this.loadMask) {
                this.loadMask = new Ext.LoadMask(
                    this.mainBody.dom.parentNode.parentNode,
                    this.loadMask
                );
            }
        },
    
        /**
         * The extended implementation attaches an listener to the beforeload
         * event of the store of the grid. It is guaranteed that the listener will
         * only be executed upon reloading of the store, sorting and initial loading
         * of data. When the store does "buffer", all events are suspended and the
         * beforeload event will not be triggered.
         *
         * @param {Ext.grid.GridPanel} grid The grid panel this view is attached to
         */
        init: function(grid)
        {
            this._gridViewSuperclass.init.call(this, grid);
    
            grid.on('expand', this._onExpand, this);
        },
    
        initData : function(ds, cm)
        {
            if(this.ds){
                this.ds.un('bulkremove', this.onBulkRemove, this);
                this.ds.un('beforeload', this.onBeforeLoad, this);
            }
            if(ds){
                ds.on('bulkremove', this.onBulkRemove, this);
                ds.on('beforeload', this.onBeforeLoad, this);
            }
    
            this._gridViewSuperclass.initData.call(this, ds, cm);
        },
    
        /**
         * Only render the viewable rect of the table. The number of rows visible to
         * the user is defined in <tt>visibleRows</tt>.
         * This implementation does completely overwrite the parent's implementation.
         */
        // private
        renderBody : function()
        {
            var markup = this.renderRows(0, this.visibleRows-1);
            return this.templates.body.apply({rows: markup});
        },
    
        /**
         * Overriden so the renderer of the specific cells gets the index of the
         * row as available in the view passed (row's rowIndex property)-
         *
         */
        doRender : function(cs, rs, ds, startRow, colCount, stripe)
        {
            return this._gridViewSuperclass.doRender.call(
                this, cs, rs, ds, startRow + this.ds.bufferRange[0], colCount, stripe
            );
    
        },
    
        /**
         * Inits the DOM native elements for this component.
         * The properties <tt>liveScroller</tt> and <tt>liveScrollerInset</tt> will
         * be respected as provided by the master template.
         * The <tt>scroll</tt> listener for the <tt>liverScroller</tt> will also be
         * added here as the <tt>mousewheel</tt> listener.
         * This method overwrites the parents implementation.
         */
        // private
        initElements : function()
        {
            var E = Ext.Element;
    
            var el = this.grid.getGridEl().dom.firstChild;
            var cs = el.childNodes;
    
            this.el = new E(el);
    
            this.mainWrap = new E(cs[1]);
    
            // liveScroller and liveScrollerInset
            this.liveScroller       = new E(cs[0]);
            this.liveScrollerInset  = this.liveScroller.dom.firstChild;
            this.liveScroller.on('scroll', this.onLiveScroll,  this, {buffer : this.scrollDelay});
    
            var thd = this.mainWrap.dom.firstChild;
            this.mainHd = new E(thd);
    
            this.hdHeight = thd.offsetHeight;
    
            this.innerHd = this.mainHd.dom.firstChild;
            this.scroller = new E(this.mainWrap.dom.childNodes[1]);
            if(this.forceFit){
                this.scroller.setStyle('overflow-x', 'hidden');
            }
            this.mainBody = new E(this.scroller.dom.firstChild);
    
            // addd the mousewheel event to the table's body
            this.mainBody.on('mousewheel', this.handleWheel,  this);
    
            this.focusEl = new E(this.scroller.dom.childNodes[1]);
            this.focusEl.swallowEvent("click", true);
    
            this.resizeMarker = new E(cs[2]);
            this.resizeProxy = new E(cs[3]);
    
        },
    
        /**
         * Layouts the grid's view taking the scroller into account. The height
         * of the scroller gets adjusted depending on the total width of the columns.
         * The width of the grid view will be adjusted so the header and the rows do
         * not overlap the scroller.
         * This method will also compute the row-height based on the first row this
         * grid displays and will adjust the number of visible rows if a resize
         * of the grid component happened.
         * This method overwrites the parents implementation.
         */
        //private
        layout : function()
        {
            if(!this.mainBody){
                return; // not rendered
            }
            var g = this.grid;
            var c = g.getGridEl(), cm = this.cm,
                    expandCol = g.autoExpandColumn,
                    gv = this;
    
            var csize = c.getSize(true);
    
            // set vw to 19 to take scrollbar width into account!
            var vw = csize.width;
    
            if(vw < 20 || csize.height < 20){ // display: none?
                return;
            }
    
            if(g.autoHeight){
                this.scroller.dom.style.overflow = 'visible';
            }else{
                this.el.setSize(csize.width, csize.height);
    
                var hdHeight = this.mainHd.getHeight();
                var vh = csize.height - (hdHeight);
    
                this.scroller.setSize(vw, vh);
                if(this.innerHd){
                    this.innerHd.style.width = (vw)+'px';
                }
            }
    
            this.liveScroller.dom.style.top = this.hdHeight+"px";
            
            if(this.forceFit){
                if(this.lastViewWidth != vw){
                    this.fitColumns(false, false);
                    this.lastViewWidth = vw;
                }
            }else {
                this.autoExpand();
            }
    
            // adjust the number of visible rows and the height of the scroller.
            this.adjustVisibleRows();
            this.adjustBufferInset();
    
            this.onLayout(vw, vh);
        },
    
        /**
         * Overriden for Ext 2.2 to prevent call to focus Row.
         *
         */
        removeRow : function(row)
        {
            Ext.removeNode(this.getRow(row));
        },
    
        /**
         * Overriden for Ext 2.2 to prevent call to focus Row.
         * This method i s here for dom operations only - the passed arguments are the
         * index of the nodes in the dom, not in the model.
         *
         */
        removeRows : function(firstRow, lastRow)
        {
            var bd = this.mainBody.dom;
            for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
                Ext.removeNode(bd.childNodes[firstRow]);
            }
        },
    
    // {{{ ----------------------dom/mouse listeners--------------------------------
    
        /**
         * Tells the view to recalculate the number of rows displayable
         * and the buffer inset, when it gets expanded after it has been
         * collapsed.
         *
         */
        _onExpand : function(panel)
        {
            this.adjustVisibleRows();
            this.adjustBufferInset();
            this.adjustScrollerPos(this.rowHeight*this.rowIndex, true);
        },
    
        // private
        onColumnMove : function(cm, oldIndex, newIndex)
        {
            this.indexMap = null;
            this.replaceLiveRows(this.rowIndex, true);
            this.updateHeaders();
            this.updateHeaderSortState();
            this.afterMove(newIndex);
        },
    
    
        /**
         * Called when a column width has been updated. Adjusts the scroller height
         * and the number of visible rows wether the horizontal scrollbar is shown
         * or not.
         */
        onColumnWidthUpdated : function(col, w, tw)
        {
            this.adjustVisibleRows();
            this.adjustBufferInset();
        },
    
        /**
         * Called when the width of all columns has been updated. Adjusts the scroller
         * height and the number of visible rows wether the horizontal scrollbar is shown
         * or not.
         */
        onAllColumnWidthsUpdated : function(ws, tw)
        {
            this.adjustVisibleRows();
            this.adjustBufferInset();
        },
    
        /**
         * Callback for selecting a row. The index of the row is the absolute index
         * in the datamodel. If the row is not rendered, this method will do nothing.
         */
        // private
        onRowSelect : function(row)
        {
            if (row < this.rowIndex || row > this.rowIndex+this.visibleRows) {
                return;
            }
    
            this.addRowClass(row, "x-grid3-row-selected");
        },
    
        /**
         * Callback for deselecting a row. The index of the row is the absolute index
         * in the datamodel. If the row is not currently rendered in the view, this method
         * will do nothing.
         */
        // private
        onRowDeselect : function(row)
        {
            if (row < this.rowIndex || row > this.rowIndex+this.visibleRows) {
                return;
            }
    
            this.removeRowClass(row, "x-grid3-row-selected");
        },
    
    
    // {{{ ----------------------data listeners-------------------------------------
        /**
         * Called when the buffer gets cleared. Simply calls the updateLiveRows method
         * with the adjusted index and should force the store to reload
         */
        // private
        onClear : function()
        {
            this.reset(false);
        },
    
        /**
         * Callback for the "bulkremove" event of the attached datastore.
         *
         * @param {Ext.ux.grid.livegrid.Store} store
         * @param {Array} removedData
         *
         */
        onBulkRemove : function(store, removedData)
        {
            var record    = null;
            var index     = 0;
            var viewIndex = 0;
            var len       = removedData.length;
    
            var removedInView    = false;
            var removedAfterView = false;
            var scrollerAdjust   = 0;
    
            if (len == 0) {
                return;
            }
    
            var tmpRowIndex   = this.rowIndex;
            var removedBefore = 0;
            var removedAfter  = 0;
            var removedIn     = 0;
    
            for (var i = 0; i < len; i++) {
                record = removedData[i][0];
                index  = removedData[i][1];
    
                viewIndex = (index != Number.MIN_VALUE && index != Number.MAX_VALUE)
                          ? index + this.ds.bufferRange[0]
                          : index;
    
                if (viewIndex < this.rowIndex) {
                    removedBefore++;
                } else if (viewIndex >= this.rowIndex && viewIndex <= this.rowIndex+(this.visibleRows-1)) {
                    removedIn++;
                } else if (viewIndex >= this.rowIndex+this.visibleRows) {
                    removedAfter++;
                }
    
                this.fireEvent("beforerowremoved", this, viewIndex, record);
                this.fireEvent("rowremoved",       this, viewIndex, record);
            }
    
            var totalLength = this.ds.totalLength;
            this.rowIndex   = Math.max(0, Math.min(this.rowIndex - removedBefore, totalLength-(this.visibleRows-1)));
    
            this.lastRowIndex = this.rowIndex;
    
            this.adjustScrollerPos(-(removedBefore*this.rowHeight), true);
            this.updateLiveRows(this.rowIndex, true);
            this.adjustBufferInset();
            this.processRows(0, undefined, false);
    
        },
    
    
        /**
         * Callback for the underlying store's remove method. The current
         * implementation does only remove the selected row which record is in the
         * current store.
         *
         * @see onBulkRemove()
         */
        // private
        onRemove : function(ds, record, index)
        {
            this.onBulkRemove(ds, [[record, index]]);
        },
    
        /**
         * The callback for the underlying data store when new data was added.
         * If <tt>index</tt> equals to <tt>Number.MIN_VALUE</tt> or <tt>Number.MAX_VALUE</tt>, the
         * method can't tell at which position in the underlying data model the
         * records where added. However, if <tt>index</tt> equals to <tt>Number.MIN_VALUE</tt>,
         * the <tt>rowIndex</tt> property will be adjusted to <tt>rowIndex+records.length</tt>,
         * and the <tt>liveScroller</tt>'s properties get adjusted so it matches the
         * new total number of records of the underlying data model.
         * The same will happen to any records that get added at the store index which
         * is currently represented by the first visible row in the view.
         * Any other value will cause the method to compute the number of rows that
         * have to be (re-)painted and calling the <tt>insertRows</tt> method, if
         * neccessary.
         *
         * This method triggers the <tt>beforerowsinserted</tt> and <tt>rowsinserted</tt>
    
         * event, passing the indexes of the records as they may default to the
         * positions in the underlying data model. However, due to the fact that
         * any sort algorithm may have computed the indexes of the records, it is
         * not guaranteed that the computed indexes equal to the indexes of the
         * underlying data model.
         *
         * @param {Ext.ux.grid.livegrid.Store} ds The datastore that buffers records
         *                                       from the underlying data model
         * @param {Array} records An array containing the newly added
         *                        {@link Ext.data.Record}s
         * @param {Number} index The index of the position in the underlying
         *                       {@link Ext.ux.grid.livegrid.Store} where the rows
         *                       were added.
         */
        // private
        onAdd : function(ds, records, index)
        {
            var recordLen = records.length;
    
            // values of index which equal to Number.MIN_VALUE or Number.MAX_VALUE
            // indicate that the records were not added to the store. The component
            // does not know which index those records do have in the underlying
            // data model
            if (index == Number.MAX_VALUE || index == Number.MIN_VALUE) {
                this.fireEvent("beforerowsinserted", this, index, index);
                
                // if index equals to Number.MIN_VALUE, shift rows!
                if (index == Number.MIN_VALUE) {
    
                    this.rowIndex     = this.rowIndex + recordLen;
                    this.lastRowIndex = this.rowIndex;
    
                    this.adjustBufferInset();
                    this.adjustScrollerPos(this.rowHeight*recordLen, true);
    
                    this.fireEvent("rowsinserted", this, index, index, recordLen);
                    this.processRows(0, undefined, false);
                    // the cursor did virtually move
                    this.fireEvent('cursormove', this, this.rowIndex,
                                   Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped),
                                   this.ds.totalLength);
    
                    return;
                }
    
                this.adjustBufferInset();
                this.fireEvent("rowsinserted", this, index, index, recordLen);
                return;
            }
    
            // only insert the rows which affect the current view.
            var start = index+this.ds.bufferRange[0];
            var end   = start + (recordLen-1);
            var len   = this.getRows().length;
    
            var firstRow = 0;
            var lastRow  = 0;
    
            // rows would be added at the end of the rows which are currently
            // displayed, so fire the event, resize buffer and adjust visible
            // rows and return
            if (start > this.rowIndex+(this.visibleRows-1)) {
                this.fireEvent("beforerowsinserted", this, start, end);
                this.fireEvent("rowsinserted",       this, start, end, recordLen);
    
                this.adjustVisibleRows();
                this.adjustBufferInset();
    
            }
    
            // rows get added somewhere in the current view.
            else if (start >= this.rowIndex && start <= this.rowIndex+(this.visibleRows-1)) {
                firstRow = index;
                // compute the last row that would be affected of an insert operation
                lastRow  = index+(recordLen-1);
                this.lastRowIndex  = this.rowIndex;
                this.rowIndex      = (start > this.rowIndex) ? this.rowIndex : start;
    
                this.insertRows(ds, firstRow, lastRow);
    
                if (this.lastRowIndex != this.rowIndex) {
                    this.fireEvent('cursormove', this, this.rowIndex,
                                   Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped),
                                   this.ds.totalLength);
                }
    
                this.adjustVisibleRows();
                this.adjustBufferInset();
            }
    
            // rows get added before the first visible row, which would not affect any
            // rows to be re-rendered
            else if (start < this.rowIndex) {
                this.fireEvent("beforerowsinserted", this, start, end);
    
                this.rowIndex     = this.rowIndex+recordLen;
                this.lastRowIndex = this.rowIndex;
    
                this.adjustVisibleRows();
                this.adjustBufferInset();
    
                this.adjustScrollerPos(this.rowHeight*recordLen, true);
    
                this.fireEvent("rowsinserted", this, start, end, recordLen);
                this.processRows(0, undefined, true);
    
                this.fireEvent('cursormove', this, this.rowIndex,
                               Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped),
                               this.ds.totalLength);
            }
    
        },
    
    // {{{ ----------------------store listeners------------------------------------
        /**
         * This callback for the store's "beforeload" event will adjust the start
         * position and the limit of the data in the model to fetch. It is guaranteed
         * that this method will only be called when the store initially loads,
         * remeote-sorts or reloads.
         * All other load events will be suspended when the view requests buffer data.
         * See {updateLiveRows}.
         *
         * @param {Ext.data.Store} store The store the Grid Panel uses
         * @param {Object} options The configuration object for the proxy that loads
         *                         data from the server
         */
        onBeforeLoad : function(store, options)
        {
            options.params = options.params || {};
    
            var apply = Ext.apply;
    
            apply(options, {
                scope    : this,
                callback : function(){
                    this.reset(false);
                }
            });
    
            apply(options.params, {
                start    : 0,
                limit    : this.ds.bufferSize
            });
    
            return true;
        },
    
        /**
         * Method is used as a callback for the load-event of the attached data store.
         * Adjusts the buffer inset based upon the <tt>totalCount</tt> property
         * returned by the response.
         * Overwrites the parent's implementation.
         */
        onLoad : function(o1, o2, options)
        {
            this.adjustBufferInset();
        },
    
        /**
         * This will be called when the data in the store has changed, i.e. a
         * re-buffer has occured. If the table was not rendered yet, a call to
         * <tt>refresh</tt> will initially render the table, which DOM elements will
         * then be used to re-render the table upon scrolling.
         *
         */
        // private
        onDataChange : function(store)
        {
            this.updateHeaderSortState();
        },
    
        /**
         * A callback for the store when new data has been buffered successfully.
         * If the current row index is not within the range of the newly created
         * data buffer or another request to new data has been made while the store
         * was loading, new data will be re-requested.
         *
         * Additionally, if there are any rows that have been selected which were not
         * in the data store, the method will request the pending selections from
         * the grid's selection model and add them to the selections if available.
         * This is because the component assumes that a user who scrolls through the
         * rows and updates the view's buffer during scrolling, can check the selected
         * rows which come into the view for integrity. It is up to the user to
         * deselect those rows not matchuing the selection.
         * Additionally, if the version of the store changes during various requests
         * and selections are still pending, the versionchange event of the store
         * can delete the pending selections after a re-bufer happened and before this
         * method was called.
         *
         */
        // private
        liveBufferUpdate : function(records, options, success)
        {
            if (success === true) {
                this.fireEvent('buffer', this, this.ds, this.rowIndex,
                    Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped),
                    this.ds.totalLength,
                    options
                );
    
                this.isBuffering    = false;
                this.isPrebuffering = false;
                this.showLoadMask(false);
    
                // this is needed since references to records which have been unloaded
                // get lost when the store gets loaded with new data.
                // from the store
                this.grid.selModel.replaceSelections(records);
    
    
                if (this.isInRange(this.rowIndex)) {
                    this.replaceLiveRows(this.rowIndex, options.forceRepaint);
                } else {
                    this.updateLiveRows(this.rowIndex);
                }
    
                if (this.requestQueue >= 0) {
                    var offset = this.requestQueue;
                    this.requestQueue = -1;
                    this.updateLiveRows(offset);
                }
    
                return;
            } else {
                this.fireEvent('bufferfailure', this, this.ds, options);
            }
    
            this.requestQueue   = -1;
            this.isBuffering    = false;
            this.isPrebuffering = false;
            this.showLoadMask(false);
        },
    
    
    // {{{ ----------------------scroll listeners------------------------------------
        /**
         * Handles mousewheel event on the table's body. This is neccessary since the
         * <tt>liveScroller</tt> element is completely detached from the table's body.
         *
         * @param {Ext.EventObject} e The event object
         */
        handleWheel : function(e)
        {
            if (this.rowHeight == -1) {
                e.stopEvent();
                return;
            }
            var d = e.getWheelDelta();
    
            this.adjustScrollerPos(-(d*this.rowHeight));
    
            e.stopEvent();
        },
    
        /**
         * Handles scrolling through the grid. Since the grid is fixed and rows get
         * removed/ added subsequently, the only way to determine the actual row in
         * view is to measure the <tt>scrollTop</tt> property of the <tt>liveScroller</tt>'s
         * DOM element.
         *
         */
        onLiveScroll : function()
        {
            var scrollTop = this.liveScroller.dom.scrollTop;
    
            var cursor = Math.floor((scrollTop)/this.rowHeight);
    
            this.rowIndex = cursor;
            // the lastRowIndex will be set when refreshing the view has finished
            if (cursor == this.lastRowIndex) {
                return;
            }
    
            this.updateLiveRows(cursor);
    
            this.lastScrollPos = this.liveScroller.dom.scrollTop;
        },
    
    
    
    // {{{ --------------------------helpers----------------------------------------
    
        // private
        refreshRow : function(record)
        {
            var ds = this.ds, index;
            if(typeof record == 'number'){
                index = record;
                record = ds.getAt(index);
            }else{
                index = ds.indexOf(record);
            }
    
            var viewIndex = index + this.ds.bufferRange[0];
    
            if (viewIndex < this.rowIndex || viewIndex >= this.rowIndex + this.visibleRows) {
                this.fireEvent("rowupdated", this, viewIndex, record);
                return;
            }
    
            this.insertRows(ds, index, index, true);
            this.fireEvent("rowupdated", this, viewIndex, record);
        },
    
        /**
         * Overwritten so the rowIndex can be changed to the absolute index.
         *
         * If the third parameter equals to <tt>true</tt>, the method will also
         * repaint the selections.
         */
        // private
        processRows : function(startRow, skipStripe, paintSelections)
        {
            skipStripe = skipStripe || !this.grid.stripeRows;
            // we will always process all rows in the view
            startRow = 0;
            var rows = this.getRows();
            var cls = ' x-grid3-row-alt ';
            var cursor = this.rowIndex;
    
            var index      = 0;
            var selections = this.grid.selModel.selections;
            var ds         = this.ds;
            var row        = null;
            for(var i = startRow, len = rows.length; i < len; i++){
                index = i+cursor;
                row   = rows[i];
                // changed!
                row.rowIndex = index;
    
                if (paintSelections !== false) {
                    if (this.grid.selModel.isSelected(this.ds.getAt(index)) === true) {
                        this.addRowClass(index, "x-grid3-row-selected");
                    } else {
                        this.removeRowClass(index, "x-grid3-row-selected");
                    }
                    this.fly(row).removeClass("x-grid3-row-over");
                }
    
                if(!skipStripe){
                    var isAlt = ((index+1) % 2 == 0);
                    var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
                    if(isAlt == hasAlt){
                        continue;
                    }
                    if(isAlt){
                        row.className += " x-grid3-row-alt";
                    }else{
                        row.className = row.className.replace("x-grid3-row-alt", "");
                    }
                }
            }
        },
    
        /**
         * API only, since the passed arguments are the indexes in the buffer store.
         * However, the method will try to compute the indexes so they might match
         * the indexes of the records in the underlying data model.
         *
         */
        // private
        insertRows : function(dm, firstRow, lastRow, isUpdate)
        {
            var viewIndexFirst = firstRow + this.ds.bufferRange[0];
            var viewIndexLast  = lastRow  + this.ds.bufferRange[0];
    
            if (!isUpdate) {
                this.fireEvent("beforerowsinserted", this, viewIndexFirst, viewIndexLast);
            }
    
            // first off, remove the rows at the bottom of the view to match the
            // visibleRows value and to not cause any spill in the DOM
            if (isUpdate !== true && (this.getRows().length + (lastRow-firstRow)) >= this.visibleRows) {
                this.removeRows((this.visibleRows-1)-(lastRow-firstRow), this.visibleRows-1);
            } else if (isUpdate) {
                this.removeRows(viewIndexFirst-this.rowIndex, viewIndexLast-this.rowIndex);
            }
    
            // compute the range of possible records which could be drawn into the view without
            // causing any spill
            var lastRenderRow = (firstRow == lastRow)
                              ? lastRow
                              : Math.min(lastRow,  (this.rowIndex-this.ds.bufferRange[0])+(this.visibleRows-1));
    
            var html = this.renderRows(firstRow, lastRenderRow);
    
            var before = this.getRow(viewIndexFirst);
    
            if (before) {
                Ext.DomHelper.insertHtml('beforeBegin', before, html);
            } else {
                Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
            }
    
            // if a row is replaced, we need to set the row index for this
            // row
            if (isUpdate === true) {
                var rows   = this.getRows();
                var cursor = this.rowIndex;
                for (var i = 0, max_i = rows.length; i < max_i; i++) {
                    rows[i].rowIndex = cursor+i;
                }
            }
    
            if (!isUpdate) {
                this.fireEvent("rowsinserted", this, viewIndexFirst, viewIndexLast, (viewIndexLast-viewIndexFirst)+1);
                this.processRows(0, undefined, true);
            }
        },
    
        /**
         * Return the <TR> HtmlElement which represents a Grid row for the specified index.
         * The passed argument is assumed to be the absolute index and will get translated
         * to the index of the row that represents the data in the view.
         *
         * @param {Number} index The row index
         *
         * @return {null|HtmlElement} The <TR> element, or null if the row is not rendered
         * in the view.
         */
        getRow : function(row)
        {
            if (row-this.rowIndex < 0) {
                return null;
            }
    
            return this.getRows()[row-this.rowIndex];
        },
    
        /**
         * Returns the grid's <TD> HtmlElement at the specified coordinates.
         * Returns null if the specified row is not currently rendered.
         *
         * @param {Number} row The row index in which to find the cell.
         * @param {Number} col The column index of the cell.
         * @return {HtmlElement} The &lt;TD> at the specified coordinates.
         */
        getCell : function(row, col)
        {
            var row = this.getRow(row);
    
            return row
                   ? row.getElementsByTagName('td')[col]
                   : null;
        },
    
        /**
         * Focuses the specified cell.
         * @param {Number} row The row index
         * @param {Number} col The column index
         */
        focusCell : function(row, col, hscroll)
        {
            var xy = this.ensureVisible(row, col, hscroll);
    
            if (!xy) {
                return;
            }
    
            this.focusEl.setXY(xy);
    
            if(Ext.isGecko){
                this.focusEl.focus();
            }else{
                this.focusEl.focus.defer(1, this.focusEl);
            }
    
        },
    
        /**
         * Makes sure that the requested /row/col is visible in the viewport.
         * The method may invoke a request for new buffer data and triggers the
         * scroll-event of the <tt>liveScroller</tt> element.
         *
         */
        // private
        ensureVisible : function(row, col, hscroll)
        {
            if(typeof row != "number"){
                row = row.rowIndex;
            }
    
            if(row < 0 || row >= this.ds.totalLength){
                return;
            }
    
            col = (col !== undefined ? col : 0);
    
            var rowInd = row-this.rowIndex;
    
            if (this.rowClipped && row == this.rowIndex+this.visibleRows-1) {
                this.adjustScrollerPos(this.rowHeight );
            } else if (row >= this.rowIndex+this.visibleRows) {
                this.adjustScrollerPos(((row-(this.rowIndex+this.visibleRows))+1)*this.rowHeight);
            } else if (row <= this.rowIndex) {
                this.adjustScrollerPos((rowInd)*this.rowHeight);
            }
    
            var rowEl = this.getRow(row), cellEl;
    
            if(!rowEl){
                return;
            }
    
            if(!(hscroll === false && col === 0)){
                while(this.cm.isHidden(col)){
                    col++;
                }
                cellEl = this.getCell(row, col);
            }
    
            var c = this.scroller.dom;
    
            if(hscroll !== false){
                var cleft = parseInt(cellEl.offsetLeft, 10);
                var cright = cleft + cellEl.offsetWidth;
    
                var sleft = parseInt(c.scrollLeft, 10);
                var sright = sleft + c.clientWidth;
                if(cleft < sleft){
                    c.scrollLeft = cleft;
                }else if(cright > sright){
                    c.scrollLeft = cright-c.clientWidth;
                }
            }
    
    
            return cellEl ?
                Ext.fly(cellEl).getXY() :
                [c.scrollLeft+this.el.getX(), Ext.fly(rowEl).getY()];
        },
    
        /**
         * Return strue if the passed record is in the visible rect of this view.
         *
         * @param {Ext.data.Record} record
         *
         * @return {Boolean} true if the record is rendered in the view, otherwise false.
         */
        isRecordRendered : function(record)
        {
            var ind = this.ds.indexOf(record);
    
            if (ind >= this.rowIndex && ind < this.rowIndex+this.visibleRows) {
                return true;
            }
    
            return false;
        },
    
        /**
         * Checks if the passed argument <tt>cursor</tt> lays within a renderable
         * area. The area is renderable, if the sum of cursor and the visibleRows
         * property does not exceed the current upper buffer limit.
         *
         * If this method returns <tt>true</tt>, it's basically save to re-render
         * the view with <tt>cursor</tt> as the absolute position in the model
         * as the first visible row.
         *
         * @param {Number} cursor The absolute position of the row in the data model.
         *
         * @return {Boolean} <tt>true</tt>, if the row can be rendered, otherwise
         *                   <tt>false</tt>
    
         *
         */
        isInRange : function(rowIndex)
        {
            var lastRowIndex = Math.min(this.ds.totalLength-1,
                                        rowIndex + (this.visibleRows-1));
    
            return (rowIndex     >= this.ds.bufferRange[0]) &&
                   (lastRowIndex <= this.ds.bufferRange[1]);
        },
    
        /**
         * Calculates the bufferRange start index for a buffer request
         *
         * @param {Boolean} inRange If the index is within the current buffer range
         * @param {Number} index The index to use as a reference for the calculations
         * @param {Boolean} down Wether the calculation was requested when the user scrolls down
         */
        getPredictedBufferIndex : function(index, inRange, down)
        {
            if (!inRange) {
                if (index + this.ds.bufferSize >= this.ds.totalLength) {
                    return this.ds.totalLength - this.ds.bufferSize;
                }
                // we need at last to render the index + the visible Rows
                return Math.max(0, (index + this.visibleRows) - Math.round(this.ds.bufferSize/2));
            }
            if (!down) {
                return Math.max(0, (index-this.ds.bufferSize)+this.visibleRows);
            }
    
            if (down) {
                return Math.max(0, Math.min(index, this.ds.totalLength-this.ds.bufferSize));
            }
        },
    
    
        /**
         * Updates the table view. Removes/appends rows as needed and fetches the
         * cells content out of the available store. If the needed rows are not within
         * the buffer, the method will advise the store to update it's contents.
         *
         * The method puts the requested cursor into the queue if a previously called
         * buffering is in process.
         *
         * @param {Number} cursor The row's position, absolute to it's position in the
         *                        data model
         *
         */
        updateLiveRows: function(index, forceRepaint, forceReload)
        {
            var inRange = this.isInRange(index);
    
            if (this.isBuffering) {
                if (this.isPrebuffering) {
                    if (inRange) {
                        this.replaceLiveRows(index);
                    } else {
                        this.showLoadMask(true);
                    }
                }
    
                this.fireEvent('cursormove', this, index,
                               Math.min(this.ds.totalLength,
                               this.visibleRows-this.rowClipped),
                               this.ds.totalLength);
    
                this.requestQueue = index;
                return;
            }
    
            var lastIndex  = this.lastIndex;
            this.lastIndex = index;
            var inRange    = this.isInRange(index);
    
            var down = false;
    
            if (inRange && forceReload !== true) {
    
                // repaint the table's view
                this.replaceLiveRows(index, forceRepaint);
                // has to be called AFTER the rowIndex was recalculated
                this.fireEvent('cursormove', this, index,
                           Math.min(this.ds.totalLength,
                           this.visibleRows-this.rowClipped),
                           this.ds.totalLength);
                // lets decide if we can void this method or stay in here for
                // requesting a buffer update
                if (index > lastIndex) { // scrolling down
    
                    down = true;
                    var totalCount = this.ds.totalLength;
    
                    // while scrolling, we have not yet reached the row index
                    // that would trigger a re-buffer
                    if (index+this.visibleRows+this.nearLimit <= this.ds.bufferRange[1]) {
                        return;
                    }
    
                    // If we have already buffered the last range we can ever get
                    // by the queried data repository, we don't need to buffer again.
                    // This basically means that a re-buffer would only occur again
                    // if we are scrolling up.
                    if (this.ds.bufferRange[1]+1 >= totalCount) {
                        return;
                    }
                } else if (index < lastIndex) { // scrolling up
    
                    down = false;
                    // We are scrolling up in the first buffer range we can ever get
                    // Re-buffering would only occur upon scrolling down.
                    if (this.ds.bufferRange[0] <= 0) {
                        return;
                    }
    
                    // if we are scrolling up and we are moving in an acceptable
                    // buffer range, lets return.
                    if (index - this.nearLimit > this.ds.bufferRange[0]) {
                        return;
                    }
                } else {
                    return;
                }
    
                this.isPrebuffering = true;
            }
    
            // prepare for rebuffering
            this.isBuffering = true;
    
            var bufferOffset = this.getPredictedBufferIndex(index, inRange, down);
    
            if (!inRange) {
                this.showLoadMask(true);
            }
    
            this.ds.suspendEvents();
            var sInfo  = this.ds.sortInfo;
    
            var params = {};
            if (this.ds.lastOptions) {
                Ext.apply(params, this.ds.lastOptions.params);
            }
    
            params.start = bufferOffset;
            params.limit = this.ds.bufferSize;
    
            if (sInfo) {
                params.dir  = sInfo.direction;
                params.sort = sInfo.field;
            }
    
            var opts = {
                forceRepaint : forceRepaint,
                callback     : this.liveBufferUpdate,
                scope        : this,
                params       : params
            };
    
            this.fireEvent('beforebuffer', this, this.ds, index,
                Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped),
                this.ds.totalLength, opts
            );
    
            this.ds.load(opts);
            this.ds.resumeEvents();
        },
    
        /**
         * Shows this' view own load mask to indicate that a large amount of buffer
         * data was requested by the store.
         * @param {Boolean} show <tt>true</tt> to show the load mask, otherwise
         *                       <tt>false</tt>
         */
        showLoadMask : function(show)
        {
            if (this.loadMask == null) {
                if (show) {
                    this.loadMask = new Ext.LoadMask(
                        this.mainBody.dom.parentNode.parentNode,
                        this.loadMaskConfig
                    );
                } else {
                    return;
                }
            }
    
            if (show) {
                this.loadMask.show();
                this.liveScroller.setStyle('zIndex', this._maskIndex);
            } else {
                this.loadMask.hide();
                this.liveScroller.setStyle('zIndex', 1);
            }
        },
    
        /**
         * Renders the table body with the contents of the model. The method will
         * prepend/ append rows after removing from either the end or the beginning
         * of the table DOM to reduce expensive DOM calls.
         * It will also take care of rendering the rows selected, taking the property
         * <tt>bufferedSelections</tt> of the {@link BufferedRowSelectionModel} into
         * account.
         * Instead of calling this method directly, the <tt>updateLiveRows</tt> method
         * should be called which takes care of rebuffering if needed, since this method
         * will behave erroneous if data of the buffer is requested which may not be
         * available.
         *
         * @param {Number} cursor The position of the data in the model to start
         *                        rendering.
         *
         * @param {Boolean} forceReplace <tt>true</tt> for recomputing the DOM in the
         *                               view, otherwise <tt>false</tt>.
         */
        // private
        replaceLiveRows : function(cursor, forceReplace, processRows)
        {
            var spill = cursor-this.lastRowIndex;
    
            if (spill == 0 && forceReplace !== true) {
                return;
            }
    
            // decide wether to prepend or append rows
            // if spill is negative, we are scrolling up. Thus we have to prepend
            // rows. If spill is positive, we have to append the buffers data.
            var append = spill > 0;
    
            // abs spill for simplyfiying append/prepend calculations
            spill = Math.abs(spill);
    
            // adjust cursor to the buffered model index
            var bufferRange = this.ds.bufferRange;
            var cursorBuffer = cursor-bufferRange[0];
    
            // compute the last possible renderindex
            var lpIndex = Math.min(cursorBuffer+this.visibleRows-1, bufferRange[1]-bufferRange[0]);
            // we can skip checking for append or prepend if the spill is larger than
            // visibleRows. We can paint the whole rows new then-
            if (spill >= this.visibleRows || spill == 0) {
                this.mainBody.update(this.renderRows(cursorBuffer, lpIndex));
            } else {
                if (append) {
    
                    this.removeRows(0, spill-1);
    
                    if (cursorBuffer+this.visibleRows-spill <= bufferRange[1]-bufferRange[0]) {
                        var html = this.renderRows(
                            cursorBuffer+this.visibleRows-spill,
                            lpIndex
                        );
                        Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
    
                    }
    
                } else {
                    this.removeRows(this.visibleRows-spill, this.visibleRows-1);
                    var html = this.renderRows(cursorBuffer, cursorBuffer+spill-1);
                    Ext.DomHelper.insertHtml('beforeBegin', this.mainBody.dom.firstChild, html);
    
                }
            }
    
            if (processRows !== false) {
                this.processRows(0, undefined, true);
            }
            this.lastRowIndex = cursor;
        },
    
    
    
        /**
        * Adjusts the scroller height to make sure each row in the dataset will be
        * can be displayed, no matter which value the current height of the grid
        * component equals to.
        */
        // protected
        adjustBufferInset : function()
        {
            var liveScrollerDom = this.liveScroller.dom;
            var g = this.grid, ds = g.store;
            var c  = g.getGridEl();
            var elWidth = c.getSize().width;
    
            // hidden rows is the number of rows which cannot be
            // displayed and for which a scrollbar needs to be
            // rendered. This does also take clipped rows into account
            var hiddenRows = (ds.totalLength == this.visibleRows-this.rowClipped)
                           ? 0
                           : Math.max(0, ds.totalLength-(this.visibleRows-this.rowClipped));
    
            if (hiddenRows == 0) {
                this.scroller.setWidth(elWidth);
                liveScrollerDom.style.display = 'none';
                return;
            } else {
                this.scroller.setWidth(elWidth-this.scrollOffset);
                liveScrollerDom.style.display = '';
            }
    
            var scrollbar = this.cm.getTotalWidth()+this.scrollOffset > elWidth;
    
            // adjust the height of the scrollbar
            var contHeight = liveScrollerDom.parentNode.offsetHeight +
                             ((ds.totalLength > 0 && scrollbar)
                             ? - this.horizontalScrollOffset
                             : 0)
                             - this.hdHeight;
    
            liveScrollerDom.style.height = Math.max(contHeight, this.horizontalScrollOffset*2)+"px";
    
            if (this.rowHeight == -1) {
                return;
            }
    
            this.liveScrollerInset.style.height = (hiddenRows == 0 ? 0 : contHeight+(hiddenRows*this.rowHeight))+"px";
        },
    
        /**
         * Recomputes the number of visible rows in the table based upon the height
         * of the component. The method adjusts the <tt>rowIndex</tt> property as
         * needed, if the sum of visible rows and the current row index exceeds the
         * number of total data available.
         */
        // protected
        adjustVisibleRows : function()
        {
            if (this.rowHeight == -1) {
                if (this.getRows()[0]) {
                    this.rowHeight = this.getRows()[0].offsetHeight;
    
                    if (this.rowHeight <= 0) {
                        this.rowHeight = -1;
                        return;
                    }
    
                } else {
                    return;
                }
            }
    
    
            var g = this.grid, ds = g.store;
    
            var c     = g.getGridEl();
            var cm    = this.cm;
            var size  = c.getSize();
            var width = size.width;
            var vh    = size.height;
    
            var vw = width-this.scrollOffset;
            // horizontal scrollbar shown?
            if (cm.getTotalWidth() > vw) {
                // yes!
                vh -= this.horizontalScrollOffset;
            }
    
            vh -= this.mainHd.getHeight();
    
            var totalLength = ds.totalLength || 0;
    
            var visibleRows = Math.max(1, Math.floor(vh/this.rowHeight));
    
            this.rowClipped = 0;
            // only compute the clipped row if the total length of records
            // exceeds the number of visible rows displayable
            if (totalLength > visibleRows && this.rowHeight / 3 < (vh - (visibleRows*this.rowHeight))) {
                visibleRows = Math.min(visibleRows+1, totalLength);
                this.rowClipped = 1;
            }
    
            // if visibleRows   didn't change, simply void and return.
            if (this.visibleRows == visibleRows) {
                return;
            }
    
            this.visibleRows = visibleRows;
    
            // skip recalculating the row index if we are currently buffering.
            if (this.isBuffering) {
                return;
            }
    
            // when re-rendering, doe not take the clipped row into account
            if (this.rowIndex + (visibleRows-this.rowClipped) > totalLength) {
                this.rowIndex     = Math.max(0, totalLength-(visibleRows-this.rowClipped));
                this.lastRowIndex = this.rowIndex;
            }
    
            this.updateLiveRows(this.rowIndex, true);
        },
    
    
        adjustScrollerPos : function(pixels, suspendEvent)
        {
            if (pixels == 0) {
                return;
            }
            var liveScroller = this.liveScroller;
            var scrollDom    = liveScroller.dom;
    
            if (suspendEvent === true) {
                liveScroller.un('scroll', this.onLiveScroll, this);
            }
            this.lastScrollPos   = scrollDom.scrollTop;
            scrollDom.scrollTop += pixels;
    
            if (suspendEvent === true) {
                scrollDom.scrollTop = scrollDom.scrollTop;
                liveScroller.on('scroll', this.onLiveScroll, this, {buffer : this.scrollDelay});
            }
    
        }
    
    
    
    });
    
    /**
     * Ext.ux.grid.livegrid.RowSelectionModel
     * Copyright (c) 2007-2008, http://www.siteartwork.de
     *
     * Ext.ux.grid.livegrid.RowSelectionModel is licensed under the terms of the
     *                  GNU Open Source GPL 3.0
     * license.
     *
     * Commercial use is prohibited. Visit <http://www.siteartwork.de/livegrid>
    
     * if you need to obtain a commercial license.
     *
     * This program is free software: you can redistribute it and/or modify it under
     * the terms of the GNU General Public License as published by the Free Software
     * Foundation, either version 3 of the License, or any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
     * details.
     *
     * You should have received a copy of the GNU General Public License along with
     * this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
     *
     */
    
    Ext.namespace('Ext.ux.grid.livegrid');
    
    /**
     * @class Ext.ux.grid.livegrid.RowSelectionModel
     * @extends Ext.grid.RowSelectionModel
     * @constructor
     * @param {Object} config
     *
     * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
     */
    Ext.ux.grid.livegrid.RowSelectionModel = function(config) {
    
    
        this.addEvents({
            /**
             * The selection dirty event will be triggered in case records were
             * inserted/ removed at view indexes that may affect the current
             * selection ranges which are only represented by view indexes, but not
             * current record-ids
             */
            'selectiondirty' : true
        });
    
        Ext.apply(this, config);
    
        this.pendingSelections = {};
    
        Ext.ux.grid.livegrid.RowSelectionModel.superclass.constructor.call(this);
    
    };
    
    Ext.extend(Ext.ux.grid.livegrid.RowSelectionModel, Ext.grid.RowSelectionModel, {
    
    
     // private
        initEvents : function()
        {
            Ext.ux.grid.livegrid.RowSelectionModel.superclass.initEvents.call(this);
    
            this.grid.view.on('rowsinserted',    this.onAdd,            this);
            this.grid.store.on('selectionsload', this.onSelectionsLoad, this);
        },
    
        /**
         * Callback is called when a row gets removed in the view. The process to
         * invoke this method is as follows:
         *
         * <ul>
         *  <li>1. store.remove(record);</li>
         *  <li>2. view.onRemove(store, record, indexInStore, isUpdate)<br />
         *   [view triggers rowremoved event]</li>
    
         *  <li>3. this.onRemove(view, indexInStore, record)</li>
         * </ul>
         *
         * If r defaults to <tt>null</tt> and index is within the pending selections
         * range, the selectionchange event will be called, too.
         * Additionally, the method will shift all selections and trigger the
         * selectiondirty event if any selections are pending.
         *
         */
        onRemove : function(v, index, r)
        {
            var ranges           = this.getPendingSelections();
            var rangesLength     = ranges.length;
            var selectionChanged = false;
    
            // if index equals to Number.MIN_VALUE or Number.MAX_VALUE, mark current
            // pending selections as dirty
            if (index == Number.MIN_VALUE || index == Number.MAX_VALUE) {
    
                if (r) {
                    // if the record is part of the current selection, shift the selection down by 1
                    // if the index equals to Number.MIN_VALUE
                    if (this.isIdSelected(r.id) && index == Number.MIN_VALUE) {
                        // bufferRange already counted down when this method gets
                        // called
                        this.shiftSelections(this.grid.store.bufferRange[1], -1);
                    }
                    this.selections.remove(r);
                    selectionChanged = true;
                }
    
                // clear all pending selections that are behind the first
                // bufferrange, and shift all pending Selections that lay in front
                // front of the second bufferRange down by 1!
                if (index == Number.MIN_VALUE) {
                    this.clearPendingSelections(0, this.grid.store.bufferRange[0]);
                } else {
                    // clear pending selections that are in front of bufferRange[1]
                    this.clearPendingSelections(this.grid.store.bufferRange[1]);
                }
    
                // only fire the selectiondirty event if there were pendning ranges
                if (rangesLength != 0) {
                    this.fireEvent('selectiondirty', this, index, 1);
                }
    
            } else {
    
                selectionChanged = this.isIdSelected(r.id);
    
                // if the record was not part of the selection, return
                if (!selectionChanged) {
                    return;
                }
    
                this.selections.remove(r);
                //this.last = false;
                // if there are currently pending selections, look up the interval
                // to tell whether removing the record would mark the selection dirty
                if (rangesLength != 0) {
    
                    var startRange = ranges[0];
                    var endRange   = ranges[rangesLength-1];
                    if (index <= endRange || index <= startRange) {
                        this.shiftSelections(index, -1);
                        this.fireEvent('selectiondirty', this, index, 1);
                    }
                 }
    
            }
    
            if (selectionChanged) {
                this.fireEvent('selectionchange', this);
            }
        },
    
    
        /**
         * If records where added to the store, this method will work as a callback,
         * called by the views' rowsinserted event.
         * Selections will be shifted down if, and only if, the listeners for the
         * selectiondirty event will return <tt>true</tt>.
         *
         */
        onAdd : function(store, index, endIndex, recordLength)
        {
            var ranges       = this.getPendingSelections();
            var rangesLength = ranges.length;
    
            // if index equals to Number.MIN_VALUE or Number.MAX_VALUE, mark current
            // pending selections as dirty
            if ((index == Number.MIN_VALUE || index == Number.MAX_VALUE)) {
    
                if (index == Number.MIN_VALUE) {
                    // bufferRange already counted down when this method gets
                    // called
                    this.clearPendingSelections(0, this.grid.store.bufferRange[0]);
                    this.shiftSelections(this.grid.store.bufferRange[1], recordLength);
                } else {
                    this.clearPendingSelections(this.grid.store.bufferRange[1]);
                }
    
                // only fire the selectiondirty event if there were pendning ranges
                if (rangesLength != 0) {
                    this.fireEvent('selectiondirty', this, index, r);
                }
    
                return;
            }
    
            // it is safe to say that the selection is dirty when the inserted index
            // is less or equal to the first selection range index or less or equal
            // to the last selection range index
            var startRange = ranges[0];
            var endRange   = ranges[rangesLength-1];
            var viewIndex  = index;
            if (viewIndex <= endRange || viewIndex <= startRange) {
                this.fireEvent('selectiondirty', this, viewIndex, recordLength);
                this.shiftSelections(viewIndex, recordLength);
            }
        },
    
    
    
        /**
         * Shifts current/pending selections. This method can be used when rows where
         * inserted/removed and the selection model has to synchronize itself.
         */
        shiftSelections : function(startRow, length)
        {
            var index         = 0;
            var newIndex      = 0;
            var newRequests   = {};
    
            var ds            = this.grid.store;
            var storeIndex    = startRow-ds.bufferRange[0];
            var newStoreIndex = 0;
            var totalLength   = this.grid.store.totalLength;
            var rec           = null;
    
            //this.last = false;
    
            var ranges       = this.getPendingSelections();
            var rangesLength = ranges.length;
    
            if (rangesLength == 0) {
                return;
            }
    
            for (var i = 0; i < rangesLength; i++) {
                index = ranges[i];
    
                if (index < startRow) {
                    continue;
                }
    
                newIndex      = index+length;
                newStoreIndex = storeIndex+length;
                if (newIndex >= totalLength) {
                    break;
                }
    
                rec = ds.getAt(newStoreIndex);
                if (rec) {
                    this.selections.add(rec);
                } else {
                    newRequests[newIndex] = true;
                }
            }
    
            this.pendingSelections = newRequests;
        },
    
        /**
         *
         * @param {Array} records The records that have been loaded
         * @param {Array} ranges  An array representing the model index ranges the
         *                        reords have been loaded for.
         */
        onSelectionsLoad : function(store, records, ranges)
        {
            this.replaceSelections(records);
        },
    
        /**
         * Returns true if there is a next record to select
         * @return {Boolean}
         */
        hasNext : function()
        {
            return this.last !== false && (this.last+1) < this.grid.store.getTotalCount();
        },
    
        /**
         * Gets the number of selected rows.
         * @return {Number}
         */
        getCount : function()
        {
            return this.selections.length + this.getPendingSelections().length;
        },
    
        /**
         * Returns True if the specified row is selected.
         *
         * @param {Number/Record} record The record or index of the record to check
         * @return {Boolean}
         */
        isSelected : function(index)
        {
            if (typeof index == "number") {
                var orgInd = index;
                index = this.grid.store.getAt(orgInd);
                if (!index) {
                    var ind = this.getPendingSelections().indexOf(orgInd);
                    if (ind != -1) {
                        return true;
                    }
    
                    return false;
                }
            }
    
            var r = index;
            return (r && this.selections.key(r.id) ? true : false);
        },
    
    
        /**
         * Deselects a record.
         * The emthod assumes that the record is physically available, i.e.
         * pendingSelections will not be taken into account
         */
        deselectRecord : function(record, preventViewNotify)
        {
            if(this.locked) {
                return;
            }
    
            var isSelected = this.selections.key(record.id);
    
            if (!isSelected) {
                return;
            }
    
            var store = this.grid.store;
            var index = store.indexOfId(record.id);
    
            if (index == -1) {
                index = store.findInsertIndex(record);
                if (index != Number.MIN_VALUE && index != Number.MAX_VALUE) {
                    index += store.bufferRange[0];
                }
            } else {
                // just to make sure, though this should not be
                // set if the record was availablein the selections
                delete this.pendingSelections[index];
            }
    
            if (this.last == index) {
                this.last = false;
            }
    
            if (this.lastActive == index) {
                this.lastActive = false;
            }
    
            this.selections.remove(record);
    
            if(!preventViewNotify){
                this.grid.getView().onRowDeselect(index);
            }
    
            this.fireEvent("rowdeselect", this, index, record);
            this.fireEvent("selectionchange", this);
        },
    
        /**
         * Deselects a row.
         * @param {Number} row The index of the row to deselect
         */
        deselectRow : function(index, preventViewNotify)
        {
            if(this.locked) return;
            if(this.last == index){
                this.last = false;
            }
    
            if(this.lastActive == index){
                this.lastActive = false;
            }
            var r = this.grid.store.getAt(index);
    
            delete this.pendingSelections[index];
    
            if (r) {
                this.selections.remove(r);
            }
            if(!preventViewNotify){
                this.grid.getView().onRowDeselect(index);
            }
            this.fireEvent("rowdeselect", this, index, r);
            this.fireEvent("selectionchange", this);
        },
    
    
        /**
         * Selects a row.
         * @param {Number} row The index of the row to select
         * @param {Boolean} keepExisting (optional) True to keep existing selections
         */
        selectRow : function(index, keepExisting, preventViewNotify)
        {
            if(//this.last === index
               //||
               this.locked
               || index < 0
               || index >= this.grid.store.getTotalCount()) {
                return;
            }
    
            var r = this.grid.store.getAt(index);
    
            if(this.fireEvent("beforerowselect", this, index, keepExisting, r) !== false){
                if(!keepExisting || this.singleSelect){
                    this.clearSelections();
                }
    
                if (r) {
                    this.selections.add(r);
                    delete this.pendingSelections[index];
                } else {
                    this.pendingSelections[index] = true;
                }
    
                this.last = this.lastActive = index;
    
                if(!preventViewNotify){
                    this.grid.getView().onRowSelect(index);
                }
    
                this.fireEvent("rowselect", this, index, r);
                this.fireEvent("selectionchange", this);
            }
        },
    
        clearPendingSelections : function(startIndex, endIndex)
        {
            if (endIndex == undefined) {
                endIndex = Number.MAX_VALUE;
            }
    
            var newSelections = {};
    
            var ranges       = this.getPendingSelections();
            var rangesLength = ranges.length;
    
            var index = 0;
    
            for (var i = 0; i < rangesLength; i++) {
                index = ranges[i];
                if (index <= endIndex && index >= startIndex) {
                    continue;
                }
    
                newSelections[index] = true;
            }
    
            this.pendingSelections = newSelections;
        },
    
        /**
         * Replaces already set data with new data from the store if those
         * records can be found within this.selections or this.pendingSelections
         *
         * @param {Array} An array with records buffered by the store
         */
        replaceSelections : function(records)
        {
            if (!records || records.length == 0) {
                return;
            }
    
            var ds  = this.grid.store;
            var rec = null;
    
            var assigned     = [];
            var ranges       = this.getPendingSelections();
            var rangesLength = ranges.length
    
            var selections = this.selections;
            var index      = 0;
    
            for (var i = 0; i < rangesLength; i++) {
                index = ranges[i];
                rec   = ds.getAt(index);
                if (rec) {
                    selections.add(rec);
                    assigned.push(rec.id);
                    delete this.pendingSelections[index];
                }
            }
    
            var id  = null;
            for (i = 0, len = records.length; i < len; i++) {
                rec = records[i];
                id  = rec.id;
                if (assigned.indexOf(id) == -1 && selections.containsKey(id)) {
                    selections.add(rec);
                }
            }
    
        },
    
        getPendingSelections : function(asRange)
        {
            var index         = 1;
            var ranges        = [];
            var currentRange  = 0;
            var tmpArray      = [];
    
            for (var i in this.pendingSelections) {
                tmpArray.push(parseInt(i));
            }
    
            tmpArray.sort(function(o1,o2){
                if (o1 > o2) {
                    return 1;
                } else if (o1 < o2) {
                    return -1;
                } else {
                    return 0;
                }
            });
    
            if (!asRange) {
                return tmpArray;
            }
    
            var max_i = tmpArray.length;
    
            if (max_i == 0) {
                return [];
            }
    
            ranges[currentRange] = [tmpArray[0], tmpArray[0]];
            for (var i = 0, max_i = max_i-1; i < max_i; i++) {
                if (tmpArray[i+1] - tmpArray[i] == 1) {
                    ranges[currentRange][1] = tmpArray[i+1];
                } else {
                    currentRange++;
                    ranges[currentRange] = [tmpArray[i+1], tmpArray[i+1]];
                }
            }
    
            return ranges;
        },
    
        /**
         * Clears all selections.
         */
        clearSelections : function(fast)
        {
            if(this.locked) return;
            if(fast !== true){
                var ds  = this.grid.store;
                var s   = this.selections;
                var ind = -1;
                s.each(function(r){
                    ind = ds.indexOfId(r.id);
                    if (ind != -1) {
                        this.deselectRow(ind+ds.bufferRange[0]);
                    }
                }, this);
                s.clear();
    
                this.pendingSelections = {};
    
            }else{
                this.selections.clear();
                this.pendingSelections    = {};
            }
            this.last = false;
        },
    
    
        /**
         * Selects a range of rows. All rows in between startRow and endRow are also
         * selected.
         *
         * @param {Number} startRow The index of the first row in the range
         * @param {Number} endRow The index of the last row in the range
         * @param {Boolean} keepExisting (optional) True to retain existing selections
         */
        selectRange : function(startRow, endRow, keepExisting)
        {
            if(this.locked) {
                return;
            }
    
            if(!keepExisting) {
                this.clearSelections();
            }
    
            if (startRow <= endRow) {
                for(var i = startRow; i <= endRow; i++) {
                    this.selectRow(i, true);
                }
            } else {
                for(var i = startRow; i >= endRow; i--) {
                    this.selectRow(i, true);
                }
            }
    
        }
    
    });
    
    
    
    
    /**
     * Ext.ux.grid.livegrid.Store
     * Copyright (c) 2007-2008, http://www.siteartwork.de
     *
     * Ext.ux.grid.livegrid.Store is licensed under the terms of the
     *                  GNU Open Source GPL 3.0
     * license.
     *
     * Commercial use is prohibited. Visit <http://www.siteartwork.de/livegrid>
    
     * if you need to obtain a commercial license.
     *
     * This program is free software: you can redistribute it and/or modify it under
     * the terms of the GNU General Public License as published by the Free Software
     * Foundation, either version 3 of the License, or any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
     * details.
     *
     * You should have received a copy of the GNU General Public License along with
     * this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
     *
     */
    
    Ext.namespace('Ext.ux.grid.livegrid');
    
    /**
     * @class Ext.ux.grid.livegrid.Store
     * @extends Ext.data.Store
     *
     * The BufferedGridSore is a special implementation of a Ext.data.Store. It is used
     * for loading chunks of data from the underlying data repository as requested
     * by the Ext.ux.BufferedGridView. It's size is limited to the config parameter
     * bufferSize and is thereby guaranteed to never hold more than this amount
     * of records in the store.
     *
     * Requesting selection ranges:
     * ----------------------------
     * This store implementation has 2 Http-proxies: A data proxy for requesting data
     * from the server for displaying and another proxy to request pending selections:
     * Pending selections are represented by row indexes which have been selected but
     * which records have not yet been available in the store. The loadSelections method
     * will initiate a request to the data repository (same url as specified in the
     * url config parameter for the store) to fetch the pending selections. The additional
     * parameter send to the server is the "ranges" parameter, which will hold a json
     * encoded string representing ranges of row indexes to load from the data repository.
     * As an example, pending selections with the indexes 1,2,3,4,5,9,10,11,16 would
     * have to be translated to [1,5],[9,11],[16].
     * Please note, that by indexes we do not understand (primary) keys of the data,
     * but indexes as represented by the view. To get the ranges of pending selections,
     * you can use the getPendingSelections method of the BufferedRowSelectionModel, which
     * should be used as the default selection model of the grid.
     *
     * Version-property:
     * -----------------
     * This implementation does also introduce a new member called "version". The version
     * property will help you in determining if any pending selections indexes are still
     * valid or may have changed. This is needed to reduce the danger of data inconsitence
     * when you are requesting data from the server: As an example, a range of indexes must
     * be read from the server but may have been become invalid when the row represented
     * by the index is no longer available in teh underlying data store, caused by a
     * delete or insert operation. Thus, you have to take care of the version property
     * by yourself (server side) and change this value whenever a row was deleted or
     * inserted. You can specify the path to the version property in the BufferedJsonReader,
     * which should be used as the default reader for this store. If the store recognizes
     * a version change, it will fire the versionchange event. It is up to the user
     * to remove all selections which are pending, or use them anyway.
     *
     * Inserting data:
     * ---------------
     * Another thing to notice is the way a user inserts records into the data store.
     * A user should always provide a sortInfo for the grid, so the findInsertIndex
     * method can return a value that comes close to the value as it would have been
     * computed by the underlying store's sort algorithm. Whenever a record should be
     * added to the store, the insert index should be calculated and the used as the
     * parameter for the insert method. The findInsertIndex method will return a value
     * that equals to Number.MIN_VALUE or Number.MAX_VALUE if the added record would not
     * change the current state of the store. If that happens, this data is not available
     * in the store, and may be requested later on when a new request for new data is made.
     *
     * Sorting:
     * --------
     * remoteSort will always be set to true, no matter what value the user provides
     * using the config object.
     *
     * @constructor
     * Creates a new Store.
     * @param {Object} config A config object containing the objects needed for the Store to access data,
     * and read the data into Records.
     *
     * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
     */
    Ext.ux.grid.livegrid.Store = function(config) {
    
        config = config || {};
    
        // remoteSort will always be set to true.
        config.remoteSort = true;
    
        // we will intercept the autoLoad property and set it to false so we do not
        // load any contents of the store before the View has not fully initialized
        // itself. if autoLoad was set to true, the Ext.ux.grid.livegrid.GridPanel
        // will take care of loading the store once it has been rendered
        this._autoLoad  = config.autoLoad ? true : false;
        config.autoLoad = false;
    
        this.addEvents(
             /**
              * @event bulkremove
              * Fires when a bulk remove operation was finished.
              * @param {Ext.ux.BufferedGridStore} this
              * @param {Array} An array with the records that have been removed.
              * The values for each array index are
              * record - the record that was removed
              * index - the index of the removed record in the store
              */
            'bulkremove',
             /**
              * @event versionchange
              * Fires when the version property has changed.
              * @param {Ext.ux.BufferedGridStore} this
              * @param {String} oldValue
              * @param {String} newValue
              */
            'versionchange',
             /**
              * @event beforeselectionsload
              * Fires before the store sends a request for ranges of records to
              * the server.
              * @param {Ext.ux.BufferedGridStore} this
              * @param {Array} ranges
              */
            'beforeselectionsload',
             /**
              * @event selectionsload
              * Fires when selections have been loaded.
              * @param {Ext.ux.BufferedGridStore} this
              * @param {Array} records An array containing the loaded records from
              * the server.
              * @param {Array} ranges An array containing the ranges of indexes this
              * records may represent.
              */
            'selectionsload'
        );
    
        Ext.ux.grid.livegrid.Store.superclass.constructor.call(this, config);
    
        this.totalLength = 0;
    
    
        /**
         * The array represents the range of rows available in the buffer absolute to
         * the indexes of the data model. Initialized with  [-1, -1] which tells that no
         * records are currrently buffered
         * @param {Array}
         */
        this.bufferRange = [-1, -1];
    
        this.on('clear', function (){
            this.bufferRange = [-1, -1];
        }, this);
    
        if(this.url && !this.selectionsProxy){
            this.selectionsProxy = new Ext.data.HttpProxy({url: this.url});
        }
    
    };
    
    Ext.extend(Ext.ux.grid.livegrid.Store, Ext.data.Store, {
    
        /**
         * The version of the data in the store. This value is represented by the
         * versionProperty-property of the BufferedJsonReader.
         * @property
         */
        version : null,
    
        /**
         * Inserts a record at the position as specified in index.
         * If the index equals to Number.MIN_VALUE or Number.MAX_VALUE, the record will
         * not be added to the store, but still fire the add-event to indicate that
         * the set of data in the underlying store has been changed.
         * If the index equals to 0 and the length of data in the store equals to
         * bufferSize, the add-event will be triggered with Number.MIN_VALUE to
         * indicate that a record has been prepended. If the index equals to
         * bufferSize, the method will assume that the record has been appended and
         * trigger the add event with index set to Number.MAX_VALUE.
         *
         * Note:
         * -----
         * The index parameter is not a view index, but a value in the range of
         * [0, this.bufferSize].
         *
         * You are strongly advised to not use this method directly. Instead, call
         * findInsertIndex wirst and use the return-value as the first parameter for
         * for this method.
         */
        insert : function(index, records)
        {
            // hooray for haskell!
            records = [].concat(records);
    
            index = index >= this.bufferSize ? Number.MAX_VALUE : index;
    
            if (index == Number.MIN_VALUE || index == Number.MAX_VALUE) {
                var l = records.length;
                if (index == Number.MIN_VALUE) {
                    this.bufferRange[0] += l;
                    this.bufferRange[1] += l;
                }
    
                this.totalLength += l;
                this.fireEvent("add", this, records, index);
                return;
            }
    
            var split = false;
            var insertRecords = records;
            if (records.length + index >= this.bufferSize) {
                split = true;
                insertRecords = records.splice(0, this.bufferSize-index)
            }
            this.totalLength += insertRecords.length;
    
            // if the store was loaded without data and the bufferRange
            // has to be filled first
            if (this.bufferRange[0] <= -1) {
                this.bufferRange[0] = 0;
            }
            if (this.bufferRange[1] < (this.bufferSize-1)) {
                this.bufferRange[1] = Math.min(this.bufferRange[1] + insertRecords.length, this.bufferSize-1);
            }
    
            for (var i = 0, len = insertRecords.length; i < len; i++) {
                this.data.insert(index, insertRecords[i]);
                insertRecords[i].join(this);
            }
    
            while (this.getCount() > this.bufferSize) {
                this.data.remove(this.data.last());
            }
    
            this.fireEvent("add", this, insertRecords, index);
    
            if (split == true) {
                this.fireEvent("add", this, records, Number.MAX_VALUE);
            }
        },
    
        /**
         * Remove a Record from the Store and fires the remove event.
         *
         * This implementation will check for the appearance of the record id
         * in the store. The record to be removed does not neccesarily be bound
         * to the instance of this store.
         * If the record is not within the store, the method will try to guess it's
         * index by calling findInsertIndex.
         *
         * Please note that this method assumes that the records that's about to
         * be removed from the store does belong to the data within the store or the
         * underlying data store, thus the remove event will always be fired.
         * This may lead to inconsitency if you have to stores up at once. Let A
         * be the store that reads from the data repository C, and B the other store
         * that only represents a subset of data of the data repository C. If you
         * now remove a record X from A, which has not been in the store, but is assumed
         * to be available in the data repository, and would like to sync the available
         * data of B, then you have to check first if X may have apperead in the subset
         * of data C represented by B before calling remove from the B store (because
         * the remove operation will always trigger the "remove" event, no matter what).
         * (Common use case: you have selected a range of records which are then stored in
         * the row selection model. User scrolls through the data and the store's buffer
         * gets refreshed with new data for displaying. Now you want to remove all records
         * which are within the rowselection model, but not anymore within the store.)
         * One possible workaround is to only remove the record X from B if, and only
         * if the return value of a call to [object instance of store B].data.indexOf(X)
         * does not return a value less than 0. Though not removing the record from
         * B may not update the view of an attached BufferedGridView immediately.
         *
         * @param {Ext.data.Record} record
         * @param {Boolean} suspendEvent true to suspend the "remove"-event
         *
         * @return Number the index of the record removed.
         */
        remove : function(record, suspendEvent)
        {
            // check wether the record.id can be found in this store
            var index = this._getIndex(record);
    
            if (index < 0) {
                this.totalLength -= 1;
                if(this.pruneModifiedRecords){
                    this.modified.remove(record);
                }
                // adjust the buffer range if a record was removed
                // in the range that is actually behind the bufferRange
                this.bufferRange[0] = Math.max(-1, this.bufferRange[0]-1);
                this.bufferRange[1] = Math.max(-1, this.bufferRange[1]-1);
    
                if (suspendEvent !== true) {
                    this.fireEvent("remove", this, record, index);
                }
                return index;
            }
    
            this.bufferRange[1] = Math.max(-1, this.bufferRange[1]-1);
            this.data.removeAt(index);
    
            if(this.pruneModifiedRecords){
                this.modified.remove(record);
            }
    
            this.totalLength -= 1;
            if (suspendEvent !== true) {
                this.fireEvent("remove", this, record, index);
            }
    
            return index;
        },
    
        _getIndex : function(record)
        {
            var index = this.indexOfId(record.id);
    
            if (index < 0) {
                index = this.findInsertIndex(record);
            }
    
            return index;
        },
    
        /**
         * Removes a larger amount of records from the store and fires the "bulkremove"
         * event.
         * This helps listeners to determine whether the remove operation of multiple
         * records is still pending.
         *
         * @param {Array} records
         */
        bulkRemove : function(records)
        {
            var rec  = null;
            var recs = [];
            var ind  = 0;
            var len  = records.length;
    
            var orgIndexes = [];
            for (var i = 0; i < len; i++) {
                rec = records[i];
    
                orgIndexes[rec.id] = this._getIndex(rec);
            }
    
            for (var i = 0; i < len; i++) {
                rec = records[i];
                this.remove(rec, true);
                recs.push([rec, orgIndexes[rec.id]]);
            }
    
            this.fireEvent("bulkremove", this, recs);
        },
    
        /**
         * Remove all Records from the Store and fires the clear event.
         * The method assumes that there will be no data available anymore in the
         * underlying data store.
         */
        removeAll : function()
        {
            this.totalLength = 0;
            this.bufferRange = [-1, -1];
            this.data.clear();
    
            if(this.pruneModifiedRecords){
                this.modified = [];
            }
            this.fireEvent("clear", this);
        },
    
        /**
         * Requests a range of data from the underlying data store. Similiar to the
         * start and limit parameter usually send to the server, the method needs
         * an array of ranges of indexes.
         * Example: To load all records at the positions 1,2,3,4,9,12,13,14, the supplied
         * parameter should equal to [[1,4],[9],[12,14]].
         * The request will only be done if the beforeselectionsloaded events return
         * value does not equal to false.
         */
        loadRanges : function(ranges)
        {
            var max_i = ranges.length;
    
            if(max_i > 0 && !this.selectionsProxy.activeRequest
               && this.fireEvent("beforeselectionsload", this, ranges) !== false){
    
                var lParams = this.lastOptions.params;
    
                var params = {};
                params.ranges = Ext.encode(ranges);
    
                if (lParams) {
                    if (lParams.sort) {
                        params.sort = lParams.sort;
                    }
                    if (lParams.dir) {
                        params.dir = lParams.dir;
                    }
                }
    
                var options = {};
                for (var i in this.lastOptions) {
                    options.i = this.lastOptions.i;
                }
    
                options.ranges = params.ranges;
                
                this.selectionsProxy.load(params, this.reader,
                                this.selectionsLoaded, this,
                                options);
            }
        },
    
        /**
         * Alias for loadRanges.
         */
        loadSelections : function(ranges)
        {
            if (ranges.length == 0) {
                return;
            }
            this.loadRanges(ranges);
        },
    
        /**
         * Called as a callback by the proxy which loads pending selections.
         * Will fire the selectionsload event with the loaded records if, and only
         * if the return value of the checkVersionChange event does not equal to
         * false.
         */
        selectionsLoaded : function(o, options, success)
        {
            if (this.checkVersionChange(o, options, success) !== false) {
    
                var r = o.records;
                for(var i = 0, len = r.length; i < len; i++){
                    r[i].join(this);
                }
    
                this.fireEvent("selectionsload", this, o.records, Ext.decode(options.ranges));
            } else {
                this.fireEvent("selectionsload", this, [], Ext.decode(options.ranges));
            }
        },
    
        /**
         * Checks if the version supplied in <tt>o</tt> differs from the version
         * property of the current instance of this object and fires the versionchange
         * event if it does.
         */
        // private
        checkVersionChange : function(o, options, success)
        {
            if(o && success !== false){
                if (o.version !== undefined) {
                    var old      = this.version;
                    this.version = o.version;
                    if (this.version !== old) {
                        return this.fireEvent('versionchange', this, old, this.version);
                    }
                }
            }
        },
    
        /**
         * The sort procedure tries to respect the current data in the buffer. If the
         * found index would not be within the bufferRange, Number.MIN_VALUE is returned to
         * indicate that the record would be sorted below the first record in the buffer
         * range, while Number.MAX_VALUE would indicate that the record would be added after
         * the last record in the buffer range.
         *
         * The method is not guaranteed to return the relative index of the record
         * in the data model as returned by the underlying domain model.
         */
        findInsertIndex : function(record)
        {
            this.remoteSort = false;
            var index = Ext.ux.grid.livegrid.Store.superclass.findInsertIndex.call(this, record);
            this.remoteSort = true;
    
            // special case... index is 0 and we are at the very first record
            // buffered
            if (this.bufferRange[0] <= 0 && index == 0) {
                return index;
            } else if (this.bufferRange[0] > 0 && index == 0) {
                return Number.MIN_VALUE;
            } else if (index >= this.bufferSize) {
                return Number.MAX_VALUE;
            }
    
            return index;
        },
    
        /**
         * Removed snapshot check
         */
        // private
        sortData : function(f, direction)
        {
            direction = direction || 'ASC';
            var st = this.fields.get(f).sortType;
            var fn = function(r1, r2){
                var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
                return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
            };
            this.data.sort(direction, fn);
        },
    
    
    
        /**
         * @cfg {Number} bufferSize The number of records that will at least always
         * be available in the store for rendering. This value will be send to the
         * server as the <tt>limit</tt> parameter and should not change during the
         * lifetime of a grid component. Note: In a paging grid, this number would
         * indicate the page size.
         * The value should be set high enough to make a userfirendly scrolling
         * possible and should be greater than the sum of {nearLimit} and
         * {visibleRows}. Usually, a value in between 150 and 200 is good enough.
         * A lesser value will more often make the store re-request new data, while
         * a larger number will make loading times higher.
         */
    
    
        // private
        onMetaChange : function(meta, rtype, o)
        {
            this.version = null;
            Ext.ux.grid.livegrid.Store.superclass.onMetaChange.call(this, meta, rtype, o);
        },
    
    
        /**
         * Will fire the versionchange event if the version of incoming data has changed.
         */
        // private
        loadRecords : function(o, options, success)
        {
            this.checkVersionChange(o, options, success);
    
            // we have to stay in sync with rows that may have been skipped while
            // the request was loading.
            // if the response didn't make it through, set buffer range to -1,-1
            if (!o) {
                this.bufferRange = [-1,-1];
            } else {
                this.bufferRange = [
                    options.params.start,
                    Math.max(0, Math.min((options.params.start+options.params.limit)-1, o.totalRecords-1))
                ];
            }
            
            Ext.ux.grid.livegrid.Store.superclass.loadRecords.call(this, o, options, success);
        },
    
        /**
         * Get the Record at the specified index.
         * The function will take the bufferRange into account and translate the passed argument
         * to the index of the record in the current buffer.
         *
         * @param {Number} index The index of the Record to find.
         * @return {Ext.data.Record} The Record at the passed index. Returns undefined if not found.
         */
        getAt : function(index)
        {
            //anything buffered yet?
            if (this.bufferRange[0] == -1) {
                return undefined;
            }
    
            var modelIndex = index - this.bufferRange[0];
            return this.data.itemAt(modelIndex);
        },
    
    //--------------------------------------EMPTY-----------------------------------
        // no interface concept, so simply overwrite and leave them empty as for now
        clearFilter : function(){},
        isFiltered : function(){},
        collect : function(){},
        createFilterFn : function(){},
        sum : function(){},
        filter : function(){},
        filterBy : function(){},
        query : function(){},
        queryBy : function(){},
        find : function(){},
        findBy : function(){}
    
    });
    /**
     * Ext.ux.grid.livegrid.Toolbar
     * Copyright (c) 2007-2008, http://www.siteartwork.de
     *
     * Ext.ux.grid.livegrid.Toolbar is licensed under the terms of the
     *                  GNU Open Source GPL 3.0
     * license.
     *
     * Commercial use is prohibited. Visit <http://www.siteartwork.de/livegrid>
    
     * if you need to obtain a commercial license.
     *
     * This program is free software: you can redistribute it and/or modify it under
     * the terms of the GNU General Public License as published by the Free Software
     * Foundation, either version 3 of the License, or any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
     * details.
     *
     * You should have received a copy of the GNU General Public License along with
     * this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
     *
     */
    
    Ext.namespace('Ext.ux.grid.livegrid');
    
    /**
     * toolbar that is bound to a {@link Ext.ux.grid.livegrid.GridView}
     * and provides information about the indexes of the requested data and the buffer
     * state.
     *
     * @class Ext.ux.grid.livegrid.Toolbar
     * @extends Ext.Toolbar
     * @constructor
     * @param {Object} config
     *
     * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
     */
    Ext.ux.grid.livegrid.Toolbar = Ext.extend(Ext.Toolbar, {
    
        /**
         * @cfg {Ext.grid.GridPanel} grid
         * The grid the toolbar is bound to. If ommited, use the cfg property "view"
         */
    
        /**
         * @cfg {Ext.grid.GridView} view The view the toolbar is bound to
         * The grid the toolbar is bound to. If ommited, use the cfg property "grid"
         */
    
        /**
         * @cfg {Boolean} displayInfo
         * True to display the displayMsg (defaults to false)
         */
    
        /**
         * @cfg {String} displayMsg
         * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
         */
        displayMsg : 'Displaying {0} - {1} of {2}',
    
        /**
         * @cfg {String} emptyMsg
         * The message to display when no records are found (defaults to "No data to display")
         */
        emptyMsg : 'No data to display',
    
        /**
         * Value to display as the tooltip text for the refresh button. Defaults to
         * "Refresh"
         * @param {String}
         */
        refreshText : "Refresh",
    
        initComponent : function()
        {
            Ext.ux.grid.livegrid.Toolbar.superclass.initComponent.call(this);
    
            if (this.grid) {
                this.view = this.grid.getView();
            }
    
            var me = this;
            this.view.init = this.view.init.createSequence(function(){
                me.bind(this);
            }, this.view);
        },
    
        // private
        updateInfo : function(rowIndex, visibleRows, totalCount)
        {
            if(this.displayEl){
                var msg = totalCount == 0 ?
                    this.emptyMsg :
                    String.format(this.displayMsg, rowIndex+1,
                                  rowIndex+visibleRows, totalCount);
                this.displayEl.update(msg);
            }
        },
    
        /**
         * Unbinds the toolbar.
         *
         * @param {Ext.grid.GridView|Ext.gid.GridPanel} view Either The view to unbind
         * or the grid
         */
        unbind : function(view)
        {
            var st;
            var vw;
    
            if (view instanceof Ext.grid.GridView) {
                vw = view;
            } else {
                // assuming parameter is of type Ext.grid.GridPanel
                vw = view.getView();
            }
    
            st = view.ds;
    
            st.un('loadexception', this.enableLoading,  this);
            st.un('beforeload',    this.disableLoading, this);
            st.un('load',          this.enableLoading,  this);
            vw.un('rowremoved',    this.onRowRemoved,   this);
            vw.un('rowsinserted',  this.onRowsInserted, this);
            vw.un('beforebuffer',  this.beforeBuffer,   this);
            vw.un('cursormove',    this.onCursorMove,   this);
            vw.un('buffer',        this.onBuffer,       this);
            vw.un('bufferfailure', this.enableLoading,  this);
    
            this.view = undefined;
        },
    
        /**
         * Binds the toolbar to the specified {@link Ext.ux.grid.Livegrid}
         *
         * @param {Ext.grird.GridView} view The view to bind
         */
        bind : function(view)
        {
            this.view = view;
            var st = view.ds;
    
            st.on('loadexception',   this.enableLoading,  this);
            st.on('beforeload',      this.disableLoading, this);
            st.on('load',            this.enableLoading,  this);
            view.on('rowremoved',    this.onRowRemoved,   this);
            view.on('rowsinserted',  this.onRowsInserted, this);
            view.on('beforebuffer',  this.beforeBuffer,   this);
            view.on('cursormove',    this.onCursorMove,   this);
            view.on('buffer',        this.onBuffer,       this);
            view.on('bufferfailure', this.enableLoading,  this);
        },
    
    // ----------------------------------- Listeners -------------------------------
        enableLoading : function()
        {
            this.loading.setDisabled(false);
        },
    
        disableLoading : function()
        {
            this.loading.setDisabled(true);
        },
    
        onCursorMove : function(view, rowIndex, visibleRows, totalCount)
        {
            this.updateInfo(rowIndex, visibleRows, totalCount);
        },
    
        // private
        onRowsInserted : function(view, start, end)
        {
            this.updateInfo(view.rowIndex, Math.min(view.ds.totalLength, view.visibleRows-view.rowClipped),
                            view.ds.totalLength);
        },
    
        // private
        onRowRemoved : function(view, index, record)
        {
            this.updateInfo(view.rowIndex, Math.min(view.ds.totalLength, view.visibleRows-view.rowClipped),
                            view.ds.totalLength);
        },
    
        // private
        beforeBuffer : function(view, store, rowIndex, visibleRows, totalCount, options)
        {
            this.loading.disable();
            this.updateInfo(rowIndex, visibleRows, totalCount);
        },
    
        // private
        onBuffer : function(view, store, rowIndex, visibleRows, totalCount)
        {
            this.loading.enable();
            this.updateInfo(rowIndex, visibleRows, totalCount);
        },
    
        // private
        onClick : function(type)
        {
            switch (type) {
                case 'refresh':
                    if (this.view.reset(true)) {
                        this.loading.disable();
                    } else {
                        this.loading.enable();
                    }
                break;
    
            }
        },
    
        // private
        onRender : function(ct, position)
        {
            Ext.PagingToolbar.superclass.onRender.call(this, ct, position);
    
            this.loading = this.addButton({
                tooltip : this.refreshText,
                iconCls : "x-tbar-loading",
                handler : this.onClick.createDelegate(this, ["refresh"])
            });
    
            this.addSeparator();
    
            if(this.displayInfo){
                this.displayEl = Ext.fly(this.el.dom).createChild({cls:'x-paging-info'});
            }
        }
    });
    
    /**
     * Ext.ux.grid.livegrid.JsonReader
     * Copyright (c) 2007-2008, http://www.siteartwork.de
     *
     * Ext.ux.grid.livegrid.JsonReader is licensed under the terms of the
     *                  GNU Open Source GPL 3.0
     * license.
     *
     * Commercial use is prohibited. Visit <http://www.siteartwork.de/livegrid>
     * if you need to obtain a commercial license.
     *
     * This program is free software: you can redistribute it and/or modify it under
     * the terms of the GNU General Public License as published by the Free Software
     * Foundation, either version 3 of the License, or any later version.
     *
     * This program is distributed in the hope that it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
     * details.
     *
     * You should have received a copy of the GNU General Public License along with
     * this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
     *
     */
    
    Ext.namespace('Ext.ux.grid.livegrid');
    
    /**
     * @class Ext.ux.grid.livegrid.JsonReader
     * @extends Ext.data.JsonReader
     * @constructor
     * @param {Object} config
     *
     * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
     */
    Ext.ux.grid.livegrid.JsonReader = function(meta, recordType){
    
        Ext.ux.grid.livegrid.JsonReader.superclass.constructor.call(this, meta, recordType);
    };
    
    
    Ext.extend(Ext.ux.grid.livegrid.JsonReader, Ext.data.JsonReader, {
    
        /**
         * @cfg {String} versionProperty Name of the property from which to retrieve the
         *                               version of the data repository this reader parses
         *                               the reponse from
         */
    
    
    
        /**
         * Create a data block containing Ext.data.Records from a JSON object.
         * @param {Object} o An object which contains an Array of row objects in the property specified
         * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
         * which contains the total size of the dataset.
         * @return {Object} data A data block which is used by an Ext.data.Store object as
         * a cache of Ext.data.Records.
         */
        readRecords : function(o)
        {
            var s = this.meta;
    
            if(!this.ef && s.versionProperty) {
                this.getVersion = this.getJsonAccessor(s.versionProperty);
            }
    
            // shorten for future calls
            if (!this.__readRecords) {
                this.__readRecords = Ext.ux.grid.livegrid.JsonReader.superclass.readRecords;
            }
    
            var intercept = this.__readRecords.call(this, o);
    
    
            if (s.versionProperty) {
                var v = this.getVersion(o);
                intercept.version = (v === undefined || v === "") ? null : v;
            }
    
    
            return intercept;
        }
    
    });
    thanks,

    Joe

  8. #508
    Sencha User ThorstenSuckow's Avatar
    Join Date
    Sep 2007
    Location
    Aachen, Germany
    Posts
    597
    Vote Rating
    2
    ThorstenSuckow is on a distinguished road

      0  

    Default


    Joe,

    Quote Originally Posted by jmaisel View Post
    Hey...

    So, to rehash I'm on FF 3 .0.10 on Ubuntu. I'm using some smart logic to assemble my js includes on the fly into 1 large file, however doing so with the grid seems to break the grids rendering (as seen in my original screen cap). Once I realized that I made 1 static include file containing all the live grid js files in the same order as the script src tags:
    1. GridPanel.js
    2. GridView.js
    3. RowSelectionModel.js
    4. Store.js
    5. Toolbar.js
    6. JsonReader.js
    trying to include these files into 1 logical include with all my other js files, or even including it seperately into 1 large file (as seen below) breaks the layout and I get the rendering problem described in my original post. I tried with both my version of ext (2.2.1) and whichever version you have on the demo page. In either case the behavior is the same. Maybe I'm being super thick and I missed something obvious, but I don't think so. If I have then'll I'll be the happy recipient of an id10t award.
    I'm curious about what happens when you include livegrid-all.js or livegrid-all-debug.js out of the build folder (all needed files merged together by the build-script). Can you check and provide feedback?

    Regards

    Thorsten

  9. #509
    Ext User
    Join Date
    May 2009
    Posts
    4
    Vote Rating
    0
    pdugas is on a distinguished road

      0  

    Default LiveGrid with GridHeaderFilters?

    LiveGrid with GridHeaderFilters?


    Came across Damiano Zucconi's GridHeaderFilters[1] extension a couple days ago and it looks like what I want for one of my applications. I'm trying to use it with a LiveGrid but it's behaving strangely. Specificaly, I see the filter editors when the page renders but as soon as the data from the server arives, the table is populated and the filters disappear. Anybody got any thoughts on why?

    I'm working with ext-3.0-rc1.1 and LiveGrid from svn://svn.ext-livegrid.com/branches/Ext3.0/trunk.

    [1] http://extjs.com/forum/showthread.php?t=41658

  10. #510
    Ext User
    Join Date
    May 2008
    Posts
    51
    Vote Rating
    0
    excelsis is on a distinguished road

      0  

    Default LiveScroller loses focus when loadmask shown

    LiveScroller loses focus when loadmask shown


    When dragging the scroller down the grid buffers data and shows the loadmask as expected. However, the masking operation seems to take focus from the livescroller and so everytime it is shown I have to re-click on the scroller to continue dragging.
    The online example does not do this
    I see that there is a call to make sure the liveScroller z-index is higher than the mask z-index so I checked my code for z-indexes but they look OK.
    I changed the size of the mask in ext-el-mask to a reduced width to try and stop it impinging on the liveScroller but no joy.

    If I edit out the loadmask show call in showLoadMask() then the drag is OK.
    If I change the masked element from
    Code:
    mainBody.dom.parentNode.parentNode
    to just
    Code:
    mainBody
    then the drag is OK. This is a problem though as mainBody for me often has h. scroll so the mask is off the visisble center. I tried using scroller as the mask element but then the drag fails - this is odd as surely mainbody is higher in the source order and would in thoery be on a higher z-index ???

    I've been banging on this for almost day now - any insight or help greatly appreciated.
    Seems to be IE8
    FF3 is OK but with FF I never see the liverscroll scollbar because the livegrid css width is too small (18px - needs to be 22px)

Thread Participants: 248

  1. JeffHowden (1 Post)
  2. Animal (4 Posts)
  3. rodiniz (1 Post)
  4. galdaka (2 Posts)
  5. mdissel (1 Post)
  6. Wolfgang (1 Post)
  7. zzo (2 Posts)
  8. Frank (1 Post)
  9. herve (2 Posts)
  10. ericd (2 Posts)
  11. RWaters (5 Posts)
  12. Digital God (1 Post)
  13. Dumbledore (11 Posts)
  14. KimH (1 Post)
  15. pjordan (1 Post)
  16. cpantel (1 Post)
  17. mystix (3 Posts)
  18. wanclark (1 Post)
  19. MD (3 Posts)
  20. drew (1 Post)
  21. jheid (15 Posts)
  22. tsprague (1 Post)
  23. Confused (2 Posts)
  24. thesilentman (1 Post)
  25. andreas.linde (1 Post)
  26. violinista (1 Post)
  27. redxiii (1 Post)
  28. akannu (1 Post)
  29. theo (1 Post)
  30. Troy Wolf (3 Posts)
  31. chh (4 Posts)
  32. Phenothiasine (1 Post)
  33. danh2000 (1 Post)
  34. tobiu (1 Post)
  35. badgerd (1 Post)
  36. mlarese (1 Post)
  37. pluesch0r (1 Post)
  38. krycek (1 Post)
  39. gtaylor (4 Posts)
  40. ftftft (1 Post)
  41. hallikpapa (7 Posts)
  42. tech-nova (1 Post)
  43. provagino (1 Post)
  44. mjlecomte (2 Posts)
  45. iancmcc (1 Post)
  46. andrei.neculau (7 Posts)
  47. zieli1 (1 Post)
  48. meteorbites (1 Post)
  49. brookd (3 Posts)
  50. alexpetri (1 Post)
  51. urskipfer (1 Post)
  52. JEBriggs (1 Post)
  53. magunes117 (6 Posts)
  54. shiweiwei97 (3 Posts)
  55. vpell (1 Post)
  56. gelleneu (4 Posts)
  57. ohhowihateie (2 Posts)
  58. Andrewd2 (1 Post)
  59. Jacob (1 Post)
  60. cherbert (2 Posts)
  61. DragonFist (1 Post)
  62. marcoas (1 Post)
  63. Shmoo (1 Post)
  64. GraemeBryce (1 Post)
  65. w011117 (1 Post)
  66. luxxxian (1 Post)
  67. loverofdream (1 Post)
  68. lvanderree (1 Post)
  69. robw (1 Post)
  70. SeaSharp (1 Post)
  71. xpressive (1 Post)
  72. jeremia (1 Post)
  73. wm003 (21 Posts)
  74. miti (1 Post)
  75. sfrancolla (1 Post)
  76. Blob (1 Post)
  77. WoLpH (5 Posts)
  78. khatuido (3 Posts)
  79. zacware (3 Posts)
  80. mepfuso (2 Posts)
  81. wasp (1 Post)
  82. sharpguy (1 Post)
  83. stevets (1 Post)
  84. fred (1 Post)
  85. eliasp (3 Posts)
  86. h0tzenpl0tz (1 Post)
  87. tonedeaf (1 Post)
  88. Zolcsi (3 Posts)
  89. dearsina (1 Post)
  90. efattal (3 Posts)
  91. franck34 (3 Posts)
  92. tyr (1 Post)
  93. cybertaz (1 Post)
  94. zergworld (8 Posts)
  95. sekundek (1 Post)
  96. cs_alpha (3 Posts)
  97. Sultanalifezar (3 Posts)
  98. emily (7 Posts)
  99. jwendt@iscinternational.com (1 Post)
  100. sinma (1 Post)
  101. ItsMee (3 Posts)
  102. Nic (1 Post)
  103. sksoft (4 Posts)
  104. mjhaston (1 Post)
  105. mattb (4 Posts)
  106. jenner (1 Post)
  107. 2le (1 Post)
  108. bluefeet (1 Post)
  109. PremiereGlobal (2 Posts)
  110. rtozati (1 Post)
  111. KirkOlson (4 Posts)
  112. False Maria (1 Post)
  113. jbd007 (5 Posts)
  114. c.barca (1 Post)
  115. nctag (34 Posts)
  116. kfironit123 (1 Post)
  117. Emt (1 Post)
  118. ub3rn00b (12 Posts)
  119. Ballsacian1 (1 Post)
  120. mprice (1 Post)
  121. srikanthnukala (2 Posts)
  122. Mots (2 Posts)
  123. yhwh (1 Post)
  124. el777 (1 Post)
  125. JoomlaMan (1 Post)
  126. sanjivank (1 Post)
  127. sdetweil (1 Post)
  128. Snakehit (1 Post)
  129. msynovic (2 Posts)
  130. Snuyt (1 Post)
  131. as (2 Posts)
  132. pkmiec (2 Posts)
  133. epoks (2 Posts)
  134. NoahK17 (1 Post)
  135. praneeth528 (2 Posts)
  136. bemn (1 Post)
  137. Remy (1 Post)
  138. Daniel_Brazil_Campinas (1 Post)
  139. freddyk (4 Posts)
  140. dshorthouse (1 Post)
  141. dahman7 (1 Post)
  142. Canard64 (1 Post)
  143. dkuz (2 Posts)
  144. xsuniwov (1 Post)
  145. neha.chopra (1 Post)
  146. Eric24 (2 Posts)
  147. Mandeep (2 Posts)
  148. ttbgwt (6 Posts)
  149. suzan (1 Post)
  150. tenthcup (5 Posts)
  151. excelsis (5 Posts)
  152. DaveBrewster (6 Posts)
  153. rusty124 (1 Post)
  154. bcmatz (3 Posts)
  155. bjcullinan (1 Post)
  156. sstratton (4 Posts)
  157. Scorpie (1 Post)
  158. supercharge2 (3 Posts)
  159. Bing Qiao (6 Posts)
  160. tmaung (1 Post)
  161. xenon (4 Posts)
  162. sureaintme (5 Posts)
  163. animeshsingh (2 Posts)
  164. NicoP (29 Posts)
  165. cyfl (2 Posts)
  166. simplessus (1 Post)
  167. imnphd (1 Post)
  168. mono blaine (5 Posts)
  169. Kango_V (5 Posts)
  170. cain06 (1 Post)
  171. charak (2 Posts)
  172. vayumahesh (1 Post)
  173. Gabor Turi (1 Post)
  174. daeghran (2 Posts)
  175. maceido (5 Posts)
  176. sgoswami (1 Post)
  177. rubaiz (1 Post)
  178. Jabe (1 Post)
  179. ecarrenho (1 Post)
  180. mpereira (1 Post)
  181. changhua (4 Posts)
  182. alexw23 (1 Post)
  183. fxmisticat (5 Posts)
  184. extjssiva (1 Post)
  185. f1xxx3r (1 Post)
  186. SimoAmi (1 Post)
  187. aj3423 (1 Post)
  188. kkothari (2 Posts)
  189. jmariani (28 Posts)
  190. ibet (3 Posts)
  191. SunWuKung (1 Post)
  192. micgala (2 Posts)
  193. inptisto (1 Post)
  194. TheColonel (1 Post)
  195. cdeguzman (1 Post)
  196. ektanit (6 Posts)
  197. James Wang (1 Post)
  198. PCBingoB (1 Post)
  199. flylaputa (1 Post)
  200. MacSimon (1 Post)
  201. pibree (2 Posts)
  202. Markus (1 Post)
  203. aleister999 (2 Posts)
  204. adamli (2 Posts)
  205. jmaisel (4 Posts)
  206. pdugas (1 Post)
  207. plaak (1 Post)
  208. coriolis (2 Posts)
  209. weazil (4 Posts)
  210. fwiethof (1 Post)
  211. meroy (19 Posts)
  212. lxf1101 (2 Posts)
  213. stephen.friedrich (3 Posts)
  214. vinepod (1 Post)
  215. yuriy (2 Posts)
  216. completej (1 Post)
  217. dan_jf (1 Post)
  218. harel (2 Posts)
  219. veenvliet.morion (2 Posts)
  220. yura620310 (1 Post)
  221. barncat (1 Post)
  222. DmitrySistor (2 Posts)
  223. pclovec (3 Posts)
  224. Ranma13 (1 Post)
  225. swang (4 Posts)
  226. calugaru.cristian (5 Posts)
  227. mohan_b (1 Post)
  228. karlsnyder0 (2 Posts)
  229. JimmyInMD (2 Posts)
  230. tolitius (1 Post)
  231. a.labeau (2 Posts)
  232. benjixx (1 Post)
  233. psm1963 (1 Post)
  234. sosy (1 Post)
  235. nosferatum (10 Posts)
  236. daddie888 (1 Post)
  237. ixvivxi (1 Post)
  238. dp814082 (1 Post)
  239. nickelj (4 Posts)
  240. wifi4psp (1 Post)
  241. drian (1 Post)
  242. su-aska (1 Post)
  243. SebTardif (1 Post)
  244. danceric (1 Post)
  245. DTSman (1 Post)
  246. brian.moeskau (1 Post)
  247. Neethi (2 Posts)
  248. sango (1 Post)