1 | (function ($) {
|
2 |
|
3 | $.extend(true, window, {
|
4 | "Slick": {
|
5 | "CellExternalCopyManager": CellExternalCopyManager
|
6 | }
|
7 | });
|
8 |
|
9 |
|
10 | function CellExternalCopyManager(options) {
|
11 | |
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | var _grid;
|
33 | var _self = this;
|
34 | var _copiedRanges;
|
35 | var _options = options || {};
|
36 | var _copiedCellStyleLayerKey = _options.copiedCellStyleLayerKey || "copy-manager";
|
37 | var _copiedCellStyle = _options.copiedCellStyle || "copied";
|
38 | var _clearCopyTI = 0;
|
39 | var _bodyElement = _options.bodyElement || document.body;
|
40 | var _onCopyInit = _options.onCopyInit || null;
|
41 | var _onCopySuccess = _options.onCopySuccess || null;
|
42 |
|
43 | var keyCodes = {
|
44 | 'C': 67,
|
45 | 'V': 86,
|
46 | 'ESC': 27,
|
47 | 'INSERT': 45
|
48 | };
|
49 |
|
50 | function init(grid) {
|
51 | _grid = grid;
|
52 | _grid.onKeyDown.subscribe(handleKeyDown);
|
53 |
|
54 |
|
55 | var cellSelectionModel = grid.getSelectionModel();
|
56 | if (!cellSelectionModel){
|
57 | 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())");
|
58 | }
|
59 |
|
60 |
|
61 | cellSelectionModel.onSelectedRangesChanged.subscribe(function(e, args){
|
62 | _grid.focus();
|
63 | });
|
64 | }
|
65 |
|
66 | function destroy() {
|
67 | _grid.onKeyDown.unsubscribe(handleKeyDown);
|
68 | }
|
69 |
|
70 | function getDataItemValueForColumn(item, columnDef) {
|
71 | if (_options.dataItemColumnValueExtractor) {
|
72 | var dataItemColumnValueExtractorValue = _options.dataItemColumnValueExtractor(item, columnDef);
|
73 |
|
74 | if (dataItemColumnValueExtractorValue)
|
75 | return dataItemColumnValueExtractorValue;
|
76 | }
|
77 |
|
78 | var retVal = '';
|
79 |
|
80 |
|
81 | if (columnDef.editor){
|
82 | var editorArgs = {
|
83 | 'container':$("<p>"),
|
84 | 'column':columnDef,
|
85 | 'position':{'top':0, 'left':0},
|
86 | 'grid':_grid
|
87 | };
|
88 | var editor = new columnDef.editor(editorArgs);
|
89 | editor.loadValue(item);
|
90 | retVal = editor.serializeValue();
|
91 | editor.destroy();
|
92 | } else {
|
93 | retVal = item[columnDef.field];
|
94 | }
|
95 |
|
96 | return retVal;
|
97 | }
|
98 |
|
99 | function setDataItemValueForColumn(item, columnDef, value) {
|
100 | if (_options.dataItemColumnValueSetter) {
|
101 | return _options.dataItemColumnValueSetter(item, columnDef, value);
|
102 | }
|
103 |
|
104 |
|
105 | if (columnDef.editor){
|
106 | var editorArgs = {
|
107 | 'container':$("body"),
|
108 | 'column':columnDef,
|
109 | 'position':{'top':0, 'left':0},
|
110 | 'grid':_grid
|
111 | };
|
112 | var editor = new columnDef.editor(editorArgs);
|
113 | editor.loadValue(item);
|
114 | editor.applyValue(item, value);
|
115 | editor.destroy();
|
116 | } else {
|
117 | item[columnDef.field] = value;
|
118 | }
|
119 | }
|
120 |
|
121 |
|
122 | function _createTextBox(innerText){
|
123 | var ta = document.createElement('textarea');
|
124 | ta.style.position = 'absolute';
|
125 | ta.style.left = '-1000px';
|
126 | ta.style.top = document.body.scrollTop + 'px';
|
127 | ta.value = innerText;
|
128 | _bodyElement.appendChild(ta);
|
129 | ta.select();
|
130 |
|
131 | return ta;
|
132 | }
|
133 |
|
134 | function _decodeTabularData(_grid, ta){
|
135 | var columns = _grid.getColumns();
|
136 | var clipText = ta.value;
|
137 | var clipRows = clipText.split(/[\n\f\r]/);
|
138 |
|
139 | if (clipRows[clipRows.length - 1]==="") { clipRows.pop(); }
|
140 |
|
141 | var clippedRange = [];
|
142 | var j = 0;
|
143 |
|
144 | _bodyElement.removeChild(ta);
|
145 | for (var i=0; i<clipRows.length; i++) {
|
146 | if (clipRows[i]!=="")
|
147 | clippedRange[j++] = clipRows[i].split("\t");
|
148 | else
|
149 | clippedRange[i] = [""];
|
150 | }
|
151 | var selectedCell = _grid.getActiveCell();
|
152 | var ranges = _grid.getSelectionModel().getSelectedRanges();
|
153 | var selectedRange = ranges && ranges.length ? ranges[0] : null;
|
154 | var activeRow = null;
|
155 | var activeCell = null;
|
156 |
|
157 | if (selectedRange){
|
158 | activeRow = selectedRange.fromRow;
|
159 | activeCell = selectedRange.fromCell;
|
160 | } else if (selectedCell){
|
161 | activeRow = selectedCell.row;
|
162 | activeCell = selectedCell.cell;
|
163 | } else {
|
164 |
|
165 | return;
|
166 | }
|
167 |
|
168 | var oneCellToMultiple = false;
|
169 | var destH = clippedRange.length;
|
170 | var destW = clippedRange.length ? clippedRange[0].length : 0;
|
171 | if (clippedRange.length == 1 && clippedRange[0].length == 1 && selectedRange){
|
172 | oneCellToMultiple = true;
|
173 | destH = selectedRange.toRow - selectedRange.fromRow +1;
|
174 | destW = selectedRange.toCell - selectedRange.fromCell +1;
|
175 | }
|
176 | var availableRows = _grid.getData().length - activeRow;
|
177 | var addRows = 0;
|
178 |
|
179 |
|
180 | if(availableRows < destH && _options.newRowCreator)
|
181 | {
|
182 | var d = _grid.getData();
|
183 | for(addRows = 1; addRows <= destH - availableRows; addRows++)
|
184 | d.push({});
|
185 | _grid.setData(d);
|
186 | _grid.render();
|
187 | }
|
188 |
|
189 | var overflowsBottomOfGrid = activeRow + destH > _grid.getDataLength();
|
190 |
|
191 | if (_options.newRowCreator && overflowsBottomOfGrid) {
|
192 |
|
193 | var newRowsNeeded = activeRow + destH - _grid.getDataLength();
|
194 |
|
195 | _options.newRowCreator(newRowsNeeded);
|
196 |
|
197 | }
|
198 |
|
199 | var clipCommand = {
|
200 |
|
201 | isClipboardCommand: true,
|
202 | clippedRange: clippedRange,
|
203 | oldValues: [],
|
204 | cellExternalCopyManager: _self,
|
205 | _options: _options,
|
206 | setDataItemValueForColumn: setDataItemValueForColumn,
|
207 | markCopySelection: markCopySelection,
|
208 | oneCellToMultiple: oneCellToMultiple,
|
209 | activeRow: activeRow,
|
210 | activeCell: activeCell,
|
211 | destH: destH,
|
212 | destW: destW,
|
213 | maxDestY: _grid.getDataLength(),
|
214 | maxDestX: _grid.getColumns().length,
|
215 | h: 0,
|
216 | w: 0,
|
217 |
|
218 | execute: function() {
|
219 | this.h=0;
|
220 | for (var y = 0; y < this.destH; y++){
|
221 | this.oldValues[y] = [];
|
222 | this.w=0;
|
223 | this.h++;
|
224 | for (var x = 0; x < this.destW; x++){
|
225 | this.w++;
|
226 | var desty = activeRow + y;
|
227 | var destx = activeCell + x;
|
228 |
|
229 | if (desty < this.maxDestY && destx < this.maxDestX ) {
|
230 | var nd = _grid.getCellNode(desty, destx);
|
231 | var dt = _grid.getDataItem(desty);
|
232 | this.oldValues[y][x] = dt[columns[destx]['field']];
|
233 | if (oneCellToMultiple)
|
234 | this.setDataItemValueForColumn(dt, columns[destx], clippedRange[0][0]);
|
235 | else
|
236 | this.setDataItemValueForColumn(dt, columns[destx], clippedRange[y] ? clippedRange[y][x] : '');
|
237 | _grid.updateCell(desty, destx);
|
238 | _grid.onCellChange.notify({
|
239 | row: desty,
|
240 | cell: destx,
|
241 | item: dt,
|
242 | grid: _grid
|
243 | });
|
244 |
|
245 | }
|
246 | }
|
247 | }
|
248 |
|
249 | var bRange = {
|
250 | 'fromCell': activeCell,
|
251 | 'fromRow': activeRow,
|
252 | 'toCell': activeCell+this.w-1,
|
253 | 'toRow': activeRow+this.h-1
|
254 | };
|
255 |
|
256 | this.markCopySelection([bRange]);
|
257 | _grid.getSelectionModel().setSelectedRanges([bRange]);
|
258 | this.cellExternalCopyManager.onPasteCells.notify({ranges: [bRange]});
|
259 | },
|
260 |
|
261 | undo: function() {
|
262 | for (var y = 0; y < this.destH; y++){
|
263 | for (var x = 0; x < this.destW; x++){
|
264 | var desty = activeRow + y;
|
265 | var destx = activeCell + x;
|
266 |
|
267 | if (desty < this.maxDestY && destx < this.maxDestX ) {
|
268 | var nd = _grid.getCellNode(desty, destx);
|
269 | var dt = _grid.getDataItem(desty);
|
270 | if (oneCellToMultiple)
|
271 | this.setDataItemValueForColumn(dt, columns[destx], this.oldValues[0][0]);
|
272 | else
|
273 | this.setDataItemValueForColumn(dt, columns[destx], this.oldValues[y][x]);
|
274 | _grid.updateCell(desty, destx);
|
275 | _grid.onCellChange.notify({
|
276 | row: desty,
|
277 | cell: destx,
|
278 | item: dt,
|
279 | grid: _grid
|
280 | });
|
281 | }
|
282 | }
|
283 | }
|
284 |
|
285 | var bRange = {
|
286 | 'fromCell': activeCell,
|
287 | 'fromRow': activeRow,
|
288 | 'toCell': activeCell+this.w-1,
|
289 | 'toRow': activeRow+this.h-1
|
290 | };
|
291 |
|
292 | this.markCopySelection([bRange]);
|
293 | _grid.getSelectionModel().setSelectedRanges([bRange]);
|
294 | this.cellExternalCopyManager.onPasteCells.notify({ranges: [bRange]});
|
295 |
|
296 | if(addRows > 1){
|
297 | var d = _grid.getData();
|
298 | for(; addRows > 1; addRows--)
|
299 | d.splice(d.length - 1, 1);
|
300 | _grid.setData(d);
|
301 | _grid.render();
|
302 | }
|
303 | }
|
304 | };
|
305 |
|
306 | if(_options.clipboardCommandHandler) {
|
307 | _options.clipboardCommandHandler(clipCommand);
|
308 | }
|
309 | else {
|
310 | clipCommand.execute();
|
311 | }
|
312 | }
|
313 |
|
314 |
|
315 | function handleKeyDown(e, args) {
|
316 | var ranges;
|
317 | if (!_grid.getEditorLock().isActive() || _grid.getOptions().autoEdit) {
|
318 | if (e.which == keyCodes.ESC) {
|
319 | if (_copiedRanges) {
|
320 | e.preventDefault();
|
321 | clearCopySelection();
|
322 | _self.onCopyCancelled.notify({ranges: _copiedRanges});
|
323 | _copiedRanges = null;
|
324 | }
|
325 | }
|
326 |
|
327 | if ((e.which === keyCodes.C || e.which === keyCodes.INSERT) && (e.ctrlKey || e.metaKey) && !e.shiftKey) {
|
328 | if (_onCopyInit) {
|
329 | _onCopyInit.call();
|
330 | }
|
331 | ranges = _grid.getSelectionModel().getSelectedRanges();
|
332 | if (ranges.length !== 0) {
|
333 | _copiedRanges = ranges;
|
334 | markCopySelection(ranges);
|
335 | _self.onCopyCells.notify({ranges: ranges});
|
336 |
|
337 | var columns = _grid.getColumns();
|
338 | var clipText = "";
|
339 |
|
340 | for (var rg = 0; rg < ranges.length; rg++){
|
341 | var range = ranges[rg];
|
342 | var clipTextRows = [];
|
343 | for (var i=range.fromRow; i< range.toRow+1 ; i++){
|
344 | var clipTextCells = [];
|
345 | var dt = _grid.getDataItem(i);
|
346 |
|
347 | if (clipTextRows === "" && _options.includeHeaderWhenCopying) {
|
348 | var clipTextHeaders = [];
|
349 | for (var j = range.fromCell; j < range.toCell + 1 ; j++) {
|
350 | if (columns[j].name.length > 0)
|
351 | clipTextHeaders.push(columns[j].name);
|
352 | }
|
353 | clipTextRows.push(clipTextHeaders.join("\t"));
|
354 | }
|
355 |
|
356 | for (var j=range.fromCell; j< range.toCell+1 ; j++){
|
357 | clipTextCells.push(getDataItemValueForColumn(dt, columns[j]));
|
358 | }
|
359 | clipTextRows.push(clipTextCells.join("\t"));
|
360 | }
|
361 | clipText += clipTextRows.join("\r\n") + "\r\n";
|
362 | }
|
363 |
|
364 | if(window.clipboardData) {
|
365 | window.clipboardData.setData("Text", clipText);
|
366 | return true;
|
367 | }
|
368 | else {
|
369 | var focusEl = document.activeElement;
|
370 |
|
371 | var ta = _createTextBox(clipText);
|
372 |
|
373 | ta.focus();
|
374 |
|
375 | setTimeout(function(){
|
376 | _bodyElement.removeChild(ta);
|
377 |
|
378 | if (focusEl)
|
379 | focusEl.focus();
|
380 | else
|
381 | console.log("Not element to restore focus to after copy?");
|
382 |
|
383 | }, 100);
|
384 |
|
385 | if (_onCopySuccess) {
|
386 | var rowCount = 0;
|
387 |
|
388 | if (ranges.length === 1) {
|
389 | rowCount = (ranges[0].toRow + 1) - ranges[0].fromRow;
|
390 | }
|
391 | else {
|
392 | rowCount = ranges.length;
|
393 | }
|
394 | _onCopySuccess.call(this, rowCount);
|
395 | }
|
396 |
|
397 | return false;
|
398 | }
|
399 | }
|
400 | }
|
401 |
|
402 | if (!_options.readOnlyMode && (
|
403 | (e.which === keyCodes.V && (e.ctrlKey || e.metaKey) && !e.shiftKey)
|
404 | || (e.which === keyCodes.INSERT && e.shiftKey && !e.ctrlKey)
|
405 | )) {
|
406 | var ta = _createTextBox('');
|
407 |
|
408 | setTimeout(function(){
|
409 | _decodeTabularData(_grid, ta);
|
410 | }, 100);
|
411 |
|
412 | return false;
|
413 | }
|
414 | }
|
415 | }
|
416 |
|
417 | function markCopySelection(ranges) {
|
418 | clearCopySelection();
|
419 |
|
420 | var columns = _grid.getColumns();
|
421 | var hash = {};
|
422 | for (var i = 0; i < ranges.length; i++) {
|
423 | for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
|
424 | hash[j] = {};
|
425 | for (var k = ranges[i].fromCell; k <= ranges[i].toCell && k<columns.length; k++) {
|
426 | hash[j][columns[k].id] = _copiedCellStyle;
|
427 | }
|
428 | }
|
429 | }
|
430 | _grid.setCellCssStyles(_copiedCellStyleLayerKey, hash);
|
431 | clearTimeout(_clearCopyTI);
|
432 | _clearCopyTI = setTimeout(function(){
|
433 | _self.clearCopySelection();
|
434 | }, 2000);
|
435 | }
|
436 |
|
437 | function clearCopySelection() {
|
438 | _grid.removeCellCssStyles(_copiedCellStyleLayerKey);
|
439 | }
|
440 |
|
441 | $.extend(this, {
|
442 | "init": init,
|
443 | "destroy": destroy,
|
444 | "pluginName": "CellExternalCopyManager",
|
445 |
|
446 | "clearCopySelection": clearCopySelection,
|
447 | "handleKeyDown":handleKeyDown,
|
448 |
|
449 | "onCopyCells": new Slick.Event(),
|
450 | "onCopyCancelled": new Slick.Event(),
|
451 | "onPasteCells": new Slick.Event()
|
452 | });
|
453 | }
|
454 | })(jQuery);
|