define([
|
'dojo/_base/lang',
|
'dojo/_base/declare',
|
'dojo/_base/array',
|
'dojo/dom-class',
|
'dojo/on',
|
'dojo/query',
|
'dojo/dnd/Source'
|
], function (lang, declare, arrayUtil, domClass, on, query, DndSource) {
|
var dndTypeRx = /(\d+)(?:-(\d+))?$/; // used to determine subrow from dndType
|
|
// The following 2 functions are used by onDropInternal logic for
|
// retrieving/modifying a given subRow. The `match` variable in each is
|
// expected to be the result of executing dndTypeRx on a subRow ID.
|
|
function getMatchingSubRow(grid, match) {
|
var hasColumnSets = match[2],
|
rowOrSet = grid[hasColumnSets ? 'columnSets' : 'subRows'][match[1]];
|
|
return hasColumnSets ? rowOrSet[match[2]] : rowOrSet;
|
}
|
|
function setMatchingSubRow(grid, match, subRow) {
|
if (match[2]) {
|
grid.columnSets[match[1]][match[2]] = subRow;
|
}
|
else {
|
grid.subRows[match[1]] = subRow;
|
}
|
}
|
|
// Builds a prefix for a dndtype value based on a grid id.
|
function makeDndTypePrefix(gridId) {
|
return 'dgrid-' + gridId + '-';
|
}
|
|
// Removes the grid id prefix from a dndtype value. This allows the grid id to contain
|
// a dash-number suffix. This works only if a column is dropped on the grid from which it
|
// originated. Otherwise, a dash-number suffix will cause the regex to match on the wrong values.
|
function stripIdPrefix(gridId, dndtype) {
|
return dndtype.slice(makeDndTypePrefix(gridId).length);
|
}
|
|
var ColumnDndSource = declare(DndSource, {
|
// summary:
|
// Custom dojo/dnd source extension configured specifically for
|
// dgrid column reordering.
|
|
copyState: function () {
|
return false; // never copy
|
},
|
|
checkAcceptance: function (source) {
|
return source === this; // self-accept only
|
},
|
|
_legalMouseDown: function (evt) {
|
// Overridden to prevent blocking ColumnResizer resize handles.
|
return evt.target.className.indexOf('dgrid-resize-handle') > -1 ? false :
|
this.inherited(arguments);
|
},
|
|
onDropInternal: function (nodes) {
|
var grid = this.grid,
|
match = dndTypeRx.exec(stripIdPrefix(grid.id, nodes[0].getAttribute('dndType'))),
|
structureProperty = match[2] ? 'columnSets' : 'subRows',
|
oldSubRow = getMatchingSubRow(grid, match),
|
columns = grid.columns;
|
|
// First, allow original DnD logic to place node in new location.
|
this.inherited(arguments);
|
|
if (!match) {
|
return;
|
}
|
|
// Then, iterate through the header cells in their new order,
|
// to populate a new row array to assign as a new sub-row to the grid.
|
// (Wait until the next turn to avoid errors in Opera.)
|
setTimeout(function () {
|
var newSubRow = arrayUtil.map(nodes[0].parentNode.childNodes, function (col) {
|
return columns[col.columnId];
|
}),
|
eventObject;
|
|
setMatchingSubRow(grid, match, newSubRow);
|
|
eventObject = {
|
grid: grid,
|
subRow: newSubRow,
|
column: columns[nodes[0].columnId],
|
bubbles: true,
|
cancelable: true,
|
// Set parentType to indicate this is the result of user interaction.
|
parentType: 'dnd'
|
};
|
// Set columnSets or subRows depending on which the grid is using.
|
eventObject[structureProperty] = grid[structureProperty];
|
|
// Emit a custom event which passes the new structure.
|
// Allow calling preventDefault() to cancel the reorder operation.
|
if (on.emit(grid.domNode, 'dgrid-columnreorder', eventObject)) {
|
// Event was not canceled - force processing of modified structure.
|
grid.set(structureProperty, grid[structureProperty]);
|
}
|
else {
|
// Event was canceled - revert the structure and re-render the header
|
// (since the inherited logic invoked above will have shifted cells).
|
setMatchingSubRow(grid, match, oldSubRow);
|
grid.renderHeader();
|
// After re-rendering the header, re-apply the sort arrow if needed.
|
if (grid.sort.length) {
|
grid.updateSortArrow(grid.sort);
|
}
|
}
|
}, 0);
|
}
|
});
|
|
var ColumnReorder = declare(null, {
|
// summary:
|
// Extension allowing reordering of columns in a grid via drag'n'drop.
|
// Reordering of columns within the same subrow or columnset is also
|
// supported; between different ones is not.
|
|
// columnDndConstructor: Function
|
// Constructor to call for instantiating DnD sources within the grid's
|
// header.
|
columnDndConstructor: ColumnDndSource,
|
|
_initSubRowDnd: function (subRow, dndType) {
|
// summary:
|
// Initializes a dojo/dnd source for one subrow of a grid;
|
// this could be its only subrow, one of several, or a subrow within a
|
// columnset.
|
|
var dndParent, c, len, col, th;
|
|
for (c = 0, len = subRow.length; c < len; c++) {
|
col = subRow[c];
|
if (col.reorderable === false) {
|
continue;
|
}
|
|
th = col.headerNode;
|
// Add dojoDndItem class, and a dndType unique to this subrow.
|
domClass.add(th, 'dojoDndItem');
|
th.setAttribute('dndType', dndType);
|
|
if (!dndParent) {
|
dndParent = th.parentNode;
|
}
|
}
|
|
if (dndParent) {
|
this._columnDndSources.push(new this.columnDndConstructor(dndParent, {
|
horizontal: true,
|
grid: this
|
}));
|
}
|
// (If dndParent wasn't set, no columns are draggable)
|
},
|
|
renderHeader: function () {
|
var dndTypePrefix = makeDndTypePrefix(this.id),
|
csLength, cs;
|
|
this.inherited(arguments);
|
|
// After header is rendered, set up a dnd source on each of its subrows.
|
|
this._columnDndSources = [];
|
|
if (this.columnSets) {
|
// Iterate columnsets->subrows->columns.
|
for (cs = 0, csLength = this.columnSets.length; cs < csLength; cs++) {
|
arrayUtil.forEach(this.columnSets[cs], function (subRow, sr) {
|
this._initSubRowDnd(subRow, dndTypePrefix + cs + '-' + sr);
|
}, this);
|
}
|
}
|
else {
|
// Iterate subrows->columns.
|
arrayUtil.forEach(this.subRows, function (subRow, sr) {
|
this._initSubRowDnd(subRow, dndTypePrefix + sr);
|
}, this);
|
}
|
},
|
|
_destroyColumns: function () {
|
if (this._columnDndSources) {
|
// Destroy old dnd sources.
|
arrayUtil.forEach(this._columnDndSources, function (source) {
|
source.destroy();
|
});
|
}
|
|
this.inherited(arguments);
|
}
|
});
|
|
ColumnReorder.ColumnDndSource = ColumnDndSource;
|
return ColumnReorder;
|
});
|