Selector.js 46 KB

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