aboutsummaryrefslogtreecommitdiff
path: root/includes/js/dojox/grid/VirtualGrid.js
diff options
context:
space:
mode:
Diffstat (limited to 'includes/js/dojox/grid/VirtualGrid.js')
-rw-r--r--includes/js/dojox/grid/VirtualGrid.js779
1 files changed, 779 insertions, 0 deletions
diff --git a/includes/js/dojox/grid/VirtualGrid.js b/includes/js/dojox/grid/VirtualGrid.js
new file mode 100644
index 0000000..104054c
--- /dev/null
+++ b/includes/js/dojox/grid/VirtualGrid.js
@@ -0,0 +1,779 @@
+if(!dojo._hasResource["dojox.grid.VirtualGrid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.grid.VirtualGrid"] = true;
+dojo.provide("dojox.grid.VirtualGrid");
+
+dojo.require("dojox.grid._grid.lib");
+dojo.require("dojox.grid._grid.scroller");
+dojo.require("dojox.grid._grid.view");
+dojo.require("dojox.grid._grid.views");
+dojo.require("dojox.grid._grid.layout");
+dojo.require("dojox.grid._grid.rows");
+dojo.require("dojox.grid._grid.focus");
+dojo.require("dojox.grid._grid.selection");
+dojo.require("dojox.grid._grid.edit");
+dojo.require("dojox.grid._grid.rowbar");
+dojo.require("dojox.grid._grid.publicEvents");
+
+dojo.declare('dojox.VirtualGrid',
+ [ dijit._Widget, dijit._Templated ],
+ {
+ // summary:
+ // A grid widget with virtual scrolling, cell editing, complex rows,
+ // sorting, fixed columns, sizeable columns, etc.
+ //
+ // description:
+ // VirtualGrid provides the full set of grid features without any
+ // direct connection to a data store.
+ //
+ // The grid exposes a get function for the grid, or optionally
+ // individual columns, to populate cell contents.
+ //
+ // The grid is rendered based on its structure, an object describing
+ // column and cell layout.
+ //
+ // example:
+ // A quick sample:
+ //
+ // define a get function
+ // | function get(inRowIndex){ // called in cell context
+ // | return [this.index, inRowIndex].join(', ');
+ // | }
+ //
+ // define the grid structure:
+ // | var structure = [ // array of view objects
+ // | { cells: [// array of rows, a row is an array of cells
+ // | [
+ // | { name: "Alpha", width: 6 },
+ // | { name: "Beta" },
+ // | { name: "Gamma", get: get }]
+ // | ]}
+ // | ];
+ //
+ // | <div id="grid"
+ // | rowCount="100" get="get"
+ // | structure="structure"
+ // | dojoType="dojox.VirtualGrid"></div>
+
+ templateString:"<div class=\"dojoxGrid\" hidefocus=\"hidefocus\" role=\"wairole:grid\">\n\t<div class=\"dojoxGrid-master-header\" dojoAttachPoint=\"viewsHeaderNode\"></div>\n\t<div class=\"dojoxGrid-master-view\" dojoAttachPoint=\"viewsNode\"></div>\n\t<span dojoAttachPoint=\"lastFocusNode\" tabindex=\"0\"></span>\n</div>\n",
+
+ // classTag: String
+ // CSS class applied to the grid's domNode
+ classTag: 'dojoxGrid',
+
+ get: function(inRowIndex){
+ // summary: Default data getter.
+ // description:
+ // Provides data to display in a grid cell. Called in grid cell context.
+ // So this.cell.index is the column index.
+ // inRowIndex: Integer
+ // Row for which to provide data
+ // returns:
+ // Data to display for a given grid cell.
+ },
+
+ // settings
+ // rowCount: Integer
+ // Number of rows to display.
+ rowCount: 5,
+
+ // keepRows: Integer
+ // Number of rows to keep in the rendering cache.
+ keepRows: 75,
+
+ // rowsPerPage: Integer
+ // Number of rows to render at a time.
+ rowsPerPage: 25,
+
+ // autoWidth: Boolean
+ // If autoWidth is true, grid width is automatically set to fit the data.
+ autoWidth: false,
+
+ // autoHeight: Boolean
+ // If autoHeight is true, grid height is automatically set to fit the data.
+ autoHeight: false,
+
+ // autoRender: Boolean
+ // If autoRender is true, grid will render itself after initialization.
+ autoRender: true,
+
+ // defaultHeight: String
+ // default height of the grid, measured in any valid css unit.
+ defaultHeight: '15em',
+
+ // structure: Object|String
+ // View layout defintion. Can be set to a layout object, or to the (string) name of a layout object.
+ structure: '',
+
+ // elasticView: Integer
+ // Override defaults and make the indexed grid view elastic, thus filling available horizontal space.
+ elasticView: -1,
+
+ // singleClickEdit: boolean
+ // Single-click starts editing. Default is double-click
+ singleClickEdit: false,
+
+ // Used to store the last two clicks, to ensure double-clicking occurs based on the intended row
+ _click: null,
+
+ // private
+ sortInfo: 0,
+ themeable: true,
+
+ // initialization
+ buildRendering: function(){
+ this.inherited(arguments);
+ // reset get from blank function (needed for markup parsing) to null, if not changed
+ if(this.get == dojox.VirtualGrid.prototype.get){
+ this.get = null;
+ }
+ if(!this.domNode.getAttribute('tabIndex')){
+ this.domNode.tabIndex = "0";
+ }
+ this.createScroller();
+ this.createLayout();
+ this.createViews();
+ this.createManagers();
+ dojox.grid.initTextSizePoll();
+ this.connect(dojox.grid, "textSizeChanged", "textSizeChanged");
+ dojox.grid.funnelEvents(this.domNode, this, 'doKeyEvent', dojox.grid.keyEvents);
+ this.connect(this, "onShow", "renderOnIdle");
+ },
+ postCreate: function(){
+ // replace stock styleChanged with one that triggers an update
+ this.styleChanged = this._styleChanged;
+ this.setStructure(this.structure);
+ this._click = [];
+ },
+
+ destroy: function(){
+ this.domNode.onReveal = null;
+ this.domNode.onSizeChange = null;
+ this.edit.destroy();
+ this.views.destroyViews();
+ this.inherited(arguments);
+ },
+
+ styleChanged: function(){
+ this.setStyledClass(this.domNode, '');
+ },
+
+ _styleChanged: function(){
+ this.styleChanged();
+ this.update();
+ },
+
+ textSizeChanged: function(){
+ setTimeout(dojo.hitch(this, "_textSizeChanged"), 1);
+ },
+
+ _textSizeChanged: function(){
+ if(this.domNode){
+ this.views.forEach(function(v){
+ v.content.update();
+ });
+ this.render();
+ }
+ },
+
+ sizeChange: function(){
+ dojox.grid.jobs.job(this.id + 'SizeChange', 50, dojo.hitch(this, "update"));
+ },
+
+ renderOnIdle: function() {
+ setTimeout(dojo.hitch(this, "render"), 1);
+ },
+
+ createManagers: function(){
+ // summary:
+ // create grid managers for various tasks including rows, focus, selection, editing
+
+ // row manager
+ this.rows = new dojox.grid.rows(this);
+ // focus manager
+ this.focus = new dojox.grid.focus(this);
+ // selection manager
+ this.selection = new dojox.grid.selection(this);
+ // edit manager
+ this.edit = new dojox.grid.edit(this);
+ },
+
+ createScroller: function(){
+ // summary: Creates a new virtual scroller
+ this.scroller = new dojox.grid.scroller.columns();
+ this.scroller._pageIdPrefix = this.id + '-';
+ this.scroller.renderRow = dojo.hitch(this, "renderRow");
+ this.scroller.removeRow = dojo.hitch(this, "rowRemoved");
+ },
+
+ createLayout: function(){
+ // summary: Creates a new Grid layout
+ this.layout = new dojox.grid.layout(this);
+ },
+
+ // views
+ createViews: function(){
+ this.views = new dojox.grid.views(this);
+ this.views.createView = dojo.hitch(this, "createView");
+ },
+
+ createView: function(inClass){
+ if(dojo.isAIR){
+ var obj = window;
+ var names = inClass.split('.');
+ for(var i=0;i<names.length;i++){
+ if(typeof obj[names[i]]=='undefined'){
+ var undefstring = names[0];
+ for(var j=1;j<=i;j++){
+ undefstring+="."+names[j];
+ }
+ throw new Error(undefstring+" is undefined");
+ }
+ obj = obj[names[i]];
+ }
+ var c = obj;
+ }else{
+ var c = eval(inClass);
+ }
+ var view = new c({ grid: this });
+ this.viewsNode.appendChild(view.domNode);
+ this.viewsHeaderNode.appendChild(view.headerNode);
+ this.views.addView(view);
+ return view;
+ },
+
+ buildViews: function(){
+ for(var i=0, vs; (vs=this.layout.structure[i]); i++){
+ this.createView(vs.type || dojox._scopeName + ".GridView").setStructure(vs);
+ }
+ this.scroller.setContentNodes(this.views.getContentNodes());
+ },
+
+ setStructure: function(inStructure){
+ // summary:
+ // Install a new structure and rebuild the grid.
+ // inStructure: Object
+ // Structure object defines the grid layout and provides various
+ // options for grid views and columns
+ // description:
+ // A grid structure is an array of view objects. A view object can
+ // specify a view type (view class), width, noscroll (boolean flag
+ // for view scrolling), and cells. Cells is an array of objects
+ // corresponding to each grid column. The view cells object is an
+ // array of subrows comprising a single row. Each subrow is an
+ // array of column objects. A column object can have a name,
+ // width, value (default), get function to provide data, styles,
+ // and span attributes (rowSpan, colSpan).
+
+ this.views.destroyViews();
+ this.structure = inStructure;
+ if((this.structure)&&(dojo.isString(this.structure))){
+ this.structure=dojox.grid.getProp(this.structure);
+ }
+ if(!this.structure){
+ this.structure=window["layout"];
+ }
+ if(!this.structure){
+ return;
+ }
+ this.layout.setStructure(this.structure);
+ this._structureChanged();
+ },
+
+ _structureChanged: function() {
+ this.buildViews();
+ if(this.autoRender){
+ this.render();
+ }
+ },
+
+ hasLayout: function() {
+ return this.layout.cells.length;
+ },
+
+ // sizing
+ resize: function(sizeBox){
+ // summary:
+ // Update the grid's rendering dimensions and resize it
+ // sizeBox: Object?
+ // {w: int, h: int, l: int, t: int}
+
+ // FIXME: If grid is not sized explicitly, sometimes bogus scrollbars
+ // can appear in our container, which may require an extra call to 'resize'
+ // to sort out.
+ this._sizeBox = sizeBox;
+ this._resize();
+ this.sizeChange();
+ },
+
+ _getPadBorder: function() {
+ this._padBorder = this._padBorder || dojo._getPadBorderExtents(this.domNode);
+ return this._padBorder;
+ },
+
+ _resize: function(){
+ // if we have set up everything except the DOM, we cannot resize
+ if(!this.domNode.parentNode || this.domNode.parentNode.nodeType != 1 || !this.hasLayout()){
+ return;
+ }
+ // useful measurement
+ var padBorder = this._getPadBorder();
+ // grid height
+ if(this.autoHeight){
+ this.domNode.style.height = 'auto';
+ this.viewsNode.style.height = '';
+ }else if(this.flex > 0){
+ }else if(this.domNode.clientHeight <= padBorder.h){
+ if(this.domNode.parentNode == document.body){
+ this.domNode.style.height = this.defaultHeight;
+ }else{
+ this.fitTo = "parent";
+ }
+ }
+ // if we are given dimensions, size the grid's domNode to those dimensions
+ if(this._sizeBox){
+ dojo.contentBox(this.domNode, this._sizeBox);
+ }else if(this.fitTo == "parent"){
+ var h = dojo._getContentBox(this.domNode.parentNode).h;
+ dojo.marginBox(this.domNode, { h: Math.max(0, h) });
+ }
+
+ var h = dojo._getContentBox(this.domNode).h;
+ if(h == 0 && !this.autoHeight){
+ // We need to hide the header, since the Grid is essentially hidden.
+ this.viewsHeaderNode.style.display = "none";
+ }else{
+ // Otherwise, show the header and give it an appropriate height.
+ this.viewsHeaderNode.style.display = "block";
+ }
+
+ // NOTE: it is essential that width be applied before height
+ // Header height can only be calculated properly after view widths have been set.
+ // This is because flex column width is naturally 0 in Firefox.
+ // Therefore prior to width sizing flex columns with spaces are maximally wrapped
+ // and calculated to be too tall.
+ this.adaptWidth();
+ this.adaptHeight();
+
+ // default row height (FIXME: use running average(?), remove magic #)
+ this.scroller.defaultRowHeight = this.rows.getDefaultHeightPx() + 1;
+ this.postresize();
+ },
+
+ adaptWidth: function() {
+ // private: sets width and position for views and update grid width if necessary
+ var
+ w = this.autoWidth ? 0 : this.domNode.clientWidth || (this.domNode.offsetWidth - this._getPadBorder().w);
+ vw = this.views.arrange(1, w);
+ this.views.onEach("adaptWidth");
+ if (this.autoWidth)
+ this.domNode.style.width = vw + "px";
+ },
+
+ adaptHeight: function(){
+ // private: measures and normalizes header height, then sets view heights, and then updates scroller
+ var vns = this.viewsHeaderNode.style, t = vns.display == "none" ? 0 : this.views.measureHeader();
+ vns.height = t + 'px';
+ // header heights are reset during measuring so must be normalized after measuring.
+ this.views.normalizeHeaderNodeHeight();
+ // content extent
+ var h = (this.autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0);
+ this.views.onEach('setSize', [0, h]);
+ this.views.onEach('adaptHeight');
+ this.scroller.windowHeight = h;
+ },
+
+ // render
+ render: function(){
+ // summary:
+ // Render the grid, headers, and views. Edit and scrolling states are reset. To retain edit and
+ // scrolling states, see Update.
+
+ if(!this.domNode){return;}
+
+ if(!this.hasLayout()) {
+ this.scroller.init(0, this.keepRows, this.rowsPerPage);
+ return;
+ }
+ //
+ this.update = this.defaultUpdate;
+ this.scroller.init(this.rowCount, this.keepRows, this.rowsPerPage);
+ this.prerender();
+ this.setScrollTop(0);
+ this.postrender();
+ },
+
+ prerender: function(){
+ // if autoHeight, make sure scroller knows not to virtualize; everything must be rendered.
+ this.keepRows = this.autoHeight ? 0 : this.constructor.prototype.keepRows;
+ this.scroller.setKeepInfo(this.keepRows);
+ this.views.render();
+ this._resize();
+ },
+
+ postrender: function(){
+ this.postresize();
+ this.focus.initFocusView();
+ // make rows unselectable
+ dojo.setSelectable(this.domNode, false);
+ },
+
+ postresize: function(){
+ // views are position absolute, so they do not inflate the parent
+ if(this.autoHeight){
+ this.viewsNode.style.height = this.views.measureContent() + 'px';
+ }
+ },
+
+ renderRow: function(inRowIndex, inNodes){
+ // summary: private, used internally to render rows
+ this.views.renderRow(inRowIndex, inNodes);
+ },
+
+ rowRemoved: function(inRowIndex){
+ // summary: private, used internally to remove rows
+ this.views.rowRemoved(inRowIndex);
+ },
+
+ invalidated: null,
+
+ updating: false,
+
+ beginUpdate: function(){
+ // summary:
+ // Use to make multiple changes to rows while queueing row updating.
+ // NOTE: not currently supporting nested begin/endUpdate calls
+ this.invalidated = [];
+ this.updating = true;
+ },
+
+ endUpdate: function(){
+ // summary:
+ // Use after calling beginUpdate to render any changes made to rows.
+ this.updating = false;
+ var i = this.invalidated;
+ if(i.all){
+ this.update();
+ }else if(i.rowCount != undefined){
+ this.updateRowCount(i.rowCount);
+ }else{
+ for(r in i){
+ this.updateRow(Number(r));
+ }
+ }
+ this.invalidated = null;
+ },
+
+ // update
+ defaultUpdate: function(){
+ // note: initial update calls render and subsequently this function.
+ if(!this.domNode){return;}
+ if(this.updating){
+ this.invalidated.all = true;
+ return;
+ }
+ //this.edit.saveState(inRowIndex);
+ this.prerender();
+ this.scroller.invalidateNodes();
+ this.setScrollTop(this.scrollTop);
+ this.postrender();
+ //this.edit.restoreState(inRowIndex);
+ },
+
+ update: function(){
+ // summary:
+ // Update the grid, retaining edit and scrolling states.
+ this.render();
+ },
+
+ updateRow: function(inRowIndex){
+ // summary:
+ // Render a single row.
+ // inRowIndex: Integer
+ // Index of the row to render
+ inRowIndex = Number(inRowIndex);
+ if(this.updating){
+ this.invalidated[inRowIndex]=true;
+ }else{
+ this.views.updateRow(inRowIndex, this.rows.getHeight(inRowIndex));
+ this.scroller.rowHeightChanged(inRowIndex);
+ }
+ },
+
+ updateRowCount: function(inRowCount){
+ //summary:
+ // Change the number of rows.
+ // inRowCount: int
+ // Number of rows in the grid.
+ if(this.updating){
+ this.invalidated.rowCount = inRowCount;
+ }else{
+ this.rowCount = inRowCount;
+ if(this.layout.cells.length){
+ this.scroller.updateRowCount(inRowCount);
+ this.setScrollTop(this.scrollTop);
+ }
+ this._resize();
+ }
+ },
+
+ updateRowStyles: function(inRowIndex){
+ // summary:
+ // Update the styles for a row after it's state has changed.
+ this.views.updateRowStyles(inRowIndex);
+ },
+
+ rowHeightChanged: function(inRowIndex){
+ // summary:
+ // Update grid when the height of a row has changed. Row height is handled automatically as rows
+ // are rendered. Use this function only to update a row's height outside the normal rendering process.
+ // inRowIndex: Integer
+ // index of the row that has changed height
+
+ this.views.renormalizeRow(inRowIndex);
+ this.scroller.rowHeightChanged(inRowIndex);
+ },
+
+ // fastScroll: Boolean
+ // flag modifies vertical scrolling behavior. Defaults to true but set to false for slower
+ // scroll performance but more immediate scrolling feedback
+ fastScroll: true,
+
+ delayScroll: false,
+
+ // scrollRedrawThreshold: int
+ // pixel distance a user must scroll vertically to trigger grid scrolling.
+ scrollRedrawThreshold: (dojo.isIE ? 100 : 50),
+
+ // scroll methods
+ scrollTo: function(inTop){
+ // summary:
+ // Vertically scroll the grid to a given pixel position
+ // inTop: Integer
+ // vertical position of the grid in pixels
+ if(!this.fastScroll){
+ this.setScrollTop(inTop);
+ return;
+ }
+ var delta = Math.abs(this.lastScrollTop - inTop);
+ this.lastScrollTop = inTop;
+ if(delta > this.scrollRedrawThreshold || this.delayScroll){
+ this.delayScroll = true;
+ this.scrollTop = inTop;
+ this.views.setScrollTop(inTop);
+ dojox.grid.jobs.job('dojoxGrid-scroll', 200, dojo.hitch(this, "finishScrollJob"));
+ }else{
+ this.setScrollTop(inTop);
+ }
+ },
+
+ finishScrollJob: function(){
+ this.delayScroll = false;
+ this.setScrollTop(this.scrollTop);
+ },
+
+ setScrollTop: function(inTop){
+ this.scrollTop = this.views.setScrollTop(inTop);
+ this.scroller.scroll(this.scrollTop);
+ },
+
+ scrollToRow: function(inRowIndex){
+ // summary:
+ // Scroll the grid to a specific row.
+ // inRowIndex: Integer
+ // grid row index
+ this.setScrollTop(this.scroller.findScrollTop(inRowIndex) + 1);
+ },
+
+ // styling (private, used internally to style individual parts of a row)
+ styleRowNode: function(inRowIndex, inRowNode){
+ if(inRowNode){
+ this.rows.styleRowNode(inRowIndex, inRowNode);
+ }
+ },
+
+ // cells
+ getCell: function(inIndex){
+ // summary:
+ // Retrieves the cell object for a given grid column.
+ // inIndex: Integer
+ // Grid column index of cell to retrieve
+ // returns:
+ // a grid cell
+ return this.layout.cells[inIndex];
+ },
+
+ setCellWidth: function(inIndex, inUnitWidth) {
+ this.getCell(inIndex).unitWidth = inUnitWidth;
+ },
+
+ getCellName: function(inCell){
+ // summary: Returns the cell name of a passed cell
+ return "Cell " + inCell.index; // String
+ },
+
+ // sorting
+ canSort: function(inSortInfo){
+ // summary:
+ // Determines if the grid can be sorted
+ // inSortInfo: Integer
+ // Sort information, 1-based index of column on which to sort, positive for an ascending sort
+ // and negative for a descending sort
+ // returns: Boolean
+ // True if grid can be sorted on the given column in the given direction
+ },
+
+ sort: function(){
+ },
+
+ getSortAsc: function(inSortInfo){
+ // summary:
+ // Returns true if grid is sorted in an ascending direction.
+ inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
+ return Boolean(inSortInfo > 0); // Boolean
+ },
+
+ getSortIndex: function(inSortInfo){
+ // summary:
+ // Returns the index of the column on which the grid is sorted
+ inSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;
+ return Math.abs(inSortInfo) - 1; // Integer
+ },
+
+ setSortIndex: function(inIndex, inAsc){
+ // summary:
+ // Sort the grid on a column in a specified direction
+ // inIndex: Integer
+ // Column index on which to sort.
+ // inAsc: Boolean
+ // If true, sort the grid in ascending order, otherwise in descending order
+ var si = inIndex +1;
+ if(inAsc != undefined){
+ si *= (inAsc ? 1 : -1);
+ } else if(this.getSortIndex() == inIndex){
+ si = -this.sortInfo;
+ }
+ this.setSortInfo(si);
+ },
+
+ setSortInfo: function(inSortInfo){
+ if(this.canSort(inSortInfo)){
+ this.sortInfo = inSortInfo;
+ this.sort();
+ this.update();
+ }
+ },
+
+ // DOM event handler
+ doKeyEvent: function(e){
+ e.dispatch = 'do' + e.type;
+ this.onKeyEvent(e);
+ },
+
+ // event dispatch
+ //: protected
+ _dispatch: function(m, e){
+ if(m in this){
+ return this[m](e);
+ }
+ },
+
+ dispatchKeyEvent: function(e){
+ this._dispatch(e.dispatch, e);
+ },
+
+ dispatchContentEvent: function(e){
+ this.edit.dispatchEvent(e) || e.sourceView.dispatchContentEvent(e) || this._dispatch(e.dispatch, e);
+ },
+
+ dispatchHeaderEvent: function(e){
+ e.sourceView.dispatchHeaderEvent(e) || this._dispatch('doheader' + e.type, e);
+ },
+
+ dokeydown: function(e){
+ this.onKeyDown(e);
+ },
+
+ doclick: function(e){
+ if(e.cellNode){
+ this.onCellClick(e);
+ }else{
+ this.onRowClick(e);
+ }
+ },
+
+ dodblclick: function(e){
+ if(e.cellNode){
+ this.onCellDblClick(e);
+ }else{
+ this.onRowDblClick(e);
+ }
+ },
+
+ docontextmenu: function(e){
+ if(e.cellNode){
+ this.onCellContextMenu(e);
+ }else{
+ this.onRowContextMenu(e);
+ }
+ },
+
+ doheaderclick: function(e){
+ if(e.cellNode){
+ this.onHeaderCellClick(e);
+ }else{
+ this.onHeaderClick(e);
+ }
+ },
+
+ doheaderdblclick: function(e){
+ if(e.cellNode){
+ this.onHeaderCellDblClick(e);
+ }else{
+ this.onHeaderDblClick(e);
+ }
+ },
+
+ doheadercontextmenu: function(e){
+ if(e.cellNode){
+ this.onHeaderCellContextMenu(e);
+ }else{
+ this.onHeaderContextMenu(e);
+ }
+ },
+
+ // override to modify editing process
+ doStartEdit: function(inCell, inRowIndex){
+ this.onStartEdit(inCell, inRowIndex);
+ },
+
+ doApplyCellEdit: function(inValue, inRowIndex, inFieldIndex){
+ this.onApplyCellEdit(inValue, inRowIndex, inFieldIndex);
+ },
+
+ doCancelEdit: function(inRowIndex){
+ this.onCancelEdit(inRowIndex);
+ },
+
+ doApplyEdit: function(inRowIndex){
+ this.onApplyEdit(inRowIndex);
+ },
+
+ // row editing
+ addRow: function(){
+ // summary:
+ // Add a row to the grid.
+ this.updateRowCount(this.rowCount+1);
+ },
+
+ removeSelectedRows: function(){
+ // summary:
+ // Remove the selected rows from the grid.
+ this.updateRowCount(Math.max(0, this.rowCount - this.selection.getSelected().length));
+ this.selection.clear();
+ }
+
+});
+
+dojo.mixin(dojox.VirtualGrid.prototype, dojox.grid.publicEvents);
+
+}