Blame | Last modification | View Log | RSS feed
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"><div class="dojoxGrid-master-header" dojoAttachPoint="headerNode"></div><div class="dojoxGrid-master-view" dojoAttachPoint="viewsNode"></div><span dojoAttachPoint="lastFocusNode" tabindex="0"></span></div>',// classTag: string// css class applied to the grid's domNodeclassTag: '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: integerrow for which to provide datareturns:data to display for a given grid cell.*/},// settings// rowCount: int// Number of rows to display.rowCount: 5,// keepRows: int// Number of rows to keep in the rendering cache.keepRows: 75,// rowsPerPage: int// 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 or string// View layout defintion. Can be set to a layout object, or to the (string) name of a layout object.structure: '',// elasticView: int// 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-clicksingleClickEdit: false,// privatesortInfo: 0,themeable: true,// initializationbuildRendering: function(){this.inherited(arguments);// reset get from blank function (needed for markup parsing) to null, if not changedif(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 updatethis.styleChanged = this._styleChanged;this.setStructure(this.structure);},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);},// managerscreateManagers: function(){// summary:// create grid managers for various tasks including rows, focus, selection, editing// row managerthis.rows = new dojox.grid.rows(this);// focus managerthis.focus = new dojox.grid.focus(this);// selection managerthis.selection = new dojox.grid.selection(this);// edit managerthis.edit = new dojox.grid.edit(this);},// virtual scrollercreateScroller: function(){this.scroller = new dojox.grid.scroller.columns();this.scroller.renderRow = dojo.hitch(this, "renderRow");this.scroller.removeRow = dojo.hitch(this, "rowRemoved");},// layoutcreateLayout: function(){this.layout = new dojox.grid.layout(this);},// viewscreateViews: function(){this.views = new dojox.grid.views(this);this.views.createView = dojo.hitch(this, "createView");},createView: function(inClass){var c = eval(inClass);var view = new c({ grid: this });this.viewsNode.appendChild(view.domNode);this.headerNode.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.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();}},// sizingresize: function(){// summary:// Update the grid's rendering dimensions and resize it// 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.// if we have set up everything except the DOM, we cannot resizeif(!this.domNode.parentNode){return;}// useful measurementvar padBorder = dojo._getPadBorderExtents(this.domNode);// grid heightif(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(this.fitTo == "parent"){var h = dojo._getContentBox(this.domNode.parentNode).h;dojo.marginBox(this.domNode, { h: Math.max(0, h) });}// header heightvar t = this.views.measureHeader();this.headerNode.style.height = t + 'px';// content extentvar l = 1, h = (this.autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0);if(this.autoWidth){// grid width set to total widththis.domNode.style.width = this.views.arrange(l, 0, 0, h) + 'px';}else{// views fit to our clientWidthvar w = this.domNode.clientWidth || (this.domNode.offsetWidth - padBorder.w);this.views.arrange(l, 0, w, h);}// virtual scroller heightthis.scroller.windowHeight = h;// default row height (FIXME: use running average(?), remove magic #)this.scroller.defaultRowHeight = this.rows.getDefaultHeightPx() + 1;this.postresize();},resizeHeight: function(){var t = this.views.measureHeader();this.headerNode.style.height = t + 'px';// content extentvar h = (this.autoHeight ? -1 : Math.max(this.domNode.clientHeight - t, 0) || 0);//this.views.arrange(0, 0, 0, h);this.views.onEach('setSize', [0, h]);this.views.onEach('resizeHeight');this.scroller.windowHeight = h;},// renderrender: 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;}//this.update = this.defaultUpdate;this.scroller.init(this.rowCount, this.keepRows, this.rowsPerPage);this.prerender();this.setScrollTop(0);this.postrender();},prerender: function(){this.views.render();this.resize();},postrender: function(){this.postresize();this.focus.initFocusView();// make rows unselectabledojo.setSelectable(this.domNode, false);},postresize: function(){// views are position absolute, so they do not inflate the parentif(this.autoHeight){this.viewsNode.style.height = this.views.measureContent() + 'px';}},// private, used internally to render rowsrenderRow: function(inRowIndex, inNodes){this.views.renderRow(inRowIndex, inNodes);},// private, used internally to remove rowsrowRemoved: function(inRowIndex){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 callsthis.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;},// updatedefaultUpdate: function(){// note: initial update calls render and subsequently this function.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: int// index of the row to renderinRowIndex = Number(inRowIndex);if(this.updating){this.invalidated[inRowIndex]=true;return;}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;return;}this.rowCount = inRowCount;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: int// index of the row that has changed heightthis.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 feedbackfastScroll: true,delayScroll: false,// scrollRedrawThreshold: int// pixel distance a user must scroll vertically to trigger grid scrolling.scrollRedrawThreshold: (dojo.isIE ? 100 : 50),// scroll methodsscrollTo: function(inTop){// summary:// Vertically scroll the grid to a given pixel position// inTop: int// vertical position of the grid in pixelsif(!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: int// grid row indexthis.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);}},// cellsgetCell: function(inIndex){// summary:// retrieves the cell object for a given grid column.// inIndex: int// grid column index of cell to retrieve// returns:// a grid cellreturn this.layout.cells[inIndex];},setCellWidth: function(inIndex, inUnitWidth) {this.getCell(inIndex).unitWidth = inUnitWidth;},getCellName: function(inCell){return "Cell " + inCell.index;},// sortingcanSort: function(inSortInfo){// summary:// determines if the grid can be sorted// inSortInfo: int// Sort information, 1-based index of column on which to sort, positive for an ascending sort// and negative for a descending sort// returns:// 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);},getSortIndex: function(inSortInfo){// summary:// returns the index of the column on which the grid is sortedinSortInfo = inSortInfo == undefined ? this.sortInfo : inSortInfo;return Math.abs(inSortInfo) - 1;},setSortIndex: function(inIndex, inAsc){// summary:// Sort the grid on a column in a specified direction// inIndex: int// Column index on which to sort.// inAsc: boolean// If true, sort the grid in ascending order, otherwise in descending ordervar 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 handlerdoKeyEvent: 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 processdoStartEdit: 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 editingaddRow: 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);}