PDA

View Full Version : Ext.ux.LiveGrid



Pages : 1 2 [3] 4

nctag
7 May 2009, 10:53 PM
@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

ThorstenSuckow
8 May 2009, 12:14 AM
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

jmaisel
12 May 2009, 8:48 AM
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.

jmaisel
12 May 2009, 11:28 AM
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

adamli
12 May 2009, 1:25 PM
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

ThorstenSuckow
12 May 2009, 2:30 PM
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?

jmaisel
13 May 2009, 7:27 AM
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:
GridPanel.js
GridView.js
RowSelectionModel.js
Store.js
Toolbar.js
JsonReader.jstrying 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>
<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):


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

ThorstenSuckow
13 May 2009, 10:15 AM
Joe,


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:
GridPanel.js
GridView.js
RowSelectionModel.js
Store.js
Toolbar.js
JsonReader.jstrying 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

pdugas
21 May 2009, 7:07 AM
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

excelsis
21 May 2009, 7:24 AM
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
mainBody.dom.parentNode.parentNode to just
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)

gtaylor
27 May 2009, 12:09 PM
I'm trying to convert my working Livegrid from 0.2 and Ext 2.1 to 0.3 and Ext 2.2.1.

In the old code, when I select a record then scroll away far enough to load a new buffer then scroll back again, the record still shows selected. In the new code, the selected row is not repainted. [==> fixed ==> bug in my specialized reader did not set record id properly].

Also, I used the array bufferedSelections to get the indexes of the selected rows. I could then sort this array to get the selected rows in order. This was handy if the user selected random rows with the CTRL key. I could then get them in row-order and not the order the user selected them. I don't see how I can do that process anymore in the new code. [==> fixed ==> added 'gridRowNumber' attribute to 'selections' items in RowSelectionModel].

Troy Wolf
4 Jun 2009, 7:50 AM
I hope my post title is helpful! This is a bit of an odd one for me.

LOVE the Livegrid! I have a livegrid in a Border Layout with 2 regions. I used Saki's excellent example at http://examples.extjs.eu/?ex=gridinbl. Difference is, his example is using a normal GridPanel -- not a Livegrid.

I have 2 regions. The 'north' region is a FormPanel. The Livegrid is in the 'center' panel at the bottom. On app load, all looks fine (the grid does not load any data until the form in the top region is submitted). Once the form is submitted, the grid's store loads but the grid "collapses". That is, all I can see is the header bar and the bbar shoved down to the bottom of the panel. The bbar shows that the data loaded as evidenced by "Displaying 1 - 19 of 195".

I've tried several fit and height options without any luck. I searched the forums for anyone having a similar issue, but did not find anything. For what it's worth, the issue is the same on both FF and IE8.

Ideas?

ThorstenSuckow
4 Jun 2009, 7:59 AM
I hope my post title is helpful! This is a bit of an odd one for me.

LOVE the Livegrid! I have a livegrid in a Border Layout with 2 regions. I used Saki's excellent example at http://examples.extjs.eu/?ex=gridinbl. Difference is, his example is using a normal GridPanel -- not a Livegrid.

I have 2 regions. The 'north' region is a FormPanel. The Livegrid is in the 'center' panel at the bottom. On app load, all looks fine (the grid does not load any data until the form in the top region is submitted). Once the form is submitted, the grid's store loads but the grid "collapses". That is, all I can see is the header bar and the bbar shoved down to the bottom of the panel. The bbar shows that the data loaded as evidenced by "Displaying 1 - 19 of 195".

I've tried several fit and height options without any luck. I searched the forums for anyone having a similar issue, but did not find anything. For what it's worth, the issue is the same on both FF and IE8.

Ideas?

First off - are you loading the css files for the livegrid into your page? If that does not help: Can you post your setup, or is there something I can view online?

Troy Wolf
5 Jun 2009, 11:40 AM
First off - are you loading the css files for the livegrid into your page?
Oh my gosh. No. I was not loading your CSS, and go figure--once I do, the problem is solved. My apologies. Your extension comes with that CSS for a reason and I read in another post that it is important to use your CSS---yet somehow I skipped it.

Thanks!

Frank
7 Jun 2009, 8:03 PM
Thanks for your job,Great!

ThorstenSuckow
8 Jun 2009, 6:48 AM
There is a new revision in svn://svn.ext-livegrid.com/branches/Ext3.0/trunk that makes Ext.ux.Livegrid work with Ext3.0RC2. People who are using Ext3.0RC2 should update their codebase accordingly.
Please refer to http://www.ext-livegrid.com/download/ for instructions on how to obtain code directly out of the svn repository.

ektanit
9 Jun 2009, 12:08 PM
Hi, I posted my question on the main help for Extjs 3.0 and learned to use live grid.
I tried with a very simple example and it works great. However when I do a little tweaks here and there, something or the other gets messed up. Actually it would sound stupid but where can i refer to the API docs for live grid.
I want to know the configurable parameters I can set while creating the live grid- Somehow when I am scrolling in the grid, I cannot see the message "Please wait.." I know I am missing something but what ?



var bufferedReader = new Ext.ux.grid.livegrid.JsonReader({
root : 'grid.gridData',
// versionProperty : 'response.value.version',
totalProperty : 'grid.totalProperty',
id : 'flowerId'
}, [
-- my columns
]);

var bufferedStore = new Ext.ux.grid.livegrid.Store({
autoLoad : true,
bufferSize : 300,
reader : bufferedReader,
baseParams: {'action': 'listflowers'},
sortInfo : {field: 'flowerId', direction: 'ASC'},
url : '/ext/testServlet'
});

var selModel = new Ext.ux.grid.livegrid.RowSelectionModel();

var bufferedView = new Ext.ux.grid.livegrid.GridView({
nearLimit : 100,
loadMask : {
msg : 'Please wait...'
}
});

var liveGridbbar = new Ext.ux.grid.livegrid.Toolbar({
view : bufferedView,
displayInfo : true
});

var liveGrid = new Ext.ux.grid.livegrid.GridPanel ({
enableDragDrop : false
,ds:bufferedStore
,columns:[
new Ext.grid.RowNumberer()
,-- my columns
]
,loadMask : {
msg : 'Loading...'
}
,width:1200
,height: 500
, title: 'Scrolling Table'
,bbar:liveGridbbar
});

ThorstenSuckow
9 Jun 2009, 12:38 PM
Hi, I posted my question on the main help for Extjs 3.0 and learned to use live grid.
I tried with a very simple example and it works great. However when I do a little tweaks here and there, something or the other gets messed up. Actually it would sound stupid but where can i refer to the API docs for live grid.
I want to know the configurable parameters I can set while creating the live grid- Somehow when I am scrolling in the grid, I cannot see the message "Please wait.." I know I am missing something but what ?


I have made some changes to the loadMask config in the gridView class - did you get the latest revision (3.0 branch) out of the repository?

Remy
10 Jun 2009, 2:52 AM
I have seen a reference to an example of LiveGrid hosted on siteartwork earlier in the thread, this now redirects to www.ext-livegrid.com. There doesn't appear to be an example on this host, I'm not sure if the config/api was documented there also but I've seen a few posts in this thread looking for docs, is there a hosted example and any docs on config?

ThorstenSuckow
10 Jun 2009, 3:03 AM
I have seen a reference to an example of LiveGrid hosted on siteartwork earlier in the thread, this now redirects to www.ext-livegrid.com. There doesn't appear to be an example on this host, I'm not sure if the config/api was documented there also but I've seen a few posts in this thread looking for docs, is there a hosted example and any docs on config?

The demo referring to the 2.* version can be found at http://www.siteartwork.de/livegrid_demo

Api docs have not been generated yet, I plan to move the demo to the ext-livegrid.com domain together with a page for generated api docs soon.

ektanit
11 Jun 2009, 9:23 AM
I just now updated my livegrid java scripts and also ExtJS to be compatible with RC2 Release of ExtJS 3.0.
When I am trying to render the live grid, it is throwing the error- Please suggest me what am I missing. Is there a demo of live grid with RC2 compatibility.


this.loading is undefined
this.loading.setDisabled(true);

My code is as below. This was working fine until I upgraded to RC2. Here is the link from where I downloaded the rar file for live grid rc2 http://ext-ux-livegrid.googlecode.com/files/ext-ux-livegrid-0.3RC2.rar



Ext.ux.Livegrid = Ext.extend(Ext.ux.grid.livegrid.GridPanel, {

initComponent : function()
{

var bufferedReader = new Ext.ux.grid.livegrid.JsonReader({
root : 'grid.gridData',
totalProperty : 'grid.totalProperty',
id : 'tradeId'
}, [
--my columns
]);


this.store = new Ext.ux.grid.livegrid.Store({
autoLoad : true,
bufferSize : 300,
reader : bufferedReader,
baseParams: {'action': 'listTrades'},
sortInfo : {field: 'tradeId', direction: 'ASC'},
url : '/ext/testServlet'
});


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);
}

});

var liveGrid = new Ext.ux.Livegrid({
enableDragDrop : false
,columns:[
-- my columns
]
,loadMask : {
msg : 'Loading...'
}
,width:1200
,height: 500
,title: 'Scrolling Table'

});

function LoadAndShowLiveGrid(combovalue,clientcombo){
// configure callback
liveGrid.getStore().on('load', loadSuccessful);
// render the grid
liveGrid.getStore().removeAll();
liveGrid.show();
liveGrid.render(document.getElementById('tradeGrid'));
var urlStr = prepare the url str
Ext.Ajax.request({
url: urlStr,
success: function(response) {
var data = splitResponse( response);
liveGrid.getStore().reload();
fetchDb = false;
}
});
}

ThorstenSuckow
11 Jun 2009, 11:01 AM
I just now updated my livegrid java scripts and also ExtJS to be compatible with RC2 Release of ExtJS 3.0.
When I am trying to render the live grid, it is throwing the error- Please suggest me what am I missing. Is there a demo of live grid with RC2 compatibility.

You can get the latest release here: http://www.ext-livegrid.com/download (see also notes on the startpage of the Google Code Project).

ektanit
11 Jun 2009, 11:58 AM
I had already taken the latest release by navigating from the URL you sent me
http://www.ext-livegrid.com/download ->
http://wiki.ext-livegrid.com/wiki/User/Download ->
http://ext-ux-livegrid.googlecode.com/files/ext-ux-livegrid-0.3RC2.rar

But still not getting what configuration to add to my live grid creation to get away with the error this.loading is undefined ?

ThorstenSuckow
11 Jun 2009, 12:13 PM
I had already taken the latest release by navigating from the URL you sent me
http://www.ext-livegrid.com/download ->
http://wiki.ext-livegrid.com/wiki/User/Download ->
http://ext-ux-livegrid.googlecode.com/files/ext-ux-livegrid-0.3RC2.rar

But still not getting what configuration to add to my live grid creation to get away with the error this.loading is undefined ?

You are right, the instructions on the wiki page can be a bit confusing. I have updated the wiki entry to be more clear about the various download options.

ektanit
11 Jun 2009, 12:57 PM
See here is the problem I found when I debugged the point of error. i am getting error this.loading not defined for a Ext.ux.grid.livegrid.Toolbar which has various listeners like

enableLoading : function()
{
this.loading.setDisabled(false);
},

Now loading should be a config parameter either in livegrid.Toolbar or Ext.Toolbar which I do not find in version RC2. Is my conclusion correct ? What should I do not resolve this ?

ThorstenSuckow
11 Jun 2009, 1:23 PM
See here is the problem I found when I debugged the point of error. i am getting error this.loading not defined for a Ext.ux.grid.livegrid.Toolbar which has various listeners like

enableLoading : function()
{
this.loading.setDisabled(false);
},

Now loading should be a config parameter either in livegrid.Toolbar or Ext.Toolbar which I do not find in version RC2. Is my conclusion correct ? What should I do not resolve this ?

As said, check out the latest code from svn://svn.ext-livegrid.com/branches/3.0/trunk
The problem you encounter is a missing return param in Ext 3.0 which was fixed in one of the later revisions.

ektanit
12 Jun 2009, 7:13 AM
Ok see here is what I did. I downloaded SVN client ( TortoiseSVN) to access your repository but when I access it give me unknown hostname- svn.ext-livegrid.com.
Then, what I did is went to the Browse Source link and then opened up the source of livegrid-all-debug.js (branches/Ext3.0/trunk) , scrolled down to the bottom of the page and downloaded the file using the link 'download in other format/original format' .

This now included in my source and retried but still getting the same error. A couple of questions
1. Why do I get unknown host name. Do you have any SVN client I can try out.
2. I think livegrid-all-debug.js together with css file is all you need to have the live grid come up. if not, what other files should I include.


Can you help please.

ThorstenSuckow
15 Jun 2009, 12:51 AM
1. Why do I get unknown host name. Do you have any SVN client I can try out.


Dunno, maybe a firewall blocking svn protocoll? Did you clearly use svn://svn.ext-livegrid.com as the url? Best SVN client so far is TortoiseSVN, IMHO.



2. I think livegrid-all-debug.js together with css file is all you need to have the live grid come up. if not, what other files should I include.


Yes, either livegrid-all-debug.js or livegrid-all.js (along with the css file).
See here (http://wiki.ext-livegrid.com/changeset/51/branches/Ext3.0/trunk/build/livegrid-all-debug.js) for a changeset, it does not look like you have the proper files, since the latest revision for the 3.0 branch had this issue fixed.

cs_alpha
15 Jun 2009, 2:20 PM
First of all, thanks for this great plugin!

Unfortunalety I'm having some trouble with large selections in the grid. When I start a selection, scroll down a lot and finish the selection with the shift key. It looks like I selected all the records. But when I retreive the selection info from the selectionmodel, It only gives me a part of the selection. This is probably only the information that's in the buffer at that moment.

How can I make sure that the whole selection is in the buffer and ready to read?

ThorstenSuckow
16 Jun 2009, 2:42 AM
First of all, thanks for this great plugin!

Unfortunalety I'm having some trouble with large selections in the grid. When I start a selection, scroll down a lot and finish the selection with the shift key. It looks like I selected all the records. But when I retreive the selection info from the selectionmodel, It only gives me a part of the selection. This is probably only the information that's in the buffer at that moment.

How can I make sure that the whole selection is in the buffer and ready to read?

Handling selections which were not part of the record-set in the buffer is somewhat different, yet not feature complete in the current version of Livegrid. However, here's the basic workflow:

Call "getPendingSelections()" from the RowSelectionModel. This will return an array with ranges of assumed indizes of the records which were not in the buffer, but marked as selected.
Then, pass this array to the Store by calling "loadSelections(ranges)". This will invoke a server request by passing all indizes of the selected records (which have not yet been buffered) to your server side script. Of course, you'd have to implement your own logic to return the appropriate records based on the ranges.

Target release for a fully functional "pending selection" is 0.4, which also will consider the "version" property, along with a documentation on how to handle this best. make sure you do not use this feature in a live environment, as it may lead to data inconsistency when deleting /updating selected records which were pending.

ThorstenSuckow
16 Jun 2009, 4:26 AM
As said, check out the latest code from svn://svn.ext-livegrid.com/branches/3.0/trunk
The problem you encounter is a missing return param in Ext 3.0 which was fixed in one of the later revisions.

Check http://www.ext-livegrid.com for direct download options (i.e. nightlies)

gtaylor
16 Jun 2009, 4:33 AM
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
mainBody.dom.parentNode.parentNode to just
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)

I'm seeing the exact same problem with my application, but the workaround doesn't seem to work from me. Also, I don't see it in the demo app provided with LiveGrid. It works just fine in IE. Again, any insight or help would be greatly appreciated.

ThorstenSuckow
16 Jun 2009, 4:50 AM
I'm seeing the exact same problem with my application, but the workaround doesn't seem to work from me. Also, I don't see it in the demo app provided with LiveGrid. It works just fine in IE. Again, any insight or help would be greatly appreciated.

1. Are you loading the css for Livegrid into your app?
if this doesn't help -
2. which version from Ext.ux.Livegrid are you using? In which browser does the problem appear?

gtaylor
16 Jun 2009, 5:00 AM
1. Are you loading the css for Livegrid into your app?
if this doesn't help -
2. which version from Ext.ux.Livegrid are you using? In which browser does the problem appear?
1. Yes, I'm using the css for Livegrid.
2. I'm using 0.3RC2. I see the problem in IE7 only. Firefox3 is okay.

Also, your demo app is okay even with IE7, so there must be something else not just related to the loadMask and the z-index. The main difference between my app and the demo app is that my app is using a DWR Proxy instead of a url. Maybe events are being suspended during the loading of a new buffer?

ThorstenSuckow
16 Jun 2009, 5:02 AM
1. Yes, I'm using the css for Livegrid.
2. I'm using 0.3RC2. I see the problem in IE7 only. Firefox3 is okay.

Also, your demo app is okay even with IE7, so there must be something else not just related to the loadMask and the z-index. The main difference between my app and the demo app is that my app is using a DWR Proxy instead of a url. Maybe events are being suspended during the loading of a new buffer?

It's possible related to the changes that came with Ext 3.0 - I'll look into this issue.

EDIT:
confirmed. it's not related to to your DWR Proxy, it looks like a focus issue.

ektanit
16 Jun 2009, 8:46 AM
Hi, I have a question on the live grid store reloading with a new set of data from the server.
My live grid come up and works great but what are the steps involved to a get new data set from the server. I am not talking about handling modifications to the same set( using version change property). The store for the live grid should reload with a new data set..If I do like below- its is firing request to the server two times to get the first 300 records in the buffer.


liveGrid.render(document.getElementById('tradeGrid')); // this fires one request
liveGrid.getStore().reload(); // this fires second request.

Without the reload(), the grid doesn't reload with the new data until I click in refresh icon in the toolbar.

fwiethof
17 Jun 2009, 1:34 PM
hello!

i'm having a problem with Ext.ux.grid.livegrid (nightly build r51) with ext 3 rc2.

as soon as additional data has been loaded (due to scrolling), the loading indicator does not stop "loading" after i click a grid header to sort. the data within the grid is displayed correctly, only the indicator behave incorrect.
this does not happen if i don't scroll before (or stop scrolling before the first chunk of data is loaded).

any idea?

here's my code:



btRia.Applet.UsersGrid = function(config) {
btRia.Applet.UsersGrid.superclass.constructor.call(this, config);
};

Ext.extend(btRia.Applet.UsersGrid, btRia.AppletGrid, {

buildReader: function() {
return new Ext.ux.grid.livegrid.JsonReader({
root: 'items',
versionProperty: 'version',
totalProperty: 'total',
id: 'id'
},[
{ name: 'username', type: 'string', allowBlank: false },
{ name: 'type', type: 'string', allowBlank: false },
{ name: 'gender', type: 'string' },
{ name: 'name', type: 'string', allowBlank: false },
{ name: 'email_address', type: 'string', allowBlank: false },
{ name: 'is_active', type: 'boolean' },
{ name: 'last_login', type: 'date', dateFormat: 'Y-m-d H:i:s' }
]);
},

buildStore: function() {
return new Ext.ux.grid.livegrid.Store({
autoLoad: true,
bufferSize: RiaApplication.gridBufferSize,
sortInfo: {
field: 'username',
direction: 'ASC'
},
reader: this.buildReader(),
writer: this.buildWriter(),
url: RiaApplication.userStoreUrl,
listeners: {
'loadexception': function(dataProxy, response, arg) {
console.log('Exception:', dataProxy, response, arg);
}
}
});
},

buildColumns: function() {
return new Ext.grid.ColumnModel([
{
header: btRia.I18N.users_applet_grid_username_column,
width: 150,
sortable: true,
dataIndex: 'username'
},{
header: btRia.I18N.users_applet_grid_name_column,
width: 200,
sortable: true,
dataIndex: 'name'
},{
header: btRia.I18N.users_applet_grid_email_address_column,
width: 200,
sortable: true,
dataIndex: 'email_address'
},{
header: btRia.I18N.users_applet_grid_type_column,
renderer: this.renderType,
width: 80,
sortable: true,
dataIndex: 'type'
},{
header: btRia.I18N.users_applet_grid_is_active_column,
renderer: this.renderActive,
width: 60,
align: 'center',
sortable: true,
dataIndex: 'is_active'
},{
header: btRia.I18N.users_applet_grid_last_login_column,
renderer: this.renderLastLogin,
width: 115,
sortable: true,
dataIndex: 'last_login'
}
]);
},

renderType: function(val) {
if (val == 'role') {
text = btRia.I18N.users_applet_form_type_role;
iconCls = 'x-icon-group_gear';
} else {
text = btRia.I18N.users_applet_form_type_person;
iconCls = 'x-icon-user_gray';
}
return '<div class="x-icon-grid-icon ' + iconCls + '">' + text + '</div>';
},

renderActive: function(val) {
if (val) {
return btRia.I18N.general_yes;
} else {
return '<div class="x-text-red">' + btRia.I18N.general_no + '</div>';
}
},

renderLastLogin: function(val) {
return val? val.dateFormat(btRia.I18N.date_time_format_short): btRia.I18N.general_never;
},

buildTopToolbar: function() {
return [{
text: btRia.I18N.users_applet_grid_add_button,
tooltip: btRia.I18N.users_applet_grid_add_button_tooltip,
iconCls: 'x-icon-user_add',
scope: this
},{
text: btRia.I18N.users_applet_grid_copy_button,
tooltip: btRia.I18N.users_applet_grid_copy_button_tooltip,
iconCls: 'x-icon-group_go',
ref: '../copyButton',
disabled: true,
scope: this
},{
text: btRia.I18N.users_applet_grid_remove_button,
tooltip: btRia.I18N.users_applet_grid_remove_button_tooltip,
iconCls: 'x-icon-user_delete',
ref: '../removeButton',
disabled: true,
scope: this
},'-',{
text: btRia.I18N.users_applet_grid_lock_button,
tooltip: btRia.I18N.users_applet_grid_lock_button_tooltip,
iconCls: 'x-icon-lock',
ref: '../lockButton',
disabled: true,
scope: this
},{
text: btRia.I18N.users_applet_grid_unlock_button,
tooltip: btRia.I18N.users_applet_grid_unlock_button_tooltip,
iconCls: 'x-icon-lock_open',
ref: '../unlockButton',
disabled: true,
scope: this
},'-',{
text: btRia.I18N.users_applet_grid_reset_password_button,
tooltip: btRia.I18N.users_applet_grid_reset_password_button_tooltip,
iconCls: 'x-icon-key',
ref: '../resetPasswordButton',
disabled: true,
scope: this
}];
},

onChangeSelection: function(sm) {
if (sm.getCount()) {
// Enable the form if exactly one use is selected
if (sm.getCount() > 1) {
sm.grid.form.disable();
} else {
sm.grid.form.enable();
}
// Enable the copy button if exactly one user is selected
if (sm.getCount() > 1) {
sm.grid.copyButton.disable();
} else {
sm.grid.copyButton.enable();
}
// Enable the remove button
this.grid.removeButton.enable();
// Enable the unlock button if at least one of the selected users is not active
anyLocked = false;
sm.selections.items.each(function(item) {
if (! item.data.is_active) {
anyLocked = true;
}
});
if (anyLocked) {
sm.grid.unlockButton.enable();
} else {
sm.grid.unlockButton.disable();
}
// Enable the lock button if at least one of the selected users is active
anyUnlocked = false;
sm.selections.items.each(function(item) {
if (item.data.is_active) {
anyUnlocked = true;
}
});
if (anyUnlocked) {
sm.grid.lockButton.enable();
} else {
sm.grid.lockButton.disable();
}
// Enable the reset password button
sm.grid.resetPasswordButton.enable();
} else {
// Disable the form if no user is selected
sm.grid.form.disable();
// Disable all button except add when no user is selected
sm.grid.copyButton.disable();
sm.grid.removeButton.disable();
sm.grid.lockButton.disable();
sm.grid.unlockButton.disable();
sm.grid.resetPasswordButton.disable();
}
}

});


