Rearrange.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. define("dojox/grid/enhanced/plugins/Rearrange", [
  2. "dojo/_base/kernel",
  3. "dojo/_base/lang",
  4. "dojo/_base/declare",
  5. "dojo/_base/array",
  6. "dojo/_base/connect",
  7. "../../EnhancedGrid",
  8. "../_Plugin",
  9. "./_RowMapLayer"
  10. ], function(dojo, lang, declare, array, connect, EnhancedGrid, _Plugin, _RowMapLayer){
  11. var Rearrange = declare("dojox.grid.enhanced.plugins.Rearrange", _Plugin, {
  12. // summary:
  13. // Provides a set of method to re-arrange the structure of grid.
  14. // name: String
  15. // plugin name
  16. name: "rearrange",
  17. constructor: function(grid, args){
  18. this.grid = grid;
  19. this.setArgs(args);
  20. var rowMapLayer = new _RowMapLayer(grid);
  21. dojox.grid.enhanced.plugins.wrap(grid, "_storeLayerFetch", rowMapLayer);
  22. },
  23. setArgs: function(args){
  24. this.args = lang.mixin(this.args || {}, args || {});
  25. this.args.setIdentifierForNewItem = this.args.setIdentifierForNewItem || function(v){return v;};
  26. },
  27. destroy: function(){
  28. this.inherited(arguments);
  29. this.grid.unwrap("rowmap");
  30. },
  31. onSetStore: function(store){
  32. this.grid.layer("rowmap").clearMapping();
  33. },
  34. _hasIdentity: function(points){
  35. var g = this.grid, s = g.store, cells = g.layout.cells;
  36. if(s.getFeatures()["dojo.data.api.Identity"]){
  37. if(array.some(points, function(point){
  38. return s.getIdentityAttributes(g._by_idx[point.r].item) == cells[point.c].field;
  39. })){
  40. return true;
  41. }
  42. }
  43. return false;
  44. },
  45. moveColumns: function(colsToMove, targetPos){
  46. // summary:
  47. // Move a set of columns to a given position.
  48. // tag:
  49. // public
  50. // colsToMove: Integer[]
  51. // Array of column indexes.
  52. // targetPos: Integer
  53. // The target position
  54. var g = this.grid,
  55. layout = g.layout,
  56. cells = layout.cells,
  57. colIndex, i, delta = 0,
  58. before = true, tmp = {}, mapping = {};
  59. colsToMove.sort(function(a, b){
  60. return a - b;
  61. });
  62. for(i = 0; i < colsToMove.length; ++i){
  63. tmp[colsToMove[i]] = i;
  64. if(colsToMove[i] < targetPos){
  65. ++delta;
  66. }
  67. }
  68. var leftCount = 0, rightCount = 0;
  69. var maxCol = Math.max(colsToMove[colsToMove.length - 1], targetPos);
  70. if(maxCol == cells.length){
  71. --maxCol;
  72. }
  73. var minCol = Math.min(colsToMove[0], targetPos);
  74. for(i = minCol; i <= maxCol; ++i){
  75. var j = tmp[i];
  76. if(j >= 0){
  77. mapping[i] = targetPos - delta + j;
  78. }else if(i < targetPos){
  79. mapping[i] = minCol + leftCount;
  80. ++leftCount;
  81. }else if(i >= targetPos){
  82. mapping[i] = targetPos + colsToMove.length - delta + rightCount;
  83. ++rightCount;
  84. }
  85. }
  86. //console.log("mapping:", mapping, ", colsToMove:", colsToMove,", target:", targetPos);
  87. delta = 0;
  88. if(targetPos == cells.length){
  89. --targetPos;
  90. before = false;
  91. }
  92. g._notRefreshSelection = true;
  93. for(i = 0; i < colsToMove.length; ++i){
  94. colIndex = colsToMove[i];
  95. if(colIndex < targetPos){
  96. colIndex -= delta;
  97. }
  98. ++delta;
  99. if(colIndex != targetPos){
  100. layout.moveColumn(cells[colIndex].view.idx, cells[targetPos].view.idx, colIndex, targetPos, before);
  101. cells = layout.cells;
  102. }
  103. if(targetPos <= colIndex){
  104. ++targetPos;
  105. }
  106. }
  107. delete g._notRefreshSelection;
  108. connect.publish("dojox/grid/rearrange/move/" + g.id, ["col", mapping, colsToMove]);
  109. },
  110. moveRows: function(rowsToMove, targetPos){
  111. // summary:
  112. // Move a set of rows to a given position
  113. // tag:
  114. // public
  115. // rowsToMove: Integer[]
  116. // Array of row indexes.
  117. // targetPos: Integer
  118. // The target position
  119. var g = this.grid,
  120. mapping = {},
  121. preRowsToMove = [],
  122. postRowsToMove = [],
  123. len = rowsToMove.length,
  124. i, r, k, arr, rowMap, lastPos;
  125. for(i = 0; i < len; ++i){
  126. r = rowsToMove[i];
  127. if(r >= targetPos){
  128. break;
  129. }
  130. preRowsToMove.push(r);
  131. }
  132. postRowsToMove = rowsToMove.slice(i);
  133. arr = preRowsToMove;
  134. len = arr.length;
  135. if(len){
  136. rowMap = {};
  137. array.forEach(arr, function(r){
  138. rowMap[r] = true;
  139. });
  140. mapping[arr[0]] = targetPos - len;
  141. for(k = 0, i = arr[k] + 1, lastPos = i - 1; i < targetPos; ++i){
  142. if(!rowMap[i]){
  143. mapping[i] = lastPos;
  144. ++lastPos;
  145. }else{
  146. ++k;
  147. mapping[i] = targetPos - len + k;
  148. }
  149. }
  150. }
  151. arr = postRowsToMove;
  152. len = arr.length;
  153. if(len){
  154. rowMap = {};
  155. array.forEach(arr, function(r){
  156. rowMap[r] = true;
  157. });
  158. mapping[arr[len - 1]] = targetPos + len - 1;
  159. for(k = len - 1, i = arr[k] - 1, lastPos = i + 1; i >= targetPos; --i){
  160. if(!rowMap[i]){
  161. mapping[i] = lastPos;
  162. --lastPos;
  163. }else{
  164. --k;
  165. mapping[i] = targetPos + k;
  166. }
  167. }
  168. }
  169. var tmpMapping = lang.clone(mapping);
  170. g.layer("rowmap").setMapping(mapping);
  171. g.forEachLayer(function(layer){
  172. if(layer.name() != "rowmap"){
  173. layer.invalidate();
  174. return true;
  175. }else{
  176. return false;
  177. }
  178. }, false);
  179. g.selection.selected = [];
  180. g._noInternalMapping = true;
  181. g._refresh();
  182. setTimeout(function(){
  183. connect.publish("dojox/grid/rearrange/move/" + g.id, ["row", tmpMapping, rowsToMove]);
  184. g._noInternalMapping = false;
  185. }, 0);
  186. },
  187. moveCells: function(cellsToMove, target){
  188. var g = this.grid,
  189. s = g.store;
  190. if(s.getFeatures()["dojo.data.api.Write"]){
  191. if(cellsToMove.min.row == target.min.row && cellsToMove.min.col == target.min.col){
  192. //Same position, no need to move
  193. return;
  194. }
  195. var cells = g.layout.cells,
  196. cnt = cellsToMove.max.row - cellsToMove.min.row + 1,
  197. r, c, tr, tc,
  198. sources = [], targets = [];
  199. for(r = cellsToMove.min.row, tr = target.min.row; r <= cellsToMove.max.row; ++r, ++tr){
  200. for(c = cellsToMove.min.col, tc = target.min.col; c <= cellsToMove.max.col; ++c, ++tc){
  201. while(cells[c] && cells[c].hidden){
  202. ++c;
  203. }
  204. while(cells[tc] && cells[tc].hidden){
  205. ++tc;
  206. }
  207. sources.push({
  208. "r": r,
  209. "c": c
  210. });
  211. targets.push({
  212. "r": tr,
  213. "c": tc,
  214. "v": cells[c].get(r, g._by_idx[r].item)
  215. });
  216. }
  217. }
  218. if(this._hasIdentity(sources.concat(targets))){
  219. console.warn("Can not write to identity!");
  220. return;
  221. }
  222. array.forEach(sources, function(point){
  223. s.setValue(g._by_idx[point.r].item, cells[point.c].field, "");
  224. });
  225. array.forEach(targets, function(point){
  226. s.setValue(g._by_idx[point.r].item, cells[point.c].field, point.v);
  227. });
  228. s.save({
  229. onComplete: function(){
  230. connect.publish("dojox/grid/rearrange/move/" + g.id, ["cell", {
  231. "from": cellsToMove,
  232. "to": target
  233. }]);
  234. }
  235. });
  236. }
  237. },
  238. copyCells: function(cellsToMove, target){
  239. var g = this.grid,
  240. s = g.store;
  241. if(s.getFeatures()["dojo.data.api.Write"]){
  242. if(cellsToMove.min.row == target.min.row && cellsToMove.min.col == target.min.col){
  243. return;
  244. }
  245. var cells = g.layout.cells,
  246. cnt = cellsToMove.max.row - cellsToMove.min.row + 1,
  247. r, c, tr, tc,
  248. targets = [];
  249. for(r = cellsToMove.min.row, tr = target.min.row; r <= cellsToMove.max.row; ++r, ++tr){
  250. for(c = cellsToMove.min.col, tc = target.min.col; c <= cellsToMove.max.col; ++c, ++tc){
  251. while(cells[c] && cells[c].hidden){
  252. ++c;
  253. }
  254. while(cells[tc] && cells[tc].hidden){
  255. ++tc;
  256. }
  257. targets.push({
  258. "r": tr,
  259. "c": tc,
  260. "v": cells[c].get(r, g._by_idx[r].item)
  261. });
  262. }
  263. }
  264. if(this._hasIdentity(targets)){
  265. console.warn("Can not write to identity!");
  266. return;
  267. }
  268. array.forEach(targets, function(point){
  269. s.setValue(g._by_idx[point.r].item, cells[point.c].field, point.v);
  270. });
  271. s.save({
  272. onComplete: function(){
  273. setTimeout(function(){
  274. connect.publish("dojox/grid/rearrange/copy/" + g.id, ["cell", {
  275. "from": cellsToMove,
  276. "to": target
  277. }]);
  278. }, 0);
  279. }
  280. });
  281. }
  282. },
  283. changeCells: function(sourceGrid, cellsToMove, target){
  284. var g = this.grid,
  285. s = g.store;
  286. if(s.getFeatures()["dojo.data.api.Write"]){
  287. var srcg = sourceGrid,
  288. cells = g.layout.cells,
  289. srccells = srcg.layout.cells,
  290. cnt = cellsToMove.max.row - cellsToMove.min.row + 1,
  291. r, c, tr, tc, targets = [];
  292. for(r = cellsToMove.min.row, tr = target.min.row; r <= cellsToMove.max.row; ++r, ++tr){
  293. for(c = cellsToMove.min.col, tc = target.min.col; c <= cellsToMove.max.col; ++c, ++tc){
  294. while(srccells[c] && srccells[c].hidden){
  295. ++c;
  296. }
  297. while(cells[tc] && cells[tc].hidden){
  298. ++tc;
  299. }
  300. targets.push({
  301. "r": tr,
  302. "c": tc,
  303. "v": srccells[c].get(r, srcg._by_idx[r].item)
  304. });
  305. }
  306. }
  307. if(this._hasIdentity(targets)){
  308. console.warn("Can not write to identity!");
  309. return;
  310. }
  311. array.forEach(targets, function(point){
  312. s.setValue(g._by_idx[point.r].item, cells[point.c].field, point.v);
  313. });
  314. s.save({
  315. onComplete: function(){
  316. connect.publish("dojox/grid/rearrange/change/" + g.id, ["cell", target]);
  317. }
  318. });
  319. }
  320. },
  321. clearCells: function(cellsToClear){
  322. var g = this.grid,
  323. s = g.store;
  324. if(s.getFeatures()["dojo.data.api.Write"]){
  325. var cells = g.layout.cells,
  326. cnt = cellsToClear.max.row - cellsToClear.min.row + 1,
  327. r, c, targets = [];
  328. for(r = cellsToClear.min.row; r <= cellsToClear.max.row; ++r){
  329. for(c = cellsToClear.min.col; c <= cellsToClear.max.col; ++c){
  330. while(cells[c] && cells[c].hidden){
  331. ++c;
  332. }
  333. targets.push({
  334. "r": r,
  335. "c": c
  336. });
  337. }
  338. }
  339. if(this._hasIdentity(targets)){
  340. console.warn("Can not write to identity!");
  341. return;
  342. }
  343. array.forEach(targets, function(point){
  344. s.setValue(g._by_idx[point.r].item, cells[point.c].field, "");
  345. });
  346. s.save({
  347. onComplete: function(){
  348. connect.publish("dojox/grid/rearrange/change/" + g.id, ["cell", cellsToClear]);
  349. }
  350. });
  351. }
  352. },
  353. insertRows: function(sourceGrid, rowsToMove, targetPos){
  354. try{
  355. var g = this.grid,
  356. s = g.store,
  357. rowCnt = g.rowCount,
  358. mapping = {},
  359. obj = {idx: 0},
  360. newRows = [], i,
  361. emptyTarget = targetPos < 0;
  362. _this = this;
  363. var len = rowsToMove.length;
  364. if(emptyTarget){
  365. targetPos = 0;
  366. }else{
  367. for(i = targetPos; i < g.rowCount; ++i){
  368. mapping[i] = i + len;
  369. }
  370. }
  371. if(s.getFeatures()['dojo.data.api.Write']){
  372. if(sourceGrid){
  373. var srcg = sourceGrid,
  374. srcs = srcg.store,
  375. thisItem, attrs;
  376. if(!emptyTarget){
  377. for(i = 0; !thisItem; ++i){
  378. thisItem = g._by_idx[i];
  379. }
  380. attrs = s.getAttributes(thisItem.item);
  381. }else{
  382. //If the target grid is empty, there is no way to retrieve attributes.
  383. //So try to get attrs from grid.layout.cells[], but this might not be right
  384. //since some fields may be missed(e.g ID fields), please use "setIdentifierForNewItem()"
  385. //to add those missed fields
  386. attrs = array.map(g.layout.cells, function(cell){
  387. return cell.field;
  388. });
  389. }
  390. var rowsToFetch = [];
  391. array.forEach(rowsToMove, function(rowIndex, i){
  392. var item = {};
  393. var srcItem = srcg._by_idx[rowIndex];
  394. if(srcItem){
  395. array.forEach(attrs, function(attr){
  396. item[attr] = srcs.getValue(srcItem.item, attr);
  397. });
  398. item = _this.args.setIdentifierForNewItem(item, s, rowCnt + obj.idx) || item;
  399. try{
  400. s.newItem(item);
  401. newRows.push(targetPos + i);
  402. mapping[rowCnt + obj.idx] = targetPos + i;
  403. ++obj.idx;
  404. }catch(e){
  405. console.log("insertRows newItem:",e,item);
  406. }
  407. }else{
  408. rowsToFetch.push(rowIndex);
  409. }
  410. });
  411. }else if(rowsToMove.length && lang.isObject(rowsToMove[0])){
  412. array.forEach(rowsToMove, function(rowData, i){
  413. var item = _this.args.setIdentifierForNewItem(rowData, s, rowCnt + obj.idx) || rowData;
  414. try{
  415. s.newItem(item);
  416. newRows.push(targetPos + i);
  417. mapping[rowCnt + obj.idx] = targetPos + i;
  418. ++obj.idx;
  419. }catch(e){
  420. console.log("insertRows newItem:",e,item);
  421. }
  422. });
  423. }else{
  424. return;
  425. }
  426. g.layer("rowmap").setMapping(mapping);
  427. s.save({
  428. onComplete: function(){
  429. g._refresh();
  430. setTimeout(function(){
  431. connect.publish("dojox/grid/rearrange/insert/" + g.id, ["row", newRows]);
  432. }, 0);
  433. }
  434. });
  435. }
  436. }catch(e){
  437. console.log("insertRows:",e);
  438. }
  439. },
  440. removeRows: function(rowsToRemove){
  441. var g = this.grid;
  442. var s = g.store;
  443. try{
  444. array.forEach(array.map(rowsToRemove, function(rowIndex){
  445. return g._by_idx[rowIndex];
  446. }), function(row){
  447. if(row){
  448. s.deleteItem(row.item);
  449. }
  450. });
  451. s.save({
  452. onComplete: function(){
  453. connect.publish("dojox/grid/rearrange/remove/" + g.id, ["row", rowsToRemove]);
  454. }
  455. });
  456. }catch(e){
  457. console.log("removeRows:",e);
  458. }
  459. },
  460. _getPageInfo: function(){
  461. // summary:
  462. // Find pages that contain visible rows
  463. // return: Object
  464. // {topPage: xx, bottomPage: xx, invalidPages: [xx,xx,...]}
  465. var scroller = this.grid.scroller,
  466. topPage = scroller.page,
  467. bottomPage = scroller.page,
  468. firstVisibleRow = scroller.firstVisibleRow,
  469. lastVisibleRow = scroller.lastVisibleRow,
  470. rowsPerPage = scroller.rowsPerPage,
  471. renderedPages = scroller.pageNodes[0],
  472. topRow, bottomRow, matched,
  473. invalidPages = [];
  474. array.forEach(renderedPages, function(page, pageIndex){
  475. if(!page){ return; }
  476. matched = false;
  477. topRow = pageIndex * rowsPerPage;
  478. bottomRow = (pageIndex + 1) * rowsPerPage - 1;
  479. if(firstVisibleRow >= topRow && firstVisibleRow <= bottomRow){
  480. topPage = pageIndex;
  481. matched = true;
  482. }
  483. if(lastVisibleRow >= topRow && lastVisibleRow <= bottomRow){
  484. bottomPage = pageIndex;
  485. matched = true;
  486. }
  487. if(!matched && (topRow > lastVisibleRow || bottomRow < firstVisibleRow)){
  488. invalidPages.push(pageIndex);
  489. }
  490. });
  491. return {topPage: topPage, bottomPage: bottomPage, invalidPages: invalidPages};
  492. }
  493. });
  494. EnhancedGrid.registerPlugin(Rearrange/*name:'rearrange'*/);
  495. return Rearrange;
  496. });