/*
Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojox.grid.LazyTreeGrid"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.grid.LazyTreeGrid"] = true;
dojo.provide("dojox.grid.LazyTreeGrid");
dojo.require("dojox.grid._View");
dojo.require("dojox.grid.TreeGrid");
dojo.require("dojox.grid.cells.tree");
dojo.require("dojox.grid.LazyTreeGridStoreModel");
dojo.declare("dojox.grid._LazyExpando", [dijit._Widget, dijit._Templated], {
itemId: "",
cellIdx: -1,
view: null,
rowIdx: -1,
expandoCell: null,
level: 0,
open: false,
templateString:"
\n",
onToggle: function(event){
// Summary
// Function for expand/collapse row
this.setOpen(!this.view.grid.cache.getExpandoStatusByRowIndex(this.rowIdx));
try{
dojo.stopEvent(event);
}catch(e){}
},
setOpen: function(open){
var g = this.view.grid,
item = g.cache.getItemByRowIndex(this.rowIdx);
if(!g.treeModel.mayHaveChildren(item)){
g.stateChangeNode = null;
return;
}
if(item && !g._loading){
g.stateChangeNode = this.domNode;
g.cache.updateCache(this.rowIdx, {"expandoStatus": open});
g.expandoFetch(this.rowIdx, open);
this.open = open;
}
this._updateOpenState(item);
},
_updateOpenState: function(item){
// Summary
// Update the expando icon
var grid = this.view.grid;
if(item && grid.treeModel.mayHaveChildren(item)){
var state = grid.cache.getExpandoStatusByRowIndex(this.rowIdx);
this.expandoInner.innerHTML = state ? "-" : "+";
dojo.toggleClass(this.domNode, "dojoxGridExpandoOpened", state);
dijit.setWaiState(this.domNode.parentNode, "expanded", state);
}else{
dojo.removeClass(this.domNode, "dojoxGridExpandoOpened");
}
},
setRowNode: function(rowIdx, rowNode, view){
if(this.cellIdx < 0 || !this.itemId){ return false; }
this._initialized = false;
this.view = view;
this.rowIdx = rowIdx;
this.expandoCell = view.structure.cells[0][this.cellIdx];
var d = this.domNode;
if(d && d.parentNode && d.parentNode.parentNode){
this._tableRow = d.parentNode.parentNode;
}
dojo.style(this.domNode , "marginLeft" , (this.level * 1.125) + "em");
this._updateOpenState(view.grid.cache.getItemByRowIndex(this.rowIdx));
return true;
}
});
dojo.declare("dojox.grid._TreeGridContentBuilder", dojox.grid._ContentBuilder, {
// summary:
// Could create row content innerHTML by different appoarch for different data structure
generateHtml: function(inDataIndex, inRowIndex){
// summary:
// create row innterHTML for flat data structure
var html = this.getTableArray(),
grid = this.grid,
v = this.view,
cells = v.structure.cells,
item = grid.getItem(inRowIndex),
level = 0,
treePath = grid.cache.getTreePathByRowIndex(inRowIndex),
rowStack = [],
toggleClasses = [];
dojox.grid.util.fire(this.view, "onBeforeRow", [inRowIndex, cells]);
if(item !== null && treePath !== null){
rowStack = treePath.split("/");
level = rowStack.length - 1;
toggleClasses[0] = "dojoxGridRowToggle-" + rowStack.join("-");
if(!grid.treeModel.mayHaveChildren(item)){
toggleClasses.push("dojoxGridNoChildren");
}
}
for(var j = 0, row; (row = cells[j]); j++){
if(row.hidden || row.header){
continue;
}
var tr = '';
html.push(tr);
var k = 0, mergedCells = this._getColSpans(level);
var totalWidth = 0, totalWidthes = [];
if(mergedCells){
dojo.forEach(mergedCells, function(c){
for(var i = 0, cell;(cell = row[i]); i++){
if(i >= c.start && i <= c.end){
totalWidth += this._getCellWidth(row, i);
}
}
totalWidthes.push(totalWidth);
totalWidth = 0;
}, this);
}
for(var i = 0, cell, m, cc, cs; (cell = row[i]); i++){
m = cell.markup;
cc = cell.customClasses = [];
cs = cell.customStyles = [];
if(mergedCells && mergedCells[k] && (i >= mergedCells[k].start && i <= mergedCells[k].end)){
var primaryIdx = mergedCells[k].primary ? mergedCells[k].primary : mergedCells[k].start;
if(i === primaryIdx){
m[5] = cell.formatAtLevel(rowStack, item, level, false, toggleClasses[0], cc, inRowIndex);
// classes
m[1] = cc.join(' ');
// styles
var pbm = dojo.marginBox(cell.getHeaderNode()).w - dojo.contentBox(cell.getHeaderNode()).w;
cs = cell.customStyles = ['width:' + (totalWidthes[k] - pbm) + "px"];
m[3] = cs.join(';');
html.push.apply(html, m);
}else if(i === mergedCells[k].end){
k++;
continue;
}else{
continue;
}
}else{
// content (format can fill in cc and cs as side-effects)
// m[5] = cell.format(inRowIndex, item);
m[5] = cell.formatAtLevel(rowStack, item, level, false, toggleClasses[0], cc, inRowIndex);
// classes
m[1] = cc.join(' ');
// styles
m[3] = cs.join(';');
// in-place concat
html.push.apply(html, m);
}
}
html.push('
');
}
html.push('');
return html.join(''); // String
},
_getColSpans: function(level){
// summary:
// handle the column span object
var colSpans = this.grid.colSpans;
if(colSpans && (colSpans[level])){
return colSpans[level];
}else{
return null;
}
},
_getCellWidth: function(cells, colIndex){
// summary:
// calculate column width by header cell's size
var curCell = cells[colIndex], node = curCell.getHeaderNode();
if(curCell.hidden){
return 0;
}
if(colIndex == cells.length - 1 || dojo.every(cells.slice(colIndex + 1), function(cell){
return cell.hidden;
})){
var headerNodePos = dojo.position(cells[colIndex].view.headerContentNode.firstChild);
return headerNodePos.x + headerNodePos.w - dojo.position(node).x;
}else{
var nextCell;
do{
nextCell = cells[++colIndex];
}while(nextCell.hidden);
return dojo.position(nextCell.getHeaderNode()).x - dojo.position(node).x;
}
}
});
dojo.declare("dojox.grid._TreeGridView", [dojox.grid._View], {
_contentBuilderClass: dojox.grid._TreeGridContentBuilder,
postCreate: function(){
this.inherited(arguments);
this._expandos = {};
this.connect(this.grid, '_cleanupExpandoCache', '_cleanupExpandoCache');
},
_cleanupExpandoCache: function(index, identity, item){
if(index === -1){
return;
}
dojo.forEach(this.grid.layout.cells, function(cell){
if(cell.openStates && cell.openStates[identity]){
delete cell.openStates[identity];
}
});
for(var i in this._expandos){
if(this._expandos[i]){
this._expandos[i].destroy();
}
}
this._expandos = {};
},
onAfterRow: function(inRowIndex, cells, inRowNode){
// summary:
// parse the expando of each row to a widget
dojo.query("span.dojoxGridExpando", inRowNode).forEach(function(n){
if(n && n.parentNode){
// Either create our expando or put the existing expando back
// into place
var idty, expando, _byIdx = this.grid._by_idx;
if(_byIdx && _byIdx[inRowIndex] && _byIdx[inRowIndex].idty){
idty = _byIdx[inRowIndex].idty;
expando = this._expandos[idty];
}
if(expando){
dojo.place(expando.domNode, n, "replace");
expando.itemId = n.getAttribute("itemId");
expando.cellIdx = parseInt(n.getAttribute("cellIdx"), 10);
if(isNaN(expando.cellIdx)){
expando.cellIdx = -1;
}
}else{
expando = dojo.parser.parse(n.parentNode)[0];
if(idty){
this._expandos[idty] = expando;
}
}
if(!expando.setRowNode(inRowIndex, inRowNode, this)){
expando.domNode.parentNode.removeChild(expando.domNode);
}
dojo.destroy(n);
}
}, this);
this.inherited(arguments);
}
});
dojox.grid.cells.LazyTreeCell = dojo.mixin(dojo.clone(dojox.grid.cells.TreeCell), {
formatAtLevel: function(inRowIndexes, inItem, level, summaryRow, toggleClass, cellClasses, inRowIndex){
if(!inItem){
return this.formatIndexes(inRowIndex, inRowIndexes, inItem, level);
}
if(!dojo.isArray(inRowIndexes)){
inRowIndexes = [inRowIndexes];
}
var result = "";
var ret = "";
if(this.isCollapsable){
var store = this.grid.store, id = "";
if(inItem && store.isItem(inItem)){
id = store.getIdentity(inItem);
}
cellClasses.push("dojoxGridExpandoCell");
ret = '';
}
var content = this.formatIndexes(inRowIndex, inRowIndexes, inItem, level);
result = ret !== "" ? '' + ret + content + '
' : content;
if(this.grid.focus.cell && this.index === this.grid.focus.cell.index && inRowIndexes.join('/') === this.grid.focus.rowIndex){
cellClasses.push(this.grid.focus.focusClass);
}
return result;
},
formatIndexes: function(inRowIndex, inRowIndexes, inItem, level){
var info = this.grid.edit.info,
d = this.get ? this.get(inRowIndexes[0], inItem, inRowIndexes) : (this.value || this.defaultValue);
if(this.editable && (this.alwaysEditing || (info.rowIndex === inRowIndexes[0] && info.cell === this))){
return this.formatEditing(d, inRowIndex, inRowIndexes);
}else{
return this._defaultFormat(d, [d, inRowIndex, level, this]);
}
}
});
dojo.declare("dojox.grid._LazyTreeLayout", dojox.grid._Layout, {
// summary:
// Override the dojox.grid._TreeLayout to modify the _TreeGridView and cell formatter
setStructure: function(inStructure){
var s = inStructure;
var g = this.grid;
if(g && !dojo.every(s, function(i){
return ("cells" in i);
})){
s = arguments[0] = [{cells:[s]}];//intentionally change arguments[0]
}
if(s.length === 1 && s[0].cells.length === 1){
s[0].type = "dojox.grid._TreeGridView";
this._isCollapsable = true;
s[0].cells[0][this.grid.expandoCell].isCollapsable = true;
}
this.inherited(arguments);
},
addCellDef: function(inRowIndex, inCellIndex, inDef){
var obj = this.inherited(arguments);
return dojo.mixin(obj, dojox.grid.cells.LazyTreeCell);
}
});
dojo.declare("dojox.grid.TreeGridItemCache", null, {
unInit: true,
items: null,
constructor: function(grid){
this.rowsPerPage = grid.rowsPerPage;
this._buildCache(grid.rowsPerPage);
},
_buildCache: function(size){
// Summary
// Build the cache only with the treepath using given size
this.items = [];
for(var i = 0; i < size; i++){
this.cacheItem(i, {item: null, treePath: i + "", expandoStatus: false});
}
},
cacheItem: function(/*integer*/rowIdx, cacheObj){
// Summary
// Add an item and its tree structure information to the cache.
this.items[rowIdx] = dojo.mixin({
item: null,
treePath: "",
expandoStatus: false
}, cacheObj);
},
insertItem: function(/*integer*/rowIdx, cacheObj){
this.items.splice(rowIdx, 0, dojo.mixin({
item: null,
treePath: "",
expandoStatus: false
}, cacheObj));
},
initCache: function(size){
if(!this.unInit){ return; }
this._buildCache(size);
this.unInit = false;
},
getItemByRowIndex: function(/*integer*/rowIdx){
return this.items[rowIdx] ? this.items[rowIdx].item : null;
},
getItemByTreePath: function(treePath){
for(var i = 0, len = this.items.length; i < len; i++){
if(this.items[i].treePath === treePath){
return this.items[i].item;
}
}
return null;
},
getTreePathByRowIndex: function(/*integer*/rowIdx){
return this.items[rowIdx] ? this.items[rowIdx].treePath : null;
},
getExpandoStatusByRowIndex: function(/*integer*/rowIdx){
return this.items[rowIdx] ? this.items[rowIdx].expandoStatus : null;
},
getInfoByItem: function(item){
for(var i = 0, len = this.items.length; i < len; i++){
if(this.items[i].item === item){
return dojo.mixin({rowIdx: i}, this.items[i]);
}
}
return null;
},
updateCache: function(/*integer*/rowIdx, cacheObj){
if(this.items[rowIdx]){
dojo.mixin(this.items[rowIdx], cacheObj);
}
},
deleteItem: function(rowIdx){
if(this.items[rowIdx]){
var treePath = this.items[rowIdx].treePath,
i = rowIdx, indexes,
parentTreePath = treePath.indexOf('/') > 0 ? treePath.substring(0, treePath.lastIndexOf("/") + 1) : "";
for(; i < this.items.length; i++){
if(this.items[i].treePath.indexOf(parentTreePath + '/') == 0){
indexes = this.items[i].treePath.substring(parentTreePath.length).split('/');
indexes[0] = parseInt(indexes[0], 10) - 1;
this.updateCache(i, {treePath: parentTreePath + indexes.join('/')});
}else{
break;
}
}
this.items.splice(rowIdx, 1);
}
},
cleanChildren: function(rowIdx){
var treePath = this.getTreePathByRowIndex(rowIdx);
var childNum = 0, i = this.items.length - 1;
for(; i >= 0; i--){
if(this.items[i].treePath.indexOf(treePath + '/') === 0 && this.items[i].treePath !== treePath){
this.items.splice(i, 1);
childNum++;
}
}
return childNum;
},
emptyCache: function(){
this.unInit = true;
this._buildCache(this.rowsPerPage);
},
cleanupCache: function(){
this.items = null;
}
});
dojo.declare("dojox.grid.LazyTreeGrid", dojox.grid.TreeGrid, {
// summary:
// An enhanced TreeGrid widget which supports lazy-loading nested-level items
//
// description:
// LazyTreeGrid inherits from dojo.grid.TreeGrid, and applies virtual scrolling mechanism
// to nested children rows so that it's possible to deal with large data set specifically
// in tree structure with large number of children rows. It's also compatible with dijit.tree.ForestStoreModel
//
// Most methods and properties pertaining to the dojox.grid.DataGrid
// and dojox.grid.TreeGrid also apply here
//
// LazyTreeGrid does not support summary row/items aggregate for the
// lazy-loading reason.
treeModel: null,
_layoutClass: dojox.grid._LazyTreeLayout,
// colSpans: Object
// a json object that defines column span of each level rows
// attributes:
// 0/1/..: which level need to colspan
// start: start column index of colspan
// end: end column index of colspan
// primary: index of column which content will be displayed (default is value of start).
// example:
// | colSpans = {
// | 0: [
// | {start: 0, end: 1, primary: 0},
// | {start: 2, end: 4, primary: 3}
// | ],
// | 1: [
// | {start: 0, end: 3, primary: 1}
// | ]
// | };
colSpans: null,
postCreate: function(){
this.inherited(arguments);
this.cache = new dojox.grid.TreeGridItemCache(this);
if(!this.treeModel || !(this.treeModel instanceof dijit.tree.ForestStoreModel)){
throw new Error("dojox.grid.LazyTreeGrid: must use a treeModel and treeModel must be an instance of dijit.tree.ForestStoreModel");
}
dojo.addClass(this.domNode, "dojoxGridTreeModel");
dojo.setSelectable(this.domNode, this.selectable);
},
createManagers: function(){
// summary:
// create grid managers for various tasks including rows, focus, selection, editing
this.rows = new dojox.grid._RowManager(this);
this.focus = new dojox.grid._FocusManager(this);
this.edit = new dojox.grid._EditManager(this);
},
createSelection: function(){
this.selection = new dojox.grid.DataSelection(this);
},
setModel: function(treeModel){
if(!treeModel){
return;
}
this._setModel(treeModel);
this._cleanup();
this._refresh(true);
},
setStore: function(store, query, queryOptions){
if(!store){
return;
}
this._setQuery(query, queryOptions);
this.treeModel.query = query;
this.treeModel.store = store;
this.treeModel.root.children = [];
this.setModel(this.treeModel);
},
_setQuery: function(query, queryOptions){
this.inherited(arguments);
this.treeModel.query = query;
},
destroy: function(){
this._cleanup();
this.inherited(arguments);
},
_cleanup: function(){
this.cache.emptyCache();
this._cleanupExpandoCache();
},
setSortIndex: function(inIndex, inAsc){
// Need to clean up the cache before sorting
if(this.canSort(inIndex + 1)){
this._cleanup();
}
this.inherited(arguments);
},
_refresh: function(isRender){
this._clearData();
this.updateRowCount(this.cache.items.length);
this._fetch(0, true);
},
_updateChangedRows: function(start){
dojo.forEach(this.scroller.stack, function(p){
if(p * this.rowsPerPage >= start){
this.updateRows(p * this.rowsPerPage, this.rowsPerPage);
}else if((p + 1) * this.rowsPerPage >= start){
this.updateRows(start, (p + 1) * this.rowsPerPage - start + 1);
}
}, this);
},
render: function(){
this.inherited(arguments);
this.setScrollTop(this.scrollTop);
},
_onNew: function(item, parentInfo){
var isAddingChild = false,
info,
items = this.cache.items;
if(parentInfo && this.store.isItem(parentInfo.item) && dojo.some(this.treeModel.childrenAttrs, function(c){
return c === parentInfo.attribute;
})){
isAddingChild = true;
info = this.cache.getInfoByItem(parentInfo.item);
}
if(!isAddingChild){
this.inherited(arguments);
var treePath = items.length > 0 ? String(parseInt(items[items.length - 1].treePath.split("/")[0], 10) + 1) : "0";
this.cache.insertItem(this.get('rowCount'), {item: item, treePath: treePath, expandoStatus: false});
}else if(info && info.expandoStatus && info.rowIdx >= 0){
// update cache
var childrenNum = info.childrenNum;
var childTreePath = info.treePath + "/" + childrenNum;
var childItem = {item: item, treePath: childTreePath, expandoStatus: false};
var index = info.rowIdx + 1;
for(; index < this.cache.items.length; index++){
if(!this.cache.items[index] || this.cache.items[index].treePath.indexOf(info.treePath + "/") != 0){
break;
}
}
this.cache.insertItem(index, childItem);
this.cache.updateCache(info.rowIdx, {childrenNum: childrenNum + 1});
// update grid._by_idx
var idty = this.store.getIdentity(item);
this._by_idty[idty] = { idty: idty, item: item };
this._by_idx.splice(index, 0, this._by_idty[idty]);
// update grid
this.updateRowCount(items.length);
this._updateChangedRows(index);
}else if(info && info.rowIdx >= 0){
this.updateRow(info.rowIdx);
}
},
_onDelete: function(item){
var info = this.cache.getInfoByItem(item), i;
if(info && info.rowIdx >= 0){
if(info.expandoStatus){
var num = this.cache.cleanChildren(info.rowIdx);
this._by_idx.splice(info.rowIdx + 1, num);
}
if(info.treePath.indexOf("/") > 0){
var parentTreePath = info.treePath.substring(0, info.treePath.lastIndexOf("/"));
for(i = info.rowIdx; i >=0; i--){
if(this.cache.items[i].treePath === parentTreePath){
this.cache.items[i].childrenNum--;
break;
}
}
}
this.cache.deleteItem(info.rowIdx);
this._by_idx.splice(info.rowIdx, 1);
this.updateRowCount(this.cache.items.length);
this._updateChangedRows(info.rowIdx);
}
},
_cleanupExpandoCache: function(index, identity, item){},
_fetch: function(start, isRender){
// summary:
// Function for fetch data when initializing TreeGrid and
// scroll the TreeGrid
if(!this._loading){
this._loading = true;
}
start = start || 0;
this.reqQueue = [];
// Check cache, do not need to fetch data if there are required data in cache
var i = 0, fetchedItems = [];
var count = Math.min(this.rowsPerPage, this.cache.items.length - start);
for(i = start; i < start + count; i++){
if(this.cache.getItemByRowIndex(i)){
fetchedItems.push(this.cache.getItemByRowIndex(i));
}else{
break;
}
}
if(fetchedItems.length === count){// || !this.cache.getTreePathByRowIndex(start + fetchedItems.length)){
this._reqQueueLen = 1;
this._onFetchBegin(this.cache.items.length, {startRowIdx: start, count: count});
this._onFetchComplete(fetchedItems, {startRowIdx: start, count: count});
}else{
// In case there need different level data, we need to do multiple fetch.
// Do next fetch only when the last request complete.
this.reqQueueIndex = 0;
var level = "",
nextRowLevel = "",
startRowIdx = start,
startTreePath = this.cache.getTreePathByRowIndex(start);
count = 0;
// Create request queue
for(i = start + 1; i < start + this.rowsPerPage; i++){
if(!this.cache.getTreePathByRowIndex(i)){
break;
}
level = this.cache.getTreePathByRowIndex(i - 1).split("/").length - 1;
nextRowLevel = this.cache.getTreePathByRowIndex(i).split("/").length - 1;
if(level !== nextRowLevel){
this.reqQueue.push({
startTreePath: startTreePath,
startRowIdx: startRowIdx,
count: count + 1
});
count = 0;
startRowIdx = i;
startTreePath = this.cache.getTreePathByRowIndex(i);
}else{
count++;
}
}
this.reqQueue.push({
startTreePath: startTreePath,
startRowIdx: startRowIdx,
count: count + 1
});
this._reqQueueLen = this.reqQueue.length;
for(i = 0; i < this.reqQueue.length; i++){
this._fetchItems(i, dojo.hitch(this, "_onFetchBegin"), dojo.hitch(this, "_onFetchComplete"), dojo.hitch(this, "_onFetchError"));
}
}
},
_fetchItems: function(idx, onBegin, onComplete, onError){
if(this._pending_requests[this.reqQueue[idx].startRowIdx]){
return;
}
this.showMessage(this.loadingMessage);
var level = this.reqQueue[idx].startTreePath.split("/").length - 1;
this._pending_requests[this.reqQueue[idx].startRowIdx] = true;
if(level === 0){
this.store.fetch({
start: parseInt(this.reqQueue[idx].startTreePath, 10),
startRowIdx: this.reqQueue[idx].startRowIdx,
count: this.reqQueue[idx].count,
query: this.query,
sort: this.getSortProps(),
queryOptions: this.queryOptions,
onBegin: onBegin,
onComplete: onComplete,
onError: onError
});
}else{
var startTreePath = this.reqQueue[idx].startTreePath;
var parentTreePath = startTreePath.substring(0, startTreePath.lastIndexOf("/"));
var startIdx = startTreePath.substring(startTreePath.lastIndexOf("/") + 1);
var parentItem = this.cache.getItemByTreePath(parentTreePath);
if(!parentItem){
throw new Error("Lazy loading TreeGrid on fetch error:");
}
var parentId = this.store.getIdentity(parentItem);
var queryObj = {
start: parseInt(startIdx, 10),
startRowIdx: this.reqQueue[idx].startRowIdx,
count: this.reqQueue[idx].count,
parentId: parentId,
sort: this.getSortProps()
};
var _this = this;
var onFetchComplete = function(){
if(arguments.length == 1){
onComplete.apply(_this, [arguments[0], queryObj]);
}else{
onComplete.apply(_this, arguments);
}
};
this.treeModel.getChildren(parentItem, onFetchComplete, onError, queryObj);
}
},
_onFetchBegin: function(size, request){
this.cache.initCache(size);
size = this.cache.items.length;
this.inherited(arguments);
},
filter: function(query, reRender){
this.cache.emptyCache();
this.inherited(arguments);
},
_onFetchComplete: function(items, request, size){
var treePath = "",
startRowIdx = request.startRowIdx,
count = request.count,
start = items.length <= count ? 0: request.start;
if(items && items.length > 0){
for(var i = 0; i < count; i++){
treePath = this.cache.getTreePathByRowIndex(startRowIdx + i);
if(treePath){
if(!this.cache.getItemByRowIndex(startRowIdx + i)){
this.cache.cacheItem(startRowIdx + i, {
item: items[start + i],
treePath: treePath,
expandoStatus: this.cache.getExpandoStatusByRowIndex(startRowIdx + i)
});
}
}
}
// Add items when all request complete
if(!this.scroller){
return;
}
var len = Math.min(count, items.length);
for(i = 0; i < len; i++){
this._addItem(items[start + i], startRowIdx + i, true);
}
this.updateRows(startRowIdx, len);
}
if(!this.cache.items.length){
this.showMessage(this.noDataMessage);
}else{
this.showMessage();
}
this._pending_requests[startRowIdx] = false;
this._reqQueueLen--;
if(this._loading && this._reqQueueLen === 0){
this._loading = false;
if(this._lastScrollTop){
this.setScrollTop(this._lastScrollTop);
}
}
},
expandoFetch: function(rowIndex, open){
// summary:
// Function for fetch children of a given row
if(this._loading){return;}
this._loading = true;
this.toggleLoadingClass(true);
var item = this.cache.getItemByRowIndex(rowIndex);
this.expandoRowIndex = rowIndex;
this._pages = [];
if(open){
var parentId = this.store.getIdentity(item);
var queryObj = {
start: 0,
count: this.rowsPerPage,
parentId: parentId,
sort: this.getSortProps()
};
this.treeModel.getChildren(item, dojo.hitch(this, "_onExpandoComplete"), dojo.hitch(this, "_onFetchError"), queryObj);
}else{
// get the whole children number when clear the children from cache
var num = this.cache.cleanChildren(rowIndex);
// remove the items from grid._by_idx
this._by_idx.splice(rowIndex + 1, num);
this._bop = this._eop = -1;
//update grid
this.updateRowCount(this.cache.items.length);
this._updateChangedRows(rowIndex + 1);
this.toggleLoadingClass(false);
if(this._loading){
this._loading = false;
}
this.focus._delayedCellFocus();
}
},
_onExpandoComplete: function(childItems, request, size){
var parentTreePath = this.cache.getTreePathByRowIndex(this.expandoRowIndex);
if(size && !isNaN(parseInt(size, 10))){
size = parseInt(size, 10);
}else{
size = childItems.length;
}
var i, j = 0, len = this._by_idx.length;
for(i = this.expandoRowIndex + 1; j < size; i++, j++){
this.cache.insertItem(i, {
item: null,
treePath: parentTreePath + "/" + j,
expandoStatus: false
});
}
this.updateRowCount(this.cache.items.length);
this.cache.updateCache(this.expandoRowIndex, {childrenNum: size});
for(i = 0; i < size; i++){
this.cache.updateCache(this.expandoRowIndex + 1 + i, {item: childItems[i]});
}
// insert NULL to grid._by_idx
for(i = 0; i < size; i++){
this._by_idx.splice(this.expandoRowIndex + 1 + i, 0, null);
}
// insert the fetched items to grid._by_idx
for(i = 0; i < Math.min(size, this.rowsPerPage); i++){
var idty = this.store.getIdentity(childItems[i]);
this._by_idty[idty] = { idty: idty, item: childItems[i] };
this._by_idx.splice(this.expandoRowIndex + 1 + i, 1, this._by_idty[idty]);
}
this._updateChangedRows(this.expandoRowIndex + 1);
this.toggleLoadingClass(false);
this.stateChangeNode = null;
if(this._loading){
this._loading = false;
}
// correct focus
this.focus._delayedCellFocus();
},
toggleLoadingClass: function(flag){
// summary:
// set loading class when expanding/collapsing
if(this.stateChangeNode){
dojo.toggleClass(this.stateChangeNode, "dojoxGridExpandoLoading", flag);
}
},
styleRowNode: function(inRowIndex, inRowNode){
if(inRowNode){
this.rows.styleRowNode(inRowIndex, inRowNode);
}
},
onStyleRow: function(row){
if(!this.layout._isCollapsable){
this.inherited(arguments);
return;
}
var base = dojo.attr(row.node, 'dojoxTreeGridBaseClasses');
if(base){
row.customClasses = base;
}
var i = row;
i.customClasses += (i.odd ? " dojoxGridRowOdd" : "") + (i.selected ? " dojoxGridRowSelected" : "") + (i.over ? " dojoxGridRowOver" : "");
this.focus.styleRow(i);
this.edit.styleRow(i);
},
dokeydown: function(e){
if(e.altKey || e.metaKey){
return;
}
var dk = dojo.keys,
expando = dijit.findWidgets(e.target)[0];
if(e.keyCode === dk.ENTER && expando instanceof dojox.grid._LazyExpando){
expando.onToggle();
}
this.onKeyDown(e);
}
});
dojox.grid.LazyTreeGrid.markupFactory = function(props, node, ctor, cellFunc){
return dojox.grid.TreeGrid.markupFactory(props, node, ctor, cellFunc);
};
}