274 lines
No EOL
9.1 KiB
JavaScript
274 lines
No EOL
9.1 KiB
JavaScript
(function ($) {
|
|
// register namespace
|
|
$.extend(true, window, {
|
|
"Slick": {
|
|
"CellExternalCopyManager": CellExternalCopyManager
|
|
}
|
|
});
|
|
|
|
|
|
function CellExternalCopyManager(options) {
|
|
/*
|
|
This manager enables users to copy/paste data from/to an external Spreadsheet application
|
|
|
|
Since it is not possible to access directly the clipboard in javascript, the plugin uses
|
|
a trick to do it's job. After detecting the keystroke, we dynamically create a textarea
|
|
where the browser copies/pastes the serialized data.
|
|
|
|
options:
|
|
copiedCellStyle : sets the css className used for copied cells. default : "copied"
|
|
copiedCellStyleLayerKey : sets the layer key for setting css values of copied cells. default : "copy-manager"
|
|
dataItemColumnValueExtractor : option to specify a custom column value extractor function
|
|
dataItemColumnValueSetter : option to specify a custom column value setter function
|
|
*/
|
|
var _grid;
|
|
var _self = this;
|
|
var _copiedRanges;
|
|
var _options = options || {};
|
|
var _copiedCellStyleLayerKey = _options.copiedCellStyleLayerKey || "copy-manager";
|
|
var _copiedCellStyle = _options.copiedCellStyle || "copied";
|
|
var _clearCopyTI = 0;
|
|
|
|
var keyCodes = {
|
|
'C':67,
|
|
'V':86
|
|
}
|
|
|
|
function init(grid) {
|
|
_grid = grid;
|
|
_grid.onKeyDown.subscribe(handleKeyDown);
|
|
|
|
// we need a cell selection model
|
|
var cellSelectionModel = grid.getSelectionModel();
|
|
if (!cellSelectionModel){
|
|
throw new Error("Selection model is mandatory for this plugin. Please set a selection model on the grid before adding this plugin: grid.setSelectionModel(new Slick.CellSelectionModel())");
|
|
}
|
|
// we give focus on the grid when a selection is done on it.
|
|
// without this, if the user selects a range of cell without giving focus on a particular cell, the grid doesn't get the focus and key stroke handles (ctrl+c) don't work
|
|
cellSelectionModel.onSelectedRangesChanged.subscribe(function(e, args){
|
|
_grid.focus();
|
|
});
|
|
}
|
|
|
|
function destroy() {
|
|
_grid.onKeyDown.unsubscribe(handleKeyDown);
|
|
}
|
|
|
|
function getDataItemValueForColumn(item, columnDef) {
|
|
if (_options.dataItemColumnValueExtractor) {
|
|
return _options.dataItemColumnValueExtractor(item, columnDef);
|
|
}
|
|
// if a custom getter is not defined, we call serializeValue of the editor to serialize
|
|
var editorArgs = {
|
|
'container':$(document), // a dummy container
|
|
'column':columnDef
|
|
};
|
|
var editor = new columnDef.editor(editorArgs);
|
|
var retVal = '';
|
|
editor.loadValue(item);
|
|
retVal = editor.serializeValue();
|
|
editor.destroy();
|
|
|
|
return retVal;
|
|
}
|
|
|
|
function setDataItemValueForColumn(item, columnDef, value) {
|
|
if (_options.dataItemColumnValueSetter) {
|
|
return _options.dataItemColumnValueSetter(item, columnDef, value);
|
|
}
|
|
// if a custom setter is not defined, we call applyValue of the editor to unserialize
|
|
var editorArgs = {
|
|
'container':$(document), // a dummy container
|
|
'column':columnDef
|
|
};
|
|
var editor = new columnDef.editor(editorArgs);
|
|
editor.loadValue(item);
|
|
editor.applyValue(item, value);
|
|
editor.destroy();
|
|
}
|
|
|
|
|
|
function _createTextBox(innerText){
|
|
var ta = document.createElement('textarea');
|
|
ta.style.position = 'absolute';
|
|
ta.style.left = '-1000px';
|
|
ta.style.top = '-1000px';
|
|
ta.value = innerText;
|
|
document.body.appendChild(ta);
|
|
ta.focus();
|
|
|
|
return ta;
|
|
}
|
|
|
|
function _decodeTabularData(_grid, ta){
|
|
var columns = _grid.getColumns();
|
|
var clipText = ta.value;
|
|
var clipRows = clipText.split(/[\n\f\r]/);
|
|
var clippedRange = [];
|
|
|
|
document.body.removeChild(ta);
|
|
|
|
for (var i=0; i<clipRows.length; i++) {
|
|
if (clipRows[i]!="")
|
|
clippedRange[i] = clipRows[i].split("\t");
|
|
}
|
|
|
|
var selectedCell = _grid.getActiveCell();
|
|
var ranges = _grid.getSelectionModel().getSelectedRanges();
|
|
var selectedRange = ranges && ranges.length ? ranges[0] : null; // pick only one selection
|
|
var activeRow = null;
|
|
var activeCell = null;
|
|
|
|
if (selectedRange){
|
|
activeRow = selectedRange.fromRow;
|
|
activeCell = selectedRange.fromCell;
|
|
} else if (selectedCell){
|
|
activeRow = selectedCell.row;
|
|
activeCell = selectedCell.cell;
|
|
} else {
|
|
// we don't know where to paste
|
|
return;
|
|
}
|
|
|
|
var oneCellToMultiple = false;
|
|
var destH = clippedRange.length;
|
|
var destW = clippedRange.length ? clippedRange[0].length : 0;
|
|
if (clippedRange.length == 1 && clippedRange[0].length == 1 && selectedRange){
|
|
oneCellToMultiple = true;
|
|
destH = selectedRange.toRow - selectedRange.fromRow +1;
|
|
destW = selectedRange.toCell - selectedRange.fromCell +1;
|
|
}
|
|
|
|
var desty = activeRow;
|
|
var destx = activeCell;
|
|
var h = 0;
|
|
var w = 0;
|
|
|
|
for (var y = 0; y < destH; y++){
|
|
h++;
|
|
w=0;
|
|
for (var x = 0; x < destW; x++){
|
|
w++;
|
|
var desty = activeRow + y;
|
|
var destx = activeCell + x;
|
|
|
|
if (desty < data.length && destx < grid.getColumns().length ) {
|
|
var nd = _grid.getCellNode(desty, destx);
|
|
var dt = _grid.getDataItem(desty);
|
|
if (oneCellToMultiple)
|
|
setDataItemValueForColumn(dt, columns[destx], clippedRange[0][0]);
|
|
else
|
|
setDataItemValueForColumn(dt, columns[destx], clippedRange[y][x]);
|
|
_grid.updateCell(desty, destx);
|
|
}
|
|
}
|
|
}
|
|
|
|
var bRange = {
|
|
'fromCell': activeCell,
|
|
'fromRow': activeRow,
|
|
'toCell': activeCell+w-1,
|
|
'toRow': activeRow+h-1
|
|
}
|
|
|
|
markCopySelection([bRange]);
|
|
_grid.getSelectionModel().setSelectedRanges([bRange]);
|
|
_self.onPasteCells.notify({ranges: [bRange]});
|
|
}
|
|
|
|
|
|
function handleKeyDown(e, args) {
|
|
var ranges;
|
|
if (!_grid.getEditorLock().isActive()) {
|
|
if (e.which == $.ui.keyCode.ESCAPE) {
|
|
if (_copiedRanges) {
|
|
e.preventDefault();
|
|
clearCopySelection();
|
|
_self.onCopyCancelled.notify({ranges: _copiedRanges});
|
|
_copiedRanges = null;
|
|
}
|
|
}
|
|
|
|
if (e.which == keyCodes.C && (e.ctrlKey || e.metaKey)) { // CTRL + C
|
|
ranges = _grid.getSelectionModel().getSelectedRanges();
|
|
if (ranges.length != 0) {
|
|
_copiedRanges = ranges;
|
|
markCopySelection(ranges);
|
|
_self.onCopyCells.notify({ranges: ranges});
|
|
|
|
var columns = _grid.getColumns();
|
|
var clipTextArr = [];
|
|
|
|
for (var rg = 0; rg < ranges.length; rg++){
|
|
var range = ranges[rg];
|
|
var clipTextRows = [];
|
|
for (var i=range.fromRow; i< range.toRow+1 ; i++){
|
|
var clipTextCells = [];
|
|
var dt = _grid.getDataItem(i);
|
|
|
|
for (var j=range.fromCell; j< range.toCell+1 ; j++){
|
|
clipTextCells.push(getDataItemValueForColumn(dt, columns[j]));
|
|
}
|
|
clipTextRows.push(clipTextCells.join("\t"));
|
|
}
|
|
clipTextArr.push(clipTextRows.join("\r\n"));
|
|
}
|
|
var clipText = clipTextArr.join('');
|
|
var ta = _createTextBox(clipText);
|
|
$(ta).select();
|
|
|
|
setTimeout(function(){
|
|
document.body.removeChild(ta);
|
|
}, 100);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (e.which == keyCodes.V && (e.ctrlKey || e.metaKey)) { // CTRL + V
|
|
var ta = _createTextBox('');
|
|
|
|
setTimeout(function(){
|
|
_decodeTabularData(_grid, ta);
|
|
}, 100);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
function markCopySelection(ranges) {
|
|
clearCopySelection();
|
|
|
|
var columns = _grid.getColumns();
|
|
var hash = {};
|
|
for (var i = 0; i < ranges.length; i++) {
|
|
for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
|
|
hash[j] = {};
|
|
for (var k = ranges[i].fromCell; k <= ranges[i].toCell && k<columns.length; k++) {
|
|
hash[j][columns[k].id] = _copiedCellStyle;
|
|
}
|
|
}
|
|
}
|
|
_grid.setCellCssStyles(_copiedCellStyleLayerKey, hash);
|
|
clearTimeout(_clearCopyTI);
|
|
_clearCopyTI = setTimeout(function(){
|
|
_self.clearCopySelection();
|
|
}, 2000);
|
|
}
|
|
|
|
function clearCopySelection() {
|
|
_grid.removeCellCssStyles(_copiedCellStyleLayerKey);
|
|
}
|
|
|
|
$.extend(this, {
|
|
"init": init,
|
|
"destroy": destroy,
|
|
"clearCopySelection": clearCopySelection,
|
|
"handleKeyDown":handleKeyDown,
|
|
"onCopyCells": new Slick.Event(),
|
|
"onCopyCancelled": new Slick.Event(),
|
|
"onPasteCells": new Slick.Event()
|
|
});
|
|
}
|
|
})(jQuery); |