GridContainer.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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.layout.GridContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.layout.GridContainer"] = true;
  8. dojo.provide("dojox.layout.GridContainer");
  9. dojo.require("dojox.layout.GridContainerLite");
  10. dojo.declare(
  11. "dojox.layout.GridContainer",
  12. dojox.layout.GridContainerLite,
  13. {
  14. // summary:
  15. // A grid containing any kind of objects and acting like web portals.
  16. //
  17. // description:
  18. // This component inherits of all features of gridContainerLite plus :
  19. // - Resize colums
  20. // - Add / remove columns
  21. // - Fix columns at left or at right.
  22. // example:
  23. // | <div dojoType="dojox.layout.GridContainer" nbZones="3" isAutoOrganized="true">
  24. // | <div dojoType="dijit.layout.ContentPane">Content Pane 1 : Drag Me !</div>
  25. // | <div dojoType="dijit.layout.ContentPane">Content Pane 2 : Drag Me !</div>
  26. // | <div dojoType="dijit.layout.ContentPane">Content Pane 3 : Drag Me !</div>
  27. // | </div>
  28. //
  29. // example:
  30. // | dojo.ready(function(){
  31. // | var cpane1 = new dijit.layout.ContentPane({ title:"cpane1", content: "Content Pane 1 : Drag Me !" }),
  32. // | cpane2 = new dijit.layout.ContentPane({ title:"cpane2", content: "Content Pane 2 : Drag Me !" }),
  33. // | cpane3 = new dijit.layout.ContentPane({ title:"cpane3", content: "Content Pane 3 : Drag Me !" });
  34. // |
  35. // | var widget = new dojox.layout.GridContainer({
  36. // | nbZones: 3,
  37. // | isAutoOrganized: true
  38. // | }, dojo.byId("idNode"));
  39. // | widget.addChild(cpane1, 0, 0);
  40. // | widget.addChild(cpane2, 1, 0);
  41. // | widget.addChild(cpane3, 2, 1);
  42. // | widget.startup();
  43. // | });
  44. // hasResizableColumns: Boolean
  45. // Allow or not resizing of columns by a grip handle.
  46. hasResizableColumns: true,
  47. // liveResizeColumns: Boolean
  48. // Specifies whether columns resize as you drag (true) or only upon mouseup (false)
  49. liveResizeColumns : false,
  50. // minColWidth: Integer
  51. // Minimum column width in percentage.
  52. minColWidth: 20,
  53. // minChildWidth: Integer
  54. // Minimum children width in pixel (only used for IE6 which doesn't handle min-width css property)
  55. minChildWidth: 150,
  56. // mode: String
  57. // Location to add/remove columns, must be set to 'left' or 'right' (default).
  58. mode: "right",
  59. // isRightFixed: Boolean
  60. // Define if the last right column is fixed.
  61. // Used when you add or remove columns by calling setColumns method.
  62. isRightFixed: false,
  63. // isLeftFixed: Boolean
  64. // Define if the last left column is fixed.
  65. // Used when you add or remove columns by calling setColumns method.
  66. isLeftFixed: false,
  67. startup: function(){
  68. // summary:
  69. // Call the startup of GridContainerLite and place grips
  70. // if user has chosen the hasResizableColumns attribute to true.
  71. //console.log("dojox.layout.GridContainer ::: startup");
  72. this.inherited(arguments);
  73. if(this.hasResizableColumns){
  74. for(var i = 0; i < this._grid.length - 1; i++){
  75. this._createGrip(i);
  76. }
  77. // If widget has a container parent, grips will be placed
  78. // by method onShow.
  79. if(!this.getParent()){
  80. // Fix IE7 :
  81. // The CSS property height:100% for the grip
  82. // doesn't work anytime. It's necessary to wait
  83. // the end of loading before to place grips.
  84. dojo.ready(dojo.hitch(this, "_placeGrips"));
  85. }
  86. }
  87. },
  88. resizeChildAfterDrop : function(/*Node*/node, /*Object*/targetArea, /*Integer*/indexChild){
  89. // summary:
  90. // Call when a child is dropped.
  91. // description:
  92. // Allow to resize and put grips
  93. // node:
  94. // domNode of dropped widget.
  95. // targetArea:
  96. // AreaManager Object containing information of targetArea
  97. // indexChild:
  98. // Index where the dropped widget has been placed
  99. if(this.inherited(arguments)){
  100. this._placeGrips();
  101. }
  102. },
  103. onShow: function(){
  104. // summary:
  105. // Place grips in the right place when the GridContainer becomes visible.
  106. //console.log("dojox.layout.GridContainer ::: onShow");
  107. this.inherited(arguments);
  108. this._placeGrips();
  109. },
  110. resize: function(){
  111. // summary:
  112. // Resize the GridContainer widget and columns.
  113. // Replace grips if it's necessary.
  114. // tags:
  115. // callback
  116. //console.log("dojox.layout.GridContainer ::: resize");
  117. this.inherited(arguments);
  118. // Fix IE6 :
  119. // IE6 calls method resize itself.
  120. // If the GridContainer is not visible at this time,
  121. // the method _placeGrips can return a negative value with
  122. // contentBox method. (see method _placeGrip() with Fix Ie6 for the height)
  123. if(this._isShown() && this.hasResizableColumns){
  124. this._placeGrips();
  125. }
  126. },
  127. _createGrip: function(/*Integer*/ index){
  128. // summary:
  129. // Create a grip for a specific zone.
  130. // index:
  131. // index where the grip has to be created.
  132. // tags:
  133. // protected
  134. //console.log("dojox.layout.GridContainer ::: _createGrip");
  135. var dropZone = this._grid[index],
  136. grip = dojo.create("div", { 'class': "gridContainerGrip" }, this.domNode);
  137. dropZone.grip = grip;
  138. dropZone.gripHandler = [
  139. this.connect(grip, "onmouseover", function(e){
  140. var gridContainerGripShow = false;
  141. for(var i = 0; i < this._grid.length - 1; i++){
  142. if(dojo.hasClass(this._grid[i].grip, "gridContainerGripShow")){
  143. gridContainerGripShow = true;
  144. break;
  145. }
  146. }
  147. if(!gridContainerGripShow){
  148. dojo.removeClass(e.target, "gridContainerGrip");
  149. dojo.addClass(e.target, "gridContainerGripShow");
  150. }
  151. })[0],
  152. this.connect(grip, "onmouseout", function(e){
  153. if(!this._isResized){
  154. dojo.removeClass(e.target, "gridContainerGripShow");
  155. dojo.addClass(e.target, "gridContainerGrip");
  156. }
  157. })[0],
  158. this.connect(grip, "onmousedown", "_resizeColumnOn")[0],
  159. this.connect(grip, "ondblclick", "_onGripDbClick")[0]
  160. ];
  161. },
  162. _placeGrips: function(){
  163. // summary:
  164. // Define the position of a grip and place it on page.
  165. // tags:
  166. // protected
  167. //console.log("dojox.layout.GridContainer ::: _placeGrips");
  168. var gripWidth, height, left = 0, grip;
  169. var scroll = this.domNode.style.overflowY;
  170. dojo.forEach(this._grid, function(dropZone){
  171. if(dropZone.grip){
  172. grip = dropZone.grip;
  173. if(!gripWidth){
  174. gripWidth = grip.offsetWidth / 2;
  175. }
  176. left += dojo.marginBox(dropZone.node).w;
  177. dojo.style(grip, "left", (left - gripWidth) + "px");
  178. //if(dojo.isIE == 6){ do it fot all navigators
  179. if(!height){
  180. height = dojo.contentBox(this.gridNode).h;
  181. }
  182. if(height > 0){
  183. dojo.style(grip, "height", height + "px");
  184. }
  185. //}
  186. }
  187. }, this);
  188. },
  189. _onGripDbClick: function(){
  190. // summary:
  191. // Called when a double click is catch. Resize all columns with the same width.
  192. // The method resize of children have to be called.
  193. // tags:
  194. // callback protected
  195. //console.log("dojox.layout.GridContainer ::: _onGripDbClick");
  196. this._updateColumnsWidth(this._dragManager);
  197. this.resize();
  198. },
  199. _resizeColumnOn: function(/*Event*/e){
  200. // summary:
  201. // Connect events to listen the resize action.
  202. // Change the type of width columns (% to px).
  203. // Calculate the minwidth according to the children.
  204. // tags:
  205. // callback
  206. //console.log("dojox.layout.GridContainer ::: _resizeColumnOn", e);
  207. this._activeGrip = e.target;
  208. this._initX = e.pageX;
  209. e.preventDefault();
  210. dojo.body().style.cursor = "ew-resize";
  211. this._isResized = true;
  212. var tabSize = [];
  213. var grid;
  214. var i;
  215. for(i = 0; i < this._grid.length; i++){
  216. tabSize[i] = dojo.contentBox(this._grid[i].node).w;
  217. }
  218. this._oldTabSize = tabSize;
  219. for(i = 0; i < this._grid.length; i++){
  220. grid = this._grid[i];
  221. if(this._activeGrip == grid.grip){
  222. this._currentColumn = grid.node;
  223. this._currentColumnWidth = tabSize[i];
  224. this._nextColumn = this._grid[i + 1].node;
  225. this._nextColumnWidth = tabSize[i + 1];
  226. }
  227. grid.node.style.width = tabSize[i] + "px";
  228. }
  229. // calculate the minWidh of all children for current and next column
  230. var calculateChildMinWidth = function(childNodes, minChild){
  231. var width = 0;
  232. var childMinWidth = 0;
  233. dojo.forEach(childNodes, function(child){
  234. if(child.nodeType == 1){
  235. var objectStyle = dojo.getComputedStyle(child);
  236. var minWidth = (dojo.isIE) ? minChild : parseInt(objectStyle.minWidth);
  237. childMinWidth = minWidth +
  238. parseInt(objectStyle.marginLeft) +
  239. parseInt(objectStyle.marginRight);
  240. if(width < childMinWidth){
  241. width = childMinWidth;
  242. }
  243. }
  244. });
  245. return width;
  246. }
  247. var currentColumnMinWidth = calculateChildMinWidth(this._currentColumn.childNodes, this.minChildWidth);
  248. var nextColumnMinWidth = calculateChildMinWidth(this._nextColumn.childNodes, this.minChildWidth);
  249. var minPix = Math.round((dojo.marginBox(this.gridContainerTable).w * this.minColWidth) / 100);
  250. this._currentMinCol = currentColumnMinWidth;
  251. this._nextMinCol = nextColumnMinWidth;
  252. if(minPix > this._currentMinCol){
  253. this._currentMinCol = minPix;
  254. }
  255. if(minPix > this._nextMinCol){
  256. this._nextMinCol = minPix;
  257. }
  258. this._connectResizeColumnMove = dojo.connect(dojo.doc, "onmousemove", this, "_resizeColumnMove");
  259. this._connectOnGripMouseUp = dojo.connect(dojo.doc, "onmouseup", this, "_onGripMouseUp");
  260. },
  261. _onGripMouseUp: function(){
  262. // summary:
  263. // Call on the onMouseUp only if the reiszeColumnMove was not called.
  264. // tags:
  265. // callback
  266. //console.log(dojox.layout.GridContainer ::: _onGripMouseUp");
  267. dojo.body().style.cursor = "default";
  268. dojo.disconnect(this._connectResizeColumnMove);
  269. dojo.disconnect(this._connectOnGripMouseUp);
  270. this._connectOnGripMouseUp = this._connectResizeColumnMove = null;
  271. if(this._activeGrip){
  272. dojo.removeClass(this._activeGrip, "gridContainerGripShow");
  273. dojo.addClass(this._activeGrip, "gridContainerGrip");
  274. }
  275. this._isResized = false;
  276. },
  277. _resizeColumnMove: function(/*Event*/e){
  278. // summary:
  279. // Change columns size.
  280. // tags:
  281. // callback
  282. //console.log("dojox.layout.GridContainer ::: _resizeColumnMove");
  283. e.preventDefault();
  284. if(!this._connectResizeColumnOff){
  285. dojo.disconnect(this._connectOnGripMouseUp);
  286. this._connectOnGripMouseUp = null;
  287. this._connectResizeColumnOff = dojo.connect(dojo.doc, "onmouseup", this, "_resizeColumnOff");
  288. }
  289. var d = e.pageX - this._initX;
  290. if(d == 0){ return; }
  291. if(!(this._currentColumnWidth + d < this._currentMinCol ||
  292. this._nextColumnWidth - d < this._nextMinCol)){
  293. this._currentColumnWidth += d;
  294. this._nextColumnWidth -= d;
  295. this._initX = e.pageX;
  296. this._activeGrip.style.left = parseInt(this._activeGrip.style.left) + d + "px";
  297. if(this.liveResizeColumns){
  298. this._currentColumn.style["width"] = this._currentColumnWidth + "px";
  299. this._nextColumn.style["width"] = this._nextColumnWidth + "px";
  300. this.resize();
  301. }
  302. }
  303. },
  304. _resizeColumnOff: function(/*Event*/e){
  305. // summary:
  306. // Disconnect resize events.
  307. // Change the type of width columns (px to %).
  308. // tags:
  309. // callback
  310. //console.log("dojox.layout.GridContainer ::: _resizeColumnOff");
  311. dojo.body().style.cursor = "default";
  312. dojo.disconnect(this._connectResizeColumnMove);
  313. dojo.disconnect(this._connectResizeColumnOff);
  314. this._connectResizeColumnOff = this._connectResizeColumnMove = null;
  315. if(!this.liveResizeColumns){
  316. this._currentColumn.style["width"] = this._currentColumnWidth + "px";
  317. this._nextColumn.style["width"] = this._nextColumnWidth + "px";
  318. //this.resize();
  319. }
  320. var tabSize = [],
  321. testSize = [],
  322. tabWidth = this.gridContainerTable.clientWidth,
  323. node,
  324. update = false,
  325. i;
  326. for(i = 0; i < this._grid.length; i++){
  327. node = this._grid[i].node;
  328. if(dojo.isIE){
  329. tabSize[i] = dojo.marginBox(node).w;
  330. testSize[i] = dojo.contentBox(node).w;
  331. }
  332. else{
  333. tabSize[i] = dojo.contentBox(node).w;
  334. testSize = tabSize;
  335. }
  336. }
  337. for(i = 0; i < testSize.length; i++){
  338. if(testSize[i] != this._oldTabSize[i]){
  339. update = true;
  340. break;
  341. }
  342. }
  343. if(update){
  344. var mul = dojo.isIE ? 100 : 10000;
  345. for(i = 0; i < this._grid.length; i++){
  346. this._grid[i].node.style.width = Math.round((100 * mul * tabSize[i]) / tabWidth) / mul + "%";
  347. }
  348. this.resize();
  349. }
  350. if(this._activeGrip){
  351. dojo.removeClass(this._activeGrip, "gridContainerGripShow");
  352. dojo.addClass(this._activeGrip, "gridContainerGrip");
  353. }
  354. this._isResized = false;
  355. },
  356. setColumns: function(/*Integer*/nbColumns){
  357. // summary:
  358. // Set the number of columns.
  359. // nbColumns:
  360. // Number of columns
  361. //console.log("dojox.layout.GridContainer ::: setColumns");
  362. var z, j;
  363. if(nbColumns > 0){
  364. var length = this._grid.length,
  365. delta = length - nbColumns;
  366. if(delta > 0){
  367. var count = [], zone, start, end, nbChildren;
  368. // Check if right or left columns are fixed
  369. // Columns are not taken in account and can't be deleted
  370. if(this.mode == "right"){
  371. end = (this.isLeftFixed && length > 0) ? 1 : 0;
  372. start = (this.isRightFixed) ? length - 2 : length - 1
  373. for(z = start; z >= end; z--){
  374. nbChildren = 0;
  375. zone = this._grid[z].node;
  376. for(j = 0; j < zone.childNodes.length; j++){
  377. if(zone.childNodes[j].nodeType == 1 && !(zone.childNodes[j].id == "")){
  378. nbChildren++;
  379. break;
  380. }
  381. }
  382. if(nbChildren == 0){ count[count.length] = z; }
  383. if(count.length >= delta){
  384. this._deleteColumn(count);
  385. break;
  386. }
  387. }
  388. if(count.length < delta){
  389. dojo.publish("/dojox/layout/gridContainer/noEmptyColumn", [this]);
  390. }
  391. }
  392. else{ // mode = "left"
  393. start = (this.isLeftFixed && length > 0) ? 1 : 0;
  394. end = (this.isRightFixed) ? length - 1 : length;
  395. for(z = start; z < end; z++){
  396. nbChildren = 0;
  397. zone = this._grid[z].node;
  398. for(j = 0; j < zone.childNodes.length; j++){
  399. if(zone.childNodes[j].nodeType == 1 && !(zone.childNodes[j].id == "")){
  400. nbChildren++;
  401. break;
  402. }
  403. }
  404. if(nbChildren == 0){ count[count.length] = z; }
  405. if(count.length >= delta){
  406. this._deleteColumn(count);
  407. break;
  408. }
  409. }
  410. if(count.length < delta){
  411. //Not enough empty columns
  412. dojo.publish("/dojox/layout/gridContainer/noEmptyColumn", [this]);
  413. }
  414. }
  415. }
  416. else{
  417. if(delta < 0){ this._addColumn(Math.abs(delta)); }
  418. }
  419. if(this.hasResizableColumns){ this._placeGrips(); }
  420. }
  421. },
  422. _addColumn: function(/*Integer*/nbColumns){
  423. // summary:
  424. // Add some columns.
  425. // nbColumns:
  426. // Number of column to added
  427. // tags:
  428. // private
  429. //console.log("dojox.layout.GridContainer ::: _addColumn");
  430. var grid = this._grid,
  431. dropZone,
  432. node,
  433. index,
  434. length,
  435. isRightMode = (this.mode == "right"),
  436. accept = this.acceptTypes.join(","),
  437. m = this._dragManager;
  438. //Add a grip to the last column
  439. if(this.hasResizableColumns && ((!this.isRightFixed && isRightMode)
  440. || (this.isLeftFixed && !isRightMode && this.nbZones == 1) )){
  441. this._createGrip(grid.length - 1);
  442. }
  443. for(var i = 0; i < nbColumns; i++){
  444. // Fix CODEX defect #53025 :
  445. // Apply acceptType attribute on each new column.
  446. node = dojo.create("td", {
  447. 'class': "gridContainerZone dojoxDndArea" ,
  448. 'accept': accept,
  449. 'id': this.id + "_dz" + this.nbZones
  450. });
  451. length = grid.length;
  452. if(isRightMode){
  453. if(this.isRightFixed){
  454. index = length - 1;
  455. grid.splice(index, 0, {
  456. 'node': grid[index].node.parentNode.insertBefore(node, grid[index].node)
  457. });
  458. }
  459. else{
  460. index = length;
  461. grid.push({ 'node': this.gridNode.appendChild(node) });
  462. }
  463. }
  464. else{
  465. if(this.isLeftFixed){
  466. index = (length == 1) ? 0 : 1;
  467. this._grid.splice(1, 0, {
  468. 'node': this._grid[index].node.parentNode.appendChild(node, this._grid[index].node)
  469. });
  470. index = 1;
  471. }
  472. else{
  473. index = length - this.nbZones;
  474. this._grid.splice(index, 0, {
  475. 'node': grid[index].node.parentNode.insertBefore(node, grid[index].node)
  476. });
  477. }
  478. }
  479. if(this.hasResizableColumns){
  480. //Add a grip to resize columns
  481. if((!isRightMode && this.nbZones != 1) ||
  482. (!isRightMode && this.nbZones == 1 && !this.isLeftFixed) ||
  483. (isRightMode && i < nbColumns-1) ||
  484. (isRightMode && i == nbColumns-1 && this.isRightFixed)){
  485. this._createGrip(index);
  486. }
  487. }
  488. // register tnbZoneshe new area into the areaManager
  489. m.registerByNode(grid[index].node);
  490. this.nbZones++;
  491. }
  492. this._updateColumnsWidth(m);
  493. },
  494. _deleteColumn: function(/*Array*/indices){
  495. // summary:
  496. // Remove some columns with indices passed as an array.
  497. // indices:
  498. // Column index array
  499. // tags:
  500. // private
  501. //console.log("dojox.layout.GridContainer ::: _deleteColumn");
  502. var child, grid, index,
  503. nbDelZones = 0,
  504. length = indices.length,
  505. m = this._dragManager;
  506. for(var i = 0; i < length; i++){
  507. index = (this.mode == "right") ? indices[i] : indices[i] - nbDelZones;
  508. grid = this._grid[index];
  509. if(this.hasResizableColumns && grid.grip){
  510. dojo.forEach(grid.gripHandler, function(handler){
  511. dojo.disconnect(handler);
  512. });
  513. dojo.destroy(this.domNode.removeChild(grid.grip));
  514. grid.grip = null;
  515. }
  516. m.unregister(grid.node);
  517. dojo.destroy(this.gridNode.removeChild(grid.node));
  518. this._grid.splice(index, 1);
  519. this.nbZones--;
  520. nbDelZones++;
  521. }
  522. // last grip
  523. var lastGrid = this._grid[this.nbZones-1];
  524. if(lastGrid.grip){
  525. dojo.forEach(lastGrid.gripHandler, dojo.disconnect);
  526. dojo.destroy(this.domNode.removeChild(lastGrid.grip));
  527. lastGrid.grip = null;
  528. }
  529. this._updateColumnsWidth(m);
  530. },
  531. _updateColumnsWidth: function(/*Object*/ manager){
  532. // summary:
  533. // Update the columns width.
  534. // manager:
  535. // dojox.mdnd.AreaManager singleton
  536. // tags:
  537. // private
  538. //console.log("dojox.layout.GridContainer ::: _updateColumnsWidth");
  539. this.inherited(arguments);
  540. manager._dropMode.updateAreas(manager._areaList);
  541. },
  542. destroy: function(){
  543. dojo.unsubscribe(this._dropHandler);
  544. this.inherited(arguments);
  545. }
  546. });
  547. }