btRia.AppletGrid = Ext.extend(Ext.ux.grid.livegrid.GridPanel, {

form: null,

columnLines: true,
stripeRows: true,
listeners: {
'rowclick': function(g, index, e) {
var rec = g.store.getAt(index);
g.form.loadRecord(rec);
}
},

initComponent: function() {
this.store = this.buildStore();
this.selModel = this.buildSelectionModel();
this.cm = this.buildColumns();
this.view = this.buildView();
this.tbar = this.buildTopToolbar();
this.bbar = this.buildBottomToolbar();
btRia.AppletGrid.superclass.initComponent.call(this);
},

buildWriter: function() {
return new Ext.data.JsonWriter({
returnJson: true,
writeAllFields: true
});
},

buildReader: function() {
return null;
},

buildStore: function() {
return null;
},

buildSelectionModel: function() {
return new Ext.ux.grid.livegrid.RowSelectionModel({
listeners: {
'selectionchange': this.onChangeSelection
}
});
},

buildColumns: function() {
return null;
},

buildTopToolbar: function() {
return [];
},

buildView: function() {
return new Ext.ux.grid.livegrid.GridView({
nearLimit: RiaApplication.gridNearLimit,
loadMask: {
msg: btRia.I18N.grid_loading_text
}
});
},

buildBottomToolbar: function() {
return new Ext.ux.grid.livegrid.Toolbar({
view: this.view,
displayInfo: true
});
},

onChangeSelection: function(sm) {
}

});


btRia.Applet.Users = Ext.extend(btRia.Applet, {

layout: 'border',

initComponent: function() {
this.form = this.buildForm();
this.grid = this.buildGrid();
this.items = this.buildItems();
btRia.Applet.Users.superclass.initComponent.call(this);
},

buildForm: function() {
return new btRia.Applet.UsersForm({
region: 'south',
disabled: true
});
},

buildGrid: function() {
return new btRia.Applet.UsersGrid({
region: 'center',
form: this.form
});
},

buildItems: function() {
return [
this.grid,
this.form
];
}

});

ThorstenSuckow
19 Jun 2009, 2:49 AM
hello!

i'm having a problem with Ext.ux.grid.livegrid (nightly build r51) with ext 3 rc2.

as soon as additional data has been loaded (due to scrolling), the loading indicator does not stop "loading" after i click a grid header to sort. the data within the grid is displayed correctly, only the indicator behave incorrect.
this does not happen if i don't scroll before (or stop scrolling before the first chunk of data is loaded).

any idea?


Confirmed, this is a bug.

ThorstenSuckow
19 Jun 2009, 2:51 AM
Hi, I have a question on the live grid store reloading with a new set of data from the server.
My live grid come up and works great but what are the steps involved to a get new data set from the server. I am not talking about handling modifications to the same set( using version change property). The store for the live grid should reload with a new data set..If I do like below- its is firing request to the server two times to get the first 300 records in the buffer.


liveGrid.render(document.getElementById('tradeGrid')); // this fires one request
liveGrid.getStore().reload(); // this fires second request.

Without the reload(), the grid doesn't reload with the new data until I click in refresh icon in the toolbar.


Best way would be to listen to the beforeload-event and inject the necessary request parameter into the "options" argument... however, a more detailed description of what you want to achieve would be helpful.

ThorstenSuckow
20 Jun 2009, 3:48 AM
hello!

i'm having a problem with Ext.ux.grid.livegrid (nightly build r51) with ext 3 rc2.

as soon as additional data has been loaded (due to scrolling), the loading indicator does not stop "loading" after i click a grid header to sort. the data within the grid is displayed correctly, only the indicator behave incorrect.
this does not happen if i don't scroll before (or stop scrolling before the first chunk of data is loaded).

any idea?



fixed in r52, see http://wiki.ext-livegrid.com/changeset/52

ThorstenSuckow
20 Jun 2009, 9:23 AM
I'm seeing the exact same problem with my application, but the workaround doesn't seem to work from me. Also, I don't see it in the demo app provided with LiveGrid. It works just fine in IE. Again, any insight or help would be greatly appreciated.

fixed in r55

gelleneu
22 Jun 2009, 5:27 AM
I think there is an error in the latest nightly build rev. 55 in
EditorGridPanel initComponent:

I replaced:

return Ext.ux.grid.livegrid.EditorGridPanel.prototype.initComponent.call(this);

with:

return Ext.ux.grid.livegrid.EditorGridPanel.superclass.initComponent.call(this);


Then, the grid works properly.

ThorstenSuckow
22 Jun 2009, 8:26 AM
I think there is an error in the latest nightly build rev. 55 in
EditorGridPanel initComponent:

I replaced:

return Ext.ux.grid.livegrid.EditorGridPanel.prototype.initComponent.call(this);

with:

return Ext.ux.grid.livegrid.EditorGridPanel.superclass.initComponent.call(this);


Then, the grid works properly.

thanks, confirmed, copy & paste was a bad idea at this place. Will be in SVN ASAP.

coriolis
25 Jun 2009, 7:10 PM
Can anyone tell me why I am getting a "too much recursion error" at line EditorGriddPanel.js (line 92)





new Ext.ux.grid.livegrid.EditorGridPanel({
id: 'KvrgkoZSnJ',
footer: false,
stripeRows: true,
clicksToEdit: 2,
autoScroll: true,
serverSorting: true,
autoWidth: true,
autoHeight: true,
border: false,
header: false,
plugins: [new Ext.ux.grid.GridFilters({
menuFilterText: 'Filters',
filters: [{
type: 'boolean',
dataIndex: 'eXpQbsgXku'
},
{
type: 'string',
dataIndex: 'dIEvkfFhGP'
},
{
type: 'boolean',
dataIndex: 'OKYbKWWGyA'
},
{
type: 'string',
dataIndex: 'RaAvwJCOPh'
},
{
type: 'list',
options: ['True', 'False'],
dataIndex: 'HPreiAfYSp'
},
{
type: 'string',
dataIndex: 'PbHRsiFjaL'
},
{
type: 'date',
beforeText: 'Before1',
afterText: 'After1',
onText: 'On1',
dateFormat: 'm/d/Y',
dataIndex: 'QaWogeLlVB'
},
{
type: 'boolean',
dataIndex: 'rhNTnmNOOD'
},
{
type: 'string',
dataIndex: 'qvJWValxBx'
},
{
type: 'list',
options: ['True', 'False'],
dataIndex: 'DvZDMHHDwu'
},
{
type: 'string',
dataIndex: 'nzVayCrVMC'
},
{
type: 'string',
dataIndex: 'ItGSsNCdiW'
}]
})],
tbar: [
new Ext.Toolbar.Spacer({
width: 5
}), new Ext.Panel({
width: 23,
height: 25,
border: false,
bodyStyle: 'background-color: transparent',
style: 'padding-top: 5px',
html: '<img src="images/eo.png">'
}), new Ext.Toolbar.TextItem({
text: 'Application List'
}), new Ext.Toolbar.Fill({}), new Ext.Button({
text: 'Toggle',
iconCls: 'prFGwfTgPi',
menu: new Ext.menu.Menu({
items: []
})
}), new Ext.Button({
text: 'Actions',
iconCls: 'prFGwfTgPi',
menu: new Ext.menu.Menu({
items: [new Ext.menu.Item({
text: 'Refresh',
iconCls: 'vmtnJqgWoM',
handler:

function onItemClick(item, event) {
Ext.getCmp('KvrgkoZSnJ').getStore().reload();
}

}), new Ext.menu.Item({
text: 'Edit',
menu: new Ext.menu.Menu({
items: [new Ext.menu.Item({
text: 'Undo Record',
iconCls: 'vmtnJqgWoM',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'Redo Record',
iconCls: 'vmtnJqgWoM',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'New Record',
iconCls: 'vmtnJqgWoM',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'Copy Record',
iconCls: 'vmtnJqgWoM',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'Deep Copy Record',
iconCls: 'vmtnJqgWoM',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'Delete Record',
iconCls: 'vmtnJqgWoM',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'Compare Record',
iconCls: 'vmtnJqgWoM',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

})]
})
}), new Ext.menu.Item({
text: 'View',
menu: new Ext.menu.Menu({
items: [new Ext.menu.Item({
text: 'Enterprise Views',
handler:

function onItemClick(item, event) {
Ext.Msg.alert('Feature not yet implemented');
}

}), new Ext.menu.Item({
text: 'Displayed Columns...',
handler:

function onItemClick(item, event) {
Ext.Msg.alert('Feature not yet implemented');
}

}), new Ext.menu.Item({
text: 'Freeze Columns...',
handler:

function onItemClick(item, event) {
Ext.Msg.alert('Feature not yet implemented');
}

})]
})
}), new Ext.menu.Item({
text: 'Go',
menu: new Ext.menu.Menu({
items: [new Ext.menu.Item({
text: 'Previous Record',
handler:

function onItemClick(item, event) {

Ext.getCmp('KvrgkoZSnJ').getSelectionModel().selectPrevious();
}

}), new Ext.menu.Item({
text: 'Next Record',
handler:

function onItemClick(item, event) {
Ext.getCmp('KvrgkoZSnJ').getSelectionModel().selectNext();
}

}), new Ext.menu.Item({
text: 'First Record',
handler:

function onItemClick(item, event) {
Ext.getCmp('KvrgkoZSnJ').getSelectionModel().selectFirstRow();
}

}), new Ext.menu.Item({
text: 'Last Record',
handler:

function onItemClick(item, event) {
Ext.getCmp('KvrgkoZSnJ').getSelectionModel().selectLastRow();
}

})]
})
}), new Ext.menu.Item({
text: 'Export',
menu: new Ext.menu.Menu({
items: [new Ext.menu.Item({
text: 'To XML',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'To CSV',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

})]
})
}), new Ext.menu.Item({
text: 'Help',
menu: new Ext.menu.Menu({
items: [new Ext.menu.Item({
text: 'Help...'
}), new Ext.menu.Item({
text: 'About Record...',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'About Component...',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'About Perspective...',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'About Dimension...',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

}), new Ext.menu.Item({
text: 'About Application...',
handler:

function onItemClick(item, event) {
Ext.Msg.show({
title: 'Coriolis 5 IAP',
msg: 'Feature does not exist.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING
});
}

})]
})
})]
})
}), new Ext.Button({
text: 'Views',
iconCls: 'prFGwfTgPi',
menu: new Ext.menu.Menu({})
})],
bbar: new Ext.ux.grid.livegrid.Toolbar({
view: myView,
displayInfo: true
}),
loadMask: {
msg: 'Loading...'
},
view: myView,
/*
view: new Ext.grid.GroupingView({
forceFit: false,

// Return CSS class to apply to rows depending upon data values
getRowClass: function(record, index, rowParams, store) {
var a = record.get('OKYbKWWGyA');
var c = record.get('rhNTnmNOOD');
var d = record.get('eXpQbsgXku');

if (c == 'true') {
return 'developer-inactive-row';
} else if (d == 'true') {
return 'developer-locked-row';
}
return '';
}
}),
*/
selModel: new Ext.ux.grid.livegrid.RowSelectionModel({
singleSelect: true,
moveEditorOnEnter: false,
listeners: {
'beforerowselect': {
fn: function(selectionModel, rowIndex, keepExisting, record) {
return false;
}
},
'rowselect': {
fn: function(selectionModel, rowIndex, record) {
alert("In Ext.grid.RowSelectionModel.rowselect");
}
},
'rowdeselect': {
fn: function(selectionModel, rowIndex, record) {
alert("rowdeselect");
}
},
'selectionchange': {
fn: function(selectionModel) {
alert("selectionchange");
}
}
}
}),
store: new Ext.ux.grid.livegrid.Store({
// new Ext.data.GroupingStore({
url: '/Coriolis/processor?app=gjcaINqkBn&action=hcRPmPYwPB',
autoLoad: true,
groupField: 'RaAvwJCOPh',
sortInfo: {
field: 'dIEvkfFhGP',
direction: 'ASC'
},
remoteSort: true,
reader: new Ext.data.XmlReader({
record: 'row',
id: 'id'
},
Ext.data.Record.create([{
name: 'eXpQbsgXku',
type: 'boolean'
},
{
name: 'dIEvkfFhGP',
type: 'string'
},
{
name: 'OKYbKWWGyA',
type: 'boolean'
},
{
name: 'RaAvwJCOPh',
type: 'string'
},
{
name: 'HPreiAfYSp',
type: 'string'
},
{
name: 'PbHRsiFjaL',
type: 'string'
},
{
name: 'QaWogeLlVB',
type: 'date',
dateFormat: 'm/d/Y h:i:s A'
},
{
name: 'rhNTnmNOOD',
type: 'boolean'
},
{
name: 'qvJWValxBx',
type: 'string'
},
{
name: 'DvZDMHHDwu',
type: 'string'
},
{
name: 'nzVayCrVMC',
type: 'string'
},
{
name: 'ItGSsNCdiW',
type: 'string'
}])),
listeners: {
'add': {
fn: function(store, records, index) {}
},
'beforeload': {
fn: function(store, options) {}
},
'clear': {
fn: function(store) {}
},
'datachanged': {
fn: function(store) {}
},
'load': {
fn: function(store, records, options) {}
},
'loadexception': {
fn: function() {
alert('loadexception fired in EditorGridPanel.store');
}
},
'metachange': {
fn: function(store, meta) {}
},
'remove': {
fn: function(store, record, index) {}
},
'update': {
fn: function(store, record, operation) {}
}
}
}),
cm: new Ext.grid.ColumnModel({
columns: [
new Ext.grid.RowNumberer(),

new Ext.grid.Column({
id: 'rxHmDCQooh',
menuDisabled: true,
width: 25,
align: 'center',
sortable: false,
resizable: true,
fixed: true,
editable: false,
header: 'M',
dataIndex: 'eXpQbsgXku',
renderer:

function coriolis_boolean_renderer(value, metadata, record, rowIndex, colIndex, store) {
if (value == true) {
return '<img src="images/check.png"/>';
}
return '';
}

})

,

new Ext.grid.Column({
id: 'LXsTsoIVMV',
width: 250,
resizable: true,
fixed: false,
header: 'Name',
dataIndex: 'dIEvkfFhGP',
renderer: coriolis_uppercase_renderer,
editor: new Ext.form.TextField({
id: 'RHlsYtMZTz',
listeners: {
'autosize': {
fn: function(item, width) {}
},
'beforedestroy': {
fn: function(item) {}
},
'beforehide': {
fn: function(item) {}
},
'beforerender': {
fn: function(item) {}
},
'beforeshow': {
fn: function(item) {}
},
'beforestaterestore': {
fn: function(item, state) {}
},
'beforestatesave': {
fn: function(item, state) {}
},
'blur': {
fn: function(field) {}
},
'change': {
fn: function(field, newValue, oldValue) {}
},
'destroy': {
fn: function(item) {}
},
'disable': {
fn: function(item) {}
},
'enable': {
fn: function(item) {}
},
'focus': {
fn: function(field) {}
},
'hide': {
fn: function(item) {}
},
'invalid': {
fn: function(field, msg) {}
},
'keydown': {
fn: function(field, event) {}
},
'keypress': {
fn: function(field, event) {}
},
'keyup': {
fn: function(field, event) {}
},
'move': {
fn: function(item, x, y) {}
},
'render': {
fn: function(item) {}
},
'resize': {
fn: function(item, adjWidth, adjHeight, rawWidth) {}
},
'show': {
fn: function(item) {}
},
'specialkey': {
fn: function(field, event) {}
},
'staterestore': {
fn: function(item, state) {}
},
'statesave': {
fn: function(item, state) {}
},
'valid': {
fn: function(field) {}
}
}
})

})

,

new Ext.grid.Column({
id: 'rVfRrPXMQt',
menuDisabled: true,
width: 80,
align: 'center',
sortable: false,
resizable: true,
fixed: true,
header: 'Modified',
dataIndex: 'OKYbKWWGyA',
renderer:

function coriolis_boolean_renderer(value, metadata, record, rowIndex, colIndex, store) {
if (value == true) {
return '<img src="images/check.png"/>';
}
return '';
}

})

,

new Ext.grid.Column({
id: 'xaUMmGfusl',
width: 250,
header: 'Stripe',
dataIndex: 'RaAvwJCOPh'

,
editor: new Ext.form.TextField({
id: 'epCkpWfcVR',
listeners: {
'autosize': {
fn: function(item, width) {}
},
'beforedestroy': {
fn: function(item) {}
},
'beforehide': {
fn: function(item) {}
},
'beforerender': {
fn: function(item) {}
},
'beforeshow': {
fn: function(item) {}
},
'beforestaterestore': {
fn: function(item, state) {}
},
'beforestatesave': {
fn: function(item, state) {}
},
'blur': {
fn: function(field) {}
},
'change': {
fn: function(field, newValue, oldValue) {}
},
'destroy': {
fn: function(item) {}
},
'disable': {
fn: function(item) {}
},
'enable': {
fn: function(item) {}
},
'focus': {
fn: function(field) {}
},
'hide': {
fn: function(item) {}
},
'invalid': {
fn: function(field, msg) {}
},
'keydown': {
fn: function(field, event) {}
},
'keypress': {
fn: function(field, event) {}
},
'keyup': {
fn: function(field, event) {}
},
'move': {
fn: function(item, x, y) {}
},
'render': {
fn: function(item) {}
},
'resize': {
fn: function(item, adjWidth, adjHeight, rawWidth) {}
},
'show': {
fn: function(item) {}
},
'specialkey': {
fn: function(field, event) {}
},
'staterestore': {
fn: function(item, state) {}
},
'statesave': {
fn: function(item, state) {}
},
'valid': {
fn: function(field) {}
}
}
})

})

,

new Ext.grid.Column({
id: 'DfODWYcbFU',
width: 250,
header: 'Default Display Option',
dataIndex: 'HPreiAfYSp'

,
editor: new Ext.form.ComboBox({
id: 'HNHcfJTpGY',
editable: false,
typeAhead: true,
triggerAction: 'all',
displayField: 'value',
mode: 'local',
store: new Ext.data.SimpleStore({
fields: ['value'],
data: [['True'], ['False']]
}),
listeners: {
'autosize': {
fn: function(item, width) {}
},
'beforedestroy': {
fn: function(item) {}
},
'beforehide': {
fn: function(item) {}
},
'beforequery': {
fn: function(item) {}
},
'beforerender': {
fn: function(item) {}
},
'beforeselect': {
fn: function(item, record, index) {}
},
'beforeshow': {
fn: function(item) {}
},
'beforestaterestore': {
fn: function(item, state) {}
},
'beforestatesave': {
fn: function(item, state) {}
},
'blur': {
fn: function(field) {}
},
'change': {
fn: function(field, newValue, oldValue) {}
},
'collapse': {
fn: function(field) {}
},
'destroy': {
fn: function(item) {}
},
'disable': {
fn: function(item) {}
},
'enable': {
fn: function(item) {}
},
'expand': {
fn: function(item) {}
},
'focus': {
fn: function(field) {}
},
'hide': {
fn: function(item) {}
},
'invalid': {
fn: function(field, msg) {}
},
'keydown': {
fn: function(field, event) {}
},
'keypress': {
fn: function(field, event) {}
},
'keyup': {
fn: function(field, event) {}
},
'move': {
fn: function(item, x, y) {}
},
'render': {
fn: function(item) {}
},
'resize': {
fn: function(item, adjWidth, adjHeight, rawWidth) {}
},
'select': {
fn: function(field, record, index) {}
},
'show': {
fn: function(item) {}
},
'specialkey': {
fn: function(field, event) {}
},
'staterestore': {
fn: function(item, state) {}
},
'statesave': {
fn: function(item, state) {}
},
'valid': {
fn: function(field) {}
}
}
})

})

,

new Ext.grid.Column({
id: 'FMtokjJqYt',
width: 150,
sortable: false,
editable: false,
header: 'Locked By',
dataIndex: 'PbHRsiFjaL'

})

,

new Ext.grid.Column({
id: 'bLqnXpFYtz',
width: 150,
sortable: false,
editable: false,
header: 'Locked Date',
dataIndex: 'QaWogeLlVB',
renderer:

function coriolis_datetime_renderer(value, metadata, record, rowIndex, colIndex, store) {
return value ? Ext.util.Format.date(value, 'm/d/Y h:i:s A') : '';
}

})

,

new Ext.grid.Column({
id: 'mTSXHtNqmR',
menuDisabled: false,
width: 80,
align: 'center',
sortable: false,
resizable: false,
fixed: true,
header: 'Inactive',
dataIndex: 'rhNTnmNOOD',
renderer:

function coriolis_boolean_renderer(value, metadata, record, rowIndex, colIndex, store) {
if (value == true) {
return '<img src="images/check.png"/>';
}
return '';
}

,
editor: new Ext.form.Checkbox({
id: 'VenQlKBvOr'
})

})

,

new Ext.grid.Column({
id: 'QItAMzopQG',
width: 250,
sortable: false,
resizable: true,
header: 'Comments',
dataIndex: 'qvJWValxBx'

,
editor: new Ext.form.TextField({
id: 'PivlTOFAEl',
listeners: {
'autosize': {
fn: function(item, width) {}
},
'beforedestroy': {
fn: function(item) {}
},
'beforehide': {
fn: function(item) {}
},
'beforerender': {
fn: function(item) {}
},
'beforeshow': {
fn: function(item) {}
},
'beforestaterestore': {
fn: function(item, state) {}
},
'beforestatesave': {
fn: function(item, state) {}
},
'blur': {
fn: function(field) {}
},
'change': {
fn: function(field, newValue, oldValue) {}
},
'destroy': {
fn: function(item) {}
},
'disable': {
fn: function(item) {}
},
'enable': {
fn: function(item) {}
},
'focus': {
fn: function(field) {}
},
'hide': {
fn: function(item) {}
},
'invalid': {
fn: function(field, msg) {}
},
'keydown': {
fn: function(field, event) {}
},
'keypress': {
fn: function(field, event) {}
},
'keyup': {
fn: function(field, event) {}
},
'move': {
fn: function(item, x, y) {}
},
'render': {
fn: function(item) {}
},
'resize': {
fn: function(item, adjWidth, adjHeight, rawWidth) {}
},
'show': {
fn: function(item) {}
},
'specialkey': {
fn: function(field, event) {}
},
'staterestore': {
fn: function(item, state) {}
},
'statesave': {
fn: function(item, state) {}
},
'valid': {
fn: function(field) {}
}
}
})

})

,

new Ext.grid.Column({
id: 'QpjlSrvbUs',
width: 250,
header: 'Java Class',
dataIndex: 'DvZDMHHDwu'

,
editor: new Ext.form.ComboBox({
id: 'bPaWVZTyCw',
editable: false,
typeAhead: true,
triggerAction: 'all',
displayField: 'value',
mode: 'local',
store: new Ext.data.SimpleStore({
fields: ['value'],
data: [['True'], ['False']]
}),
listeners: {
'autosize': {
fn: function(item, width) {}
},
'beforedestroy': {
fn: function(item) {}
},
'beforehide': {
fn: function(item) {}
},
'beforequery': {
fn: function(item) {}
},
'beforerender': {
fn: function(item) {}
},
'beforeselect': {
fn: function(item, record, index) {}
},
'beforeshow': {
fn: function(item) {}
},
'beforestaterestore': {
fn: function(item, state) {}
},
'beforestatesave': {
fn: function(item, state) {}
},
'blur': {
fn: function(field) {}
},
'change': {
fn: function(field, newValue, oldValue) {}
},
'collapse': {
fn: function(field) {}
},
'destroy': {
fn: function(item) {}
},
'disable': {
fn: function(item) {}
},
'enable': {
fn: function(item) {}
},
'expand': {
fn: function(item) {}
},
'focus': {
fn: function(field) {}
},
'hide': {
fn: function(item) {}
},
'invalid': {
fn: function(field, msg) {}
},
'keydown': {
fn: function(field, event) {}
},
'keypress': {
fn: function(field, event) {}
},
'keyup': {
fn: function(field, event) {}
},
'move': {
fn: function(item, x, y) {}
},
'render': {
fn: function(item) {}
},
'resize': {
fn: function(item, adjWidth, adjHeight, rawWidth) {}
},
'select': {
fn: function(field, record, index) {}
},
'show': {
fn: function(item) {}
},
'specialkey': {
fn: function(field, event) {}
},
'staterestore': {
fn: function(item, state) {}
},
'statesave': {
fn: function(item, state) {}
},
'valid': {
fn: function(field) {}
}
}
})

})

,

new Ext.grid.Column({
id: 'wwQXYYDApu',
width: 250,
header: 'Module',
dataIndex: 'nzVayCrVMC'

,
editor: new Ext.form.TextField({
id: 'VssOyZEoMk',
listeners: {
'autosize': {
fn: function(item, width) {}
},
'beforedestroy': {
fn: function(item) {}
},
'beforehide': {
fn: function(item) {}
},
'beforerender': {
fn: function(item) {}
},
'beforeshow': {
fn: function(item) {}
},
'beforestaterestore': {
fn: function(item, state) {}
},
'beforestatesave': {
fn: function(item, state) {}
},
'blur': {
fn: function(field) {}
},
'change': {
fn: function(field, newValue, oldValue) {}
},
'destroy': {
fn: function(item) {}
},
'disable': {
fn: function(item) {}
},
'enable': {
fn: function(item) {}
},
'focus': {
fn: function(field) {}
},
'hide': {
fn: function(item) {}
},
'invalid': {
fn: function(field, msg) {}
},
'keydown': {
fn: function(field, event) {}
},
'keypress': {
fn: function(field, event) {}
},
'keyup': {
fn: function(field, event) {}
},
'move': {
fn: function(item, x, y) {}
},
'render': {
fn: function(item) {}
},
'resize': {
fn: function(item, adjWidth, adjHeight, rawWidth) {}
},
'show': {
fn: function(item) {}
},
'specialkey': {
fn: function(field, event) {}
},
'staterestore': {
fn: function(item, state) {}
},
'statesave': {
fn: function(item, state) {}
},
'valid': {
fn: function(field) {}
}
}
})

})

,

new Ext.grid.Column({
id: 'hXynzUkdsp',
width: 250,
header: 'Repository',
dataIndex: 'ItGSsNCdiW',
renderer:

function renderer(value, metadata, record, rowIndex, colIndex, store) {
return Ext.util.Format.ellipsis(value, 25);
}

,
editor: new Ext.form.TextField({
id: 'pxvHnUkqiS',
listeners: {
'autosize': {
fn: function(item, width) {}
},
'beforedestroy': {
fn: function(item) {}
},
'beforehide': {
fn: function(item) {}
},
'beforerender': {
fn: function(item) {}
},
'beforeshow': {
fn: function(item) {}
},
'beforestaterestore': {
fn: function(item, state) {}
},
'beforestatesave': {
fn: function(item, state) {}
},
'blur': {
fn: function(field) {}
},
'change': {
fn: function(field, newValue, oldValue) {}
},
'destroy': {
fn: function(item) {}
},
'disable': {
fn: function(item) {}
},
'enable': {
fn: function(item) {}
},
'focus': {
fn: function(field) {}
},
'hide': {
fn: function(item) {}
},
'invalid': {
fn: function(field, msg) {}
},
'keydown': {
fn: function(field, event) {}
},
'keypress': {
fn: function(field, event) {}
},
'keyup': {
fn: function(field, event) {}
},
'move': {
fn: function(item, x, y) {}
},
'render': {
fn: function(item) {}
},
'resize': {
fn: function(item, adjWidth, adjHeight, rawWidth) {}
},
'show': {
fn: function(item) {}
},
'specialkey': {
fn: function(field, event) {}
},
'staterestore': {
fn: function(item, state) {}
},
'statesave': {
fn: function(item, state) {}
},
'valid': {
fn: function(field) {}
}
}
})

})

],
defaults: {
width: 90,
sortable: true,
menuDisabled: false
},
listeners: {
'columnmoved': {
fn: function(cm, oldIndex, newIndex) {}
},
'configchange': {
fn: function(cm) {}
},
'headerchange': {
fn: function(cm, columnIndex, newText) {}
},
'hiddenchange': {
fn: function(cm, columnIndex, hidden) {}
},
'widthchange': {
fn: function(cm, columnIndex, newWidth) {}
}
}
}),
listeners: {
'activate': {
fn: function(panel) {}
},
'add': {
fn: function(grid, component, index) {}
},
'afteredit': {
fn: function(event) {}
},
'afterlayout': {
fn: function(grid, layout) {}
},
'beforeadd': {
fn: function(grid, component, index) {}
},
'beforeclose': {
fn: function(panel) {}
},
'beforecollapse': {
fn: function(panel, animate) {}
},
'beforedestroy': {
fn: function(grid) {}
},
'beforeedit': {
fn: function(event) {
event.grid.getSelectionModel().lock();
}
},
'beforeexpand': {
fn: function(panel, animate) {}
},
'beforehide': {
fn: function(grid) {}
},
'beforeremove': {
fn: function(grid, component) {}
},
'beforerender': {
fn: function(grid) {}
},
'beforeshow': {
fn: function(grid) {}
},
'beforestaterestore': {
fn: function(grid, state) {}
},
'beforestatesave': {
fn: function(grid, state) {}
},
'bodyresize': {
fn: function(panel, width, height) {}
},
'bodyscroll': {
fn: function(scrollLeft, scrollTop) {}
},
'cellclick': {
fn: function(grid, rowIndex, columnIndex, event) {}
},
'cellcontextmenu': {
fn: function(grid, rowIndex, cellIndex) {}
},
'celldbclick': {
fn: function(grid, rowIndex, columnIndex, event) {}
},
'cellmousedown': {
fn: function(grid, rowIndex, columnIndex, event) {}
},
'click': {
fn: function(event) {}
},
'close': {
fn: function(panel) {}
},
'collapse': {
fn: function(panel) {}
},
'beforestatesave': {
fn: function() {}
},
'bodyresize': {
fn: function(panel, width, height) {}
},
'checkchange': {
fn: function(tree, checked) {}
},
'click': {
fn: function(node, event) {}
},
'close': {
fn: function(panel) {}
},
'collapse': {
fn: function(panel) {}
},
'columnmove': {
fn: function(oldIndex, newIndex) {}
},
'columnresize': {
fn: function(columnIndex, newSize) {}
},
'contextmenu': {
fn: function(node, event) {}
},
'dblclick': {
fn: function(event) {}
},
'deactivate': {
fn: function(panel) {}
},
'destroy': {
fn: function(grid) {}
},
'disable': {
fn: function(grid) {}
},
'enable': {
fn: function(grid) {}
},
'expand': {
fn: function(panel) {}
},
'headerclick': {
fn: function(grid, columnIndex, event) {}
},
'headercontextmenu': {
fn: function(grid, columnIndex, event) {}
},
'headerdblclick': {
fn: function(grid, columnIndex) {}
},
'headermousedown': {
fn: function(node) {}
},
'hide': {
fn: function(grid) {}
},
'iconchange': {
fn: function(panel, newIcon, oldIcon) {}
},
'keydown': {
fn: function(event) {}
},
'keypress': {
fn: function(event) {}
},
'mousedown': {
fn: function(event) {}
},
'mouseout': {
fn: function(event) {}
},
'mouseover': {
fn: function(event) {}
},
'mouseup': {
fn: function(event) {}
},
'move': {
fn: function(grid, x, y) {}
},
'remove': {
fn: function(grid, component) {}
},
'render': {
fn: function(grid) {}
},
'resize': {
fn: function(grid, adjWidth, adjHeight, rawWidth, rawHeight) {}
},
'rowclick': {
fn: function(grid, rowIndex, event) {}
},
'rowcontextmenu': {
fn: function(grid, rowIndex, event) {
event.stopEvent();
}
},
'rowdblclick': {
fn: function(grid, rowIndex, event) {}
},
'rowmousedown': {
fn: function(grid, rowIndex, event) {}
},
'show': {
fn: function(grid) {}
},
'sortchange': {
fn: function(grid, sortInfo) {}
},
'staterestore': {
fn: function(grid, state) {}
},
'statesave': {
fn: function(grid, state) {}
},
'titlechange': {
fn: function(panel, newTitle) {}
},
'validateedit': {
fn: function(object) {
alert("validateedit");
object.cancel = false;
}
}
}
})

