_View.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.grid._View"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.grid._View"] = true;
  8. dojo.provide("dojox.grid._View");
  9. dojo.require("dijit._Widget");
  10. dojo.require("dijit._Templated");
  11. dojo.require("dojox.grid._Builder");
  12. dojo.require("dojox.html.metrics");
  13. dojo.require("dojox.grid.util");
  14. dojo.require("dojo.dnd.Source");
  15. dojo.require("dojo.dnd.Manager");
  16. (function(){
  17. // a private function
  18. var getStyleText = function(inNode, inStyleText){
  19. return inNode.style.cssText == undefined ? inNode.getAttribute("style") : inNode.style.cssText;
  20. };
  21. // some public functions
  22. dojo.declare('dojox.grid._View', [dijit._Widget, dijit._Templated], {
  23. // summary:
  24. // A collection of grid columns. A grid is comprised of a set of views that stack horizontally.
  25. // Grid creates views automatically based on grid's layout structure.
  26. // Users should typically not need to access individual views directly.
  27. //
  28. // defaultWidth: String
  29. // Default width of the view
  30. defaultWidth: "18em",
  31. // viewWidth: String
  32. // Width for the view, in valid css unit
  33. viewWidth: "",
  34. templateString:"<div class=\"dojoxGridView\" role=\"presentation\">\n\t<div class=\"dojoxGridHeader\" dojoAttachPoint=\"headerNode\" role=\"presentation\">\n\t\t<div dojoAttachPoint=\"headerNodeContainer\" style=\"width:9000em\" role=\"presentation\">\n\t\t\t<div dojoAttachPoint=\"headerContentNode\" role=\"row\"></div>\n\t\t</div>\n\t</div>\n\t<input type=\"checkbox\" class=\"dojoxGridHiddenFocus\" dojoAttachPoint=\"hiddenFocusNode\" role=\"presentation\" />\n\t<input type=\"checkbox\" class=\"dojoxGridHiddenFocus\" role=\"presentation\" />\n\t<div class=\"dojoxGridScrollbox\" dojoAttachPoint=\"scrollboxNode\" role=\"presentation\">\n\t\t<div class=\"dojoxGridContent\" dojoAttachPoint=\"contentNode\" hidefocus=\"hidefocus\" role=\"presentation\"></div>\n\t</div>\n</div>\n",
  35. themeable: false,
  36. classTag: 'dojoxGrid',
  37. marginBottom: 0,
  38. rowPad: 2,
  39. // _togglingColumn: int
  40. // Width of the column being toggled (-1 for none)
  41. _togglingColumn: -1,
  42. // _headerBuilderClass: Object
  43. // The class to use for our header builder
  44. _headerBuilderClass: dojox.grid._HeaderBuilder,
  45. // _contentBuilderClass: Object
  46. // The class to use for our content builder
  47. _contentBuilderClass: dojox.grid._ContentBuilder,
  48. postMixInProperties: function(){
  49. this.rowNodes = {};
  50. },
  51. postCreate: function(){
  52. this.connect(this.scrollboxNode,"onscroll","doscroll");
  53. dojox.grid.util.funnelEvents(this.contentNode, this, "doContentEvent", [ 'mouseover', 'mouseout', 'click', 'dblclick', 'contextmenu', 'mousedown' ]);
  54. dojox.grid.util.funnelEvents(this.headerNode, this, "doHeaderEvent", [ 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'click', 'contextmenu' ]);
  55. this.content = new this._contentBuilderClass(this);
  56. this.header = new this._headerBuilderClass(this);
  57. //BiDi: in RTL case, style width='9000em' causes scrolling problem in head node
  58. if(!dojo._isBodyLtr()){
  59. this.headerNodeContainer.style.width = "";
  60. }
  61. },
  62. destroy: function(){
  63. dojo.destroy(this.headerNode);
  64. delete this.headerNode;
  65. for(var i in this.rowNodes){
  66. this._cleanupRowWidgets(this.rowNodes[i]);
  67. dojo.destroy(this.rowNodes[i]);
  68. }
  69. this.rowNodes = {};
  70. if(this.source){
  71. this.source.destroy();
  72. }
  73. this.inherited(arguments);
  74. },
  75. // focus
  76. focus: function(){
  77. if(dojo.isIE || dojo.isWebKit || dojo.isOpera){
  78. this.hiddenFocusNode.focus();
  79. }else{
  80. this.scrollboxNode.focus();
  81. }
  82. },
  83. setStructure: function(inStructure){
  84. var vs = (this.structure = inStructure);
  85. // FIXME: similar logic is duplicated in layout
  86. if(vs.width && !isNaN(vs.width)){
  87. this.viewWidth = vs.width + 'em';
  88. }else{
  89. this.viewWidth = vs.width || (vs.noscroll ? 'auto' : this.viewWidth); //|| this.defaultWidth;
  90. }
  91. this._onBeforeRow = vs.onBeforeRow||function(){};
  92. this._onAfterRow = vs.onAfterRow||function(){};
  93. this.noscroll = vs.noscroll;
  94. if(this.noscroll){
  95. this.scrollboxNode.style.overflow = "hidden";
  96. }
  97. this.simpleStructure = Boolean(vs.cells.length == 1);
  98. // bookkeeping
  99. this.testFlexCells();
  100. // accomodate new structure
  101. this.updateStructure();
  102. },
  103. _cleanupRowWidgets: function(inRowNode){
  104. // Summary:
  105. // Cleans up the widgets for the given row node so that
  106. // we can reattach them if needed
  107. if(inRowNode){
  108. dojo.forEach(dojo.query("[widgetId]", inRowNode).map(dijit.byNode), function(w){
  109. if(w._destroyOnRemove){
  110. w.destroy();
  111. delete w;
  112. }else if(w.domNode && w.domNode.parentNode){
  113. w.domNode.parentNode.removeChild(w.domNode);
  114. }
  115. });
  116. }
  117. },
  118. onBeforeRow: function(inRowIndex, cells){
  119. this._onBeforeRow(inRowIndex, cells);
  120. if(inRowIndex >= 0){
  121. this._cleanupRowWidgets(this.getRowNode(inRowIndex));
  122. }
  123. },
  124. onAfterRow: function(inRowIndex, cells, inRowNode){
  125. this._onAfterRow(inRowIndex, cells, inRowNode);
  126. var g = this.grid;
  127. dojo.forEach(dojo.query(".dojoxGridStubNode", inRowNode), function(n){
  128. if(n && n.parentNode){
  129. var lw = n.getAttribute("linkWidget");
  130. var cellIdx = window.parseInt(dojo.attr(n, "cellIdx"), 10);
  131. var cellDef = g.getCell(cellIdx);
  132. var w = dijit.byId(lw);
  133. if(w){
  134. n.parentNode.replaceChild(w.domNode, n);
  135. if(!w._started){
  136. w.startup();
  137. }
  138. dojo.destroy(n);
  139. }else{
  140. n.innerHTML = "";
  141. }
  142. }
  143. }, this);
  144. },
  145. testFlexCells: function(){
  146. // FIXME: cheater, this function does double duty as initializer and tester
  147. this.flexCells = false;
  148. for(var j=0, row; (row=this.structure.cells[j]); j++){
  149. for(var i=0, cell; (cell=row[i]); i++){
  150. cell.view = this;
  151. this.flexCells = this.flexCells || cell.isFlex();
  152. }
  153. }
  154. return this.flexCells;
  155. },
  156. updateStructure: function(){
  157. // header builder needs to update table map
  158. this.header.update();
  159. // content builder needs to update markup cache
  160. this.content.update();
  161. },
  162. getScrollbarWidth: function(){
  163. var hasScrollSpace = this.hasVScrollbar();
  164. var overflow = dojo.style(this.scrollboxNode, "overflow");
  165. if(this.noscroll || !overflow || overflow == "hidden"){
  166. hasScrollSpace = false;
  167. }else if(overflow == "scroll"){
  168. hasScrollSpace = true;
  169. }
  170. return (hasScrollSpace ? dojox.html.metrics.getScrollbar().w : 0); // Integer
  171. },
  172. getColumnsWidth: function(){
  173. var h = this.headerContentNode;
  174. return h && h.firstChild ? (h.firstChild.offsetWidth || dojo.style(h.firstChild, 'width')) : 0; // Integer
  175. },
  176. setColumnsWidth: function(width){
  177. this.headerContentNode.firstChild.style.width = width + 'px';
  178. if(this.viewWidth){
  179. this.viewWidth = width + 'px';
  180. }
  181. },
  182. getWidth: function(){
  183. return this.viewWidth || (this.getColumnsWidth()+this.getScrollbarWidth()) +'px'; // String
  184. },
  185. getContentWidth: function(){
  186. return Math.max(0, dojo._getContentBox(this.domNode).w - this.getScrollbarWidth()) + 'px'; // String
  187. },
  188. render: function(){
  189. this.scrollboxNode.style.height = '';
  190. this.renderHeader();
  191. if(this._togglingColumn >= 0){
  192. this.setColumnsWidth(this.getColumnsWidth() - this._togglingColumn);
  193. this._togglingColumn = -1;
  194. }
  195. var cells = this.grid.layout.cells;
  196. var getSibling = dojo.hitch(this, function(node, before){
  197. !dojo._isBodyLtr() && (before = !before);
  198. var inc = before?-1:1;
  199. var idx = this.header.getCellNodeIndex(node) + inc;
  200. var cell = cells[idx];
  201. while(cell && cell.getHeaderNode() && cell.getHeaderNode().style.display == "none"){
  202. idx += inc;
  203. cell = cells[idx];
  204. }
  205. if(cell){
  206. return cell.getHeaderNode();
  207. }
  208. return null;
  209. });
  210. if(this.grid.columnReordering && this.simpleStructure){
  211. if(this.source){
  212. this.source.destroy();
  213. }
  214. // Create the top and bottom markers
  215. var bottomMarkerId = "dojoxGrid_bottomMarker";
  216. var topMarkerId = "dojoxGrid_topMarker";
  217. if(this.bottomMarker){
  218. dojo.destroy(this.bottomMarker);
  219. }
  220. this.bottomMarker = dojo.byId(bottomMarkerId);
  221. if(this.topMarker){
  222. dojo.destroy(this.topMarker);
  223. }
  224. this.topMarker = dojo.byId(topMarkerId);
  225. if (!this.bottomMarker) {
  226. this.bottomMarker = dojo.create("div", {
  227. "id": bottomMarkerId,
  228. "class": "dojoxGridColPlaceBottom"
  229. }, dojo.body());
  230. this._hide(this.bottomMarker);
  231. this.topMarker = dojo.create("div", {
  232. "id": topMarkerId,
  233. "class": "dojoxGridColPlaceTop"
  234. }, dojo.body());
  235. this._hide(this.topMarker);
  236. }
  237. this.arrowDim = dojo.contentBox(this.bottomMarker);
  238. var headerHeight = dojo.contentBox(this.headerContentNode.firstChild.rows[0]).h;
  239. this.source = new dojo.dnd.Source(this.headerContentNode.firstChild.rows[0], {
  240. horizontal: true,
  241. accept: [ "gridColumn_" + this.grid.id ],
  242. viewIndex: this.index,
  243. generateText: false,
  244. onMouseDown: dojo.hitch(this, function(e){
  245. this.header.decorateEvent(e);
  246. if((this.header.overRightResizeArea(e) || this.header.overLeftResizeArea(e)) &&
  247. this.header.canResize(e) && !this.header.moveable){
  248. this.header.beginColumnResize(e);
  249. }else{
  250. if(this.grid.headerMenu){
  251. this.grid.headerMenu.onCancel(true);
  252. }
  253. // IE reports a left click as 1, where everything else reports 0
  254. if(e.button === (dojo.isIE < 9 ? 1 : 0)){
  255. dojo.dnd.Source.prototype.onMouseDown.call(this.source, e);
  256. }
  257. }
  258. }),
  259. onMouseOver: dojo.hitch(this, function(e){
  260. var src = this.source;
  261. if(src._getChildByEvent(e)){
  262. dojo.dnd.Source.prototype.onMouseOver.apply(src, arguments);
  263. }
  264. }),
  265. _markTargetAnchor: dojo.hitch(this, function(before){
  266. var src = this.source;
  267. if(src.current == src.targetAnchor && src.before == before){ return; }
  268. if(src.targetAnchor && getSibling(src.targetAnchor, src.before)){
  269. src._removeItemClass(getSibling(src.targetAnchor, src.before), src.before ? "After" : "Before");
  270. }
  271. dojo.dnd.Source.prototype._markTargetAnchor.call(src, before);
  272. var target = before ? src.targetAnchor : getSibling(src.targetAnchor, src.before);
  273. var endAdd = 0;
  274. if (!target) {
  275. target = src.targetAnchor;
  276. endAdd = dojo.contentBox(target).w + this.arrowDim.w/2 + 2;
  277. }
  278. // NOTE: this is for backwards compatibility with Dojo 1.3
  279. var pos = (dojo.position||dojo._abs)(target, true);
  280. var left = Math.floor(pos.x - this.arrowDim.w/2 + endAdd);
  281. dojo.style(this.bottomMarker, "visibility", "visible");
  282. dojo.style(this.topMarker, "visibility", "visible");
  283. dojo.style(this.bottomMarker, {
  284. "left": left + "px",
  285. "top" : (headerHeight + pos.y) + "px"
  286. });
  287. dojo.style(this.topMarker, {
  288. "left": left + "px",
  289. "top" : (pos.y - this.arrowDim.h) + "px"
  290. });
  291. if(src.targetAnchor && getSibling(src.targetAnchor, src.before)){
  292. src._addItemClass(getSibling(src.targetAnchor, src.before), src.before ? "After" : "Before");
  293. }
  294. }),
  295. _unmarkTargetAnchor: dojo.hitch(this, function(){
  296. var src = this.source;
  297. if(!src.targetAnchor){ return; }
  298. if(src.targetAnchor && getSibling(src.targetAnchor, src.before)){
  299. src._removeItemClass(getSibling(src.targetAnchor, src.before), src.before ? "After" : "Before");
  300. }
  301. this._hide(this.bottomMarker);
  302. this._hide(this.topMarker);
  303. dojo.dnd.Source.prototype._unmarkTargetAnchor.call(src);
  304. }),
  305. destroy: dojo.hitch(this, function(){
  306. dojo.disconnect(this._source_conn);
  307. dojo.unsubscribe(this._source_sub);
  308. dojo.dnd.Source.prototype.destroy.call(this.source);
  309. if(this.bottomMarker){
  310. dojo.destroy(this.bottomMarker);
  311. delete this.bottomMarker;
  312. }
  313. if(this.topMarker){
  314. dojo.destroy(this.topMarker);
  315. delete this.topMarker;
  316. }
  317. }),
  318. onDndCancel: dojo.hitch(this, function(){
  319. dojo.dnd.Source.prototype.onDndCancel.call(this.source);
  320. this._hide(this.bottomMarker);
  321. this._hide(this.topMarker);
  322. })
  323. });
  324. this._source_conn = dojo.connect(this.source, "onDndDrop", this, "_onDndDrop");
  325. this._source_sub = dojo.subscribe("/dnd/drop/before", this, "_onDndDropBefore");
  326. this.source.startup();
  327. }
  328. },
  329. _hide: function(node){
  330. dojo.style(node, {
  331. left: "-10000px",
  332. top: "-10000px",
  333. "visibility": "hidden"
  334. });
  335. },
  336. _onDndDropBefore: function(source, nodes, copy){
  337. if(dojo.dnd.manager().target !== this.source){
  338. return;
  339. }
  340. this.source._targetNode = this.source.targetAnchor;
  341. this.source._beforeTarget = this.source.before;
  342. var views = this.grid.views.views;
  343. var srcView = views[source.viewIndex];
  344. var tgtView = views[this.index];
  345. if(tgtView != srcView){
  346. srcView.convertColPctToFixed();
  347. tgtView.convertColPctToFixed();
  348. }
  349. },
  350. _onDndDrop: function(source, nodes, copy){
  351. if(dojo.dnd.manager().target !== this.source){
  352. if(dojo.dnd.manager().source === this.source){
  353. this._removingColumn = true;
  354. }
  355. return;
  356. }
  357. this._hide(this.bottomMarker);
  358. this._hide(this.topMarker);
  359. var getIdx = function(n){
  360. return n ? dojo.attr(n, "idx") : null;
  361. };
  362. var w = dojo.marginBox(nodes[0]).w;
  363. if(source.viewIndex !== this.index){
  364. var views = this.grid.views.views;
  365. var srcView = views[source.viewIndex];
  366. var tgtView = views[this.index];
  367. if(srcView.viewWidth && srcView.viewWidth != "auto"){
  368. srcView.setColumnsWidth(srcView.getColumnsWidth() - w);
  369. }
  370. if(tgtView.viewWidth && tgtView.viewWidth != "auto"){
  371. tgtView.setColumnsWidth(tgtView.getColumnsWidth());
  372. }
  373. }
  374. var stn = this.source._targetNode;
  375. var stb = this.source._beforeTarget;
  376. !dojo._isBodyLtr() && (stb = !stb);
  377. var layout = this.grid.layout;
  378. var idx = this.index;
  379. delete this.source._targetNode;
  380. delete this.source._beforeTarget;
  381. layout.moveColumn(
  382. source.viewIndex,
  383. idx,
  384. getIdx(nodes[0]),
  385. getIdx(stn),
  386. stb);
  387. },
  388. renderHeader: function(){
  389. this.headerContentNode.innerHTML = this.header.generateHtml(this._getHeaderContent);
  390. if(this.flexCells){
  391. this.contentWidth = this.getContentWidth();
  392. this.headerContentNode.firstChild.style.width = this.contentWidth;
  393. }
  394. dojox.grid.util.fire(this, "onAfterRow", [-1, this.structure.cells, this.headerContentNode]);
  395. },
  396. // note: not called in 'view' context
  397. _getHeaderContent: function(inCell){
  398. var n = inCell.name || inCell.grid.getCellName(inCell);
  399. var ret = [ '<div class="dojoxGridSortNode' ];
  400. if(inCell.index != inCell.grid.getSortIndex()){
  401. ret.push('">');
  402. }else{
  403. ret = ret.concat([ ' ',
  404. inCell.grid.sortInfo > 0 ? 'dojoxGridSortUp' : 'dojoxGridSortDown',
  405. '"><div class="dojoxGridArrowButtonChar">',
  406. inCell.grid.sortInfo > 0 ? '&#9650;' : '&#9660;',
  407. '</div><div class="dojoxGridArrowButtonNode" role="presentation"></div>',
  408. '<div class="dojoxGridColCaption">']);
  409. }
  410. ret = ret.concat([n, '</div></div>']);
  411. return ret.join('');
  412. },
  413. resize: function(){
  414. this.adaptHeight();
  415. this.adaptWidth();
  416. },
  417. hasHScrollbar: function(reset){
  418. var hadScroll = this._hasHScroll||false;
  419. if(this._hasHScroll == undefined || reset){
  420. if(this.noscroll){
  421. this._hasHScroll = false;
  422. }else{
  423. var style = dojo.style(this.scrollboxNode, "overflow");
  424. if(style == "hidden"){
  425. this._hasHScroll = false;
  426. }else if(style == "scroll"){
  427. this._hasHScroll = true;
  428. }else{
  429. this._hasHScroll = (this.scrollboxNode.offsetWidth - this.getScrollbarWidth() < this.contentNode.offsetWidth );
  430. }
  431. }
  432. }
  433. if(hadScroll !== this._hasHScroll){
  434. this.grid.update();
  435. }
  436. return this._hasHScroll; // Boolean
  437. },
  438. hasVScrollbar: function(reset){
  439. var hadScroll = this._hasVScroll||false;
  440. if(this._hasVScroll == undefined || reset){
  441. if(this.noscroll){
  442. this._hasVScroll = false;
  443. }else{
  444. var style = dojo.style(this.scrollboxNode, "overflow");
  445. if(style == "hidden"){
  446. this._hasVScroll = false;
  447. }else if(style == "scroll"){
  448. this._hasVScroll = true;
  449. }else{
  450. this._hasVScroll = (this.scrollboxNode.scrollHeight > this.scrollboxNode.clientHeight);
  451. }
  452. }
  453. }
  454. if(hadScroll !== this._hasVScroll){
  455. this.grid.update();
  456. }
  457. return this._hasVScroll; // Boolean
  458. },
  459. convertColPctToFixed: function(){
  460. // Fix any percentage widths to be pixel values
  461. var hasPct = false;
  462. this.grid.initialWidth = "";
  463. var cellNodes = dojo.query("th", this.headerContentNode);
  464. var fixedWidths = dojo.map(cellNodes, function(c, vIdx){
  465. var w = c.style.width;
  466. dojo.attr(c, "vIdx", vIdx);
  467. if(w && w.slice(-1) == "%"){
  468. hasPct = true;
  469. }else if(w && w.slice(-2) == "px"){
  470. return window.parseInt(w, 10);
  471. }
  472. return dojo.contentBox(c).w;
  473. });
  474. if(hasPct){
  475. dojo.forEach(this.grid.layout.cells, function(cell, idx){
  476. if(cell.view == this){
  477. var cellNode = cell.view.getHeaderCellNode(cell.index);
  478. if(cellNode && dojo.hasAttr(cellNode, "vIdx")){
  479. var vIdx = window.parseInt(dojo.attr(cellNode, "vIdx"));
  480. this.setColWidth(idx, fixedWidths[vIdx]);
  481. dojo.removeAttr(cellNode, "vIdx");
  482. }
  483. }
  484. }, this);
  485. return true;
  486. }
  487. return false;
  488. },
  489. adaptHeight: function(minusScroll){
  490. if(!this.grid._autoHeight){
  491. var h = (this.domNode.style.height && parseInt(this.domNode.style.height.replace(/px/,''), 10)) || this.domNode.clientHeight;
  492. var self = this;
  493. var checkOtherViewScrollers = function(){
  494. var v;
  495. for(var i in self.grid.views.views){
  496. v = self.grid.views.views[i];
  497. if(v !== self && v.hasHScrollbar()){
  498. return true;
  499. }
  500. }
  501. return false;
  502. };
  503. if(minusScroll || (this.noscroll && checkOtherViewScrollers())){
  504. h -= dojox.html.metrics.getScrollbar().h;
  505. }
  506. dojox.grid.util.setStyleHeightPx(this.scrollboxNode, h);
  507. }
  508. this.hasVScrollbar(true);
  509. },
  510. adaptWidth: function(){
  511. if(this.flexCells){
  512. // the view content width
  513. this.contentWidth = this.getContentWidth();
  514. this.headerContentNode.firstChild.style.width = this.contentWidth;
  515. }
  516. // FIXME: it should be easier to get w from this.scrollboxNode.clientWidth,
  517. // but clientWidth seemingly does not include scrollbar width in some cases
  518. var w = this.scrollboxNode.offsetWidth - this.getScrollbarWidth();
  519. if(!this._removingColumn){
  520. w = Math.max(w, this.getColumnsWidth()) + 'px';
  521. }else{
  522. w = Math.min(w, this.getColumnsWidth()) + 'px';
  523. this._removingColumn = false;
  524. }
  525. var cn = this.contentNode;
  526. cn.style.width = w;
  527. this.hasHScrollbar(true);
  528. },
  529. setSize: function(w, h){
  530. var ds = this.domNode.style;
  531. var hs = this.headerNode.style;
  532. if(w){
  533. ds.width = w;
  534. hs.width = w;
  535. }
  536. ds.height = (h >= 0 ? h + 'px' : '');
  537. },
  538. renderRow: function(inRowIndex){
  539. var rowNode = this.createRowNode(inRowIndex);
  540. this.buildRow(inRowIndex, rowNode);
  541. //this.grid.edit.restore(this, inRowIndex);
  542. return rowNode;
  543. },
  544. createRowNode: function(inRowIndex){
  545. var node = document.createElement("div");
  546. node.className = this.classTag + 'Row';
  547. if (this instanceof dojox.grid._RowSelector){
  548. dojo.attr(node,"role","presentation");
  549. }else{
  550. dojo.attr(node,"role","row");
  551. if (this.grid.selectionMode != "none") {
  552. dojo.attr(node, "aria-selected", "false"); //rows can be selected so add aria-selected prop
  553. }
  554. }
  555. node[dojox.grid.util.gridViewTag] = this.id;
  556. node[dojox.grid.util.rowIndexTag] = inRowIndex;
  557. this.rowNodes[inRowIndex] = node;
  558. return node;
  559. },
  560. buildRow: function(inRowIndex, inRowNode){
  561. this.buildRowContent(inRowIndex, inRowNode);
  562. this.styleRow(inRowIndex, inRowNode);
  563. },
  564. buildRowContent: function(inRowIndex, inRowNode){
  565. inRowNode.innerHTML = this.content.generateHtml(inRowIndex, inRowIndex);
  566. if(this.flexCells && this.contentWidth){
  567. // FIXME: accessing firstChild here breaks encapsulation
  568. inRowNode.firstChild.style.width = this.contentWidth;
  569. }
  570. dojox.grid.util.fire(this, "onAfterRow", [inRowIndex, this.structure.cells, inRowNode]);
  571. },
  572. rowRemoved:function(inRowIndex){
  573. if(inRowIndex >= 0){
  574. this._cleanupRowWidgets(this.getRowNode(inRowIndex));
  575. }
  576. this.grid.edit.save(this, inRowIndex);
  577. delete this.rowNodes[inRowIndex];
  578. },
  579. getRowNode: function(inRowIndex){
  580. return this.rowNodes[inRowIndex];
  581. },
  582. getCellNode: function(inRowIndex, inCellIndex){
  583. var row = this.getRowNode(inRowIndex);
  584. if(row){
  585. return this.content.getCellNode(row, inCellIndex);
  586. }
  587. },
  588. getHeaderCellNode: function(inCellIndex){
  589. if(this.headerContentNode){
  590. return this.header.getCellNode(this.headerContentNode, inCellIndex);
  591. }
  592. },
  593. // styling
  594. styleRow: function(inRowIndex, inRowNode){
  595. inRowNode._style = getStyleText(inRowNode);
  596. this.styleRowNode(inRowIndex, inRowNode);
  597. },
  598. styleRowNode: function(inRowIndex, inRowNode){
  599. if(inRowNode){
  600. this.doStyleRowNode(inRowIndex, inRowNode);
  601. }
  602. },
  603. doStyleRowNode: function(inRowIndex, inRowNode){
  604. this.grid.styleRowNode(inRowIndex, inRowNode);
  605. },
  606. // updating
  607. updateRow: function(inRowIndex){
  608. var rowNode = this.getRowNode(inRowIndex);
  609. if(rowNode){
  610. rowNode.style.height = '';
  611. this.buildRow(inRowIndex, rowNode);
  612. }
  613. return rowNode;
  614. },
  615. updateRowStyles: function(inRowIndex){
  616. this.styleRowNode(inRowIndex, this.getRowNode(inRowIndex));
  617. },
  618. // scrolling
  619. lastTop: 0,
  620. firstScroll:0,
  621. _nativeScroll: false,
  622. doscroll: function(inEvent){
  623. if(dojo.isFF >= 13){
  624. this._nativeScroll = true;
  625. }
  626. //var s = dojo.marginBox(this.headerContentNode.firstChild);
  627. var isLtr = dojo._isBodyLtr();
  628. if(this.firstScroll < 2){
  629. if((!isLtr && this.firstScroll == 1) || (isLtr && this.firstScroll === 0)){
  630. var s = dojo.marginBox(this.headerNodeContainer);
  631. if(dojo.isIE){
  632. this.headerNodeContainer.style.width = s.w + this.getScrollbarWidth() + 'px';
  633. }else if(dojo.isMoz){
  634. //TODO currently only for FF, not sure for safari and opera
  635. this.headerNodeContainer.style.width = s.w - this.getScrollbarWidth() + 'px';
  636. //this.headerNodeContainer.style.width = s.w + 'px';
  637. //set scroll to right in FF
  638. this.scrollboxNode.scrollLeft = isLtr ?
  639. this.scrollboxNode.clientWidth - this.scrollboxNode.scrollWidth :
  640. this.scrollboxNode.scrollWidth - this.scrollboxNode.clientWidth;
  641. }
  642. }
  643. this.firstScroll++;
  644. }
  645. this.headerNode.scrollLeft = this.scrollboxNode.scrollLeft;
  646. // 'lastTop' is a semaphore to prevent feedback-loop with setScrollTop below
  647. var top = this.scrollboxNode.scrollTop;
  648. if(top !== this.lastTop){
  649. this.grid.scrollTo(top);
  650. }
  651. this._nativeScroll = false;
  652. },
  653. setScrollTop: function(inTop){
  654. // 'lastTop' is a semaphore to prevent feedback-loop with doScroll above
  655. this.lastTop = inTop;
  656. if(!this._nativeScroll){
  657. //fix #15487
  658. this.scrollboxNode.scrollTop = inTop;
  659. }
  660. return this.scrollboxNode.scrollTop;
  661. },
  662. // event handlers (direct from DOM)
  663. doContentEvent: function(e){
  664. if(this.content.decorateEvent(e)){
  665. this.grid.onContentEvent(e);
  666. }
  667. },
  668. doHeaderEvent: function(e){
  669. if(this.header.decorateEvent(e)){
  670. this.grid.onHeaderEvent(e);
  671. }
  672. },
  673. // event dispatch(from Grid)
  674. dispatchContentEvent: function(e){
  675. return this.content.dispatchEvent(e);
  676. },
  677. dispatchHeaderEvent: function(e){
  678. return this.header.dispatchEvent(e);
  679. },
  680. // column resizing
  681. setColWidth: function(inIndex, inWidth){
  682. this.grid.setCellWidth(inIndex, inWidth + 'px');
  683. },
  684. update: function(){
  685. if(!this.domNode){
  686. return;
  687. }
  688. this.content.update();
  689. this.grid.update();
  690. //get scroll after update or scroll left setting goes wrong on IE.
  691. //See trac: #8040
  692. var left = this.scrollboxNode.scrollLeft;
  693. this.scrollboxNode.scrollLeft = left;
  694. this.headerNode.scrollLeft = left;
  695. }
  696. });
  697. dojo.declare("dojox.grid._GridAvatar", dojo.dnd.Avatar, {
  698. construct: function(){
  699. var dd = dojo.doc;
  700. var a = dd.createElement("table");
  701. a.cellPadding = a.cellSpacing = "0";
  702. a.className = "dojoxGridDndAvatar";
  703. a.style.position = "absolute";
  704. a.style.zIndex = 1999;
  705. a.style.margin = "0px"; // to avoid dojo.marginBox() problems with table's margins
  706. var b = dd.createElement("tbody");
  707. var tr = dd.createElement("tr");
  708. var td = dd.createElement("td");
  709. var img = dd.createElement("td");
  710. tr.className = "dojoxGridDndAvatarItem";
  711. img.className = "dojoxGridDndAvatarItemImage";
  712. img.style.width = "16px";
  713. var source = this.manager.source, node;
  714. if(source.creator){
  715. // create an avatar representation of the node
  716. node = source._normalizedCreator(source.getItem(this.manager.nodes[0].id).data, "avatar").node;
  717. }else{
  718. // or just clone the node and hope it works
  719. node = this.manager.nodes[0].cloneNode(true);
  720. var table, tbody;
  721. if(node.tagName.toLowerCase() == "tr"){
  722. // insert extra table nodes
  723. table = dd.createElement("table");
  724. tbody = dd.createElement("tbody");
  725. tbody.appendChild(node);
  726. table.appendChild(tbody);
  727. node = table;
  728. }else if(node.tagName.toLowerCase() == "th"){
  729. // insert extra table nodes
  730. table = dd.createElement("table");
  731. tbody = dd.createElement("tbody");
  732. var r = dd.createElement("tr");
  733. table.cellPadding = table.cellSpacing = "0";
  734. r.appendChild(node);
  735. tbody.appendChild(r);
  736. table.appendChild(tbody);
  737. node = table;
  738. }
  739. }
  740. node.id = "";
  741. td.appendChild(node);
  742. tr.appendChild(img);
  743. tr.appendChild(td);
  744. dojo.style(tr, "opacity", 0.9);
  745. b.appendChild(tr);
  746. a.appendChild(b);
  747. this.node = a;
  748. var m = dojo.dnd.manager();
  749. this.oldOffsetY = m.OFFSET_Y;
  750. m.OFFSET_Y = 1;
  751. },
  752. destroy: function(){
  753. dojo.dnd.manager().OFFSET_Y = this.oldOffsetY;
  754. this.inherited(arguments);
  755. }
  756. });
  757. var oldMakeAvatar = dojo.dnd.manager().makeAvatar;
  758. dojo.dnd.manager().makeAvatar = function(){
  759. var src = this.source;
  760. if(src.viewIndex !== undefined && !dojo.hasClass(dojo.body(),"dijit_a11y")){
  761. return new dojox.grid._GridAvatar(this);
  762. }
  763. return oldMakeAvatar.call(dojo.dnd.manager());
  764. };
  765. })();
  766. }