_ViewManager.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. define("dojox/grid/_ViewManager", [
  2. "dojo/_base/declare",
  3. "dojo/_base/sniff",
  4. "dojo/dom-class"
  5. ], function(declare, has, domClass){
  6. return declare('dojox.grid._ViewManager', null, {
  7. // summary:
  8. // A collection of grid views. Owned by grid and used internally for managing grid views.
  9. // description:
  10. // Grid creates views automatically based on grid's layout structure.
  11. // Users should typically not need to access individual views or the views collection directly.
  12. constructor: function(inGrid){
  13. this.grid = inGrid;
  14. },
  15. defaultWidth: 200,
  16. views: [],
  17. // operations
  18. resize: function(){
  19. this.onEach("resize");
  20. },
  21. render: function(){
  22. this.onEach("render");
  23. },
  24. // views
  25. addView: function(inView){
  26. inView.idx = this.views.length;
  27. this.views.push(inView);
  28. },
  29. destroyViews: function(){
  30. for(var i=0, v; v=this.views[i]; i++){
  31. v.destroy();
  32. }
  33. this.views = [];
  34. },
  35. getContentNodes: function(){
  36. var nodes = [];
  37. for(var i=0, v; v=this.views[i]; i++){
  38. nodes.push(v.contentNode);
  39. }
  40. return nodes;
  41. },
  42. forEach: function(inCallback){
  43. for(var i=0, v; v=this.views[i]; i++){
  44. inCallback(v, i);
  45. }
  46. },
  47. onEach: function(inMethod, inArgs){
  48. inArgs = inArgs || [];
  49. for(var i=0, v; v=this.views[i]; i++){
  50. if(inMethod in v){
  51. v[inMethod].apply(v, inArgs);
  52. }
  53. }
  54. },
  55. // layout
  56. normalizeHeaderNodeHeight: function(){
  57. var rowNodes = [];
  58. for(var i=0, v; (v=this.views[i]); i++){
  59. if(v.headerContentNode.firstChild){
  60. rowNodes.push(v.headerContentNode);
  61. }
  62. }
  63. this.normalizeRowNodeHeights(rowNodes);
  64. },
  65. normalizeRowNodeHeights: function(inRowNodes){
  66. var h = 0;
  67. var currHeights = [];
  68. if(this.grid.rowHeight){
  69. h = this.grid.rowHeight;
  70. }else{
  71. if(inRowNodes.length <= 1){
  72. // no need to normalize if we are the only one...
  73. return;
  74. }
  75. for(var i=0, n; (n=inRowNodes[i]); i++){
  76. // We only care about the height - so don't use marginBox. This
  77. // depends on the container not having any margin (which it shouldn't)
  78. // Also - we only look up the height if the cell doesn't have the
  79. // dojoxGridNonNormalizedCell class (like for row selectors)
  80. if(!domClass.contains(n, "dojoxGridNonNormalizedCell")){
  81. currHeights[i] = n.firstChild.offsetHeight;
  82. h = Math.max(h, currHeights[i]);
  83. }
  84. }
  85. h = (h >= 0 ? h : 0);
  86. //Work around odd FF3 rendering bug: #8864.
  87. //A one px increase fixes FireFox 3's rounding bug for fractional font sizes.
  88. if((has("mozilla") || has("ie") > 8 ) && h){h++;}
  89. }
  90. for(i=0; (n=inRowNodes[i]); i++){
  91. if(currHeights[i] != h){
  92. n.firstChild.style.height = h + "px";
  93. }
  94. }
  95. },
  96. resetHeaderNodeHeight: function(){
  97. for(var i=0, v, n; (v=this.views[i]); i++){
  98. n = v.headerContentNode.firstChild;
  99. if(n){
  100. n.style.height = "";
  101. }
  102. }
  103. },
  104. renormalizeRow: function(inRowIndex){
  105. var rowNodes = [];
  106. for(var i=0, v, n; (v=this.views[i])&&(n=v.getRowNode(inRowIndex)); i++){
  107. n.firstChild.style.height = '';
  108. rowNodes.push(n);
  109. }
  110. this.normalizeRowNodeHeights(rowNodes);
  111. },
  112. getViewWidth: function(inIndex){
  113. return this.views[inIndex].getWidth() || this.defaultWidth;
  114. },
  115. // must be called after view widths are properly set or height can be miscalculated
  116. // if there are flex columns
  117. measureHeader: function(){
  118. // need to reset view header heights so they are properly measured.
  119. this.resetHeaderNodeHeight();
  120. this.forEach(function(inView){
  121. inView.headerContentNode.style.height = '';
  122. });
  123. var h = 0;
  124. // calculate maximum view header height
  125. this.forEach(function(inView){
  126. h = Math.max(inView.headerNode.offsetHeight, h);
  127. });
  128. return h;
  129. },
  130. measureContent: function(){
  131. var h = 0;
  132. this.forEach(function(inView){
  133. h = Math.max(inView.domNode.offsetHeight, h);
  134. });
  135. return h;
  136. },
  137. findClient: function(inAutoWidth){
  138. // try to use user defined client
  139. var c = this.grid.elasticView || -1;
  140. // attempt to find implicit client
  141. if(c < 0){
  142. for(var i=1, v; (v=this.views[i]); i++){
  143. if(v.viewWidth){
  144. for(i=1; (v=this.views[i]); i++){
  145. if(!v.viewWidth){
  146. c = i;
  147. break;
  148. }
  149. }
  150. break;
  151. }
  152. }
  153. }
  154. // client is in the middle by default
  155. if(c < 0){
  156. c = Math.floor(this.views.length / 2);
  157. }
  158. return c;
  159. },
  160. arrange: function(l, w){
  161. var i, v, vw, len = this.views.length, self = this;
  162. // find the client
  163. var c = (w <= 0 ? len : this.findClient());
  164. // layout views
  165. var setPosition = function(v, l){
  166. var ds = v.domNode.style;
  167. var hs = v.headerNode.style;
  168. if(!self.grid.isLeftToRight()){
  169. ds.right = l + 'px';
  170. // fixed rtl, the scrollbar is on the right side in FF < 4
  171. if (has("ff") < 4){
  172. hs.right = l + v.getScrollbarWidth() + 'px';
  173. }else{
  174. hs.right = l + 'px';
  175. }
  176. if(!has("webkit")){
  177. hs.width = parseInt(hs.width, 10) - v.getScrollbarWidth() + 'px';
  178. }
  179. }else{
  180. ds.left = l + 'px';
  181. hs.left = l + 'px';
  182. }
  183. ds.top = 0 + 'px';
  184. hs.top = 0;
  185. };
  186. // for views left of the client
  187. //BiDi TODO: The left and right should not appear in BIDI environment. Should be replaced with
  188. //leading and tailing concept.
  189. for(i=0; (v=this.views[i])&&(i<c); i++){
  190. // get width
  191. vw = this.getViewWidth(i);
  192. // process boxes
  193. v.setSize(vw, 0);
  194. setPosition(v, l);
  195. if(v.headerContentNode && v.headerContentNode.firstChild){
  196. vw = v.getColumnsWidth()+v.getScrollbarWidth();
  197. }else{
  198. vw = v.domNode.offsetWidth;
  199. }
  200. // update position
  201. l += vw;
  202. }
  203. // next view (is the client, i++ == c)
  204. i++;
  205. // start from the right edge
  206. var r = w;
  207. // for views right of the client (iterated from the right)
  208. for(var j=len-1; (v=this.views[j])&&(i<=j); j--){
  209. // get width
  210. vw = this.getViewWidth(j);
  211. // set size
  212. v.setSize(vw, 0);
  213. // measure in pixels
  214. vw = v.domNode.offsetWidth;
  215. // update position
  216. r -= vw;
  217. // set position
  218. setPosition(v, r);
  219. }
  220. if(c<len){
  221. v = this.views[c];
  222. // position the client box between left and right boxes
  223. vw = Math.max(1, r-l);
  224. // set size
  225. v.setSize(vw + 'px', 0);
  226. setPosition(v, l);
  227. }
  228. return l;
  229. },
  230. // rendering
  231. renderRow: function(inRowIndex, inNodes, skipRenorm){
  232. var rowNodes = [];
  233. for(var i=0, v, n, rowNode; (v=this.views[i])&&(n=inNodes[i]); i++){
  234. rowNode = v.renderRow(inRowIndex);
  235. n.appendChild(rowNode);
  236. rowNodes.push(rowNode);
  237. }
  238. if(!skipRenorm){
  239. this.normalizeRowNodeHeights(rowNodes);
  240. }
  241. },
  242. rowRemoved: function(inRowIndex){
  243. this.onEach("rowRemoved", [ inRowIndex ]);
  244. },
  245. // updating
  246. updateRow: function(inRowIndex, skipRenorm){
  247. for(var i=0, v; v=this.views[i]; i++){
  248. v.updateRow(inRowIndex);
  249. }
  250. if(!skipRenorm){
  251. this.renormalizeRow(inRowIndex);
  252. }
  253. },
  254. updateRowStyles: function(inRowIndex){
  255. this.onEach("updateRowStyles", [ inRowIndex ]);
  256. },
  257. // scrolling
  258. setScrollTop: function(inTop){
  259. var top = inTop;
  260. for(var i=0, v; v=this.views[i]; i++){
  261. top = v.setScrollTop(inTop);
  262. // Work around IE not firing scroll events that cause header offset
  263. // issues to occur.
  264. if(has("ie") && v.headerNode && v.scrollboxNode){
  265. v.headerNode.scrollLeft = v.scrollboxNode.scrollLeft;
  266. }
  267. }
  268. return top;
  269. //this.onEach("setScrollTop", [ inTop ]);
  270. },
  271. getFirstScrollingView: function(){
  272. // summary: Returns the first grid view with a scroll bar
  273. for(var i=0, v; (v=this.views[i]); i++){
  274. if(v.hasHScrollbar() || v.hasVScrollbar()){
  275. return v;
  276. }
  277. }
  278. return null;
  279. }
  280. });
  281. });