ThorstenSuckow
26 Jun 2009, 4:47 AM
Fixed in r56 (http://wiki.ext-livegrid.com/changeset/56/branches). Error due to wrong call to parent's superclass "initComponent()" method.

coriolis
26 Jun 2009, 6:25 AM
Hi,

Will this very essential extension support GroupingView and GroupingStore soon? I hope so since I absolutely need this.

Thanks

lxf1101
30 Jun 2009, 8:08 AM
The LiveGrid is fantastic, love it.
I do have one question and I apologise if someone has asked this before but I have searched and am unable to find a solution.
When I have variable row heights in a grid and I scroll to the end of the grid there are a few records that are unreachable.
I think this is because the onLiveScroll method in the view takes the first row in the current view and uses this rowHeight to calculate if it is at the end of the records.
The problem comes if the remaining rows have a bigger row height then some are off the screen but the scrollbar down arrow is disabled.
Has anybody seen this and does anybody know of a solution?

Any help is very much appreciated.

f1xxx3r
2 Jul 2009, 5:12 AM
Hello,
I was assigned to replace all the grids with paging in a application with this component.
It is generated with php and it's loaded in a tabpanel.
The grid shows up, it loads the first 300(given) records and when I scroll down it makes another request from the 7th record (start: 7 param) and then crashes with r is undefined (ext-all-debug.js (line 32435)).

ext-ux-livegridnightlyr49
ext-2.2.1
Here's the code (I made just small modifications to convert it to live)



var echild = Ext.get('queryUsersqueryParentsgrid');
var eparent = Ext.get('queryUsersqueryParentsgrid').parent();
var leftAlign = (eparent.getWidth()-echild.getWidth())/2;
Ext.get('queryUsersqueryParentstopsearchbox').applyStyles('position: absolute; left:'+leftAlign+'px;');
Ext.get('queryUsersqueryParentsgrid').applyStyles('position: absolute; left:'+leftAlign+'px; clear: left');

// Ext.state.Manager.setProvider(new Ext.state.CookieProvider());


var myView = new Ext.ux.grid.livegrid.GridView({
nearLimit : '100',
loadMask : {
msg : 'Buffering. Please wait...'
}
});

var store = new Ext.ux.grid.livegrid.Store({
proxy: new Ext.data.HttpProxy({
url: 'queryUsers/queryParentsSearch/'
}),
bufferSize : '300',
autoLoad: true,
reader: new Ext.ux.grid.livegrid.JsonReader({
root: 'results',
totalProperty: 'total',
versionProperty : 'version',
id: 'id'
},[
{name: 'id'},{name: 'pid'},{name: 'student'},{name: 'parent'},{name: 'sid'}
]
)
});

var cm = new Ext.grid.ColumnModel([
new Ext.grid.RowNumberer({header : "#" , width : 40}),{header: 'Student', sortable: true, dataIndex: 'student'},{header: 'Parent', sortable: true, dataIndex: 'parent'} ]);

grid = new Ext.ux.grid.livegrid.GridPanel({
id: 'queryUsersqueryParentsgrid',
enableDragDrop : false,
store: store,
cm: cm,
stripeRows: true,
selModel: new Ext.ux.grid.livegrid.RowSelectionModel(),
view: myView,
height:470,
width:550,
title:'',
bbar: new Ext.ux.grid.livegrid.Toolbar({
view: myView,
displayInfo: true
}),
loadMask: {
msg: 'Loading...'
}
,renderTo: 'queryUsersqueryParentsgrid'
,tbar:[editParentBtn, deleteParentBtn] });

grid.render('queryUsersqueryParentsgrid');
Cheers

gelleneu
2 Jul 2009, 6:20 AM
In Ext3.0 RC3 the load Mask appears, and then, the ErrorConsole
sends a message:

Error: this._loadMaskAnchor._mask is undefined
Zeile: 566

The LoadMask never disappears.

"Normal" grids, are working perfectly, In Ext3.0Rc2 the Livegrid (built 56) worked fine too.

ThorstenSuckow
3 Jul 2009, 1:07 PM
In Ext3.0 RC3 the load Mask appears, and then, the ErrorConsole
sends a message:

Error: this._loadMaskAnchor._mask is undefined
Zeile: 566

The LoadMask never disappears.

"Normal" grids, are working perfectly, In Ext3.0Rc2 the Livegrid (built 56) worked fine too.

Haven't tested against RC3, I guess the next version for livegrid will be Ext3.0 final though, since its scheduled for monday.

MD
5 Jul 2009, 9:53 AM
After several messages, you left me hanging -- I haven't heard back from you. You mentioned it would be fairly easy -- do you still have plans to implement support for the the row expander plugin/grouping? I see from this thread that I'm now not the only one hoping to see this functionality added. :)

ThorstenSuckow
5 Jul 2009, 10:28 AM
Hi MD,


After several messages, you left me hanging -- I haven't heard back from you. You mentioned it would be fairly easy -- do you still have plans to implement support for the the row expander plugin/grouping? I see from this thread that I'm now not the only one hoping to see this functionality added. :)

I'm pretty sure this is not the case - right now there is more important stuff to do. Since it's open source your welcome to contribute your own ideas regarding support for row expander.

MD
5 Jul 2009, 11:22 AM
Sorry, I'm not clear -- you're pretty sure what is not the case? I realize there is other higher priority stuff, and I wasn't expecting anything immediately, which is why I only asked if you still had _plans_ for it. I don't understand, did I hit a sore spot with you or something?


I'm pretty sure this is not the case - right now there is more important stuff to do. Since it's open source your welcome to contribute your own ideas regarding support for row expander.

In one of our last PM correspondences on the subject, you said:
Regarding the RowExpander - never used it, but searched the forums; yes, it's used by a number of people and I think it would be a nice addition.

I immediately knew the problem you are referring to - the computing of the scroll offset and the cursor position is based on a fixed row-height - I think I can tweak it easily so that RowExpander is supported.

I guess I got the wrong impression from your last line there.

ThorstenSuckow
5 Jul 2009, 12:03 PM
I guess I got the wrong impression from your last line there.

If you want to continue the discussion why there hasn't been the time to implement the feature you can pm me. Other than that: See post above.

MD
5 Jul 2009, 12:53 PM
Again, to be clear, I was in no way expecting/demanding anything, I was simply asking if you had plans for reworking the ensureVisible method to support it, not when it would be added or why it hasn't been added. I held off on re-attempting to change ensureVisible based on my evidently incorrect assumption re: that last line. And that's fine, I'll have a go at it again on my own.

PM sent.

MD


If you want to continue the discussion why there hasn't been the time to implement the feature you can pm me. Other than that: See post above.

bcmatz
6 Jul 2009, 12:45 PM
I was able to make this work by changing the assumption in BufferedGridView.adjustVisibleRows() that initializes the rowheight only once. So, the rowheight is still fixed over all the rows, but this allows it to change based on the rowbody mode in effect and basically allows for expressing data as "inline detail"

changed the beginning portion to

if (this.getRows()[0]) {
this.rowHeight = this.getRows()[0].offsetHeight;
} else {
return;
}

as of the version i have, this appears to be std in the code. can anyone confirm this ?

bcmatz
6 Jul 2009, 12:47 PM
I do not say it is impossible, BUT... the height of the scrollbar gets calculated using the fixed row height and the number of available data in the underlying data model. If the script doesn't know the row height of each record, it cannot draw the scrollbar correctly, thus, browsing through all of the data and each and every record may become clumsy or even impossible.



Edit:
Updated first post with contents of data-proxy.php

Has anybody gotten this to work ?

Zolcsi
7 Jul 2009, 6:47 AM
Hi,

I am trying to reload the livegrid using defer, but the loading mask won't disappear. Do you have any clue about what could cause this?

I am using this piece of code:


var store = Ext.getCmp('grdSzamla').getStore();
store.reload.defer(3000, store);

The loading mask appears, the store realoads, but the loading mask just doesn't want to disappear.

Thanks.

bcmatz
7 Jul 2009, 7:47 AM
Remove the 2nd param (store) from your reload.defer:
Should be store.reload.defer(3000);

-b

Zolcsi
7 Jul 2009, 7:56 AM
Remove the 2nd param (store) from your reload.defer:
Should be store.reload.defer(3000);

-b

If I remove it, then the store won't reload at all :(

ThorstenSuckow
7 Jul 2009, 4:28 PM
If I remove it, then the store won't reload at all :(

Second parameter is needed as it gives a hint for the scope to use - reload should be called in the scope of "store", so the argument is needed.

Anyway, to reload the store, use the following code:



var view = Ext.getCmp('grdSzamla').getView();
view.reset.defer(3000, view, [true]);


From the API-Doc (Ext.ux.livegrid.GridView.reset):



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



HTH!

Zolcsi
8 Jul 2009, 1:17 AM
Worked like a charm, thank you!

dahman7
8 Jul 2009, 3:38 AM
good very impressive work

excelsis
8 Jul 2009, 8:31 AM
Small fix (see below) required to handle case when header row size not known upon init.



Ext.override( Ext.ux.grid.livegrid.GridView, {
updateHeaders : function(){
this._gridViewSuperclass.updateHeaders.call(this);
this.hdHeight = this.mainHd.getHeight();
}
});


Here's the "use case".
I use LiveGrid with a custom dynamic grid builder that uses metadata stored in DB.
Grid initializes, makes ajax call for metadata, build column model and then makes ajax call for data. As a result the header row height on init is 11px (based on default empty CSS) but then changes to the correct height once the header data from the columnmodel has been applied. However, without the fix the liveScroll element is based on the initial 11px.

If there's a better way (or if this might have unintended consequences) please let me know...

lxf1101
8 Jul 2009, 12:01 PM
Hi Thorsten,

At first: really perfect work! Your code now is one of the most significant arguments in our EXT/Dojo choice for our further projects. Thanks for sharing!

And now the question. How do you think, is it possible to adopt your solution for the grid with non-constant row heights? I know you are using rowHeight field which is "computed once the store has been loaded for the first time and used for various calculations during the lifetime of the grid component" (your code comments cite), so it looks not so easy to change code for vary-height rows, but... What do you think about this issue? Do you plan to make such changes in the near future?

One suggestion: I realize that re-calculating row height for each row rendered in order to decide if we can draw the next row can significantly decrease the performance. But I think there can be two configurable modes - one like "constRowHeight : true" (current realization) and another like "constRowHeight : false" with slowest calculation but correct displaying vary-height rows.

Thank you in advance,
Andrey



Has anybody gotten this to work ?
I am having the same problem, was hoping somebody may have a solution..

ThorstenSuckow
9 Jul 2009, 4:57 AM
Ext.ux.Livegrid now works with Ext3.0.0. I have added some changes and fixes in r57 (http://wiki.ext-livegrid.com/changeset/57).

nctag
10 Jul 2009, 12:18 AM
Thank you.
Nice release.
Works fine with Ext 3.0.0 in FF 3.5 with FB 1.5X0a10!

Dumbledore
10 Jul 2009, 1:48 AM
i updated today and now i get following error when loading:


D.activeRequest is undefined

any ideas?

ThorstenSuckow
10 Jul 2009, 1:51 AM
i updated today and now i get following error when loading:


D.activeRequest is undefined

any ideas?

Can you use livegrid-all-debug.js in your setup and post some firebug output?

Are you using a custom HttpProxy for your Livegrid-Store?

Dumbledore
10 Jul 2009, 2:46 AM
yes i use a Ext.data.DirectProxy for the grid. I updated today on r58. With an older 3.x release it runs fine (i don´t know which one)

the error occurs in line 957

957 var proxy = store.proxy;
958 if (proxy.activeRequest[Ext.data.Api.actions.read]) {
959 proxy.getConnection().abort(proxy.activeRequest[Ext.data.Api.actions.read]);
960 }

Bye, dumbledore

ThorstenSuckow
10 Jul 2009, 4:19 AM
yes i use a Ext.data.DirectProxy for the grid. I updated today on r58. With an older 3.x release it runs fine (i don´t know which one)

the error occurs in line 957

957 var proxy = store.proxy;
958 if (proxy.activeRequest[Ext.data.Api.actions.read]) {
959 proxy.getConnection().abort(proxy.activeRequest[Ext.data.Api.actions.read]);
960 }

Bye, dumbledore

Can you post your setup for the DirectProxy?

The problem is that Livegrid assumes that a HttpProxy is being used. I'm currently in the process of how to figure out how to cancel any ongoing (read) request made by a DirectProxy - any hint in form of your setup is greatly appreciated.

Dumbledore
10 Jul 2009, 4:22 AM
i defined the proxy like this:


var dproxy = new Ext.data.DirectProxy({
storeId : 'app_informationstore',
paramsAsHash: false,
paramOrder : ['product_id', 'start', 'limit', 'sort', 'dir'],
api: {
read: Ext.app.ProductInformation.getForProductID
}
});

ThorstenSuckow
10 Jul 2009, 4:26 AM
i defined the proxy like this:


var dproxy = new Ext.data.DirectProxy({
storeId : 'app_informationstore',
paramsAsHash: false,
paramOrder : ['product_id', 'start', 'limit', 'sort', 'dir'],
api: {
read: Ext.app.ProductInformation.getForProductID
}
});

What's the property for "read" - is it an url-string or a custom function that takes care of initiating a server request?

Dumbledore
10 Jul 2009, 5:00 AM
öhm... read is one command of the api of Ext.data.DirectProxy like in the documentation:



api : Object
Specific urls to call on CRUD action methods "read", "create", "update" and "destroy". Defaults to:

api: {
read : undefined,
create : undefined,
update : undefined,
destroy : undefined
}

If the specific URL for a given CRUD action is undefined, the CRUD action request will be directed to the configured url.

I use the Alternative Ext Direct PHP Implementation from TommyMaintz (http://extjs.com/forum/showthread.php?t=68186)

ThorstenSuckow
10 Jul 2009, 5:13 AM
Please comment the erroneous lines in livegrid-all-debug.js and tell me if it works - I'm getting a "directFn.appl is not a function"-error which tells my right now that there's something worg with building up a connection object...

Dumbledore
10 Jul 2009, 5:25 AM
Hi,

when i comment out the lines, all runs fine!

ThorstenSuckow
10 Jul 2009, 5:28 AM
Hi,

when i comment out the lines, all runs fine!

wtf?

Can you please post your complete example?

Are you adding some kind of Provider using Ext.Direct.addProvider?

ThorstenSuckow
10 Jul 2009, 5:32 AM
and just to clarify: In your example, api.read does hold a string, correct?

Dumbledore
10 Jul 2009, 5:45 AM
ui, a complete example is a little bit complicated...

I add a Provider via Ext.Direct.addProvider(Ext.app.REMOTING_API);

read holds a function, see image:

ThorstenSuckow
10 Jul 2009, 5:49 AM
ui, a complete example is a little bit complicated...

I add a Provider via Ext.Direct.addProvider(Ext.app.REMOTING_API);

read holds a function, see image:

Okay, I thought it would contain an url to call on the server. How does your api.read method look like? Are you building up a connection object in there?

What I don't get: The doc clearly states that it's okay to pass either a string or a function to api.read/destroy/update/write - however, passing strings will trigger a few errors - see http://extjs.com/forum/showthread.php?p=356070#post356070

ThorstenSuckow
10 Jul 2009, 6:08 AM
Fixed in r59 - see http://wiki.ext-livegrid.com/changeset/59

From the docs:

Ext.ux.grid.livegrid.GridView.onBeforeLoad():



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}.
Note:
If you are using a custom proxy, such as {Ext.data.DirectProxy}, you should listen
to the 'abortrequest'-event, which will tell that an ongoing "read" request should be
aborted, since the grid's store gets refreshed.
If the store is using an instance of {Ext.data.HttpProxy}, the method will still be
fired, but the request made through this proxy will be aborted automatically.

@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

ThorstenSuckow
11 Jul 2009, 5:43 AM
Ext.ux.Livegrid 0.3.1 was released today. This is a maintenance release which fixes several bugs regarding focus-loss of the scrollbar and an issue with the "load"-listener once the Grid has buffered data for the first time.

Note:
This release is for Ext 2.2.1 only! People who are using Ext 3.0.0 already should refer to the nightlies for Ext 3.0.0, which are also available from the download page.

see also http://www.ext-livegrid.com/2009/07/extuxlivegrid-031-released/

Dumbledore
11 Jul 2009, 7:01 AM
it seems that everthing runs fine now. Really nice work!

Bye, Dumbledore

Dumbledore
12 Jul 2009, 12:35 AM
:-) Next question

how to add toolbar items on the right side of the refreshing button? When i try following:



bbar : new Ext.ux.grid.livegrid.Toolbar({
view : myView,
displayInfo : true,
items : [
{
pressed : false,
enableToggle : true,
text : 'Vorschau',
iconCls : 'ctb_show_preview',
toggleHandler : function(state){
information_expander.toggleAll(state)
}
}
]
}),


it will allways add the buttons on the left side...

Bye, Dumbledore

ThorstenSuckow
12 Jul 2009, 5:43 AM
:-) Next question

