"use strict"; require("core-js/modules/es.symbol"); require("core-js/modules/es.symbol.description"); require("core-js/modules/es.symbol.iterator"); require("core-js/modules/es.array.concat"); require("core-js/modules/es.array.from"); require("core-js/modules/es.array.includes"); require("core-js/modules/es.array.index-of"); require("core-js/modules/es.array.iterator"); require("core-js/modules/es.object.get-own-property-descriptor"); require("core-js/modules/es.object.get-prototype-of"); require("core-js/modules/es.object.set-prototype-of"); require("core-js/modules/es.object.to-string"); require("core-js/modules/es.reflect.get"); require("core-js/modules/es.regexp.to-string"); require("core-js/modules/es.string.includes"); require("core-js/modules/es.string.iterator"); require("core-js/modules/web.dom-collections.iterator"); require("core-js/modules/web.timers"); exports.__esModule = true; exports.default = void 0; var _base = _interopRequireDefault(require("./../_base")); var _pluginHooks = _interopRequireDefault(require("./../../pluginHooks")); var _element = require("./../../helpers/dom/element"); var _array = require("./../../helpers/array"); var _number = require("./../../helpers/number"); var _eventManager = _interopRequireDefault(require("./../../eventManager")); var _plugins = require("./../../plugins"); var _src = require("./../../3rdparty/walkontable/src"); var _utils = require("./utils"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } _pluginHooks.default.getSingleton().register('modifyAutofillRange'); _pluginHooks.default.getSingleton().register('beforeAutofill'); var INSERT_ROW_ALTER_ACTION_NAME = 'insert_row'; var INTERVAL_FOR_ADDING_ROW = 200; /** * This plugin provides "drag-down" and "copy-down" functionalities, both operated using the small square in the right * bottom of the cell selection. * * "Drag-down" expands the value of the selected cells to the neighbouring cells when you drag the small square in the corner. * * "Copy-down" copies the value of the selection to all empty cells below when you double click the small square. * * @class Autofill * @plugin Autofill */ var Autofill = /*#__PURE__*/ function (_BasePlugin) { _inherits(Autofill, _BasePlugin); function Autofill(hotInstance) { var _this; _classCallCheck(this, Autofill); _this = _possibleConstructorReturn(this, _getPrototypeOf(Autofill).call(this, hotInstance)); /** * Event manager instance. * * @private * @type {EventManager} */ _this.eventManager = new _eventManager.default(_assertThisInitialized(_this)); /** * Specifies if adding new row started. * * @private * @type {Boolean} */ _this.addingStarted = false; /** * Specifies if there was mouse down on the cell corner. * * @private * @type {Boolean} */ _this.mouseDownOnCellCorner = false; /** * Specifies if mouse was dragged outside Handsontable. * * @private * @type {Boolean} */ _this.mouseDragOutside = false; /** * Specifies how many cell levels were dragged using the handle. * * @private * @type {Boolean} */ _this.handleDraggedCells = 0; /** * Specifies allowed directions of drag (`'horizontal'` or '`vertical`'). * * @private * @type {String[]} */ _this.directions = []; /** * Specifies if can insert new rows if needed. * * @type {Boolean} */ _this.autoInsertRow = false; return _this; } /** * Checks if the plugin is enabled in the Handsontable settings. * * @returns {Boolean} */ _createClass(Autofill, [{ key: "isEnabled", value: function isEnabled() { return this.hot.getSettings().fillHandle; } /** * Enables the plugin functionality for this Handsontable instance. */ }, { key: "enablePlugin", value: function enablePlugin() { var _this2 = this; if (this.enabled) { return; } this.mapSettings(); this.registerEvents(); this.addHook('afterOnCellCornerMouseDown', function (event) { return _this2.onAfterCellCornerMouseDown(event); }); this.addHook('afterOnCellCornerDblClick', function (event) { return _this2.onCellCornerDblClick(event); }); this.addHook('beforeOnCellMouseOver', function (event, coords) { return _this2.onBeforeCellMouseOver(coords); }); _get(_getPrototypeOf(Autofill.prototype), "enablePlugin", this).call(this); } /** * Updates the plugin state. This method is executed when {@link Core#updateSettings} is invoked. */ }, { key: "updatePlugin", value: function updatePlugin() { this.disablePlugin(); this.enablePlugin(); _get(_getPrototypeOf(Autofill.prototype), "updatePlugin", this).call(this); } /** * Disables the plugin functionality for this Handsontable instance. */ }, { key: "disablePlugin", value: function disablePlugin() { this.clearMappedSettings(); _get(_getPrototypeOf(Autofill.prototype), "disablePlugin", this).call(this); } /** * Prepares copyable ranges from the cells selection. * * @private * @returns {Object[]} ranges Array of objects with properties `startRow`, `startCol`, `endRow` and `endCol`. */ }, { key: "getCopyableRanges", value: function getCopyableRanges() { var selRange = this.hot.getSelectedRangeLast(); 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 copyableRanges = []; copyableRanges.push({ startRow: startRow, startCol: startCol, endRow: endRow, endCol: endCol }); copyableRanges = this.hot.runHooks('modifyCopyableRange', copyableRanges); return copyableRanges; } /** * Gets selection data * * @private * @returns {Array} Array with the data. */ }, { key: "getSelectionData", value: function getSelectionData() { var _this3 = this; var copyableRanges = this.getCopyableRanges(); var copyableRows = []; var copyableColumns = []; var data = []; (0, _array.arrayEach)(copyableRanges, function (range) { (0, _number.rangeEach)(range.startRow, range.endRow, function (row) { if (copyableRows.indexOf(row) === -1) { copyableRows.push(row); } }); (0, _number.rangeEach)(range.startCol, range.endCol, function (column) { if (copyableColumns.indexOf(column) === -1) { copyableColumns.push(column); } }); }); (0, _array.arrayEach)(copyableRows, function (row) { var rowSet = []; (0, _array.arrayEach)(copyableColumns, function (column) { rowSet.push(_this3.hot.getCopyableData(row, column)); }); data.push(rowSet); }); return data; } /** * Try to apply fill values to the area in fill border, omitting the selection border. * * @private * @returns {Boolean} reports if fill was applied. * * @fires Hooks#modifyAutofillRange * @fires Hooks#beforeAutofill */ }, { key: "fillIn", value: function fillIn() { if (this.hot.selection.highlight.getFill().isEmpty()) { return false; } var cornersOfSelectionAndDragAreas = this.hot.selection.highlight.getFill().getCorners(); this.resetSelectionOfDraggedArea(); var cornersOfSelectedCells = this.getCornersOfSelectedCells(); cornersOfSelectionAndDragAreas = this.hot.runHooks('modifyAutofillRange', cornersOfSelectionAndDragAreas, cornersOfSelectedCells); var _getDragDirectionAndR = (0, _utils.getDragDirectionAndRange)(cornersOfSelectedCells, cornersOfSelectionAndDragAreas), directionOfDrag = _getDragDirectionAndR.directionOfDrag, startOfDragCoords = _getDragDirectionAndR.startOfDragCoords, endOfDragCoords = _getDragDirectionAndR.endOfDragCoords; if (startOfDragCoords && startOfDragCoords.row > -1 && startOfDragCoords.col > -1) { var selectionData = this.getSelectionData(); this.hot.runHooks('beforeAutofill', startOfDragCoords, endOfDragCoords, selectionData); var deltas = (0, _utils.getDeltas)(startOfDragCoords, endOfDragCoords, selectionData, directionOfDrag); var fillData = selectionData; if (['up', 'left'].indexOf(directionOfDrag) > -1) { fillData = []; var dragLength = null; var fillOffset = null; if (directionOfDrag === 'up') { dragLength = endOfDragCoords.row - startOfDragCoords.row + 1; fillOffset = dragLength % selectionData.length; for (var i = 0; i < dragLength; i++) { fillData.push(selectionData[(i + (selectionData.length - fillOffset)) % selectionData.length]); } } else { dragLength = endOfDragCoords.col - startOfDragCoords.col + 1; fillOffset = dragLength % selectionData[0].length; for (var _i = 0; _i < selectionData.length; _i++) { fillData.push([]); for (var j = 0; j < dragLength; j++) { fillData[_i].push(selectionData[_i][(j + (selectionData[_i].length - fillOffset)) % selectionData[_i].length]); } } } } this.hot.populateFromArray(startOfDragCoords.row, startOfDragCoords.col, fillData, endOfDragCoords.row, endOfDragCoords.col, "".concat(this.pluginName, ".fill"), null, directionOfDrag, deltas); this.setSelection(cornersOfSelectionAndDragAreas); } else { // reset to avoid some range bug this.hot._refreshBorders(); } return true; } /** * Reduces the selection area if the handle was dragged outside of the table or on headers. * * @private * @param {CellCoords} coords indexes of selection corners. * @returns {CellCoords} */ }, { key: "reduceSelectionAreaIfNeeded", value: function reduceSelectionAreaIfNeeded(coords) { if (coords.row < 0) { coords.row = 0; } if (coords.col < 0) { coords.col = 0; } return coords; } /** * Gets the coordinates of the drag & drop borders. * * @private * @param {CellCoords} coordsOfSelection `CellCoords` coord object. * @returns {Array} */ }, { key: "getCoordsOfDragAndDropBorders", value: function getCoordsOfDragAndDropBorders(coordsOfSelection) { var topLeftCorner = this.hot.getSelectedRangeLast().getTopLeftCorner(); var bottomRightCorner = this.hot.getSelectedRangeLast().getBottomRightCorner(); var coords; if (this.directions.includes(_utils.DIRECTIONS.vertical) && (bottomRightCorner.row < coordsOfSelection.row || topLeftCorner.row > coordsOfSelection.row)) { coords = new _src.CellCoords(coordsOfSelection.row, bottomRightCorner.col); } else if (this.directions.includes(_utils.DIRECTIONS.horizontal)) { coords = new _src.CellCoords(bottomRightCorner.row, coordsOfSelection.col); } else { // wrong direction return; } return this.reduceSelectionAreaIfNeeded(coords); } /** * Show the fill border. * * @private * @param {CellCoords} coordsOfSelection `CellCoords` coord object. */ }, { key: "showBorder", value: function showBorder(coordsOfSelection) { var coordsOfDragAndDropBorders = this.getCoordsOfDragAndDropBorders(coordsOfSelection); if (coordsOfDragAndDropBorders) { this.redrawBorders(coordsOfDragAndDropBorders); } } /** * Add new row * * @private */ }, { key: "addRow", value: function addRow() { var _this4 = this; this.hot._registerTimeout(setTimeout(function () { _this4.hot.alter(INSERT_ROW_ALTER_ACTION_NAME, void 0, 1, "".concat(_this4.pluginName, ".fill")); _this4.addingStarted = false; }, INTERVAL_FOR_ADDING_ROW)); } /** * Add new rows if they are needed to continue auto-filling values. * * @private */ }, { key: "addNewRowIfNeeded", value: function addNewRowIfNeeded() { if (this.hot.selection.highlight.getFill().cellRange && this.addingStarted === false && this.autoInsertRow) { var cornersOfSelectedCells = this.hot.getSelectedLast(); var cornersOfSelectedDragArea = this.hot.selection.highlight.getFill().getCorners(); var nrOfTableRows = this.hot.countRows(); if (cornersOfSelectedCells[2] < nrOfTableRows - 1 && cornersOfSelectedDragArea[2] === nrOfTableRows - 1) { this.addingStarted = true; this.addRow(); } } } /** * Get corners of selected cells. * * @private * @returns {Array} */ }, { key: "getCornersOfSelectedCells", value: function getCornersOfSelectedCells() { if (this.hot.selection.isMultiple()) { return this.hot.selection.highlight.createOrGetArea().getCorners(); } return this.hot.selection.highlight.getCell().getCorners(); } /** * Get index of last adjacent filled in row * * @private * @param {Array} cornersOfSelectedCells indexes of selection corners. * @returns {Number} gives number greater than or equal to zero when selection adjacent can be applied. * or -1 when selection adjacent can't be applied */ }, { key: "getIndexOfLastAdjacentFilledInRow", value: function getIndexOfLastAdjacentFilledInRow(cornersOfSelectedCells) { var data = this.hot.getData(); var nrOfTableRows = this.hot.countRows(); var lastFilledInRowIndex; for (var rowIndex = cornersOfSelectedCells[2] + 1; rowIndex < nrOfTableRows; rowIndex++) { for (var columnIndex = cornersOfSelectedCells[1]; columnIndex <= cornersOfSelectedCells[3]; columnIndex++) { var dataInCell = data[rowIndex][columnIndex]; if (dataInCell) { return -1; } } var dataInNextLeftCell = data[rowIndex][cornersOfSelectedCells[1] - 1]; var dataInNextRightCell = data[rowIndex][cornersOfSelectedCells[3] + 1]; if (!!dataInNextLeftCell || !!dataInNextRightCell) { lastFilledInRowIndex = rowIndex; } } return lastFilledInRowIndex; } /** * Adds a selection from the start area to the specific row index. * * @private * @param {Array} selectStartArea selection area from which we start to create more comprehensive selection. * @param {Number} rowIndex */ }, { key: "addSelectionFromStartAreaToSpecificRowIndex", value: function addSelectionFromStartAreaToSpecificRowIndex(selectStartArea, rowIndex) { this.hot.selection.highlight.getFill().clear().add(new _src.CellCoords(selectStartArea[0], selectStartArea[1])).add(new _src.CellCoords(rowIndex, selectStartArea[3])); } /** * Sets selection based on passed corners. * * @private * @param {Array} cornersOfArea */ }, { key: "setSelection", value: function setSelection(cornersOfArea) { var _this$hot; (_this$hot = this.hot).selectCell.apply(_this$hot, _toConsumableArray(cornersOfArea).concat([false, false])); } /** * Try to select cells down to the last row in the left column and then returns if selection was applied. * * @private * @returns {Boolean} */ }, { key: "selectAdjacent", value: function selectAdjacent() { var cornersOfSelectedCells = this.getCornersOfSelectedCells(); var lastFilledInRowIndex = this.getIndexOfLastAdjacentFilledInRow(cornersOfSelectedCells); if (lastFilledInRowIndex === -1 || lastFilledInRowIndex === void 0) { return false; } this.addSelectionFromStartAreaToSpecificRowIndex(cornersOfSelectedCells, lastFilledInRowIndex); return true; } /** * Resets selection of dragged area. * * @private */ }, { key: "resetSelectionOfDraggedArea", value: function resetSelectionOfDraggedArea() { this.handleDraggedCells = 0; this.hot.selection.highlight.getFill().clear(); } /** * Redraws borders. * * @private * @param {CellCoords} coords `CellCoords` coord object. */ }, { key: "redrawBorders", value: function redrawBorders(coords) { this.hot.selection.highlight.getFill().clear().add(this.hot.getSelectedRangeLast().from).add(this.hot.getSelectedRangeLast().to).add(coords); this.hot.view.render(); } /** * Get if mouse was dragged outside. * * @private * @param {MouseEvent} event `mousemove` event properties. * @returns {Boolean} */ }, { key: "getIfMouseWasDraggedOutside", value: function getIfMouseWasDraggedOutside(event) { var documentElement = this.hot.rootDocument.documentElement; var tableBottom = (0, _element.offset)(this.hot.table).top - (this.hot.rootWindow.pageYOffset || documentElement.scrollTop) + (0, _element.outerHeight)(this.hot.table); var tableRight = (0, _element.offset)(this.hot.table).left - (this.hot.rootWindow.pageXOffset || documentElement.scrollLeft) + (0, _element.outerWidth)(this.hot.table); return event.clientY > tableBottom && event.clientX <= tableRight; } /** * Bind the events used by the plugin. * * @private */ }, { key: "registerEvents", value: function registerEvents() { var _this5 = this; var documentElement = this.hot.rootDocument.documentElement; this.eventManager.addEventListener(documentElement, 'mouseup', function () { return _this5.onMouseUp(); }); this.eventManager.addEventListener(documentElement, 'mousemove', function (event) { return _this5.onMouseMove(event); }); } /** * On cell corner double click callback. * * @private */ }, { key: "onCellCornerDblClick", value: function onCellCornerDblClick() { var selectionApplied = this.selectAdjacent(); if (selectionApplied) { this.fillIn(); } } /** * On after cell corner mouse down listener. * * @private */ }, { key: "onAfterCellCornerMouseDown", value: function onAfterCellCornerMouseDown() { this.handleDraggedCells = 1; this.mouseDownOnCellCorner = true; } /** * On before cell mouse over listener. * * @private * @param {CellCoords} coords `CellCoords` coord object. */ }, { key: "onBeforeCellMouseOver", value: function onBeforeCellMouseOver(coords) { if (this.mouseDownOnCellCorner && !this.hot.view.isMouseDown() && this.handleDraggedCells) { this.handleDraggedCells += 1; this.showBorder(coords); this.addNewRowIfNeeded(); } } /** * On mouse up listener. * * @private */ }, { key: "onMouseUp", value: function onMouseUp() { if (this.handleDraggedCells) { if (this.handleDraggedCells > 1) { this.fillIn(); } this.handleDraggedCells = 0; this.mouseDownOnCellCorner = false; } } /** * On mouse move listener. * * @private * @param {MouseEvent} event `mousemove` event properties. */ }, { key: "onMouseMove", value: function onMouseMove(event) { var mouseWasDraggedOutside = this.getIfMouseWasDraggedOutside(event); if (this.addingStarted === false && this.handleDraggedCells > 0 && mouseWasDraggedOutside) { this.mouseDragOutside = true; this.addingStarted = true; } else { this.mouseDragOutside = false; } if (this.mouseDragOutside && this.autoInsertRow) { this.addRow(); } } /** * Clears mapped settings. * * @private */ }, { key: "clearMappedSettings", value: function clearMappedSettings() { this.directions.length = 0; this.autoInsertRow = false; } /** * Map settings. * * @private */ }, { key: "mapSettings", value: function mapSettings() { var mappedSettings = (0, _utils.getMappedFillHandleSetting)(this.hot.getSettings().fillHandle); this.directions = mappedSettings.directions; this.autoInsertRow = mappedSettings.autoInsertRow; } /** * Destroys the plugin instance. */ }, { key: "destroy", value: function destroy() { _get(_getPrototypeOf(Autofill.prototype), "destroy", this).call(this); } }]); return Autofill; }(_base.default); (0, _plugins.registerPlugin)('autofill', Autofill); var _default = Autofill; exports.default = _default;