UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,101 lines (1,098 loc) • 83.5 kB
/** * DevExtreme (ui/grid_core/ui.grid_core.editing.js) * Version: 18.1.3 * Build date: Tue May 15 2018 * * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; var $ = require("../../core/renderer"), domAdapter = require("../../core/dom_adapter"), window = require("../../core/utils/window").getWindow(), eventsEngine = require("../../events/core/events_engine"), Guid = require("../../core/guid"), typeUtils = require("../../core/utils/type"), each = require("../../core/utils/iterator").each, deepExtendArraySafe = require("../../core/utils/object").deepExtendArraySafe, extend = require("../../core/utils/extend").extend, modules = require("./ui.grid_core.modules"), clickEvent = require("../../events/click"), gridCoreUtils = require("./ui.grid_core.utils"), _getIndexByKey = gridCoreUtils.getIndexByKey, eventUtils = require("../../events/utils"), addNamespace = eventUtils.addNamespace, dialog = require("../dialog"), messageLocalization = require("../../localization/message"), Button = require("../button"), Popup = require("../popup"), errors = require("../widget/ui.errors"), devices = require("../../core/devices"), Form = require("../form"), holdEvent = require("../../events/hold"), deferredUtils = require("../../core/utils/deferred"), when = deferredUtils.when, Deferred = deferredUtils.Deferred; var EDIT_FORM_CLASS = "edit-form", EDIT_FORM_ITEM_CLASS = "edit-form-item", FOCUS_OVERLAY_CLASS = "focus-overlay", READONLY_CLASS = "readonly", EDIT_POPUP_CLASS = "edit-popup", FORM_BUTTONS_CONTAINER_CLASS = "form-buttons-container", ADD_ROW_BUTTON_CLASS = "addrow-button", LINK_CLASS = "dx-link", EDITOR_CELL_CLASS = "dx-editor-cell", ROW_SELECTED = "dx-selection", EDIT_ROW = "dx-edit-row", EDIT_BUTTON_CLASS = "dx-edit-button", COMMAND_EDIT_CLASS = "dx-command-edit", COMMAND_EDIT_WITH_ICONS_CLASS = COMMAND_EDIT_CLASS + "-with-icons", BUTTON_CLASS = "dx-button", INSERT_INDEX = "__DX_INSERT_INDEX__", ROW_CLASS = "dx-row", ROW_REMOVED = "dx-row-removed", ROW_INSERTED = "dx-row-inserted", ROW_MODIFIED = "dx-row-modified", CELL_MODIFIED = "dx-cell-modified", CELL_HIGHLIGHT_OUTLINE = "dx-highlight-outline", EDITING_NAMESPACE = "dxDataGridEditing", DATA_ROW_CLASS = "dx-data-row", CELL_FOCUS_DISABLED_CLASS = "dx-cell-focus-disabled", EDITORS_INPUT_SELECTOR = "input:not([type='hidden'])", FOCUSABLE_ELEMENT_SELECTOR = "[tabindex], " + EDITORS_INPUT_SELECTOR, EDIT_MODE_BATCH = "batch", EDIT_MODE_ROW = "row", EDIT_MODE_CELL = "cell", EDIT_MODE_FORM = "form", EDIT_MODE_POPUP = "popup", DATA_EDIT_DATA_INSERT_TYPE = "insert", DATA_EDIT_DATA_UPDATE_TYPE = "update", DATA_EDIT_DATA_REMOVE_TYPE = "remove", POINTER_EVENTS_NONE_CLASS = "dx-pointer-events-none", POINTER_EVENTS_TARGET_CLASS = "dx-pointer-events-target", EDIT_MODES = [EDIT_MODE_BATCH, EDIT_MODE_ROW, EDIT_MODE_CELL, EDIT_MODE_FORM, EDIT_MODE_POPUP], ROW_BASED_MODES = [EDIT_MODE_ROW, EDIT_MODE_FORM, EDIT_MODE_POPUP], CELL_BASED_MODES = [EDIT_MODE_BATCH, EDIT_MODE_CELL], MODES_WITH_DELAYED_FOCUS = [EDIT_MODE_ROW, EDIT_MODE_FORM]; var EDIT_LINK_CLASS = { saveEditData: "dx-link-save", cancelEditData: "dx-link-cancel", editRow: "dx-link-edit", undeleteRow: "dx-link-undelete", deleteRow: "dx-link-delete", addRowByRowIndex: "dx-link-add" }, EDIT_ICON_CLASS = { saveEditData: "dx-icon-save", cancelEditData: "dx-icon-revert", editRow: "dx-icon-edit", undeleteRow: "dx-icon-revert", deleteRow: "dx-icon-trash", addRowByRowIndex: "dx-icon-add" }; var _getEditMode = function(that) { var editMode = that.option("editing.mode"); if (EDIT_MODES.indexOf(editMode) !== -1) { return editMode } return EDIT_MODE_ROW }; var _isRowEditMode = function(that) { var editMode = _getEditMode(that); return ROW_BASED_MODES.indexOf(editMode) !== -1 }; var EditingController = modules.ViewController.inherit(function() { var getDefaultEditorTemplate = function(that) { return function(container, options) { var $editor = $("<div>").appendTo(container); that.getController("editorFactory").createEditor($editor, extend({}, options.column, { value: options.value, setValue: options.setValue, row: options.row, parentType: "dataRow", width: null, readOnly: !options.setValue, isOnForm: options.isOnForm, id: options.id, updateValueImmediately: _isRowEditMode(that) })) } }; var editCellTemplate = function(container, options) { var editingTexts, editingOptions, $container = $(container), editingController = options.component.getController("editing"), isRowMode = _isRowEditMode(editingController); if ("data" === options.rowType) { $container.css("textAlign", "center"); options.rtlEnabled = editingController.option("rtlEnabled"); editingOptions = editingController.option("editing") || {}; editingTexts = editingOptions.texts || {}; if (options.row && options.row.rowIndex === editingController._getVisibleEditRowIndex() && isRowMode) { editingController._createLink($container, editingTexts.saveRowChanges, "saveEditData", options, editingOptions.useIcons); editingController._createLink($container, editingTexts.cancelRowChanges, "cancelEditData", options, editingOptions.useIcons) } else { editingController._createEditingLinks($container, options, editingOptions, isRowMode) } } else { $container.get(0).innerHTML = "&nbsp;" } }; return { init: function() { var that = this; that._editRowIndex = -1; that._editData = []; that._editColumnIndex = -1; that._columnsController = that.getController("columns"); that._dataController = that.getController("data"); that._rowsView = that.getView("rowsView"); if (!that._dataChangedHandler) { that._dataChangedHandler = that._handleDataChanged.bind(that); that._dataController.changed.add(that._dataChangedHandler) } if (!that._saveEditorHandler) { that.createAction("onInitNewRow", { excludeValidators: ["disabled", "readOnly"] }); that.createAction("onRowInserting", { excludeValidators: ["disabled", "readOnly"] }); that.createAction("onRowInserted", { excludeValidators: ["disabled", "readOnly"] }); that.createAction("onEditingStart", { excludeValidators: ["disabled", "readOnly"] }); that.createAction("onRowUpdating", { excludeValidators: ["disabled", "readOnly"] }); that.createAction("onRowUpdated", { excludeValidators: ["disabled", "readOnly"] }); that.createAction("onRowRemoving", { excludeValidators: ["disabled", "readOnly"] }); that.createAction("onRowRemoved", { excludeValidators: ["disabled", "readOnly"] }); that._saveEditorHandler = that.createAction(function(e) { var isEditorPopup, isDomElement, isFocusOverlay, isAddRowButton, isCellEditMode, $target, event = e.event; if (!_isRowEditMode(that) && !that._editCellInProgress) { $target = $(event.target); isEditorPopup = $target.closest(".dx-dropdowneditor-overlay").length; isDomElement = $target.closest(window.document).length; isAddRowButton = $target.closest("." + that.addWidgetPrefix(ADD_ROW_BUTTON_CLASS)).length; isFocusOverlay = $target.hasClass(that.addWidgetPrefix(FOCUS_OVERLAY_CLASS)); isCellEditMode = _getEditMode(that) === EDIT_MODE_CELL; if (!isEditorPopup && !isFocusOverlay && !(isAddRowButton && isCellEditMode && that.isEditing()) && isDomElement) { that._closeEditItem.bind(that)($target) } } }); eventsEngine.on(domAdapter.getDocument(), clickEvent.name, that._saveEditorHandler) } that._updateEditColumn(); that._updateEditButtons() }, _closeEditItem: function($targetElement) { var isDataRow = $targetElement.closest("." + DATA_ROW_CLASS).length, $targetCell = $targetElement.closest("." + ROW_CLASS + "> td"), columnIndex = $targetCell[0] && $targetCell[0].cellIndex, rowIndex = this.getView("rowsView").getRowIndex($targetCell.parent()), visibleColumns = this._columnsController.getVisibleColumns(), allowEditing = visibleColumns[columnIndex] && visibleColumns[columnIndex].allowEditing; if (this.isEditing() && (!isDataRow || isDataRow && !allowEditing && !this.isEditCell(rowIndex, columnIndex))) { this.closeEditCell() } }, _handleDataChanged: function(args) { var that = this, editForm = that._editForm; if ("standard" === that.option("scrolling.mode")) { that.resetRowAndPageIndices() } if ("prepend" === args.changeType) { each(that._editData, function(_, editData) { editData.rowIndex += args.items.length; if (editData.type === DATA_EDIT_DATA_INSERT_TYPE) { editData.key.rowIndex += args.items.length; editData.key.dataRowIndex += args.items.filter(function(item) { return "data" === item.rowType }).length } }) } if ("refresh" === args.changeType && _getEditMode(that) === EDIT_MODE_POPUP && editForm && editForm.option("visible")) { editForm.repaint() } }, isRowEditMode: function() { return _isRowEditMode(this) }, getEditMode: function() { return _getEditMode(this) }, getFirstEditableColumnIndex: function() { var columnIndex, columnsController = this.getController("columns"); if (_getEditMode(this) === EDIT_MODE_FORM && this._firstFormItem) { columnIndex = this._firstFormItem.column.index } else { var visibleColumns = columnsController.getVisibleColumns(); each(visibleColumns, function(index, column) { if (column.allowEditing) { columnIndex = index; return false } }) } return columnIndex }, getFirstEditableCellInRow: function(rowIndex) { return this.getView("rowsView")._getCellElement(rowIndex ? rowIndex : 0, this.getFirstEditableColumnIndex()) }, getFocusedCellInRow: function(rowIndex) { return this.getFirstEditableCellInRow(rowIndex) }, getIndexByKey: function(key, items) { return _getIndexByKey(key, items) }, hasChanges: function() { var that = this, result = false; for (var i = 0; i < that._editData.length; i++) { if (that._editData[i].type) { result = true; break } } return result }, dispose: function() { this.callBase(); clearTimeout(this._inputFocusTimeoutID); eventsEngine.off(domAdapter.getDocument(), clickEvent.name, this._saveEditorHandler) }, optionChanged: function(args) { if ("editing" === args.name) { this.init(); args.handled = true } else { this.callBase(args) } }, publicMethods: function() { return ["insertRow", "addRow", "removeRow", "deleteRow", "undeleteRow", "editRow", "editCell", "closeEditCell", "saveEditData", "cancelEditData", "hasEditData"] }, refresh: function() { if (_getEditMode(this) === EDIT_MODE_CELL) { return } if (_getEditMode(this) !== EDIT_MODE_BATCH) { this.init() } else { this._editRowIndex = -1; this._editColumnIndex = -1 } }, isEditing: function() { return this._editRowIndex > -1 }, isEditRow: function(rowIndex) { var editMode = _getEditMode(this); return this._getVisibleEditRowIndex() === rowIndex && ROW_BASED_MODES.indexOf(editMode) !== -1 }, getEditRowKey: function() { var items = this._dataController.items(), item = items[this._getVisibleEditRowIndex()]; return item && item.key }, getEditFormRowIndex: function() { var editMode = _getEditMode(this); return editMode === EDIT_MODE_FORM || editMode === EDIT_MODE_POPUP ? this._getVisibleEditRowIndex() : -1 }, isEditCell: function(rowIndex, columnIndex) { return this._getVisibleEditRowIndex() === rowIndex && this._editColumnIndex === columnIndex }, getPopupContent: function() { var editMode = _getEditMode(this), popupVisible = this._editPopup && this._editPopup.option("visible"); if (editMode === EDIT_MODE_POPUP && popupVisible) { return this._editPopup.$content() } }, getEditForm: function() { return this._editForm }, _needInsertItem: function(editData, changeType) { var that = this, dataSource = that._dataController.dataSource(), scrollingMode = that.option("scrolling.mode"), pageIndex = dataSource.pageIndex(), beginPageIndex = dataSource.beginPageIndex ? dataSource.beginPageIndex() : pageIndex, endPageIndex = dataSource.endPageIndex ? dataSource.endPageIndex() : pageIndex; if ("standard" !== scrollingMode) { switch (changeType) { case "append": return editData.key.pageIndex === endPageIndex; case "prepend": return editData.key.pageIndex === beginPageIndex; case "refresh": editData.key.rowIndex = 0; editData.key.dataRowIndex = 0; editData.key.pageIndex = 0; break; default: return editData.key.pageIndex >= beginPageIndex && editData.key.pageIndex <= endPageIndex } } return editData.key.pageIndex === pageIndex }, _generateNewItem: function(key) { var item = { key: key }; if (key && key[INSERT_INDEX]) { item[INSERT_INDEX] = key[INSERT_INDEX] } return item }, processItems: function(items, changeType) { var i, key, item, that = this, editData = that._editData; that.update(changeType); for (i = 0; i < editData.length; i++) { key = editData[i].key; item = that._generateNewItem(key); if (editData[i].type === DATA_EDIT_DATA_INSERT_TYPE && that._needInsertItem(editData[i], changeType, items, item)) { items.splice(key.dataRowIndex, 0, item) } } return items }, processDataItem: function(item, options, generateDataValues) { var data, editMode, editData, editIndex, that = this, columns = options.visibleColumns, key = item.data[INSERT_INDEX] ? item.data.key : item.key; editIndex = _getIndexByKey(key, that._editData); if (editIndex >= 0) { editMode = _getEditMode(that); editData = that._editData[editIndex]; data = editData.data; item.isEditing = options.rowIndex === that._getVisibleEditRowIndex(); switch (editData.type) { case DATA_EDIT_DATA_INSERT_TYPE: if (editMode === EDIT_MODE_POPUP) { item.visible = false } item.inserted = true; item.key = key; item.data = data; break; case DATA_EDIT_DATA_UPDATE_TYPE: item.modified = true; item.oldData = item.data; item.data = gridCoreUtils.createObjectWithChanges(item.data, data); item.modifiedValues = generateDataValues(data, columns); break; case DATA_EDIT_DATA_REMOVE_TYPE: if (editMode === EDIT_MODE_BATCH) { item.data = gridCoreUtils.createObjectWithChanges(item.data, data) } item.removed = true } } }, insertRow: function() { errors.log("W0002", "dxDataGrid", "insertRow", "15.2", "Use the 'addRow' method instead"); return this.addRow() }, _initNewRow: function(options, insertKey) { this.executeAction("onInitNewRow", options); var rows = this._dataController.items(), row = rows[insertKey.rowIndex]; if (row && (!row.isEditing && "detail" === row.rowType || "detailAdaptive" === row.rowType)) { insertKey.rowIndex++ } insertKey.dataRowIndex = rows.filter(function(row, index) { return index < insertKey.rowIndex && ("data" === row.rowType || "group" === row.rowType) }).length }, _getInsertIndex: function() { var maxInsertIndex = 0; this._editData.forEach(function(editItem) { if (editItem.type === DATA_EDIT_DATA_INSERT_TYPE && editItem.key[INSERT_INDEX] > maxInsertIndex) { maxInsertIndex = editItem.key[INSERT_INDEX] } }); return maxInsertIndex + 1 }, addRow: function(parentKey) { var $firstCell, that = this, dataController = that._dataController, store = dataController.store(), key = store && store.key(), rowsView = that.getView("rowsView"), param = { data: {} }, parentRowIndex = dataController.getRowIndexByKey(parentKey), insertKey = { pageIndex: dataController.pageIndex(), rowIndex: parentRowIndex >= 0 ? parentRowIndex + 1 : rowsView ? rowsView.getTopVisibleItemIndex() : 0, parentKey: parentKey }, oldEditRowIndex = that._getVisibleEditRowIndex(), editMode = _getEditMode(that); if (editMode === EDIT_MODE_CELL && that.hasChanges()) { that.saveEditData() } that.refresh(); var insertIndex = that._getInsertIndex(); if (editMode !== EDIT_MODE_BATCH && insertIndex > 1) { return } if (!key) { param.data.__KEY__ = String(new Guid) } that._initNewRow(param, insertKey); if (editMode !== EDIT_MODE_BATCH) { that._editRowIndex = insertKey.rowIndex + that._dataController.getRowIndexOffset() } insertKey[INSERT_INDEX] = insertIndex; that._addEditData({ key: insertKey, data: param.data, type: DATA_EDIT_DATA_INSERT_TYPE }); dataController.updateItems({ changeType: "update", rowIndices: [oldEditRowIndex, insertKey.rowIndex] }); if (editMode === EDIT_MODE_POPUP) { that._showEditPopup(insertKey.rowIndex) } else { $firstCell = that.getFirstEditableCellInRow(insertKey.rowIndex); that._editCellInProgress = true; that._delayedInputFocus($firstCell, function() { that._editCellInProgress = false; var $cell = that.getFirstEditableCellInRow(insertKey.rowIndex); $cell && eventsEngine.trigger($cell, clickEvent.name) }) } that._afterInsertRow({ key: insertKey, data: param.data }) }, _isEditingStart: function(options) { this.executeAction("onEditingStart", options); return options.cancel }, _beforeEditCell: function(rowIndex, columnIndex, item) { var that = this; if (_getEditMode(that) === EDIT_MODE_CELL && !item.inserted && that.hasChanges()) { var d = new Deferred; that.saveEditData().always(function() { d.resolve(that.hasChanges()) }); return d } }, _beforeUpdateItems: function() {}, _getVisibleEditRowIndex: function() { return this._editRowIndex >= 0 ? this._editRowIndex - this._dataController.getRowIndexOffset() : -1 }, editRow: function(rowIndex) { var $editingCell, that = this, dataController = that._dataController, items = dataController.items(), item = items[rowIndex], params = { data: item && item.data, cancel: false }, oldEditRowIndex = that._getVisibleEditRowIndex(); if (!item) { return } if (rowIndex === oldEditRowIndex) { return true } if (!item.inserted) { params.key = item.key } if (that._isEditingStart(params)) { return } that.init(); that._pageIndex = dataController.pageIndex(); that._editRowIndex = (items[0].inserted ? rowIndex - 1 : rowIndex) + that._dataController.getRowIndexOffset(); that._addEditData({ data: {}, key: item.key, oldData: item.data }); var rowIndices = [oldEditRowIndex, rowIndex], editMode = _getEditMode(that); that._beforeUpdateItems(rowIndices, rowIndex, oldEditRowIndex); if (editMode === EDIT_MODE_POPUP) { that._showEditPopup(rowIndex) } else { dataController.updateItems({ changeType: "update", rowIndices: rowIndices }) } if (MODES_WITH_DELAYED_FOCUS.indexOf(editMode) !== -1) { $editingCell = that.getFocusedCellInRow(that._getVisibleEditRowIndex()); that._delayedInputFocus($editingCell, function() { $editingCell && that.component.focus($editingCell) }) } }, _showEditPopup: function(rowIndex) { var that = this, isMobileDevice = "desktop" !== devices.current().deviceType, popupOptions = extend({ showTitle: false, fullScreen: isMobileDevice, toolbarItems: [{ toolbar: "bottom", location: "after", widget: "dxButton", options: that._getSaveButtonConfig() }, { toolbar: "bottom", location: "after", widget: "dxButton", options: that._getCancelButtonConfig() }], contentTemplate: that._getPopupEditFormTemplate(rowIndex) }, that.option("editing.popup")); if (!that._editPopup) { var $popupContainer = $("<div>").appendTo(that.component.$element()).addClass(that.addWidgetPrefix(EDIT_POPUP_CLASS)); that._editPopup = that._createComponent($popupContainer, Popup, {}); that._editPopup.on("hidden", that._getEditPopupHiddenHandler()); that._editPopup.on("shown", function(e) { eventsEngine.trigger(e.component.$content().find(FOCUSABLE_ELEMENT_SELECTOR).first(), "focus") }) } that._editPopup.option(popupOptions); that._editPopup.show() }, _getEditPopupHiddenHandler: function() { var that = this; return function(e) { if (that.isEditing()) { that.cancelEditData() } } }, _getPopupEditFormTemplate: function(rowIndex) { var that = this, row = that.component.getVisibleRows()[rowIndex], templateOptions = { row: row, rowType: row.rowType, key: row.key }; return function($container) { var formTemplate = that.getEditFormTemplate(); formTemplate($container, templateOptions, true) } }, _getSaveButtonConfig: function() { return { text: this.option("editing.texts.saveRowChanges"), onClick: this.saveEditData.bind(this) } }, _getCancelButtonConfig: function() { return { text: this.option("editing.texts.cancelRowChanges"), onClick: this.cancelEditData.bind(this) } }, editCell: function(rowIndex, columnIndex) { var that = this, columnsController = that._columnsController, dataController = that._dataController, items = dataController.items(), item = items[rowIndex], params = { data: item && item.data, cancel: false }, oldEditRowIndex = that._getVisibleEditRowIndex(), visibleColumns = columnsController.getVisibleColumns(), oldColumn = visibleColumns[that._editColumnIndex]; if (typeUtils.isString(columnIndex)) { columnIndex = columnsController.columnOption(columnIndex, "index"); columnIndex = columnsController.getVisibleIndex(columnIndex) } var column = params.column = visibleColumns[columnIndex]; if (column && item && ("data" === item.rowType || "detailAdaptive" === item.rowType) && !item.removed && !_isRowEditMode(that)) { if (that.isEditCell(rowIndex, columnIndex)) { return true } var editRowIndex = rowIndex + dataController.getRowIndexOffset(); return when(that._beforeEditCell(rowIndex, columnIndex, item)).done(function(cancel) { if (cancel) { return } if (that._prepareEditCell(params, item, columnIndex, editRowIndex)) { that._repaintEditCell(column, oldColumn, oldEditRowIndex) } }) } return false }, _prepareEditCell: function(params, item, editColumnIndex, editRowIndex) { var that = this; if (!item.inserted) { params.key = item.key } if (that._isEditingStart(params)) { return false } that._editRowIndex = editRowIndex; that._editColumnIndex = editColumnIndex; that._pageIndex = that._dataController.pageIndex(); that._addEditData({ data: {}, key: item.key, oldData: item.data }); return true }, _repaintEditCell: function(column, oldColumn, oldEditRowIndex) { var that = this, rowsView = that._rowsView; if (!column || !column.showEditorAlways || oldColumn && !oldColumn.showEditorAlways) { that._editCellInProgress = true; that.getController("editorFactory").loseFocus(); that._dataController.updateItems({ changeType: "update", rowIndices: [oldEditRowIndex, that._getVisibleEditRowIndex()] }) } var $cell = rowsView && rowsView._getCellElement(that._getVisibleEditRowIndex(), that._editColumnIndex); if ($cell && !$cell.find(":focus").length) { that._focusEditingCell(function() { that._editCellInProgress = false }, $cell, true) } else { that._editCellInProgress = false } }, _delayedInputFocus: function($cell, beforeFocusCallback, callBeforeFocusCallbackAlways) { var that = this; function inputFocus() { if (beforeFocusCallback) { beforeFocusCallback() } $cell && eventsEngine.trigger($cell.find(FOCUSABLE_ELEMENT_SELECTOR).first(), "focus"); that._beforeFocusCallback = null } if (devices.real().ios || devices.real().android) { inputFocus() } else { if (that._beforeFocusCallback) { that._beforeFocusCallback() } clearTimeout(that._inputFocusTimeoutID); if (callBeforeFocusCallbackAlways) { that._beforeFocusCallback = beforeFocusCallback } that._inputFocusTimeoutID = setTimeout(inputFocus) } }, _focusEditingCell: function(beforeFocusCallback, $editCell, callBeforeFocusCallbackAlways) { var that = this, rowsView = that.getView("rowsView"); $editCell = $editCell || rowsView && rowsView._getCellElement(that._getVisibleEditRowIndex(), that._editColumnIndex); that._delayedInputFocus($editCell, beforeFocusCallback, callBeforeFocusCallbackAlways) }, removeRow: function(rowIndex) { errors.log("W0002", "dxDataGrid", "removeRow", "15.2", "Use the 'deleteRow' method instead"); return this.deleteRow(rowIndex) }, deleteRow: function(rowIndex) { var removeByKey, showDialogTitle, that = this, editingOptions = that.option("editing"), editingTexts = editingOptions && editingOptions.texts, confirmDeleteTitle = editingTexts && editingTexts.confirmDeleteTitle, isBatchMode = editingOptions && editingOptions.mode === EDIT_MODE_BATCH, confirmDeleteMessage = editingTexts && editingTexts.confirmDeleteMessage, dataController = that._dataController, oldEditRowIndex = that._getVisibleEditRowIndex(), item = dataController.items()[rowIndex], key = item && item.key; if (item) { removeByKey = function(key) { that.refresh(); var editIndex = _getIndexByKey(key, that._editData); if (editIndex >= 0) { if (that._editData[editIndex].type === DATA_EDIT_DATA_INSERT_TYPE) { that._editData.splice(editIndex, 1) } else { that._editData[editIndex].type = DATA_EDIT_DATA_REMOVE_TYPE } } else { that._addEditData({ key: key, oldData: item.data, type: DATA_EDIT_DATA_REMOVE_TYPE }) } if (isBatchMode) { dataController.updateItems({ changeType: "update", rowIndices: [oldEditRowIndex, rowIndex] }) } else { that.saveEditData() } }; if (isBatchMode || !confirmDeleteMessage) { removeByKey(key) } else { showDialogTitle = typeUtils.isDefined(confirmDeleteTitle) && confirmDeleteTitle.length > 0; dialog.confirm(confirmDeleteMessage, confirmDeleteTitle, showDialogTitle).done(function(confirmResult) { if (confirmResult) { removeByKey(key) } }) } } }, undeleteRow: function(rowIndex) { var that = this, dataController = that._dataController, item = dataController.items()[rowIndex], oldEditRowIndex = that._getVisibleEditRowIndex(), key = item && item.key; if (item) { var editData, editIndex = _getIndexByKey(key, that._editData); if (editIndex >= 0) { editData = that._editData[editIndex]; if (typeUtils.isEmptyObject(editData.data)) { that._editData.splice(editIndex, 1) } else { editData.type = DATA_EDIT_DATA_UPDATE_TYPE } dataController.updateItems({ changeType: "update", rowIndices: [oldEditRowIndex, rowIndex] }) } } }, _saveEditDataCore: function(deferreds, results) { var that = this, store = that._dataController.store(), isDataSaved = true; function executeEditingAction(actionName, params, func) { var deferred = new Deferred; that.executeAction(actionName, params); function createFailureHandler(deferred) { return function(arg) { var error = arg instanceof Error ? arg : new Error(arg && String(arg) || "Unknown error"); deferred.reject(error) } } when(deferredUtils.fromPromise(params.cancel)).done(function(cancel) { if (cancel) { deferred.resolve("cancel") } else { func(params).done(deferred.resolve).fail(createFailureHandler(deferred)) } }).fail(createFailureHandler(deferred)); return deferred } each(that._editData, function(index, editData) { var deferred, doneDeferred, params, data = editData.data, oldData = editData.oldData, type = editData.type; if (that._beforeSaveEditData(editData, index)) { return } switch (type) { case DATA_EDIT_DATA_REMOVE_TYPE: params = { data: oldData, key: editData.key, cancel: false }; deferred = executeEditingAction("onRowRemoving", params, function() { return store.remove(editData.key) }); break; case DATA_EDIT_DATA_INSERT_TYPE: params = { data: data, cancel: false }; deferred = executeEditingAction("onRowInserting", params, function() { return store.insert(params.data).done(function(data, key) { editData.key = key }) }); break; case DATA_EDIT_DATA_UPDATE_TYPE: params = { newData: data, oldData: oldData, key: editData.key, cancel: false }; deferred = executeEditingAction("onRowUpdating", params, function() { return store.update(editData.key, params.newData) }) } if (deferred) { doneDeferred = new Deferred; deferred.always(function(data) { isDataSaved = "cancel" !== data; results.push({ key: editData.key, result: data }) }).always(doneDeferred.resolve); deferreds.push(doneDeferred.promise()) } }); return isDataSaved }, _processSaveEditDataResult: function(results) { var i, arg, cancel, editData, editIndex, isError, $popupContent, that = this, dataController = that._dataController, hasSavedData = false, editMode = _getEditMode(that); for (i = 0; i < results.length; i++) { arg = results[i].result; cancel = "cancel" === arg; editIndex = _getIndexByKey(results[i].key, that._editData); editData = that._editData[editIndex]; if (editData) { isError = arg && arg instanceof Error; if (isError) { editData.error = arg; $popupContent = that.getPopupContent(); dataController.dataErrorOccurred.fire(arg, $popupContent); if (editMode !== EDIT_MODE_BATCH) { break } } else { if (!cancel || editMode !== EDIT_MODE_BATCH && editData.type === DATA_EDIT_DATA_REMOVE_TYPE) { that._editData.splice(editIndex, 1); hasSavedData = !cancel } } } } return hasSavedData }, _fireSaveEditDataEvents: function(editData) { var that = this; each(editData, function(_, itemData) { var data = itemData.data, key = itemData.key, type = itemData.type, params = { key: key, data: data }; if (itemData.error) { params.error = itemData.error } switch (type) { case DATA_EDIT_DATA_REMOVE_TYPE: that.executeAction("onRowRemoved", extend({}, params, { data: itemData.oldData })); break; case DATA_EDIT_DATA_INSERT_TYPE: that.executeAction("onRowInserted", params); break; case DATA_EDIT_DATA_UPDATE_TYPE: that.executeAction("onRowUpdated", params) } }) }, saveEditData: function() { var editData, that = this, results = [], deferreds = [], dataController = that._dataController, dataSource = dataController.dataSource(), editMode = _getEditMode(that), result = new Deferred; var resetEditIndices = function(that) { if (editMode !== EDIT_MODE_CELL) { that._editColumnIndex = -1; that._editRowIndex = -1 } }; if (that._beforeSaveEditData() || that._saving) { that._afterSaveEditData(); return result.resolve().promise() } if (!that._saveEditDataCore(deferreds, results) && editMode === EDIT_MODE_CELL) { that._focusEditingCell() } if (deferreds.length) { that._saving = true; dataSource && dataSource.beginLoading(); when.apply($, deferreds).done(function() { editData = that._editData.slice(0); if (that._processSaveEditDataResult(results)) { resetEditIndices(that); if (editMode === EDIT_MODE_POPUP && that._editPopup) { that._editPopup.hide() } dataSource && dataSource.endLoading(); when(dataController.refresh()).always(function() { that._fireSaveEditDataEvents(editData); that._afterSaveEditData(); result.resolve() }) } else { dataSource && dataSource.endLoading(); result.resolve() } }).fail(function() { dataSource && dataSource.endLoading(); result.resolve() }); return result.always(function() { that._focusEditingCell(); that._saving = false }).promise() } if (_isRowEditMode(that)) { if (!that.hasChanges()) { that.cancelEditData() } } else { if (CELL_BASED_MODES.indexOf(editMode) !== -1) { resetEditIndices(that); dataController.updateItems() } else { that._focusEditingCell() } } that._afterSaveEditData(); return result.resolve().promise() }, isSaving: function() { return this._saving }, _updateEditColumn: function() { var that = this, useIcons = that.option("editing.useIcons"), isEditColumnVisible = that._isEditColumnVisible(), cssClass = COMMAND_EDIT_CLASS + (useIcons ? " " + COMMAND_EDIT_WITH_ICONS_CLASS : ""); that._columnsController.addCommandColumn({ command: "edit", visible: isEditColumnVisible, cssClass: cssClass, width: "auto", cellTemplate: editCellTemplate }); that._columnsController.columnOption("command:edit", { visible: isEditColumnVisible, cssClass: cssClass }) }, _isEditColumnVisible: function() { var that = this, editingOptions = that.option("editing"); if (editingOptions) { var editMode = _getEditMode(that), isVisibleWithCurrentEditMode = false; switch (editMode) { case EDIT_MODE_ROW: isVisibleWithCurrentEditMode = editingOptions.allowUpdating || editingOptions.allowAdding; break; case EDIT_MODE_FORM: case EDIT_MODE_POPUP: isVisibleWithCurrentEditMode = editingOptions.allowUpdating } return editingOptions.allowDeleting || isVisibleWithCurrentEditMode } }, _updateEditButtons: function() { var that = this, headerPanel = that.getView("headerPanel"), hasChanges = that.hasChanges(); if (headerPanel) { headerPanel.setToolbarItemDisabled("saveButton", !hasChanges); headerPanel.setToolbarItemDisabled("revertButton", !hasChanges) } }, _applyModified: function($element) { $element && $element.addClass(CELL_MODIFIED) }, _beforeCloseEditCellInBatchMode: function() {}, cancelEditData: function() { var that = this, editMode = _getEditMode(that), rowIndex = this._editRowIndex, dataController = that._dataController; that._beforeCancelEditData(); that.init(); if (ROW_BASED_MODES.indexOf(editMode) !== -1 && rowIndex >= 0) { dataController.updateItems({ changeType: "update", rowIndices: [rowIndex, rowIndex + 1] }) } else { dataController.updateItems() } if (editMode === EDIT_MODE_POPUP) { that._hideEditPopup() } }, _hideEditPopup: function() { this._editPopup && this._editPopup.option("visible", false) }, hasEditData: function() { return this.hasChanges() }, closeEditCell: function() { var that = this, editMode = _getEditMode(that), oldEditRowIndex = that._getVisibleEditRowIndex(), dataController = that._dataController; if (!_isRowEditMode(that)) { setTimeout(function() { if (editMode === EDIT_MODE_CELL && that.hasChanges()) { that.saveEditData().done(function() { if (!that.hasChanges()) { that.closeEditCell() } }) } else { if (oldEditRowIndex >= 0) { var rowIndices = [oldEditRowIndex]; that._editRowIndex = -1; that._editColumnIndex = -1; that._beforeCloseEditCellInBatchMode(rowIndices); dataController.updateItems({ changeType: "update", rowIndices: rowIndices }) } } }) } }, update: function(changeType) { var that = this, dataController = that._dataController; if (dataController && that._pageIndex !== dataController.pageIndex()) { if ("refresh" === changeType) { that.refresh() } that._pageIn