123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- import copyPaste from './../../../lib/copyPaste/copyPaste';
- import SheetClip from './../../../lib/SheetClip/SheetClip';
- import Hooks from './../../pluginHooks';
- import { KEY_CODES, isCtrlKey } from './../../helpers/unicode';
- import { arrayEach } from './../../helpers/array';
- import { rangeEach } from './../../helpers/number';
- import { stopImmediatePropagation, isImmediatePropagationStopped } from './../../helpers/dom/event';
- import { getSelectionText } from './../../helpers/dom/element';
- import { CellCoords, CellRange } from './../../3rdparty/walkontable/src';
- Hooks.getSingleton().register('afterCopyLimit');
- Hooks.getSingleton().register('modifyCopyableRange');
- Hooks.getSingleton().register('beforeCut');
- Hooks.getSingleton().register('afterCut');
- Hooks.getSingleton().register('beforePaste');
- Hooks.getSingleton().register('afterPaste');
- Hooks.getSingleton().register('beforeCopy');
- Hooks.getSingleton().register('afterCopy');
- /**
- * @description
- * This plugin enables the copy/paste functionality in Handsontable.
- *
- * @example
- * ```js
- * ...
- * copyPaste: true,
- * ...
- * ```
- * @class CopyPaste
- * @plugin CopyPaste
- */
- function CopyPastePlugin(instance) {
- var _this = this;
- this.copyPasteInstance = copyPaste();
- this.copyPasteInstance.onCut(onCut);
- this.copyPasteInstance.triggerCopy = callCopyAction;
- this.copyPasteInstance.onPaste(onPaste);
- this.onPaste = onPaste; // for paste testing purposes
- this.copyableRanges = [];
- instance.addHook('beforeKeyDown', onBeforeKeyDown);
- function onCut() {
- instance.isListening();
- }
- function callCutAction() {
- var rangedData = _this.getRangedData(_this.copyableRanges);
- if (instance.getSettings().fragmentSelection && SheetClip.stringify(rangedData) != getSelectionText()) {
- return;
- }
- var allowCuttingOut = !!instance.runHooks('beforeCut', rangedData, _this.copyableRanges);
- if (allowCuttingOut) {
- instance.copyPaste.copyPasteInstance.copyable(SheetClip.stringify(rangedData));
- instance.selection.empty();
- instance.runHooks('afterCut', rangedData, _this.copyableRanges);
- } else {
- instance.copyPaste.copyPasteInstance.copyable('');
- }
- }
- function callCopyAction() {
- if (!instance.isListening()) {
- return;
- }
- var rangedData = _this.getRangedData(_this.copyableRanges);
- if (instance.getSettings().fragmentSelection && SheetClip.stringify(rangedData) != getSelectionText()) {
- return;
- }
- var allowCopying = !!instance.runHooks('beforeCopy', rangedData, _this.copyableRanges);
- if (allowCopying) {
- instance.copyPaste.copyPasteInstance.copyable(SheetClip.stringify(rangedData));
- instance.runHooks('afterCopy', rangedData, _this.copyableRanges);
- } else {
- instance.copyPaste.copyPasteInstance.copyable('');
- }
- }
- function onPaste(str) {
- var input, inputArray, selected, coordsFrom, coordsTo, cellRange, topLeftCorner, bottomRightCorner, areaStart, areaEnd;
- if (!instance.isListening() || !instance.selection.isSelected()) {
- return;
- }
- input = str;
- inputArray = SheetClip.parse(input);
- selected = instance.getSelected();
- coordsFrom = new CellCoords(selected[0], selected[1]);
- coordsTo = new CellCoords(selected[2], selected[3]);
- cellRange = new CellRange(coordsFrom, coordsFrom, coordsTo);
- topLeftCorner = cellRange.getTopLeftCorner();
- bottomRightCorner = cellRange.getBottomRightCorner();
- areaStart = topLeftCorner;
- areaEnd = new CellCoords(Math.max(bottomRightCorner.row, inputArray.length - 1 + topLeftCorner.row), Math.max(bottomRightCorner.col, inputArray[0].length - 1 + topLeftCorner.col));
- var isSelRowAreaCoverInputValue = coordsTo.row - coordsFrom.row >= inputArray.length - 1;
- var isSelColAreaCoverInputValue = coordsTo.col - coordsFrom.col >= inputArray[0].length - 1;
- instance.addHookOnce('afterChange', function (changes, source) {
- var changesLength = changes ? changes.length : 0;
- if (changesLength) {
- var offset = { row: 0, col: 0 };
- var highestColumnIndex = -1;
- arrayEach(changes, function (change, index) {
- var nextChange = changesLength > index + 1 ? changes[index + 1] : null;
- if (nextChange) {
- if (!isSelRowAreaCoverInputValue) {
- offset.row += Math.max(nextChange[0] - change[0] - 1, 0);
- }
- if (!isSelColAreaCoverInputValue && change[1] > highestColumnIndex) {
- highestColumnIndex = change[1];
- offset.col += Math.max(nextChange[1] - change[1] - 1, 0);
- }
- }
- });
- instance.selectCell(areaStart.row, areaStart.col, areaEnd.row + offset.row, areaEnd.col + offset.col);
- }
- });
- var allowPasting = !!instance.runHooks('beforePaste', inputArray, _this.copyableRanges);
- if (allowPasting) {
- instance.populateFromArray(areaStart.row, areaStart.col, inputArray, areaEnd.row, areaEnd.col, 'CopyPaste.paste', instance.getSettings().pasteMode);
- instance.runHooks('afterPaste', inputArray, _this.copyableRanges);
- }
- }
- function onBeforeKeyDown(event) {
- if (!instance.getSelected()) {
- return;
- }
- if (instance.getActiveEditor() && instance.getActiveEditor().isOpened()) {
- return;
- }
- if (isImmediatePropagationStopped(event)) {
- return;
- }
- if (isCtrlKey(event.keyCode)) {
- // When fragmentSelection is enabled and some text is selected then don't blur selection calling 'setCopyableText'
- if (instance.getSettings().fragmentSelection && getSelectionText()) {
- return;
- }
- // when CTRL is pressed, prepare selectable text in textarea
- _this.setCopyableText();
- stopImmediatePropagation(event);
- return;
- }
- // catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)
- var ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey;
- if (ctrlDown) {
- if (event.keyCode == KEY_CODES.A) {
- instance._registerTimeout(setTimeout(_this.setCopyableText.bind(_this), 0));
- }
- if (event.keyCode == KEY_CODES.X) {
- callCutAction();
- }
- if (event.keyCode == KEY_CODES.C) {
- callCopyAction();
- }
- }
- }
- /**
- * Destroy plugin instance.
- *
- * @function destroy
- * @memberof CopyPaste#
- */
- this.destroy = function () {
- if (this.copyPasteInstance) {
- this.copyPasteInstance.removeCallback(onCut);
- this.copyPasteInstance.removeCallback(onPaste);
- this.copyPasteInstance.destroy();
- this.copyPasteInstance = null;
- }
- instance.removeHook('beforeKeyDown', onBeforeKeyDown);
- };
- instance.addHook('afterDestroy', this.destroy.bind(this));
- /**
- * @function triggerPaste
- * @memberof CopyPaste#
- */
- this.triggerPaste = this.copyPasteInstance.triggerPaste.bind(this.copyPasteInstance);
- /**
- * @function triggerCut
- * @memberof CopyPaste#
- */
- this.triggerCut = this.copyPasteInstance.triggerCut.bind(this.copyPasteInstance);
- /**
- * Prepares copyable text in the invisible textarea.
- *
- * @function setCopyable
- * @memberof CopyPaste#
- */
- this.setCopyableText = function () {
- var settings = instance.getSettings();
- var copyRowsLimit = settings.copyRowsLimit;
- var copyColsLimit = settings.copyColsLimit;
- var selRange = instance.getSelectedRange();
- var topLeft = selRange.getTopLeftCorner();
- var bottomRight = selRange.getBottomRightCorner();
- var startRow = topLeft.row;
- var startCol = topLeft.col;
- var endRow = bottomRight.row;
- var endCol = bottomRight.col;
- var finalEndRow = Math.min(endRow, startRow + copyRowsLimit - 1);
- var finalEndCol = Math.min(endCol, startCol + copyColsLimit - 1);
- this.copyableRanges.length = 0;
- this.copyableRanges.push({
- startRow: startRow,
- startCol: startCol,
- endRow: finalEndRow,
- endCol: finalEndCol
- });
- this.copyableRanges = instance.runHooks('modifyCopyableRange', this.copyableRanges);
- var copyableData = this.getRangedCopyableData(this.copyableRanges);
- instance.copyPaste.copyPasteInstance.copyable(copyableData);
- if (endRow !== finalEndRow || endCol !== finalEndCol) {
- instance.runHooks('afterCopyLimit', endRow - startRow + 1, endCol - startCol + 1, copyRowsLimit, copyColsLimit);
- }
- };
- /**
- * Create copyable text releated to range objects.
- *
- * @since 0.19.0
- * @param {Array} ranges Array of Objects with properties `startRow`, `endRow`, `startCol` and `endCol`.
- * @returns {String} Returns string which will be copied into clipboard.
- */
- this.getRangedCopyableData = function (ranges) {
- var dataSet = [];
- var copyableRows = [];
- var copyableColumns = [];
- // Count all copyable rows and columns
- arrayEach(ranges, function (range) {
- rangeEach(range.startRow, range.endRow, function (row) {
- if (copyableRows.indexOf(row) === -1) {
- copyableRows.push(row);
- }
- });
- rangeEach(range.startCol, range.endCol, function (column) {
- if (copyableColumns.indexOf(column) === -1) {
- copyableColumns.push(column);
- }
- });
- });
- // Concat all rows and columns data defined in ranges into one copyable string
- arrayEach(copyableRows, function (row) {
- var rowSet = [];
- arrayEach(copyableColumns, function (column) {
- rowSet.push(instance.getCopyableData(row, column));
- });
- dataSet.push(rowSet);
- });
- return SheetClip.stringify(dataSet);
- };
- /**
- * Create copyable text releated to range objects.
- *
- * @since 0.31.1
- * @param {Array} ranges Array of Objects with properties `startRow`, `startCol`, `endRow` and `endCol`.
- * @returns {Array} Returns array of arrays which will be copied into clipboard.
- */
- this.getRangedData = function (ranges) {
- var dataSet = [];
- var copyableRows = [];
- var copyableColumns = [];
- // Count all copyable rows and columns
- arrayEach(ranges, function (range) {
- rangeEach(range.startRow, range.endRow, function (row) {
- if (copyableRows.indexOf(row) === -1) {
- copyableRows.push(row);
- }
- });
- rangeEach(range.startCol, range.endCol, function (column) {
- if (copyableColumns.indexOf(column) === -1) {
- copyableColumns.push(column);
- }
- });
- });
- // Concat all rows and columns data defined in ranges into one copyable string
- arrayEach(copyableRows, function (row) {
- var rowSet = [];
- arrayEach(copyableColumns, function (column) {
- rowSet.push(instance.getCopyableData(row, column));
- });
- dataSet.push(rowSet);
- });
- return dataSet;
- };
- }
- /**
- * Init plugin.
- *
- * @function init
- * @memberof CopyPaste#
- */
- function init() {
- var instance = this,
- pluginEnabled = instance.getSettings().copyPaste !== false;
- if (pluginEnabled && !instance.copyPaste) {
- /**
- * Instance of CopyPaste Plugin {@link Handsontable.CopyPaste}
- *
- * @alias copyPaste
- * @memberof! Handsontable.Core#
- * @type {CopyPaste}
- */
- instance.copyPaste = new CopyPastePlugin(instance);
- } else if (!pluginEnabled && instance.copyPaste) {
- instance.copyPaste.destroy();
- instance.copyPaste = null;
- }
- }
- Hooks.getSingleton().add('afterInit', init);
- Hooks.getSingleton().add('afterUpdateSettings', init);
- export default CopyPastePlugin;
|