how to add toolbar items on the right side of the refreshing button? When i try following:



bbar : new Ext.ux.grid.livegrid.Toolbar({
view : myView,
displayInfo : true,
items : [
{
pressed : false,
enableToggle : true,
text : 'Vorschau',
iconCls : 'ctb_show_preview',
toggleHandler : function(state){
information_expander.toggleAll(state)
}
}
]
}),


it will allways add the buttons on the left side...

Bye, Dumbledore


Best way right now would be to override the Toolbar's "onRender" method - just like this:



bbar : new Ext.ux.grid.livegrid.Toolbar({
view : myView,
displayInfo : true,
onRender : function() {
Ext.PagingToolbar.superclass.onRender.call(this, ct, position);

this.loading = new Ext.Toolbar.Button({
tooltip : this.refreshText,
iconCls : "x-tbar-loading",
handler : this.onClick.createDelegate(this, ["refresh"])
});

this.addButton(this.loading);

this.addFill();

this.addButton({
pressed : false,
enableToggle : true,
text : 'Vorschau',
iconCls : 'ctb_show_preview',
toggleHandler : function(state){
information_expander.toggleAll(state)
}
});

this.addSeparator();

if(this.displayInfo){
this.displayEl = Ext.fly(this.el.dom).createChild({cls:'x-paging-info'});
}
}
})




I'm looking into a way to make this more flexible.

animeshsingh
13 Jul 2009, 10:26 PM
Hi, I am using extjs livegrid "ext-ux-livegridnightlyr59" My grid is rendered fine and record are showing well, I am using Search (filter) along with this, so when record is less then the grid overflow, the scroll is still there in the grid Panel and when I scroll the it , it shows me error
in the line p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds); of following code r is undefined . in ext-all-debug file






doRender : function(cs, rs, ds, startRow, colCount, stripe){
var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
var tstyle = 'width:'+this.getTotalWidth()+';';
// buffers
var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
for(var j = 0, len = rs.length; j < len; j++){
r = rs[j]; cb = [];
var rowIndex = (j+startRow);
for(var i = 0; i < colCount; i++){
c = cs[i];
p.id = c.id;
p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
p.attr = p.cellAttr = "";
p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
p.style = c.style;
if(p.value == undefined || p.value === "") p.value = " ";
if(this.markDirty && r.dirty && typeof r.modified[c.name] !== 'undefined'){
p.css += ' x-grid3-dirty-cell';
}
cb[cb.length] = ct.apply(p);
}
var alt = [];
if(stripe && ((rowIndex+1) % 2 == 0)){
alt[0] = "x-grid3-row-alt";
}
if(r.dirty){
alt[1] = " x-grid3-dirty-row";
}
rp.cols = colCount;
if(this.getRowClass){
alt[2] = this.getRowClass(r, rowIndex, rp, ds);
}
rp.alt = alt.join(" ");
rp.cells = cb.join("");
buf[buf.length] = rt.apply(rp);
}
return buf.join("");
},



Please help...

ThorstenSuckow
14 Jul 2009, 1:09 AM
Hi, I am using extjs livegrid "ext-ux-livegridnightlyr59" My grid is rendered fine and record are showing well, I am using Search (filter) along with this, so when record is less then the grid overflow, the scroll is still there in the grid Panel and when I scroll the it , it shows me error
in the line p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds); of following code r is undefined . in ext-all-debug file





...



Please help...

Which Plugin are you using, exactly? A search has always to be done serverside as the grid was implemented to render only a snapshot of the actual data at a time...

animeshsingh
14 Jul 2009, 10:28 PM
Hi I am using
Ext.app.SearchField({ }); for search from database.. as the record is searched and the total size count is less then the overflow of the grid , and I scroll it , it gives me error , (my count is not changed it remains same(670) as I have not changed it)


Thanks

PremiereGlobal
22 Jul 2009, 10:20 AM
Hi,

My requirement is to show large number of data without pagination and livegrid seems to fit in my requirement, I've few questions before I start on this.

1. Is this editable too?
2. Has it been released for EXT3.0 RC2?


Thanks,
Hemant

ThorstenSuckow
22 Jul 2009, 10:22 AM
1. Is this editable too?


Yes.



2. Has it been released for EXT3.0 RC2?


For Ext 3.0RC you need to get an older revision. I don't recommend this. If you want to use Ext 3.0.0 final - see http://www.ext-livegrid.com/download/

PremiereGlobal
22 Jul 2009, 11:07 AM
Thanks for the information.

In the downloaded example i don't see any documentation and there is no example for editable livegrid. is it not required? or one can use your editablegridpanel similar to ext js editable grid panel, Is there anything (documentation or example) which can enable to make smooth start with this?

Thanks,
Hemant

excelsis
22 Jul 2009, 12:18 PM
Latest code Rev 59 from SVN.
Can enter an endless loop scrolling to top of a LiveGrid when number of visible rows is greater than bufferSize/2.
Cause seems to be in getPredictedBufferIndex

return Math.max(0, (index + this.visibleRows) - Math.round(this.ds.bufferSize/2));

index is 0; visible rows is 30, buffersize is 50 - so result is always 5 which means that the server request has a params.start of 5 and of course the target index of 0 will continue to fail the inRange() check and will try and buffer, come back here and get a value of 5 again ad infinitum....

I am using a low buffersize because each row of data is very expensive to produce at the server. The visible rows is normally smaller but the user resized and then tried to drag scoller to top.

I can add some patch code of my own but if there is a strict relationship/assumption about the size of buffer and/or nearlimit relative to visible rows I suggest this should be "enforced/policed" in the component.
My fix is simply:


return Math.max(0, (index + this.visibleRows) - Math.max( this.visibleRows, Math.round(this.ds.bufferSize/2) ) );

ThorstenSuckow
22 Jul 2009, 1:04 PM
Latest code Rev 59 from SVN.
Can enter an endless loop scrolling to top of a LiveGrid when number of visible rows is greater than bufferSize/2.

What's your "nearLimit"?

excelsis
22 Jul 2009, 1:10 PM
nearLimit was 15

jheid
24 Jul 2009, 2:30 AM
I have a problem with Ext 3.0 inside a border layout.
Using the livegrid.GridView only one row is displayed at the bottom together with the header above.
From Firebug I can see that <div class="liveScroller"> uses 468 px which acts like a spacer between the top of the grid and the header.

Any idea?

BTW, I've added Ext.reg ('languagecombo', Ext.ux.LanguageCombo); Any reason why I can't find it in the source?

ThorstenSuckow
24 Jul 2009, 3:43 AM
I have a problem with Ext 3.0 inside a border layout.
Using the livegrid.GridView only one row is displayed at the bottom together with the header above.
From Firebug I can see that <div class="liveScroller"> uses 468 px which acts like a spacer between the top of the grid and the header.

Any idea?


That definitely sounds like you did not include the css... otherwise, please post your setup :)

jheid
24 Jul 2009, 3:52 AM
That definitely sounds like you did not include the css... otherwise, please post your setup :)

You got me! Now I will hide myself :)

iancmcc
26 Jul 2009, 11:40 AM
I'm having a problem with maintaining selections. I'm using revision 62 against Ext 3.0.0 final.

When rows are selected, and the grid is scrolled far enough to make a request, then scrolled back up again (causing another request), the rows are no longer selected.

Examining the records as they pass through replaceSelections(), I noticed that the ids of the records were incremented, not decremented. That is, the first request after scrolling down yielded records ext-record-101 through ext-record-200. The request made upon scrolling back up returned records ext-record-201 through ext-record-300, when it should have returned records somewhere between ext-record-0 and ext-record-100. Since that id is used to compute selection status, I figured that was probably the problem, but can't figure out anything I might have done to cause it.

I added a simple log statement inside the for block in replaceSelections(), line 2404 of livegrid-all-debug.js:


for (i = 0, len = records.length; i < len; i++) {
rec = records[i];
id = rec.id;

// Debug statement
console.log(rec.id);

if (assigned.indexOf(id) == -1 && selections.containsKey(id)) {
console.log(rec);
selections.add(rec);
}
}bufferSize is 100, nearLimit is 50.

Can others reproduce this? Have I neglected to pass some config option in somewhere?

hallikpapa
27 Jul 2009, 10:31 PM
In Ext3.0 RC3 the load Mask appears, and then, the ErrorConsole
sends a message:

Error: this._loadMaskAnchor._mask is undefined
Zeile: 566

The LoadMask never disappears.

"Normal" grids, are working perfectly, In Ext3.0Rc2 the Livegrid (built 56) worked fine too.

I am getting this same error to trying to include the GroupingView and GroupingStore vars & methods into the live-grid-all-debug.js. All the menus look fine, but rendering the data like it did before I tried to include grouping has caused it to fail there.

Even when I ignore the loadMaskStore temporarily, I get the next error here?
gs[i].childNodes[1].childNodes; is null



// private
getRows : function(){
if(!this.enableGrouping){
return Ext.ux.grid.livegrid.GridView.superclass.getRows.call(this);
}
var r = [];
console.log(this.getGroups());
var g, gs = this.getGroups();
for(var i = 0, len = gs.length; i < len; i++){
g = gs[i].childNodes[1].childNodes;
for(var j = 0, jlen = g.length; j < jlen; j++){
r[r.length] = g[j];
}
}
return r;
},

sanjivank
28 Jul 2009, 12:15 AM
Hi,

Is there any tutorial or example with php , mysql and livegrid?

flylaputa
28 Jul 2009, 1:10 AM
Hi,

I am wondering if livegrid supports grouping?

hallikpapa
28 Jul 2009, 8:32 AM
Hi,

I am wondering if livegrid supports grouping?

Right now it doesn't. I am trying to add it. Here is my livegrid-all-debug.js. I have included the GroupingStore & GroupingView methods within it. On line 860, it dies. Cannot find childNodes[1]. If I set it to [0], it loads the data, but it's off centered until I sort, but grouping isn't working (even though the menu options are there).

So I am getting close if anyone wants to help, since there are lots of requests for LiveGrid grouping. Get this code, and search for NWB if you want to see where I made edits to include the grouping js.

The problem is here:
// private
getGroups : function(){
console.log(this.mainBody.dom.childNodes);
return this.hasRows() ? this.mainBody.dom.childNodes : [];
},

It returns empty, instead of the initial group divs to group by.


/**
* 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, {

initComponent : function()
{
if (this.cls) {
this.cls += ' ext-ux-livegrid';
} else {
this.cls = 'ext-ux-livegrid';
}

Ext.ux.grid.livegrid.GridPanel.superclass.initComponent.call(this);
},

/**
* 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,
/**
* @event abortrequest
* Fires when the store is about to reload (this does NOT mean buffering).
* If you are using a custom proxy in your store, you should listen to this event
* and abort any ongoing server request established in your custom proxy.
* @param {Ext.data.Store} store
* @param {Object} options
*/
'abortrequest' : 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;

/**
* @type {Boolean} _checkEmptyBody Since Ext 3.0, &nbsp; would initially added to the mainBody
* as the first child if there are no rows to render. This element has to be removed when
* the first rows get added so the UI does not crash. This property is here to determine if
* this element was already removed, so we don't have to query innerHTML all the time.
*/
this._checkEmptyBody = true;

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="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" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
'<div class="x-grid3-scroller" style="overflow-y:hidden !important;"><div class="x-grid3-body" style="{bstyle}">{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, {

//NWB Start************************
/**
* @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column
* (defaults to 'Group By This Field').
*/
groupByText : 'Group By This Field',
/**
* @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping
* (defaults to 'Show in Groups').
*/
showGroupsText : 'Show in Groups',
/**
* @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)
*/
hideGroupedColumn : false,
/**
* @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value
* in the group header line. The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>
* (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the
* {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).
*/
showGroupName : true,
/**
* @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)
*/
startCollapsed : false,
/**
* @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)
*/
enableGrouping : true,
/**
* @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)
*/
enableGroupingMenu : true,
/**
* @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)
*/
enableNoGroups : true,
/**
* @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).
* May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.
*/
emptyGroupText : '(None)',
/**
* @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)
*/
ignoreAdd : false,

groupTextTpl : '{text}',
/**
* @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for
* each column.
*/

// private
gidSeed : 1000,
//NWB End**************************
// {{{ --------------------------properties-------------------------------------

/**
* 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,

/**
* An {@Ext.LoadMask} config that will be shown when a request to data was made
* and there are no rows in the buffer left to render.
* @param {Object}
*/
loadMask : false,

/**
* 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,

/**
* The dom node for which the node mask will be rendered.
* @type {Ext.Element}
* @private
*/
_loadMaskAnchor : null,

// }}}

// {{{ --------------------------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.
*/
//NWB Comment out**********************
// 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._loadMaskAnchor = Ext.get(this.mainBody.dom.parentNode.parentNode);
// Ext.apply(this.loadMask,{
// msgCls : 'x-mask-loading'
// });
// this._loadMaskAnchor.mask(
// this.loadMask.msg, this.loadMask.msgCls
// );
// var dom = this._loadMaskAnchor.dom;
// var data = Ext.Element.data;
// data(dom, 'mask').addClass('ext-ux-livegrid');
// data(dom, 'mask').setDisplayed(false);
// data(dom, 'maskMsg').setDisplayed(false);
// }
// },

/**
* 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);
},

//NWB Start***************************
// private
initTemplates : function(){
this._gridViewSuperclass.initTemplates.call(this);
this.state = {};

var sm = this.grid.getSelectionModel();
sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',
this.onBeforeRowSelect, this);

if(!this.startGroup){
this.startGroup = new Ext.XTemplate(
'<div id="{groupId}" class="x-grid-group {cls}">',
'<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',
'<div id="{groupId}-bd" class="x-grid-group-body">'
);
}
this.startGroup.compile();
this.endGroup = '</div></div>';
},

// private
findGroup : function(el){
return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);
},

// private
getGroups : function(){
//console.log(this.mainBody.dom.childNodes);
return this.hasRows() ? this.mainBody.dom.childNodes : [];
},

// private
onAdd : function(){
if(this.enableGrouping && !this.ignoreAdd){
var ss = this.getScrollState();
this.refresh();
this.restoreScroll(ss);
}else if(!this.enableGrouping){
this._gridViewSuperclass.onAdd.apply(this, arguments);
}
},

// private
onRemove : function(ds, record, index, isUpdate){
this._gridViewSuperclass.onRemove.apply(this, arguments);
var g = document.getElementById(record._groupId);
if(g && g.childNodes[1].childNodes.length < 1){
Ext.removeNode(g);
}
this.applyEmptyText();
},

// private
refreshRow : function(record){
if(this.ds.getCount()==1){
this.refresh();
}else{
this.isUpdating = true;
this._gridViewSuperclass.refreshRow.apply(this, arguments);
this.isUpdating = false;
}
},

// private
beforeMenuShow : function(){
var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;
if((item = items.get('groupBy'))){
item.setDisabled(disabled);
}
if((item = items.get('showGroups'))){
item.setDisabled(disabled);
item.setChecked(!!this.getGroupField(), true);
}
},

// private
renderUI : function(){
this._gridViewSuperclass.renderUI.call(this);
this.mainBody.on('mousedown', this.interceptMouse, this);

if(this.enableGroupingMenu && this.hmenu){
this.hmenu.add('-',{
itemId:'groupBy',
text: this.groupByText,
handler: this.onGroupByClick,
scope: this,
iconCls:'x-group-by-icon'
});
if(this.enableNoGroups){
this.hmenu.add({
itemId:'showGroups',
text: this.showGroupsText,
checked: true,
checkHandler: this.onShowGroupsClick,
scope: this
});
}
this.hmenu.on('beforeshow', this.beforeMenuShow, this);
}
},

// private
onGroupByClick : function(){
this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));
this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups
},

// private
onShowGroupsClick : function(mi, checked){
if(checked){
this.onGroupByClick();
}else{
this.grid.store.clearGrouping();
}
},

/**
* Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.
* @param {String} groupId The groupId assigned to the group (see getGroupId)
* @param {Boolean} expanded (optional)
*/
toggleGroup : function(group, expanded){
this.grid.stopEditing(true);
group = Ext.getDom(group);
var gel = Ext.fly(group);
expanded = expanded !== undefined ?
expanded : gel.hasClass('x-grid-group-collapsed');

this.state[gel.dom.id] = expanded;
gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');
},

/**
* Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.
* @param {Boolean} expanded (optional)
*/
toggleAllGroups : function(expanded){
var groups = this.getGroups();
for(var i = 0, len = groups.length; i < len; i++){
this.toggleGroup(groups[i], expanded);
}
},

/**
* Expands all grouped rows.
*/
expandAllGroups : function(){
this.toggleAllGroups(true);
},

/**
* Collapses all grouped rows.
*/
collapseAllGroups : function(){
this.toggleAllGroups(false);
},

// private
interceptMouse : function(e){
var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
if(hd){
e.stopEvent();
this.toggleGroup(hd.parentNode);
}
},

// private
getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){
var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);
if(g === ''){
g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;
}
return g;
},

// private
getGroupField : function(){
return this.grid.store.getGroupState();
},

// private
afterRender : function(){
this._gridViewSuperclass.afterRender.call(this);
if(this.grid.deferRowRender){
this.updateGroupWidths();
}
},

// private
renderRows : function(){
var groupField = this.getGroupField();
var eg = !!groupField;
// if they turned off grouping and the last grouped field is hidden
if(this.hideGroupedColumn) {
var colIndex = this.cm.findColumnIndex(groupField);
if(!eg && this.lastGroupField !== undefined) {
this.mainBody.update('');
this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);
delete this.lastGroupField;
}else if (eg && this.lastGroupField === undefined) {
this.lastGroupField = groupField;
this.cm.setHidden(colIndex, true);
}else if (eg && this.lastGroupField !== undefined && groupField !== this.lastGroupField) {
this.mainBody.update('');
var oldIndex = this.cm.findColumnIndex(this.lastGroupField);
this.cm.setHidden(oldIndex, false);
this.lastGroupField = groupField;
this.cm.setHidden(colIndex, true);
}
}
return this._gridViewSuperclass.renderRows.apply(
this, arguments);
},

// private
doRender : function(cs, rs, ds, startRow, colCount, stripe){
if(rs.length < 1){
return '';
}
var groupField = this.getGroupField(),
colIndex = this.cm.findColumnIndex(groupField),
g;

this.enableGrouping = !!groupField;

if(!this.enableGrouping || this.isUpdating){
return this._gridViewSuperclass.doRender.apply(
this, arguments);
}
var gstyle = 'width:'+this.getTotalWidth()+';';

var gidPrefix = this.grid.getGridEl().id;
var cfg = this.cm.config[colIndex];
var groupRenderer = cfg.groupRenderer || cfg.renderer;
var prefix = this.showGroupName ?
(cfg.groupName || cfg.header)+': ' : '';

var groups = [], curGroup, i, len, gid;
for(i = 0, len = rs.length; i < len; i++){
var rowIndex = startRow + i,
r = rs[i],
gvalue = r.data[groupField];

g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);
if(!curGroup || curGroup.group != g){
gid = gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(g);
// if state is defined use it, however state is in terms of expanded
// so negate it, otherwise use the default.
var isCollapsed = typeof this.state[gid] !== 'undefined' ? !this.state[gid] : this.startCollapsed;
var gcls = isCollapsed ? 'x-grid-group-collapsed' : '';
curGroup = {
group: g,
gvalue: gvalue,
text: prefix + g,
groupId: gid,
startRow: rowIndex,
rs: [r],
cls: gcls,
style: gstyle
};
groups.push(curGroup);
}else{
curGroup.rs.push(r);
}
r._groupId = gid;
}

var buf = [];
for(i = 0, len = groups.length; i < len; i++){
g = groups[i];
this.doGroupStart(buf, g, cs, ds, colCount);
buf[buf.length] = this._gridViewSuperclass.doRender.call(
this, cs, g.rs, ds, g.startRow, colCount, stripe);

this.doGroupEnd(buf, g, cs, ds, colCount);
}
return buf.join('');
},

/**
* Dynamically tries to determine the groupId of a specific value
* @param {String} value
* @return {String} The group id
*/
getGroupId : function(value){
var gidPrefix = this.grid.getGridEl().id;
var groupField = this.getGroupField();
var colIndex = this.cm.findColumnIndex(groupField);
var cfg = this.cm.config[colIndex];
var groupRenderer = cfg.groupRenderer || cfg.renderer;
var gtext = this.getGroup(value, {data:{}}, groupRenderer, 0, colIndex, this.ds);
return gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(value);
},

// private
doGroupStart : function(buf, g, cs, ds, colCount){
buf[buf.length] = this.startGroup.apply(g);
},

// private
doGroupEnd : function(buf, g, cs, ds, colCount){
buf[buf.length] = this.endGroup;
},

// private
getRows : function(){
if(!this.enableGrouping){
return this._gridViewSuperclass.getRows.call(this);
}
var r = [];
var g, gs = this.getGroups();
for(var i = 0, len = gs.length; i < len; i++){
//console.log(gs[i].childNodes[1]);
g = gs[i].childNodes[1].childNodes;
for(var j = 0, jlen = g.length; j < jlen; j++){
r[r.length] = g[j];
}
}
return r;
},

// private
updateGroupWidths : function(){
if(!this.enableGrouping || !this.hasRows()){
return;
}
var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.scrollOffset) +'px';
var gs = this.getGroups();
for(var i = 0, len = gs.length; i < len; i++){
gs[i].firstChild.style.width = tw;
}
},

// private
onColumnWidthUpdated : function(col, w, tw){
this._gridViewSuperclass.onColumnWidthUpdated.call(this, col, w, tw);
this.updateGroupWidths();
},

// private
onAllColumnWidthsUpdated : function(ws, tw){
this._gridViewSuperclass.onAllColumnWidthsUpdated.call(this, ws, tw);
this.updateGroupWidths();
},

// private
onColumnHiddenUpdated : function(col, hidden, tw){
this._gridViewSuperclass.onColumnHiddenUpdated.call(this, col, hidden, tw);
this.updateGroupWidths();
},

// private
onLayout : function(){
this.updateGroupWidths();
},

