PageRenderTime 57ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/PhoneGapDemoClient/dojo/dojox/grid/enhanced/plugins/Selector.js

https://bitbucket.org/sebsto/phonegapdemo
JavaScript | 1475 lines | 1123 code | 33 blank | 319 comment | 289 complexity | 8ea0a636dab546d9279ced63da5bfdcf MD5 | raw file
Possible License(s): LGPL-2.0, BSD-3-Clause, MIT
  1. //>>built
  2. define("dojox/grid/enhanced/plugins/Selector", [
  3. "dojo/_base/kernel",
  4. "dojo/_base/lang",
  5. "dojo/_base/declare",
  6. "dojo/_base/array",
  7. "dojo/_base/event",
  8. "dojo/keys",
  9. "dojo/query",
  10. "dojo/_base/html",
  11. "dojo/_base/window",
  12. "dijit/focus",
  13. "../../_RowSelector",
  14. "../_Plugin",
  15. "../../EnhancedGrid",
  16. "../../cells/_base",
  17. "./AutoScroll"
  18. ], function(dojo, lang, declare, array, event, keys, query, html, win, dijitFocus, _RowSelector, _Plugin, EnhancedGrid){
  19. /*=====
  20. dojo.declare("__SelectItem", null,{
  21. // summary:
  22. // An abstract representation of an item.
  23. });
  24. dojo.declare("__SelectCellItem", __SelectItem,{
  25. // summary:
  26. // An abstract representation of a cell.
  27. // row: Integer
  28. // Row index of this cell
  29. row: 0,
  30. // col: Integer
  31. // Column index of this cell
  32. col: 0
  33. });
  34. dojo.declare("__SelectRowItem", __SelectItem,{
  35. // summary:
  36. // An abstract representation of a row.
  37. // row: Integer
  38. // Row index of this row
  39. row: 0,
  40. // except: Integer[]
  41. // An array of column indexes of all the unselected cells in this row.
  42. except: []
  43. });
  44. dojo.declare("__SelectColItem", __SelectItem,{
  45. // summary:
  46. // An abstract representation of a column.
  47. // col: Integer
  48. // Column index of this column
  49. col: 0,
  50. // except: Integer[]
  51. // An array of row indexes of all the unselected cells in this column.
  52. except: []
  53. });
  54. =====*/
  55. var DISABLED = 0, SINGLE = 1, MULTI = 2,
  56. _theOther = { col: "row", row: "col" },
  57. _inRange = function(type, value, start, end, halfClose){
  58. if(type !== "cell"){
  59. value = value[type];
  60. start = start[type];
  61. end = end[type];
  62. if(typeof value !== "number" || typeof start !== "number" || typeof end !== "number"){
  63. return false;
  64. }
  65. return halfClose ? ((value >= start && value < end) || (value > end && value <= start))
  66. : ((value >= start && value <= end) || (value >= end && value <= start));
  67. }else{
  68. return _inRange("col", value, start, end, halfClose) && _inRange("row", value, start, end, halfClose);
  69. }
  70. },
  71. _isEqual = function(type, v1, v2){
  72. try{
  73. if(v1 && v2){
  74. switch(type){
  75. case "col": case "row":
  76. return v1[type] == v2[type] && typeof v1[type] == "number" &&
  77. !(_theOther[type] in v1) && !(_theOther[type] in v2);
  78. case "cell":
  79. return v1.col == v2.col && v1.row == v2.row && typeof v1.col == "number" && typeof v1.row == "number";
  80. }
  81. }
  82. }catch(e){}
  83. return false;
  84. },
  85. _stopEvent = function(evt){
  86. try{
  87. if(evt && evt.preventDefault){
  88. event.stop(evt);
  89. }
  90. }catch(e){}
  91. },
  92. _createItem = function(type, rowIndex, colIndex){
  93. switch(type){
  94. case "col":
  95. return {
  96. "col": typeof colIndex == "undefined" ? rowIndex : colIndex,
  97. "except": []
  98. };
  99. case "row":
  100. return {
  101. "row": rowIndex,
  102. "except": []
  103. };
  104. case "cell":
  105. return {
  106. "row": rowIndex,
  107. "col": colIndex
  108. };
  109. }
  110. return null;
  111. };
  112. var Selector = declare("dojox.grid.enhanced.plugins.Selector", _Plugin, {
  113. // summary:
  114. // Provides standard extended selection for grid.
  115. // Supports mouse/keyboard selection, multi-selection, and de-selection.
  116. // Acceptable plugin parameters:
  117. // The whole plugin parameter object is a config object passed to the setupConfig function.
  118. //
  119. // Acceptable cell parameters defined in layout:
  120. // 1. notselectable: boolean
  121. // Whether this column is (and all the cells in it are) selectable.
  122. // name: String
  123. // plugin name
  124. name: "selector",
  125. // noClear: Boolean
  126. // Not to clear rows selected by IndirectSelection.
  127. /*
  128. // _config: null,
  129. // _enabled: true,
  130. // _selecting: {
  131. // row: false,
  132. // col: false,
  133. // cell: false
  134. // },
  135. // _selected: {
  136. // row: [],
  137. // col: [],
  138. // cell: []
  139. // },
  140. // _startPoint: {},
  141. // _currentPoint: {},
  142. // _lastAnchorPoint: {},
  143. // _lastEndPoint: {},
  144. // _lastSelectedAnchorPoint: {},
  145. // _lastSelectedEndPoint: {},
  146. // _keyboardSelect: {
  147. // row: 0,
  148. // col: 0,
  149. // cell: 0
  150. // },
  151. // _curType: null,
  152. // _lastType: null,
  153. // _usingKeyboard: false,
  154. // _toSelect: true,
  155. */
  156. constructor: function(grid, args){
  157. this.grid = grid;
  158. this._config = {
  159. row: MULTI,
  160. col: MULTI,
  161. cell: MULTI
  162. };
  163. this.noClear = args && args.noClear;
  164. this.setupConfig(args);
  165. if(grid.selectionMode === "single"){
  166. this._config.row = SINGLE;
  167. }
  168. this._enabled = true;
  169. this._selecting = {};
  170. this._selected = {
  171. "col": [],
  172. "row": [],
  173. "cell": []
  174. };
  175. this._startPoint = {};
  176. this._currentPoint = {};
  177. this._lastAnchorPoint = {};
  178. this._lastEndPoint = {};
  179. this._lastSelectedAnchorPoint = {};
  180. this._lastSelectedEndPoint = {};
  181. this._keyboardSelect = {};
  182. this._lastType = null;
  183. this._selectedRowModified = {};
  184. this._hacks();
  185. this._initEvents();
  186. this._initAreas();
  187. this._mixinGrid();
  188. },
  189. destroy: function(){
  190. this.inherited(arguments);
  191. },
  192. //------------public--------------------
  193. setupConfig: function(config){
  194. // summary:
  195. // Set selection mode for row/col/cell.
  196. // config: Object
  197. // An object with the following structure (all properties are optional):
  198. // {
  199. // //Default is "multi", all other values are same as "multi".
  200. // row: false|"disabled"|"single",
  201. // col: false|"disabled"|"single",
  202. // cell: false|"disabled"|"single"
  203. // }
  204. if(!config || !lang.isObject(config)){
  205. return;
  206. }
  207. var types = ["row", "col", "cell"];
  208. for(var type in config){
  209. if(array.indexOf(types, type) >= 0){
  210. if(!config[type] || config[type] == "disabled"){
  211. this._config[type] = DISABLED;
  212. }else if(config[type] == "single"){
  213. this._config[type] = SINGLE;
  214. }else{
  215. this._config[type] = MULTI;
  216. }
  217. }
  218. }
  219. //Have to set mode to default grid selection.
  220. var mode = ["none","single","extended"][this._config.row];
  221. this.grid.selection.setMode(mode);
  222. },
  223. isSelected: function(type, rowIndex, colIndex){
  224. // summary:
  225. // Check whether a location (a cell, a column or a row) is selected.
  226. // tag:
  227. // public
  228. // type: String
  229. // "row" or "col" or "cell"
  230. // rowIndex: Integer
  231. // If type is "row" or "cell", this is the row index.
  232. // If type if "col", this is the column index.
  233. // colIndex: Integer?
  234. // Only valid when type is "cell"
  235. // return: Boolean
  236. // true if selected, false if not. If cell is covered by a selected column, it's selected.
  237. return this._isSelected(type, _createItem(type, rowIndex, colIndex));
  238. },
  239. toggleSelect: function(type, rowIndex, colIndex){
  240. this._startSelect(type, _createItem(type, rowIndex, colIndex), this._config[type] === MULTI, false, false, !this.isSelected(type, rowIndex, colIndex));
  241. this._endSelect(type);
  242. },
  243. select: function(type, rowIndex, colIndex){
  244. // summary:
  245. // Select a location (a cell, a column or a row).
  246. // tag:
  247. // public
  248. // type: String
  249. // "row" or "col" or "cell"
  250. // rowIndex: Integer
  251. // If type is "row" or "cell", this is the row index.
  252. // If type if "col", this is the column index.
  253. // colIndex: Integer?
  254. // Only valid when type is "cell"
  255. if(!this.isSelected(type, rowIndex, colIndex)){
  256. this.toggleSelect(type, rowIndex, colIndex);
  257. }
  258. },
  259. deselect: function(type, rowIndex, colIndex){
  260. if(this.isSelected(type, rowIndex, colIndex)){
  261. this.toggleSelect(type, rowIndex, colIndex);
  262. }
  263. },
  264. selectRange: function(type, start, end, toSelect){
  265. // summary:
  266. // Select a continuous range (a block of cells, a set of continuous columns or rows)
  267. // tag:
  268. // public
  269. // type: String
  270. // "row" or "col" or "cell"
  271. // start: Integer | Object
  272. // If type is "row" or "col", this is the index of the starting row or column.
  273. // If type if "cell", this is the left-top cell of the range.
  274. // end: Integer | Object
  275. // If type is "row" or "col", this is the index of the ending row or column.
  276. // If type if "cell", this is the right-bottom cell of the range.
  277. this.grid._selectingRange = true;
  278. var startPoint = type == "cell" ? _createItem(type, start.row, start.col) : _createItem(type, start),
  279. endPoint = type == "cell" ? _createItem(type, end.row, end.col) : _createItem(type, end);
  280. this._startSelect(type, startPoint, false, false, false, toSelect);
  281. this._highlight(type, endPoint, toSelect === undefined ? true : toSelect);
  282. this._endSelect(type);
  283. this.grid._selectingRange = false;
  284. },
  285. clear: function(type){
  286. // summary:
  287. // Clear all selections.
  288. // tag:
  289. // public
  290. // type: String?
  291. // "row" or "col" or "cell". If omitted, clear all.
  292. this._clearSelection(type || "all");
  293. },
  294. isSelecting: function(type){
  295. // summary:
  296. // Check whether the user is currently selecting something.
  297. // tag:
  298. // public
  299. // type: String
  300. // "row" or "col" or "cell"
  301. // return: Boolean
  302. // true if is selection, false otherwise.
  303. if(typeof type == "undefined"){
  304. return this._selecting.col || this._selecting.row || this._selecting.cell;
  305. }
  306. return this._selecting[type];
  307. },
  308. selectEnabled: function(toEnable){
  309. // summary:
  310. // Turn on/off this selection functionality if *toEnable* is provided.
  311. // Check whether this selection functionality is enabled if nothing is passed in.
  312. // tag:
  313. // public
  314. // toEnable: Boolean?
  315. // To enable or not. Optional.
  316. // return: Boolean | undefined
  317. // Enabled or not.
  318. if(typeof toEnable != "undefined" && !this.isSelecting()){
  319. this._enabled = !!toEnable;
  320. }
  321. return this._enabled;
  322. },
  323. getSelected: function(type, includeExceptions){
  324. // summary:
  325. // Get an array of selected locations.
  326. // tag:
  327. // public
  328. // type: String
  329. // "row" or "col" or "cell"
  330. // includeExceptions: Boolean
  331. // Only meaningful for rows/columns. If true, all selected rows/cols, even they are partly selected, are all returned.
  332. // return: __SelectItem[]
  333. switch(type){
  334. case "cell":
  335. return array.map(this._selected[type], function(item){ return item; });
  336. case "col": case "row":
  337. return array.map(includeExceptions ? this._selected[type]
  338. : array.filter(this._selected[type], function(item){
  339. return item.except.length === 0;
  340. }), function(item){
  341. return includeExceptions ? item : item[type];
  342. });
  343. }
  344. return [];
  345. },
  346. getSelectedCount: function(type, includeExceptions){
  347. // summary:
  348. // Get the number of selected items.
  349. // tag:
  350. // public
  351. // type: String
  352. // "row" or "col" or "cell"
  353. // includeExceptions: Boolean
  354. // Only meaningful for rows/columns. If true, all selected rows/cols, even they are partly selected, are all returned.
  355. // return: Integer
  356. // The number of selected items.
  357. switch(type){
  358. case "cell":
  359. return this._selected[type].length;
  360. case "col": case "row":
  361. return (includeExceptions ? this._selected[type]
  362. : array.filter(this._selected[type], function(item){
  363. return item.except.length === 0;
  364. })).length;
  365. }
  366. return 0;
  367. },
  368. getSelectedType: function(){
  369. // summary:
  370. // Get the type of selected items.
  371. // tag:
  372. // public
  373. // return: String
  374. // "row" or "col" or "cell", or any mix of these (separator is | ).
  375. var s = this._selected;
  376. return ["", "cell", "row", "row|cell",
  377. "col", "col|cell", "col|row", "col|row|cell"
  378. ][(!!s.cell.length) | (!!s.row.length << 1) | (!!s.col.length << 2)];
  379. },
  380. getLastSelectedRange: function(type){
  381. // summary:
  382. // Get last selected range of the given type.
  383. // tag:
  384. // public
  385. // return: Object
  386. // {start: __SelectItem, end: __SelectItem}
  387. // return null if nothing is selected.
  388. return this._lastAnchorPoint[type] ? {
  389. "start": this._lastAnchorPoint[type],
  390. "end": this._lastEndPoint[type]
  391. } : null;
  392. },
  393. //--------------------------private----------------------------
  394. _hacks: function(){
  395. // summary:
  396. // Complete the event system of grid, hack some grid functions to prevent default behavior.
  397. var g = this.grid;
  398. var doContentMouseUp = function(e){
  399. if(e.cellNode){
  400. g.onMouseUp(e);
  401. }
  402. g.onMouseUpRow(e);
  403. };
  404. var mouseUp = lang.hitch(g, "onMouseUp");
  405. var mouseDown = lang.hitch(g, "onMouseDown");
  406. var doRowSelectorFocus = function(e){
  407. e.cellNode.style.border = "solid 1px";
  408. };
  409. array.forEach(g.views.views, function(view){
  410. view.content.domouseup = doContentMouseUp;
  411. view.header.domouseup = mouseUp;
  412. if(view.declaredClass == "dojox.grid._RowSelector"){
  413. view.domousedown = mouseDown;
  414. view.domouseup = mouseUp;
  415. view.dofocus = doRowSelectorFocus;
  416. }
  417. });
  418. //Disable default selection.
  419. g.selection.clickSelect = function(){};
  420. this._oldDeselectAll = g.selection.deselectAll;
  421. var _this = this;
  422. g.selection.selectRange = function(from, to){
  423. _this.selectRange("row", from, to, true);
  424. if(g.selection.preserver){
  425. g.selection.preserver._updateMapping(true, true, false, from, to);
  426. }
  427. g.selection.onChanged();
  428. };
  429. g.selection.deselectRange = function(from, to){
  430. _this.selectRange("row", from, to, false);
  431. if(g.selection.preserver){
  432. g.selection.preserver._updateMapping(true, false, false, from, to);
  433. }
  434. g.selection.onChanged();
  435. };
  436. g.selection.deselectAll = function(){
  437. g._selectingRange = true;
  438. _this._oldDeselectAll.apply(g.selection, arguments);
  439. _this._clearSelection("all");
  440. g._selectingRange = false;
  441. if(g.selection.preserver){
  442. g.selection.preserver._updateMapping(true, false, true);
  443. }
  444. g.selection.onChanged();
  445. };
  446. var rowSelector = g.views.views[0];
  447. //The default function re-write the whole className, so can not insert any other classes.
  448. if(rowSelector instanceof _RowSelector){
  449. rowSelector.doStyleRowNode = function(inRowIndex, inRowNode){
  450. html.removeClass(inRowNode, "dojoxGridRow");
  451. html.addClass(inRowNode, "dojoxGridRowbar");
  452. html.addClass(inRowNode, "dojoxGridNonNormalizedCell");
  453. html.toggleClass(inRowNode, "dojoxGridRowbarOver", g.rows.isOver(inRowIndex));
  454. html.toggleClass(inRowNode, "dojoxGridRowbarSelected", !!g.selection.isSelected(inRowIndex));
  455. };
  456. }
  457. this.connect(g, "updateRow", function(rowIndex){
  458. array.forEach(g.layout.cells, function(cell){
  459. if(this.isSelected("cell", rowIndex, cell.index)){
  460. this._highlightNode(cell.getNode(rowIndex), true);
  461. }
  462. }, this);
  463. });
  464. },
  465. _mixinGrid: function(){
  466. // summary:
  467. // Expose events to grid.
  468. var g = this.grid;
  469. g.setupSelectorConfig = lang.hitch(this, this.setupConfig);
  470. g.onStartSelect = function(){};
  471. g.onEndSelect = function(){};
  472. g.onStartDeselect = function(){};
  473. g.onEndDeselect = function(){};
  474. g.onSelectCleared = function(){};
  475. },
  476. _initEvents: function(){
  477. // summary:
  478. // Connect events, create event handlers.
  479. var g = this.grid,
  480. _this = this,
  481. dp = lang.partial,
  482. starter = function(type, e){
  483. if(type === "row"){
  484. _this._isUsingRowSelector = true;
  485. }
  486. //only left mouse button can select.
  487. if(_this.selectEnabled() && _this._config[type] && e.button != 2){
  488. if(_this._keyboardSelect.col || _this._keyboardSelect.row || _this._keyboardSelect.cell){
  489. _this._endSelect("all");
  490. _this._keyboardSelect.col = _this._keyboardSelect.row = _this._keyboardSelect.cell = 0;
  491. }
  492. if(_this._usingKeyboard){
  493. _this._usingKeyboard = false;
  494. }
  495. var target = _createItem(type, e.rowIndex, e.cell && e.cell.index);
  496. _this._startSelect(type, target, e.ctrlKey, e.shiftKey);
  497. }
  498. },
  499. ender = lang.hitch(this, "_endSelect");
  500. this.connect(g, "onHeaderCellMouseDown", dp(starter, "col"));
  501. this.connect(g, "onHeaderCellMouseUp", dp(ender, "col"));
  502. this.connect(g, "onRowSelectorMouseDown", dp(starter, "row"));
  503. this.connect(g, "onRowSelectorMouseUp", dp(ender, "row"));
  504. this.connect(g, "onCellMouseDown", function(e){
  505. if(e.cell && e.cell.isRowSelector){ return; }
  506. if(g.singleClickEdit){
  507. _this._singleClickEdit = true;
  508. g.singleClickEdit = false;
  509. }
  510. starter(_this._config["cell"] == DISABLED ? "row" : "cell", e);
  511. });
  512. this.connect(g, "onCellMouseUp", function(e){
  513. if(_this._singleClickEdit){
  514. delete _this._singleClickEdit;
  515. g.singleClickEdit = true;
  516. }
  517. ender("all", e);
  518. });
  519. this.connect(g, "onCellMouseOver", function(e){
  520. if(_this._curType != "row" && _this._selecting[_this._curType] && _this._config[_this._curType] == MULTI){
  521. _this._highlight("col", _createItem("col", e.cell.index), _this._toSelect);
  522. if(!_this._keyboardSelect.cell){
  523. _this._highlight("cell", _createItem("cell", e.rowIndex, e.cell.index), _this._toSelect);
  524. }
  525. }
  526. });
  527. this.connect(g, "onHeaderCellMouseOver", function(e){
  528. if(_this._selecting.col && _this._config.col == MULTI){
  529. _this._highlight("col", _createItem("col", e.cell.index), _this._toSelect);
  530. }
  531. });
  532. this.connect(g, "onRowMouseOver", function(e){
  533. if(_this._selecting.row && _this._config.row == MULTI){
  534. _this._highlight("row", _createItem("row", e.rowIndex), _this._toSelect);
  535. }
  536. });
  537. //When row order has changed in a unpredictable way (sorted or filtered), map the new rowindex.
  538. this.connect(g, "onSelectedById", "_onSelectedById");
  539. //When the grid refreshes, all those selected should still appear selected.
  540. this.connect(g, "_onFetchComplete", function(){
  541. //console.debug("refresh after buildPage:", g._notRefreshSelection);
  542. if(!g._notRefreshSelection){
  543. this._refreshSelected(true);
  544. }
  545. });
  546. //Small scroll might not refresh the grid.
  547. this.connect(g.scroller, "buildPage", function(){
  548. //console.debug("refresh after buildPage:", g._notRefreshSelection);
  549. if(!g._notRefreshSelection){
  550. this._refreshSelected(true);
  551. }
  552. });
  553. //Whenever the mouse is up, end selecting.
  554. this.connect(win.doc, "onmouseup", dp(ender, "all"));
  555. //If autoscroll is enabled, connect to it.
  556. this.connect(g, "onEndAutoScroll", function(isVertical, isForward, view, target){
  557. var selectCell = _this._selecting.cell,
  558. type, current, dir = isForward ? 1 : -1;
  559. if(isVertical && (selectCell || _this._selecting.row)){
  560. type = selectCell ? "cell" : "row";
  561. current = _this._currentPoint[type];
  562. _this._highlight(type, _createItem(type, current.row + dir, current.col), _this._toSelect);
  563. }else if(!isVertical && (selectCell || _this._selecting.col)){
  564. type = selectCell ? "cell" : "col";
  565. current = _this._currentPoint[type];
  566. _this._highlight(type, _createItem(type, current.row, target), _this._toSelect);
  567. }
  568. });
  569. //If the grid is changed, selection should be consistent.
  570. this.subscribe("dojox/grid/rearrange/move/" + g.id, "_onInternalRearrange");
  571. this.subscribe("dojox/grid/rearrange/copy/" + g.id, "_onInternalRearrange");
  572. this.subscribe("dojox/grid/rearrange/change/" + g.id, "_onExternalChange");
  573. this.subscribe("dojox/grid/rearrange/insert/" + g.id, "_onExternalChange");
  574. this.subscribe("dojox/grid/rearrange/remove/" + g.id, "clear");
  575. //have to also select when the grid's default select is used.
  576. this.connect(g, "onSelected", function(rowIndex){
  577. if(this._selectedRowModified && this._isUsingRowSelector){
  578. delete this._selectedRowModified;
  579. }else if(!this.grid._selectingRange){
  580. this.select("row", rowIndex);
  581. }
  582. });
  583. this.connect(g, "onDeselected", function(rowIndex){
  584. if(this._selectedRowModified && this._isUsingRowSelector){
  585. delete this._selectedRowModified;
  586. }else if(!this.grid._selectingRange){
  587. this.deselect("row", rowIndex);
  588. }
  589. });
  590. },
  591. _onSelectedById: function(id, newIndex, isSelected){
  592. if(this.grid._noInternalMapping){
  593. return;
  594. }
  595. var pointSet = [this._lastAnchorPoint.row, this._lastEndPoint.row,
  596. this._lastSelectedAnchorPoint.row, this._lastSelectedEndPoint.row];
  597. pointSet = pointSet.concat(this._selected.row);
  598. var found = false;
  599. array.forEach(pointSet, function(item){
  600. if(item){
  601. if(item.id === id){
  602. found = true;
  603. item.row = newIndex;
  604. }else if(item.row === newIndex && item.id){
  605. item.row = -1;
  606. }
  607. }
  608. });
  609. if(!found && isSelected){
  610. array.some(this._selected.row, function(item){
  611. if(item && !item.id && !item.except.length){
  612. item.id = id;
  613. item.row = newIndex;
  614. return true;
  615. }
  616. return false;
  617. });
  618. }
  619. found = false;
  620. pointSet = [this._lastAnchorPoint.cell, this._lastEndPoint.cell,
  621. this._lastSelectedAnchorPoint.cell, this._lastSelectedEndPoint.cell];
  622. pointSet = pointSet.concat(this._selected.cell);
  623. array.forEach(pointSet, function(item){
  624. if(item){
  625. if(item.id === id){
  626. found = true;
  627. item.row = newIndex;
  628. }else if(item.row === newIndex && item.id){
  629. item.row = -1;
  630. }
  631. }
  632. });
  633. },
  634. onSetStore: function(){
  635. this._clearSelection("all");
  636. },
  637. _onInternalRearrange: function(type, mapping){
  638. try{
  639. //The column can not refresh it self!
  640. this._refresh("col", false);
  641. array.forEach(this._selected.row, function(item){
  642. array.forEach(this.grid.layout.cells, function(cell){
  643. this._highlightNode(cell.getNode(item.row), false);
  644. }, this);
  645. }, this);
  646. //The rowbar must be cleaned manually
  647. query(".dojoxGridRowSelectorSelected").forEach(function(node){
  648. html.removeClass(node, "dojoxGridRowSelectorSelected");
  649. html.removeClass(node, "dojoxGridRowSelectorSelectedUp");
  650. html.removeClass(node, "dojoxGridRowSelectorSelectedDown");
  651. });
  652. var cleanUp = function(item){
  653. if(item){
  654. delete item.converted;
  655. }
  656. },
  657. pointSet = [this._lastAnchorPoint[type], this._lastEndPoint[type],
  658. this._lastSelectedAnchorPoint[type], this._lastSelectedEndPoint[type]];
  659. if(type === "cell"){
  660. this.selectRange("cell", mapping.to.min, mapping.to.max);
  661. var cells = this.grid.layout.cells;
  662. array.forEach(pointSet, function(item){
  663. if(item.converted){ return; }
  664. for(var r = mapping.from.min.row, tr = mapping.to.min.row; r <= mapping.from.max.row; ++r, ++tr){
  665. for(var c = mapping.from.min.col, tc = mapping.to.min.col; c <= mapping.from.max.col; ++c, ++tc){
  666. while(cells[c].hidden){ ++c; }
  667. while(cells[tc].hidden){ ++tc; }
  668. if(item.row == r && item.col == c){
  669. //console.log('mapping found: (', item.row, ",",item.col,") to (", tr, ",", tc,")");
  670. item.row = tr;
  671. item.col = tc;
  672. item.converted = true;
  673. return;
  674. }
  675. }
  676. }
  677. });
  678. }else{
  679. pointSet = this._selected.cell.concat(this._selected[type]).concat(pointSet).concat(
  680. [this._lastAnchorPoint.cell, this._lastEndPoint.cell,
  681. this._lastSelectedAnchorPoint.cell, this._lastSelectedEndPoint.cell]);
  682. array.forEach(pointSet, function(item){
  683. if(item && !item.converted){
  684. var from = item[type];
  685. if(from in mapping){
  686. item[type] = mapping[from];
  687. }
  688. item.converted = true;
  689. }
  690. });
  691. array.forEach(this._selected[_theOther[type]], function(item){
  692. for(var i = 0, len = item.except.length; i < len; ++i){
  693. var from = item.except[i];
  694. if(from in mapping){
  695. item.except[i] = mapping[from];
  696. }
  697. }
  698. });
  699. }
  700. array.forEach(pointSet, cleanUp);
  701. this._refreshSelected(true);
  702. this._focusPoint(type, this._lastEndPoint);
  703. }catch(e){
  704. console.warn("Selector._onInternalRearrange() error",e);
  705. }
  706. },
  707. _onExternalChange: function(type, target){
  708. var start = type == "cell" ? target.min : target[0],
  709. end = type == "cell" ? target.max : target[target.length - 1];
  710. this.selectRange(type, start, end);
  711. },
  712. _refresh: function(type, toHighlight){
  713. if(!this._keyboardSelect[type]){
  714. array.forEach(this._selected[type], function(item){
  715. this._highlightSingle(type, toHighlight, item, undefined, true);
  716. }, this);
  717. }
  718. },
  719. _refreshSelected: function(){
  720. this._refresh("col", true);
  721. this._refresh("row", true);
  722. this._refresh("cell", true);
  723. },
  724. _initAreas: function(){
  725. var g = this.grid, f = g.focus, _this = this,
  726. keyboardSelectReady = 1, duringKeyboardSelect = 2,
  727. onmove = function(type, createNewEnd, rowStep, colStep, evt){
  728. //Keyboard swipe selection is SHIFT + Direction Keys.
  729. var ks = _this._keyboardSelect;
  730. //Tricky, rely on valid status not being 0.
  731. if(evt.shiftKey && ks[type]){
  732. if(ks[type] === keyboardSelectReady){
  733. if(type === "cell"){
  734. var item = _this._lastEndPoint[type];
  735. if(f.cell != g.layout.cells[item.col + colStep] || f.rowIndex != item.row + rowStep){
  736. ks[type] = 0;
  737. return;
  738. }
  739. }
  740. //If selecting is not started, start it
  741. _this._startSelect(type, _this._lastAnchorPoint[type], true, false, true);
  742. _this._highlight(type, _this._lastEndPoint[type], _this._toSelect);
  743. ks[type] = duringKeyboardSelect;
  744. }
  745. //Highlight to the new end point.
  746. var newEnd = createNewEnd(type, rowStep, colStep, evt);
  747. if(_this._isValid(type, newEnd, g)){
  748. _this._highlight(type, newEnd, _this._toSelect);
  749. }
  750. _stopEvent(evt);
  751. }
  752. },
  753. onkeydown = function(type, getTarget, evt, isBubble){
  754. if(isBubble && _this.selectEnabled() && _this._config[type] != DISABLED){
  755. switch(evt.keyCode){
  756. case keys.SPACE:
  757. //Keyboard single point selection is SPACE.
  758. _this._startSelect(type, getTarget(), evt.ctrlKey, evt.shiftKey);
  759. _this._endSelect(type);
  760. break;
  761. case keys.SHIFT:
  762. //Keyboard swipe selection starts with SHIFT.
  763. if(_this._config[type] == MULTI && _this._isValid(type, _this._lastAnchorPoint[type], g)){
  764. //End last selection if any.
  765. _this._endSelect(type);
  766. _this._keyboardSelect[type] = keyboardSelectReady;
  767. _this._usingKeyboard = true;
  768. }
  769. }
  770. }
  771. },
  772. onkeyup = function(type, evt, isBubble){
  773. if(isBubble && evt.keyCode == keys.SHIFT && _this._keyboardSelect[type]){
  774. _this._endSelect(type);
  775. _this._keyboardSelect[type] = 0;
  776. }
  777. };
  778. //TODO: this area "rowHeader" should be put outside, same level as header/content.
  779. if(g.views.views[0] instanceof _RowSelector){
  780. this._lastFocusedRowBarIdx = 0;
  781. f.addArea({
  782. name:"rowHeader",
  783. onFocus: function(evt, step){
  784. var view = g.views.views[0];
  785. if(view instanceof _RowSelector){
  786. var rowBarNode = view.getCellNode(_this._lastFocusedRowBarIdx, 0);
  787. if(rowBarNode){
  788. html.toggleClass(rowBarNode, f.focusClass, false);
  789. }
  790. //evt might not be real event, it may be a mock object instead.
  791. if(evt && "rowIndex" in evt){
  792. if(evt.rowIndex >= 0){
  793. _this._lastFocusedRowBarIdx = evt.rowIndex;
  794. }else if(!_this._lastFocusedRowBarIdx){
  795. _this._lastFocusedRowBarIdx = 0;
  796. }
  797. }
  798. rowBarNode = view.getCellNode(_this._lastFocusedRowBarIdx, 0);
  799. if(rowBarNode){
  800. dijitFocus.focus(rowBarNode);
  801. html.toggleClass(rowBarNode, f.focusClass, true);
  802. }
  803. f.rowIndex = _this._lastFocusedRowBarIdx;
  804. _stopEvent(evt);
  805. return true;
  806. }
  807. return false;
  808. },
  809. onBlur: function(evt, step){
  810. var view = g.views.views[0];
  811. if(view instanceof _RowSelector){
  812. var rowBarNode = view.getCellNode(_this._lastFocusedRowBarIdx, 0);
  813. if(rowBarNode){
  814. html.toggleClass(rowBarNode, f.focusClass, false);
  815. }
  816. _stopEvent(evt);
  817. }
  818. return true;
  819. },
  820. onMove: function(rowStep, colStep, evt){
  821. var view = g.views.views[0];
  822. if(rowStep && view instanceof _RowSelector){
  823. var next = _this._lastFocusedRowBarIdx + rowStep;
  824. if(next >= 0 && next < g.rowCount){
  825. //TODO: these logic require a better Scroller.
  826. _stopEvent(evt);
  827. var rowBarNode = view.getCellNode(_this._lastFocusedRowBarIdx, 0);
  828. html.toggleClass(rowBarNode, f.focusClass, false);
  829. //If the row is not fetched, fetch it.
  830. var sc = g.scroller;
  831. var lastPageRow = sc.getLastPageRow(sc.page);
  832. var rc = g.rowCount - 1, row = Math.min(rc, next);
  833. if(next > lastPageRow){
  834. g.setScrollTop(g.scrollTop + sc.findScrollTop(row) - sc.findScrollTop(_this._lastFocusedRowBarIdx));
  835. }
  836. //Now we have fetched the row.
  837. rowBarNode = view.getCellNode(next, 0);
  838. dijitFocus.focus(rowBarNode);
  839. html.toggleClass(rowBarNode, f.focusClass, true);
  840. _this._lastFocusedRowBarIdx = next;
  841. //If the row is out of view, scroll to it.
  842. f.cell = rowBarNode;
  843. f.cell.view = view;
  844. f.cell.getNode = function(index){
  845. return f.cell;
  846. };
  847. f.rowIndex = _this._lastFocusedRowBarIdx;
  848. f.scrollIntoView();
  849. f.cell = null;
  850. }
  851. }
  852. }
  853. });
  854. f.placeArea("rowHeader","before","content");
  855. }
  856. //Support keyboard selection.
  857. f.addArea({
  858. name:"cellselect",
  859. onMove: lang.partial(onmove, "cell", function(type, rowStep, colStep, evt){
  860. var current = _this._currentPoint[type];
  861. return _createItem("cell", current.row + rowStep, current.col + colStep);
  862. }),
  863. onKeyDown: lang.partial(onkeydown, "cell", function(){
  864. return _createItem("cell", f.rowIndex, f.cell.index);
  865. }),
  866. onKeyUp: lang.partial(onkeyup, "cell")
  867. });
  868. f.placeArea("cellselect","below","content");
  869. f.addArea({
  870. name:"colselect",
  871. onMove: lang.partial(onmove, "col", function(type, rowStep, colStep, evt){
  872. var current = _this._currentPoint[type];
  873. return _createItem("col", current.col + colStep);
  874. }),
  875. onKeyDown: lang.partial(onkeydown, "col", function(){
  876. return _createItem("col", f.getHeaderIndex());
  877. }),
  878. onKeyUp: lang.partial(onkeyup, "col")
  879. });
  880. f.placeArea("colselect","below","header");
  881. f.addArea({
  882. name:"rowselect",
  883. onMove: lang.partial(onmove, "row", function(type, rowStep, colStep, evt){
  884. return _createItem("row", f.rowIndex);
  885. }),
  886. onKeyDown: lang.partial(onkeydown, "row", function(){
  887. return _createItem("row", f.rowIndex);
  888. }),
  889. onKeyUp: lang.partial(onkeyup, "row")
  890. });
  891. f.placeArea("rowselect","below","rowHeader");
  892. },
  893. _clearSelection: function(type, reservedItem){
  894. // summary:
  895. // Clear selection for given type and fire events, but retain the highlight for *reservedItem*,
  896. // thus avoid "flashing".
  897. // tag:
  898. // private
  899. // type: String
  900. // "row", "col", or "cell
  901. // reservedItem: __SelectItem
  902. // The item to retain highlight.
  903. if(type == "all"){
  904. this._clearSelection("cell", reservedItem);
  905. this._clearSelection("col", reservedItem);
  906. this._clearSelection("row", reservedItem);
  907. return;
  908. }
  909. this._isUsingRowSelector = true;
  910. array.forEach(this._selected[type], function(item){
  911. if(!_isEqual(type, reservedItem, item)){
  912. this._highlightSingle(type, false, item);
  913. }
  914. }, this);
  915. this._blurPoint(type, this._currentPoint);
  916. this._selecting[type] = false;
  917. this._startPoint[type] = this._currentPoint[type] = null;
  918. this._selected[type] = [];
  919. //Have to also deselect default grid selection.
  920. if(type == "row" && !this.grid._selectingRange){
  921. this._oldDeselectAll.call(this.grid.selection);
  922. this.grid.selection._selectedById = {};
  923. }
  924. //Fire events.
  925. this.grid.onEndDeselect(type, null, null, this._selected);
  926. this.grid.onSelectCleared(type);
  927. },
  928. _startSelect: function(type, start, extending, isRange, mandatarySelect, toSelect){
  929. // summary:
  930. // Start selection, setup start point and current point, fire events.
  931. // tag:
  932. // private
  933. // type: String
  934. // "row", "col", or "cell"
  935. // extending: Boolean
  936. // Whether this is a multi selection
  937. // isRange: Boolean
  938. // Whether this is a range selection (i.e. select from the last end point to this point)
  939. // start: __SelectItem
  940. // The start point
  941. // mandatarySelect: Boolean
  942. // If true, toSelect will be same as the original selection status.
  943. if(!this._isValid(type, start)){
  944. return;
  945. }
  946. var lastIsSelected = this._isSelected(type, this._lastEndPoint[type]),
  947. isSelected = this._isSelected(type, start);
  948. if(this.noClear && !extending){
  949. this._toSelect = toSelect === undefined ? true : toSelect;
  950. }else{
  951. //If we are modifying the selection using keyboard, retain the old status.
  952. this._toSelect = mandatarySelect ? isSelected : !isSelected;
  953. }
  954. //If CTRL is not pressed or it's SINGLE mode, this is a brand new selection.
  955. if(!extending || (!isSelected && this._config[type] == SINGLE)){
  956. this._clearSelection("col", start);
  957. this._clearSelection("cell", start);
  958. if(!this.noClear || (type === 'row' && this._config[type] == SINGLE)){
  959. this._clearSelection('row', start);
  960. }
  961. this._toSelect = toSelect === undefined ? true : toSelect;
  962. }
  963. this._selecting[type] = true;
  964. this._currentPoint[type] = null;
  965. //We're holding SHIFT while clicking, it's a Click-Range selection.
  966. if(isRange && this._lastType == type && lastIsSelected == this._toSelect && this._config[type] == MULTI){
  967. if(type === "row"){
  968. this._isUsingRowSelector = true;
  969. }
  970. this._startPoint[type] = this._lastEndPoint[type];
  971. this._highlight(type, this._startPoint[type]);
  972. this._isUsingRowSelector = false;
  973. }else{
  974. this._startPoint[type] = start;
  975. }
  976. //Now start selection
  977. this._curType = type;
  978. this._fireEvent("start", type);
  979. this._isStartFocus = true;
  980. this._isUsingRowSelector = true;
  981. this._highlight(type, start, this._toSelect);
  982. this._isStartFocus = false;
  983. },
  984. _endSelect: function(type){
  985. // summary:
  986. // End selection. Keep records, fire events and cleanup status.
  987. // tag:
  988. // private
  989. // type: String
  990. // "row", "col", or "cell"
  991. if(type === "row"){
  992. delete this._isUsingRowSelector;
  993. }
  994. if(type == "all"){
  995. this._endSelect("col");
  996. this._endSelect("row");
  997. this._endSelect("cell");
  998. }else if(this._selecting[type]){
  999. this._addToSelected(type);
  1000. this._lastAnchorPoint[type] = this._startPoint[type];
  1001. this._lastEndPoint[type] = this._currentPoint[type];
  1002. if(this._toSelect){
  1003. this._lastSelectedAnchorPoint[type] = this._lastAnchorPoint[type];
  1004. this._lastSelectedEndPoint[type] = this._lastEndPoint[type];
  1005. }
  1006. this._startPoint[type] = this._currentPoint[type] = null;
  1007. this._selecting[type] = false;
  1008. this._lastType = type;
  1009. this._fireEvent("end", type);
  1010. }
  1011. },
  1012. _fireEvent: function(evtName, type){
  1013. switch(evtName){
  1014. case "start":
  1015. this.grid[this._toSelect ? "onStartSelect" : "onStartDeselect"](type, this._startPoint[type], this._selected);
  1016. break;
  1017. case "end":
  1018. this.grid[this._toSelect ? "onEndSelect" : "onEndDeselect"](type, this._lastAnchorPoint[type], this._lastEndPoint[type], this._selected);
  1019. break;
  1020. }
  1021. },
  1022. _calcToHighlight: function(type, target, toHighlight, toSelect){
  1023. // summary:
  1024. // Calculate what status should *target* have.
  1025. // If *toSelect* is not provided, this is a no op.
  1026. // This function is time-critical!!
  1027. if(toSelect !== undefined){
  1028. var sltd;
  1029. if(this._usingKeyboard && !toHighlight){
  1030. var last = this._isInLastRange(this._lastType, target);
  1031. if(last){
  1032. sltd = this._isSelected(type, target);
  1033. //This 2 cases makes the keyboard swipe selection valid!
  1034. if(toSelect && sltd){
  1035. return false;
  1036. }
  1037. if(!toSelect && !sltd && this._isInLastRange(this._lastType, target, true)){
  1038. return true;
  1039. }
  1040. }
  1041. }
  1042. return toHighlight ? toSelect : (sltd || this._isSelected(type, target));
  1043. }
  1044. return toHighlight;
  1045. },
  1046. _highlightNode: function(node, toHighlight){
  1047. // summary:
  1048. // Do the actual highlight work.
  1049. if(node){
  1050. var selectCSSClass = "dojoxGridRowSelected";
  1051. var selectCellClass = "dojoxGridCellSelected";
  1052. html.toggleClass(node, selectCSSClass, toHighlight);
  1053. html.toggleClass(node, selectCellClass, toHighlight);
  1054. }
  1055. },
  1056. _highlightHeader: function(colIdx, toHighlight){
  1057. var cells = this.grid.layout.cells;
  1058. var node = cells[colIdx].getHeaderNode();
  1059. var selectedClass = "dojoxGridHeaderSelected";
  1060. html.toggleClass(node, selectedClass, toHighlight);
  1061. },
  1062. _highlightRowSelector: function(rowIdx, toHighlight){
  1063. //var t1 = (new Date()).getTime();
  1064. var rowSelector = this.grid.views.views[0];
  1065. if(rowSelector instanceof _RowSelector){
  1066. var node = rowSelector.getRowNode(rowIdx);
  1067. if(node){
  1068. var selectedClass = "dojoxGridRowSelectorSelected";
  1069. html.toggleClass(node, selectedClass, toHighlight);
  1070. }
  1071. }
  1072. //console.log((new Date()).getTime() - t1);
  1073. },
  1074. _highlightSingle: function(type, toHighlight, target, toSelect, isRefresh){
  1075. // summary:
  1076. // Highlight a single item.
  1077. // This function is time critical!!
  1078. var _this = this, toHL, g = _this.grid, cells = g.layout.cells;
  1079. switch(type){
  1080. case "cell":
  1081. toHL = this._calcToHighlight(type, target, toHighlight, toSelect);
  1082. var c = cells[target.col];
  1083. if(!c.hidden && !c.notselectable){
  1084. this._highlightNode(target.node || c.getNode(target.row), toHL);
  1085. }
  1086. break;
  1087. case "col":
  1088. toHL = this._calcToHighlight(type, target, toHighlight, toSelect);
  1089. this._highlightHeader(target.col, toHL);
  1090. query("td[idx='" + target.col + "']", g.domNode).forEach(function(cellNode){
  1091. var rowNode = cells[target.col].view.content.findRowTarget(cellNode);
  1092. if(rowNode){
  1093. var rowIndex = rowNode[dojox.grid.util.rowIndexTag];
  1094. _this._highlightSingle("cell", toHL, {
  1095. "row": rowIndex,
  1096. "col": target.col,
  1097. "node": cellNode
  1098. });
  1099. }
  1100. });
  1101. break;
  1102. case "row":
  1103. toHL = this._calcToHighlight(type, target, toHighlight, toSelect);
  1104. this._highlightRowSelector(target.row, toHL);
  1105. if(this._config.cell){
  1106. array.forEach(cells, function(cell){
  1107. _this._highlightSingle("cell", toHL, {
  1108. "row": target.row,
  1109. "col": cell.index,
  1110. "node": cell.getNode(target.row)
  1111. });
  1112. });
  1113. }
  1114. //To avoid dead lock
  1115. this._selectedRowModified = true;
  1116. if(!isRefresh){
  1117. g.selection.setSelected(target.row, toHL);
  1118. }
  1119. }
  1120. },
  1121. _highlight: function(type, target, toSelect){
  1122. // summary:
  1123. // Highlight from start point to target.
  1124. // toSelect: Boolean
  1125. // Whether we are selecting or deselecting.
  1126. // This function is time critical!!
  1127. if(this._selecting[type] && target !== null){
  1128. var start = this._startPoint[type],
  1129. current = this._currentPoint[type],
  1130. _this = this,
  1131. highlight = function(from, to, toHL){
  1132. _this._forEach(type, from, to, function(item){
  1133. _this._highlightSingle(type, toHL, item, toSelect);
  1134. }, true);
  1135. };
  1136. switch(type){
  1137. case "col": case "row":
  1138. if(current !== null){
  1139. if(_inRange(type, target, start, current, true)){
  1140. //target is between start and current, some selected should be deselected.
  1141. highlight(current, target, false);
  1142. }else{
  1143. if(_inRange(type, start, target, current, true)){
  1144. //selection has jumped to different direction, all should be deselected.
  1145. highlight(current, start, false);
  1146. current = start;
  1147. }
  1148. highlight(target, current, true);
  1149. }
  1150. }else{
  1151. //First time select.
  1152. this._highlightSingle(type, true, target, toSelect);
  1153. }
  1154. break;
  1155. case "cell":
  1156. if(current !== null){
  1157. if(_inRange("row", target, start, current, true) ||
  1158. _inRange("col", target, start, current, true) ||
  1159. _inRange("row", start, target, current, true) ||
  1160. _inRange("col", start, target, current, true)){
  1161. highlight(start, current, false);
  1162. }
  1163. }
  1164. highlight(start, target, true);
  1165. }
  1166. this._currentPoint[type] = target;
  1167. this._focusPoint(type, this._currentPoint);
  1168. }
  1169. },
  1170. _focusPoint: function(type, point){
  1171. // summary:
  1172. // Focus the current point, so when you move mouse, the focus indicator follows you.
  1173. if(!this._isStartFocus){
  1174. var current = point[type],
  1175. f = this.grid.focus;
  1176. if(type == "col"){
  1177. f._colHeadFocusIdx = current.col;
  1178. f.focusArea("header");
  1179. }else if(type == "row"){
  1180. f.focusArea("rowHeader", {
  1181. "rowIndex": current.row
  1182. });
  1183. }else if(type == "cell"){
  1184. f.setFocusIndex(current.row, current.col);
  1185. }
  1186. }
  1187. },
  1188. _blurPoint: function(type, point){
  1189. // summary:
  1190. // Blur the current point.
  1191. var f = this.grid.focus;
  1192. if(type == "cell"){
  1193. f._blurContent();
  1194. }
  1195. },
  1196. _addToSelected: function(type){
  1197. // summary:
  1198. // Record the selected items.
  1199. var toSelect = this._toSelect, _this = this,
  1200. toAdd = [], toRemove = [],
  1201. start = this._startPoint[type],
  1202. end = this._currentPoint[type];
  1203. if(this._usingKeyboard){
  1204. //If using keyboard, selection will be ended after every move. But we have to remember the original selection status,
  1205. //so as to return to correct status when we shrink the selection region.
  1206. this._forEach(type, this._lastAnchorPoint[type], this._lastEndPoint[type], function(item){
  1207. //If the original selected item is not in current range, change its status.
  1208. if(!_inRange(type, item, start, end)){
  1209. (toSelect ? toRemove : toAdd).push(item);
  1210. }
  1211. });
  1212. }
  1213. this._forEach(type, start, end, function(item){
  1214. var isSelected = _this._isSelected(type, item);
  1215. if(toSelect && !isSelected){
  1216. //Add new selected items
  1217. toAdd.push(item);
  1218. }else if(!toSelect){
  1219. //Remove deselected items.
  1220. toRemove.push(item);
  1221. }
  1222. });
  1223. this._add(type, toAdd);
  1224. this._remove(type, toRemove);
  1225. // have to keep record in original grid selection
  1226. array.forEach(this._selected.row, function(item){
  1227. if(item.except.length > 0){
  1228. //to avoid dead lock
  1229. this._selectedRowModified = true;
  1230. this.grid.selection.setSelected(item.row, false);
  1231. }
  1232. }, this);
  1233. },
  1234. _forEach: function(type, start, end, func, halfClose){
  1235. // summary:
  1236. // Go through items from *start* point to *end* point.
  1237. // This function is time critical!!
  1238. if(!this._isValid(type, start, true) || !this._isValid(type, end, true)){
  1239. return;
  1240. }
  1241. switch(type){
  1242. case "col": case "row":
  1243. start = start[type];
  1244. end = end[type];
  1245. var dir = end > start ? 1 : -1;
  1246. if(!halfClose){
  1247. end += dir;
  1248. }
  1249. for(; start != end; start += dir){
  1250. func(_createItem(type, start));
  1251. }
  1252. break;
  1253. case "cell":
  1254. var colDir = end.col > start.col ? 1 : -1,
  1255. rowDir = end.row > start.row ? 1 : -1;
  1256. for(var i = start.row, p = end.row + rowDir; i != p; i += rowDir){
  1257. for(var j = start.col, q = end.col + colDir; j != q; j += colDir){
  1258. func(_createItem(type, i, j));
  1259. }
  1260. }
  1261. }
  1262. },
  1263. _makeupForExceptions: function(type, newCellItems){
  1264. // summary:
  1265. // When new cells is selected, maybe they will fill in the "holes" in selected rows and columns.
  1266. var makedUps = [];
  1267. array.forEach(this._selected[type], function(v1){
  1268. array.forEach(newCellItems, function(v2){
  1269. if(v1[type] == v2[type]){
  1270. var pos = array.indexOf(v1.except, v2[_theOther[type]]);
  1271. if(pos >= 0){
  1272. v1.except.splice(pos, 1);
  1273. }
  1274. makedUps.push(v2);
  1275. }
  1276. });
  1277. });
  1278. return makedUps;
  1279. },
  1280. _makeupForCells: function(type, newItems){
  1281. // summary:
  1282. // When some rows/cols are selected, maybe they can cover some of the selected cells,
  1283. // and fill some of the "holes" in the selected cols/rows.
  1284. var toRemove = [];
  1285. array.forEach(this._selected.cell, function(v1){
  1286. array.some(newItems, function(v2){
  1287. if(v1[type] == v2[type]){
  1288. toRemove.push(v1);
  1289. return true;
  1290. }
  1291. return false;
  1292. });
  1293. });
  1294. this._remove("cell", toRemove);
  1295. array.forEach(this._selected[_theOther[type]], function(v1){
  1296. array.forEach(newItems, function(v2){
  1297. var pos = array.indexOf(v1.except, v2[type]);
  1298. if(pos >= 0){
  1299. v1.except.splice(pos, 1);
  1300. }
  1301. });
  1302. });
  1303. },
  1304. _addException: function(type, items){
  1305. // summary:
  1306. // If some rows/cols are deselected, maybe they have created "holes" in selected cols/rows.
  1307. array.forEach(this._selected[type], function(v1){
  1308. array.forEach(items, function(v2){
  1309. v1.except.push(v2[_theOther[type]]);
  1310. });
  1311. });
  1312. },
  1313. _addCellException: function(type, items){
  1314. // summary:
  1315. // If some cells are deselected, maybe they have created "holes" in selected rows/cols.
  1316. array.forEach(this._selected[type], function(v1){
  1317. array.forEach(items, function(v2){
  1318. if(v1[type] == v2[type]){
  1319. v1.except.push(v2[_theOther[type]]);
  1320. }
  1321. });
  1322. });
  1323. },
  1324. _add: function(type, items){
  1325. // summary:
  1326. // Add to the selection record.
  1327. var cells = this.grid.layout.cells;
  1328. if(type == "cell"){
  1329. var colMakedup = this._makeupForExceptions("col", items);
  1330. var rowMakedup = this._makeupForExceptions("row", items);
  1331. //Step over hidden columns.
  1332. items = array.filter(items, function(item){
  1333. return array.indexOf(colMakedup, item) < 0 && array.indexOf(rowMakedup, item) < 0 &&
  1334. !cells[item.col].hidden && !cells[item.col].notselectable;
  1335. });
  1336. }else{
  1337. if(type == "col"){
  1338. //Step over hidden columns.
  1339. items = array.filter(items, function(item){
  1340. return !cells[item.col].hidden && !cells[item.col].notselectable;
  1341. });
  1342. }
  1343. this._makeupForCells(type, items);
  1344. this._selected[type] = array.filter(this._selected[type], function(v){
  1345. return array.every(items, function(item){
  1346. return v[type] !== item[type];
  1347. });
  1348. });
  1349. }
  1350. if(type != "col" && this.grid._hasIdentity){
  1351. array.forEach(items, function(item){
  1352. var record = this.grid._by_idx[item.row];
  1353. if(record){
  1354. item.id = record.idty;
  1355. }
  1356. }, this);
  1357. }
  1358. this._selected[type] = this._selected[type].concat(items);
  1359. },
  1360. _remove: function(type, items){
  1361. // summary:
  1362. // Remove from the selection record.
  1363. var comp = lang.partial(_isEqual, type);
  1364. this._selected[type] = array.filter(this._selected[type], function(v1){
  1365. return !array.some(items, function(v2){
  1366. return comp(v1, v2);
  1367. });
  1368. });
  1369. if(type == "cell"){
  1370. this._addCellException("col", items);
  1371. this._addCellException("row", items);
  1372. }else if(this._config.cell){
  1373. this._addException(_theOther[type], items);
  1374. }
  1375. },
  1376. _isCellNotInExcept: function(type, item){
  1377. // summary:
  1378. // Return true only when a cell is covered by selected row/col, and its not a "hole".
  1379. var attr = item[type], corres = item[_theOther[type]];
  1380. return array.some(this._selected[type], function(v){
  1381. return v[type] == attr && array.indexOf(v.except, corres) < 0;
  1382. });
  1383. },
  1384. _isSelected: function(type, item){
  1385. // summary:
  1386. // Return true when the item is selected. (or logically selected, i.e, covered by a row/col).
  1387. if(!item){ return false; }
  1388. var res = array.some(this._selected[type], function(v){
  1389. var ret = _isEqual(type, item, v);
  1390. if(ret && type !== "cell"){
  1391. return v.except.length === 0;
  1392. }
  1393. return ret;
  1394. });
  1395. if(!res && type === "cell"){
  1396. res = (this._isCellNotInExcept("col", item) || this._isCellNotInExcept("row", item));
  1397. if(type === "cell"){
  1398. res = res && !this.grid.layout.cells[item.col].notselectable;
  1399. }
  1400. }
  1401. return res;
  1402. },
  1403. _isInLastRange: function(type, item, isSelected){
  1404. // summary:
  1405. // Return true only when the item is in the last seletion/deseletion range.
  1406. var start = this[isSelected ? "_lastSelectedAnchorPoint" : "_lastAnchorPoint"][type],
  1407. end = this[isSelected ? "_lastSelectedEndPoint" : "_lastEndPoint"][type];
  1408. if(!item || !start || !end){ return false; }
  1409. return _inRange(type, item, start, end);
  1410. },
  1411. _isValid: function(type, item, allowNotSelectable){
  1412. // summary:
  1413. // Check whether the item is a valid __SelectItem for the given type.
  1414. if(!item){ return false; }
  1415. try{
  1416. var g = this.grid, index = item[type];
  1417. switch(type){
  1418. case "col":
  1419. return index >= 0 && index < g.layout.cells.length && lang.isArray(item.except) &&
  1420. (allowNotSelectable || !g.layout.cells[index].notselectable);
  1421. case "row":
  1422. return index >= 0 && index < g.rowCount && lang.isArray(item.except);
  1423. case "cell":
  1424. return item.col >= 0 && item.col < g.layout.cells.length &&
  1425. item.row >= 0 && item.row < g.rowCount &&
  1426. (allowNotSelectable || !g.layout.cells[item.col].notselectable);
  1427. }
  1428. }catch(e){}
  1429. return false;
  1430. }
  1431. });
  1432. EnhancedGrid.registerPlugin(Selector/*name:'selector'*/, {
  1433. "dependency": ["autoScroll"]
  1434. });
  1435. return Selector;
  1436. });