1477 lines
46 KiB
JavaScript
1477 lines
46 KiB
JavaScript
//>>built
|
|
define("dojox/grid/enhanced/plugins/Selector", [
|
|
"dojo/_base/kernel",
|
|
"dojo/_base/lang",
|
|
"dojo/_base/declare",
|
|
"dojo/_base/array",
|
|
"dojo/_base/event",
|
|
"dojo/keys",
|
|
"dojo/query",
|
|
"dojo/_base/html",
|
|
"dojo/_base/window",
|
|
"dijit/focus",
|
|
"../../_RowSelector",
|
|
"../_Plugin",
|
|
"../../EnhancedGrid",
|
|
"../../cells/_base",
|
|
"./AutoScroll"
|
|
], function(dojo, lang, declare, array, event, keys, query, html, win, dijitFocus, _RowSelector, _Plugin, EnhancedGrid){
|
|
|
|
/*=====
|
|
dojo.declare("__SelectItem", null,{
|
|
// summary:
|
|
// An abstract representation of an item.
|
|
});
|
|
dojo.declare("__SelectCellItem", __SelectItem,{
|
|
// summary:
|
|
// An abstract representation of a cell.
|
|
|
|
// row: Integer
|
|
// Row index of this cell
|
|
row: 0,
|
|
|
|
// col: Integer
|
|
// Column index of this cell
|
|
col: 0
|
|
});
|
|
dojo.declare("__SelectRowItem", __SelectItem,{
|
|
// summary:
|
|
// An abstract representation of a row.
|
|
|
|
// row: Integer
|
|
// Row index of this row
|
|
row: 0,
|
|
|
|
// except: Integer[]
|
|
// An array of column indexes of all the unselected cells in this row.
|
|
except: []
|
|
});
|
|
dojo.declare("__SelectColItem", __SelectItem,{
|
|
// summary:
|
|
// An abstract representation of a column.
|
|
|
|
// col: Integer
|
|
// Column index of this column
|
|
col: 0,
|
|
|
|
// except: Integer[]
|
|
// An array of row indexes of all the unselected cells in this column.
|
|
except: []
|
|
});
|
|
=====*/
|
|
|
|
var DISABLED = 0, SINGLE = 1, MULTI = 2,
|
|
_theOther = { col: "row", row: "col" },
|
|
_inRange = function(type, value, start, end, halfClose){
|
|
if(type !== "cell"){
|
|
value = value[type];
|
|
start = start[type];
|
|
end = end[type];
|
|
if(typeof value !== "number" || typeof start !== "number" || typeof end !== "number"){
|
|
return false;
|
|
}
|
|
return halfClose ? ((value >= start && value < end) || (value > end && value <= start))
|
|
: ((value >= start && value <= end) || (value >= end && value <= start));
|
|
}else{
|
|
return _inRange("col", value, start, end, halfClose) && _inRange("row", value, start, end, halfClose);
|
|
}
|
|
},
|
|
_isEqual = function(type, v1, v2){
|
|
try{
|
|
if(v1 && v2){
|
|
switch(type){
|
|
case "col": case "row":
|
|
return v1[type] == v2[type] && typeof v1[type] == "number" &&
|
|
!(_theOther[type] in v1) && !(_theOther[type] in v2);
|
|
case "cell":
|
|
return v1.col == v2.col && v1.row == v2.row && typeof v1.col == "number" && typeof v1.row == "number";
|
|
}
|
|
}
|
|
}catch(e){}
|
|
return false;
|
|
},
|
|
_stopEvent = function(evt){
|
|
try{
|
|
if(evt && evt.preventDefault){
|
|
event.stop(evt);
|
|
}
|
|
}catch(e){}
|
|
},
|
|
_createItem = function(type, rowIndex, colIndex){
|
|
switch(type){
|
|
case "col":
|
|
return {
|
|
"col": typeof colIndex == "undefined" ? rowIndex : colIndex,
|
|
"except": []
|
|
};
|
|
case "row":
|
|
return {
|
|
"row": rowIndex,
|
|
"except": []
|
|
};
|
|
case "cell":
|
|
return {
|
|
"row": rowIndex,
|
|
"col": colIndex
|
|
};
|
|
}
|
|
return null;
|
|
};
|
|
var Selector = declare("dojox.grid.enhanced.plugins.Selector", _Plugin, {
|
|
// summary:
|
|
// Provides standard extended selection for grid.
|
|
// Supports mouse/keyboard selection, multi-selection, and de-selection.
|
|
// Acceptable plugin parameters:
|
|
// The whole plugin parameter object is a config object passed to the setupConfig function.
|
|
//
|
|
// Acceptable cell parameters defined in layout:
|
|
// 1. notselectable: boolean
|
|
// Whether this column is (and all the cells in it are) selectable.
|
|
|
|
// name: String
|
|
// plugin name
|
|
name: "selector",
|
|
|
|
// noClear: Boolean
|
|
// Not to clear rows selected by IndirectSelection.
|
|
/*
|
|
// _config: null,
|
|
// _enabled: true,
|
|
// _selecting: {
|
|
// row: false,
|
|
// col: false,
|
|
// cell: false
|
|
// },
|
|
// _selected: {
|
|
// row: [],
|
|
// col: [],
|
|
// cell: []
|
|
// },
|
|
// _startPoint: {},
|
|
// _currentPoint: {},
|
|
// _lastAnchorPoint: {},
|
|
// _lastEndPoint: {},
|
|
// _lastSelectedAnchorPoint: {},
|
|
// _lastSelectedEndPoint: {},
|
|
// _keyboardSelect: {
|
|
// row: 0,
|
|
// col: 0,
|
|
// cell: 0
|
|
// },
|
|
// _curType: null,
|
|
// _lastType: null,
|
|
// _usingKeyboard: false,
|
|
// _toSelect: true,
|
|
*/
|
|
|
|
constructor: function(grid, args){
|
|
this.grid = grid;
|
|
this._config = {
|
|
row: MULTI,
|
|
col: MULTI,
|
|
cell: MULTI
|
|
};
|
|
this.noClear = args && args.noClear;
|
|
this.setupConfig(args);
|
|
if(grid.selectionMode === "single"){
|
|
this._config.row = SINGLE;
|
|
}
|
|
this._enabled = true;
|
|
this._selecting = {};
|
|
this._selected = {
|
|
"col": [],
|
|
"row": [],
|
|
"cell": []
|
|
};
|
|
this._startPoint = {};
|
|
this._currentPoint = {};
|
|
this._lastAnchorPoint = {};
|
|
this._lastEndPoint = {};
|
|
this._lastSelectedAnchorPoint = {};
|
|
this._lastSelectedEndPoint = {};
|
|
this._keyboardSelect = {};
|
|
this._lastType = null;
|
|
this._selectedRowModified = {};
|
|
this._hacks();
|
|
this._initEvents();
|
|
this._initAreas();
|
|
this._mixinGrid();
|
|
},
|
|
destroy: function(){
|
|
this.inherited(arguments);
|
|
},
|
|
//------------public--------------------
|
|
setupConfig: function(config){
|
|
// summary:
|
|
// Set selection mode for row/col/cell.
|
|
// config: Object
|
|
// An object with the following structure (all properties are optional):
|
|
// {
|
|
// //Default is "multi", all other values are same as "multi".
|
|
// row: false|"disabled"|"single",
|
|
// col: false|"disabled"|"single",
|
|
// cell: false|"disabled"|"single"
|
|
// }
|
|
if(!config || !lang.isObject(config)){
|
|
return;
|
|
}
|
|
var types = ["row", "col", "cell"];
|
|
for(var type in config){
|
|
if(array.indexOf(types, type) >= 0){
|
|
if(!config[type] || config[type] == "disabled"){
|
|
this._config[type] = DISABLED;
|
|
}else if(config[type] == "single"){
|
|
this._config[type] = SINGLE;
|
|
}else{
|
|
this._config[type] = MULTI;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Have to set mode to default grid selection.
|
|
var mode = ["none","single","extended"][this._config.row];
|
|
this.grid.selection.setMode(mode);
|
|
},
|
|
isSelected: function(type, rowIndex, colIndex){
|
|
// summary:
|
|
// Check whether a location (a cell, a column or a row) is selected.
|
|
// tag:
|
|
// public
|
|
// type: String
|
|
// "row" or "col" or "cell"
|
|
// rowIndex: Integer
|
|
// If type is "row" or "cell", this is the row index.
|
|
// If type if "col", this is the column index.
|
|
// colIndex: Integer?
|
|
// Only valid when type is "cell"
|
|
// return: Boolean
|
|
// true if selected, false if not. If cell is covered by a selected column, it's selected.
|
|
return this._isSelected(type, _createItem(type, rowIndex, colIndex));
|
|
},
|
|
toggleSelect: function(type, rowIndex, colIndex){
|
|
this._startSelect(type, _createItem(type, rowIndex, colIndex), this._config[type] === MULTI, false, false, !this.isSelected(type, rowIndex, colIndex));
|
|
this._endSelect(type);
|
|
},
|
|
select: function(type, rowIndex, colIndex){
|
|
// summary:
|
|
// Select a location (a cell, a column or a row).
|
|
// tag:
|
|
// public
|
|
// type: String
|
|
// "row" or "col" or "cell"
|
|
// rowIndex: Integer
|
|
// If type is "row" or "cell", this is the row index.
|
|
// If type if "col", this is the column index.
|
|
// colIndex: Integer?
|
|
// Only valid when type is "cell"
|
|
if(!this.isSelected(type, rowIndex, colIndex)){
|
|
this.toggleSelect(type, rowIndex, colIndex);
|
|
}
|
|
},
|
|
deselect: function(type, rowIndex, colIndex){
|
|
if(this.isSelected(type, rowIndex, colIndex)){
|
|
this.toggleSelect(type, rowIndex, colIndex);
|
|
}
|
|
},
|
|
selectRange: function(type, start, end, toSelect){
|
|
// summary:
|
|
// Select a continuous range (a block of cells, a set of continuous columns or rows)
|
|
// tag:
|
|
// public
|
|
// type: String
|
|
// "row" or "col" or "cell"
|
|
// start: Integer | Object
|
|
// If type is "row" or "col", this is the index of the starting row or column.
|
|
// If type if "cell", this is the left-top cell of the range.
|
|
// end: Integer | Object
|
|
// If type is "row" or "col", this is the index of the ending row or column.
|
|
// If type if "cell", this is the right-bottom cell of the range.
|
|
this.grid._selectingRange = true;
|
|
var startPoint = type == "cell" ? _createItem(type, start.row, start.col) : _createItem(type, start),
|
|
endPoint = type == "cell" ? _createItem(type, end.row, end.col) : _createItem(type, end);
|
|
this._startSelect(type, startPoint, false, false, false, toSelect);
|
|
this._highlight(type, endPoint, toSelect === undefined ? true : toSelect);
|
|
this._endSelect(type);
|
|
this.grid._selectingRange = false;
|
|
},
|
|
clear: function(type){
|
|
// summary:
|
|
// Clear all selections.
|
|
// tag:
|
|
// public
|
|
// type: String?
|
|
// "row" or "col" or "cell". If omitted, clear all.
|
|
this._clearSelection(type || "all");
|
|
},
|
|
isSelecting: function(type){
|
|
// summary:
|
|
// Check whether the user is currently selecting something.
|
|
// tag:
|
|
// public
|
|
// type: String
|
|
// "row" or "col" or "cell"
|
|
// return: Boolean
|
|
// true if is selection, false otherwise.
|
|
if(typeof type == "undefined"){
|
|
return this._selecting.col || this._selecting.row || this._selecting.cell;
|
|
}
|
|
return this._selecting[type];
|
|
},
|
|
selectEnabled: function(toEnable){
|
|
// summary:
|
|
// Turn on/off this selection functionality if *toEnable* is provided.
|
|
// Check whether this selection functionality is enabled if nothing is passed in.
|
|
// tag:
|
|
// public
|
|
// toEnable: Boolean?
|
|
// To enable or not. Optional.
|
|
// return: Boolean | undefined
|
|
// Enabled or not.
|
|
if(typeof toEnable != "undefined" && !this.isSelecting()){
|
|
this._enabled = !!toEnable;
|
|
}
|
|
return this._enabled;
|
|
},
|
|
getSelected: function(type, includeExceptions){
|
|
// summary:
|
|
// Get an array of selected locations.
|
|
// tag:
|
|
// public
|
|
// type: String
|
|
// "row" or "col" or "cell"
|
|
// includeExceptions: Boolean
|
|
// Only meaningful for rows/columns. If true, all selected rows/cols, even they are partly selected, are all returned.
|
|
// return: __SelectItem[]
|
|
switch(type){
|
|
case "cell":
|
|
return array.map(this._selected[type], function(item){ return item; });
|
|
case "col": case "row":
|
|
return array.map(includeExceptions ? this._selected[type]
|
|
: array.filter(this._selected[type], function(item){
|
|
return item.except.length === 0;
|
|
}), function(item){
|
|
return includeExceptions ? item : item[type];
|
|
});
|
|
}
|
|
return [];
|
|
},
|
|
getSelectedCount: function(type, includeExceptions){
|
|
// summary:
|
|
// Get the number of selected items.
|
|
// tag:
|
|
// public
|
|
// type: String
|
|
// "row" or "col" or "cell"
|
|
// includeExceptions: Boolean
|
|
// Only meaningful for rows/columns. If true, all selected rows/cols, even they are partly selected, are all returned.
|
|
// return: Integer
|
|
// The number of selected items.
|
|
switch(type){
|
|
case "cell":
|
|
return this._selected[type].length;
|
|
case "col": case "row":
|
|
return (includeExceptions ? this._selected[type]
|
|
: array.filter(this._selected[type], function(item){
|
|
return item.except.length === 0;
|
|
})).length;
|
|
}
|
|
return 0;
|
|
},
|
|
getSelectedType: function(){
|
|
// summary:
|
|
// Get the type of selected items.
|
|
// tag:
|
|
// public
|
|
// return: String
|
|
// "row" or "col" or "cell", or any mix of these (separator is | ).
|
|
var s = this._selected;
|
|
return ["", "cell", "row", "row|cell",
|
|
"col", "col|cell", "col|row", "col|row|cell"
|
|
][(!!s.cell.length) | (!!s.row.length << 1) | (!!s.col.length << 2)];
|
|
},
|
|
getLastSelectedRange: function(type){
|
|
// summary:
|
|
// Get last selected range of the given type.
|
|
// tag:
|
|
// public
|
|
// return: Object
|
|
// {start: __SelectItem, end: __SelectItem}
|
|
// return null if nothing is selected.
|
|
return this._lastAnchorPoint[type] ? {
|
|
"start": this._lastAnchorPoint[type],
|
|
"end": this._lastEndPoint[type]
|
|
} : null;
|
|
},
|
|
|
|
//--------------------------private----------------------------
|
|
_hacks: function(){
|
|
// summary:
|
|
// Complete the event system of grid, hack some grid functions to prevent default behavior.
|
|
var g = this.grid;
|
|
var doContentMouseUp = function(e){
|
|
if(e.cellNode){
|
|
g.onMouseUp(e);
|
|
}
|
|
g.onMouseUpRow(e);
|
|
};
|
|
var mouseUp = lang.hitch(g, "onMouseUp");
|
|
var mouseDown = lang.hitch(g, "onMouseDown");
|
|
var doRowSelectorFocus = function(e){
|
|
e.cellNode.style.border = "solid 1px";
|
|
};
|
|
array.forEach(g.views.views, function(view){
|
|
view.content.domouseup = doContentMouseUp;
|
|
view.header.domouseup = mouseUp;
|
|
if(view.declaredClass == "dojox.grid._RowSelector"){
|
|
view.domousedown = mouseDown;
|
|
view.domouseup = mouseUp;
|
|
view.dofocus = doRowSelectorFocus;
|
|
}
|
|
});
|
|
//Disable default selection.
|
|
g.selection.clickSelect = function(){};
|
|
|
|
this._oldDeselectAll = g.selection.deselectAll;
|
|
var _this = this;
|
|
g.selection.selectRange = function(from, to){
|
|
_this.selectRange("row", from, to, true);
|
|
if(g.selection.preserver){
|
|
g.selection.preserver._updateMapping(true, true, false, from, to);
|
|
}
|
|
g.selection.onChanged();
|
|
};
|
|
g.selection.deselectRange = function(from, to){
|
|
_this.selectRange("row", from, to, false);
|
|
if(g.selection.preserver){
|
|
g.selection.preserver._updateMapping(true, false, false, from, to);
|
|
}
|
|
g.selection.onChanged();
|
|
};
|
|
g.selection.deselectAll = function(){
|
|
g._selectingRange = true;
|
|
_this._oldDeselectAll.apply(g.selection, arguments);
|
|
_this._clearSelection("all");
|
|
g._selectingRange = false;
|
|
if(g.selection.preserver){
|
|
g.selection.preserver._updateMapping(true, false, true);
|
|
}
|
|
g.selection.onChanged();
|
|
};
|
|
|
|
var rowSelector = g.views.views[0];
|
|
//The default function re-write the whole className, so can not insert any other classes.
|
|
if(rowSelector instanceof _RowSelector){
|
|
rowSelector.doStyleRowNode = function(inRowIndex, inRowNode){
|
|
html.removeClass(inRowNode, "dojoxGridRow");
|
|
html.addClass(inRowNode, "dojoxGridRowbar");
|
|
html.addClass(inRowNode, "dojoxGridNonNormalizedCell");
|
|
html.toggleClass(inRowNode, "dojoxGridRowbarOver", g.rows.isOver(inRowIndex));
|
|
html.toggleClass(inRowNode, "dojoxGridRowbarSelected", !!g.selection.isSelected(inRowIndex));
|
|
};
|
|
}
|
|
this.connect(g, "updateRow", function(rowIndex){
|
|
array.forEach(g.layout.cells, function(cell){
|
|
if(this.isSelected("cell", rowIndex, cell.index)){
|
|
this._highlightNode(cell.getNode(rowIndex), true);
|
|
}
|
|
}, this);
|
|
});
|
|
},
|
|
_mixinGrid: function(){
|
|
// summary:
|
|
// Expose events to grid.
|
|
var g = this.grid;
|
|
g.setupSelectorConfig = lang.hitch(this, this.setupConfig);
|
|
g.onStartSelect = function(){};
|
|
g.onEndSelect = function(){};
|
|
g.onStartDeselect = function(){};
|
|
g.onEndDeselect = function(){};
|
|
g.onSelectCleared = function(){};
|
|
},
|
|
_initEvents: function(){
|
|
// summary:
|
|
// Connect events, create event handlers.
|
|
var g = this.grid,
|
|
_this = this,
|
|
dp = lang.partial,
|
|
starter = function(type, e){
|
|
if(type === "row"){
|
|
_this._isUsingRowSelector = true;
|
|
}
|
|
//only left mouse button can select.
|
|
if(_this.selectEnabled() && _this._config[type] && e.button != 2){
|
|
if(_this._keyboardSelect.col || _this._keyboardSelect.row || _this._keyboardSelect.cell){
|
|
_this._endSelect("all");
|
|
_this._keyboardSelect.col = _this._keyboardSelect.row = _this._keyboardSelect.cell = 0;
|
|
}
|
|
if(_this._usingKeyboard){
|
|
_this._usingKeyboard = false;
|
|
}
|
|
var target = _createItem(type, e.rowIndex, e.cell && e.cell.index);
|
|
_this._startSelect(type, target, e.ctrlKey, e.shiftKey);
|
|
}
|
|
},
|
|
ender = lang.hitch(this, "_endSelect");
|
|
this.connect(g, "onHeaderCellMouseDown", dp(starter, "col"));
|
|
this.connect(g, "onHeaderCellMouseUp", dp(ender, "col"));
|
|
|
|
this.connect(g, "onRowSelectorMouseDown", dp(starter, "row"));
|
|
this.connect(g, "onRowSelectorMouseUp", dp(ender, "row"));
|
|
|
|
this.connect(g, "onCellMouseDown", function(e){
|
|
if(e.cell && e.cell.isRowSelector){ return; }
|
|
if(g.singleClickEdit){
|
|
_this._singleClickEdit = true;
|
|
g.singleClickEdit = false;
|
|
}
|
|
starter(_this._config["cell"] == DISABLED ? "row" : "cell", e);
|
|
});
|
|
this.connect(g, "onCellMouseUp", function(e){
|
|
if(_this._singleClickEdit){
|
|
delete _this._singleClickEdit;
|
|
g.singleClickEdit = true;
|
|
}
|
|
ender("all", e);
|
|
});
|
|
|
|
this.connect(g, "onCellMouseOver", function(e){
|
|
if(_this._curType != "row" && _this._selecting[_this._curType] && _this._config[_this._curType] == MULTI){
|
|
_this._highlight("col", _createItem("col", e.cell.index), _this._toSelect);
|
|
if(!_this._keyboardSelect.cell){
|
|
_this._highlight("cell", _createItem("cell", e.rowIndex, e.cell.index), _this._toSelect);
|
|
}
|
|
}
|
|
});
|
|
this.connect(g, "onHeaderCellMouseOver", function(e){
|
|
if(_this._selecting.col && _this._config.col == MULTI){
|
|
_this._highlight("col", _createItem("col", e.cell.index), _this._toSelect);
|
|
}
|
|
});
|
|
this.connect(g, "onRowMouseOver", function(e){
|
|
if(_this._selecting.row && _this._config.row == MULTI){
|
|
_this._highlight("row", _createItem("row", e.rowIndex), _this._toSelect);
|
|
}
|
|
});
|
|
|
|
//When row order has changed in a unpredictable way (sorted or filtered), map the new rowindex.
|
|
this.connect(g, "onSelectedById", "_onSelectedById");
|
|
|
|
//When the grid refreshes, all those selected should still appear selected.
|
|
this.connect(g, "_onFetchComplete", function(){
|
|
//console.debug("refresh after buildPage:", g._notRefreshSelection);
|
|
if(!g._notRefreshSelection){
|
|
this._refreshSelected(true);
|
|
}
|
|
});
|
|
|
|
//Small scroll might not refresh the grid.
|
|
this.connect(g.scroller, "buildPage", function(){
|
|
//console.debug("refresh after buildPage:", g._notRefreshSelection);
|
|
if(!g._notRefreshSelection){
|
|
this._refreshSelected(true);
|
|
}
|
|
});
|
|
|
|
//Whenever the mouse is up, end selecting.
|
|
this.connect(win.doc, "onmouseup", dp(ender, "all"));
|
|
|
|
//If autoscroll is enabled, connect to it.
|
|
this.connect(g, "onEndAutoScroll", function(isVertical, isForward, view, target){
|
|
var selectCell = _this._selecting.cell,
|
|
type, current, dir = isForward ? 1 : -1;
|
|
if(isVertical && (selectCell || _this._selecting.row)){
|
|
type = selectCell ? "cell" : "row";
|
|
current = _this._currentPoint[type];
|
|
_this._highlight(type, _createItem(type, current.row + dir, current.col), _this._toSelect);
|
|
}else if(!isVertical && (selectCell || _this._selecting.col)){
|
|
type = selectCell ? "cell" : "col";
|
|
current = _this._currentPoint[type];
|
|
_this._highlight(type, _createItem(type, current.row, target), _this._toSelect);
|
|
}
|
|
});
|
|
//If the grid is changed, selection should be consistent.
|
|
this.subscribe("dojox/grid/rearrange/move/" + g.id, "_onInternalRearrange");
|
|
this.subscribe("dojox/grid/rearrange/copy/" + g.id, "_onInternalRearrange");
|
|
this.subscribe("dojox/grid/rearrange/change/" + g.id, "_onExternalChange");
|
|
this.subscribe("dojox/grid/rearrange/insert/" + g.id, "_onExternalChange");
|
|
this.subscribe("dojox/grid/rearrange/remove/" + g.id, "clear");
|
|
|
|
//have to also select when the grid's default select is used.
|
|
this.connect(g, "onSelected", function(rowIndex){
|
|
if(this._selectedRowModified && this._isUsingRowSelector){
|
|
delete this._selectedRowModified;
|
|
}else if(!this.grid._selectingRange){
|
|
this.select("row", rowIndex);
|
|
}
|
|
});
|
|
this.connect(g, "onDeselected", function(rowIndex){
|
|
if(this._selectedRowModified && this._isUsingRowSelector){
|
|
delete this._selectedRowModified;
|
|
}else if(!this.grid._selectingRange){
|
|
this.deselect("row", rowIndex);
|
|
}
|
|
});
|
|
},
|
|
_onSelectedById: function(id, newIndex, isSelected){
|
|
if(this.grid._noInternalMapping){
|
|
return;
|
|
}
|
|
var pointSet = [this._lastAnchorPoint.row, this._lastEndPoint.row,
|
|
this._lastSelectedAnchorPoint.row, this._lastSelectedEndPoint.row];
|
|
pointSet = pointSet.concat(this._selected.row);
|
|
var found = false;
|
|
array.forEach(pointSet, function(item){
|
|
if(item){
|
|
if(item.id === id){
|
|
found = true;
|
|
item.row = newIndex;
|
|
}else if(item.row === newIndex && item.id){
|
|
item.row = -1;
|
|
}
|
|
}
|
|
});
|
|
if(!found && isSelected){
|
|
array.some(this._selected.row, function(item){
|
|
if(item && !item.id && !item.except.length){
|
|
item.id = id;
|
|
item.row = newIndex;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
found = false;
|
|
pointSet = [this._lastAnchorPoint.cell, this._lastEndPoint.cell,
|
|
this._lastSelectedAnchorPoint.cell, this._lastSelectedEndPoint.cell];
|
|
pointSet = pointSet.concat(this._selected.cell);
|
|
array.forEach(pointSet, function(item){
|
|
if(item){
|
|
if(item.id === id){
|
|
found = true;
|
|
item.row = newIndex;
|
|
}else if(item.row === newIndex && item.id){
|
|
item.row = -1;
|
|
}
|
|
}
|
|
});
|
|
},
|
|
onSetStore: function(){
|
|
this._clearSelection("all");
|
|
},
|
|
_onInternalRearrange: function(type, mapping){
|
|
try{
|
|
//The column can not refresh it self!
|
|
this._refresh("col", false);
|
|
|
|
array.forEach(this._selected.row, function(item){
|
|
array.forEach(this.grid.layout.cells, function(cell){
|
|
this._highlightNode(cell.getNode(item.row), false);
|
|
}, this);
|
|
}, this);
|
|
//The rowbar must be cleaned manually
|
|
query(".dojoxGridRowSelectorSelected").forEach(function(node){
|
|
html.removeClass(node, "dojoxGridRowSelectorSelected");
|
|
html.removeClass(node, "dojoxGridRowSelectorSelectedUp");
|
|
html.removeClass(node, "dojoxGridRowSelectorSelectedDown");
|
|
});
|
|
|
|
var cleanUp = function(item){
|
|
if(item){
|
|
delete item.converted;
|
|
}
|
|
},
|
|
pointSet = [this._lastAnchorPoint[type], this._lastEndPoint[type],
|
|
this._lastSelectedAnchorPoint[type], this._lastSelectedEndPoint[type]];
|
|
|
|
if(type === "cell"){
|
|
this.selectRange("cell", mapping.to.min, mapping.to.max);
|
|
var cells = this.grid.layout.cells;
|
|
array.forEach(pointSet, function(item){
|
|
if(item.converted){ return; }
|
|
for(var r = mapping.from.min.row, tr = mapping.to.min.row; r <= mapping.from.max.row; ++r, ++tr){
|
|
for(var c = mapping.from.min.col, tc = mapping.to.min.col; c <= mapping.from.max.col; ++c, ++tc){
|
|
while(cells[c].hidden){ ++c; }
|
|
while(cells[tc].hidden){ ++tc; }
|
|
if(item.row == r && item.col == c){
|
|
//console.log('mapping found: (', item.row, ",",item.col,") to (", tr, ",", tc,")");
|
|
item.row = tr;
|
|
item.col = tc;
|
|
item.converted = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}else{
|
|
pointSet = this._selected.cell.concat(this._selected[type]).concat(pointSet).concat(
|
|
[this._lastAnchorPoint.cell, this._lastEndPoint.cell,
|
|
this._lastSelectedAnchorPoint.cell, this._lastSelectedEndPoint.cell]);
|
|
array.forEach(pointSet, function(item){
|
|
if(item && !item.converted){
|
|
var from = item[type];
|
|
if(from in mapping){
|
|
item[type] = mapping[from];
|
|
}
|
|
item.converted = true;
|
|
}
|
|
});
|
|
array.forEach(this._selected[_theOther[type]], function(item){
|
|
for(var i = 0, len = item.except.length; i < len; ++i){
|
|
var from = item.except[i];
|
|
if(from in mapping){
|
|
item.except[i] = mapping[from];
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
array.forEach(pointSet, cleanUp);
|
|
|
|
this._refreshSelected(true);
|
|
this._focusPoint(type, this._lastEndPoint);
|
|
}catch(e){
|
|
console.warn("Selector._onInternalRearrange() error",e);
|
|
}
|
|
},
|
|
_onExternalChange: function(type, target){
|
|
var start = type == "cell" ? target.min : target[0],
|
|
end = type == "cell" ? target.max : target[target.length - 1];
|
|
this.selectRange(type, start, end);
|
|
},
|
|
_refresh: function(type, toHighlight){
|
|
if(!this._keyboardSelect[type]){
|
|
array.forEach(this._selected[type], function(item){
|
|
this._highlightSingle(type, toHighlight, item, undefined, true);
|
|
}, this);
|
|
}
|
|
},
|
|
_refreshSelected: function(){
|
|
this._refresh("col", true);
|
|
this._refresh("row", true);
|
|
this._refresh("cell", true);
|
|
},
|
|
_initAreas: function(){
|
|
var g = this.grid, f = g.focus, _this = this,
|
|
keyboardSelectReady = 1, duringKeyboardSelect = 2,
|
|
onmove = function(type, createNewEnd, rowStep, colStep, evt){
|
|
//Keyboard swipe selection is SHIFT + Direction Keys.
|
|
var ks = _this._keyboardSelect;
|
|
//Tricky, rely on valid status not being 0.
|
|
if(evt.shiftKey && ks[type]){
|
|
if(ks[type] === keyboardSelectReady){
|
|
if(type === "cell"){
|
|
var item = _this._lastEndPoint[type];
|
|
if(f.cell != g.layout.cells[item.col + colStep] || f.rowIndex != item.row + rowStep){
|
|
ks[type] = 0;
|
|
return;
|
|
}
|
|
}
|
|
//If selecting is not started, start it
|
|
_this._startSelect(type, _this._lastAnchorPoint[type], true, false, true);
|
|
_this._highlight(type, _this._lastEndPoint[type], _this._toSelect);
|
|
ks[type] = duringKeyboardSelect;
|
|
}
|
|
//Highlight to the new end point.
|
|
var newEnd = createNewEnd(type, rowStep, colStep, evt);
|
|
if(_this._isValid(type, newEnd, g)){
|
|
_this._highlight(type, newEnd, _this._toSelect);
|
|
}
|
|
_stopEvent(evt);
|
|
}
|
|
},
|
|
onkeydown = function(type, getTarget, evt, isBubble){
|
|
if(isBubble && _this.selectEnabled() && _this._config[type] != DISABLED){
|
|
switch(evt.keyCode){
|
|
case keys.SPACE:
|
|
//Keyboard single point selection is SPACE.
|
|
_this._startSelect(type, getTarget(), evt.ctrlKey, evt.shiftKey);
|
|
_this._endSelect(type);
|
|
break;
|
|
case keys.SHIFT:
|
|
//Keyboard swipe selection starts with SHIFT.
|
|
if(_this._config[type] == MULTI && _this._isValid(type, _this._lastAnchorPoint[type], g)){
|
|
//End last selection if any.
|
|
_this._endSelect(type);
|
|
_this._keyboardSelect[type] = keyboardSelectReady;
|
|
_this._usingKeyboard = true;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
onkeyup = function(type, evt, isBubble){
|
|
if(isBubble && evt.keyCode == keys.SHIFT && _this._keyboardSelect[type]){
|
|
_this._endSelect(type);
|
|
_this._keyboardSelect[type] = 0;
|
|
}
|
|
};
|
|
//TODO: this area "rowHeader" should be put outside, same level as header/content.
|
|
if(g.views.views[0] instanceof _RowSelector){
|
|
this._lastFocusedRowBarIdx = 0;
|
|
f.addArea({
|
|
name:"rowHeader",
|
|
onFocus: function(evt, step){
|
|
var view = g.views.views[0];
|
|
if(view instanceof _RowSelector){
|
|
var rowBarNode = view.getCellNode(_this._lastFocusedRowBarIdx, 0);
|
|
if(rowBarNode){
|
|
html.toggleClass(rowBarNode, f.focusClass, false);
|
|
}
|
|
//evt might not be real event, it may be a mock object instead.
|
|
if(evt && "rowIndex" in evt){
|
|
if(evt.rowIndex >= 0){
|
|
_this._lastFocusedRowBarIdx = evt.rowIndex;
|
|
}else if(!_this._lastFocusedRowBarIdx){
|
|
_this._lastFocusedRowBarIdx = 0;
|
|
}
|
|
}
|
|
rowBarNode = view.getCellNode(_this._lastFocusedRowBarIdx, 0);
|
|
if(rowBarNode){
|
|
dijitFocus.focus(rowBarNode);
|
|
html.toggleClass(rowBarNode, f.focusClass, true);
|
|
}
|
|
f.rowIndex = _this._lastFocusedRowBarIdx;
|
|
_stopEvent(evt);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
onBlur: function(evt, step){
|
|
var view = g.views.views[0];
|
|
if(view instanceof _RowSelector){
|
|
var rowBarNode = view.getCellNode(_this._lastFocusedRowBarIdx, 0);
|
|
if(rowBarNode){
|
|
html.toggleClass(rowBarNode, f.focusClass, false);
|
|
}
|
|
_stopEvent(evt);
|
|
}
|
|
return true;
|
|
},
|
|
onMove: function(rowStep, colStep, evt){
|
|
var view = g.views.views[0];
|
|
if(rowStep && view instanceof _RowSelector){
|
|
var next = _this._lastFocusedRowBarIdx + rowStep;
|
|
if(next >= 0 && next < g.rowCount){
|
|
//TODO: these logic require a better Scroller.
|
|
_stopEvent(evt);
|
|
var rowBarNode = view.getCellNode(_this._lastFocusedRowBarIdx, 0);
|
|
html.toggleClass(rowBarNode, f.focusClass, false);
|
|
//If the row is not fetched, fetch it.
|
|
var sc = g.scroller;
|
|
var lastPageRow = sc.getLastPageRow(sc.page);
|
|
var rc = g.rowCount - 1, row = Math.min(rc, next);
|
|
if(next > lastPageRow){
|
|
g.setScrollTop(g.scrollTop + sc.findScrollTop(row) - sc.findScrollTop(_this._lastFocusedRowBarIdx));
|
|
}
|
|
//Now we have fetched the row.
|
|
rowBarNode = view.getCellNode(next, 0);
|
|
dijitFocus.focus(rowBarNode);
|
|
html.toggleClass(rowBarNode, f.focusClass, true);
|
|
_this._lastFocusedRowBarIdx = next;
|
|
//If the row is out of view, scroll to it.
|
|
f.cell = rowBarNode;
|
|
f.cell.view = view;
|
|
f.cell.getNode = function(index){
|
|
return f.cell;
|
|
};
|
|
f.rowIndex = _this._lastFocusedRowBarIdx;
|
|
f.scrollIntoView();
|
|
f.cell = null;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
f.placeArea("rowHeader","before","content");
|
|
}
|
|
//Support keyboard selection.
|
|
f.addArea({
|
|
name:"cellselect",
|
|
onMove: lang.partial(onmove, "cell", function(type, rowStep, colStep, evt){
|
|
var current = _this._currentPoint[type];
|
|
return _createItem("cell", current.row + rowStep, current.col + colStep);
|
|
}),
|
|
onKeyDown: lang.partial(onkeydown, "cell", function(){
|
|
return _createItem("cell", f.rowIndex, f.cell.index);
|
|
}),
|
|
onKeyUp: lang.partial(onkeyup, "cell")
|
|
});
|
|
f.placeArea("cellselect","below","content");
|
|
f.addArea({
|
|
name:"colselect",
|
|
onMove: lang.partial(onmove, "col", function(type, rowStep, colStep, evt){
|
|
var current = _this._currentPoint[type];
|
|
return _createItem("col", current.col + colStep);
|
|
}),
|
|
onKeyDown: lang.partial(onkeydown, "col", function(){
|
|
return _createItem("col", f.getHeaderIndex());
|
|
}),
|
|
onKeyUp: lang.partial(onkeyup, "col")
|
|
});
|
|
f.placeArea("colselect","below","header");
|
|
f.addArea({
|
|
name:"rowselect",
|
|
onMove: lang.partial(onmove, "row", function(type, rowStep, colStep, evt){
|
|
return _createItem("row", f.rowIndex);
|
|
}),
|
|
onKeyDown: lang.partial(onkeydown, "row", function(){
|
|
return _createItem("row", f.rowIndex);
|
|
}),
|
|
onKeyUp: lang.partial(onkeyup, "row")
|
|
});
|
|
f.placeArea("rowselect","below","rowHeader");
|
|
},
|
|
_clearSelection: function(type, reservedItem){
|
|
// summary:
|
|
// Clear selection for given type and fire events, but retain the highlight for *reservedItem*,
|
|
// thus avoid "flashing".
|
|
// tag:
|
|
// private
|
|
// type: String
|
|
// "row", "col", or "cell
|
|
// reservedItem: __SelectItem
|
|
// The item to retain highlight.
|
|
if(type == "all"){
|
|
this._clearSelection("cell", reservedItem);
|
|
this._clearSelection("col", reservedItem);
|
|
this._clearSelection("row", reservedItem);
|
|
return;
|
|
}
|
|
this._isUsingRowSelector = true;
|
|
array.forEach(this._selected[type], function(item){
|
|
if(!_isEqual(type, reservedItem, item)){
|
|
this._highlightSingle(type, false, item);
|
|
}
|
|
}, this);
|
|
this._blurPoint(type, this._currentPoint);
|
|
this._selecting[type] = false;
|
|
this._startPoint[type] = this._currentPoint[type] = null;
|
|
this._selected[type] = [];
|
|
|
|
//Have to also deselect default grid selection.
|
|
if(type == "row" && !this.grid._selectingRange){
|
|
this._oldDeselectAll.call(this.grid.selection);
|
|
this.grid.selection._selectedById = {};
|
|
}
|
|
|
|
//Fire events.
|
|
this.grid.onEndDeselect(type, null, null, this._selected);
|
|
this.grid.onSelectCleared(type);
|
|
},
|
|
_startSelect: function(type, start, extending, isRange, mandatarySelect, toSelect){
|
|
// summary:
|
|
// Start selection, setup start point and current point, fire events.
|
|
// tag:
|
|
// private
|
|
// type: String
|
|
// "row", "col", or "cell"
|
|
// extending: Boolean
|
|
// Whether this is a multi selection
|
|
// isRange: Boolean
|
|
// Whether this is a range selection (i.e. select from the last end point to this point)
|
|
// start: __SelectItem
|
|
// The start point
|
|
// mandatarySelect: Boolean
|
|
// If true, toSelect will be same as the original selection status.
|
|
if(!this._isValid(type, start)){
|
|
return;
|
|
}
|
|
var lastIsSelected = this._isSelected(type, this._lastEndPoint[type]),
|
|
isSelected = this._isSelected(type, start);
|
|
|
|
if(this.noClear && !extending){
|
|
this._toSelect = toSelect === undefined ? true : toSelect;
|
|
}else{
|
|
//If we are modifying the selection using keyboard, retain the old status.
|
|
this._toSelect = mandatarySelect ? isSelected : !isSelected;
|
|
}
|
|
|
|
//If CTRL is not pressed or it's SINGLE mode, this is a brand new selection.
|
|
if(!extending || (!isSelected && this._config[type] == SINGLE)){
|
|
this._clearSelection("col", start);
|
|
this._clearSelection("cell", start);
|
|
if(!this.noClear || (type === 'row' && this._config[type] == SINGLE)){
|
|
this._clearSelection('row', start);
|
|
}
|
|
this._toSelect = toSelect === undefined ? true : toSelect;
|
|
}
|
|
|
|
this._selecting[type] = true;
|
|
this._currentPoint[type] = null;
|
|
|
|
//We're holding SHIFT while clicking, it's a Click-Range selection.
|
|
if(isRange && this._lastType == type && lastIsSelected == this._toSelect && this._config[type] == MULTI){
|
|
if(type === "row"){
|
|
this._isUsingRowSelector = true;
|
|
}
|
|
this._startPoint[type] = this._lastEndPoint[type];
|
|
this._highlight(type, this._startPoint[type]);
|
|
this._isUsingRowSelector = false;
|
|
}else{
|
|
this._startPoint[type] = start;
|
|
}
|
|
//Now start selection
|
|
this._curType = type;
|
|
this._fireEvent("start", type);
|
|
this._isStartFocus = true;
|
|
this._isUsingRowSelector = true;
|
|
this._highlight(type, start, this._toSelect);
|
|
this._isStartFocus = false;
|
|
},
|
|
_endSelect: function(type){
|
|
// summary:
|
|
// End selection. Keep records, fire events and cleanup status.
|
|
// tag:
|
|
// private
|
|
// type: String
|
|
// "row", "col", or "cell"
|
|
if(type === "row"){
|
|
delete this._isUsingRowSelector;
|
|
}
|
|
if(type == "all"){
|
|
this._endSelect("col");
|
|
this._endSelect("row");
|
|
this._endSelect("cell");
|
|
}else if(this._selecting[type]){
|
|
this._addToSelected(type);
|
|
this._lastAnchorPoint[type] = this._startPoint[type];
|
|
this._lastEndPoint[type] = this._currentPoint[type];
|
|
if(this._toSelect){
|
|
this._lastSelectedAnchorPoint[type] = this._lastAnchorPoint[type];
|
|
this._lastSelectedEndPoint[type] = this._lastEndPoint[type];
|
|
}
|
|
this._startPoint[type] = this._currentPoint[type] = null;
|
|
this._selecting[type] = false;
|
|
this._lastType = type;
|
|
this._fireEvent("end", type);
|
|
}
|
|
},
|
|
_fireEvent: function(evtName, type){
|
|
switch(evtName){
|
|
case "start":
|
|
this.grid[this._toSelect ? "onStartSelect" : "onStartDeselect"](type, this._startPoint[type], this._selected);
|
|
break;
|
|
case "end":
|
|
this.grid[this._toSelect ? "onEndSelect" : "onEndDeselect"](type, this._lastAnchorPoint[type], this._lastEndPoint[type], this._selected);
|
|
break;
|
|
}
|
|
},
|
|
_calcToHighlight: function(type, target, toHighlight, toSelect){
|
|
// summary:
|
|
// Calculate what status should *target* have.
|
|
// If *toSelect* is not provided, this is a no op.
|
|
// This function is time-critical!!
|
|
if(toSelect !== undefined){
|
|
var sltd;
|
|
if(this._usingKeyboard && !toHighlight){
|
|
var last = this._isInLastRange(this._lastType, target);
|
|
if(last){
|
|
sltd = this._isSelected(type, target);
|
|
//This 2 cases makes the keyboard swipe selection valid!
|
|
if(toSelect && sltd){
|
|
return false;
|
|
}
|
|
if(!toSelect && !sltd && this._isInLastRange(this._lastType, target, true)){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return toHighlight ? toSelect : (sltd || this._isSelected(type, target));
|
|
}
|
|
return toHighlight;
|
|
},
|
|
_highlightNode: function(node, toHighlight){
|
|
// summary:
|
|
// Do the actual highlight work.
|
|
if(node){
|
|
var selectCSSClass = "dojoxGridRowSelected";
|
|
var selectCellClass = "dojoxGridCellSelected";
|
|
html.toggleClass(node, selectCSSClass, toHighlight);
|
|
html.toggleClass(node, selectCellClass, toHighlight);
|
|
}
|
|
},
|
|
_highlightHeader: function(colIdx, toHighlight){
|
|
var cells = this.grid.layout.cells;
|
|
var node = cells[colIdx].getHeaderNode();
|
|
var selectedClass = "dojoxGridHeaderSelected";
|
|
html.toggleClass(node, selectedClass, toHighlight);
|
|
},
|
|
_highlightRowSelector: function(rowIdx, toHighlight){
|
|
//var t1 = (new Date()).getTime();
|
|
var rowSelector = this.grid.views.views[0];
|
|
if(rowSelector instanceof _RowSelector){
|
|
var node = rowSelector.getRowNode(rowIdx);
|
|
if(node){
|
|
var selectedClass = "dojoxGridRowSelectorSelected";
|
|
html.toggleClass(node, selectedClass, toHighlight);
|
|
}
|
|
}
|
|
//console.log((new Date()).getTime() - t1);
|
|
},
|
|
_highlightSingle: function(type, toHighlight, target, toSelect, isRefresh){
|
|
// summary:
|
|
// Highlight a single item.
|
|
// This function is time critical!!
|
|
var _this = this, toHL, g = _this.grid, cells = g.layout.cells;
|
|
switch(type){
|
|
case "cell":
|
|
toHL = this._calcToHighlight(type, target, toHighlight, toSelect);
|
|
var c = cells[target.col];
|
|
if(!c.hidden && !c.notselectable){
|
|
this._highlightNode(target.node || c.getNode(target.row), toHL);
|
|
}
|
|
break;
|
|
case "col":
|
|
toHL = this._calcToHighlight(type, target, toHighlight, toSelect);
|
|
this._highlightHeader(target.col, toHL);
|
|
query("td[idx='" + target.col + "']", g.domNode).forEach(function(cellNode){
|
|
var rowNode = cells[target.col].view.content.findRowTarget(cellNode);
|
|
if(rowNode){
|
|
var rowIndex = rowNode[dojox.grid.util.rowIndexTag];
|
|
_this._highlightSingle("cell", toHL, {
|
|
"row": rowIndex,
|
|
"col": target.col,
|
|
"node": cellNode
|
|
});
|
|
}
|
|
});
|
|
break;
|
|
case "row":
|
|
toHL = this._calcToHighlight(type, target, toHighlight, toSelect);
|
|
this._highlightRowSelector(target.row, toHL);
|
|
if(this._config.cell){
|
|
array.forEach(cells, function(cell){
|
|
_this._highlightSingle("cell", toHL, {
|
|
"row": target.row,
|
|
"col": cell.index,
|
|
"node": cell.getNode(target.row)
|
|
});
|
|
});
|
|
}
|
|
//To avoid dead lock
|
|
this._selectedRowModified = true;
|
|
if(!isRefresh){
|
|
g.selection.setSelected(target.row, toHL);
|
|
}
|
|
}
|
|
},
|
|
_highlight: function(type, target, toSelect){
|
|
// summary:
|
|
// Highlight from start point to target.
|
|
// toSelect: Boolean
|
|
// Whether we are selecting or deselecting.
|
|
// This function is time critical!!
|
|
if(this._selecting[type] && target !== null){
|
|
var start = this._startPoint[type],
|
|
current = this._currentPoint[type],
|
|
_this = this,
|
|
highlight = function(from, to, toHL){
|
|
_this._forEach(type, from, to, function(item){
|
|
_this._highlightSingle(type, toHL, item, toSelect);
|
|
}, true);
|
|
};
|
|
switch(type){
|
|
case "col": case "row":
|
|
if(current !== null){
|
|
if(_inRange(type, target, start, current, true)){
|
|
//target is between start and current, some selected should be deselected.
|
|
highlight(current, target, false);
|
|
}else{
|
|
if(_inRange(type, start, target, current, true)){
|
|
//selection has jumped to different direction, all should be deselected.
|
|
highlight(current, start, false);
|
|
current = start;
|
|
}
|
|
highlight(target, current, true);
|
|
}
|
|
}else{
|
|
//First time select.
|
|
this._highlightSingle(type, true, target, toSelect);
|
|
}
|
|
break;
|
|
case "cell":
|
|
if(current !== null){
|
|
if(_inRange("row", target, start, current, true) ||
|
|
_inRange("col", target, start, current, true) ||
|
|
_inRange("row", start, target, current, true) ||
|
|
_inRange("col", start, target, current, true)){
|
|
highlight(start, current, false);
|
|
}
|
|
}
|
|
highlight(start, target, true);
|
|
}
|
|
this._currentPoint[type] = target;
|
|
this._focusPoint(type, this._currentPoint);
|
|
}
|
|
},
|
|
_focusPoint: function(type, point){
|
|
// summary:
|
|
// Focus the current point, so when you move mouse, the focus indicator follows you.
|
|
if(!this._isStartFocus){
|
|
var current = point[type],
|
|
f = this.grid.focus;
|
|
if(type == "col"){
|
|
f._colHeadFocusIdx = current.col;
|
|
f.focusArea("header");
|
|
}else if(type == "row"){
|
|
f.focusArea("rowHeader", {
|
|
"rowIndex": current.row
|
|
});
|
|
}else if(type == "cell"){
|
|
f.setFocusIndex(current.row, current.col);
|
|
}
|
|
}
|
|
},
|
|
_blurPoint: function(type, point){
|
|
// summary:
|
|
// Blur the current point.
|
|
var f = this.grid.focus;
|
|
if(type == "col"){
|
|
f._blurHeader();
|
|
}else if(type == "cell"){
|
|
f._blurContent();
|
|
}
|
|
},
|
|
_addToSelected: function(type){
|
|
// summary:
|
|
// Record the selected items.
|
|
var toSelect = this._toSelect, _this = this,
|
|
toAdd = [], toRemove = [],
|
|
start = this._startPoint[type],
|
|
end = this._currentPoint[type];
|
|
if(this._usingKeyboard){
|
|
//If using keyboard, selection will be ended after every move. But we have to remember the original selection status,
|
|
//so as to return to correct status when we shrink the selection region.
|
|
this._forEach(type, this._lastAnchorPoint[type], this._lastEndPoint[type], function(item){
|
|
//If the original selected item is not in current range, change its status.
|
|
if(!_inRange(type, item, start, end)){
|
|
(toSelect ? toRemove : toAdd).push(item);
|
|
}
|
|
});
|
|
}
|
|
this._forEach(type, start, end, function(item){
|
|
var isSelected = _this._isSelected(type, item);
|
|
if(toSelect && !isSelected){
|
|
//Add new selected items
|
|
toAdd.push(item);
|
|
}else if(!toSelect){
|
|
//Remove deselected items.
|
|
toRemove.push(item);
|
|
}
|
|
});
|
|
this._add(type, toAdd);
|
|
this._remove(type, toRemove);
|
|
|
|
// have to keep record in original grid selection
|
|
array.forEach(this._selected.row, function(item){
|
|
if(item.except.length > 0){
|
|
//to avoid dead lock
|
|
this._selectedRowModified = true;
|
|
this.grid.selection.setSelected(item.row, false);
|
|
}
|
|
}, this);
|
|
},
|
|
_forEach: function(type, start, end, func, halfClose){
|
|
// summary:
|
|
// Go through items from *start* point to *end* point.
|
|
// This function is time critical!!
|
|
if(!this._isValid(type, start, true) || !this._isValid(type, end, true)){
|
|
return;
|
|
}
|
|
switch(type){
|
|
case "col": case "row":
|
|
start = start[type];
|
|
end = end[type];
|
|
var dir = end > start ? 1 : -1;
|
|
if(!halfClose){
|
|
end += dir;
|
|
}
|
|
for(; start != end; start += dir){
|
|
func(_createItem(type, start));
|
|
}
|
|
break;
|
|
case "cell":
|
|
var colDir = end.col > start.col ? 1 : -1,
|
|
rowDir = end.row > start.row ? 1 : -1;
|
|
for(var i = start.row, p = end.row + rowDir; i != p; i += rowDir){
|
|
for(var j = start.col, q = end.col + colDir; j != q; j += colDir){
|
|
func(_createItem(type, i, j));
|
|
}
|
|
}
|
|
}
|
|
},
|
|
_makeupForExceptions: function(type, newCellItems){
|
|
// summary:
|
|
// When new cells is selected, maybe they will fill in the "holes" in selected rows and columns.
|
|
var makedUps = [];
|
|
array.forEach(this._selected[type], function(v1){
|
|
array.forEach(newCellItems, function(v2){
|
|
if(v1[type] == v2[type]){
|
|
var pos = array.indexOf(v1.except, v2[_theOther[type]]);
|
|
if(pos >= 0){
|
|
v1.except.splice(pos, 1);
|
|
}
|
|
makedUps.push(v2);
|
|
}
|
|
});
|
|
});
|
|
return makedUps;
|
|
},
|
|
_makeupForCells: function(type, newItems){
|
|
// summary:
|
|
// When some rows/cols are selected, maybe they can cover some of the selected cells,
|
|
// and fill some of the "holes" in the selected cols/rows.
|
|
var toRemove = [];
|
|
array.forEach(this._selected.cell, function(v1){
|
|
array.some(newItems, function(v2){
|
|
if(v1[type] == v2[type]){
|
|
toRemove.push(v1);
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
});
|
|
this._remove("cell", toRemove);
|
|
array.forEach(this._selected[_theOther[type]], function(v1){
|
|
array.forEach(newItems, function(v2){
|
|
var pos = array.indexOf(v1.except, v2[type]);
|
|
if(pos >= 0){
|
|
v1.except.splice(pos, 1);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
_addException: function(type, items){
|
|
// summary:
|
|
// If some rows/cols are deselected, maybe they have created "holes" in selected cols/rows.
|
|
array.forEach(this._selected[type], function(v1){
|
|
array.forEach(items, function(v2){
|
|
v1.except.push(v2[_theOther[type]]);
|
|
});
|
|
});
|
|
},
|
|
_addCellException: function(type, items){
|
|
// summary:
|
|
// If some cells are deselected, maybe they have created "holes" in selected rows/cols.
|
|
array.forEach(this._selected[type], function(v1){
|
|
array.forEach(items, function(v2){
|
|
if(v1[type] == v2[type]){
|
|
v1.except.push(v2[_theOther[type]]);
|
|
}
|
|
});
|
|
});
|
|
},
|
|
_add: function(type, items){
|
|
// summary:
|
|
// Add to the selection record.
|
|
var cells = this.grid.layout.cells;
|
|
if(type == "cell"){
|
|
var colMakedup = this._makeupForExceptions("col", items);
|
|
var rowMakedup = this._makeupForExceptions("row", items);
|
|
//Step over hidden columns.
|
|
items = array.filter(items, function(item){
|
|
return array.indexOf(colMakedup, item) < 0 && array.indexOf(rowMakedup, item) < 0 &&
|
|
!cells[item.col].hidden && !cells[item.col].notselectable;
|
|
});
|
|
}else{
|
|
if(type == "col"){
|
|
//Step over hidden columns.
|
|
items = array.filter(items, function(item){
|
|
return !cells[item.col].hidden && !cells[item.col].notselectable;
|
|
});
|
|
}
|
|
this._makeupForCells(type, items);
|
|
this._selected[type] = array.filter(this._selected[type], function(v){
|
|
return array.every(items, function(item){
|
|
return v[type] !== item[type];
|
|
});
|
|
});
|
|
}
|
|
if(type != "col" && this.grid._hasIdentity){
|
|
array.forEach(items, function(item){
|
|
var record = this.grid._by_idx[item.row];
|
|
if(record){
|
|
item.id = record.idty;
|
|
}
|
|
}, this);
|
|
}
|
|
this._selected[type] = this._selected[type].concat(items);
|
|
},
|
|
_remove: function(type, items){
|
|
// summary:
|
|
// Remove from the selection record.
|
|
var comp = lang.partial(_isEqual, type);
|
|
this._selected[type] = array.filter(this._selected[type], function(v1){
|
|
return !array.some(items, function(v2){
|
|
return comp(v1, v2);
|
|
});
|
|
});
|
|
if(type == "cell"){
|
|
this._addCellException("col", items);
|
|
this._addCellException("row", items);
|
|
}else if(this._config.cell){
|
|
this._addException(_theOther[type], items);
|
|
}
|
|
},
|
|
_isCellNotInExcept: function(type, item){
|
|
// summary:
|
|
// Return true only when a cell is covered by selected row/col, and its not a "hole".
|
|
var attr = item[type], corres = item[_theOther[type]];
|
|
return array.some(this._selected[type], function(v){
|
|
return v[type] == attr && array.indexOf(v.except, corres) < 0;
|
|
});
|
|
},
|
|
_isSelected: function(type, item){
|
|
// summary:
|
|
// Return true when the item is selected. (or logically selected, i.e, covered by a row/col).
|
|
if(!item){ return false; }
|
|
var res = array.some(this._selected[type], function(v){
|
|
var ret = _isEqual(type, item, v);
|
|
if(ret && type !== "cell"){
|
|
return v.except.length === 0;
|
|
}
|
|
return ret;
|
|
});
|
|
if(!res && type === "cell"){
|
|
res = (this._isCellNotInExcept("col", item) || this._isCellNotInExcept("row", item));
|
|
if(type === "cell"){
|
|
res = res && !this.grid.layout.cells[item.col].notselectable;
|
|
}
|
|
}
|
|
return res;
|
|
},
|
|
_isInLastRange: function(type, item, isSelected){
|
|
// summary:
|
|
// Return true only when the item is in the last seletion/deseletion range.
|
|
var start = this[isSelected ? "_lastSelectedAnchorPoint" : "_lastAnchorPoint"][type],
|
|
end = this[isSelected ? "_lastSelectedEndPoint" : "_lastEndPoint"][type];
|
|
if(!item || !start || !end){ return false; }
|
|
return _inRange(type, item, start, end);
|
|
},
|
|
_isValid: function(type, item, allowNotSelectable){
|
|
// summary:
|
|
// Check whether the item is a valid __SelectItem for the given type.
|
|
if(!item){ return false; }
|
|
try{
|
|
var g = this.grid, index = item[type];
|
|
switch(type){
|
|
case "col":
|
|
return index >= 0 && index < g.layout.cells.length && lang.isArray(item.except) &&
|
|
(allowNotSelectable || !g.layout.cells[index].notselectable);
|
|
case "row":
|
|
return index >= 0 && index < g.rowCount && lang.isArray(item.except);
|
|
case "cell":
|
|
return item.col >= 0 && item.col < g.layout.cells.length &&
|
|
item.row >= 0 && item.row < g.rowCount &&
|
|
(allowNotSelectable || !g.layout.cells[item.col].notselectable);
|
|
}
|
|
}catch(e){}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
EnhancedGrid.registerPlugin(Selector/*name:'selector'*/, {
|
|
"dependency": ["autoScroll"]
|
|
});
|
|
|
|
return Selector;
|
|
|
|
}); |