// private
onBeforeRowSelect : function(sm, rowIndex){
if(!this.enableGrouping){
return;
}
var row = this.getRow(rowIndex);
if(row && !row.offsetParent){
var g = this.findGroup(row);
this.toggleGroup(g, true);
}
},
//NWB End******************************
/**
* 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(!g.hideHeaders && vw < 20 || csize.height < 20){ // display: none?
return;
}

if(g.autoHeight){
this.scroller.dom.style.overflow = 'visible';
if(Ext.isWebKit){
this.scroller.dom.style.position = 'static';
}
}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);
this.grid.fireEvent('columnmove', oldIndex, 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, this.selectedRowClass);
},

/**
* 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, this.selectedRowClass);
},


// {{{ ----------------------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)
{
if (this._checkEmptyBody) {
if (this.mainBody.dom.innerHTML == '&nbsp;') {
this.mainBody.dom.innerHTML = '';
}
this._checkEmptyBody = false;
}

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}.
* Note:
* If you are using a custom proxy, such as {Ext.data.DirectProxy}, you should listen
* to the 'abortrequest'-event, which will tell that an ongoing "read" request should be
* aborted, since the grid's store gets refreshed.
* If the store is using an instance of {Ext.data.HttpProxy}, the method will still be
* fired, but the request made through this proxy will be aborted automatically.
*
*
* @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)
{
var proxy = store.proxy;
if (proxy.activeRequest && proxy.activeRequest[Ext.data.Api.actions.read]) {
proxy.getConnection().abort(proxy.activeRequest[Ext.data.Api.actions.read]);
}
this.fireEvent('abortrequest', store, options);

this.isBuffering = false;
this.isPreBuffering = false;

options.params = options.params || {};

var apply = Ext.apply;

apply(options, {
scope : this,
callback : function(){
this.reset(false);
},
suspendLoadEvent : 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.adjustBufferInset();

this.fireEvent('buffer', this, this.ds, this.rowIndex,
Math.min(this.ds.totalLength, this.visibleRows-this.rowClipped),
this.ds.totalLength,
options
);

// 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);

this.isBuffering = false;
this.isPrebuffering = false;
this.showLoadMask(false);

if (this.requestQueue >= 0) {
var offset = this.requestQueue;
this.requestQueue = -1;
this.updateLiveRows(offset);
return;
}

if (this.isInRange(this.rowIndex)) {
this.replaceLiveRows(this.rowIndex, options.forceRepaint);
} else {
this.updateLiveRows(this.rowIndex);
}


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)
{
if(!this.ds || this.ds.getCount() < 1){
return;
}

var cursor = this.rowIndex;
skipStripe = skipStripe || !this.grid.stripeRows;
var rows = this.getRows();
var index = 0;

Ext.each(rows, function(row, idx) {
row.rowIndex = index = cursor+idx;
row.className = row.className.replace(this.rowClsRe, ' ');
if (!skipStripe && (index + 1) % 2 === 0) {
row.className += ' x-grid3-row-alt';
}

if (paintSelections !== false) {
if (this.grid.selModel.isSelected(this.ds.getAt(index)) === true) {
this.addRowClass(index, this.selectedRowClass);
} else {
this.removeRowClass(index, this.selectedRowClass);
}
this.fly(row).removeClass("x-grid3-row-over");
}
}, this);

// add first/last-row classes
if(cursor === 0){
Ext.fly(rows[0]).addClass(this.firstRowCls);
} else if (cursor + rows.length == this.ds.totalLength) {
Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
}
},

/**
* 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, forceRepaint);
} 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,
suspendLoadEvent : true
};

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) {
return;
}
this._loadMaskAnchor = Ext.get(this.mainBody.dom.parentNode.parentNode); //NWB Added
var dom = this._loadMaskAnchor.dom;
var data = Ext.Element.data;
//NWB Add Start****
Ext.apply(this.loadMask,{
msgCls : 'x-mask-loading'
});
this._loadMaskAnchor.mask(
this.loadMask.msg, this.loadMask.msgCls
);
//NWB Add End******
var mask = data(dom, 'mask');
var maskMsg = data(dom, 'maskMsg');

if (show) {
mask.setDisplayed(true);
maskMsg.setDisplayed(true);
maskMsg.center(this._loadMaskAnchor);
// this lines will help IE8 to re-calculate the height of the loadmask
if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && this._loadMaskAnchor.getStyle('height') == 'auto'){
mask.setSize(undefined, this._loadMaskAnchor.getHeight());
}
} else {
mask.setDisplayed(false);
maskMsg.setDisplayed(false);
}
},

/**
* 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, but not if we
// are just pre-buffering
if (this.isBuffering && !this.isPrebuffering) {
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.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;
}

});/**
* 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.applyGroupField(); //NWB
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,
//NWB Start**************
/**
* @cfg {String} groupField
* The field name by which to sort the store's data (defaults to '').
*/
/**
* @cfg {Boolean} remoteGroup
* True if the grouping should apply on the server side, false if it is local only (defaults to false). If the
* grouping is local, it can be applied immediately to the data. If it is remote, then it will simply act as a
* helper, automatically sending the grouping field name as the 'groupBy' param with each XHR call.
*/
remoteGroup : false,
/**
* @cfg {Boolean} groupOnSort
* True to sort the data on the grouping field when a grouping operation occurs, false to sort based on the
* existing sort info (defaults to false).
*/
groupOnSort:false,

groupDir : 'ASC',

/**
* Clears any existing grouping and refreshes the data using the default sort.
*/
clearGrouping : function(){
this.groupField = false;
if(this.remoteGroup){
if(this.baseParams){
delete this.baseParams.groupBy;
}
var lo = this.lastOptions;
if(lo && lo.params){
delete lo.params.groupBy;
}
this.reload();
}else{
this.applySort();
this.fireEvent('datachanged', this);
}
},

/**
* Groups the data by the specified field.
* @param {String} field The field name by which to sort the store's data
* @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed
* in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)
*/
groupBy : function(field, forceRegroup, direction){
direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;
if(this.groupField == field && this.groupDir == direction && !forceRegroup){
return; // already grouped by this field
}
this.groupField = field;
this.groupDir = direction;
this.applyGroupField();
if(this.groupOnSort){
this.sort(field, direction);
return;
}
if(this.remoteGroup){
this.reload();
}else{
var si = this.sortInfo || {};
if(si.field != field || si.direction != direction){
this.applySort();
}else{
this.sortData(field, direction);
}
this.fireEvent('datachanged', this);
}
},

// private
applyGroupField: function(){
if(this.remoteGroup){
if(!this.baseParams){
this.baseParams = {};
}
this.baseParams.groupBy = this.groupField;
this.baseParams.groupDir = this.groupDir;
}
},

// private
applySort : function(){
Ext.data.GroupingStore.superclass.applySort.call(this);
if(!this.groupOnSort && !this.remoteGroup){
var gs = this.getGroupState();
if(gs && (gs != this.sortInfo.field || this.groupDir != this.sortInfo.direction)){
this.sortData(this.groupField, this.groupDir);
}
}
},

// private
applyGrouping : function(alwaysFireChange){
if(this.groupField !== false){
this.groupBy(this.groupField, true, this.groupDir);
return true;
}else{
if(alwaysFireChange === true){
this.fireEvent('datachanged', this);
}
return false;
}
},

// private
getGroupState : function(){
return this.groupOnSort && this.groupField !== false ?
(this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;
},

//NWB End**************
/**
* 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[Ext.data.Api.actions.read]
&& 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))
];
}

if (options.suspendLoadEvent === true) {
this.suspendEvents();
}
Ext.ux.grid.livegrid.Store.superclass.loadRecords.call(this, o, options, success);
if (options.suspendLoadEvent === true) {
this.resumeEvents();
}
},

/**
* 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 = new Ext.Toolbar.Button({
tooltip : this.refreshText,
iconCls : "x-tbar-loading",
handler : this.onClick.createDelegate(this, ["refresh"])
});

this.addButton(this.loading);

this.addSeparator();

if(this.displayInfo){
this.displayEl = Ext.fly(this.el.dom).createChild({cls:'x-paging-info'});
}
}
});/**
* Ext.ux.grid.livegrid.DragZone
* Copyright (c) 2007-2008, http://www.siteartwork.de
*
* Ext.ux.grid.livegrid.DragZone 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.DragZone
* @extends Ext.dd.DragZone
* @author Thorsten Suckow-Homberg <ts@siteartwork.de>
*/
Ext.ux.grid.livegrid.DragZone = function(grid, config){

Ext.ux.grid.livegrid.DragZone.superclass.constructor.call(this, grid, config);

this.view.ds.on('beforeselectionsload', this._onBeforeSelectionsLoad, this);
this.view.ds.on('selectionsload', this._onSelectionsLoad, this);
};

Ext.extend(Ext.ux.grid.livegrid.DragZone, Ext.grid.GridDragZone, {

/**
* Tells whether a drop is valid. Used inetrnally to determine if pending
* selections need to be loaded/ have been loaded.
* @type {Boolean}
*/
isDropValid : true,

/**
* Overriden for loading pending selections if needed.
*/
onInitDrag : function(e)
{
this.view.ds.loadSelections(this.grid.selModel.getPendingSelections(true));

Ext.ux.grid.livegrid.DragZone.superclass.onInitDrag.call(this, e);
},

/**
* Gets called before pending selections are loaded. Any drop
* operations are invalid/get paused if the component needs to
* wait for selections to load from the server.
*
*/
_onBeforeSelectionsLoad : function()
{
this.isDropValid = false;
Ext.fly(this.proxy.el.dom.firstChild).addClass('ext-ux-livegrid-drop-waiting');
},

/**
* Gets called after pending selections have been loaded.
* Any paused drop operation will be resumed.
*
*/
_onSelectionsLoad : function()
{
this.isDropValid = true;
this.ddel.innerHTML = this.grid.getDragDropText();
Ext.fly(this.proxy.el.dom.firstChild).removeClass('ext-ux-livegrid-drop-waiting');
}
});/**
* Ext.ux.grid.livegrid.EditorGridPanel
* Copyright (c) 2007-2008, http://www.siteartwork.de
*
* Ext.ux.grid.livegrid.EditorGridPanel 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.EditorGridPanel
* @extends Ext.grid.EditorGridPanel
* @constructor
* @param {Object} config
*
* @author Thorsten Suckow-Homberg <ts@siteartwork.de>
*/
Ext.ux.grid.livegrid.EditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, {

/**
* Overriden so the panel listens to the "cursormove" event for
* cancelling any edit that is in progress.
*
* @private
*/
initEvents : function()
{
Ext.ux.grid.livegrid.EditorGridPanel.superclass.initEvents.call(this);

this.view.on("cursormove", this.stopEditing, this, [true]);
},

/**
* Starts editing the specified for the specified row/column
* Will be cancelled if the requested row index to edit is not
* represented by data due to out of range regarding the view's
* store buffer.
*
* @param {Number} rowIndex
* @param {Number} colIndex
*/
startEditing : function(row, col)
{
this.stopEditing();
if(this.colModel.isCellEditable(col, row)){
this.view.ensureVisible(row, col, true);
if (!this.store.getAt(row)) {
return;
}
}

return Ext.ux.grid.livegrid.EditorGridPanel.superclass.startEditing.call(this, row, col);
},

// Since we do not have multiple inheritance, we need to override the
// same methods in this class we have overriden for
// Ext.ux.grid.livegrid.GridPanel
walkCells : function(row, col, step, fn, scope)
{
return Ext.ux.grid.livegrid.GridPanel.prototype.walkCells.call(this, row, col, step, fn, scope);
},

onRender : function(ct, position)
{
return Ext.ux.grid.livegrid.GridPanel.prototype.onRender.call(this, ct, position);
},

initComponent : function()
{
if (this.cls) {
this.cls += ' ext-ux-livegrid';
} else {
this.cls = 'ext-ux-livegrid';
}

return Ext.ux.grid.livegrid.EditorGridPanel.superclass.initComponent.call(this);
}

});

hallikpapa
28 Jul 2009, 10:21 AM
Now I have it drawing the first row in the grid, and it dies here.

rows is undefined

line 1624 from code above


// add first/last-row classes
if(cursor === 0){
console.log(rows);
Ext.fly(rows[0]).addClass(this.firstRowCls);
} else if (cursor + rows.length == this.ds.totalLength) {
Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);


<edit> Actually the problem is still with the childNodes. I tried putting this in the getRows() method, but no bueno.

if (!gs[i] || !gs[i].childNodes[1] || !gs[i].childNodes[1].childNodes) { continue; } //NWB

I checked my data sample. Everything is returning, no fields have null values.

hallikpapa
28 Jul 2009, 12:59 PM
I got grouping working with live grid. Still has some needed tweaks, but it works. Here is the post if anyone wants to help improve on it or just use it. Thanks for the awesome plugin.

http://extjs.com/forum/showthread.php?p=365300

jheid
2 Aug 2009, 12:17 PM
Edit: Problem was in my own extended version. Nevermind.

weazil
17 Aug 2009, 5:54 AM
Curious if it was me or this did this to everyone.

in livegrid i have alternating row colors on, in ext2 it worked fine.. in 3 it looses the alternating color when you press the refresh button on the bottom toolbar. until you scroll up or down when it has to render the grid again.

also does params work on intial load? i could only get them to pass by doing a store.reload({params.....

jwendt@iscinternational.com
26 Aug 2009, 6:28 AM
First, I want to congratulate you on a great extension. We secured the commercial license and are currently using it in all of our grids!

Second, I apologize if this question was previously asked. I was experiencing grid column alignment problems on one of my grids. I looked back at others and found similar problems. To confirm I wasn't doing anything silly, I tried to replicate the problem on your demo site (http://www.siteartwork.de/livegrid_demo/). The attached image shows a snapshot of the problem.

The alignment appears to correct itself if you move the bottom scrollbar to the left most (home) position.

Any help would be greatly appreciated.

http://www.iscinternational.com/images/livegrid.gif

Dumbledore
27 Aug 2009, 2:02 AM
Hi,

i just updated to ExtJS 3.0.1. And now i get an error with the livegrid.

Currently i can´t figure out the reason, the error.message is:

params.jsonData is not a object

When using the Livegrid with a Direct-Proxy then the error occurs. With 3.0.0 there is no problem.

Any idea?

vinepod
14 Sep 2009, 1:18 PM
Thank you for this grid panel. Even without the data pagination, it is much lighter on it's feet than the core grid panel. We can now resize and reposition our grid panel without having to take a coffee break during the repaint.

In testing for our implementation, I have come across one issue. I want to call 'load' on your livegrid.Store to reload the data. It will not contain the same list of data. It is reloading for a different data set (based on date, id, or some other input from the user).

However, when I call 'load', the grid sits there without changing. If I just use the autoload : true and click the refresh button, all is well, but there does not seem to be a way to change the 'params : { .... }' dynamically.

I have rummaged thru livegrid.Store and happened upon 'onClick', but this does not seem to allow me to supply more params.

Can you point me in the right direction?

TIA.

**[edit]** - sorry... I was using the "Stable" download (Ext 2 only - Rrrrrrr). I just downloaded and tried the "Nightly" for Ext 3 (Ext.ux.Livegrid @r59 2009-07-10 (http://www.ext-livegrid.com/wp-content/uploads/2009/07/ext-ux-livegridnightlyr59.rar)), and the load function works fine.

However, in resizing the grid more than a 2 or 3 times causes the window holding the grid (window has sub panels, one of which contains the grid) to repaint very odd for a second and then vanish. There is an internal extjs javascript error that firebug complains about, but that is just a typical core side-affect of a larger problem with this nightly build for extjs 3.

This nightly release is not usable for us.

ibet
15 Sep 2009, 3:59 PM
very cool!
but :
when the store's autoLoad =false, use store.load()
like:


ds_md.load({params:{tj:'text'}});
grid_md.render();
however no data display only the header.
but :


grid_md.render();
ds_md.load({params:{tj:'text'}});

it works good.:>:>
I don't know it was a bug ot not.

alexpetri
16 Sep 2009, 6:18 AM
does the livegrid work with an XML Store / and filtering?

KirkOlson
18 Sep 2009, 1:57 AM
Originally Posted by Phenothiasine
Hi Thorsten,

At first: really perfect work! Your code now is one of the most significant arguments in our EXT/Dojo choice for our further projects. Thanks for sharing!

And now the question. How do you think, is it possible to adopt your solution for the grid with non-constant row heights? I know you are using rowHeight field which is "computed once the store has been loaded for the first time and used for various calculations during the lifetime of the grid component" (your code comments cite), so it looks not so easy to change code for vary-height rows, but... What do you think about this issue? Do you plan to make such changes in the near future?

One suggestion: I realize that re-calculating row height for each row rendered in order to decide if we can draw the next row can significantly decrease the performance. But I think there can be two configurable modes - one like "constRowHeight : true" (current realization) and another like "constRowHeight : false" with slowest calculation but correct displaying vary-height rows.

Thank you in advance,
Andrey


Originally Posted by bcmatz
Has anybody gotten this to work ?



I am having the same problem, was hoping somebody may have a solution..

Same problem here, still no solution around?

jheid
19 Sep 2009, 10:58 AM
Found a nasty bug in the 3.x version:

Replace all


this.scrollOffset

to


this.horizontalScrollOffset

or you will get an error in the IE.

Cheers,
Jörn

ThorstenSuckow
19 Sep 2009, 5:55 PM
However, in resizing the grid more than a 2 or 3 times causes the window holding the grid (window has sub panels, one of which contains the grid) to repaint very odd for a second and then vanish.

Did you include the CSS file?

ThorstenSuckow
19 Sep 2009, 5:56 PM
Found a nasty bug in the 3.x version:

Replace all


this.scrollOffset

to


this.horizontalScrollOffset

or you will get an error in the IE.

Cheers,
Jörn


No problems here whatsoever, been using the grid with 3.0 for months now. Do you have a more detailed issue report?

jheid
19 Sep 2009, 10:17 PM
No problems here whatsoever, been using the grid with 3.0 for months now. Do you have a more detailed issue report?

I don't know exactly. I know there IS the mistyping and I know that I haven't had problems until now. It may have occured after switching to ExtJS 3.0.1 and 3.0.2. And it only occurs in IE (8). The problem is that adjustHeight will fail because of that.

ThorstenSuckow
29 Sep 2009, 2:48 PM
I don't know exactly. I know there IS the mistyping and I know that I haven't had problems until now. It may have occured after switching to ExtJS 3.0.1 and 3.0.2. And it only occurs in IE (8). The problem is that adjustHeight will fail because of that.

confirmed for 3.0.2

cdeguzman
1 Oct 2009, 3:47 AM
Hello, we've recently migrated one of our apps from ExtJS 2.0.2 to ExtJS 3.0. Part of the effort was to replace our livegrid versions. We've gotten most of the errors, but somehow, one of our grids just won't work properly. The issue we're having is that some rows are being painted twice.

For example, I have a grid that displays 20 rows at a time, and there are 25 rows to be displayed. The initial screen rendering is correct, but when I scroll down, the 21st row appears twice, like so:

21st row
22nd row
23rd row
24th row
25th row
21st row

I either have to resize my screen or scroll up and down several times for the last row to disappear.

On debugging, I saw the individual rows being added by GridView.onRender(), and then a call to Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html) re-adds the 5 rows at the bottom of the grid.

Has anyone else encountered this issue before? Thanks a lot!

Animal
9 Oct 2009, 1:51 AM
Hi Thorsten,

A couple of things have come up in the forums.

I think the new (Ext 3.0) Panel bodyscroll event needs to be fired from the live GridView to allow for saving of the current scroll position.

Right now, I use a function sequence:



Ext.ux.grid.livegrid.GridView.prototype.onLiveScroll =
Ext.ux.grid.livegrid.GridView.prototype.onLiveScroll.createSequence(function() {
this.grid.fireEvent("bodyscroll", this.scroller.dom.scrollLeft, this.liveScroller.dom.scrollTop);
});


But I think onLiveScroll should fire this event so that the live scroll position can be saved and restored easily.

Could be that it also needs an easy way to restore position because people won't want to read the source to find the liveScroller.dom element, and won't understand what scrollTop is!



/**
* Restores the LiveGrid's scroll position, and loads the data.
*/
scrollTo: function(pos) {
this.liveScroller.dom.scrollTop = pos;
}

ThorstenSuckow
9 Oct 2009, 8:10 AM
Thanks Animal, I'm curently in the process of deciding whether I create another branch for the patch releases, since not all users have a silver subscription and might still rely on the point release.

Expect some larger bug fixes for Ext 3.0.* in the next week.

NicoP
12 Oct 2009, 2:55 AM
Hi,

I have a LiveGrid with about 500.000 rows or higher. If a user try select a large amount of rows I get a script error : "A script on this page may be busy or may not responded". The CPU on the client side is 100% busy.

Is there a way to disable multiple row selection when a user try to select for instance more than 1000 rows?

Thanks.

ThorstenSuckow
12 Oct 2009, 3:01 AM
Hi,

I have a LiveGrid with about 500.000 rows or higher. If a user try select a large amount of rows I get a script error : "A script on this page may be busy or may not responded". The CPU on the client side is 100% busy.

Is there a way to disable multiple row selection when a user try to select for instance more than 1000 rows?

Thanks.

1) What's the size of your buffer?
2) What Ext version are you using?
3) Did you try with firebug disabled?

NicoP
12 Oct 2009, 4:03 AM
Hi Mind,


1) What's the size of your buffer?


Here my live grid parameters:
buffer.size=60
near.limit=10



2) What Ext version are you using?


I am trying to do some tests under Ext2.2.1 and 3.0.0. The problems is here on the both releases.


3) Did you try with firebug disabled?

Yes, the problem still remains. See attached screenshot of my demo grid page with 2 million rows.

I can select about 20.000 rows max without errors (It takes approximatively 10seconds). The grid try to keep in memory all selected rows that is why the browser is busy.
A solution will be to disable multi row selection if selection size is greater than for instance 1000 or 5000 rows. But I don't know if it is possible.

NicoP
12 Oct 2009, 4:28 AM
Mind,

Just for your information I also have disabled the show load mask when I use the vertical scrollbar : as my json flow is returned by the server side in about 60ms, the buffer refresh on screen is very quick and more user friendly. The load mask take too much time to be displayed.

However I keep the load mask when I sort or filter the grid data especially if I have a lot of rows :)

ThorstenSuckow
12 Oct 2009, 6:09 AM
I just checked the selection with appr. 45000 rows - now problem whatsoever so I still assume it has to deal with your hardware - I'm using a Win XP, FF3.5.3, Dual Core 2,4 with 3 Gb RAM - I notice some delay but no warnings from FF whatsoever.

NicoP
12 Oct 2009, 6:14 AM
I just checked the selection with appr. 45000 rows - now problem whatsoever so I still assume it has to deal with your hardware - I'm using a Win XP, FF3.5.3, Dual Core 2,4 with 3 Gb RAM - I notice some delay but no warnings from FF whatsoever.

Ok, for me Ubuntu + FF2 (I will try v3) + Pentium 4 2.8GHz + 3Gb RAM.

NicoP
12 Oct 2009, 6:26 AM
I just checked the selection with appr. 45000 rows - now problem whatsoever so I still assume it has to deal with your hardware - I'm using a Win XP, FF3.5.3, Dual Core 2,4 with 3 Gb RAM - I notice some delay but no warnings from FF whatsoever.

More the client computer is powerfull more you can select row.

With an Intel Core2Quad CPU Q9400 @ 2.66GHz I can multi select until 175,000 rows.

However I think the best solution will to disable the multi selection row when we try to select a large number of row.

ThorstenSuckow
12 Oct 2009, 6:29 AM
That would be an option - I'm planning a kind of "view selection model" where only those rows can be selected which are currently in the view (or at least in the buffer) - that should help (if your buffersize doesn't tend to be ~ 50000 ;) )

NicoP
12 Oct 2009, 6:58 AM
That would be an option - I'm planning a kind of "view selection model" where only those rows can be selected which are currently in the view (or at least in the buffer) - that should help (if your buffersize doesn't tend to be ~ 50000 ;) )

yes ;)
As my servlet (i.e server side) is optimized, I always use small buffer: lower than 100 rows :)

NicoP
14 Oct 2009, 2:48 AM
So, I have done a quick fix to solve this problem by adding a test in selectRange function rowSelectionModel object:


selectRange : function(startRow, endRow, keepExisting) {
if (this.locked) {
return;
}

if (!keepExisting) {
this.clearSelections();
}

var totalSelected = Math.abs(endRow-startRow);
if(this.maxSelectedRow && totalSelected>this.maxSelectedRow) {
this.clearSelections();
this.displayMaxSelectedRow(this.maxSelectedRow);
return;
}

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);
}
}
}

dan_jf
16 Oct 2009, 5:47 AM
Hi,

First of all, the livegrid really is a great component! Works really well.

I am using it with a enableRowBody to show some detail of each row, working fine for this.

But I want to add an option to toggle between the extra detail and just the basic row. I have done this using a Cookie to store the preference (Toggled by a button) which is then checked on the getRowClass: function(record, rowIndex, rp, ds) method to actually output the info.

So when someone toggles between the detail and basic row info, I calling myView.refresh(); which redraws the grid with or without the extra data.

All working well expect for when we toggle between extra data (So deeper rows) and basic data (Slim, 1 line rows) the grid/view does not resize itself to match so we end up with just 3 or 4 rows showing.

Whats the best way to fully redraw the entire livegrid?

Thanks,

Animal
18 Oct 2009, 5:51 AM
I want to propose some new functionality. How about being able to configure the start position of the livegrid?

This would have to be in two parts.


The start parameter to request the correct buffer of rows from the server
The liveScroller element scrollTop value


Right now, to resume a LiveGrid to a saved position, it MUST load page 1. It is actually written to always page 1 first. It then calculates the row height in order to be able to size the liveScroller element's child element to get a scrollbar.

To resume to a scroll position, you have to load page one, wait for it to do its calculations so that the liveScroller is set to the right height, and THEN scroll the liveScroller, and the listener machinery then kicks in, and loads the new buffer of data if necessary.

All of which makes for a poor user experience.

It would be very useful to be able to configure a start position and a startScrollPosition in the GridPanel's config object. You would need both. You cannot get all the information needed from just one. You have to load the buffer, render one line, calculate the rowHeight, set the scroller's inner height, and THEN scroll to the specified position.

I've been hacking through the code, but it's a fundamental change, and would take me a long time to integrate, and I would then end up out on a limb with no upgrade path.

How about adding this facility Thorsten?

