_TreeView.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. require({cache:{
  2. 'url:dojox/grid/resources/Expando.html':"<div class=\"dojoxGridExpando\"\n\t><div class=\"dojoxGridExpandoNode\" dojoAttachEvent=\"onclick:onToggle\"\n\t\t><div class=\"dojoxGridExpandoNodeInner\" dojoAttachPoint=\"expandoInner\"></div\n\t></div\n></div>\n"}});
  3. define("dojox/grid/_TreeView", [
  4. "dijit/registry",
  5. "../main",
  6. "dojo/_base/declare",
  7. "dojo/_base/array",
  8. "dojo/_base/lang",
  9. "dojo/_base/event",
  10. "dojo/dom-attr",
  11. "dojo/dom-class",
  12. "dojo/dom-style",
  13. "dojo/dom-construct",
  14. "dojo/query",
  15. "dojo/parser",
  16. "dojo/text!./resources/Expando.html",
  17. "dijit/_Widget",
  18. "dijit/_TemplatedMixin",
  19. "./_View",
  20. "./_Builder",
  21. "./util"
  22. ], function(dijit, dojox, declare, array, lang, event, domAttr, domClass,
  23. domStyle, domCtr, query, parser, template, _Widget, _TemplatedMixin, _View, _Builder, util){
  24. declare("dojox.grid._Expando", [ _Widget, _TemplatedMixin ], {
  25. open: false,
  26. toggleClass: "",
  27. itemId: "",
  28. cellIdx: -1,
  29. view: null,
  30. rowNode: null,
  31. rowIdx: -1,
  32. expandoCell: null,
  33. level: 0,
  34. templateString: template,
  35. _toggleRows: function(toggleClass, open){
  36. if(!toggleClass || !this.rowNode){ return; }
  37. if(query("table.dojoxGridRowTableNeedsRowUpdate").length){
  38. if(this._initialized){
  39. this.view.grid.updateRow(this.rowIdx);
  40. }
  41. return;
  42. }
  43. var self = this;
  44. var g = this.view.grid;
  45. if(g.treeModel){
  46. var p = this._tableRow ? domAttr.get(this._tableRow, "dojoxTreeGridPath") : "";
  47. if(p){
  48. query("tr[dojoxTreeGridPath^=\"" + p + "/\"]", this.rowNode).forEach(function(n){
  49. var en = query(".dojoxGridExpando", n)[0];
  50. if(en && en.parentNode && en.parentNode.parentNode &&
  51. !domClass.contains(en.parentNode.parentNode, "dojoxGridNoChildren")){
  52. var ew = dijit.byNode(en);
  53. if(ew){
  54. ew._toggleRows(toggleClass, ew.open&&open);
  55. }
  56. }
  57. n.style.display = open ? "" : "none";
  58. });
  59. }
  60. }else{
  61. query("tr." + toggleClass, this.rowNode).forEach(function(n){
  62. if(domClass.contains(n, "dojoxGridExpandoRow")){
  63. var en = query(".dojoxGridExpando", n)[0];
  64. if(en){
  65. var ew = dijit.byNode(en);
  66. var toggleClass = ew ? ew.toggleClass : en.getAttribute("toggleClass");
  67. var wOpen = ew ? ew.open : self.expandoCell.getOpenState(en.getAttribute("itemId"));
  68. self._toggleRows(toggleClass, wOpen&&open);
  69. }
  70. }
  71. n.style.display = open ? "" : "none";
  72. });
  73. }
  74. },
  75. setOpen: function(open){
  76. if(open && domClass.contains(this.domNode, "dojoxGridExpandoLoading")){
  77. open = false;
  78. }
  79. var view = this.view;
  80. var grid = view.grid;
  81. var store = grid.store;
  82. var treeModel = grid.treeModel;
  83. var d = this;
  84. var idx = this.rowIdx;
  85. var me = grid._by_idx[idx];
  86. if(!me){ return; }
  87. if(treeModel && !this._loadedChildren){
  88. if(open){
  89. // Do this to make sure our children are fully-loaded
  90. var itm = grid.getItem(domAttr.get(this._tableRow, "dojoxTreeGridPath"));
  91. if(itm){
  92. this.expandoInner.innerHTML = "o";
  93. domClass.add(this.domNode, "dojoxGridExpandoLoading");
  94. treeModel.getChildren(itm, function(items){
  95. d._loadedChildren = true;
  96. d._setOpen(open);
  97. });
  98. }else{
  99. this._setOpen(open);
  100. }
  101. }else{
  102. this._setOpen(open);
  103. }
  104. }else if(!treeModel && store){
  105. if(open){
  106. var data = grid._by_idx[this.rowIdx];
  107. if(data&&!store.isItemLoaded(data.item)){
  108. this.expandoInner.innerHTML = "o";
  109. domClass.add(this.domNode, "dojoxGridExpandoLoading");
  110. store.loadItem({
  111. item: data.item,
  112. onItem: lang.hitch(this, function(i){
  113. var idty = store.getIdentity(i);
  114. grid._by_idty[idty] = grid._by_idx[this.rowIdx] = { idty: idty, item: i };
  115. this._setOpen(open);
  116. })
  117. });
  118. }else{
  119. this._setOpen(open);
  120. }
  121. }else{
  122. this._setOpen(open);
  123. }
  124. }else{
  125. this._setOpen(open);
  126. }
  127. },
  128. _setOpen: function(open){
  129. if(open && this._tableRow && domClass.contains(this._tableRow, "dojoxGridNoChildren")){
  130. this._setOpen(false);
  131. return;
  132. }
  133. this.expandoInner.innerHTML = open ? "-" : "+";
  134. domClass.remove(this.domNode, "dojoxGridExpandoLoading");
  135. domClass.toggle(this.domNode, "dojoxGridExpandoOpened", open);
  136. if(this._tableRow){
  137. domClass.toggle(this._tableRow, "dojoxGridRowCollapsed", !open);
  138. var base = domAttr.get(this._tableRow, "dojoxTreeGridBaseClasses");
  139. var new_base = "";
  140. if(open){
  141. new_base = lang.trim((" " + base + " ").replace(" dojoxGridRowCollapsed ", " "));
  142. }else{
  143. if((" " + base + " ").indexOf(' dojoxGridRowCollapsed ') < 0){
  144. new_base = base + (base ? ' ' : '' ) + 'dojoxGridRowCollapsed';
  145. }else{
  146. new_base = base;
  147. }
  148. }
  149. domAttr.set(this._tableRow, 'dojoxTreeGridBaseClasses', new_base);
  150. }
  151. var changed = (this.open !== open);
  152. this.open = open;
  153. if(this.expandoCell && this.itemId){
  154. this.expandoCell.openStates[this.itemId] = open;
  155. }
  156. var v = this.view;
  157. var g = v.grid;
  158. if(this.toggleClass && changed){
  159. if(!this._tableRow || !this._tableRow.style.display){
  160. this._toggleRows(this.toggleClass, open);
  161. }
  162. }
  163. if(v && this._initialized && this.rowIdx >= 0){
  164. g.rowHeightChanged(this.rowIdx);
  165. g.postresize();
  166. v.hasVScrollbar(true);
  167. }
  168. this._initialized = true;
  169. },
  170. onToggle: function(e){
  171. this.setOpen(!this.open);
  172. event.stop(e);
  173. },
  174. setRowNode: function(rowIdx, rowNode, view){
  175. if(this.cellIdx < 0 || !this.itemId){ return false; }
  176. this._initialized = false;
  177. this.view = view;
  178. this.rowNode = rowNode;
  179. this.rowIdx = rowIdx;
  180. this.expandoCell = view.structure.cells[0][this.cellIdx];
  181. var d = this.domNode;
  182. if(d && d.parentNode && d.parentNode.parentNode){
  183. this._tableRow = d.parentNode.parentNode;
  184. }
  185. this.open = this.expandoCell.getOpenState(this.itemId);
  186. if(view.grid.treeModel){
  187. // TODO: Rather than hard-code the 18px and 3px, we should probably
  188. // calculate them based off css or something... However, all the
  189. // themes that we support use these values.
  190. domStyle.set(this.domNode , "marginLeft" , (this.level * 18) + "px");
  191. if(this.domNode.parentNode){
  192. domStyle.set(this.domNode.parentNode, "backgroundPosition", ((this.level * 18) + (3)) + "px");
  193. }
  194. }
  195. this.setOpen(this.open);
  196. return true;
  197. }
  198. });
  199. var _TreeContentBuilder = declare("dojox.grid._TreeContentBuilder", _Builder._ContentBuilder, {
  200. generateHtml: function(inDataIndex, inRowIndex){
  201. var
  202. html = this.getTableArray(),
  203. v = this.view,
  204. row = v.structure.cells[0],
  205. item = this.grid.getItem(inRowIndex),
  206. grid = this.grid,
  207. store = this.grid.store;
  208. util.fire(this.view, "onBeforeRow", [inRowIndex, [row]]);
  209. var createRow = function(level, rowItem, summaryRow, toggleClasses, rowStack, shown){
  210. if(!shown){
  211. if(html[0].indexOf('dojoxGridRowTableNeedsRowUpdate') == -1){
  212. html[0] = html[0].replace("dojoxGridRowTable", "dojoxGridRowTable dojoxGridRowTableNeedsRowUpdate");
  213. }
  214. return;
  215. }
  216. var rowNodeIdx = html.length;
  217. toggleClasses = toggleClasses || [];
  218. var tcJoin = toggleClasses.join('|');
  219. var tcString = toggleClasses[toggleClasses.length - 1];
  220. var clString = tcString + (summaryRow ? " dojoxGridSummaryRow" : "");
  221. var sString = "";
  222. if(grid.treeModel && rowItem && !grid.treeModel.mayHaveChildren(rowItem)){
  223. clString += " dojoxGridNoChildren";
  224. }
  225. html.push('<tr style="' + sString + '" class="' + clString + '" dojoxTreeGridPath="' + rowStack.join('/') + '" dojoxTreeGridBaseClasses="' + clString + '">');
  226. var nextLevel = level + 1;
  227. var parentCell = null;
  228. for(var i=0, cell; (cell=row[i]); i++){
  229. var m = cell.markup, cc = cell.customClasses = [], cs = cell.customStyles = [];
  230. // content (format can fill in cc and cs as side-effects)
  231. m[5] = cell.formatAtLevel(rowStack, rowItem, level, summaryRow, tcString, cc);
  232. // classes
  233. m[1] = cc.join(' ');
  234. // styles
  235. m[3] = cs.join(';');
  236. // in-place concat
  237. html.push.apply(html, m);
  238. if(!parentCell && cell.level === nextLevel && cell.parentCell){
  239. parentCell = cell.parentCell;
  240. }
  241. }
  242. html.push('</tr>');
  243. if(rowItem && store && store.isItem(rowItem)){
  244. var idty = store.getIdentity(rowItem);
  245. if(typeof grid._by_idty_paths[idty] == "undefined"){
  246. grid._by_idty_paths[idty] = rowStack.join('/');
  247. }
  248. }
  249. var expandoCell;
  250. var parentOpen;
  251. var path;
  252. var values;
  253. var iStack = rowStack.concat([]);
  254. if(grid.treeModel && rowItem){
  255. if(grid.treeModel.mayHaveChildren(rowItem)){
  256. expandoCell = v.structure.cells[0][grid.expandoCell||0];
  257. parentOpen = expandoCell.getOpenState(rowItem) && shown;
  258. path = new dojox.grid.TreePath(rowStack.join('/'), grid);
  259. values = path.children(true)||[];
  260. array.forEach(values, function(cItm, idx){
  261. var nToggle = tcJoin.split('|');
  262. nToggle.push(nToggle[nToggle.length - 1] + "-" + idx);
  263. iStack.push(idx);
  264. createRow(nextLevel, cItm, false, nToggle, iStack, parentOpen);
  265. iStack.pop();
  266. });
  267. }
  268. }else if(rowItem && parentCell && !summaryRow){
  269. expandoCell = v.structure.cells[0][parentCell.level];
  270. parentOpen = expandoCell.getOpenState(rowItem) && shown;
  271. if(store.hasAttribute(rowItem, parentCell.field)){
  272. var tToggle = tcJoin.split('|');
  273. tToggle.pop();
  274. path = new dojox.grid.TreePath(rowStack.join('/'), grid);
  275. values = path.children(true)||[];
  276. if(values.length){
  277. html[rowNodeIdx] = '<tr class="' + tToggle.join(' ') +' dojoxGridExpandoRow" dojoxTreeGridPath="' + rowStack.join('/') + '">';
  278. array.forEach(values, function(cItm, idx){
  279. var nToggle = tcJoin.split('|');
  280. nToggle.push(nToggle[nToggle.length - 1] + "-" + idx);
  281. iStack.push(idx);
  282. createRow(nextLevel, cItm, false, nToggle, iStack, parentOpen);
  283. iStack.pop();
  284. });
  285. iStack.push(values.length);
  286. createRow(level, rowItem, true, toggleClasses, iStack, parentOpen);
  287. }else{
  288. html[rowNodeIdx] = '<tr class="' + tcString + ' dojoxGridNoChildren" dojoxTreeGridPath="' + rowStack.join('/') + '">';
  289. }
  290. }else{
  291. if(!store.isItemLoaded(rowItem)){
  292. html[0] = html[0].replace("dojoxGridRowTable", "dojoxGridRowTable dojoxGridRowTableNeedsRowUpdate");
  293. }else{
  294. html[rowNodeIdx] = '<tr class="' + tcString + ' dojoxGridNoChildren" dojoxTreeGridPath="' + rowStack.join('/') + '">';
  295. }
  296. }
  297. }else if(rowItem && !summaryRow && toggleClasses.length > 1){
  298. html[rowNodeIdx] = '<tr class="' + toggleClasses[toggleClasses.length - 2] + '" dojoxTreeGridPath="' + rowStack.join('/') + '">';
  299. }
  300. };
  301. createRow(0, item, false, ["dojoxGridRowToggle-" + inRowIndex], [inRowIndex], true);
  302. html.push('</table>');
  303. return html.join(''); // String
  304. },
  305. findTarget: function(inSource, inTag){
  306. var n = inSource;
  307. while(n && (n!=this.domNode)){
  308. if(n.tagName && n.tagName.toLowerCase() == 'tr'){
  309. break;
  310. }
  311. n = n.parentNode;
  312. }
  313. return (n != this.domNode) ? n : null;
  314. },
  315. getCellNode: function(inRowNode, inCellIndex){
  316. var node = query("td[idx='" + inCellIndex + "']", inRowNode)[0];
  317. if(node&&node.parentNode&&!domClass.contains(node.parentNode, "dojoxGridSummaryRow")){
  318. return node;
  319. }
  320. },
  321. decorateEvent: function(e){
  322. e.rowNode = this.findRowTarget(e.target);
  323. if(!e.rowNode){return false;}
  324. e.rowIndex = domAttr.get(e.rowNode, 'dojoxTreeGridPath');
  325. this.baseDecorateEvent(e);
  326. e.cell = this.grid.getCell(e.cellIndex);
  327. return true; // Boolean
  328. }
  329. });
  330. return declare("dojox.grid._TreeView", _View, {
  331. _contentBuilderClass: _TreeContentBuilder,
  332. _onDndDrop: function(source, nodes, copy){
  333. if(this.grid && this.grid.aggregator){
  334. this.grid.aggregator.clearSubtotalCache();
  335. }
  336. this.inherited(arguments);
  337. },
  338. postCreate: function(){
  339. this.inherited(arguments);
  340. this.connect(this.grid, '_cleanupExpandoCache', '_cleanupExpandoCache');
  341. },
  342. _cleanupExpandoCache: function(index, identity, item){
  343. if(index == -1){
  344. return;
  345. }
  346. array.forEach(this.grid.layout.cells, function(cell){
  347. if(typeof cell['openStates'] != 'undefined'){
  348. if(identity in cell.openStates){
  349. delete cell.openStates[identity];
  350. }
  351. }
  352. });
  353. if(typeof index == "string" && index.indexOf('/') > -1){
  354. var path = new dojox.grid.TreePath(index, this.grid);
  355. var ppath = path.parent();
  356. while(ppath){
  357. path = ppath;
  358. ppath = path.parent();
  359. }
  360. var pitem = path.item();
  361. if(!pitem){
  362. return;
  363. }
  364. var idty = this.grid.store.getIdentity(pitem);
  365. if(typeof this._expandos[idty] != 'undefined'){
  366. for(var i in this._expandos[idty]){
  367. var exp = this._expandos[idty][i];
  368. if(exp){
  369. exp.destroy();
  370. }
  371. delete this._expandos[idty][i];
  372. }
  373. delete this._expandos[idty];
  374. }
  375. }else{
  376. for(var i in this._expandos){
  377. if(typeof this._expandos[i] != 'undefined'){
  378. for(var j in this._expandos[i]){
  379. var exp = this._expandos[i][j];
  380. if(exp){
  381. exp.destroy();
  382. }
  383. }
  384. }
  385. }
  386. this._expandos = {};
  387. }
  388. },
  389. postMixInProperties: function(){
  390. this.inherited(arguments);
  391. this._expandos = {};
  392. },
  393. onBeforeRow: function(inRowIndex, cells){
  394. // Save off our expando if we have one so we don't have to create it
  395. // again
  396. var g = this.grid;
  397. if(g._by_idx && g._by_idx[inRowIndex] && g._by_idx[inRowIndex].idty){
  398. var idty = g._by_idx[inRowIndex].idty;
  399. this._expandos[idty] = this._expandos[idty] || {};
  400. }
  401. this.inherited(arguments);
  402. },
  403. onAfterRow: function(inRowIndex, cells, inRowNode){
  404. array.forEach(query("span.dojoxGridExpando", inRowNode), function(n){
  405. if(n && n.parentNode){
  406. // Either create our expando or put the existing expando back
  407. // into place
  408. var tc = n.getAttribute("toggleClass");
  409. var idty;
  410. var expando;
  411. var g = this.grid;
  412. if(g._by_idx && g._by_idx[inRowIndex] && g._by_idx[inRowIndex].idty){
  413. idty = g._by_idx[inRowIndex].idty;
  414. expando = this._expandos[idty][tc];
  415. }
  416. if(expando){
  417. domCtr.place(expando.domNode, n, "replace");
  418. expando.itemId = n.getAttribute("itemId");
  419. expando.cellIdx = parseInt(n.getAttribute("cellIdx"), 10);
  420. if(isNaN(expando.cellIdx)){
  421. expando.cellIdx = -1;
  422. }
  423. }else{
  424. if(idty){
  425. expando = parser.parse(n.parentNode)[0];
  426. this._expandos[idty][tc] = expando;
  427. }
  428. }
  429. if(expando && !expando.setRowNode(inRowIndex, inRowNode, this)){
  430. expando.domNode.parentNode.removeChild(expando.domNode);
  431. }
  432. }
  433. }, this);
  434. var alt = false;
  435. var self = this;
  436. query("tr[dojoxTreeGridPath]", inRowNode).forEach(function(n){
  437. domClass.toggle(n, "dojoxGridSubRowAlt", alt);
  438. domAttr.set(n, "dojoxTreeGridBaseClasses", n.className);
  439. alt = !alt;
  440. self.grid.rows.styleRowNode(domAttr.get(n, 'dojoxTreeGridPath'), n);
  441. });
  442. this.inherited(arguments);
  443. },
  444. updateRowStyles: function(inRowIndex){
  445. var rowNodes = query("tr[dojoxTreeGridPath='" + inRowIndex + "']", this.domNode);
  446. if(rowNodes.length){
  447. this.styleRowNode(inRowIndex, rowNodes[0]);
  448. }
  449. },
  450. getCellNode: function(inRowIndex, inCellIndex){
  451. var row = query("tr[dojoxTreeGridPath='" + inRowIndex + "']", this.domNode)[0];
  452. if(row){
  453. return this.content.getCellNode(row, inCellIndex);
  454. }
  455. },
  456. destroy: function(){
  457. this._cleanupExpandoCache();
  458. this.inherited(arguments);
  459. }
  460. });
  461. });