devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,101 lines (1,098 loc) • 83.5 kB
JavaScript
/**
* 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 = " "
}
};
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