ThorstenSuckow
18 Oct 2009, 9:06 AM
yeah, guess you're not the only one needing this... however there's a feature freeze right now until 3.1 is out there, I'm currently rather confused with the patch releases and still sorting things out, whether I will branch my ux's for every patch release... which shouldn't be necessary IMHO but giving the hassle I had with the latest releases... geez!

sksoft
19 Oct 2009, 1:26 AM
Thorsten, I don't see any new commits from July 11, 2009 at svn://svn.ext-livegrid.com. Should I use another URL?
Did you fixed "this._loadMaskAnchor._mask undefined" problem? (Ext 3.0, Ext.ux.grid.livegrid.GridView.renderUI)

ThorstenSuckow
19 Oct 2009, 2:09 AM
Thorsten, I don't see any new commits from July 11, 2009 at svn://svn.ext-livegrid.com. Should I use another URL?
Did you fixed "this._loadMaskAnchor._mask undefined" problem? (Ext 3.0, Ext.ux.grid.livegrid.GridView.renderUI)


Try this dev release - no commits so far as I'm struggling with a few issues involved in the patch releases.

Let me know if it works for you.

sksoft
21 Oct 2009, 2:14 AM
Try this dev release - no commits so far as I'm struggling with a few issues involved in the patch releases.

Let me know if it works for you.

Thorsten, do I need to modify a CSS?
BTW, why not to put it in trunk?

ThorstenSuckow
21 Oct 2009, 3:05 AM
Thorsten, do I need to modify a CSS?
BTW, why not to put it in trunk?

No, there haven't been made any changes to the CSS file.

I'll sync to the public rep as soon as I know how I will handle the patch release branches.

chh
21 Oct 2009, 6:51 AM
Try this dev release - no commits so far as I'm struggling with a few issues involved in the patch releases.

Let me know if it works for you.

Is this supposed to work with the 3.0.3 release? I'm getting this odd behavior where it places the column header bar in the bottom of the view, if there are more rows than it can display in the view. I.e. if I only have two rows of data everything is fine, but if I have 500 rows the problem shows up.

ThorstenSuckow
21 Oct 2009, 6:54 AM
Is this supposed to work with the 3.0.3 release? I'm getting this odd behavior where it places the column header bar in the bottom of the view, if there are more rows than it can display in the view. I.e. if I only have two rows of data everything is fine, but if I have 500 rows the problem shows up.

Make sure you're loading the css file into your app - get the complete package from ext-livegrid.com and replace the debug-all with the file downloaded from this thread.

chh
21 Oct 2009, 10:47 PM
Make sure you're loading the css file into your app - get the complete package from ext-livegrid.com and replace the debug-all with the file downloaded from this thread.
I was using the css from the ext 2.x version. Replaced it with the ext 3.x version and now it works much better. Thanks!

yuriy
26 Oct 2009, 1:19 AM
Thorsten, first thanks for the component - just started evaluating it.
I found a bug in the component that occurs pretty often for me:
if the grid is removed while data request is in progress, it will throw an error "this.ds is null" when data arrives in "reset" function on line:


this.ds.modified = [];

ibet
1 Nov 2009, 4:15 PM
hi,I like it.
but I wonder if livegrid surports column lock. now a project i doing is need the function.can you help me?

NicoP
2 Nov 2009, 4:12 AM
hi,I like it.
but I wonder if livegrid surports column lock. now a project i doing is need the function.can you help me?

+1

Merge those both plugins (livegrid+column lock) will be a great idea.

wm003
4 Nov 2009, 12:20 AM
When rows are selected, and the grid is scrolled far enough to make a request, then scrolled back up again (causing another request), the rows are no longer selected.


I am having the same issue here (using Ext 2.3). Any workaround available yet from anyone?

SimoAmi
10 Nov 2009, 5:55 PM
When we click on the last visible (or semi visible) row, the grid scrolls up by a few pixels. Is it possible to prevent such behavior when clicking a row?

It causes a problem for us since we have a hyperlink as a custom renderer. clicking the hyperlink on the last row selects the row but doesn't trigger the hyperlink until the next time when the row moves up.

If the above is not possible, can we have grid height spanning, to make sure no partial rows are shown?

Thanks

thesilentman
14 Nov 2009, 9:39 AM
Try this dev release - no commits so far as I'm struggling with a few issues involved in the patch releases.

Let me know if it works for you.

Hello Thorsten,

I was having some trouble with the code you posted in my quote. I made 2 fixes on your code myself:

1) Internet explorer error 'undefined argument' in the setWidth method of Ext.Element around line 5573 in ext-all-debug.js (Ext 3.0.3)
Tracing it back it was a problem around line 1706 in your livegrid-all-debug.js:

The reason was that this.scrollOffset was in some cases undefined and subtracting it from elWidth would result in a NaN variable that was passed to setWidth, causing the Error in IE(Tested in Version 8)



this.scroller.setWidth(elWidth-this.scrollOffset);


I changed it to this:


if (typeof this.scrollOffset=='undefined'){
this.scroller.setWidth(elWidth);
}else{
this.scroller.setWidth(elWidth-this.scrollOffset);
}




2) Around Line 964 in livegrid-all-debug.js:


if (proxy.activeRequest[Ext.data.Api.actions.read]) {


changed to:



if (proxy.activeRequest && proxy.activeRequest[Ext.data.Api.actions.read]) {


REASON: proxy.activeRequest was not always set, so it threw an error.
BTW: I am using it with a directproxy having configured it somewhat like this:


liveGrid = new Ext.ux.grid.livegrid.GridPanel({
enableDragDrop : false,
loadMask : {
msg : 'Loading...'
},

id: 'objectGrid',
xtype: 'grid',
region: 'center',
autoScroll:true,
store : new Ext.ux.grid.livegrid.Store({
// note by F.M.:livegrid needs to run with it's own store and we need to define our proxy object here instead of directly in the store
proxy: new Ext.data.DirectProxy({
api : {
read : Ext.ss.Folder.getObjects
}
}),
autoLoad : true,
storeId : 'objectStore',
fields : ['Number', 'Recipient', 'Sender', 'DocDate'],
remoteSort : true,
reader : new Ext.ux.grid.livegrid.JsonReader({
root : 'rows',
versionProperty : 'version',
totalProperty : 'total',
id : 'Number'
}, [ {
name : 'Number', sortType : 'int'
},{
name : 'Recipient', sortType : 'string'
},{
name : 'Sender', sortType : 'string'
},{
name : 'DocDate', sortType : 'int'
}]),
bufferSize : 300,
paramsAsHash:true,
paramsNames : {
parent : "parent", // The parameter name which
start : "start", // The parameter name which
// // specifies the start row
limit : "limit", // The parameter name which
// specifies number of rows
// to return
sort : "sort", // The parameter name which
// specifies the column to sort
// on
dir : "dir" // The parameter name which
// specifies the sort direction
},

sortInfo : {field: 'DocDate', direction: 'DESC'},
baseParams : {
parent: "102",
start : "1",
limit : "100" ,
sort : "DocDate",
dir : "ASC"
}
}),
columns: [
{id:'Number',header: 'Number', width: 160, sortable: true, dataIndex: 'Number'},
{header: 'Recipient', width: 75, sortable: true, dataIndex: 'Recipient'},
{header: 'Sender', width: 75, sortable: true, dataIndex: 'Sender'},
{header: 'DocDate', width: 75, sortable: true, dataIndex: 'DocDate'}
],
forceFit : true,
viewConfig : {
forceFit : true
},
stripeRows: true,
title: 'Objects',
stateful: true,
stateId: 'objectGrid',

selModel : new Ext.ux.grid.livegrid.RowSelectionModel(),
view : liveView,
bbar : new Ext.ux.grid.livegrid.Toolbar({
view : liveView,
displayInfo : true
})
});



Best Regards,
Frank

miti
16 Nov 2009, 6:08 PM
I know liveGrid doesn't support grouping yet. Any hint on how it can be done? I know I need override GroupingView and GroupingStore. Maybe I should get all possible values for the groupField first? Any input appreciated.

ibet
17 Nov 2009, 6:28 PM
Hi,When i have a grid with about 10000 rows,I set bufferSize = 1000.
If a user scroll to 1000,2000 ,etc,the liveGrid well request the server.
I think the store reload when the scroller pos is at buffersize's multiple.

Well then , if have a large dataset (100000) from database,then query data will connect to database again and again,it's not a good performance .

Otherwise I used the "DHTMLX" framework,its grid looks more speeder.

veenvliet.morion
18 Nov 2009, 12:31 AM
Hey,
The dhtmlx example on

http://www.dhtmlx.com/docs/products/dhtmlxGrid/samples/14_loading_big_datasets/01_50000.html

Loads around 17 items per request/ scroll action. That are a lot of calls to the webserver and database server if I do that in our production environment.

On the other hand, loading 1000 rows in 1 time (and 1000 rows to render) isn't fast either.
I use 200 as a buffersize, and can scroll through 150.000 rows just fine. I think scrolling through 150.000 e-mails in outlook will be slower.

Hope to see any commits for the livegrid for ext 3.0.

stephen.friedrich
18 Nov 2009, 5:20 PM
I am evaluating the live grid for a commercial app. Mostly it is working fine.

Small bug: I use the "stripeRows: true" at the GridPanel which works in principle, but fails directly after sorting: Grid is striped correctly after initial load. I click on a column header and the data is correctly displayed, but all the "stripes" are gone. Scrolling then brings the stripes back immediately.

Then there's a missing feature (an important requirement from my client):
After the user created a new entry refresh the view, select the new entry and scroll the grid so that it is visible. There's no problem determing the index of the new row in the backend. But how do I scroll the grid to make it visible?

I also am not so very confident about the "business maturity": Ext 3 has been out for quite a long time, but there still is no official live grid release for that Ext version. No news for a couple of months. Documentation is lacking (not even a getting started). No forum of its own (this very thread is getting overloaded endlessly).

stephen.friedrich
18 Nov 2009, 5:23 PM
Another small usability issue: About 4 out of ten mouse clicks on the scrollbar's arrow buttons do nothing.
Each of the other 6 clicks correctly seems to scroll one row.

stephen.friedrich
18 Nov 2009, 5:41 PM
Last question for now:
I tried to use the grid with checkbox selection (by changing the superclass of Ext.ux.grid.livegrid.RowSelectionModel to Ext.grid.CheckboxSelectionModel).
That also only _almost_ works: The "(De)Select All" checkbox in the column header only acts on the currently buffered rows.
Any idea how to make it work on _all_ rows?

yuriy
19 Nov 2009, 12:06 AM
Yeah, unfortunately I have to agree about "business maturity" here, adding:
- no svn activity (and some strange private builds as attachments in this thread)
- no news from author here and on "official" site for quite some time

So after evaluating it for a while I'm afraid I'm not going to buy it in this state.
But I really don't want to get pager back to the UI :(
Any alternatives?

completej
25 Nov 2009, 11:14 AM
I was attempting to use this in a desktop app with Ext-3.0.0 and I am stumped. The window loads initially without fail, scrolls with no problems, etc. When I close the window and subsequently destroy it's contents, I am unable to re-load this window as the GridView isn't lazy. edit: The noticeable change is the bbar does not exist after re-opening the window.

I used the following to set the gridpanel up as an xtype, but I cannot seem to do the same for the GridView:


Ext.reg('livegrid', Ext.ux.grid.livegrid.GridPanel);
Following code for the view config, the grid itself, and the window that contains it:



var myView = new Ext.ux.grid.livegrid.GridView({
nearLimit : 100
//,loadMask : {
// msg : 'Buffering. Please wait...'
//}
});
var livegrid = {
xtype: 'livegrid',
border: false,
enableDragDrop : false,
cm : new Ext.grid.ColumnModel([
new Ext.grid.RowNumberer({header : '#' , width : 40}),
{header: "new tech #", align : 'right', width: 160, sortable: true, dataIndex: 'new tech #'}
]),
loadMask : {
msg : 'Loading...'
},
store : new Ext.ux.grid.livegrid.Store({
autoLoad : true,
autoDestroy : false,
url : 'asp/roster_j_list.asp',
bufferSize : 300,
reader : new Ext.ux.grid.livegrid.JsonReader({
root : 'data',
versionProperty : 'version',
totalProperty : 'total',
id : 'id'
}, [ {
name : 'new tech #', sortType : 'int'
}
]),
sortInfo : {field: 'new tech #', direction: 'ASC'}
})
, selModel : new Ext.ux.grid.livegrid.RowSelectionModel()
, view : myView
, bbar : new Ext.ux.grid.livegrid.Toolbar({
view : myView,
displayInfo : true
})
};
/**/
var roster_window = {
xtype: 'window'
, shim: false
, animCollapse: false
, constrainHeader: true
, resizable: true
, maximizable: true
, autoDestroy: false
, layout: 'fit'
, iconCls: 'bogus'
//, autoHeight: true
, id: 'roster-window'
, title: 'Roster Test'
, width: 1000
, height: 600
, tbar: [
'<span style="font-weight:bold; font-size:16px;">TC Report</span>'
, '->'
, roster_status_combo
, roster_class_combo
, roster_submit_button
, roster_refresh_button
]
//, items: roster_grid
, items: livegrid
};
Firebug kicks back:


el is null:
in:


function(prop){ var el = this.dom,
v,
cs;
if(el == document) return null;
prop = chkCache(prop);
return (v = el.style[prop]) ? v :
(cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
} :
Am I missing something? I saw a few posts in the thread about lazy instantiation, but most went without response. Thanks,

NicoP
30 Nov 2009, 2:22 AM
I am having the same issue here (using Ext 2.3). Any workaround available yet from anyone?

Since LiveGrid release 0.3 the massive selection of row have been disabled when you select a number of row greater than the buffer size.

However it works with LiveGrid 0.2RC5 ...

NicoP
30 Nov 2009, 2:39 AM
Small bug: I use the "stripeRows: true" at the GridPanel which works in principle, but fails directly after sorting: Grid is striped correctly after initial load. I click on a column header and the data is correctly displayed, but all the "stripes" are gone. Scrolling then brings the stripes back immediately.

I can also reproduce this problem.
For me, one more reason to stay with LiveGrid 0.2RC5.



Then there's a missing feature (an important requirement from my client):
After the user created a new entry refresh the view, select the new entry and scroll the grid so that it is visible. There's no problem determing the index of the new row in the backend. But how do I scroll the grid to make it visible?


You can scroll to a specific row using the method adjustScrollPos, for instance:



var myGrid = Ext.getCmp('myGridId');
myGrid.getView().adjustScrollerPos(myGrid.getView().rowHeight * myGrid.getView().rowIndex, true);Do not use the reset button or function to refresh the current view because you will loose the scroll position.
I use the following function to refresh the current view of the livegrid when I receive a grid event:


grid.getView().updateLiveRows(grid.getView().lastRowIndex, true, true);

emily
30 Nov 2009, 7:02 AM
Hi all,

I have been playing with livegrid (with a view to seeing if I can adapt it to handle situations where row heights aren't uniform). Fallen at the first hurdle though - there doesn't appear to be a scroll bar when using firefox. This is the case with the examples on the livegrid website too. I haven't really investigated this yet, but was wondering if anyone else has found this and has a workaround.

Em

emily
1 Dec 2009, 5:35 AM
Hi all,

I have been playing with livegrid (with a view to seeing if I can adapt it to handle situations where row heights aren't uniform). Fallen at the first hurdle though - there doesn't appear to be a scroll bar when using firefox. This is the case with the examples on the livegrid website too. I haven't really investigated this yet, but was wondering if anyone else has found this and has a workaround.

Em

Found that the problem is with my computer. Have asked lots of other people with ff 3.5.5 and the same version of firebug, and they can see the scroll bar. I have uninstalled and reinstalled FF, and that didn't make any difference, so have resorted to using FF in a virtual machine. Anyone got any idea why this might be happening?

nctag
4 Dec 2009, 6:20 AM
For all those guys that get the following error:

this.getVersion is not a function

1.) This only occurs if you're using the latest version from SVN (afaik). At least my revision (5706) get me into this error.

Override to fix that issue:


Ext.override( Ext.ux.grid.livegrid.JsonReader, {
getVersion : function( o )
{
return null;
}
});


For me it works. Would be nice to get some feedback from the community.

nctag
14 Dec 2009, 4:46 AM
There is a bug within the horziontal scroller. If it appears the number uf visibleRows is miscalucalted. Or in other words: The last record would not be shown correctly. To fix that try the following override:



/**
* Ext.ux.grid.livegrid.GridView
*
* Behebt die Fehler im Zusammenhang mit der horizontalen Scrollbar.
*/
Ext.override( Ext.ux.grid.livegrid.GridView, {
/**
* 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.horizontalScrollOffset; //TODO: Original: 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, but not if we
// are just pre-buffering
if (this.isBuffering && !this.isPrebuffering) {
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);
},

/**
* 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.horizontalScrollOffset); //TODO: Original: this.scroller.setWidth(elWidth-this.scrollOffset);
liveScrollerDom.style.display = '';
}

var scrollbar = this.cm.getTotalWidth()+this.horizontalScrollOffset > elWidth; //TODO: Original: 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";
}
});



Just have a look at the three //TODOs.

KirkOlson
23 Dec 2009, 1:26 AM
Is it just me or is the LiveGrid not working correctly in Ext 3.1 ?

I don't see any data, it worked fine in 3.0.0

sksoft
23 Dec 2009, 1:49 AM
It looks like this project is freezed, there is no any commits for more than 5 months :(

mono blaine
23 Dec 2009, 2:17 AM
It works with Ext 3.1, but there are a couple of bugs.

To get it work with IE, you need to do the following override:


Ext.override(Ext.ux.grid.livegrid.GridView, {
scrollOffset: 0
});

There is also a bug in the method ensureVisible of GridView. In a grid that has many columns, scroll to the right end, and select any row. You will see extra blank space on the right side of the area with the rows. I don't know what really causes that or on what circumstances that bug occurs, yet the following override fixes my problem. (I think this happens due to a miscalculation of positioning)


Ext.override(Ext.ux.grid.livegrid.GridView, {
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.offsetWidth + this.el.getX() - (Ext.isIE ? 3 : 1), Ext.fly(rowEl).getY()];
}
});

But this is a really quick fix as I had no time to test it, and has no guarantee to work in your case.

KirkOlson
23 Dec 2009, 2:50 AM
It works with Ext 3.1

Are you sure? I don't seem to get the demo on http://www.ext-livegrid.com/demo/ to work with Ext 3.1 either.
It just doesn't show any data.

mono blaine
23 Dec 2009, 2:54 AM
Yes. I'm using the version posted here (attachment): http://www.extjs.com/forum/showthread.php?p=399562#post399562

KirkOlson
23 Dec 2009, 3:02 AM
Yes. I'm using the version posted here (attachment): http://www.extjs.com/forum/showthread.php?p=399562#post399562

That version indeed seems to be working. Thanks for the link!

mono blaine
23 Dec 2009, 7:16 AM
For the bug mentioned here (http://www.extjs.com/forum/showthread.php?p=356662#post356662), you can use the following override:


Ext.override(Ext.ux.grid.livegrid.Toolbar, {
initComponent: function () {
if (Ext.isEmpty(this.items)) {
this.items = [];
}
this.items.splice(0, 0, [
this.loading = new Ext.Toolbar.Button({
tooltip: this.refreshText,
iconCls: "x-tbar-loading",
handler: this.onClick.createDelegate(this, ["refresh"])
}), '-']);
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);
},
onRender: function (ct, position) {
Ext.PagingToolbar.superclass.onRender.call(this, ct, position);
if (this.displayInfo) {
this.displayEl = Ext.fly(this.el.dom).createChild({
cls: 'x-paging-info'
});
}
}
});

barncat
3 Jan 2010, 8:56 PM
This looks like a perfect solution to my current problem. Are there any docs on how to integrate it into an existing editorgrid project? I was hoping to find something on the livegrid site or its wiki, but no luck so far. I'm sure I can eventually puzzle it out, but there must be a basic guideline doc somewhere?

Thanks!

ThorstenSuckow
4 Jan 2010, 5:00 AM
I have updated the SVN with the working version for Ext 3.0.3, along with a few bug fixes (see http://wiki.ext-livegrid.com/changeset/65).

I'm gonna switch branches now so that the main dev line holds the version for Ext 3.1, and update the code to work with Ext 3.1 then.

chh
7 Jan 2010, 5:35 AM
I'm gonna switch branches now so that the main dev line holds the version for Ext 3.1, and update the code to work with Ext 3.1 then.
Great news! I'm really looking forward to an official version for Ext 3.1.

ThorstenSuckow
7 Jan 2010, 5:46 AM
Great news! I'm really looking forward to an official version for Ext 3.1.

The official version for Ext 3.1 is 0.4 - you can find it on the download page (http://www.ext-livegrid.com/download)

veenvliet.morion
7 Jan 2010, 7:44 AM
Hi,

Do you see a change to implement a DirectStore for BufferedStore.
I created my own, but maybe it's a good thing to include it in to the mainbranche.

Keep up the good work.

PS: I received you're license key. Do I have to include the key in the code somewhere?

ThorstenSuckow
7 Jan 2010, 7:53 AM
Hey, work on the Ext.Direct-API will begin as soon as it's stable. Right now I think there'll be too much changes within Ext.Direct in the next time (see the related bugs thread).

The license key is just for your personal record, you don't have to include it in the code.

hallikpapa
8 Jan 2010, 1:52 PM
Edit...Disregard. I fixed the problem

I am having problems using the livegrid with the another plugin (ux.Media.ChartPack)
http://www.extjs.com/forum/showthread.php?t=32434

...in the same app. Both plugins use a loadMask, but when I add the ChartPack, I get this error

data(dom, "mask") is undefined

The error happens on this line

if (this.loadMask) {
this._loadMaskAnchor = Ext.get(this.mainBody.dom.parentNode.parentNode);
Ext.apply(this.loadMask,{
msgCls : 'x-mask-loading'
});
this._loadMaskAnchor.mask(
this.loadMask.msg, this.loadMask.msgCls
);
var dom = this._loadMaskAnchor.dom;
var data = Ext.Element.data;
data(dom, 'mask').addClass('ext-ux-livegrid'); //ERROR HERE
data(dom, 'mask').setDisplayed(false);
data(dom, 'maskMsg').setDisplayed(false);
}


Shouldn't the loadMask property be encapsulated within each user plugin? I am not sure where the problem starts. Without knowing the real inner workings of either plugin, it SEEMS as though the ChartPack is somehow screwing with the loadMask of the LiveGrid. But it seems as though the LiveGrid and ChartPack should be completely separate from each other.

I noticed if I break on the error line, and step into the code and hit ext-all-debug, it goes to this method


// private method for getting and setting element data
El.data = function(el, key, value){
var test = El.dataCache; //I added this myself to see what is contained in the dataCache
var c = El.dataCache[el.id];
if(!c){
c = El.dataCache[el.id] = {};
}
if(arguments.length == 2){
return c[key];
}else{
c[key] = value;
}
};


When the ChartPack is plugged in, "c" is undefined. The el.id that is passed, is not contained within the El.dataCache object. But when I remove the ChartPack plugin, the el.id IS contained within the El.dataCache.

Do you have any suggestions as to why the loadMask info for livegrid seems to disappear when I add a new plugin (or never gets loaded in the El.dataCache; where does this happen)? Both of these plugins are excellent, but I cannot figure out how to use them together.

I might add I am using ext 3.0 and both the plugins that support the ext 3.x version

Mandeep
8 Jan 2010, 2:32 PM
Does 3.1 version of the live grid work with LockingGidView/LockingColumnModel (http://www.extjs.com/forum/../deploy/dev/examples/grid/locking-grid.html)?

Thanks.

alexw23
9 Jan 2010, 7:56 PM
The grid is rendered with a disabled scrollbar, despite there being lots of rows. Resizing the browser window can show/hide a few extra rows to verify that they exist. Screenshots attached.

The code used is very similar to the demo. Example JS attached.

ExtJS: 3.1
Livegrid: 0.4 stable and nightly, r68
Browsers: FF3 Linux, FF 3.5 WinXp, Chrome 2 WinXp, IE8 WinXp

JSON Response, excerpt:
{"success":true,"version":1,"total":38745912,"results":[...]}


Thank you for this extension!

rubaiz
13 Jan 2010, 2:45 PM
We recentely upgraded to livegrid 04 and ExtJS 3.1, i have been experiencing problems with the Livegrid since then.

When doing the following:

grid.reconfigure(newLiveGridDataStore,newColumnModel);

If newLiveGridDataStore had autoLoad:true, the store doesnot autoload. I beleve the livegrid needs to do something like this:


grid.on({
'reconfigure' : function ( grid, store, colModel ) {
// after reconfiguration load the store with data
if (store._autoLoad === true) {
delete store._autoLoad;
store.load();
}
}
});

TheColonel
29 Jan 2010, 6:12 PM
I was wondering if there is a way to allow the records to accumulate rather than re-buffering, them. This would allow me to leave changed records alone until the user clicks a "Save" button.

I seems this may cause memory consumption issues, but would also prevent so many redundant calls to the server.

Basically, I want it to only load records that have not been previously loaded.

I have familiarized myself with the source code. Any ideas on how to accomplish this will be appreciated.

emily
2 Feb 2010, 9:28 AM
Hi,

I have managed to get live-grid working with a large amount of data, but the grids I am using it for will not always have a large number of rows, and when the number of rows is small (I think smaller than the buffer size) they don't render when I create the grid.

I stuck some debugging in, and when there is a large amount of data (the case when it works correctly), it calls layout, then updateLiveRows. When the amount of data is small, it only calls layout (a few times), but never actually does anything else. If you resize the window the grid is in, then it displays correctly.

I am not sure if it is a timing issue, or what, but I am pretty stumped.

Has anyone else seen this? I think the version I was using was one of the nightlies that worked with extjs 3.0 (that I subsequently hacked to work with varying row heights), but I just tried it with a fresh install of the stable version for 3.1 (that I hadn't messed with ;) ) and it behaved the same.

Has anyone seen this before?

Em

Eric24
4 Feb 2010, 9:18 AM
Nice work on LiveGrid! I'm just getting started with it, so maybe this question has come up before (although I couldn't find it in the prior postings): Let's say I have a large rowset (rows 1-5000), managed by LiveGrid (with the appropriate code on the server to return the requested rows). What happens in (or how would I handle) these two cases:

1) The user deletes or inserts a row in LiveGrid. Not a problem at the UI layer, and presumably I'll need to tell the server that the row has been deleted or inserted (and where), but now there are 4999 or 5001 total rows. Does LiveGrid deal with this "automatically"?

2) Something happens on the server that adds or removes a row in the result set. The server can easily do this and add or remove the row from the "LiveGrid result set", but again (and here, without LiveGrid knowing about it, since the change didn't happen in the UI), now there are 4999 or 5001 total rows.

I suppose the brute-force approach to either of these problems is to completely refresh LiveGrid, but that seems like overkill and is not the effect I'm looking for. For example, think of a messaging system--a new message comes in that should go at the beginning of the result set that LiveGrid is currently displaying; how do I insert that new message into the result set (and LiveGrid) without refreshing?

Cheers!
Eric

Eric24
4 Feb 2010, 9:24 AM
Unusual LiveGrid buffering behavior... Using "Example #1" on your site, I simply scroll down, watching the server transactions with FB. The first transaction has start=0 and limit=300, as expected. But the second transaction has start=196 (and limit=300). Why would LiveGrid want/need to re-load rows 196-299?

Bing Qiao
5 Feb 2010, 8:34 AM
I'm using BufferedGridView v0.1 for a EditorGridPanel added to a ViewPort with "fit" layout.

Everything works fine except that the vertical scrollbar is always visible even when there are just a few items that don't fill up one page.

Is there any way to solve this?

Many thanks in advance!

danh2000
9 Feb 2010, 9:22 PM
Hi,

There is an incompatability in the loadRanges method which attempts to call HttpProxy load method which is now deprecated (this.selectionsProxy.load).

This needs to be updated to use the doRequest method.

This was found whilst attempting to use the loadSelections method to workaround the issue of having selections that are outside of the current buffer...

.. any news on if/when this will be supported by the component so that deleting full selections work as expected?

There are also knock on issues with the selectionsLoaded method etc.

Also, the ranges that are sent to the server are not entirely compatible with PHP as it's a string, which although can be parsed, it might be nice to accomodate for PHP with a config setting.

Thanks for your time,

Dan

swang
10 Feb 2010, 7:13 AM
I have the livegrid inside a TabPanel, which is inside a ViewPort using the 'fit' layout. However,the code below does not seem to work for me.

viewConfig: {
forceFit: true
},

Any suggestions?

weazil
10 Feb 2010, 7:49 AM
new Ext.Viewport({ layout:'fit' ,items: [livegrid] });

seems to work fine here that way when you resize the window it grows accordingly

swang
10 Feb 2010, 1:44 PM
new Ext.Viewport({ layout:'fit' ,items: [livegrid] });

seems to work fine here that way when you resize the window it grows accordingly

Thanks for trying.

Actually I figured out the problem. I have set the "view" property of my LiveGrid to an Ext.ux.grid.livegrid.GridView. The viewConfig property does not seem to apply. But setting "forceFit" attribute in the Ext.ux.grid.livegrid.GridView initialization works.

Ranma13
18 Feb 2010, 1:15 PM
I'm having two issues with livegrid:

1. When the grid columns don't extend the entire width of the grid (whitespace on the right), scrolling the mouse wheel inside this whitespace doesn't scroll the grid. You have to put the cursor on a column in order to scroll it.

2. When the right-most column is set to autoExpandColumn, a horizontal scrollbar shows up. Resizing the column removes this horizontal scrollbar, but on IE7 the last row at the very bottom is cut off.

supercharge2
19 Feb 2010, 12:25 PM
The grid is rendered with a disabled scrollbar, despite there being lots of rows. Resizing the browser window can show/hide a few extra rows to verify that they exist. Screenshots attached.

The code used is very similar to the demo. Example JS attached.

ExtJS: 3.1
Livegrid: 0.4 stable and nightly, r68
Browsers: FF3 Linux, FF 3.5 WinXp, Chrome 2 WinXp, IE8 WinXp

JSON Response, excerpt:
{"success":true,"version":1,"total":38745912,"results":[...]}


Thank you for this extension!


I had a very strange problem with one of my users, not sure if this applies to your issue or not. On her XP computer all of my live grid components had grayed out and unusable vertical scrollbars (all other grids were fine). I could get them to enable if I did something like a search to wake it up but then the up and down arrows were unusable, just the slider. I tried Firefox and the vertical scroll bars didn't even show up. I discovered that it was only with her XP profile so I recreated her profile and worked through it until I discovered the problem. The problem ended up being a change in the system font size. This user went into XP's desktop settings and increased fonts to large. This triggers this strange problem with the live grid scrollbars. Not sure what can be done to solve this but for now my users will not be able to change the font sizes on their systems.

Dumbledore
24 Feb 2010, 3:09 AM
Hi,

i try to extend Ext.ux.grid.livegrid.GridPanel for defined a preconfigured Grid for my application. All runs fine, but i can not add a bbar like this:



Ext.applyIf(this, {
enableDragDrop : false,
cm : this.buildColumnModel(),
height : 400,
store : store,
selModel : new Ext.ux.grid.livegrid.RowSelectionModel(),
view : gridView,
plugins : [
this.information_expander
],
viewConfig : {
forceFit: true
},
bbar : new Ext.ux.grid.livegrid.Toolbar({
view : gridView,
displayInfo : true
})
});


When i runs this, every time i get a error this.layout.layout is not a function.

When i add a normal Pagingtoolbar instead of Ext.ux.grid.livegrid.Toolbar all runs fine. But this make no sense, because there is no paging.

Here runs ExtJS 3.1.1 with Livegrid 0.4.

Any ideas?

mono blaine
26 Feb 2010, 4:10 AM
in IE, if you have for ex 500,000 records, you cannot scroll down to the records at the end, i guess sth is terribly miscalculated. you can scroll down to like the 60,000th record at most. in firefox there's no such problem.

tested on ExtJS 3.1.1, livegrid r68 build.

cherbert
10 Mar 2010, 4:56 AM
Will LiveGrid be compatible with the new multisort features introduced in ExtJS 3.2 beta?

swang
10 Mar 2010, 12:51 PM
in IE, if you have for ex 500,000 records, you cannot scroll down to the records at the end, i guess sth is terribly miscalculated. you can scroll down to like the 60,000th record at most. in firefox there's no such problem.

tested on ExtJS 3.1.1, livegrid r68 build.

I have also experienced the same problem. After inspecting the dom tree, I found the issue. The livegrid has a div element inside grid panel, and its height gives the scroll height to the vertical scrollbar. You will see something like



<div class="liveScroller" id="ext-gen44" style="top: 23px; height: 283px; ">
<div style="height: 1973617px; "></div>
</div>
However, in IE8 (not sure about other IE versions), the maximum div height you can set is 1342177 pixels. I am not sure how to work around this problem other than changing the livegrid implementation.

mono blaine
10 Mar 2010, 1:09 PM
that's quite depressing :D

calugaru.cristian
16 Mar 2010, 8:54 AM
I just want you to note that wordwrapping in the livegrid will break basically everything. I do do that (wrap) but this will throw off all scrolling calculations and therefore make it (the livegrid) unusable. The issue is that the calculations are based on the numbers of rows, where each row HAS to be the same height, i.e. it does not take into account variances in height. I did mention this to Mindpattern, and I hope he considers this to be top priority or else it will limit its applications and that would be a shame. His LiveGrid is really nice

Does anyone have a reliable solution for this issue, or this can't be fixed given the current implementation details?:-?

cherbert
17 Mar 2010, 2:07 PM
Are we likely to see any updates to this extension any time soon? I am starting to get worried about committing to using it in our application if its going to be left to die.

ThorstenSuckow
18 Mar 2010, 9:06 AM
Are we likely to see any updates to this extension any time soon? I am starting to get worried about committing to using it in our application if its going to be left to die.

Don't worry, it's still very alive!

SunWuKung
19 Mar 2010, 7:39 AM
I am trying to copy cell content but the solution in the FAQ does not seem to be working.
Anyone knows how to do this?



templates : {
cell : new Ext.Template(
'<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} x-selectable {css}" style="{style}" tabIndex="0" {cellAttr}>',
'<div class="x-grid3-cell-inner x-grid3-col-{id}" {attr}>{value}</div>',
'</td>')
}

ThorstenSuckow
20 Mar 2010, 3:33 PM
From the release announcement:

Ext.ux.Livegrid 0.4.1 is available!

The most notable change would be the treatment of the “liveScrollerInset”. In previous versions, this was a single “div” element, representing the scroll amount according to the number of records displayed by the Livegrid. This could lead to some issues in browsers which define a max height for HTML elements, making it impossible for users to scroll through all the records, as the max height of this inset would prevent scrolling beyond this point. We have now splitted this div into 3 single divs which resolves this issue.

There is also a change in Store.js - loading selections using the “selectionsProxy” would fail due to a call to a deprecated method which is no longer available since Ext 3.*.

ThorstenSuckow
20 Mar 2010, 3:37 PM
Will LiveGrid be compatible with the new multisort features introduced in ExtJS 3.2 beta?

Once 3.2. gets a stable candidate, Ext.ux.Livegrid will provide this feature, too.

ThorstenSuckow
20 Mar 2010, 3:38 PM
I have also experienced the same problem. After inspecting the dom tree, I found the issue. The livegrid has a div element inside grid panel, and its height gives the scroll height to the vertical scrollbar. You will see something like



<div class="liveScroller" id="ext-gen44" style="top: 23px; height: 283px; ">
<div style="height: 1973617px; "></div>
</div>
However, in IE8 (not sure about other IE versions), the maximum div height you can set is 1342177 pixels. I am not sure how to work around this problem other than changing the livegrid implementation.

This is fixed in 0.4.1

weazil
22 Mar 2010, 3:09 AM
With 3.1.x I noticed in livegrid suddenly my scrollbars aren't rendering correctly. When you first render the livegrid the last column on the right the scrollbar is covering part of the column, if you resize the grid after its been rendered it will auto resize with the viewport and render correctly.

curious if anyone else was experiencing this.

emily
23 Mar 2010, 1:18 AM
Does anyone have a reliable solution for this issue, or this can't be fixed given the current implementation details?:-?

I did hack the livegrid code to get around this issue, but I wouldn't say my solution is reliable (which is why I haven't posted it back!). Sometimes it renders a bit funny when you get to the bottom of the grid.

For my app (which is not for a wide audience, but more of a tool to be used by one or two people) it is fine, but I would be interested if anyone else has a better solution.

I have also noticed that with my implementation, when you instantiate a grid that has a small number of rows it doesn't render them until you resize / refresh the grid. I haven't managed to figure out if this is a problem with livegrid itself or was introduced by my nasty hacking.

Em

calugaru.cristian
23 Mar 2010, 3:37 AM
After some looking through the source code, I abandoned changing it. I will just use some tip on mouse hover, so the user will be able to see the entire text..until maybe someone comes with a better approach.

I have another question, maybe someone needed the same functionality that I am required to implement now.
So, I have some keyboard shortcuts - control+alt+down arrow. When I am on a particular row, after pressing this shortcut, I need to go to the next row that has a particular property set to true.
So now, if I am in the situation of moving through pages(ie. buffering needed), how can I somehow catch the buffering event, and continue searching into the new values that I have in the store(given that the values from the store are changing when buffering occurs)? And this could perpetuate, until the end of the grid..
Can I somehow, search through the current values of the store, and, if I won't find my next row, I continue searching into the next page, only after it loaded, of course.

Thanks.






I did hack the livegrid code to get around this issue, but I wouldn't say my solution is reliable (which is why I haven't posted it back!). Sometimes it renders a bit funny when you get to the bottom of the grid.

For my app (which is not for a wide audience, but more of a tool to be used by one or two people) it is fine, but I would be interested if anyone else has a better solution.

I have also noticed that with my implementation, when you instantiate a grid that has a small number of rows it doesn't render them until you resize / refresh the grid. I haven't managed to figure out if this is a problem with livegrid itself or was introduced by my nasty hacking.

Em

emily
23 Mar 2010, 3:48 AM
After some looking through the source code, I abandoned changing it. I will just use some tip on mouse hover, so the user will be able to see the entire text..until maybe someone comes with a better approach.


Yeah - I nearly gave up, but I needed the functionality (it is difficult to browse lots of data if you have to hover over each row) and didn't want to go back to paging because it isn't very user friendly.


I have another question, maybe someone needed the same functionality that I am required to implement now.
So, I have some keyboard shortcuts - control+alt+down arrow. When I am on a particular row, after pressing this shortcut, I need to go to the next row that has a particular property set to true.
So now, if I am in the situation of moving through pages(ie. buffering needed), how can I somehow catch the buffering event, and continue searching into the new values that I have in the store(given that the values from the store are changing when buffering occurs)? And this could perpetuate, until the end of the grid..
Can I somehow, search through the current values of the store, and, if I won't find my next row, I continue searching into the next page, only after it loaded, of course.


Would it not be better to do a serverside query to get the next value? Then if it is in the store, you can jump to it; if it isn't, you know what data you need to buffer so you can jump straight to it. Otherwise if the next row is near the end of the grid (a long way away) you will put the grid into a crazy loop of fetching each chunk of data until it finds it and it could take forever.

calugaru.cristian
23 Mar 2010, 4:12 AM
Your suggestion really seems better. I just don't understand , when you are saying ' if it isn't, you know what data you need to buffer so you can jump straight to it'..how exactly can I programmatically jump to it, so that the view goes to the next one?
Let's say, I have data loaded from [0-100], in the form of {nextAvailable:234, data[...]}, so my next selectable row is somewhere at 245..that means that some buffering is needed to get to that. I somehow need to hook to the after-buffer event, if and only if I haven't found it in the [0-100] page?

Thank you so much for your replies :)



Yeah - I nearly gave up, but I needed the functionality (it is difficult to browse lots of data if you have to hover over each row) and didn't want to go back to paging because it isn't very user friendly.



Would it not be better to do a serverside query to get the next value? Then if it is in the store, you can jump to it; if it isn't, you know what data you need to buffer so you can jump straight to it. Otherwise if the next row is near the end of the grid (a long way away) you will put the grid into a crazy loop of fetching each chunk of data until it finds it and it could take forever.

emily
23 Mar 2010, 5:29 AM
Your suggestion really seems better. I just don't understand , when you are saying ' if it isn't, you know what data you need to buffer so you can jump straight to it'..how exactly can I programmatically jump to it, so that the view goes to the next one?
Let's say, I have data loaded from [0-100], in the form of {nextAvailable:234, data[...]}, so my next selectable row is somewhere at 245..that means that some buffering is needed to get to that. I somehow need to hook to the after-buffer event, if and only if I haven't found it in the [0-100] page?

Thank you so much for your replies :)

I am by no means an expert so definitely take this with a pinch of salt (I looked at this ages ago not recently), but I would suggest looking at using the updateLiveRows method of the view. If you search for it in livegrid-all-debug.js and see if it does what you want. If you also search for 'helpers' there are some methods there that may help you to determine whether the row is in the existing data store too.

Not sure how much help that is.

Em

calugaru.cristian
24 Mar 2010, 1:25 AM
Hi again.
So, I tried playing with the updateLiveRows, but I haven't managed to use it properly.
I added a button on my window, with a hard-coded call to , let's say, the 300th index in my data. I added the livegrid.Toolbar, an, when I press the button, it seems that the information from the toolbar is getting updated ('Displaying 300-399 of 2000'), the loadMask
is getting shown, and a correct call is made to the back-end (with the start param to 300).
But right after this one, another call is made with the former index(0 in my case), and
the same rows are getting re-displayed. I don't see why 2 calls are made when calling the method..

Did anyone have a requirement to jump around rows with the livegrid?(:|




I am by no means an expert so definitely take this with a pinch of salt (I looked at this ages ago not recently), but I would suggest looking at using the updateLiveRows method of the view. If you search for it in livegrid-all-debug.js and see if it does what you want. If you also search for 'helpers' there are some methods there that may help you to determine whether the row is in the existing data store too.

Not sure how much help that is.

Em

swang
24 Mar 2010, 5:46 AM
From the release announcement:

Ext.ux.Livegrid 0.4.1 is available!

The most notable change would be the treatment of the “liveScrollerInset”. In previous versions, this was a single “div” element, representing the scroll amount according to the number of records displayed by the Livegrid. This could lead to some issues in browsers which define a max height for HTML elements, making it impossible for users to scroll through all the records, as the max height of this inset would prevent scrolling beyond this point. We have now splitted this div into 3 single divs which resolves this issue.

There is also a change in Store.js - loading selections using the “selectionsProxy” would fail due to a call to a deprecated method which is no longer available since Ext 3.*.

Thanks for the scrollbar fix. But does this mean that the new limit is simply three times the old limit? I read the source code and it appears to be the case.

meroy
25 Mar 2010, 10:58 PM
Tonight, I came across a problem with the LiveGrid user extension.

With IE 6 and clicking on the lower scroll icon in the scrollbar, the grid scrolls down by 2 or 3 rows. With Firefox 3.6, it's 0 or 1 row. This really takes away from the user friendliness of the grid in my humble opinion.

I'm expecting the grid to scroll down/up by one row when clicking on the scroll buttons in the vertical scrollbar.

It does appear to be working fine if I make the height of the entire browser window much smaller, but not always. This isn't a solution to the problem though.

It's almost a no go for me. I have spent some time looking at the source and I saw the 3 divs (pretty cool) for the scrollbar insets.

meroy
26 Mar 2010, 12:23 PM
The make.sh file failed under SUSE Linux due to missing semicolon, missing $ for one of the variables, changed one ${tp} to ${TP}, and changed one \ to /. Also the cp failed and I changed it to use cat instead. Am posting here in the event that it may be helpful to others.

Ensure your JAVA environment is set up before executing the script.



#!/bin/bash

echo "-----------------------------------------------------"
echo "-----------------------------------------------------"
echo " Ext.ux.Livegrid Build Tool "
echo " (c) 2008 Thorsten Suckow-Homberg ts@siteartwork.de "
echo "-----------------------------------------------------"
echo " Using yuicompressor: "
echo " http://developer.yahoo.com/yui/compressor/ "
echo "-----------------------------------------------------"

if [ -z "$1" ]; then
echo "Usage: $0 [path to yuicompressor.jar]"
echo "Example: $0 /temp/yuicompressor-2.4.jar"
echo "Download yuicompressor at http://developer.yahoo.com/yui/compressor/"
else
TP="../../"
yuicompressor_path=$1
livegrid_file_list_core="${TP}src/GridPanel.js ${TP}src/GridView.js ${TP}src/JsonReader.js ${TP}src/RowSelectionModel.js ${TP}src/Store.js"
livegrid_file_list_all="${livegrid_file_list_core} ${TP}src/Toolbar.js ${TP}src/DragZone.js ${TP}src/EditorGridPanel.js"

echo "...building CSS file..."
java -jar ${yuicompressor_path} -o ${TP}build/resources/css/ext-ux-livegrid.css --charset UTF-8 ${TP}src/resources/css/ext-ux-livegrid.css
echo "Done"

echo "...merging files for livegrid-core.js..."
cat ${livegrid_file_list_core} > ${TP}build/_tmp.js

echo ...building livegrid-core.js file...
java -jar ${yuicompressor_path} -o ${TP}build/livegrid-core.js --charset UTF-8 ${TP}build/_tmp.js
echo "Done!"

echo "...merging files for livegrid-all.js..."
cat ${livegrid_file_list_all} > ${TP}build/_tmp.js
echo "Done"

echo "...building livegrid-all.js file..."
java -jar ${yuicompressor_path} -o ${TP}build/livegrid-all.js --charset UTF-8 ${TP}build/_tmp.js
echo "Done"

echo "...merging files for livegrid-all-debug.js..."
cat ${livegrid_file_list_all} > ${TP}build/livegrid-all-debug.js
echo "Done"

# echo "...building livegrid-debug-all.js file..."
# java -jar ${yuicompressor_path} -o ${TP}build/livegrid-debug-all.js --nomunge --disable-optimizations --charset UTF-8 ${TP}build/_tmp.js
# echo "Done"

echo "...removing temp file..."
rm -f ${TP}build/_tmp.js
echo "FINISHED!"
fi

meroy
26 Mar 2010, 4:39 PM
I made this change and it's working very well with Firefox. Clicking on the scrollbar arrows will cause the grid to always scroll by 1 row and not 0 or 1. Clicking inside the scrollbar, for page scrolling, also works as well. This was tested with Firefox 3.6. Will look at IE 6 next.

The modification to onLiveScroll is in green below.



onLiveScroll : function()
{
var scrollTop = this.liveScroller.dom.scrollTop;

var cursor = Math.floor((scrollTop)/this.rowHeight);

if (Ext.isGecko) {
var delta = (scrollTop) % this.rowHeight;
if (delta !== 0) {
if (scrollTop > this.lastScrollPos) {
this.adjustScrollerPos(this.rowHeight - delta, true);
} else {
this.adjustScrollerPos(-(delta), true);
}
}
}

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;
},



I'm hoping I can do something similar for IE and Safari.

meroy
26 Mar 2010, 6:33 PM
Well, it's much harder to get the single-row scrolling to work correctly with Safari and IE 6 when clicking on the 2 scroll buttons. The onLiveScroll is called 3 times for IE and twice for Safari for each click. The handleWheel is working though and that scrolls the the grid by one row in either direction.

The code changes in my previous post is beneficial for Firefox 3.6. Scrolling is nice for both the scroll wheel and the scroll bar.

meroy
27 Mar 2010, 7:02 PM
Made some progress here and the scrollbar arrows are now scrolling the grid by one row (up or down) and not 0 at times for Firefox, 2 for Webkit and 3 for IE 6/7 (will test IE 8 as well).

Will post the code changes later on. The awesome livegrid user extension is a go now for me.

meroy
28 Mar 2010, 9:01 AM
The following highlights the code changes I have made to make the scroll buttons scroll the grid by one row only (up or down). I was able to combine the logic for Opera, IE 6/7/8 and WebKit into one. Gecko requires its own separate logic.

First, the onLiveScroll function is entered 3 times on each click for IE 6/7/8. To solve this, scrollDelay needs to be set to 1 or higher (1 will do though). It can be set as follow in your config:



var myView = new Ext.ux.grid.livegrid.GridView({
nearLimit : 100,
scrollDelay : Ext.isIE ? 1 : 0,
loadMask : {
msg : 'Buffering. Please wait...'
}
});


Or, it can be done inside the ext-ux-livegrid user extension. This is the method I chose.



/**
* @cfg {Number} scrollDelay The number of microseconds a call to the
* onLiveScroll-lisener should be delayed when the scroll event fires
*/
this.scrollDelay = Ext.isIE ? 1 : 0;


The scroll wheel works for all browsers as expected. Therefore, I don't want to change the behavior for that once the onLiveScroll event is called.

The one line change to handleWheel is highlighted in red below.



handleWheel : function(e)
{
if (this.rowHeight == -1) {
e.stopEvent();
return;
}
var d = e.getWheelDelta();

this.isWheelEvent = true;
this.adjustScrollerPos(-(d*this.rowHeight));

e.stopEvent();
},


Here is the updated onLiveScroll function. This has been tested with IE 6/7/8, Firefox 3.6, Safari 4 and Opera 10.5. Please note that for IE, scrollDelay set to 1 is required. I made this a default in GridView.js for IE and 0 for other browsers.



onLiveScroll : function()
{
var scrollTop = this.liveScroller.dom.scrollTop,
cursor, delta;

if (this.isWheelEvent) {
delete this.isWheelEvent;
} else {
if (Ext.isGecko) {
if (Math.abs(scrollTop - this.lastScrollPos) < 30) {
delta = scrollTop % this.rowHeight;
if (delta !== 0) {
delta = (scrollTop > this.lastScrollPos)? this.rowHeight - delta :-(delta);
this.adjustScrollerPos(delta, true);
scrollTop = this.liveScroller.dom.scrollTop;
}
}
} else {
if (Math.abs(scrollTop - this.lastScrollPos) < 80) {
delta = (scrollTop > this.lastScrollPos)? this.rowHeight :-(this.rowHeight);
this.adjustScrollerPos(-1, true, this.lastScrollPos + delta);
scrollTop = this.liveScroller.dom.scrollTop;
}
}
}

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;
},


I need to be able to "goTo" new position. The new position is passed as the 3rd argument (optional).



adjustScrollerPos : function(pixels, suspendEvent, newPos)
{
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;

if (Ext.isDefined(newPos)) {
scrollDom.scrollTop = newPos;
} else {
scrollDom.scrollTop += pixels;
}

if (suspendEvent === true) {
scrollDom.scrollTop = scrollDom.scrollTop;
liveScroller.on('scroll', this.onLiveScroll, this, {buffer : this.scrollDelay});
}
}


In another programming language such as Visual Basic or C#, one can specify how many pixels for the scrollbar thumb to move when clicking on the arrows. It would be nice if CSS allowed us to specify the smallChange/largeChange properties for the scrollbar thumb. I searched and couldn't fine a way to do this. Therefore, we have to adjust the thumb position accordingly to keep it in sync with the grid's rowHeight value.

Again, for IE 6/7/8, scrollDelay needs to be set. Fortunately, 1 will do.

Cheers,
Mario

meroy
28 Mar 2010, 1:06 PM
I have made a small change and updated the previous post.

To allow 0 for newPos, I changed this to use Ext.isDefined instead of doing: if (newPos) { ...



if (Ext.isDefined(newPos)) {
scrollDom.scrollTop = newPos;
} else {
scrollDom.scrollTop += pixels;
}

meroy
29 Mar 2010, 8:53 AM
IE 6 requires 60 as the scrollbar thumb moves by 60 pixels when clicking on the arrows. IE 8 moves the thumb by more than 60 pixels. Therefore, 60 isn't enough. It's dynamic and depends on the height of the window.

To ensure one row scrolling, when clicking on the scroll arrows, I changed the 2 values: 30 for Firefox and 80 for others.

Perhaps this was a waste of time as I just looked at the grid samples that come with Ext JS. Those will scroll 2.5 rows, 2, or 3 or anywhere in between. I had no idea these were doing that.

In any event, this completes what I wanted to achieve with the scrollbar. I thought that clicking on either of the scrollbar arrows should scroll the grid by one row -- similar to Excel. Firefox at times will not scroll the live grid at all and requires 2 clicks. That is what led me to do this work.



onLiveScroll : function()
{
var scrollTop = this.liveScroller.dom.scrollTop,
cursor, delta;

if (this.isWheelEvent) {
delete this.isWheelEvent;
} else {
if (Ext.isGecko) {
if (Math.abs(scrollTop - this.lastScrollPos) < 30) {
delta = scrollTop % this.rowHeight;
if (delta !== 0) {
delta = (scrollTop > this.lastScrollPos)? this.rowHeight - delta :-(delta);
this.adjustScrollerPos(delta, true);
scrollTop = this.liveScroller.dom.scrollTop;
}
}
} else {
if (Math.abs(scrollTop - this.lastScrollPos) < 80) {
delta = (scrollTop > this.lastScrollPos)? this.rowHeight :-(this.rowHeight);
this.adjustScrollerPos(-1, true, this.lastScrollPos + delta);
scrollTop = this.liveScroller.dom.scrollTop;
}
}
}

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;
},

meroy
29 Mar 2010, 5:27 PM
The idea above works great with Firefox. I'm seeing the behaviour of the grid and scrolling when the total records in the store is 52. As IE moves the scroll by 40+ pixels, it looks jerky due to adjusting the scrollbar to match the 1 row size of 21 pixels. :( It's fine if the total records is 2000+ though.

I will only leave in the part for Firefox as it requires 2 clicks to scroll at times when clicking on the scroll arrows. This is what I intended to fix in the first place.

It's too bad we are not able, via a CSS attribute, to specify the number of pixels to move for the scrollbar when clicking on either arrows. The optimum value would be the row height.

Humm...

meroy
29 Mar 2010, 5:51 PM
I think this can be solved.

For example, instead of setting the height of the scroller div to totalRows * rowHeight, it should instead factor in the scrollbar thumb movement in pixels.

Firefox is 14. Therefore, the height for the scrollbar div should be 14 * totalRows and not 21 (rowHeight) * totalRows.

WebKit is 40.
IE 6 is 50.

Then, it should be possible to make scrolling consistent, including single row scrolling when clicking on the scrollbar arrows. And the behaviour should be the same with 52 or 5000 total records in the store.

It's just a thought.

The question for me now is on whether the movement of the scrollbar thumb is the same, in number of pixels, when clicking on the arrows for 50 or 5000 total records. The answer is yes for IE 6. It's 50 pixels at a time for both. Firefox remains at 14 and WebKit still at 40.

At this point, I strongly believe the LiveGrid user extension will work smoother if factoring these values instead of rowHeight for the scrollbar. This should also help reduce some of the math involved for adjusting the scrollbar position and the end result a faster LiveGrid.

meroy
29 Mar 2010, 6:25 PM
This explains why, in the current implementation, IE 6 scrolls the grid by 3 rows and WebKit by 2 rows. Also, Firefox will not move and requires 2 clicks depending on which row you're on.

All of this can be solved. That is my humble opinion after a weekend retreat with the LiveGrid user extension. :-?

What works as far as scrolling goes.
Scrolling via the mouse wheel works as expected. Very smooth and nice.

What's not quite ready and described above.
Scrolling by clicking on the scrollbar arrows (up/down). This is what I'm after.

Keyboard page down/up only works if the scrollbar has focus.

meroy
29 Mar 2010, 6:58 PM
It's actually possible.

What's needed is to set minScrollMovement instead (new config) at runtime and use that for setting the hight of the scroller div and for adjusting the scroller position. It's working quite smoothly right now with Firefox. I have commented out my changes and I'm not having to click twice on the scrollbar arrow to scroll one row no matter what row I'm on. Previously, with 040 or 041, it requires 2 clicks for some rows.

This requires testing and also need to factor in page scrolling (clicking inside the scrollbar).

The rowHeight config is used solely for the grid and not the scroller div.

I just need to validate the minimum scroller movement for all browsers and set it at run time. Scrolling is smooth no matter if the total records is 50 or 5000.



minScrollMovement : Ext.isIE ? 50 : Ext.isGecko ? 14 : Ext.isWebKit ? 40 : 21, // using 21 as a default otherwise


This is turning out better than my first attempt at addressing the mouse click on the scrollbar arrrows. The behaviour can be made consistent among the browsers.

meroy
29 Mar 2010, 8:09 PM
Single click (srollbar arrows) is nice. I just need to validate the minimum scroller movement for IE 7/8 (IE 6 is 50) and Opera.

Also, I need to fudge the logic for page scrolling -- clicking inside the scrollbar -- to scroll the grid one page. The grid uses rowHeight and the scroller div uses minScrollMovement for the math.

IE still requires scrollDelay to be set to 1 to stop the event from calling onLiveScroll 3 times for each click though.

meroy
30 Mar 2010, 12:36 AM
Ok. After a long night, all was well. Then, I moved IE 6 to another monitor and changed the height of the browser window. The minScrollMovement for the scrollbar is not a fixed value whereas it is for Firefox and WebKit no matter the height of the window.

Therefore, this approach is a no go for me. It was nice to see paging/scrolling behaving consistently between the 3 browsers though.

This is quite a challenge.

Is it possible to have an Ext.Scrollbar to look and feel like the native scrollbar with CSS styling? This will allow full control of its properties: minScrollMovement, pageScrollMovement, etc.

calugaru.cristian
31 Mar 2010, 4:41 AM
I came up with a solution for this issue :



var newIndex = value; //the value in the grid you want to select
var newIndexPosition = newIndex * gridView.rowHeight;
var oldIndexPosition = gridView.lastScrollPos;
if (newIndexPosition >= oldIndexPosition) {
gridView.adjustScrollerPos(newIndexPosition - oldIndexPosition, true);
}
else {
gridView.adjustScrollerPos(newIndexPosition - oldIndexPosition, true);
}



Not sure if it's the best one, but seems to be working fine.
But I'm still stuck with the word-wrapping part. I already have a lot of functionality based on the livegrid, I would really dislike searching for a different component, just because of this problem.
Emily, maybe you could provide us with the changes you have made, even if they aren't fully working, so maybe we can see if something further can be done?:)





Hi again.
So, I tried playing with the updateLiveRows, but I haven't managed to use it properly.
I added a button on my window, with a hard-coded call to , let's say, the 300th index in my data. I added the livegrid.Toolbar, an, when I press the button, it seems that the information from the toolbar is getting updated ('Displaying 300-399 of 2000'), the loadMask
is getting shown, and a correct call is made to the back-end (with the start param to 300).
But right after this one, another call is made with the former index(0 in my case), and
the same rows are getting re-displayed. I don't see why 2 calls are made when calling the method..

Did anyone have a requirement to jump around rows with the livegrid?(:|

kkothari
31 Mar 2010, 9:18 AM
Hi,

I'm using the Livegrid extension and it works fine on FF but it gives me an "Invalid Argument" javascript error in IE6 and 7.

To reproduce the issue, please follow these steps:
1) Expand the attached zip file.

2) In livegrid.html, Replace the text "** Enter url **" for a server url that you can use and that'll return json.

3) Serve up livegrid.html through an app server. Use this text as the json returned from the server:
{"totalCount":"2","gameData":[{"Name":"John","Place":"Jacksonville","Animal":"Jackal","Thing":"Jacket"},{"Name":"Peter","Place":"Paris","Animal":"Porcupine","Thing":"Pen"}]}

The javascript error is seen right after the json is returned from the server.

A few other questions about Livegrid:
1) I see the client times out if the server takes longer than 30 sec or so (my server timeout setting is much more).
Is there some configuration i can change so that the client keeps waiting for a server response for much longer?

2) Is there any upper limit on how many records the Livegrid can handle? I mean, will I get the same performance for 100k records as for 10k records assuming the same server performance?

Thanks in advance, any info will be appreciated.

Kailash.

tolitius
31 Mar 2010, 10:24 AM
Hey people of ExtJS,

We have a requirement to implement live/endless/pageless/etc.. scrolling for the table (grid), where our clients can select rows [ checkboxes ], rows would have custom tooltips, etc..

We are now looking at Ext Livegrid and jQuery jqGrid. We like both, but the final choice would depend on how easy it is to have it fit our requirements.

Any suggestions on how to customize / configure Ext Livegrid? ( checkboxes, tooltips, etc.. ).

Thank you,
/Anatoly

P.S. Browsed through the wiki (http://wiki.ext-livegrid.com/) could not find the answer, hence falling back to the community support. thank you.

meroy
31 Mar 2010, 1:41 PM
Not sure if anybody has run into the following problem I came across just today.

We have users which have configured their Windows XP to use the Large Font Size versus the normal Font Size setting.

This makes the LiveGrid scrollbar non-functioning with IE 6 and even IE 8 (not tested with IE 7). It simply doesn't scroll when clicking on the scrollbar arrows.

This is so unfortunate. :(

meroy
31 Mar 2010, 1:51 PM
Not all is bad though. I did some benchmarks today with IE 8.

Did you know that by having IE 8 run in IE 8 mode, your scrolling will go into overdrive.

IE 8 by default takes about 44 seconds to scroll through 1172 rows when pressing on the down scrollbar arrow.

Adding the following to the header caused the scrolling to complete in 27 seconds. I was amazed by this. IE 8 kicks butt when you have this. Everything is much faster.



<meta http-equiv="X-UA-Compatible" content="IE=8" />


And for comparison, IE 6 took 59 seconds.

That made my day though. However, after testing on a couple machines where the user configured their Windows XP desktop to use Large Fonts, it quickly became a "no go" for the time being as scrolling fails completely. :(

We really need a successful LiveGrid. What @MindPatterns has done is quite impressive. Just the scrolling is flaky at times.

To solve this, we need some sort of Ext.Scrollbar where one can then specify both the small and large distance movement for the scrollbar thumb. That is not possible when using the native scrollbar.

meroy
31 Mar 2010, 8:50 PM
The scrollbar on this Live Grid works even with setting Large Fonts for the Display Appearance under Windows XP.

http://dowdybrown.com/dbprod/rico3/demo/

Look at the Editable LiveGrid (advanced) example.

On that one, keyboard page down/up is not functioning.

meroy
31 Mar 2010, 9:43 PM
Here are two more URLs where folks have mocked up a scrollbar. This is the kind of thing I believe will help complete the Live Grid user extension. It will then be possible to specify the small and large thumb movement size.

This one is the closest I have found to function well. This includes keyboard page/up when the grid has focus.

FleXcroll
http://www.hesido.com/web.php?page=customscrollbar

And I found this too while searching
http://www.akxl.net/labs/articles/scrollbars-made-from-javascript-and-css

I believe we need a new component: Ext.Scrollbar

That will help solve the issues we are seeing with scrolling.

It will also help reduce some of the math involved and therefore help make the entire LiveScrolling action faster.

meroy
2 Apr 2010, 9:59 AM
Not sure if anybody has run into the following problem I came across just today.

We have users which have configured their Windows XP to use the Large Font Size versus the normal Font Size setting.

This makes the LiveGrid scrollbar non-functioning with IE 6 and even IE 8 (not tested with IE 7). It simply doesn't scroll when clicking on the scrollbar arrows.

This is so unfortunate. :(

Want to report that this is solved for me.

All I needed to do was to increase the width from 18px to 19px and scrolling is working for folks with Windows XP and setting Display Properties --> Appearance --> Font size --> to Large. It requires 22px for Extra Large.



.ext-ux-livegrid-liveScroller {
z-index:1;
background:none!important;
position:absolute;
height:3px;
right:0px;
width:19px;
overflow:scroll;
overflow-x:hidden;
}



Attaching a picture of the Display Properties dialog. I have it set to Large for Font size for testing this configuration. The LiveGrid scrollbar is functional. It requires 19px for the width, otherwise scrolling fails when clicking on the arrows.

Animal
2 Apr 2010, 10:32 AM
That should probably be set programatically using Ext.getScrollBarWidth()

a.labeau
13 Apr 2010, 5:47 AM
Hello everybody.

I would like to use Ext.ux.liveGrid with a list of 100 000 data.

I used a Ext.data.DirectProxy to retrieve my list but I don't see my data... :-?

Here the code :



/*
* Example windows
*/

Ext.Direct.addProvider(Ext.app.REMOTING_API);

showTime.app.MyDesktop.LiveGridTest = Ext.extend(Ext.app.Module, {
id:'LiveGridTest-win',
init : function(){
this.launcher = {
text: 'LiveGridTest',
iconCls:'icon-grid',
handler : this.createWindow,
scope: this
}
},

createWindow : function(){
var desktop = this.app.getDesktop();
var win = desktop.getWindow('LiveGridTest-win');
if(!win)
{
var myView = new Ext.ux.grid.livegrid.GridView({
nearLimit : 300,
loadMask :
{
msg : 'Buffering. Please wait...'
}
});

win = desktop.createWindow({
id: 'LiveGridTest-win',
stateful:false,
title:'Grid Window',
width:740,
height:480,
shim:false,
animCollapse:false,
constrainHeader:true,

layout: 'fit',
items:
[
new Ext.ux.grid.livegrid.GridPanel({
enableDragDrop : false,
loadMask : {
msg : 'Loading...'
},
id: 'objectGrid',
store : new Ext.ux.grid.livegrid.Store({
// note by F.M.:livegrid needs to run with it's own store and we need to define our proxy object here instead of directly in the store
proxy: new Ext.data.DirectProxy({
api : {
read : Ext.app.ShowTimeManager.getListLiveGrid
}
}),

storeId : 'objectStore',
fields : ['number', 'text', 'date'],

reader : new Ext.ux.grid.livegrid.JsonReader({
root : '',
versionProperty : 'version',
totalProperty : 'total',
id : 'id'
}, [ {
name : 'number', sortType : 'string'
},{
name : 'text', sortType : 'string'
},{
name : 'date', sortType : 'string'
}]),
bufferSize : 300,
listeners :
{


'load' : function(store,records)
{
alert(records);
alert(records[0]);
alert(records[0].get("number"));
alert(records.length);
}
}

}),
columns: [
{id:'number',header: 'Number', width: 160, sortable: true, dataIndex: 'number'},
{header: 'text', width: 75, sortable: true, dataIndex: 'text'},
{header: 'date', width: 75, sortable: true, dataIndex: 'date'}
],

viewConfig : {
forceFit : true
},
stripeRows: true,
title: 'Objects',
stateful: true,
stateId: 'objectGrid',

selModel : new Ext.ux.grid.livegrid.RowSelectionModel(),
view : myView,
bbar : new Ext.ux.grid.livegrid.Toolbar({
view : myView,
displayInfo : true
})
})
]
});
}
win.show();
}
});

JimmyInMD
13 Apr 2010, 10:24 AM
Is there are way to refresh the data without changing the scrollbar position? I have looked for a couple hours and haven't found the solution.

Thanks!

chh
14 Apr 2010, 6:15 AM
Is there are way to refresh the data without changing the scrollbar position? I have looked for a couple hours and haven't found the solution.
NicoP talks about something like that here http://www.extjs.com/forum/showthread.php?17791-Ext.ux.LiveGrid&p=413587#post413587

wm003
14 Apr 2010, 10:13 PM
When using a normal Ext-Grid, i just prevent the scrolling this way:

...
view: new Ext.grid.GridView({
scrollToTop:Ext.emptyFn, //prevents scrolling to top of grid on reload
...

i haven't tested this on the livegrid yet, maybe it also works there

praneeth528
17 Apr 2010, 6:08 AM
Hi

I've been using Ext 2.1 version for sample coding with Ext JS. Does this Live Grid works with 2.1 version? I've replaced the php file with my JSP part. But when I'm trying to scroll (buffer:300) ., it's sending server requests for every single index. i.e instead of sending after 300., it's going on for just 1 index. Is there any flaw in my way.

Can anybody help me on these.....

a.labeau
17 Apr 2010, 9:02 AM
It's ok now, I forgot to insert the CSS of the liveGrid. So It didn't work...

But now It works!
Thanks for help

praneeth528
18 Apr 2010, 10:03 PM
Hi.,

I got the problem in my code. After changing the buffer limit it's working good....

I need s small clarification:::: Can we get the updated data set for grid after scroll from javascript instead of sending request to a server page? So that no of requests to server program will reduce i guess if server request is not required .

Any suggestions on this one pls...

JimmyInMD
20 Apr 2010, 6:20 AM
When using a normal Ext-Grid, i just prevent the scrolling this way:

...
view: new Ext.grid.GridView({
scrollToTop:Ext.emptyFn, //prevents scrolling to top of grid on reload
...

i haven't tested this on the livegrid yet, maybe it also works there

Thanks for the suggestion, but that doesn't work.

I am doing grid.store.reload() to do the actual refresh. Is that what I should be using?

benjixx
22 Apr 2010, 12:41 AM
Hi,

I'm just wondering when we can expect to see LiveGrid support for ExtJS 3.2.
Are there any concrete plans and release schedule for an upcoming release?

Regards,
Ben

pclovec
6 May 2010, 2:24 PM
some business machine doesn't display the up/down scroll bar. but in some dev machine is works , now i don't know what's Library or software is require for Extjs . i'm confused for it . i have tried to use google chrome in same machine ,it also works,

business machine is WinXP System , IE7 and Firefox (last release version),

Any one met this problem too . or have any advice.

also i open demo page http://www.ext-livegrid.com/demo/ the scroll bar doesn't showing. but mouse up and down are works.

thanks

xinfang

wm003
6 May 2010, 9:53 PM
I am doing grid.store.reload() to do the actual refresh. Is that what I should be using?
yes, that's the common way of reloading the store and automatically refresh the (live)-grid. i guess you need to create an event listeners prior to store-reload to remember the scrolling position.

Mandeep
10 May 2010, 8:29 AM
Is it possible to make LiveGrid work with Locking GridView extension, an example of locking grid can be seen at
http://www.extjs.com/deploy/dev/examples/grid/locking-grid.html

pclovec
11 May 2010, 10:46 AM
sometimes the grid cann't show all data, only show first record. but i
check the response data are return by firebug. also the data load into store

if i refresh or reload may be works,


What version of the product are you using? On what operating system?
Ext.ux.Livegrid 0.4.1 2010-03-20

Please provide any additional information below.

kkothari
11 May 2010, 11:04 AM
I'm having a problem with maintaining selections. I'm using revision 62 against Ext 3.0.0 final.

When rows are selected, and the grid is scrolled far enough to make a request, then scrolled back up again (causing another request), the rows are no longer selected.

Examining the records as they pass through replaceSelections(), I noticed that the ids of the records were incremented, not decremented. That is, the first request after scrolling down yielded records ext-record-101 through ext-record-200. The request made upon scrolling back up returned records ext-record-201 through ext-record-300, when it should have returned records somewhere between ext-record-0 and ext-record-100. Since that id is used to compute selection status, I figured that was probably the problem, but can't figure out anything I might have done to cause it.

I added a simple log statement inside the for block in replaceSelections(), line 2404 of livegrid-all-debug.js:


for (i = 0, len = records.length; i < len; i++) {
rec = records[i];
id = rec.id;

// Debug statement
console.log(rec.id);

if (assigned.indexOf(id) == -1 && selections.containsKey(id)) {
console.log(rec);
selections.add(rec);
}
}bufferSize is 100, nearLimit is 50.

Can others reproduce this? Have I neglected to pass some config option in somewhere?

Hi iancmcc,

Im facing the exact same issue, were you able to figure this out?

Thanks!
Kailash.

ItsMee
12 May 2010, 7:57 AM
I am working on merging LiveGrid with Column lock extension but struck at the point where in adjustBufferSize() code relies on first row that should have already been added to the grid but in my case for some reason its not getting added. Can somebody please tell me where the first row is added?

ItsMee
12 May 2010, 8:21 AM
Correction, actually the method which checks for first row is adjustVisibleRows()

ItsMee
19 May 2010, 10:50 AM
Has anyone tried livegrid .041 & Ext 3.1 with large number of columns. I just changed the demo which is available on ext-livegrid website to work with more than 20 columns. The grid displays fine but then if I scroll to the right and then click on one of the cells then suddenly headers and rows get out of sync. Is there any demo of live grid with more than 20 columns. Please let me know if you have any suggestions for fixing this issue.
Thanks.

MacSimon
20 May 2010, 4:33 AM
Last question for now:
I tried to use the grid with checkbox selection (by changing the superclass of Ext.ux.grid.livegrid.RowSelectionModel to Ext.grid.CheckboxSelectionModel).
That also only _almost_ works: The "(De)Select All" checkbox in the column header only acts on the currently buffered rows.
Any idea how to make it work on _all_ rows?

Did you ever got this working?

ThorstenSuckow
20 May 2010, 4:37 AM
hey there, CheckboxSelectionModel-Support will be available in the upcomin release!