UNPKG

@progress/kendo-ui

Version:

This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.

14,402 lines 635 kB
require("./kendo.icons.js");
require("./kendo.badge.js");
require("./kendo.html.button.js");
require("./kendo.data-T0mqWOrQ.js");
require("./kendo.selectable.js");
require("./kendo.textbox.js");
require("./kendo.editable.js");
require("./kendo.form.js");
require("./kendo.window.js");
require("./kendo.ooxml.js");
require("./kendo.excel.js");
require("./kendo.groupable.js");
require("./kendo.reorderable.js");
require("./kendo.resizable.js");
require("./kendo.sortable.js");
require("./kendo.menu.js");
require("./kendo.toolbar.js");
require("./kendo.switch.js");
require("./kendo.pager.js");
require("./kendo.pane.js");
require("./kendo.filtermenu.js");
require("./kendo.columnmenu.js");
require("./kendo.columnsorter.js");
require("./kendo.aiprompt-B8fknJle.js");
require("./kendo.filtercell.js");
require("./kendo.pdf.js");
require("./kendo.csv.js");
require("./kendo.dialog.js");
require("./kendo.smartbox.js");
require("./kendo.loader.js");
require("./kendo.html.loadercontainer.js");
const require_loaderContainer = require("./loaderContainer-DYINJiua.js");
//#region ../src/grid/contextmenu.js
(function($, undefined) {
	var kendo = window.kendo, ContextMenu = kendo.ui.ContextMenu, extend = $.extend, encode = kendo.htmlEncode;
	var ACTION = "action";
	var GridContextMenu = ContextMenu.extend({
		init: function(element, options) {
			var that = this;
			ContextMenu.fn.init.call(that, element, options);
			that._overrideTemplates();
			that._extendItems();
			that.bind("select", that._onSelect.bind(that));
			that.bind("open", that._onOpen.bind(that));
			ContextMenu.fn.endInit.call(that);
		},
		_overrideTemplates: function() {
			this.templates.sprite = ({ icon, spriteCssClass }) => `${icon || spriteCssClass ? kendo.ui.icon({
				icon: encode(icon || ""),
				iconClass: encode(spriteCssClass || "")
			}) : ""}`;
		},
		defaultItems: {
			"separator": {
				name: "separator",
				separator: true
			},
			"create": {
				name: "create",
				text: "Add",
				icon: "plus",
				command: "AddCommand",
				rules: "isEditable"
			},
			"edit": {
				name: "edit",
				text: "Edit",
				icon: "pencil",
				command: "EditCommand",
				rules: "isEditable"
			},
			"destroy": {
				name: "destroy",
				text: "Delete",
				icon: "trash",
				command: "DeleteCommand",
				rules: "isEditable"
			},
			"select": {
				name: "select",
				text: "Select",
				icon: "table-body",
				rules: "isSelectable",
				items: [
					{
						name: "selectRow",
						text: "Row",
						icon: "table-row-groups",
						command: "SelectRowCommand"
					},
					{
						name: "selectAllRows",
						text: "All rows",
						icon: "grid",
						command: "SelectAllRowsCommand"
					},
					{
						name: "clearSelection",
						text: "Clear selection",
						icon: "table-unmerge",
						softRules: "hasSelection",
						command: "ClearSelectionCommand"
					}
				]
			},
			"copySelection": {
				name: "copySelection",
				text: "Copy selection",
				icon: "page-header-section",
				rules: "isSelectable",
				softRules: "hasSelection",
				command: "CopySelectionCommand",
				options: "withHeaders"
			},
			"copySelectionNoHeaders": {
				name: "copySelectionNoHeaders",
				text: "Copy selection (No Headers)",
				icon: "file-txt",
				rules: "isSelectable",
				softRules: "hasSelection",
				command: "CopySelectionCommand"
			},
			"paste": {
				name: "paste",
				text: "Paste (use CTRL/⌘ + V)",
				rules: "allowPaste",
				softRules: "alwaysDisabled",
				icon: "clipboard"
			},
			"reorderRow": {
				name: "reorderRow",
				text: "Reorder row",
				icon: "caret-alt-expand",
				rules: "isRowReorderable",
				softRules: "isSorted",
				items: [
					{
						name: "reorderRowUp",
						text: "Up",
						icon: "chevron-up",
						command: "ReorderRowCommand",
						options: "dir:up"
					},
					{
						name: "reorderRowDown",
						text: "Down",
						icon: "chevron-down",
						command: "ReorderRowCommand",
						options: "dir:down"
					},
					{
						name: "reorderRowTop",
						text: "Top",
						icon: "caret-alt-to-top",
						command: "ReorderRowCommand",
						options: "dir:top"
					},
					{
						name: "reorderRowBottom",
						text: "Bottom",
						icon: "caret-alt-to-bottom",
						command: "ReorderRowCommand",
						options: "dir:bottom"
					}
				]
			},
			"exportPDF": {
				name: "exportPDF",
				text: "Export to PDF",
				icon: "file-pdf",
				command: "ExportPDFCommand"
			},
			"exportExcel": {
				name: "exportExcel",
				text: "Export to Excel",
				icon: "file-excel",
				items: [
					{
						name: "exportToExcelAll",
						text: "All",
						command: "ExportExcelCommand"
					},
					{
						name: "exportToExcelSelection",
						text: "Selection",
						command: "ExportExcelCommand",
						softRules: "hasSelection",
						options: "selection,withHeaders"
					},
					{
						name: "exportToExcelSelectionNoHeaders",
						text: "Selection (No Headers)",
						softRules: "hasSelection",
						command: "ExportExcelCommand",
						options: "selection"
					}
				]
			},
			"exportCSV": {
				name: "exportCSV",
				text: "Export to CSV",
				icon: "file-csv",
				items: [
					{
						name: "exportToCSVAll",
						text: "All",
						command: "ExportCSVCommand"
					},
					{
						name: "exportToCSVSelection",
						text: "Selection",
						command: "ExportCSVCommand",
						softRules: "hasSelection",
						options: "selection,withHeaders"
					},
					{
						name: "exportToCSVSelectionNoHeaders",
						text: "Selection (No Headers)",
						softRules: "hasSelection",
						command: "ExportCSVCommand",
						options: "selection"
					}
				]
			},
			"sortAsc": {
				name: "sortAsc",
				text: "Sort Ascending",
				icon: "sort-asc-small",
				rules: "isSortable",
				command: "SortCommand",
				options: "dir:asc"
			},
			"sortDesc": {
				name: "sortDesc",
				text: "Sort Descending",
				icon: "sort-desc-small",
				rules: "isSortable",
				command: "SortCommand",
				options: "dir:desc"
			},
			"moveGroupPrevious": {
				name: "moveGroupPrevious",
				text: "Move previous",
				icon: "arrow-left",
				rules: "isGroupable",
				softRules: "canMoveGroupPrev",
				command: "MoveGroupCommand",
				options: "dir:prev"
			},
			"moveGroupNext": {
				name: "moveGroupNext",
				text: "Move next",
				icon: "arrow-right",
				rules: "isGroupable",
				softRules: "canMoveGroupNext",
				command: "MoveGroupCommand",
				options: "dir:next"
			},
			"pinRow": {
				name: "pinRow",
				text: "Pin row",
				icon: "pin",
				rules: "isPinnable",
				items: [
					{
						name: "pinTop",
						text: "Pin row to top",
						icon: "pin-top",
						command: "PinTopCommand"
					},
					{
						name: "pinBottom",
						text: "Pin row to bottom",
						icon: "pin-bottom",
						command: "PinBottomCommand"
					},
					{
						name: "unpin",
						text: "Unpin row",
						icon: "unpin",
						command: "UnpinCommand"
					}
				]
			}
		},
		events: ContextMenu.fn.events.concat([ACTION]),
		_onSelect: function(ev) {
			var command = $(ev.item).data("command");
			var options = $(ev.item).data("options");
			options = options ? options.split(",").map((val) => {
				if (val.indexOf(":") > -1) {
					var [key, val] = val.split(":");
					return { [key || "_"]: val };
				}
				return { [val]: true };
			}).reduce((acc, v) => Object.assign(acc, v), {}) : {};
			var target = $(ev.target);
			if (!command) return;
			this.action({
				command,
				options: Object.assign(options, { target })
			});
		},
		_onOpen: function(ev) {
			var menu = ev.sender, items = menu.options.items, elTarget = $(ev.event ? ev.event.target : null);
			if (!items && $.isEmptyObject(this.defaultItems) || elTarget.closest(".k-grid-column-menu").length) {
				ev.preventDefault();
				return;
			}
			this._toggleSeparatorVisibility();
			menu.element.find(`[${kendo.attr("soft-rules")}]`).each((i, item) => {
				var rules = $(item).attr(kendo.attr("soft-rules")).split(";");
				menu.enable(item, this._validateSoftRules(rules, elTarget));
			});
		},
		_toggleSeparatorVisibility: function() {
			this.element.find(".k-item.k-separator").filter((i, item) => {
				var prev = $(item).prev(".k-item:not(.k-separator)");
				var next = $(item).next(".k-item:not(.k-separator)");
				return !(prev.length && next.length);
			}).hide();
		},
		_extendItems: function() {
			var that = this, items = that.options.items, item, isBuiltInTool;
			if (items && items.length) for (var i = 0; i < items.length; i++) {
				item = items[i];
				isBuiltInTool = $.isPlainObject(item) && Object.keys(item).length === 1 && item.name;
				if (isBuiltInTool) item = item.name;
				if ($.isPlainObject(item)) that._append(item);
				else if (that.defaultItems[item]) {
					item = that.defaultItems[item];
					that._append(item);
				} else if (typeof item === "string") {
					item = {
						name: item,
						text: item,
						spriteCssClass: item,
						command: item + "Command"
					};
					that._append(item);
				}
			}
			else for (var key in that.defaultItems) {
				item = that.defaultItems[key];
				that._append(item);
			}
		},
		_extendItem: function(item) {
			var that = this, messages = that.options.messages, attr = item.attr || {};
			if (item.command) attr[kendo.attr("command")] = item.command;
			if (item.options) attr[kendo.attr("options")] = item.options;
			if (item.softRules) attr[kendo.attr("soft-rules")] = item.softRules;
			if (item.items) for (var j = 0; j < item.items.length; j++) item.items.forEach((subItem) => {
				that._extendItem(subItem);
			});
			extend(item, {
				text: messages.commands[item.name],
				icon: item.icon || "",
				spriteCssClass: item.spriteCssClass || "",
				attr,
				uid: kendo.guid()
			});
		},
		_validateSoftRules: function(rules, target) {
			if (!rules || !(rules && rules.length)) return true;
			for (var i = 0; i < rules.length; i++) if (!this._readState(rules[i], target)) return false;
			return true;
		},
		_validateRules: function(tool) {
			var rules = tool.rules ? tool.rules.split(";") : [];
			if (!rules.length) return true;
			for (var i = 0; i < rules.length; i++) if (!this._readState(rules[i])) return false;
			return true;
		},
		_readState: function(state, target) {
			var states = this.options.states;
			if (kendo.isFunction(states[state])) return states[state](target);
			else return states[state];
		},
		_append: function(item) {
			var that = this;
			that._extendItem(item);
			if (that._validateRules(item)) that.append(item);
		},
		action: function(args) {
			this.trigger(ACTION, args);
		}
	});
	kendo.ui.grid = kendo.ui.grid || {};
	extend(kendo.ui.grid, { ContextMenu: GridContextMenu });
})(window.kendo.jQuery);
//#endregion
//#region ../src/grid/commands.js
(function($, undefined) {
	const kendo = window.kendo, extend = $.extend;
	const Command = kendo.Class.extend({ init: function(options) {
		this.options = options;
		this.grid = options.grid;
	} });
	const MoveGroupCommand = Command.extend({ exec: function() {
		const that = this, groupable = that.grid.groupable, options = that.options, target = options.target.closest(".k-chip"), method = options.dir === "next" ? "after" : "before";
		(options.dir === "next" ? target.next() : target.prev())[method](target);
		groupable._change();
	} });
	const SortCommand = Command.extend({ exec: function() {
		const that = this, grid = that.grid, dataSource = grid.dataSource, options = that.options, dir = options.dir, field = grid._getCellField(options.target), multipleMode = grid.options.sortable.mode && grid.options.sortable.mode === "multiple", compare = grid.options.compare;
		let length, idx, sort = dataSource.sort() || [];
		if (multipleMode) {
			for (idx = 0, length = sort.length; idx < length; idx++) if (sort[idx].field === field) {
				sort.splice(idx, 1);
				break;
			}
			sort.push({
				field,
				dir,
				compare
			});
		} else sort = [{
			field,
			dir,
			compare
		}];
		dataSource.sort(sort);
	} });
	const AddCommand = Command.extend({ exec: function() {
		this.grid.addRow();
	} });
	const EditCommand = Command.extend({ exec: function() {
		const that = this, grid = that.grid, inCellMode = grid._editMode() === "incell", target = inCellMode ? that.options.target : that.options.target.closest("tr");
		if (inCellMode) grid.editCell(target);
		else grid.editRow(target);
	} });
	const DeleteCommand = Command.extend({ exec: function() {
		const that = this, grid = that.grid, target = that.options.target.closest("tr");
		grid.removeRow(target);
	} });
	const CopySelectionCommand = Command.extend({ exec: function() {
		const that = this, grid = that.grid, withHeaders = that.options.withHeaders;
		grid.copySelectionToClipboard(withHeaders);
	} });
	const SelectRowCommand = Command.extend({ exec: function() {
		const that = this, grid = that.grid, selectMode = kendo.ui.Selectable.parseOptions(grid.options.selectable), target = that.options.target.closest("tr");
		grid.select(selectMode.cell ? target.find("td") : target);
	} });
	const SelectAllRowsCommand = Command.extend({ exec: function() {
		const grid = this.grid, selectMode = kendo.ui.Selectable.parseOptions(grid.options.selectable), rows = grid.items();
		grid.select(selectMode.cell ? rows.find("td") : rows);
	} });
	const ClearSelectionCommand = Command.extend({ exec: function() {
		this.grid.clearSelection();
	} });
	const ReorderRowCommand = Command.extend({ exec: function() {
		const that = this, grid = that.grid, dir = that.options.dir, target = that.options.target.closest("tr"), index = target.index();
		let newIndex;
		switch (dir) {
			case "up":
				newIndex = index - 1;
				break;
			case "down":
				newIndex = index + 2;
				break;
			case "top":
				newIndex = 0;
				break;
			case "bottom":
				newIndex = grid.items().length;
				break;
		}
		grid.reorderRowTo(target, newIndex);
	} });
	const ExportPDFCommand = Command.extend({ exec: function() {
		this.grid.saveAsPDF();
	} });
	const ExportExcelCommand = Command.extend({ exec: function() {
		const that = this, selection = that.options.selection, withHeaders = that.options.withHeaders, grid = that.grid;
		if (selection) grid.exportSelectedToExcel(withHeaders);
		else grid.saveAsExcel();
	} });
	const ExportCSVCommand = Command.extend({ exec: function() {
		const selection = this.options.selection;
		const withHeaders = this.options.withHeaders;
		const grid = this.grid;
		if (selection) grid.exportSelectedToCSV(withHeaders);
		else grid.saveAsCSV();
	} });
	const PinTopCommand = Command.extend({ exec: function() {
		const grid = this.grid;
		const target = this.options.target.closest("tr");
		grid.pinRows(target, "top");
	} });
	const PinBottomCommand = Command.extend({ exec: function() {
		const grid = this.grid;
		const target = this.options.target.closest("tr");
		grid.pinRows(target, "bottom");
	} });
	const UnpinCommand = Command.extend({ exec: function() {
		const grid = this.grid;
		const target = this.options.target.closest("tr");
		grid.unpinRows(target);
	} });
	kendo.ui.grid = kendo.ui.grid || {};
	extend(kendo.ui.grid, {
		GridCommand: Command,
		commands: {
			SortCommand,
			AddCommand,
			EditCommand,
			DeleteCommand,
			CopySelectionCommand,
			SelectRowCommand,
			SelectAllRowsCommand,
			ClearSelectionCommand,
			ReorderRowCommand,
			ExportPDFCommand,
			ExportExcelCommand,
			ExportCSVCommand,
			MoveGroupCommand,
			PinTopCommand,
			PinBottomCommand,
			UnpinCommand
		}
	});
})(window.kendo.jQuery);
//#endregion
//#region ../src/grid/sticky-groups.js
const GROUPING_ROW = "k-grouping-row";
function closeRange(entry, footerIndex, lastChildIndex) {
	return {
		headerIndex: entry.headerIndex,
		footerIndex,
		firstChildIndex: entry.headerIndex + 1,
		lastChildIndex,
		level: entry.level,
		collapsed: entry.collapsed
	};
}
function buildGroupRangeMap(tbody, skipOffset, lockedTbody) {
	skipOffset = skipOffset || 0;
	const ranges = [];
	const stack = [];
	const rows = tbody.children;
	const lockedRows = lockedTbody ? lockedTbody.children : null;
	const rowCount = rows.length;
	for (let i = 0; i < rowCount; i++) {
		const row = rows[i];
		const isGroupHeader = row.classList.contains(GROUPING_ROW);
		const isGroupFooter = row.classList.contains("k-group-footer");
		const globalIndex = skipOffset + i;
		if (isGroupHeader) {
			const levelRow = lockedRows ? lockedRows[i] : row;
			const level = levelRow.querySelectorAll(".k-group-cell").length;
			const collapsed = !!levelRow.querySelector("td[aria-expanded='false']");
			for (let s = stack.length - 1; s >= 0; s--) if (stack[s].level >= level) {
				ranges.push(closeRange(stack[s], null, globalIndex - 1));
				stack.splice(s, 1);
			}
			stack.push({
				headerIndex: globalIndex,
				level,
				collapsed
			});
		} else if (isGroupFooter) {
			if (stack.length > 0) {
				const closedHeader = stack.pop();
				ranges.push(closeRange(closedHeader, globalIndex, globalIndex - 1));
			}
		}
	}
	for (let r = 0; r < stack.length; r++) ranges.push(closeRange(stack[r], null, skipOffset + rowCount - 1));
	const map = {};
	for (let m = 0; m < ranges.length; m++) map[ranges[m].headerIndex] = ranges[m];
	return map;
}
function computeStickyHeaderItems(groupRanges, firstVisibleIndex, rawFirstVisibleIndex, allHeaderIndices) {
	const stickyHeaders = [];
	const footerThreshold = rawFirstVisibleIndex !== void 0 ? rawFirstVisibleIndex : firstVisibleIndex;
	const bestHeaderPerLevel = /* @__PURE__ */ new Map();
	for (const range of Object.values(groupRanges)) {
		if (range.collapsed) continue;
		if (range.headerIndex < firstVisibleIndex) {
			let groupEnd;
			if (range.footerIndex !== null && allHeaderIndices) {
				groupEnd = range.footerIndex;
				for (let h = 0; h < allHeaderIndices.length; h++) if (allHeaderIndices[h] > range.footerIndex) {
					groupEnd = allHeaderIndices[h] - 1;
					break;
				}
			} else groupEnd = range.footerIndex !== null ? range.footerIndex : range.lastChildIndex;
			if (groupEnd > range.headerIndex && groupEnd >= footerThreshold) {
				const existing = bestHeaderPerLevel.get(range.level);
				if (!existing || range.headerIndex > existing.headerIndex) bestHeaderPerLevel.set(range.level, range);
			}
		}
	}
	const levels = Array.from(bestHeaderPerLevel.keys()).sort((a, b) => a - b);
	for (const lvl of levels) stickyHeaders.push(bestHeaderPerLevel.get(lvl));
	return stickyHeaders;
}
function computeStickyFooterItems(groupRanges, lastVisibleIndex) {
	const stickyFooters = [];
	const bestFooterPerLevel = /* @__PURE__ */ new Map();
	for (const range of Object.values(groupRanges)) {
		if (range.footerIndex === null || range.collapsed) continue;
		if (range.footerIndex > lastVisibleIndex && range.headerIndex <= lastVisibleIndex) {
			const existing = bestFooterPerLevel.get(range.level);
			if (!existing || range.footerIndex < existing.footerIndex) bestFooterPerLevel.set(range.level, range);
		}
	}
	const levels = Array.from(bestFooterPerLevel.keys()).sort((a, b) => b - a);
	for (const lvl of levels) stickyFooters.push(bestFooterPerLevel.get(lvl));
	return stickyFooters;
}
function getStickyRows(containerEl) {
	const wrap = containerEl.querySelector(".k-grid-header-wrap, .k-grid-footer-wrap");
	const tbody = wrap ? wrap.querySelector("table > tbody") : containerEl.querySelector(":scope > table > tbody");
	return {
		rows: tbody ? tbody.children : [],
		heightTarget: containerEl
	};
}
function clearRowTransforms(tr) {
	tr.style.transform = "";
	tr.style.clipPath = "";
	tr.style.display = "";
}
function applyHeaderPushTransforms(containerEl, pushOffsets) {
	const { rows: trs, heightTarget } = getStickyRows(containerEl);
	let totalVisibleHeight = 0;
	let hasPush = false;
	for (let i = 0; i < trs.length && i < pushOffsets.length; i++) {
		const tr = trs[i];
		const rowH = tr.getBoundingClientRect().height || tr.offsetHeight;
		const push = pushOffsets[i];
		if (push < 0) {
			hasPush = true;
			const absPush = -push;
			const visibleHeight = Math.max(rowH - absPush, 0);
			totalVisibleHeight += visibleHeight;
			if (visibleHeight <= 0) {
				tr.style.display = "none";
				tr.style.transform = "";
				tr.style.clipPath = "";
			} else {
				tr.style.display = "";
				tr.style.transform = `translateY(${push}px)`;
				tr.style.clipPath = `inset(${absPush}px 0 0 0)`;
			}
		} else {
			totalVisibleHeight += rowH;
			clearRowTransforms(tr);
		}
	}
	if (hasPush) {
		const cs = getComputedStyle(heightTarget);
		const border = (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0);
		heightTarget.style.height = `${Math.max(totalVisibleHeight - border, 0)}px`;
	} else heightTarget.style.height = "";
}
function applyFooterPushTransforms(containerEl, pushOffsets) {
	const { rows: trs, heightTarget } = getStickyRows(containerEl);
	let totalVisibleHeight = 0;
	let hasPush = false;
	let totalShrink = 0;
	const rowHeights = [];
	for (let i = 0; i < trs.length && i < pushOffsets.length; i++) {
		const rowH = trs[i].getBoundingClientRect().height || trs[i].offsetHeight;
		rowHeights.push(rowH);
		const push = pushOffsets[i];
		if (push > 0) {
			hasPush = true;
			const visibleH = Math.max(rowH - push, 0);
			totalVisibleHeight += visibleH;
			totalShrink += rowH - visibleH;
		} else totalVisibleHeight += rowH;
	}
	for (let j = 0; j < trs.length && j < pushOffsets.length; j++) {
		const tr = trs[j];
		const pushVal = pushOffsets[j];
		const rH = rowHeights[j];
		if (pushVal > 0) {
			if (Math.max(rH - pushVal, 0) <= 0) tr.style.clipPath = "inset(0 0 100% 0)";
			else tr.style.clipPath = `inset(0 0 ${pushVal}px 0)`;
			tr.style.transform = "";
		} else if (hasPush) {
			tr.style.transform = `translateY(${-totalShrink}px)`;
			tr.style.clipPath = "";
		} else clearRowTransforms(tr);
	}
	if (hasPush) {
		const cs = getComputedStyle(heightTarget);
		const border = (parseFloat(cs.borderTopWidth) || 0) + (parseFloat(cs.borderBottomWidth) || 0);
		heightTarget.style.height = `${Math.max(totalVisibleHeight - border, 0)}px`;
	} else heightTarget.style.height = "";
}
function resetStickyTransforms(containerEl) {
	const { rows: trs, heightTarget } = getStickyRows(containerEl);
	for (let i = 0; i < trs.length; i++) clearRowTransforms(trs[i]);
	heightTarget.style.height = "";
}
function stickyItemsChanged(current, prev) {
	if (current.length !== prev.length) return true;
	for (let i = 0; i < current.length; i++) if (current[i].headerIndex !== prev[i].headerIndex) return true;
	return false;
}
function findStickyChildRow(tbody, range, skipOffset, reverse) {
	const start = reverse ? range.lastChildIndex : range.firstChildIndex;
	const end = reverse ? range.firstChildIndex : range.lastChildIndex;
	const step = reverse ? -1 : 1;
	for (let i = start; reverse ? i >= end : i <= end; i += step) {
		const row = tbody.children[i - skipOffset];
		if (row) return row;
	}
	return null;
}
function stickyParentPadding(stickyRows, items, range) {
	let padding = 0;
	for (let i = 0; i < items.length; i++) if (items[i].level < range.level) padding += stickyRows[i] ? stickyRows[i].offsetHeight || 36 : 36;
	return padding;
}
function buildStickyRowMetrics(tbody, skipOffset) {
	const rows = tbody.children;
	const rowCount = rows.length;
	const rowTops = new Array(rowCount);
	const rowHeightsArr = new Array(rowCount);
	let lastVisibleBottom = 0;
	for (let i = 0; i < rowCount; i++) {
		const h = rows[i].offsetHeight;
		if (h > 0) {
			rowTops[i] = rows[i].offsetTop;
			lastVisibleBottom = rowTops[i] + h;
		} else rowTops[i] = lastVisibleBottom;
		rowHeightsArr[i] = h;
	}
	const totalRowCount = skipOffset + rowCount;
	const getRowHeight = (index) => {
		const local = index - skipOffset;
		return local >= 0 && local < rowCount ? rowHeightsArr[local] : 36;
	};
	const getRowOffset = (index) => {
		const local = index - skipOffset;
		if (local >= 0 && local < rowCount) return rowTops[local];
		if (rowCount === 0) return;
		if (local < 0) return rowTops[0] + local * 36;
		return rowTops[rowCount - 1] + rowHeightsArr[rowCount - 1] + (local - rowCount) * 36;
	};
	const findFirstVisible = (effectiveTop) => {
		let lo = 0, hi = rowCount;
		while (lo < hi) {
			const mid = lo + hi >> 1;
			if (rowTops[mid] + rowHeightsArr[mid] <= effectiveTop) lo = mid + 1;
			else hi = mid;
		}
		return lo < rowCount ? skipOffset + lo : skipOffset;
	};
	const findFirstAtTop = (effectiveTop) => {
		let lo = 0, hi = rowCount;
		while (lo < hi) {
			const mid = lo + hi >> 1;
			if (rowTops[mid] < effectiveTop) lo = mid + 1;
			else hi = mid;
		}
		return lo < rowCount ? skipOffset + lo : skipOffset;
	};
	const findLastVisible = (effectiveBottom) => {
		let lo = 0, hi = rowCount - 1;
		while (lo <= hi) {
			const mid = lo + hi >> 1;
			if (rowTops[mid] + rowHeightsArr[mid] <= effectiveBottom) lo = mid + 1;
			else hi = mid - 1;
		}
		return hi >= 0 ? skipOffset + hi : skipOffset + rowCount - 1;
	};
	return {
		getRowHeight,
		getRowOffset,
		findFirstVisible,
		findFirstAtTop,
		findLastVisible,
		totalRowCount
	};
}
function convergeStickyHeaders(metrics, groupRanges, scrollTop) {
	const { getRowHeight, getRowOffset, findFirstVisible, findFirstAtTop, totalRowCount } = metrics;
	const rawFirstVisibleIndex = Math.min(findFirstVisible(scrollTop), totalRowCount - 1);
	let stickyHeaderHeight = 0;
	let headerPushOffsets = [];
	let stickyHeaders = [];
	const allHeaderIndices = Object.keys(groupRanges).map(Number).sort((a, b) => a - b);
	const computeHeaderPush = (ranges) => {
		const offsets = [];
		let cumHeight = 0;
		for (let ci = 0; ci < ranges.length; ci++) {
			const range = ranges[ci];
			const rowH = getRowHeight(range.headerIndex);
			let push = 0;
			let boundaryIndex;
			if (range.footerIndex !== null) {
				boundaryIndex = range.footerIndex + 1;
				for (let h = 0; h < allHeaderIndices.length; h++) if (allHeaderIndices[h] > range.footerIndex) {
					boundaryIndex = allHeaderIndices[h];
					break;
				}
			} else boundaryIndex = range.lastChildIndex + 1;
			const boundaryOffset = getRowOffset(boundaryIndex);
			if (boundaryOffset !== void 0) {
				const slotBottom = cumHeight + rowH;
				const boundaryRelative = boundaryOffset - scrollTop;
				if (boundaryRelative < slotBottom) push = boundaryRelative - slotBottom;
			}
			offsets.push(push);
			cumHeight += Math.max(rowH + push, 0);
		}
		return {
			totalHeight: cumHeight,
			offsets
		};
	};
	const seenFirstVisible = /* @__PURE__ */ new Set();
	while (true) {
		const firstVisibleIndex = Math.min(findFirstAtTop(scrollTop + Math.max(stickyHeaderHeight, 0)), totalRowCount - 1);
		const isHeaderCycle = seenFirstVisible.has(firstVisibleIndex);
		seenFirstVisible.add(firstVisibleIndex);
		const result = computeStickyHeaderItems(groupRanges, firstVisibleIndex, rawFirstVisibleIndex, allHeaderIndices);
		const headerPush = computeHeaderPush(result);
		const headerConverged = !stickyItemsChanged(result, stickyHeaders) && Math.abs(headerPush.totalHeight - stickyHeaderHeight) < 1;
		stickyHeaders = result;
		stickyHeaderHeight = headerPush.totalHeight;
		headerPushOffsets = headerPush.offsets;
		if (headerConverged || isHeaderCycle) break;
	}
	return {
		items: stickyHeaders,
		pushOffsets: headerPushOffsets
	};
}
function convergeStickyFooters(metrics, groupRanges, scrollTop, viewportHeight) {
	const { getRowHeight, getRowOffset, findLastVisible, totalRowCount } = metrics;
	let stickyFooterHeight = 0;
	let footerPushOffsets = [];
	let stickyFooters = [];
	const computeFooterPush = (ranges) => {
		const offsets = [];
		let cumHeight = 0;
		for (let ci = ranges.length - 1; ci >= 0; ci--) {
			const range = ranges[ci];
			const rowH = getRowHeight(range.footerIndex);
			let push = 0;
			const boundaryOffset = getRowOffset(range.headerIndex);
			if (boundaryOffset !== void 0) {
				const boundaryBottom = boundaryOffset + getRowHeight(range.headerIndex);
				const slotTop = scrollTop + viewportHeight - cumHeight - rowH;
				if (boundaryBottom > slotTop) push = boundaryBottom - slotTop;
			}
			offsets[ci] = push;
			cumHeight += Math.max(rowH - push, 0);
		}
		return {
			totalHeight: cumHeight,
			offsets
		};
	};
	const seenLastVisible = /* @__PURE__ */ new Set();
	while (true) {
		const effectiveLastVisible = Math.min(findLastVisible(scrollTop + viewportHeight - stickyFooterHeight), totalRowCount - 1);
		const isFooterCycle = seenLastVisible.has(effectiveLastVisible);
		seenLastVisible.add(effectiveLastVisible);
		const footerResult = computeStickyFooterItems(groupRanges, effectiveLastVisible);
		const footerPush = computeFooterPush(footerResult);
		const footerConverged = !stickyItemsChanged(footerResult, stickyFooters) && Math.abs(footerPush.totalHeight - stickyFooterHeight) < 1;
		stickyFooters = footerResult;
		stickyFooterHeight = footerPush.totalHeight;
		footerPushOffsets = footerPush.offsets;
		if (footerConverged || isFooterCycle) break;
	}
	return {
		items: stickyFooters,
		pushOffsets: footerPushOffsets
	};
}
function syncColgroup(targetTable, sourceTable) {
	if (!targetTable) return;
	const existing = targetTable.querySelector(":scope > colgroup");
	const source = sourceTable.querySelector(":scope > colgroup");
	if (source) {
		const sourceCols = source.children;
		if (existing && existing.children.length === sourceCols.length) {
			const targetCols = existing.children;
			for (let i = 0; i < sourceCols.length; i++) {
				const w = sourceCols[i].style.width;
				if (w) {
					if (targetCols[i].style.width !== w) targetCols[i].style.width = w;
				} else if (targetCols[i].style.width) targetCols[i].style.width = "";
			}
		} else {
			if (existing) existing.remove();
			targetTable.prepend(source.cloneNode(true));
		}
	} else if (existing) existing.remove();
	targetTable.style.width = sourceTable.style.width || sourceTable.getBoundingClientRect().width + "px";
}
function renderStickyRowsHtml(sourceRows, items, indexProp, skipOffset) {
	let html = "";
	for (let i = 0; i < items.length; i++) {
		const row = sourceRows[items[i][indexProp] - skipOffset];
		if (row) html += `<tr class='${row.className}'>${row.innerHTML}</tr>`;
	}
	return html;
}
const STICKY_CONTAINER_CLASS = "k-grid-sticky-container";
function createStickyTable(tableClass, sizeClass) {
	const table = document.createElement("table");
	table.className = tableClass + " k-grid-table k-table" + (sizeClass ? " " + sizeClass : "");
	const tbody = document.createElement("tbody");
	tbody.className = "k-table-tbody";
	table.appendChild(tbody);
	return table;
}
function createStickyGroupContainer(tableClass, sizeClass, extraClasses) {
	const div = document.createElement("div");
	div.className = STICKY_CONTAINER_CLASS + (extraClasses ? " " + extraClasses : "");
	div.setAttribute("aria-hidden", "true");
	const table = createStickyTable(tableClass, sizeClass);
	div.appendChild(table);
	return {
		container: div,
		table
	};
}
function createStickyGroupLockedContainer(tableClass, sizeClass) {
	const div = document.createElement("div");
	div.className = "k-grid-content-locked";
	const table = createStickyTable(tableClass, sizeClass);
	div.appendChild(table);
	return {
		container: div,
		table
	};
}
//#endregion
//#region ../src/grid/pinned-rows.js
const PINNED_CONTAINER_CLASS = "k-grid-pinned-container";
const PINNED_WRAP_CLASS = "k-grid-pinned-wrap";
const PINNED_SOURCE_CLASS = "k-pinned-source";
const kendo$1 = window.kendo;
const $ = kendo$1.jQuery;
function createPinnedContainer(position, sizeClass) {
	const isBottom = position === "bottom";
	const container = document.createElement("div");
	container.className = PINNED_CONTAINER_CLASS + (isBottom ? " k-pos-bottom" : "");
	const wrap = document.createElement("div");
	wrap.className = PINNED_WRAP_CLASS;
	const table = document.createElement("table");
	table.className = "k-grid-table k-table" + (sizeClass ? " " + sizeClass : "");
	table.setAttribute("aria-label", isBottom ? "Pinned bottom rows" : "Pinned top rows");
	const tbody = document.createElement("tbody");
	tbody.className = "k-table-tbody";
	table.appendChild(tbody);
	wrap.appendChild(table);
	container.appendChild(wrap);
	return {
		container,
		wrap,
		table,
		tbody
	};
}
function syncPinnedColgroups(sourceTable, topTable, bottomTable) {
	if (!sourceTable) return;
	const syncColgroupOnly = (target, src) => {
		const existing = target.querySelector(":scope > colgroup");
		if (existing) existing.remove();
		const srcColgroup = src.querySelector(":scope > colgroup");
		if (srcColgroup) target.prepend(srcColgroup.cloneNode(true));
	};
	if (topTable) syncColgroupOnly(topTable, sourceTable);
	if (bottomTable) syncColgroupOnly(bottomTable, sourceTable);
}
function syncPinnedScroll(source, content, topWrap, bottomWrap) {
	const scrollLeft = source.scrollLeft;
	if (content && content !== source && content.scrollLeft !== scrollLeft) content.scrollLeft = scrollLeft;
	if (topWrap && topWrap !== source && topWrap.scrollLeft !== scrollLeft) topWrap.scrollLeft = scrollLeft;
	if (bottomWrap && bottomWrap !== source && bottomWrap.scrollLeft !== scrollLeft) bottomWrap.scrollLeft = scrollLeft;
}
function getRowPinPosition(dataItem, topRows, bottomRows, idField) {
	if (!idField || !dataItem) return "none";
	const key = dataItem[idField];
	if (topRows.some((r) => r[idField] === key)) return "top";
	if (bottomRows.some((r) => r[idField] === key)) return "bottom";
	return "none";
}
function resolveInitialPinnedRows(pinnable, idField, dataSourceGet) {
	if (!pinnable || typeof pinnable !== "object" || !pinnable.top && !pinnable.bottom) return null;
	if (!idField) return null;
	const resolve = (ids) => {
		if (!Array.isArray(ids)) return [];
		return ids.map((id) => dataSourceGet(id)).filter((item) => !!item);
	};
	return {
		top: resolve(pinnable.top),
		bottom: resolve(pinnable.bottom)
	};
}
function markPinnedSourceRows(tbody, topRows, bottomRows, idField) {
	if (!tbody) return;
	const $tbody = $(tbody);
	$tbody.find("tr.k-pinned-source").removeClass(PINNED_SOURCE_CLASS);
	if (!idField) return;
	[...topRows, ...bottomRows].forEach((pinnedItem) => {
		const uid = pinnedItem.uid;
		if (uid) $tbody.find(`tr[data-uid="${uid}"]`).addClass(PINNED_SOURCE_CLASS);
	});
}
function togglePinMenuItems(menuElement, position, pinRowLocation, messages) {
	const $menu = $(menuElement);
	const pinTop = $menu.find("[data-command=PinTopCommand]").closest(".k-menu-item");
	const pinBottom = $menu.find("[data-command=PinBottomCommand]").closest(".k-menu-item");
	const unpin = $menu.find("[data-command=UnpinCommand]").closest(".k-menu-item");
	if (pinRowLocation && pinRowLocation !== "both") {
		const parentItem = pinTop.closest(".k-menu-group").closest(".k-menu-item");
		pinTop.hide();
		pinBottom.hide();
		unpin.hide();
		if (parentItem.length) {
			parentItem.children(".k-menu-group, .k-animation-container").hide();
			parentItem.find("> .k-link .k-menu-expand-arrow, > .k-link > .k-menu-expand-arrow-icon").hide();
			const linkText = parentItem.find("> .k-link .k-menu-link-text");
			if (position !== "none") {
				parentItem.show();
				parentItem.attr(kendo$1.attr("command"), "UnpinCommand");
				parentItem.data("command", "UnpinCommand");
				if (linkText.length && messages) linkText.text(messages.unpin);
			} else {
				parentItem.show();
				const cmd = pinRowLocation === "top" ? "PinTopCommand" : "PinBottomCommand";
				parentItem.attr(kendo$1.attr("command"), cmd);
				parentItem.data("command", cmd);
				if (linkText.length && messages) linkText.text(messages.pinRow);
			}
		}
	} else {
		pinTop.toggle(position !== "top");
		pinBottom.toggle(position !== "bottom");
		unpin.toggle(position !== "none");
	}
}
function destroyPinnedContainer(wrapEl, containerEl) {
	if (wrapEl) $(wrapEl).off("scroll");
	if (containerEl) $(containerEl).remove();
}
function refreshPinnedReferences(topRows, bottomRows, dataSourceData, idField) {
	if (!idField) return {
		top: topRows,
		bottom: bottomRows
	};
	const findById = (id) => {
		for (let i = 0; i < dataSourceData.length; i++) if (dataSourceData[i][idField] == id) return dataSourceData[i];
		return null;
	};
	const refresh = (items) => items.map((item) => {
		return findById(item[idField]) || null;
	}).filter((item) => item !== null);
	return {
		top: refresh(topRows),
		bottom: refresh(bottomRows)
	};
}
function syncPinnedTableWidths(sourceTable, topTable, bottomTable) {
	if (!sourceTable) return;
	const width = sourceTable.style.width;
	if (width) {
		if (topTable) topTable.style.width = width;
		if (bottomTable) bottomTable.style.width = width;
	}
}
function syncPinnedLockedWidths(lockedWidth, contentWidth, topLocked, bottomLocked, topContent, bottomContent) {
	if (topLocked) topLocked.style.width = lockedWidth;
	if (bottomLocked) bottomLocked.style.width = lockedWidth;
	if (topContent) topContent.style.width = contentWidth;
	if (bottomContent) bottomContent.style.width = contentWidth;
}
function createPinnedLockedContent(sizeClass) {
	const container = document.createElement("div");
	container.className = "k-grid-content-locked";
	const table = document.createElement("table");
	table.className = "k-grid-table k-table" + (sizeClass ? " " + sizeClass : "");
	const colgroup = document.createElement("colgroup");
	const tbody = document.createElement("tbody");
	tbody.className = "k-table-tbody";
	table.appendChild(colgroup);
	table.appendChild(tbody);
	container.appendChild(table);
	return {
		container,
		table,
		tbody
	};
}
function flattenRowSpans(html) {
	if (html.indexOf("rowspan") === -1 && html.indexOf("rowSpan") === -1 && html.indexOf("hidden") === -1) return html;
	const temp = document.createElement("table");
	const tempBody = document.createElement("tbody");
	temp.appendChild(tempBody);
	tempBody.innerHTML = html;
	const rows = tempBody.querySelectorAll("tr");
	const rowCount = rows.length;
	for (let r = 0; r < rowCount; r++) {
		const cells = rows[r].querySelectorAll("td");
		for (let c = 0; c < cells.length; c++) {
			const cell = cells[c];
			if (cell.hasAttribute("rowspan")) cell.removeAttribute("rowspan");
			if (cell.hasAttribute("hidden")) {
				cell.removeAttribute("hidden");
				cell.style.display = "";
			}
		}
	}
	return tempBody.innerHTML;
}
function applyPinnedAria(containers, getByUid, idField) {
	containers.forEach((p) => {
		if (!p || !p.tbody) return;
		p.tbody.attr("role", "rowgroup");
		p.tbody.children("tr").each((_, el) => {
			const row = $(el);
			row.attr("role", "row");
			const uid = row.data("uid");
			if (uid) {
				if (getByUid(uid) && idField) row.attr("aria-label", "Pinned row");
			}
			row.children("td").attr("role", "gridcell");
		});
		if (p.lockedTbody) {
			p.lockedTbody.attr("role", "rowgroup");
			p.lockedTbody.children("tr").each((_, el) => {
				const row = $(el);
				row.attr("role", "row");
				row.children("td").attr("role", "gridcell");
			});
		}
	});
}
//#endregion
//#region ../src/kendo.grid.js
const __meta__ = {
	id: "grid",
	name: "Grid",
	category: "web",
	description: "The Grid widget displays tabular data and offers rich support for interacting with data,including paging, sorting, grouping, and selection.",
	depends: [
		"data",
		"columnsorter",
		"sortable",
		"toolbar",
		"html.button",
		"icons",
		"menu",
		"loader",
		"html.loadercontainer",
		"badge",
		"aiprompt",
		"smartbox"
	],
	features: [
		{
			id: "grid-editing",
			name: "Editing",
			description: "Support for record editing",
			depends: [
				"editable",
				"window",
				"textbox",
				"form"
			]
		},
		{
			id: "grid-filtering",
			name: "Filtering",
			description: "Support for record filtering",
			depends: ["filtermenu"]
		},
		{
			id: "grid-columnmenu",
			name: "Column menu",
			description: "Support for header column menu",
			depends: ["columnmenu"]
		},
		{
			id: "grid-grouping",
			name: "Grouping",
			description: "Support for grid grouping",
			depends: ["groupable"]
		},
		{
			id: "grid-filtercell",
			name: "Row filter",
			description: "Support for grid header filtering",
			depends: ["filtercell"]
		},
		{
			id: "grid-paging",
			name: "Paging",
			description: "Support for grid paging",
			depends: ["pager"]
		},
		{
			id: "grid-selection",
			name: "Selection",
			description: "Support for row selection",
			depends: ["selectable"]
		},
		{
			id: "grid-column-reorder",
			name: "Column reordering",
			description: "Support for column reordering",
			depends: ["reorderable"]
		},
		{
			id: "grid-column-resize",
			name: "Column resizing",
			description: "Support for column resizing",
			depends: ["resizable"]
		},
		{
			id: "grid-mobile",
			name: "Grid adaptive rendering",
			description: "Support for adaptive rendering",
			depends: [
				"dialog",
				"pane",
				"switch"
			]
		},
		{
			id: "grid-excel-export",
			name: "Excel export",
			description: "Export grid data as Excel spreadsheet",
			depends: ["excel"]
		},
		{
			id: "grid-pdf-export",
			name: "PDF export",
			description: "Export grid data as PDF",
			depends: ["pdf", "drawing"]
		},
		{
			id: "grid-csv-export",
			name: "CSV export",
			description: "Export grid data as CSV",
			depends: ["csv"]
		}
	]
};
(function($, undefined) {
	let kendo = window.kendo, ui = kendo.ui, DataSource = kendo.data.DataSource, ObservableObject = kendo.data.ObservableObject, tbodySupportsInnerHtml = kendo.support.tbodyInnerHtml, activeElement = kendo._activeElement, Widget = ui.Widget, outerWidth = kendo._outerWidth, outerHeight = kendo._outerHeight, keys = kendo.keys, getType = kendo.type, isPlainObject = $.isPlainObject, extend = $.extend, map = $.map, grep = $.grep, isArray = Array.isArray, inArray = $.inArray, push = Array.prototype.push, isFunction = kendo.isFunction, encode = kendo.htmlEncode, isEmptyObject = $.isEmptyObject, contains = $.contains, math = Math, DOT = ".", PROGRESS = "progress", ERROR = "error", HIERARCHY_CELL_CLASS = "k-hierarchy-cell", DATA_CELL = ":not(.k-group-cell):not([" + kendo.attr("virtual") + "]):not(.k-hierarchy-cell:not(:has([ref-grid-expand-detail],[ref-grid-collapse-detail]))):visible", DATA_CELL_HIDDENINCLUDED = ":not([" + kendo.attr("virtual") + "]):not(.k-hierarchy-cell:not(:has([ref-grid-expand-detail],[ref-grid-collapse-detail])))", SELECTION_CELL_SELECTOR = "tbody>tr:not(.k-grouping-row):not(.k-detail-row):not(.k-group-footer):not([data-skeleton-row]) > td:not(.k-group-cell):not(.k-hierarchy-cell)", STACKED_CELL_SELECTOR = "tbody>tr:not(.k-grouping-row):not(.k-detail-row):not(.k-group-footer):not([data-skeleton-row]) > td:not(.k-group-cell):not(.k-hierarchy-cell) div.k-grid-stack-cell", NAVROW = "tr:not(.k-footer-template):visible", NAVCELL = ":not(.k-group-cell):not(.k-detail-cell):not(.k-hierarchy-cell):visible", ITEMROW = "tr:not(.k-grouping-row):not(.k-detail-row):not(.k-footer-template):not(.k-group-footer):visible", COLGROUP = "col:not(.k-group-col, .k-hierarchy-col)", HEADERCELLS = "th.k-header:not(.k-group-cell):not(.k-hierarchy-cell)", CARET_ALT_DOWN = "a[class*='-i-chevron-down']", CARET_ALT_RIGHT = "a[class*='-i-chevron-right']", CARET_ALT_RIGHT_CACHE = CARET_ALT_RIGHT, CARET_ALT_LEFT = "a[class*='-i-chevron-left']", WRAPPER = ".k-grid", STACKED = "k-grid-stack", NS = ".kendoGrid", INPUT_SELECTORS = ":button,a,:input,a>.k-icon,a>.k-svg-icon,textarea,span.k-select,span.k-icon,span.k-svg-icon,span.k-svg-icon>svg,span.k-svg-icon>svg>path,span.k-link,label.k-checkbox-label,.k-input,.k-multiselect-wrap,.k-picker-wrap,.k-picker-wrap>.k-selected-color,.k-tool-icon,.k-dropdownlist,.k-switch-thumb,.k-switch-track,.k-switch-label-off,.k-switch-label-on", EDIT = "edit", BEFOREEDIT = "beforeEdit", SAVE = "save", REMOVE = "remove", DETAILINIT = "detailInit", FILTERMENUINIT = "filterMenuInit", COLUMNMENUINIT = "columnMenuInit", FILTERMENUOPEN = "filterMenuOpen", COLUMNMENUOPEN = "columnMenuOpen", CELLCLOSE = "cellClose", CHANGING = "changing", CHANGE = "change", COLUMNHIDE = "columnHide", COLUMNSHOW = "columnShow", SAVECHANGES = "saveChanges", DATABOUND = "dataBound", DETAILEXPAND = "detailExpand", DETAILCOLLAPSE = "detailCollapse", ITEM_CHANGE = "itemchange", PAGE = "page", PAGING = "paging", PASTE = "paste", SCROLL = "scroll", SYNC = "sync", LOAD_START = "loadStart", LOAD_END = "loadEnd", REQUESTEND = "requestEnd", FOCUSED = "k-focus", HIGHLIGHTED = "k-highlighted", HOVER = "k-hover", ACTIVE = "k-active", FOCUSABLE = ":kendoFocusable", FOCUSABLE_GRID_ELEMENT_SELECTORS = ".k-command-cell,.k-select-checkbox,.k-grid-stack-cell[tabindex]", SELECTED = "k-selected", CHECKBOX = "k-checkbox", CHECKBOXINPUT = "input[data-role='checkbox'].k-select-checkbox.k-checkbox", NORECORDSCLASS = "k-grid-norecords", SORTED_CLASS = "k-sorted", HEADER_CLASS = "k-header", STICKY_CELL_CLASS = "k-grid-content-sticky", STICKY_HEADER_CLASS = "k-grid-header-sticky", STICKY_FOOTER_CLASS = "k-grid-footer-sticky", STICKY_HEADER_NO_BORDER_CLASS = "k-grid-no-left-border", STICKY_GROUP_HEADER_TABLE_CLASS = "k-grid-group-sticky-header-table", STICKY_GROUP_FOOTER_TABLE_CLASS = "k-grid-group-sticky-footer-table", STACKED_TEMPLATE_WRAPPER_CLASS = "k-grid-column-template", PINCELLCLASS = "k-pin-cell", GROUPING_ROW = "k-grouping-row", ROWRESIZE = "rowResize", COLUMNRESIZE = "columnResize", COLUMNREORDER = "columnReorder", COLUMNLOCK = "columnLock", COLUMNUNLOCK = "columnUnlock", COLUMNSTICK = "columnStick", COLUMNUNSTICK = "columnUnstick", ROWREORDER = "rowReorder", ROWPIN = "rowPin", ROWUNPIN = "rowUnpin", NAVIGATE = "navigate", HEIGHT = "height", WIDTH = "width", AUTO = "auto", TABINDEX = "tabIndex", FUNCTION = "function", STRING = "string", BOTTOM = "bottom", CONTAINER_FOR = "container-for", FIELD = "field", INPUT = "input", INCELL = "incell", INLINE = "inline", UNIQUE_ID = "uid", MINCOLSPANVALUE = 1, COLSPAN = "colSpan", OVERFLOW = "overflow", HIDDEN = "hidden", SORT = "sort", GROUP_SORT = "group-sort", DELETECONFIRM = "Are you sure you want to delete this record?", NORECORDS = "No records available.", CONFIRMDELETE = "Delete", CANCELDELETE = "Cancel", COLLAPSE = "Collapse", EXPAND = "Expand", ID = "id", PX = "px", TR = "tr", TH = "th", TD = "td", DIV = "div", ARIA_LABEL = "aria-label", ARIA_OWNS = "aria-owns", ARIA_ROWCOUNT = "aria-rowcount", ARIA_COLCOUNT = "aria-colcount", ARIA_CONTROLS = "aria-controls", ARIA_COLINDEX = "aria-colindex", ARIA_ROWINDEX = "aria-rowindex", ARIA_EXPANDED = "aria-expanded", ARIA_CHECKED = "aria-checked", ARIA_ACTIVEDESCENDANT = "aria-activedescendant", ROLE = "role", NONE = "none", ROW = "row", ROWGROUP = "rowgroup", COLUMNHEADER = "columnheader", GRIDCELL = "gridcell", formatRegExp = /(\}|\#)/gi, nonDataCellsRegExp = /* @__PURE__ */ new RegExp("(^|[\\x20\\t\\r\\n\\f])(k-group-cell|k-hierarchy-cell)([\\x20\\t\\r\\n\\f]|$)"), filterRowRegExp = /* @__PURE__ */ new RegExp("(^|[\\x20\\t\\r\\n\\f])(k-filter-row)([\\x20\\t\\r\\n\\f]|$)"), COMMANDBUTTONTMPL = ({ className, attr, text }) => `<button type="button" class="${className}" ${attr}>${kendo.htmlEncode(text)}</button>`, DEFAULTSELECTCOLUMNTMPL = (size, ariaLabel, label) => `<span class="k-checkbox-wrap"><input tabindex="-1" class="k-select-checkbox ${CHECKBOX} ${size}" data-role="checkbox" aria-label="${ariaLabel}" aria-checked="false" type="checkbox"></span>${label ? `<label class="k-checkbox-label">${label}</label>` : ""}`, SELECTCOLUMNTMPL = ({ size }) => DEFAULTSELECTCOLUMNTMPL(size, "Select row"), SELECTCOLUMNHEADERTMPL = ({ size, label }) => DEFAULTSELECTCOLUMNTMPL(size, "Select all rows", label), DRAGHANDLECOLUMNTMPL = () => kendo.ui.icon("reorder"), PINNABLECOLUMNTMPL = () => `<span class="${PINCELLCLASS}">${kendo.ui.icon("pin")}</span>`, DEFAULTHEADERTEMPLATE = ({ text }) => `<span class="k-cell-inner"><span class="k-link"><span class="k-column-title">${text}</span></span></span>`, isRtl = false, browser = kendo.support.browser;
	var isIE11 = browser.msie && browser.version === 11;
	var isMac = /Mac OS/.test(navigator.userAgent);
	var classNames = {
		content: "k-content",
		scrollContainer: "k-scroll-container",
		headerCellInner: "k-cell-inner"
	};
	var GroupsPager;
	const defaultBodyContextMenu = [
		"copySelection",
		"copySelectionNoHeaders",
		"paste",
		"separator",
		"create",
		"edit",
		"destroy",
		"select",
		"separator",
		"reorderRow",
		"pinRow",
		"exportPDF",
		"exportExcel",
		"exportCSV",
		"separator"
	];
	const defaultHeadContextMenu = [
		"sortAsc",
		"sortDesc",
		"separator"
	];
	const defaultGroupsContextMenu = [
		"moveGroupPrevious",
		"moveGroupNext",
		"separator"
	];
	const editableToolbarItemsSelector = [
		".k-grid-edit-command",
		".k-grid-remove-command",
		".k-grid-save-changes",
		".k-grid-cancel-changes",
		".k-grid-cancel-command",
		".k-grid-save-command"
	].join(", ");
	const defaultActionSheetFooterButtons = function(messages) {
		return {
			sort: [{
				command: "clear-sort",
				text: messages.clearButtons ? messages.clearButtons.clearSorting : "Clear Sorting",
				size: "large",
				icon: "x"
			}, {
				command: "done",
				text: messages.applyButtons ? messages.applyButtons.applySorting : "Done",
				size: "large",
				themeColor: "primary",
				icon: "check"
			}],
			group: [{
				command: "clear-group",
				text: messages.clearButtons ? messages.clearButtons.clearGrouping : "Clear Grouping",
				size: "large",
				icon: "x"
			}, {
				command: "done",
				text: messages.applyButtons ? messages.applyButtons.applyGrouping : "Done",
				size: "large",
				themeColor: "primary",
				icon: "check"
			}],
			filter: [{
				command: "clear-filter",
				text: messages.clearButtons ? messages.clearButtons.clearFiltering : "Clear All Filters",
				size: "large",
				icon: "filter-clear"
			}],
			"column-chooser": [{
				text: messages.clearButtons ? messages.clearButtons.columnChooserReset : "Reset",
				icon: "arrow-rotate-ccw"
			}, {
				text: messages.applyButtons ? messages.applyButtons.columnChooserApply : "Apply",
				themeColor: "primary",
				icon: "check"
			}]
		};
	};
	if (ui.Pager) GroupsPager = ui.Pager.extend({
		init: function(element, options) {
			ui.Pager.fn.init.call(this, element, extend(true, {}, { messages: ui.Pager.prototype.options.messages }, options));
			this.dataSource.options.useRanges = true;
			this.dataSource._omitPrefetch = true;
			ui.Pager.fn.endInit.call(this);
		},
		options: { name: "GroupsPager" },
		totalPages: function() {
			var that = this;
			return Math.ceil((that._collapsedTotal() || 0) / (that.pageSize() || 1));
		},
		_collapsedTotal: function() {
			var dataSource = this.dataSource;
			return dataSource ? dataSource.groupsTotal(true) || 0 : 0;
		}
	});
	var VirtualScrollable = Widget.extend({
		init: function(element, options) {
			var that = this;
			Widget.fn.init.call(that, element, options);
			that._refreshHandler = that.refresh.bind(that);
			that.setDataSource(options.dataSource);
			that.wrap();
			Widget.fn.endInit.call(that);
		},
		setDataSource: function(dataSource) {
			var that = this;
			if (that.dataSource) that.dataSource.unbind(CHANGE, that._refreshHandler);
			that.dataSource = dataSource;
			that.dataSource.bind(CHANGE, that._refreshHandler);
			that.dataSource.options.useRanges = true;
			that.dataSource.options.virtual = true;
		},
		options: {
			name: "VirtualScrollable",
			itemHeight: $.noop,
			prefetch: true,
			maxScrollHeight: 25e4
		},
		events: [
			PAGING,
			PAGE,
			SCROLL,
			LOAD_START,
			LOAD_END
		],
		destroy: function() {
			var that = this;
			Widget.fn.destroy.call(that);
			that.dataSource.unbind(CHANGE, that._refreshHandler);
			that.wrapper.add(that.verticalScrollbar).off(NS);
			clearTimeout(that._timeout);
			if (that._scrollingTimeout) clearTimeout(that._scrollingTimeout);
			if (that.drag) {
				that.drag.destroy();
				that.drag = null;
			}
			that.wrapper = that.element = that.verticalScrollbar = null;
			that._refreshHandler = null;
		},
		wrap: function() {
			var that = this, scrollbar = kendo.support.scrollbar() + 1, element = that.element, wrapper;
			element.css({
				width: AUTO,
				overflow: "hidden"
			}).css(isRtl ? "padding-left" : "padding-right", scrollbar);
			that.content = element.children().first();
			wrapper = that.wrapper = that.content.wrap("<div class=\"k-virtual-scrollable-wrap\"/>").parent().on("DOMMouseScroll.kendoGrid mousewheel.kendoGrid", that._wheelScroll.bind(that));
			that._wrapper();
			if (kendo.support.kineticScrollNeeded || kendo.support.touch) {
				that.wrapper.css("touch-action", NONE);
				that.drag = new kendo.UserEvents(that.wrapper, {
					global: true,
					allowSelection: true,
					start: function(e) {
						e.sender.capture();
					},
					move: function(e) {
						that.verticalScrollbar.scrollTop(that.verticalScrollbar.scrollTop() - e.y.delta);
						kendo.scrollLeft(wrapper, kendo.scrollLeft(wrapper) - e.x.delta);
						e.preventDefault();
					}
				});
			}
			that.verticalScrollbar = $("<div class=\"k-scrollbar k-scrollbar-vertical\" tabindex=\"-1\"/>").css({ width: scrollbar }).appendTo(element).on("scroll.kendoGrid", that._scroll.bind(that));
		},
		_wrapper: function() {
			var that = this;
			if (isIE11) {
				that.wrapper.css({ "overflow-y": SCROLL });
				that.element.css(isRtl ? "padding-left" : "padding-right", 0);
			}
		},
		_wheelScroll: function(e) {
			if (e.ctrlKey) return;
			var scrollbar = this.verticalScrollbar, scrollTop = scrollbar.scrollTop(), delta = kendo.wheelDeltaY(e);
			if (delta && !(delta > 0 && scrollTop === 0) && !(delta < 0 && scrollTop + scrollbar[0].clientHeight == scrollbar[0].scrollHeight)) {
				e.preventDefault();
				this.verticalScrollbar.scrollTop(scrollTop + -delta);
			}
		},
		_scroll: function(e) {
			var that = this, delayLoading = !that.options.prefetch, scrollTop = e.currentTarget.scrollTop, dataSource = that.dataSource, rowHeight = that.itemHeight, skip = dataSource.skip() || 0, start = that._rangeStart || skip, height = that.element.innerHeight(), isScrollingUp = !!(that._scrollbarTop && that._scrollbarTop > scrollTop), firstItemIndex = math.max(math.floor(scrollTop / rowHeight), 0), lastItemOffset = isScrollingUp ? math.ceil(height / rowHeight) : math.floor(height / rowHeight), lastItemIndex = math.max(firstItemIndex + lastItemOffset, 0);
			if (that._preventScroll) {
				that._preventScroll = false;
				return;
			}
			that._prevScrollTop = that._scrollTop;
			that._scrollTop = scrollTop - start * rowHeight;
			that._scrollbarTop = scrollTop;
			that._scrolling = delayLoading;
			if (!that._fetch(firstItemIndex, lastItemIndex, isScrollingUp)) that.wrapper[0].scrollTop = that._scrollTop;
			that.trigger(SCROLL);
			if (delayLoading) {
				if (that._scrollingTimeout) clearTimeout(that._scrollingTimeout);
				that._scrollingTimeout = setTimeout(function() {
					that._scrolling = false;
					that._page(that._rangeStart, that.dataSource.take());
				}, 100);
			}
		},
		scrollToTop: function() {
			this._scrollTo(0);
		},
		scrollToBottom: function() {
			var scrollbar = this.verticalScrollbar;
			this._scrollTo(scrollbar[0].scrollHeight - scrollbar.height());
		},
		_scrollWrapperToTop: function() {
			this.wrapper.scrollTop(0);
		},
		_scrollWrapperToBottom: function() {
			this.wrapper.scrollTop(this.wrapper[0].scrollHeight);
		},
		_scrollWrapperOnColumnResize: function() {
			var that = this;
			var wrapper = this.wrapper;
			var initialScrollTop = wrapper.scrollTop();
			if (wrapper[0].scrollWidth > wrapper[0].clientWidth) {
				if (!that._wrapperScrolled && initialScrollTop || that._isScrolledToBottom()) {
					wrapper.scrollTop(initialScrollTop + kendo.support.scrollbar());
					that._scrollTop = wrapper.scrollTop();
					that._wrapperScrolled = true;
				}
			} else if (that._wrapperScrolled) {
				if (!that._isWrapperScrolledToBottom()) {
					wrapper.scrollTop(initialScrollTop - kendo.support.scrollbar());
					that._scrollTop = wrapper.scrollTop();
				}
				that._wrapperScrolled = false;
			}
		},
		_scrollTo: function(scrollTop, programmaticScrollPosition) {
			var that = this;
			var scrollbar = that.verticalScrollbar;
			if (scrollbar.scrollTop() !== scrollTop) that._preventScroll = true;
			that.wrapper.scrollTop(scrollTop);
			that._scrollTop = that.wrapper.scrollTop();
			scrollbar.scrollTop(programmaticScrollPosition ?? scrollTop);
			that._scrollbarTop = scrollbar.scrollTop();
		},
		_isScrolledToTop: function() {
			return this.verticalScrollbar.scrollTop() === 0;
		},
		_isScrolledToBottom: function() {
			var scrollbar = this.verticalScrollbar;
			var scrollTop = scrollbar.scrollTop();
			return scrollTop > 0 && scrollTop >= parseInt(scrollbar[0].scrollHeight - scrollbar.height(), 10);
		},
		_isWrapperScrolledToBottom: function() {
			var wrapper = this.wrapper;
			return wrapper.scrollTop() >= parseInt(wrapper[0].scrollHeight - wrapper.height(), 10);
		},
		itemIndex: function(rowIndex) {
			return (this._rangeStart || this.dataSource.skip() || 0) + rowIndex;
		},
		position: function(index) {
			var rangeStart = this._rangeStart || this.dataSource.skip() || 0;
			var pageSize = this.dataSource.pageSize();
			var result;
			if (index > rangeStart) result = index - rangeStart;
			else result = rangeStart - index - 1;
			return result > pageSize ? pageSize : result;
		},
		scrollIntoView: function(row) {
			var container = this.wrapper[0];
			var containerHeight = container.clientHeight;
			var containerScroll = !this._isScrolledToBottom() ? this._scrollTop || container.scrollTop : container.scrollTop;
			var elementOffset = row[0].offsetTop;
			var elementHeight = row[0].offsetHeight;
			if (containerScroll > elementOffset) this.verticalScrollbar[0].scrollTop -= containerHeight / 2;
			else if (elementOffset + elementHeight >= containerScroll + containerHeight) this.verticalScrollbar[0].scrollTop += containerHeight / 2;
		},
		_fetch: function(firstItemIndex, lastItemIndex, scrollingUp) {
			var that = this, dataSource = that.dataSource, itemHeight = that.itemHeight, take = dataSource.take(), rangeStart = that._rangeStart || dataSource.skip() || 0, currentSkip = math.floor(firstItemIndex / take) * take, fetching = false, prefetchAt = .33;
			var scrollbar = that.verticalScrollbar;
			var webkitCorrection = browser.webkit ? 1 : 0;
			var total = dataSource._isGroupPaged() ? dataSource.groupsTotal(true) : dataSource.total();
			if (firstItemIndex < rangeStart) {
				fetching = true;
				if (that._alwaysScrollTop) {
					rangeStart = math.min(firstItemIndex, total - take);
					that._scrollTop = 0;
				} else {
					rangeStart = math.max(0, lastItemIndex - take);
					that._scrollTop = scrollbar.scrollTop() - rangeStart * itemHeight;
				}
				that._page(rangeStart, take);
			} else if (lastItemIndex >= rangeStart + take && !scrollingUp) {
				fetching = true;
				rangeStart = math.min(firstItemIndex, total - take);
				if (scrollbar.scrollTop() >= scrollbar[0].scrollHeight - scrollbar[0].offsetHeight - webkitCorrection) that._scrollTop = that.wrapper[0].scrollHeight - that.wrapper[0].offsetHeight;
				else if (that.dataSource._isGroupPaged() && firstItemIndex >= total - take) that._scrollTop = that.wrapper[0].scrollHeight - that.wrapper[0].offsetHeight - (that._scrollTop - that._prevScrollTop);
				else if (that._alwaysScrollTop) that._scrollTop = 0;
				else that._scrollTop = itemHeight;
				that._page(rangeStart, take);
			} else if (!that._fetching && that.options.prefetch) {
				if (firstItemIndex < currentSkip + take - take * prefetchAt && firstItemIndex > take) dataSource.prefetch(currentSkip - take, take, $.noop);
				if (lastItemIndex > currentSkip + take * prefetchAt) dataSource.prefetch(currentSkip + take, take, $.noop);
			}
			return fetching;
		},
		fetching: function() {
			return this._fetching;
		},
		_page: function(skip, take, callback) {
			var that = this, delayLoading = !that.options.prefetch, dataSource = that.dataSource, isGroupPaged = dataSource._isGroupPaged();
			callback = isFunction(callback) ? callback : $.noop;
			if (that.trigger(PAGING, {
				skip,
				take
			})) return;
			clearTimeout(that._timeout);
			that._fetching = true;
			that._rangeStart = skip;
			if (isGroupPaged && dataSource._groupRangeExists(skip, skip + take) || !isGroupPaged && dataSource.inRange(skip, take)) {
				that.trigger(LOAD_START);
				dataSource.range(skip, take, function() {
					that.trigger(LOAD_END);
					callback();
					that.trigger(PAGE);
				}, "page");
			} else {
				if (!delayLoading) that.trigger(LOAD_START);
				that._timeout = setTimeout(function() {
					if (!that._scrolling) {
						if (delayLoading) that.trigger(LOAD_START);
						dataSource.range(skip, take, function() {
							that.trigger(LOAD_END);
							callback();
							that.trigger(PAGE);
						});
					}
				}, 100);
			}
		},
		repaintScrollbar: function(shouldScrollWrapper) {
			var that = this, maxHeight = that.options.maxScrollHeight, dataSource = that.dataSource, scrollbar = !kendo.support.kineticScrollNeeded ? kendo.support.scrollbar() : 0, wrapperElement = that.wrapper[0], totalHeight, itemHeight;
			var wasScrolledToBottom = that._isScrolledToBottom();
			itemHeight = that.itemHeight = that.options.itemHeight() || 0;
			var addScrollBarHeight = wrapperElement.scrollWidth > wrapperElement.offsetWidth ? scrollbar : 0;
			totalHeight = (dataSource._isGroupPaged() ? dataSource.groupsTotal(true) : dataSource.total()) * itemHeight + addScrollBarHeight;
			var divElements = $(new Array(math.floor(totalHeight / maxHeight) + 1).join("<div></div>")).css({
				width: "1px",
				height: `${maxHeight}px`
			});
			if (totalHeight % maxHeight) divElements = divElements.add($("<div></div>").css({
				width: "1px",
				height: `${totalHeight % maxHeight}px`
			}));
			that.verticalScrollbar.empty().append(divElements);
			if (wasScrolledToBottom && !that._isScrolledToBottom() && !that.dataSource._isGroupPaged()) that.scrollToBottom();
			if (typeof that._scrollTop !== "undefined" && !!shouldScrollWrapper) {
				wrapperElement.scrollTop = that._scrollTop;
				that._scrollWrapperOnColumnResize();
			}
		},
		refresh: function(e) {
			var that = this, dataSource = that.dataSource, rangeStart = that._rangeStart;
			var action = (e || {}).action;
			var shouldScrollWrapper = that._isScrolledToBottom() || !action || action !== ITEM_CHANGE && action !== REMOVE && action !== SYNC;
			that.trigger(LOAD_END);
			clearTimeout(that._timeout);
			that.repaintScrollbar(shouldScrollWrapper);
			if (that.drag) that.drag.cancel();
			if (typeof rangeStart !== "undefined" && !that._fetching) {
				if (!action || action !== SYNC && action !== ITEM_CHANGE && action !== "expandGroup") that._rangeStart = dataSource.skip();
				if (dataSource.page() === 1 && (!action || action !== SYNC && action !== ITEM_CHANGE && action !== "expandGroup" && action !== "collapseGroup")) that.verticalScrollbar[0].scrollTop = 0;
			}
			if (that._programmaticallyScrolling && that._programmaticallyScrolling.state() !== "resolved") that._programmaticallyScrolling.resolve();
			if (that._alwaysScrollTop) delete that._alwaysScrollTop;
			that._fetching = false;
		}
	});
	function flattenFilterDescriptors(filters) {
		if (!filters || !Array.isArray(filters.filters)) return [];
		const flattened = [];
		function processFilter(filter) {
			if (filter.logic && filter.filters) filter.filters.forEach(processFilter);
			else if (filter.field && filter.operator) flattened.push({ ...filter });
		}
		filters.filters.forEach(processFilter);
		return flattened;
	}
	function flattenGroupDescriptors(groups) {
		if (!groups || !Array.isArray(groups)) return [];
		const flattened = [];
		function processGroup(group) {
			flattened.push({ ...group });
			if (group.items && Array.isArray(group.items)) group.items.forEach((item) => {
				if (item.items) processGroup(item);
			});
		}
		groups.forEach(processGroup);
		return flattened;
	}
	function flattenSortDescriptors(sorts) {
		if (!sorts || !Array.isArray(sorts)) return [];
		return sorts;
	}
	function hasInvalidDescriptor(descriptors, key) {
		let flatFunction = {
			sort: flattenSortDescriptors,
			filter: flattenFilterDescriptors,
			group: flattenGroupDescriptors
		}[key];
		if (!flatFunction) return false;
		return flatFunction(descriptors).some((item) => !item.field);
	}
	function attrEquals(attrName, attrValue) {
		return "[" + kendo.attr(attrName) + "=" + attrValue + "]";
	}
	function groupCells(count) {
		return new Array(count + 1).join("<td class=\"k-group-cell k-table-group-td k-table-td\">&nbsp;</td>");
	}
	function cellsExcludingSpecialColumns(cells) {
		return cells.filter((i, cell) => {
			const $cell = $(cell);
			const hasCheckbox = $cell.children(".k-select-checkbox").length > 0;
			const hasWrappedCheckbox = $cell.find("> .k-checkbox-wrap > .k-select-checkbox").length > 0;
			return $cell.attr("[ref-grid-drag-cell]") === undefined && !$cell.hasClass("k-command-cell") && !hasCheckbox && !hasWrappedCheckbox;
		});
	}
	function stringifyAttributes(attributes) {
		var attr, result = " ";
		if (attributes) {
			if (typeof attributes === STRING) return attributes;
			for (attr in attributes) if (attributes[attr] !== "") result += attr + "=\"" + attributes[attr] + "\"";
		}
		return result;
	}
	function parseDate(value, fields) {
		if (!fields) return value;
		return value.map((descriptor) => ({
			...descriptor,
			logic: (descriptor?.logic || descriptor?.logicalOperator || "and").toLowerCase(),
			filters: descriptor?.filters?.map((filter) => {
				const field = filter.field;
				if (fields && fields[field]) {
					if (fields[field].type.toLowerCase() === "date") return {
						...filter,
						value: new Date(filter.value)
					};
				}
				return filter;
			})
		})) || [];
	}
	function syncFooterColsWidthsWithHeader(header, footer) {
		const headerCols = header.parent().find(">colgroup>col:not(.k-group-col):not(.k-hierarchy-col)");
		const footerCols = footer.find(">.k-grid-footer-wrap>table>colgroup>col:not(.k-group-col):not(.k-hierarchy-col)");
		if (headerCols.length && footerCols.length === headerCols.length) headerCols.each(function(i) {
			const width = this.style.width;
			if (width) footerCols.eq(i).css("width", width);
		});
		const headerGroupCols = header.parent().find(">colgroup>.k-group-col");
		const footerGroupCols = footer.find(">.k-grid-footer-wrap>table>colgroup>.k-group-col");
		if (headerGroupCols.length && footerGroupCols.length === headerGroupCols.length) headerGroupCols.each(function(i) {
			const width = this.style.width;
			if (width) footerGroupCols[i].style.width = width;
		});
	}
	const defaultCommands = {
		aiassistant: {
			text: "",
			icon: "sparkles",
			rounded: "full",
			className: "k-grid-ai-assistant-tool",
			themeColor: "primary"
		},
		create: {
			text: "Add",
			className: "k-grid-add",
			iconClass: "k-i-plus"
		},
		cancel: {
			text: "Cancel changes",
			className: "k-grid-cancel-changes",
			iconClass: "k-i-cancel"
		},
		save: {
			text: "Save changes",
			className: "k-grid-save-changes",
			iconClass: "k-i-check"
		},
		selectall: { text: "Select all" },
		destroy: {
			text: "Delete",
			className: "k-grid-remove-command",
			iconClass: "k-i-trash"
		},
		edit: {
			text: "Edit",
			className: "k-grid-edit-command",
			iconClass: "k-i-pencil"
		},
		update: {
			text: "Save",
			className: "k-grid-save-command",
			iconClass: "k-i-save"
		},
		canceledit: {
			text: "Cancel",
			className: "k-grid-cancel-command",
			iconClass: "k-i-cancel"
		},
		excel: {
			text: "Export to Excel",
			className: "k-grid-excel",
			iconClass: "k-i-file-excel"
		},
		pdf: {
			text: "Export to PDF",
			className: "k-grid-pdf",
			iconClass: "k-i-file-pdf"
		},
		csv: {
			text: "Export to CSV",
			className: "k-grid-csv",
			iconClass: "k-i-file-csv"
		},
		search: {
			text: "Search...",
			className: "k-grid-search"
		},
		columns: {
			text: "Columns",
			type: "button",
			icon: "columns",
			fillMode: "flat",
			overflow: "never",
			className: "k-grid-column-menu",
			attr: { "aria-haspopup": "menu" }
		},
		columnchooser: {
			text: "Columns",
			type: "button",
			icon: "columns",
			overflow: "never",
			className: "k-grid-column-chooser",
			attr: { "aria-haspopup": "menu" }
		},
		sort: {
			text: "Sort",
			type: "button",
			icon: "arrows-swap",
			overflow: "never",
			className: "k-grid-sort-tool",
			attr: { "aria-haspopup": "menu" },
			clearButton: true
		},
		filter: {
			text: "Filter",
			type: "button",
			icon: "filter",
			overflow: "never",
			className: "k-grid-filter-tool",
			attr: { "aria-haspopup": "menu" },
			clearButton: true
		},
		group: {
			text: "Group",
			type: "button",
			icon: "group",
			overflow: "never",
			className: "k-grid-group-tool",
			attr: { "aria-haspopup": "menu" },
			clearButton: true
		},
		smartbox: {
			overflow: "never",
			template: () => `<input ref-grid-smartbox-input data-role="smartbox" />`
		}
	};
	const supportedAIDataSourceCommands = {
		"GridSort": "sort",
		"GridClearSort": "sort",
		"GridFilter": "filter",
		"GridClearFilter": "filter",
		"GridGroup": "group",
		"GridClearGroup": "group",
		"GridPage": "page",
		"GridPageSize": "pageSize"
	};
	const supportedAIGridCommands = {
		"GridHighlight": "highlight",
		"GridClearHighlight": "highlight",
		"GridSelect": "select",
		"GridClearSelect": "select",
		"GridColumnResize": "resizeColumn",
		"GridColumnHide": "hideColumn",
		"GridColumnShow": "showColumn",
		"GridColumnLock": "lockColumn",
		"GridColumnUnlock": "unlockColumn",
		"GridColumnReorder": "reorderColumn",
		"GridExportPDF": "_exportPdf",
		"GridExportExcel": "_exportExcel",
		"GridExportCSV": "_exportCsv"
	};
	const placeholderId = kendo.guid();
	function resolveCommandText(command, messages) {
		if (!command) return "";
		const defaultText = defaultCommands[command.name?.toLowerCase()]?.text;
		const messageText = messages[command.name?.toLowerCase()];
		const hasCustomText = typeof command.text === STRING && command.text.trim() !== defaultText;
		const hasMessageText = messageText && messageText !== defaultText;
		if (isPlainObject(command.text)) return command.text;
		let resolvedText = defaultText || command.text || "";
		if (hasMessageText) resolvedText = messageText;
		if (hasCustomText) resolvedText = command.text;
		return resolvedText;
	}
	function promptPlaceholderOptions(messages) {
		return {
			id: placeholderId,
			prompt: "",
			output: messages?.outputPlaceholder,
			skipHeader: true,
			skipActions: true
		};
	}
	function cursor(context, value) {
		$("th, th .k-grid-filter-menu, th .k-link", context).add(document.body).css("cursor", value);
	}
	function reorder(selector, source, dest, before, count) {
		var sourceIndex = source;
		source = $();
		count = count || 1;
		for (var idx = 0; idx < count; idx++) source = source.add(selector.eq(sourceIndex + idx));
		if (typeof dest == "number") source[before ? "insertBefore" : "insertAfter"](selector.eq(dest));
		else source.appendTo(dest);
	}
	function elements(lockedContent, content, filter) {
		return $(lockedContent).add(content).find(filter);
	}
	function attachCustomCommandEvent(context, container, commands) {
		var idx, length, command, commandName;
		commands = !isArray(commands) ? [commands] : commands;
		for (idx = 0, length = commands.length; idx < length; idx++) {
			command = commands[idx];
			if (isPlainObject(command) && command.click) {
				commandName = command.name || command.text;
				container.on("click.kendoGrid", ".k-grid-" + (commandName || "").replace(/\s/g, ""), { commandName }, command.click.bind(context));
			}
		}
	}
	function normalizeColumns(columns, encoded, hide, locked, parentIds) {
		return map(columns, function(column) {
			column = typeof column === STRING ? { field: column } : column;
			var hidden;
			column.parentIds = parentIds;
			if (column.attributes instanceof Function) column._attributesFunction = column.attributes;
			if (!isVisible(column) || hide) {
				column.attributes = addHiddenStyle(column.attributes);
				column.footerAttributes = addHiddenStyle(column.footerAttributes);
				column.headerAttributes = addHiddenStyle(column.headerAttributes);
				hidden = true;
			} else if (isVisible(column) || !hide) {
				column.attributes = removeHiddenStyle(column.attributes);
				column.footerAttributes = removeHiddenStyle(column.footerAttributes);
				column.headerAttributes = removeHiddenStyle(column.headerAttributes);
				hidden = undefined;
			}
			var uid = kendo.guid();
			if (locked && !column.locked) column.locked = locked;
			column.headerAttributes = extend({ headers: parentIds }, column.headerAttributes);
			if (!column.headerAttributes.id) {
				column.headerAttributes = extend({ id: uid }, column.headerAttributes);
				column.uid = uid;
			} else column.uid = uid = column.headerAttributes.id;
			if (column.columns) column.columns = normalizeColumns(column.columns, encoded, hidden, column.locked, parentIds ? parentIds + " " + uid : uid);
			return extend({
				encoded,
				hidden,
				locked
			}, column);
		});
	}
	function columnParent(column, columns) {
		var parents = [];
		columnParents(column, columns, parents);
		return parents[parents.length - 1];
	}
	function columnParents(column, columns, parents) {
		parents = parents || [];
		for (var idx = 0; idx < columns.length; idx++) if (column === columns[idx]) return true;
		else if (columns[idx].columns) {
			var inserted = parents.length;
			parents.push(columns[idx]);
			if (!columnParents(column, columns[idx].columns, parents)) parents.splice(inserted, parents.length - inserted);
			else return true;
		}
		return false;
	}
	function setColumnVisibility(column, visible) {
		setVisibility(column, visible, visible);
	}
	function addElementsToTab(elements) {
		elements.attr(TABINDEX, 1);
	}
	function removeElementsFromTab(elements) {
		elements.removeAttr(TABINDEX);
	}
	function setVisibility(column, visible, show) {
		var method = show ? removeHiddenStyle : addHiddenStyle;
		column.hidden = !visible;
		column.attributes = method(column.attributes);
		column.footerAttributes = method(column.footerAttributes);
		column.headerAttributes = method(column.headerAttributes);
	}
	function setColumnMediaVisibility(column, visible) {
		setColumnMatchesMedia(column);
		var hideByMedia = column._hideByMedia;
		setVisibility(column, visible, hideByMedia ? column.matchesMedia : visible);
	}
	function setColumnMatchesMedia(column) {
		column.matchesMedia = columnMatchesMedia(column);
	}
	function columnMatchesMedia(column) {
		return column && (isUndefined(column.media) || !isUndefined(column.media) && kendo.matchesMedia(column.media));
	}
	function isCellVisible() {
		return this.style.display !== NONE && !this.classList.contains("k-hidden");
	}
	function isElementVisible(element) {
		return $(element)[0].style.display !== NONE && !$(element)[0].classList.contains("k-hidden");
	}
	function isVisible(column) {
		return visibleColumns([column]).length > 0;
	}
	function visibleColumns(columns) {
		return grep(columns, function(column) {
			var result = !column.hidden && column.matchesMedia !== false;
			if (result && column.columns) result = visibleColumns(column.columns).length > 0;
			return result;
		});
	}
	function columnsWithMedia(columns) {
		var result = [];
		var column;
		for (var i = 0; i < columns.length; i++) {
			column = columns[i];
			if (!isUndefined(column.media)) {
				if (!isUndefined(column.minScreenWidth)) throw new Error("Using 'media' and 'minScreenWidth' options at the same time is not supported.");
				result.push(column);
			}
			if (column.columns) result = result.concat(columnsWithMedia(column.columns));
		}
		return result;
	}
	function isUndefined(value) {
		return typeof value === "undefined";
	}
	function toJQuery(elements) {
		return $(elements).map(function() {
			return this.toArray();
		});
	}
	function updateCellRowSpan(cell, columns, sourceLockedColumnsCount) {
		var lockedColumnDepth = depth(lockedColumns(columns));
		var nonLockedColumnDepth = depth(nonLockedColumns(columns));
		var rowSpan = cell.rowSpan;
		if (sourceLockedColumnsCount) if (lockedColumnDepth > nonLockedColumnDepth) cell.rowSpan = rowSpan - (lockedColumnDepth - nonLockedColumnDepth) || 1;
		else cell.rowSpan = rowSpan + (nonLockedColumnDepth - lockedColumnDepth);
		else if (lockedColumnDepth > nonLockedColumnDepth) cell.rowSpan = rowSpan + (lockedColumnDepth - nonLockedColumnDepth);
		else cell.rowSpan = rowSpan - (nonLockedColumnDepth - lockedColumnDepth) || 1;
	}
	function findColumnByField(columns, field) {
		for (var i = 0; i < columns.length; i++) if (columns[i].field == field) return columns[i];
	}
	function getMergedAIDescriptors(previousDescriptors, newDescriptors, key) {
		let shouldContinue;
		newDescriptors.forEach((newDescriptor) => {
			shouldContinue = false;
			let index;
			const matchingPreviousDescriptor = previousDescriptors.find((prevDescriptor, i) => {
				if (prevDescriptor.field === newDescriptor.field) {
					index = i;
					return true;
				}
			});
			if (matchingPreviousDescriptor && key === "group") {
				shouldContinue = true;
				return;
			}
			if (matchingPreviousDescriptor) previousDescriptors.splice(index, 1);
			previousDescriptors.push(newDescriptor);
		});
		return {
			descriptors: previousDescriptors,
			shouldContinue
		};
	}
	function getEnabledAICommands(options, dataSource, checkBoxSelection) {
		const sortable = options?.sortable || options?.columnMenu?.sortable;
		const filterable = options?.filterable || options?.columnMenu?.filterable;
		const selectable = options?.selectable || checkBoxSelection;
		const groupable = options?.groupable?.enabled || options?.groupable || dataSource._groupPaging;
		return {
			"GridSort": sortable,
			"GridClearSort": sortable,
			"GridFilter": filterable,
			"GridClearFilter": filterable,
			"GridGroup": groupable,
			"GridClearGroup": groupable,
			"GridSelect": selectable,
			"GridClearSelect": selectable,
			"GridColumnResize": options?.resizable || options?.resizable?.columns,
			"GridColumnReorder": options?.reorderable || options?.reorderable?.columns,
			"GridPage": options?.scrollable?.virtual && options?.scrollable?.virtual === "columns" && options?.pageable || !options?.scrollable?.endless && options?.pageable || options?.pageable,
			"GridPageSize": true,
			"GridColumnHide": true,
			"GridColumnShow": true,
			"GridColumnLock": true,
			"GridColumnUnlock": true
		};
	}
	function getToolbarRegex({ mode, hasSelected, hasChanges, editContainerVisible, differentSelectionThanEditing, _editableIsClosing, _isEditableEnabled, options }) {
		const editableCommands = /\b(k-grid-edit-command|k-grid-remove-command|k-grid-save-changes|k-grid-cancel-changes|k-grid-cancel-command|k-grid-save-command)\b/;
		const commands = [];
		if (!_isEditableEnabled || !options.editable) return editableCommands;
		if (!hasSelected) {
			commands.push("k-grid-edit-command");
			commands.push("k-grid-remove-command");
		}
		if (hasSelected && (mode === "incell" || mode === "inline" && !differentSelectionThanEditing)) commands.push("k-grid-edit-command");
		if (!hasChanges) {
			commands.push("k-grid-save-changes");
			commands.push("k-grid-cancel-changes");
		}
		if (mode !== "inline" || !editContainerVisible || editContainerVisible && _editableIsClosing) {
			commands.push("k-grid-save-command");
			commands.push("k-grid-cancel-command");
		}
		let regex = "";
		if (commands.length) {
			regex = commands.join("|");
			regex = `\\b(${regex})\\b`;
			return new RegExp(regex);
		}
		return regex;
	}
	function moveCellsBetweenContainers(sources, target, leafs, columns, container, destination, groups, action) {
		var sourcesDepth = depth(sources);
		var targetDepth = depth([target]);
		if (sourcesDepth > targetDepth) {
			var groupCells = new Array(groups + 1).join("<th class=\"k-group-cell k-header k-table-th\" scope=\"col\">&nbsp;</th>");
			var rows = destination.children(":not(.k-filter-row)");
			$(new Array(sourcesDepth - targetDepth + 1).join("<tr class='k-table-row'>" + groupCells + "</tr>")).insertAfter(rows.last());
		}
		addRowSpanValue(destination, sourcesDepth - targetDepth);
		moveCells(leafs, columns, container, destination, action);
	}
	function updateCellIndex(thead, columns, offset) {
		offset = offset || 0;
		var position;
		var cell;
		var allColumns = columns;
		columns = leafColumns(columns);
		var cells = {};
		var rows = thead.find(">tr:not(.k-filter-row)");
		var filter = function() {
			var el = $(this);
			return !el.hasClass("k-group-cell") && !el.hasClass("k-hierarchy-cell");
		};
		for (var idx = 0, length = columns.length; idx < length; idx++) {
			position = columnPosition(columns[idx], allColumns);
			if (!cells[position.row]) cells[position.row] = rows.eq(position.row).find(".k-header").filter(filter);
			cell = cells[position.row].eq(position.cell);
			cell.attr(kendo.attr("index"), offset + idx);
		}
		return columns.length;
	}
	function depth(columns) {
		var result = 1;
		var max = 0;
		for (var idx = 0; idx < columns.length; idx++) if (columns[idx].columns) {
			var temp = depth(columns[idx].columns);
			if (temp > max) max = temp;
		}
		return result + max;
	}
	function moveCells(leafs, columns, container, destination, action) {
		var sourcePosition = columnVisiblePosition(leafs[0], columns);
		var ths = container.find(">tr:not(.k-filter-row)").eq(sourcePosition.row).children("th.k-header:not(.k-group-cell)");
		var t = $();
		var sourceIndex = sourcePosition.cell;
		var idx;
		for (idx = 0; idx < leafs.length; idx++) t = t.add(ths.eq(sourceIndex + idx));
		destination.find(">tr:not(.k-filter-row)").eq(sourcePosition.row)[action](t);
		var children = [];
		for (idx = 0; idx < leafs.length; idx++) if (leafs[idx].columns) children = children.concat(leafs[idx].columns);
		if (children.length) moveCells(children, columns, container, destination, action);
	}
	function columnPosition(column, columns, row, cellCounts) {
		var result;
		var idx;
		row = row || 0;
		cellCounts = cellCounts || {};
		cellCounts[row] = cellCounts[row] || 0;
		for (idx = 0; idx < columns.length; idx++) {
			if (columns[idx] == column) {
				result = {
					cell: cellCounts[row],
					row
				};
				break;
			} else if (columns[idx].columns) {
				result = columnPosition(column, columns[idx].columns, row + 1, cellCounts);
				if (result) break;
			}
			cellCounts[row]++;
		}
		return result;
	}
	function findParentColumnWithChildren(columns, index, source, rtl) {
		var target;
		var locked = !!source.locked;
		var targetLocked;
		do {
			target = columns[index];
			index += rtl ? 1 : -1;
			targetLocked = !!target.locked;
		} while (target && index > -1 && index < columns.length && target != source && !target.columns && targetLocked === locked);
		return target;
	}
	function decorateCellWithClass(html, skipTdClass) {
		let element = html;
		let classes = element.match(/class=["][^"]+/g);
		if (classes) {
			const cssClasses = classes[0].split("\"").pop();
			element = element.replace(cssClasses, cssClasses + (skipTdClass ? "" : " k-table-td "));
		} else element = element.replace("<td", "<td class='k-table-td'");
		return element;
	}
	function findReorderTarget(columns, target, source, before, masterColumns) {
		if (target.columns) {
			target = target.columns;
			return target[before ? 0 : target.length - 1];
		} else {
			var parent = columnParent(target, columns);
			var parentColumns;
			if (parent) parentColumns = parent.columns;
			else parentColumns = columns;
			var index = inArray(target, parentColumns);
			if (index === 0 && before) index++;
			else if (index == parentColumns.length - 1 && !before || !source.locked && !target.columns && !before) index--;
			else if (index > 0 || index === 0 && !before) index++;
			var sourceIndex = inArray(source, parentColumns);
			target = findParentColumnWithChildren(parentColumns, index, source, sourceIndex > index);
			var targetIndex = inArray(target, masterColumns);
			if (target.columns && (!targetIndex || targetIndex === parentColumns.length - 1)) return null;
			if (target && target != source && target.columns) return findReorderTarget(columns, target, source, before, masterColumns);
		}
		return null;
	}
	function columnVisiblePosition(column, columns, row, cellCounts) {
		var result;
		var idx;
		row = row || 0;
		cellCounts = cellCounts || {};
		cellCounts[row] = cellCounts[row] || 0;
		for (idx = 0; idx < columns.length; idx++) {
			if (columns[idx] == column) {
				result = {
					cell: cellCounts[row],
					row
				};
				break;
			} else if (columns[idx].columns) {
				result = columnVisiblePosition(column, columns[idx].columns, row + 1, cellCounts);
				if (result) break;
			}
			if (!columns[idx].hidden) cellCounts[row]++;
		}
		return result;
	}
	function flatColumnsInDomOrder(columns) {
		return flatColumns(lockedColumns(columns)).concat(flatColumns(nonLockedColumns(columns)));
	}
	function targetParentContainerIndex(flatColumns, columns, sourceIndex, targetIndex) {
		var column = flatColumns[sourceIndex];
		var target = flatColumns[targetIndex];
		var parent = columnParent(column, columns);
		columns = parent ? parent.columns : columns;
		return inArray(target, columns);
	}
	function flatColumns(columns) {
		var result = [];
		var children = [];
		for (var idx = 0; idx < columns.length; idx++) {
			result.push(columns[idx]);
			if (columns[idx].columns) children = children.concat(columns[idx].columns);
		}
		if (children.length) result = result.concat(flatColumns(children));
		return result;
	}
	function hiddenLeafColumnsCount(columns) {
		var counter = 0;
		var column;
		for (var idx = 0; idx < columns.length; idx++) {
			column = columns[idx];
			if (column.columns) counter += hiddenLeafColumnsCount(column.columns);
			else if (column.hidden) counter++;
		}
		return counter;
	}
	function sumWidths(cols) {
		var width = 0;
		for (var idx = 0, length = cols.length; idx < length; idx++) if (!cols[idx].hidden) width += parseInt(cols[idx].width, 10);
		return width;
	}
	function columnsWidth(cols) {
		var colWidth, width = 0;
		for (var idx = 0, length = cols.length; idx < length; idx++) {
			colWidth = cols[idx].style.width;
			if (colWidth && colWidth.indexOf("%") == -1) width += parseInt(colWidth, 10);
		}
		return width;
	}
	function removeRowSpanValue(container, count) {
		var cells = container.find("tr:not(.k-filter-row) th:not(.k-group-cell,.k-hierarchy-cell)");
		var rowSpan;
		for (var idx = 0; idx < cells.length; idx++) {
			rowSpan = cells[idx].rowSpan;
			if (rowSpan > 1) cells[idx].rowSpan = rowSpan - count || 1;
		}
	}
	function addRowSpanValue(container, count) {
		var cells = container.find("tr:not(.k-filter-row) th:not(.k-group-cell,.k-hierarchy-cell)");
		for (var idx = 0; idx < cells.length; idx++) cells[idx].rowSpan += count;
	}
	function removeEmptyRows(container) {
		var rows = container.find("tr:not(.k-filter-row)");
		var emptyRowsCount = rows.filter(function() {
			return !$(this).children().length;
		}).remove().length;
		var cells = rows.find("th:not(.k-group-cell,.k-hierarchy-cell)");
		for (var idx = 0; idx < cells.length; idx++) if (cells[idx].rowSpan > 1) cells[idx].rowSpan -= emptyRowsCount;
		return rows.length - emptyRowsCount;
	}
	function mapColumnToCellRows(columns, cells, rows, rowIndex, offset) {
		var idx, row, length, children = [];
		for (idx = 0, length = columns.length; idx < length; idx++) {
			row = rows[rowIndex] || [];
			row.push(cells.eq(offset + idx));
			rows[rowIndex] = row;
			if (columns[idx].columns) children = children.concat(columns[idx].columns);
		}
		if (children.length) mapColumnToCellRows(children, cells, rows, rowIndex + 1, offset + columns.length);
	}
	function setLeftAndRightStyles(element, left, right) {
		element.css({
			"left": left,
			"right": right
		});
	}
	function createColumnAttribute(column, attribute, property) {
		column[attribute] = column[attribute] || {};
		column[attribute][property] = column[attribute][property] || "";
	}
	function addColumnAttribute(column, attribute, property, value) {
		createColumnAttribute(column, attribute, property);
		if (column[attribute][property] !== "") column[attribute][property] += " " + value;
		else column[attribute][property] = value;
	}
	function removeColumnAttribute(column, attribute, property, value, removeAttributeProperty) {
		createColumnAttribute(column, attribute, property);
		if (removeAttributeProperty) delete column[attribute][property];
		else column[attribute][property] = column[attribute][property].replace(value, "");
	}
	function lockedColumns(columns) {
		return grep(columns, function(column) {
			return column.locked;
		});
	}
	function nonLockedColumns(columns) {
		return grep(columns, function(column) {
			return !column.locked;
		});
	}
	function stickyColumns(columns) {
		return grep(columns, function(column) {
			return column.sticky && !column.locked;
		});
	}
	function visibleStickyColumns(columns) {
		return grep(columns, function(column) {
			return column.sticky && !column.locked && isVisible(column);
		});
	}
	function visibleNonLockedColumns(columns) {
		return grep(columns, function(column) {
			return !column.locked && isVisible(column);
		});
	}
	function visibleLockedColumns(columns) {
		return grep(columns, function(column) {
			return column.locked && isVisible(column);
		});
	}
	function visibleLeafColumns(columns) {
		var result = [];
		for (var idx = 0; idx < columns.length; idx++) {
			if (columns[idx].hidden) continue;
			if (columns[idx].columns) result = result.concat(visibleLeafColumns(columns[idx].columns));
			else result.push(columns[idx]);
		}
		return result;
	}
	function visibleLeafExportColumns(columns) {
		var result = [];
		for (var idx = 0; idx < columns.length; idx++) {
			if (columns[idx].hidden) continue;
			if (columns[idx].columns) result = result.concat(visibleLeafColumns(columns[idx].columns));
			else result.push({
				field: columns[idx].field,
				width: columns[idx].width,
				values: columns[idx].values,
				title: columns[idx].title
			});
		}
		return result;
	}
	function childColumns(columns) {
		var result = [];
		for (var idx = 0; idx < columns.length; idx++) if (columns[idx].columns) result = result.concat(columns[idx].columns);
		return result;
	}
	function visibleChildColumns(columns) {
		var result = childColumns(columns);
		result = result.filter(function(e) {
			return !e.hidden;
		});
		return result;
	}
	function leafColumns(columns) {
		var result = [];
		for (var idx = 0; idx < columns.length; idx++) {
			if (!columns[idx].columns) {
				result.push(columns[idx]);
				continue;
			}
			result = result.concat(leafColumns(columns[idx].columns));
		}
		return result;
	}
	function getColumnsFields(columns) {
		var result = [];
		columns = leafColumns(columns);
		for (var idx = 0; idx < columns.length; idx++) if (typeof columns[idx] === "string") result.push(columns[idx]);
		else if (columns[idx].field) result.push(columns[idx].field);
		return result;
	}
	function getAllColumns(columns) {
		var result = [];
		for (var idx = 0; idx < columns.length; idx++) {
			result.push(columns[idx]);
			if (columns[idx].columns) result = result.concat(getAllColumns(columns[idx].columns));
		}
		return result;
	}
	function createMultiHeaderTitle(gridInstance, col) {
		const separator = col.multiHeaderSeparator || " / ";
		const columns = getAllColumns(gridInstance.columns);
		const parentTitles = col.parentIds.split(" ").map((id) => {
			let parent = columns.find((c) => c.uid === id);
			return parent ? parent.title || parent.field || "" : "";
		});
		parentTitles.push(col.title || col.field);
		return parentTitles.join(separator);
	}
	function editField(column, adaptive) {
		return {
			field: column.field,
			title: column.title,
			format: column.format,
			editor: column.editor,
			values: column.values,
			editorOptions: extend(true, {
				format: column.format,
				adaptiveMode: adaptive
			}, column.editorOptions),
			label: column.title || column.field || ""
		};
	}
	function leafDataCells(container) {
		var rows = container.find(">tr:not(.k-filter-row)");
		var filter = function() {
			var el = $(this);
			return !el.hasClass("k-group-cell") && !el.hasClass("k-hierarchy-cell");
		};
		var cells = $();
		if (rows.length > 1) cells = rows.find("th").filter(filter).filter(function() {
			return this.rowSpan > 1;
		});
		cells = cells.add(rows.last().find("th").filter(filter));
		var indexAttr = kendo.attr("index");
		return [].sort.call(cells, function(a, b) {
			a = $(a);
			b = $(b);
			var indexA = a.attr(indexAttr);
			var indexB = b.attr(indexAttr);
			if (indexA === undefined) indexA = $(a).index();
			if (indexB === undefined) indexB = $(b).index();
			indexA = parseInt(indexA, 10);
			indexB = parseInt(indexB, 10);
			return indexA > indexB ? 1 : indexA < indexB ? -1 : 0;
		});
	}
	function parentColumnsCells(cell) {
		var container = cell.closest("table");
		var result = $().add(cell);
		var row = cell.closest(TR);
		var headerRows = container.find("tr:not(.k-filter-row)");
		var level = headerRows.index(row);
		if (level > 0) {
			var parentCellsWithChildren = headerRows.eq(level - 1).find("th:not(.k-group-cell,.k-hierarchy-cell)").filter(function() {
				return !$(this).attr("rowspan");
			});
			var offset = 0;
			var index = row.find("th:not(.k-group-cell,.k-hierarchy-cell)").index(cell);
			var prevCells = cell.prevAll(":not(.k-group-cell,.k-hierarchy-cell)").filter(function() {
				return this.colSpan > 1;
			});
			for (var idx = 0; idx < prevCells.length; idx++) offset += prevCells[idx].colSpan || 1;
			index += Math.max(offset - 1, 0);
			offset = 0;
			for (idx = 0; idx < parentCellsWithChildren.length; idx++) {
				var parentCell = parentCellsWithChildren.eq(idx);
				if (parentCell.attr("data-colspan")) offset += parentCell[0].getAttribute("data-colspan");
				else offset += 1;
				if (index >= idx && index < offset) {
					result = parentColumnsCells(parentCell).add(result);
					break;
				}
			}
		}
		return result;
	}
	function childColumnsCells(cell) {
		var container = cell.closest("thead");
		var result = $().add(cell);
		var row = cell.closest(TR);
		var headerRows = container.find("tr:not(.k-filter-row)");
		var level = headerRows.index(row) + cell[0].rowSpan;
		var colSpanAttr = kendo.attr("colspan");
		if (level <= headerRows.length - 1) {
			var child = row.next();
			var prevCells = cell.prevAll(":not(.k-group-cell,.k-hierarchy-cell)");
			var idx;
			prevCells = prevCells.filter(function() {
				return !this.rowSpan || this.rowSpan === 1;
			});
			var offset = 0;
			for (idx = 0; idx < prevCells.length; idx++) offset += parseInt(prevCells.eq(idx).attr(colSpanAttr), 10) || 1;
			var cells = child.find("th:not(.k-group-cell,.k-hierarchy-cell)");
			var colSpan = parseInt(cell.attr(colSpanAttr), 10) || 1;
			idx = 0;
			while (idx < colSpan) {
				child = cells.eq(idx + offset);
				result = result.add(childColumnsCells(child));
				var value = parseInt(child.attr(colSpanAttr), 10);
				if (value > 1) colSpan -= value - 1;
				idx++;
			}
		}
		return result;
	}
	function appendContent(tbody, table, html, size) {
		var placeholder, tmp = tbody;
		if (tbodySupportsInnerHtml) {
			let $html = $(html);
			kendo.applyStylesFromKendoAttributes($html, [
				"display",
				"left",
				"right"
			]);
			tbody.empty();
			$html.each((_, el) => tbody[0].appendChild(el));
		} else {
			placeholder = document.createElement(DIV);
			placeholder.innerHTML = "<table class='k-grid-table k-table'><tbody class='k-table-tbody'>" + html + "</tbody></table>";
			$(placeholder).find("table").addClass(kendo.getValidCssClass("k-table-", "size", size));
			tbody = placeholder.firstChild.firstChild;
			table[0].replaceChild(tbody, tmp[0]);
			tbody = $(tbody);
		}
		return tbody;
	}
	function addHiddenStyle(attr) {
		attr = attr || {};
		let kendoStyleAttrObject = {};
		kendoStyleAttrObject[kendo.attr("style-display")] = "none";
		return extend({}, attr, kendoStyleAttrObject);
	}
	function hasHiddenStyle(attr) {
		attr = attr || {};
		return !!attr[kendo.attr("style-display")];
	}
	function removeHiddenStyle(attr) {
		attr = attr || {};
		delete attr[kendo.attr("style-display")];
		return attr;
	}
	function normalizeCols(table, visibleColumns, hasDetails, groups, stacked) {
		var colgroup = table.find(">colgroup"), width, groupColWidths = [], cols = stacked ? ["<col>"] : map(visibleColumns, function(column) {
			width = column.width;
			if (width && parseInt(width, 10) !== 0) return kendo.format(`<col ${kendo.attr("style-width")}="{0}" ${column.draggable ? "class=k-drag-col" : ""} />`, typeof width === STRING ? width : width + PX);
			if (column.draggable) return "<col class='k-drag-col' />";
			return "<col />";
		});
		if (!stacked && (hasDetails || colgroup.find(".k-hierarchy-col").length)) cols.splice(0, 0, "<col class=\"k-hierarchy-col\" />");
		colgroup.find(".k-group-col").each(function(i) {
			if (this.style.width) groupColWidths[i] = this.style.width;
		});
		if (colgroup.length) colgroup.remove();
		colgroup = $(new Array(groups + 1).join("<col class=\"k-group-col\">") + cols.join(""));
		kendo.applyStylesFromKendoAttributes(colgroup, ["width"]);
		if (!colgroup.is("colgroup")) colgroup = $("<colgroup/>").append(colgroup);
		table.prepend(colgroup);
		if (groupColWidths.length) table.find(">colgroup>.k-group-col").each(function(i) {
			if (groupColWidths[i]) this.style.width = groupColWidths[i];
		});
	}
	function normalizeHeaderCells(container, columns) {
		var lastIndex = 0;
		var idx, len;
		var th = container.find("th:not(.k-group-cell)");
		for (idx = 0, len = columns.length; idx < len; idx++) if (columns[idx].locked) {
			th.eq(idx).insertBefore(th.eq(lastIndex));
			th = container.find("th:not(.k-group-cell)");
			lastIndex++;
		}
	}
	function convertToObject(array) {
		var result = {}, item, idx, length;
		for (idx = 0, length = array.length; idx < length; idx++) {
			item = array[idx];
			result[item.value] = item.text;
		}
		return result;
	}
	function formatGroupValue(value, format, columnValues, encoded) {
		let groupValue = columnValues && columnValues.length && isPlainObject(columnValues[0]) && "value" in columnValues[0] ? convertToObject(columnValues)[value] : value;
		groupValue = groupValue != null ? groupValue : "";
		let usedValue = encoded === false ? groupValue : kendo.htmlEncode(groupValue);
		return format ? kendo.format(format, usedValue) : usedValue;
	}
	function setCellVisibility(cells, index, visible) {
		var pad = 0, state, cell = cells[pad];
		while (cell) {
			state = visible ? true : cell.style.display !== NONE;
			if (visible && cell.classList.contains("k-hidden")) cell.classList.remove("k-hidden");
			if (state && !nonDataCellsRegExp.test(cell.className) && --index < 0) {
				cell.style.display = visible ? "" : NONE;
				break;
			}
			cell = cells[++pad];
		}
	}
	function hideColumnCells(rows, columnIndex) {
		var idx = 0, length = rows.length, cell, row;
		for (; idx < length; idx += 1) {
			row = rows.eq(idx);
			if (row.is(".k-grouping-row,.k-detail-row")) {
				cell = row.children(":not(.k-group-cell):first,.k-detail-cell").last();
				cell.attr("colspan", parseInt(cell.attr("colspan"), 10) - 1);
			} else {
				if (row.hasClass("k-grid-edit-row") && (cell = row.children(".k-edit-container")[0])) {
					cell = $(cell);
					cell.attr("colspan", parseInt(cell.attr("colspan"), 10) - 1);
					cell.find("col").eq(columnIndex).remove();
					row = cell.find(TR).first();
				}
				setCellVisibility(row[0].cells, columnIndex, false);
			}
		}
	}
	function groupRows(data) {
		var result = [];
		var item;
		for (var idx = 0; idx < data.length; idx++) {
			item = data[idx];
			if (!("field" in item && "value" in item && "items" in item)) break;
			result.push(item);
			if (item.hasSubgroups) result = result.concat(groupRows(item.items));
		}
		return result;
	}
	function groupFooters(data) {
		var result = [];
		var item;
		for (var idx = 0; idx < data.length; idx++) {
			item = data[idx];
			if (!("field" in item && "value" in item && "items" in item)) break;
			if (item.hasSubgroups) result = result.concat(groupFooters(item.items));
			result.push(item.aggregates);
		}
		return result;
	}
	function showColumnCells(rows, columnIndex) {
		var idx = 0, length = rows.length, cell, row, columns;
		for (; idx < length; idx += 1) {
			row = rows.eq(idx);
			if (row.is(".k-grouping-row,.k-detail-row")) {
				cell = row.children(":not(.k-group-cell):first,.k-detail-cell").last();
				cell.attr("colspan", parseInt(cell.attr("colspan"), 10) + 1);
			} else {
				if (row.hasClass("k-grid-edit-row") && (cell = row.children(".k-edit-container")[0])) {
					cell = $(cell);
					cell.attr("colspan", parseInt(cell.attr("colspan"), 10) + 1);
					normalizeCols(cell.find(">form>table"), visibleColumns(columns), false, 0);
					row = cell.find(TR).first();
				}
				setCellVisibility(row[0].cells, columnIndex, true);
			}
		}
	}
	function updateColspan(toAdd, toRemove, num) {
		num = num || 1;
		var item, idx, length;
		for (idx = 0, length = toAdd.length; idx < length; idx++) {
			item = toAdd.eq(idx).children(":not([hidden])").last();
			item.attr("colspan", parseInt(item.attr("colspan"), 10) + num);
			item = toRemove.eq(idx).children(":not([hidden])").last();
			item.attr("colspan", parseInt(item.attr("colspan"), 10) - num);
		}
	}
	function tableWidth(table) {
		var idx, length, width = 0;
		var cols = table.find(">colgroup>col");
		for (idx = 0, length = cols.length; idx < length; idx += 1) width += parseInt(cols[idx].style.width, 10);
		return width;
	}
	function getDefaultAIRequestConfig(prompt, columns) {
		return {
			"role": "user",
			"contents": [{
				"$type": "text",
				"text": prompt
			}],
			"columns": columns.map((col) => {
				let colType;
				if (col.selectable) colType = "checkbox";
				else if (col.command) colType = "command";
				else if (col.draggable) colType = "draggable";
				const colConfig = {
					...col,
					"id": `${col.uid}`
				};
				if (colType) colConfig.type = colType;
				return colConfig;
			})
		};
	}
	function aiPromptDefaultOutputGetter(response, messages, isRowSelection) {
		const commands = response?.commands || [];
		const messagesSeparator = "• ";
		const isError = response?.status;
		const outputMessages = commands.map((cmd, i) => {
			const selectionProtectedMessage = messagesSeparator + messages.invalidSelection;
			if (cmd?.type === "GridSelect") {
				const hasCells = Object.keys(cmd?.select.cells).length;
				if (hasCells && isRowSelection && cmd?.select?.filters.length) {
					cmd.skipCommand = true;
					return selectionProtectedMessage;
				}
				if (!hasCells && !isRowSelection && cmd?.select?.filters.length) {
					cmd.skipCommand = true;
					return selectionProtectedMessage;
				}
			}
			return cmd?.message ? messagesSeparator + cmd?.message : "";
		});
		let output = [];
		if (isError) output.push(messages.error + ` ${response.status} ${response.statusText}`);
		else if (response && response.message) output.push(response.message);
		else {
			output.push(messages.success);
			if (outputMessages) output.push(...outputMessages);
		}
		return output.join("/n");
	}
	function prepareAICommands(commands, multiple) {
		const mergedCommands = [];
		let lastUniqueCommand;
		if (!commands) return;
		for (let i = 0; i < commands.length; i++) {
			const command = commands[i];
			if (!command || !command.type) continue;
			const key = supportedAIDataSourceCommands[command.type];
			if (key === "filter" || key === "sort" && !multiple) {
				mergedCommands.push(command);
				continue;
			}
			if (key) {
				if (lastUniqueCommand && lastUniqueCommand.type !== command.type) {
					mergedCommands.push(lastUniqueCommand);
					lastUniqueCommand = null;
				}
				if (!lastUniqueCommand) lastUniqueCommand = command;
				else if (command[key] !== undefined) {
					if (!Array.isArray(lastUniqueCommand[key])) lastUniqueCommand[key] = [lastUniqueCommand[key]];
					lastUniqueCommand[key].push(command[key]);
					lastUniqueCommand = {
						...lastUniqueCommand,
						[key]: lastUniqueCommand[key]
					};
				}
			} else {
				if (lastUniqueCommand) {
					mergedCommands.push(lastUniqueCommand);
					lastUniqueCommand = null;
				}
				mergedCommands.push(command);
			}
		}
		if (lastUniqueCommand) mergedCommands.push(lastUniqueCommand);
		return mergedCommands;
	}
	function handleAIResponseOutput(output, ai, skeletonID) {
		if (!ai._requestInProgress || !ai.options.service) return;
		const outputObjects = ai.outputObjects;
		ai.updatePromptOutputContent(output.output, skeletonID);
		ai.element.find("[data-id='" + skeletonID + "']").attr("data-id", output.outputId);
		const value = outputObjects.get(skeletonID);
		outputObjects.delete(skeletonID);
		if (value) {
			value.id = output.outputId;
			value.data.id = output.outputId;
			outputObjects.set(output.outputId, value);
		}
		ai.promptOutputs = ai.promptOutputs.map((item) => {
			if (item.id === skeletonID) item.id = output.outputId;
			return item;
		});
		ai.stopStreaming();
		ai._requestInProgress = false;
	}
	function processAIOutputMessage(message) {
		const startIndex = message.indexOf("Column '");
		const valueStart = startIndex + 8;
		const endIndex = message.indexOf("'", valueStart);
		return {
			uid: message.substring(valueStart, endIndex),
			originalText: message.substring(startIndex, endIndex + 1)
		};
	}
	const aiPromptOutputTemplate = (message, messages, columns) => {
		const messageData = processAIOutputMessage(message);
		const col = columns.find((c) => c.uid === messageData?.uid);
		if (col) {
			const originalText = messageData?.originalText;
			const replacementText = `Column '${col.title || col.field}'`;
			message = message.replace(originalText, replacementText);
		}
		if (message.includes(messages.error)) return `<p class="k-text-error">${kendo.htmlEncode(message)}</p>`;
		else return `<div class="k-card-text">${kendo.htmlEncode(message)}</div>`;
	};
	var Grid = kendo.ui.DataBoundWidget.extend({
		init: function(element, options, events) {
			var that = this;
			options = isArray(options) ? { dataSource: options } : options;
			Widget.fn.init.call(that, element, options);
			if (events) that._events = events;
			isRtl = kendo.support.isRtl(element);
			CARET_ALT_RIGHT = isRtl ? CARET_ALT_LEFT : CARET_ALT_RIGHT_CACHE;
			that._element();
			that._ariaId();
			that._columns($.extend(true, [], that.options.columns));
			that._bindMediaQueries();
			if (that._foreignKeyPromises) $.when.apply(null, that._foreignKeyPromises).then(function() {
				that._foreignKeyPromises = null;
				that._continueInit();
			});
			else that._continueInit();
		},
		_continueInit: function() {
			var that = this;
			that._dataSource();
			that._stickyColumns();
			that._tbody();
			that._thead();
			that._rowResizing();
			that._groupable();
			that._toolbar();
			that._initToolbarItemsPopups();
			that._ai();
			that._pageable();
			if (!that._isPinnable()) that._setContentHeight();
			that._scrollableHeightApplied = true;
			that._templates();
			that._navigatable();
			that._initSelectableAggregates();
			that._selectable();
			that._initPinnedRowSelection();
			that._initPinnedRowEditing();
			that._statusBar();
			that._clipboard();
			that._paste();
			that._details();
			that._editable();
			that._attachCustomCommandsEvent();
			that._adaptiveColumns();
			that._minScreenSupport();
			if (that.options.autoBind) that.dataSource.fetch();
			else {
				that._group = that._groups() > 0;
				that._footer();
			}
			that._setInitialRtlScrollPosition();
			if (that.options.contextMenu) that._initContextMenu();
			if (that.lockedContent) {
				that.wrapper.addClass("k-grid-lockedcolumns");
				that._resizeHandler = function() {
					that.resize();
				};
				$(window).on("resize.kendoGrid", that._resizeHandler);
			}
			that._initLoader();
			kendo.notify(that);
			if (that._showWatermarkOverlay) that._showWatermarkOverlay(that.wrapper[0]);
		},
		events: [
			CHANGE,
			CHANGING,
			"dataBinding",
			"cancel",
			DATABOUND,
			DETAILEXPAND,
			DETAILCOLLAPSE,
			DETAILINIT,
			FILTERMENUINIT,
			FILTERMENUOPEN,
			COLUMNMENUINIT,
			COLUMNMENUOPEN,
			EDIT,
			BEFOREEDIT,
			SAVE,
			REMOVE,
			SAVECHANGES,
			CELLCLOSE,
			ROWRESIZE,
			COLUMNRESIZE,
			COLUMNREORDER,
			COLUMNSHOW,
			COLUMNHIDE,
			COLUMNLOCK,
			COLUMNUNLOCK,
			COLUMNSTICK,
			COLUMNUNSTICK,
			ROWREORDER,
			ROWPIN,
			ROWUNPIN,
			NAVIGATE,
			PASTE,
			"page",
			"sort",
			"filter",
			"group",
			"groupExpand",
			"groupCollapse",
			"kendoKeydown"
		],
		setDataSource: function(dataSource) {
			var that = this;
			var scrollable = that.options.scrollable;
			var scrollableContent;
			that.options.dataSource = dataSource;
			that._dataSource();
			that._pageable();
			that._thead();
			that._rowResizing();
			if (scrollable) if (scrollable.virtual) {
				scrollableContent = that.content.find(">.k-virtual-scrollable-wrap");
				kendo.scrollLeft(scrollableContent, leftMostPosition(scrollableContent, isRtl));
			} else {
				scrollableContent = that.tbody;
				kendo.scrollLeft(that.content, leftMostPosition(scrollableContent, isRtl));
			}
			if (that.options.groupable) that._groupable();
			if (that.virtualScrollable) that.virtualScrollable.setDataSource(that.options.dataSource);
			if (that.options.navigatable) that._navigatable();
			if (that.options.selectable) {
				that._selectable();
				that._initPinnedRowSelection();
			}
			that._initPinnedRowEditing();
			if (that.options.autoBind) that.dataSource.fetch();
			else that._footer();
		},
		options: {
			name: "Grid",
			adaptiveMode: "none",
			ai: null,
			columns: [],
			toolbar: null,
			autoBind: true,
			filterable: false,
			scrollable: true,
			sortable: false,
			selectable: false,
			allowCopy: false,
			allowPaste: false,
			navigatable: false,
			pageable: false,
			persistSelection: false,
			editable: false,
			encodeTitles: false,
			groupable: false,
			rowTemplate: "",
			altRowTemplate: "",
			pinnedRowTemplate: null,
			statusBarTemplate: null,
			search: false,
			noRecords: false,
			dataSource: {},
			height: null,
			resizable: false,
			reorderable: false,
			columnMenu: false,
			detailTemplate: null,
			contextMenu: false,
			pinnable: false,
			columnResizeHandleWidth: 3,
			size: undefined,
			mobile: "",
			loaderType: "loadingPanel",
			dataLayoutMode: "columns",
			stackedLayoutSettings: {},
			messages: {
				loader: {
					loading: "Loading...",
					exporting: "Exporting..."
				},
				editable: {
					cancelDelete: CANCELDELETE,
					confirmation: DELETECONFIRM,
					confirmDelete: CONFIRMDELETE
				},
				commands: {
					create: defaultCommands.create.text,
					cancel: defaultCommands.cancel.text,
					save: defaultCommands.save.text,
					destroy: defaultCommands.destroy.text,
					add: "Add new record",
					edit: defaultCommands.edit.text,
					update: defaultCommands.update.text,
					canceledit: defaultCommands.canceledit.text,
					excel: defaultCommands.excel.text,
					pdf: defaultCommands.pdf.text,
					csv: defaultCommands.csv.text,
					search: defaultCommands.search.text,
					columns: defaultCommands.columns.text,
					select: "Select",
					selectall: defaultCommands.selectall.text,
					sort: defaultCommands.sort.text,
					filter: defaultCommands.filter.text,
					group: defaultCommands.group.text,
					columnchooser: defaultCommands.columnchooser.text,
					selectRow: "Select Row",
					selectAllRows: "All rows",
					clearSelection: "Clear selection",
					copySelection: "Copy selection",
					copySelectionNoHeaders: "Copy selection (No Headers)",
					paste: "Paste (use CTRL/⌘ + V)",
					reorderRow: "Reorder row",
					reorderRowUp: "Up",
					reorderRowDown: "Down",
					reorderRowTop: "Top",
					reorderRowBottom: "Bottom",
					exportPdf: "Export to PDF",
					exportExcel: "Export to Excel",
					exportCSV: "Export to CSV",
					exportToCSVAll: "All",
					exportToCSVSelection: "Selection",
					exportToCSVSelectionNoHeaders: "Selection (No Headers)",
					exportToExcelAll: "All",
					exportToExcelSelection: "Selection",
					exportToExcelSelectionNoHeaders: "Selection (No Headers)",
					sortAsc: "Sort Ascending",
					sortDesc: "Sort Descending",
					moveGroupPrevious: "Move previous",
					moveGroupNext: "Move next",
					pinRow: "Pin row",
					pinTop: "Pin row to top",
					pinBottom: "Pin row to bottom",
					unpin: "Unpin row"
				},
				details: {
					expand: "Expand Details",
					collapse: "Collapse Details"
				},
				ai: {
					outputPlaceholder: "No AI output available",
					success: "Data is:",
					error: "Operation is not successful. Error:",
					invalidSelection: "This selection mode is not currently enabled. Please enable the appropriate selection option in the grid configuration.",
					promptPlaceholder: "Enter your AI prompt here..."
				},
				noRecords: NORECORDS,
				expandCollapseColumnHeader: "",
				groupHeader: "Press ctrl + space to group",
				ungroupHeader: "Press ctrl + space to ungroup",
				itemsSelected: "items selected",
				dragHandleLabel: "Drag row",
				toolbarLabel: "grid toolbar",
				groupingHeaderLabel: "grid grouping header",
				filterCellTitle: "filter cell",
				clearButtons: {
					clearFiltering: "Clear All Filters",
					clearSorting: "Clear Sorting",
					clearGrouping: "Clear Grouping",
					columnChooserReset: "Reset"
				},
				applyButtons: {
					applySorting: "Done",
					applyGrouping: "Done",
					columnChooserApply: "Apply"
				}
			},
			width: null
		},
		destroy: function() {
			var that = this, element, reorderableInstance;
			if (that.smallMQL) that.smallMQL.destroy();
			if (that.mediumMQL) that.mediumMQL.destroy();
			if (that.largeMQL) that.largeMQL.destroy();
			that._destroyColumnAttachments();
			that._unbindToolbarTools();
			that._destroyStickyGroups();
			that._destroyPinnedRows();
			Widget.fn.destroy.call(that);
			if (this._navigatableTables) {
				this._navigatableTables.off(NS);
				this._navigatableTables = null;
				this._headertables = null;
			}
			if (that._resizeHandler) $(window).off("resize.kendoGrid", that._resizeHandler);
			if (that._aiAssistant) {
				that._aiAssistant?.destroy();
				that._aiAssistant = null;
			}
			if (that._aiAssistantWindow) {
				that._aiAssistantWindow?.destroy();
				that._aiAssistantWindow = null;
			}
			if (that.pager && that.pager.element) that.pager.destroy();
			if (that._timer) clearTimeout(that._timer);
			if (that._progressTimeOut) clearTimeout(that._progressTimeOut);
			if (that._collapseGroupsTimeOut) clearTimeout(that._collapseGroupsTimeOut);
			if (that._endlessFetchTimeOut) clearTimeout(that._endlessFetchTimeOut);
			that.pager = null;
			that._destroyGroupable();
			reorderableInstance = that.wrapper.data("kendoReorderable");
			if (reorderableInstance) reorderableInstance.destroy();
			reorderableInstance = that.tbody ? that.tbody.data("kendoReorderable") : null;
			if (reorderableInstance) reorderableInstance.destroy();
			if (that.allowPaste) {
				(that.content || that.table).off("paste.kendoGrid", that.pasteHandler);
				that.unbind(that.pasteHandler);
			}
			if (that.pasteActionsDropDownList) {
				that.pasteActionsDropDownList.destroy();
				that.pasteActionsDropDownList = null;
			}
			if (that.selectable && that.selectable.element) {
				that.selectable.destroy();
				that.clearArea();
				that._selectedIds = null;
				if (that.copyHandler) {
					that.wrapper.off("keydown", that.copyHandler);
					that.unbind(that.copyHandler);
				}
				if (that.updateClipBoardState) {
					that.unbind(that.updateClipBoardState);
					that.updateClipBoardState = null;
				}
				if (that.clearAreaHandler) that.wrapper.off("keyup", that.clearAreaHandler);
			}
			that.selectable = null;
			that._selectableAggregatesOptions = null;
			if (that.resizable) {
				that.resizable.destroy();
				if (that._resizeUserEvents) {
					if (that._resizeHandleDocumentClickHandler) $(document).off("click", that._resizeHandleDocumentClickHandler);
					that._resizeUserEvents.destroy();
					that._resizeUserEvents = null;
				}
				that.resizable = null;
			}
			that._destroyRowResizing();
			that._destroyVirtualScrollable();
			if (that.editableUserEvents) {
				that.editableUserEvents.destroy();
				that.editableUserEvents = null;
			}
			if (that._lockedContentUserEvents) {
				that._lockedContentUserEvents.destroy();
				that._lockedContentUserEvents = null;
			}
			that._destroyEditable();
			if (that.dataSource) {
				that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(ERROR, that._errorHandler).unbind(SORT, that._clearSortClasses);
				that._refreshHandler = that._progressHandler = that._errorHandler = that._sortHandler = null;
			}
			element = that.element.add(that.wrapper).add(that.table).add(that.thead).add(that.wrapper.find(">.k-grid-toolbar"));
			if (that.content) element = element.add(that.content).add(that.content.find(">.k-virtual-scrollable-wrap"));
			if (that.scrollables && that.scrollables.first()) element = element.add(that.scrollables.first());
			if (that.lockedHeader) that._removeLockedContainers();
			if (that.pane) that.pane.destroy();
			if (that._isMobile) {
				that.wrapper.off("transitionend.kendoGrid");
				that.wrapper.off("contextmenu.kendoGrid");
			}
			if (that.minScreenResizeHandler) $(window).off("resize", that.minScreenResizeHandler);
			that._detachColumnMediaResizeHandler();
			if (that._draggableInstance && that._draggableInstance.element) that._draggableInstance.destroy();
			that._draggableInstance = null;
			if (that._draggableRowsInstance && that._draggableRowsInstance.element) that._draggableRowsInstance.destroy();
			if (that.tbodyContextMenu) that.tbodyContextMenu.destroy();
			if (that.theadContextMenu) that.theadContextMenu.destroy();
			if (that.loader) that.loader.destroy();
			that._draggableRowsInstance = null;
			element.off(NS);
			element[0].kendoBindingTarget = null;
			kendo.destroy(that.wrapper);
			that.rowTemplate = that.altRowTemplate = that.lockedRowTemplate = that.lockedAltRowTemplate = that.detailTemplate = that.footerTemplate = that.groupFooterTemplate = that.lockedGroupFooterTemplate = that.noRecordsTemplate = null;
			that.scrollables = that.thead = that.tbody = that.element = that.table = that.content = that.statusBar = that.footer = that.wrapper = that.lockedTable = that.lockedContent = that.lockedHeader = that.lockedFooter = that._groupableClickHandler = that._groupRows = that._setContentWidthHandler = that.loaderOverlay = that.wrapperClone = null;
		},
		getOptions: function() {
			var options = this.options;
			options.dataSource = null;
			var result = extend(true, {}, this.options);
			result.columns = kendo.deepExtend([], this.columns);
			var dataSource = this.dataSource;
			var initialData = dataSource.options.data && dataSource._data;
			dataSource.options.data = null;
			result.dataSource = $.extend(true, {}, dataSource.options);
			dataSource.options.data = initialData;
			result.dataSource.data = initialData;
			result.dataSource.page = dataSource.page();
			result.dataSource.filter = $.extend(true, {}, dataSource.filter());
			result.dataSource.pageSize = dataSource.pageSize();
			result.dataSource.sort = dataSource.sort();
			result.dataSource.group = dataSource.group();
			result.dataSource.aggregate = dataSource.aggregate();
			if (result.dataSource.transport) result.dataSource.transport.dataSource = null;
			if (result.pageable && result.pageable.pageSize) result.pageable.pageSize = dataSource.pageSize();
			return result;
		},
		setOptions: function(options) {
			var currentOptions = this.getOptions(), element = this.element;
			if (currentOptions.size) {
				const size = kendo.getValidCssClass("k-grid-", "size", currentOptions.size);
				element.removeClass(size);
			}
			kendo.deepExtend(currentOptions, options);
			if (!options.dataSource) currentOptions.dataSource = this.dataSource;
			else if (options.dataSource.filter) currentOptions.dataSource.filter = options.dataSource.filter;
			var wrapper = this.wrapper;
			var events = this._events;
			this.destroy();
			this._footerWidth = null;
			this.options = null;
			if (this._isMobile) {
				var mobileWrapper = wrapper.closest(kendo.roleSelector("pane")).parent();
				mobileWrapper.after(wrapper);
				mobileWrapper.remove();
				wrapper.removeClass("k-grid-mobile");
			}
			if (wrapper[0] !== element[0]) {
				wrapper.before(element);
				wrapper.remove();
			}
			element.empty();
			this.init(element, currentOptions, events);
			this._setEvents(currentOptions);
		},
		items: function() {
			if (this.lockedContent) return this._items(this.tbody).add(this._items(this.lockedTable.children("tbody")));
			else return this._items(this.tbody);
		},
		_items: function(container, includeGroupRows) {
			return container.children().filter(function() {
				var tr = $(this);
				return (includeGroupRows ? !tr.hasClass("k-detail-row") : !tr.hasClass(GROUPING_ROW)) && !tr.hasClass("k-detail-row") && !tr.hasClass("k-group-footer");
			});
		},
		dataItems: function() {
			var dataItems = kendo.ui.DataBoundWidget.fn.dataItems.call(this);
			if (this.lockedContent) {
				var n = dataItems.length, tmp = new Array(2 * n);
				for (var i = n; --i >= 0;) tmp[i] = tmp[i + n] = dataItems[i];
				dataItems = tmp;
			}
			return dataItems;
		},
		_destroyColumnAttachments: function() {
			var that = this;
			that.resizeHandle = null;
			if (!that.thead) return;
			that.thead.add(that.lockedHeader).find("th").each(function() {
				var th = $(this), filterMenu = th.data("kendoFilterMenu"), sortable = th.data("kendoColumnSorter"), columnMenu = th.data("kendoColumnMenu");
				if (filterMenu) filterMenu.destroy();
				if (sortable) sortable.destroy();
				if (columnMenu) columnMenu.destroy();
			});
		},
		_setInitialRtlScrollPosition: function() {
			const that = this;
			if (isRtl && that.scrollables) kendo.scrollLeft(that.scrollables, 0);
		},
		_attachCustomCommandsEvent: function() {
			var that = this, columns = leafColumns(that.columns || []), command, idx, length;
			for (idx = 0, length = columns.length; idx < length; idx++) {
				command = columns[idx].command;
				if (command) attachCustomCommandEvent(that, that.wrapper, command);
			}
		},
		_bindMediaQueries: function() {
			const that = this;
			if (that.options.adaptiveMode === "auto") {
				that.largeMQL = kendo.mediaQuery("large");
				that.mediumMQL = kendo.mediaQuery("medium");
				that.smallMQL = kendo.mediaQuery("small");
				const handler = (fullscreen, handler) => {
					if (that._editMode() !== "popup") return;
					const checkActionSheet = fullscreen === true || fullscreen === false;
					let popup;
					if (that._editContainer) popup = that._editContainer && that._editContainer.data(handler);
					if (!popup) {
						const reverseHandler = handler === "kendoActionSheet" ? "kendoWindow" : "kendoActionSheet";
						popup = that._editContainer && that._editContainer.data(reverseHandler);
					}
					if (checkActionSheet) {
						if (popup && popup.fullscreen && popup.visible()) {
							that._showAdaptiveView = true;
							popup.fullscreen(fullscreen);
						} else if (popup) {
							that._showAdaptiveView = false;
							that._destroyEditable(true);
						}
					} else if (popup && popup.fullscreen) popup.close();
				};
				that.smallMQL.onEnter(() => {
					handler(true, "kendoActionSheet");
				});
				that.mediumMQL.onEnter(() => {
					handler(false, "kendoActionSheet");
				});
				that.largeMQL.onEnter(() => {
					handler(null, "kendoWindow");
				});
			} else {
				that.smallMQL && that.smallMQL.destroy();
				that.mediumMQL && that.mediumMQL.destroy();
				that.largeMQL && that.largeMQL.destroy();
				that._showAdaptiveView = false;
			}
		},
		_aria: function() {
			var wrapper = this.wrapper, gridRole = this._hasDetails() ? "treegrid" : this.options.navigatable ? "grid" : null, table = this.table, toolbar = wrapper.find(".k-grid-toolbar"), groupingHeader = wrapper.find(".k-grouping-header"), gridId = this._ariaGridId(), tableTabindex = table.attr(TABINDEX), tbodyId, headerGroupId, footerGroupId, tableOwned, stacked = this._isStackedMode(), numberOfFixedRows = !stacked && this.thead.find(TR).length + this.wrapper.find(".k-grid-footer-wrap table tr").length, trailingColumns = this._trailingColumns(), virtual = this.virtualScroll, pageable = this.options.pageable, rowsCount;
			table.attr(TABINDEX, tableTabindex >= 0 ? tableTabindex : 0);
			if (gridRole) table.attr(ROLE, gridRole);
			const tbody = table.find("tbody");
			const thead = table.find("thead");
			const tfoot = table.find("tfoot");
			const tr = table.find(TR);
			if (tbody.find(TR).length > 0) tbody.attr(ROLE, ROWGROUP);
			if (thead.find(`${TR} ${TH}`).length > 0) thead.attr(ROLE, ROWGROUP);
			if (tfoot.find(TR).length > 0) tfoot.attr(ROLE, ROWGROUP);
			if (tr.children().length > 0) tr.attr(ROLE, ROW);
			table.find("th").attr(ROLE, COLUMNHEADER);
			table.find("td").attr(ROLE, GRIDCELL);
			if (pageable && this.dataSource.totalPages() > 1 || virtual && virtual.rows) {
				if (this._groups() > 0) rowsCount = -1;
				else if (this._hasDetails()) rowsCount = numberOfFixedRows + this.dataSource.total() * 2;
				else rowsCount = numberOfFixedRows + this.dataSource.total();
				table.attr(ARIA_ROWCOUNT, rowsCount);
			} else if (this._hasDetails()) {
				if (this._groups() > 0) rowsCount = -1;
				else rowsCount = numberOfFixedRows + this.dataSource.total() * 2;
				table.attr(ARIA_ROWCOUNT, rowsCount);
			}
			if (rowsCount && rowsCount > 0) this._ariaRowIndex();
			if (!stacked && (virtual && virtual.columns || !table.attr(ARIA_COLCOUNT) && (table.find("td:not([group-header-spanned-hidden]):hidden").length > 0 || wrapper.find(".k-grid-content-locked td:not([group-header-spanned-hidden]):hidden").length > 0))) {
				table.attr(ARIA_COLCOUNT, trailingColumns + leafColumns(this.columns).length);
				this._ariaColumnIndex();
			}
			if (this.pager) this.pager.element.attr(ARIA_CONTROLS, gridId);
			toolbar.attr({
				role: "toolbar",
				"aria-label": this.options.messages.toolbarLabel,
				"aria-controls": gridId
			});
			groupingHeader.attr({
				role: "toolbar",
				"aria-label": this.options.messages.groupingHeaderLabel,
				"aria-controls": gridId
			});
			headerGroupId = !stacked ? this._ariaHeaderFooter("header", "thead", "th, td", COLUMNHEADER) : "";
			footerGroupId = this._ariaHeaderFooter("footer", "tfoot", "td", GRIDCELL);
			if (!stacked && wrapper.find(".k-grid-content-locked").length > 0) this._ariaLockedContent();
			if (!!headerGroupId || !!footerGroupId) {
				tbodyId = this.tbody.attr(ID) || kendo.guid();
				tableOwned = [
					headerGroupId,
					tbodyId,
					footerGroupId
				].join(" ");
				this.tbody.attr(ID, tbodyId);
				table.attr(ARIA_OWNS, tableOwned);
			}
			if (this.options.groupable) this._ariaGroupTitles();
		},
		_ariaColumnIndex: function() {
			var trailingColumns = this._trailingColumns(), dataVirtual = this.tbody?.find(">tr").last().find("> td[data-virtual]"), headerRows = this.thead?.find(">tr").not(".k-filter-row"), lockedHeaderRows = this.wrapper?.find(".k-grid-header-locked thead > tr").not(".k-filter-row"), firstIndex = Number.MAX_VALUE, lastIndex = 0, lockedLastIndex = 0, previousVirtual = 0, nextVirtual = 0, previousIndex, i, cells, dataIndex, cellsIndex, eachHeaderCell = function(j, cell) {
				var current = cell.getAttribute("data-index"), currentIndex = Number(current), lockedParent = $(cell).closest(".k-grid-header-locked");
				if (lockedParent.length === 0 && currentIndex < firstIndex) firstIndex = currentIndex;
				if (lockedParent.length > 0 && lockedLastIndex < currentIndex) lockedLastIndex = currentIndex;
				if (lockedParent.length === 0 && lastIndex < currentIndex) lastIndex = currentIndex;
				if (current !== null) {
					cell.setAttribute(ARIA_COLINDEX, Number(currentIndex) + 1);
					previousIndex = Number(currentIndex) + 1 + cell.getAttribute("colspan");
				} else {
					cell.setAttribute(ARIA_COLINDEX, previousIndex + 1);
					previousIndex = previousIndex + cell.getAttribute("colspan");
				}
			};
			if (dataVirtual.length === 2) {
				previousVirtual = Number(dataVirtual[0].getAttribute("colspan"));
				nextVirtual = Number(dataVirtual[1].getAttribute("colspan"));
			} else if (dataVirtual.length === 1 && dataVirtual.prev().length === 0) previousVirtual = Number(dataVirtual[0].getAttribute("colspan"));
			else if (dataVirtual.length === 1 && dataVirtual.prev().length === 1) nextVirtual = Number(dataVirtual[0].getAttribute("colspan"));
			for (i = 0; i < lockedHeaderRows.length; i++) {
				previousIndex = 0;
				lockedHeaderRows.eq(i).find("th").each(eachHeaderCell);
			}
			for (i = 0; i < headerRows.length; i++) {
				previousIndex = 0;
				headerRows.eq(i).find("th").each(eachHeaderCell);
			}
			for (i = 0; i <= lockedLastIndex; i++) {
				dataIndex = i + trailingColumns;
				cells = this.wrapper.find(".k-grid-content-locked tbody > tr > td:nth-child(" + (i + 1) + ")");
				cells.attr(ARIA_COLINDEX, dataIndex + 1);
			}
			for (i = previousVirtual; i <= lastIndex - firstIndex - nextVirtual; i++) {
				if (previousVirtual === 0) cellsIndex = i + 1;
				else cellsIndex = i - previousVirtual + 2;
				dataIndex = firstIndex + i + trailingColumns;
				cells = this.tbody.find("> tr > td:nth-child(" + cellsIndex + ")");
				cells.attr(ARIA_COLINDEX, dataIndex + 1);
			}
		},
		_ariaGroupTitles: function() {
			var that = this, groups = that.dataSource.group(), ths = that.wrapper.find(".k-grid-header th");
			ths.each(function(i, el) {
				if (el.getAttribute("title") === that.options.messages.ungroupHeader) el.setAttribute("title", that.options.messages.groupHeader);
			});
			if (groups && groups.length > 0) groups.forEach(function(group) {
				var field = group.field, el = ths.filter("[" + kendo.attr("field") + "='" + field + "']");
				if (el.attr("title") === that.options.messages.groupHeader) el.attr("title", that.options.messages.ungroupHeader);
			});
		},
		_ariaHeaderFooter: function(type, group, el, role) {
			var that = this, wrapper = that.wrapper, table = wrapper.find(".k-grid-" + type + " .k-grid-" + type + "-wrap table"), lockedTable = wrapper.find(".k-grid-" + type + " .k-grid-" + type + "-locked table"), groupId = "", rowGroup;
			if (table.length > 0) {
				rowGroup = table.find(group + ", tbody");
				groupId = rowGroup.attr(ID) || kendo.guid();
				table.attr(ROLE, NONE);
				const tr = table.find(TR);
				if (tr.children().length > 0) {
					tr.attr(ROLE, ROW);
					rowGroup.attr(ROLE, ROWGROUP);
				}
				table.find(el).attr(ROLE, role);
				rowGroup.attr({ id: groupId });
			}
			if (lockedTable.length > 0) that._ariaLocked(type, group, el, role);
			lockedTable.find("td").attr(ROLE, GRIDCELL);
			table.find("td").attr(ROLE, GRIDCELL);
			return groupId;
		},
		_ariaId: function() {
			var id = this.element.attr(ID) || "aria";
			if (id) this._cellId = id + "_active_cell";
		},
		_ariaGridId: function() {
			var table = this.table, gridId = table.attr(ID);
			if (!gridId) {
				gridId = kendo.guid();
				table.attr(ID, gridId);
			}
			return gridId;
		},
		_ariaLocked: function(type, group, el, role) {
			var that = this, wrapper = that.wrapper, table = wrapper.find(".k-grid-" + type + " .k-grid-" + type + "-wrap table"), lockedTable = wrapper.find(".k-grid-" + type + " .k-grid-" + type + "-locked table"), rows = table.find(TR), lockedRows = lockedTable.find(TR);
			lockedTable.attr(ROLE, NONE);
			lockedTable.find(group + ", tbody").attr(ROLE, NONE);
			lockedRows.attr(ROLE, NONE);
			lockedTable.find(el).attr(ROLE, role);
			rows.each(function(i, row) {
				var ownedCells = [];
				ownedCells = that._cellsIds(lockedRows.eq(i).find(el), "locked_" + type, i);
				ownedCells = ownedCells.concat(that._cellsIds($(row).find(el), type, i));
				row.setAttribute(ARIA_OWNS, ownedCells.join(" "));
			});
		},
		_ariaLockedContent: function() {
			var that = this, tableRows = that.table.find(TR), lockedTable = that.wrapper.find(".k-grid-content-locked table"), lockedRows = lockedTable.find(TR);
			lockedTable.attr(ROLE, NONE);
			lockedTable.find("tbody").attr(ROLE, NONE);
			lockedRows.attr(ROLE, NONE);
			lockedTable.find("td").attr(ROLE, GRIDCELL);
			tableRows.each(function(i, row) {
				var ownedCells = [];
				ownedCells = that._cellsIds(lockedRows.eq(i).find("td"), "locked_datacell", i);
				ownedCells = ownedCells.concat(that._cellsIds($(row).find("td"), "datacell", i));
				row.setAttribute(ARIA_OWNS, ownedCells.join(" "));
			});
		},
		_ariaAddHiddenColIndex: function() {
			var virtualScroll = this.virtualScroll || {}, columns = this.columns, table = this.table, leafColsCount = leafColumns(columns).length;
			if (!virtualScroll.columns && !table.attr(ARIA_COLCOUNT)) {
				this._ariaColumnIndex();
				table.attr(ARIA_COLCOUNT, leafColsCount);
			}
		},
		_ariaRemoveHiddenColIndex: function() {
			var virtualScroll = this.virtualScroll || {}, columns = this.columns, leafColsCount = leafColumns(columns).length;
			if (!virtualScroll.columns && leafColsCount === visibleLeafColumns(this.columns).length) {
				this.wrapper.find("td, th").removeAttr(ARIA_COLINDEX);
				this.table.removeAttr(ARIA_COLCOUNT);
			}
		},
		_ariaRowIndex: function() {
			var headerRows = !this._isStackedMode() && this.thead.find(">tr"), numberOfHeaderRows = headerRows ? headerRows.length : 0, bodyRows = this.tbody.find(">tr"), footerRows = this.wrapper.find(".k-grid-footer-wrap tfoot > tr"), totalNumberOfItems = this.dataSource.total(), previousItems = this.dataSource.skip() || 0, currentIndex = 1, previousMaster = false, i, currentRow;
			if (this._hasDetails()) {
				totalNumberOfItems = totalNumberOfItems * 2;
				previousItems = previousItems * 2;
			}
			for (i = 0; i < numberOfHeaderRows; i++) headerRows.eq(i).attr(ARIA_ROWINDEX, currentIndex + i);
			currentIndex = numberOfHeaderRows + previousItems;
			for (i = 0; i < bodyRows.length; i++) {
				currentRow = bodyRows.eq(i);
				if (this._hasDetails() && currentRow.hasClass("k-master-row")) {
					if (previousMaster) currentIndex = currentIndex + 2;
					else currentIndex = currentIndex + 1;
					previousMaster = true;
				} else {
					currentIndex = currentIndex + 1;
					previousMaster = false;
				}
				currentRow.attr(ARIA_ROWINDEX, currentIndex);
			}
			currentIndex = numberOfHeaderRows + totalNumberOfItems + 1;
			for (i = 0; i < footerRows.length; i++) footerRows.eq(i).attr(ARIA_ROWINDEX, currentIndex + i);
		},
		_cellsIds: function(elements, prefix, i) {
			var ownedCells = [], gridId = this._ariaGridId();
			elements.each(function(j, cell) {
				var id = cell.getAttribute(ID) || gridId + "_" + prefix + "_" + i + "_" + j;
				cell.setAttribute(ID, id);
				ownedCells.push(id);
			});
			return ownedCells;
		},
		_trailingColumns: function() {
			return this._groups() + (this._hasDetails() ? 1 : 0);
		},
		_element: function() {
			const that = this;
			let table = that.element;
			if (!table.is("table")) {
				if (that.options.scrollable) table = that.element.find("> .k-grid-content > table");
				else table = that.element.children("table");
				if (!table.length) table = $("<table />").appendTo(that.element);
			}
			table.addClass("k-grid-table k-table");
			table.addClass(kendo.getValidCssClass("k-table-", "size", that.options.size));
			that.table = table;
			that._wrapper();
		},
		_createResizeHandle: function(container, th) {
			var that = this;
			var indicatorWidth = that.options.columnResizeHandleWidth;
			var scrollable = that.options.scrollable;
			var resizeHandle = that.resizeHandle;
			var halfResizeHandle = indicatorWidth * 3 / 2;
			var rtlCorrection = 0;
			var headerWrap;
			var ieCorrection;
			var webkitCorrection;
			var firefoxCorrection;
			var leftMargin;
			var leftBorderWidth;
			var scrollLeft;
			var left;
			var top;
			if (resizeHandle && that.lockedContent && resizeHandle.data("th")[0] !== th[0]) {
				resizeHandle.off(NS).remove();
				resizeHandle = null;
			}
			if (!resizeHandle) {
				resizeHandle = that.resizeHandle = $("<div class=\"k-resize-handle\"><div class=\"k-resize-handle-inner\"></div></div>");
				container.append(resizeHandle);
			}
			scrollLeft = kendo.scrollLeft(container);
			if (isRtl && (browser.mozilla || browser.webkit && browser.version >= 85)) scrollLeft = scrollLeft * -1;
			leftBorderWidth = parseFloat(container.css("borderLeftWidth"));
			left = th.offset().left + scrollLeft - parseFloat(th.css("marginLeft")) - (container.offset().left + leftBorderWidth);
			if (!isRtl) left += th[0].offsetWidth;
			else if (scrollable) {
				rtlCorrection = left <= scrollLeft ? halfResizeHandle : 0;
				headerWrap = th.closest(".k-grid-header-wrap, .k-grid-header-locked");
				headerWrap[0].scrollWidth - headerWrap[0].offsetWidth;
				leftMargin = parseFloat(headerWrap.css("marginLeft"));
				ieCorrection = browser.msie ? 2 * kendo.scrollLeft(headerWrap) + leftBorderWidth - leftMargin - rtlCorrection : 0;
				webkitCorrection = -rtlCorrection;
				firefoxCorrection = browser.mozilla ? leftBorderWidth - leftMargin - rtlCorrection : 0;
				left -= webkitCorrection + firefoxCorrection + ieCorrection;
			}
			top = th.offset().top - parseFloat(th.css("marginTop")) - (container.offset().top + parseFloat(container.css("borderTopWidth")));
			resizeHandle.css({
				top,
				left: left - halfResizeHandle,
				height: outerHeight(th),
				width: indicatorWidth * 3 - rtlCorrection
			}).data("th", th).show();
			resizeHandle.off("dblclick.kendoGrid").on("dblclick.kendoGrid", function() {
				that._autoFitLeafColumn(parseInt(th.attr(kendo.attr("index")), 10));
			});
		},
		_positionColumnResizeHandle: function() {
			var that = this, lockedHead = that.lockedHeader ? that.lockedHeader.find("thead").first() : $();
			if (that.thead) that.thead.add(lockedHead).on("mousemove.kendoGrid", "tr:not(.k-filter-row) > th:not([data-resizable=false])", function(e) {
				var button = typeof e.buttons !== "undefined" ? e.buttons : e.which || e.button;
				var th = $(this);
				if (th.hasClass("k-group-cell") || th.hasClass("k-hierarchy-cell")) return;
				if (typeof button !== "undefined" && button !== 0) return;
				if (th[0].hasAttribute(kendo.attr(COLSPAN))) return;
				that._createResizeHandle(th.closest(DIV), th);
			});
		},
		_resizeHandleDocumentClick: function(e) {
			if ($(e.target).closest(".k-column-active").length) return;
			$(document).off(e);
			this._resetResizeHandleHeader();
			this._hideResizeHandle();
		},
		_resetResizeHandleHeader: function() {
			var th;
			if (!this.resizeHandle) return;
			th = $(this.resizeHandle).data("th");
			if (th) {
				th.find(".k-link").find(".k-icon,.k-svg-icon").show();
				th.find(".k-sort-order").show();
				th.find(".k-grid-column-menu").show();
				th.find(".k-grid-filter-menu").show();
			}
		},
		_hideResizeHandle: function() {
			if (this.resizeHandle) {
				this.resizeHandle.data("th").removeClass("k-column-active");
				if (this.lockedContent && !(this._isMobile || kendo.support.mobileOS)) {
					this.resizeHandle.off(NS).remove();
					this.resizeHandle = null;
				} else this.resizeHandle.hide();
			}
		},
		_positionColumnResizeHandleTouch: function() {
			var that = this, lockedHead = that.lockedHeader ? that.lockedHeader.find("thead").first() : $();
			that._resizeUserEvents = new kendo.UserEvents(lockedHead.add(that.thead), {
				filter: "th:not(.k-group-cell):not(.k-hierarchy-cell)",
				threshold: 10,
				minHold: 500,
				hold: function(e) {
					var th = $(e.target);
					e.preventDefault();
					if (that.resizeHandle) {
						that.resizeHandle.data("th").removeClass("k-column-active");
						that._resetResizeHandleHeader();
					}
					th.addClass("k-column-active");
					th.find(".k-link").find(".k-icon,.k-svg-icon").hide();
					th.find(".k-sort-order").hide();
					th.find(".k-grid-column-menu").hide();
					th.find(".k-grid-filter-menu").hide();
					that._createResizeHandle(th.closest(DIV), th);
					if (!that._resizeHandleDocumentClickHandler) that._resizeHandleDocumentClickHandler = that._resizeHandleDocumentClick.bind(that);
					$(document).on("click", that._resizeHandleDocumentClickHandler);
				}
			});
		},
		resizeColumn: function(column, columnWidth) {
			var that = this;
			var isLocked = !!column.locked;
			var isHidden = !!column.hidden;
			var options = this.options;
			var scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0;
			var index = isLocked ? inArray(column, visibleLockedColumns(visibleLeafColumns(that.columns))) : inArray(column, visibleNonLockedColumns(visibleLeafColumns(that.columns)));
			var contentTable = isLocked ? that.lockedTable : that.table;
			var footer = that.footer || $();
			var columnMinWidth = column.minResizableWidth || 10;
			var gridWidth = isLocked ? outerWidth(contentTable.find("tbody")) : outerWidth(that.tbody);
			var col;
			if (this._isStackedMode()) return;
			var header = isLocked ? that.lockedHeader.find("table") : that.thead.closest("table");
			if (isHidden) {
				column.width = columnWidth > columnMinWidth ? columnWidth : columnMinWidth;
				return;
			}
			if (that.footer && that.lockedContent) footer = isLocked ? that.footer.children(".k-grid-footer-locked") : that.footer.children(".k-grid-footer-wrap");
			if (options.scrollable) col = header.find("col:not(.k-group-col,.k-hierarchy-col)").eq(index).add(contentTable.children("colgroup").find("col:not(.k-group-col):not(.k-hierarchy-col)").eq(index)).add(footer.find("colgroup").find("col:not(.k-group-col):not(.k-hierarchy-col)").eq(index));
			else col = contentTable.find("colgroup").find("col:not(.k-group-col):not(.k-hierarchy-col)").eq(index);
			if (options.scrollable) {
				var constrain = false;
				var totalWidth = that.wrapper.width() - scrollbar;
				var width = columnWidth = columnWidth > columnMinWidth ? columnWidth : columnMinWidth;
				if (isLocked && gridWidth - columnWidth + width > totalWidth) {
					width = columnWidth + (totalWidth - gridWidth - scrollbar * 2);
					if (width < 0) width = columnWidth;
					constrain = true;
				}
				if (width > 10 && width >= columnMinWidth) {
					col.css("width", width);
					if (gridWidth) {
						if (constrain) width = totalWidth - scrollbar * 2;
						else width = gridWidth + (columnWidth - column.width);
						contentTable.add(header).add(footer).css("width", width);
						if (!isLocked) that._footerWidth = width;
					}
				}
				that._scrollVirtualWrapperOnColumnResize();
			} else if (columnWidth > 10 && columnWidth >= columnMinWidth) col.css("width", columnWidth);
			column.width = columnWidth;
			that._applyLockedContainersWidth();
			that._syncLockedContentHeight();
			that._syncLockedHeaderHeight();
			that._updateStickyColumns();
			that._syncStickyGroupColgroups();
			that._syncPinnedColgroups();
			that._syncPinnedTableWidths();
			that._syncPinnedLockedWidths();
		},
		_adjustColWidths: function(contentTable, header, footer, gridWidth, extraTables) {
			const colWidths = {};
			contentTable.add(header).add(footer).css("width", gridWidth);
			if (extraTables && extraTables.length) extraTables.css("width", gridWidth);
			contentTable.add(header).add(footer).find("col").each((i, col) => {
				colWidths[i] = $(col).css("width");
			});
			contentTable.add(header).add(footer).find("col").each((i, col) => {
				$(col).css("width", colWidths[i]);
			});
			if (extraTables && extraTables.length) extraTables.each(function() {
				$(this).find("col").each((i, col) => {
					if (colWidths[i]) $(col).css("width", colWidths[i]);
				});
			});
		},
		_resizable: function() {
			var that = this, options = that.options, container, columnStart, columnWidth, columnMinWidth, gridWidth, isMobile = that._isMobile || kendo.support.mobileOS, scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0, isLocked, pinnedTables, col, th;
			if (!that._isStackedMode() && (options.resizable === true || options.resizable && options.resizable.columns === true)) {
				container = options.scrollable ? that.wrapper.find(".k-grid-header-wrap").first() : that.wrapper;
				if (isMobile) that._positionColumnResizeHandleTouch(container);
				else that._positionColumnResizeHandle(container);
				if (that.resizable) that.resizable.destroy();
				that.resizable = new ui.Resizable(container.add(that.lockedHeader), {
					handle: (!!options.scrollable ? "" : ">") + ".k-resize-handle",
					hint: function(handle) {
						return $("<div class=\"k-grid-resize-indicator\" />").css({ height: outerHeight(handle.data("th")) + that.tbody.attr("clientHeight") });
					},
					start: function(e) {
						th = $(e.currentTarget).data("th");
						if (isMobile) that._hideResizeHandle();
						let header = th.closest("table"), index = $.inArray(th[0], leafDataCells(th.closest("thead")).filter(":visible"));
						isLocked = header.parent().hasClass("k-grid-header-locked");
						let contentTable = isLocked ? that.lockedTable : that.table, footer = that.footer || $();
						if (that.footer && that.lockedContent) footer = isLocked ? that.footer.children(".k-grid-footer-locked") : that.footer.children(".k-grid-footer-wrap");
						let footerTable = footer.find("table");
						cursor(that.wrapper, "col-resize");
						if (options.scrollable) col = header.find("col:not(.k-group-col):not(.k-hierarchy-col)").eq(index).add(contentTable.children("colgroup").find("col:not(.k-group-col):not(.k-hierarchy-col)").eq(index)).add(footer.find("colgroup").find("col:not(.k-group-col):not(.k-hierarchy-col)").eq(index));
						else col = contentTable.children("colgroup").find("col:not(.k-group-col):not(.k-hierarchy-col)").eq(index);
						pinnedTables = $();
						const pinnedColSelector = "col:not(.k-group-col):not(.k-hierarchy-col)";
						const pinnedTableKey = isLocked ? "lockedTable" : "table";
						[that._pinnedTop, that._pinnedBottom].forEach((p) => {
							if (p && p[pinnedTableKey]) {
								col = col.add(p[pinnedTableKey].find("colgroup").find(pinnedColSelector).eq(index));
								pinnedTables = pinnedTables.add(p[pinnedTableKey]);
							}
						});
						let columns = $.map(that.columns, function(a) {
							return !a.hidden && (isLocked && a.locked || !isLocked && !a.locked) ? a : null;
						});
						columnStart = e.x.location;
						columnWidth = outerWidth(th);
						columnMinWidth = leafColumns(columns)[index].minResizableWidth || 10;
						gridWidth = isLocked ? outerWidth(contentTable.children("tbody")) : outerWidth(that.tbody);
						if (browser.webkit) that.wrapper.addClass("k-grid-column-resizing");
						that._adjustColWidths(contentTable, header, footerTable, gridWidth, pinnedTables);
					},
					resize: function(e) {
						var rtlMultiplier = isRtl ? -1 : 1, currentWidth = columnWidth + e.x.location * rtlMultiplier - columnStart * rtlMultiplier;
						if (options.scrollable) {
							var footer;
							if (isLocked && that.lockedFooter) footer = that.lockedFooter.children("table");
							else if (that.footer) footer = that.footer.find(">.k-grid-footer-wrap>table");
							if (!footer || !footer[0]) footer = $();
							var header = th.closest("table");
							var contentTable = isLocked ? that.lockedTable : that.table;
							var constrain = false;
							var totalWidth = that.wrapper.width() - scrollbar;
							var width = currentWidth;
							if (isLocked && gridWidth - columnWidth + width > totalWidth) {
								width = columnWidth + (totalWidth - gridWidth - scrollbar * 2);
								if (width < 0) width = currentWidth;
								constrain = true;
							}
							if (width > 10 && width >= columnMinWidth) {
								col.css("width", width);
								if (gridWidth) {
									if (constrain) width = totalWidth - scrollbar * 2;
									else width = gridWidth + e.x.location * rtlMultiplier - columnStart * rtlMultiplier;
									contentTable.add(header).add(footer).add(pinnedTables).css("width", width);
									if (!isLocked) that._footerWidth = width;
								}
							}
							that._scrollVirtualWrapperOnColumnResize();
						} else if (currentWidth > 10 && currentWidth >= columnMinWidth) col.css("width", currentWidth);
					},
					resizeend: function() {
						var newWidth = outerWidth(th), column, header;
						cursor(that.wrapper, "");
						if (browser.webkit) that.wrapper.removeClass("k-grid-column-resizing");
						if (th && columnWidth != newWidth) {
							header = that.lockedHeader ? that.lockedHeader.find("thead").first().find(TR).first().add(that.thead.find(TR).first()) : th.parent();
							let index = parseInt(th.attr(kendo.attr("index")), 10);
							if (isNaN(index)) index = header.find("th:not(.k-group-cell):not(.k-hierarchy-cell)").index(th);
							column = leafColumns(that.columns)[index];
							const resizeTable = th.closest("table");
							if (resizeTable.find("col.k-group-col").length) {
								const visibleColumns = leafColumns($.map(that.columns, function(col) {
									return !col.hidden && (isLocked && col.locked || !isLocked && !col.locked) ? col : null;
								}));
								const resizedColIndex = $.inArray(column, visibleColumns);
								resizeTable.find("col:not(.k-group-col):not(.k-hierarchy-col)").each(function(idx) {
									if (visibleColumns[idx] && idx !== resizedColIndex) visibleColumns[idx].width = parseFloat(this.style.width || $(this).css("width"));
								});
							}
							column.width = newWidth;
							that.trigger(COLUMNRESIZE, {
								column,
								oldWidth: columnWidth,
								newWidth
							});
							that._applyLockedContainersWidth(true);
							that._syncLockedContentHeight();
							that._syncLockedHeaderHeight();
							that._updateStickyColumns();
							that._syncStickyGroupColgroups();
							that._syncPinnedColgroups();
							that._syncPinnedTableWidths();
							that._syncPinnedLockedWidths();
						}
						that._resetResizeHandleHeader();
						that._hideResizeHandle();
						th = null;
					}
				});
			}
		},
		_addLockedRowResizing: function(tr) {
			var index = tr.index();
			return this.lockedTable.find(TR).eq(index).add(this.tbody.find(TR).eq(index));
		},
		_getMinRowHeight: function(row) {
			var minHeight = 0;
			row.each((i, el) => {
				var currentMinHeight;
				el.style.height = "";
				currentMinHeight = outerHeight(el);
				if (currentMinHeight > minHeight) minHeight = currentMinHeight;
			});
			return minHeight;
		},
		_cacheRowHeight: function(rows, height) {
			var that = this;
			if (!that._cachedRowsHeight) that._cachedRowsHeight = {};
			rows.each((i, el) => {
				var uid = el.getAttribute("data-uid");
				that._cachedRowsHeight[uid] = height;
			});
		},
		_clearCachedRowsHeight: function(rows) {
			var that = this;
			if (rows && that._cachedRowsHeight) rows.each((i, el) => {
				var uid = el.getAttribute("data-uid");
				delete that._cachedRowsHeight[uid];
			});
			else that._cachedRowsHeight = null;
		},
		_mapCachedRowsHeight: function(method, target) {
			var input = this._cachedRowsHeight, ds = this.dataSource, output = {};
			Object.keys(input).forEach((key) => {
				var item = ds[method](key);
				if (item) output[item[target]] = input[key];
			});
			this._cachedRowsHeight = output;
		},
		_rowResizerDblClick: function() {
			var that = this, resizer = that.rowResizer, row = resizer.data(TR), oldHeight = outerHeight(row), newHeight, rows;
			if (row.hasClass(SELECTED)) rows = that.select();
			else rows = row;
			if (that.lockedTable) {
				row = that._addLockedRowResizing(row);
				if (row.hasClass(SELECTED)) rows = that.lockedTable.find(".k-selected");
				else rows = that.lockedTable.find(TR).eq(row.index());
				rows.each((i, el) => {
					var rowIndex = el.rowIndex, rowPair = $(el).add(that.tbody.find(TR).eq(rowIndex)), pairMinHeight = that._getMinRowHeight(rowPair);
					rowPair.css(HEIGHT, pairMinHeight);
				});
			} else rows.css(HEIGHT, AUTO);
			that._clearCachedRowsHeight(rows);
			resizer.removeClass(HOVER);
			resizer.removeClass(ACTIVE);
			newHeight = outerHeight(row);
			if (oldHeight != newHeight) that.trigger(ROWRESIZE, {
				row,
				rows,
				oldHeight,
				newHeight
			});
		},
		_setupRowResizer(resizer, row, top) {
			resizer.data(TR, row).css({ top });
		},
		_attachRowResizerEvents: function() {
			var rowResizer = this.rowResizer, delay = 200, isIn = false;
			rowResizer.on("mousedown.kendoGrid", (e) => {
				if (e.button === 0) {
					rowResizer.removeClass(HOVER);
					rowResizer.addClass(ACTIVE);
				}
			}).on("mouseup.kendoGrid", (e) => {
				if (e.button === 0) {
					rowResizer.removeClass(ACTIVE);
					rowResizer.addClass(HOVER);
				}
			}).on("mouseenter.kendoGrid", () => {
				isIn = true;
				setTimeout(() => {
					if (isIn) rowResizer.addClass(HOVER);
				}, delay);
			}).on("mouseleave.kendoGrid", () => {
				isIn = false;
				rowResizer.removeClass(HOVER);
			});
		},
		_getResizerTop: function(tr, container) {
			var resizer = this.rowResizer, inner = resizer.find(".k-row-resizer")[0], paddingTop = parseInt(getComputedStyle(resizer[0]).paddingTop);
			return tr.offset().top - parseFloat(tr.css("marginTop")) - (container.offset().top + parseFloat(container.css("borderTopWidth"))) - inner.clientHeight - paddingTop + container.scrollTop();
		},
		_getResizerContainer: function() {
			var container = this.tbody.closest(DIV);
			if (this.lockedTable) container = container.closest(".k-grid-container");
			return container;
		},
		_createRowResizer: function(e) {
			var that = this, tr = $(e.currentTarget), targetHeight = e.currentTarget.clientHeight, positionIntarget = e.offsetY, rowResizer = that.rowResizer, previousRow = tr.prev("tr:visible"), container = that._getResizerContainer(), top;
			if (!rowResizer) {
				rowResizer = that.rowResizer = $("<div class=\"k-resizer-wrap\"><div class=\"k-row-resizer\"></div></div>");
				container.append(rowResizer);
				that._attachRowResizerEvents();
				rowResizer.off("dblclick.kendoGrid").on("dblclick.kendoGrid", that._rowResizerDblClick.bind(that));
			}
			top = that._getResizerTop(tr, container);
			if (previousRow.length !== 0 && targetHeight / 2 > positionIntarget) {
				if (!previousRow.hasClass(GROUPING_ROW)) that._setupRowResizer(rowResizer, previousRow, top);
			} else if (!tr.hasClass(GROUPING_ROW)) that._setupRowResizer(rowResizer, tr, top + targetHeight);
		},
		_detachRowResizerEvents: function() {
			this.rowResizer.off("mousedown.kendoGrid").off("mouseup.kendoGrid").off("mouseenter.kendoGrid").off("mouseleave.kendoGrid");
		},
		_mapResizedRows: function(rows, multiSelectionLocked, newHeight) {
			var that = this;
			rows.each((i, el) => {
				var minHeight;
				if (multiSelectionLocked) {
					var rowIndex = el.rowIndex, pairNew = newHeight, pairMin = 0, rowPair = $(el).add(that.tbody.find(TR).eq(rowIndex));
					rowPair.each((i, r) => {
						var currentMinHeight;
						r.style.height = "";
						currentMinHeight = outerHeight(r);
						if (currentMinHeight > pairMin) pairMin = currentMinHeight;
					});
					if (pairNew < pairMin) {
						pairNew = pairMin;
						that._clearCachedRowsHeight(rowPair.eq(0));
					} else that._cacheRowHeight(rowPair.eq(0), pairNew);
					rowPair.css(HEIGHT, pairNew);
				} else {
					el.style.height = "";
					minHeight = outerHeight(el);
					if (newHeight > minHeight) {
						el.style.height = newHeight + PX;
						that._cacheRowHeight($(el), newHeight);
					} else that._clearCachedRowsHeight($(el));
				}
			});
		},
		_rowResizing: function() {
			var that = this, options = that.options, container, rowStart, rowHeight, tr;
			if (options.resizable && options.resizable.rows === true) {
				that.tbody.parent().add(that.lockedTable).on("mousemove.kendoGrid", ".k-grid-footer tr, .k-table-tbody tr", that._createRowResizer.bind(that));
				if (that.rowResizing) that.rowResizing.destroy();
				container = that._getResizerContainer();
				that.rowResizing = new ui.Resizable(container, {
					handle: ".k-resizer-wrap",
					start: function(e) {
						tr = $(e.currentTarget).data(TR);
						if (that.lockedTable) tr = that._addLockedRowResizing(tr);
						tr.addClass(HOVER);
						that._detachRowResizerEvents();
						rowStart = e.y.location;
						rowHeight = outerHeight(tr);
					},
					resize: function(e) {
						var newHeight = rowHeight + e.y.location - rowStart, minHeight = 0;
						if (tr.length > 1) minHeight = that._getMinRowHeight(tr);
						if (newHeight < minHeight) newHeight = minHeight;
						tr.css("height", newHeight);
						that._setupRowResizer(that.rowResizer, tr, that._getResizerTop(tr, container) + newHeight);
					},
					resizeend: function() {
						var newHeight = outerHeight(tr), multiSelectionLocked = false, rows;
						if (tr.hasClass(SELECTED)) {
							rows = that.select();
							if (tr.length > 1 && rows.length > tr.length) {
								rows = that.lockedTable.find(".k-selected").not(tr);
								multiSelectionLocked = true;
							}
						} else rows = tr;
						that._mapResizedRows(rows, multiSelectionLocked, newHeight);
						tr.removeClass(HOVER);
						that.rowResizer.removeClass(ACTIVE);
						that.rowResizer.addClass(HOVER);
						that._attachRowResizerEvents();
						if (multiSelectionLocked) rows = that.select();
						if (rowHeight != newHeight) that.trigger(ROWRESIZE, {
							row: tr,
							rows,
							oldHeight: rowHeight,
							newHeight
						});
						tr = null;
					}
				});
			}
		},
		_draggable: function() {
			var that = this, reorderable = that.options.reorderable;
			if (reorderable === true || reorderable && reorderable.columns) {
				if (that._draggableInstance) that._draggableInstance.destroy();
				var header = that.wrapper.children(".k-grid-header");
				header.addClass("k-grid-draggable-header");
				that._draggableInstance = that.wrapper.kendoDraggable({
					group: kendo.guid(),
					autoScroll: true,
					filter: that.content ? ".k-grid-header:first th.k-header:not(.k-group-cell):not(.k-hierarchy-cell)" : "table:first>.k-grid-header th.k-header:not(.k-group-cell):not(.k-hierarchy-cell)",
					dragstart: function() {
						header.children(".k-grid-header-wrap").off("scroll.kendoGridscrolling").on("scroll.kendoGridscrolling", function(e) {
							if (that.virtualScrollable) kendo.scrollLeft(that.content.find(">.k-virtual-scrollable-wrap"), this.scrollLeft);
							else kendo.scrollLeft(that.scrollables.not(e.currentTarget), this.scrollLeft);
						});
					},
					dragend: function() {
						that._resetResizeHandleHeader();
						header.children(".k-grid-header-wrap").off("scroll.kendoGridscrolling");
					},
					drag: function() {
						that._hideResizeHandle();
					},
					hint: function(target) {
						var title = target.attr(kendo.attr("title"));
						if (title) title = kendo.htmlEncode(title);
						return $("<div class=\"k-reorder-clue k-drag-clue\" />").html(title || target.attr(kendo.attr("field")) || target.text()).prepend(kendo.ui.icon({
							icon: "cancel",
							iconClass: "k-drag-status"
						}));
					}
				}).data("kendoDraggable");
			}
		},
		_reorderable: function() {
			let that = this, reorderable = that.options.reorderable;
			if (reorderable === true || reorderable && reorderable.columns) {
				if (that.wrapper.data("kendoReorderable")) that.wrapper.data("kendoReorderable").destroy();
				that.wrapper.kendoReorderable({
					draggable: that._draggableInstance,
					dropFilter: HEADERCELLS,
					allowDropAfterLastItem: true,
					dragOverContainers: function(sourceIndex, targetIndex) {
						let columns = flatColumnsInDomOrder(that.columns);
						return columns[sourceIndex].lockable !== false && targetParentContainerIndex(columns, that.columns, sourceIndex, targetIndex) > -1;
					},
					inSameContainer: function(e) {
						let sourceParent = $(e.source).parent()[0], targetParent = $(e.target).parent()[0], sourceIndex = e.sourceIndex, targetIndex = e.targetIndex, flatColumns = flatColumnsInDomOrder(that.columns), lockable = flatColumns && flatColumns[sourceIndex] && !!flatColumns[sourceIndex].lockable;
						if (that._isLocked() && lockable) {
							sourceParent = $(e.source.closest(".k-grid-header"))[0];
							targetParent = $(e.target.closest(".k-grid-header"))[0];
						}
						return sourceParent === targetParent && targetParentContainerIndex(flatColumns, that.columns, sourceIndex, targetIndex) > -1;
					},
					change: function(e) {
						let columns = flatColumnsInDomOrder(that.columns);
						let column = columns[e.oldIndex];
						let newIndex = targetParentContainerIndex(columns, that.columns, e.oldIndex, e.newIndex);
						that.trigger(COLUMNREORDER, {
							newIndex,
							oldIndex: inArray(column, columns),
							column
						});
						that.reorderColumn(newIndex, column, e.position === "before");
					}
				});
			}
		},
		_reorderHeader: function(sources, target, before, container) {
			var that = this;
			var sourcePosition = columnPosition(sources[0], that.columns);
			var destPosition = columnPosition(target, that.columns);
			var action;
			var ths;
			var leafs = [];
			for (var idx = 0; idx < sources.length; idx++) if (sources[idx].columns) leafs = leafs.concat(sources[idx].columns);
			if (container) ths = elements(container, container, "tr:eq(" + sourcePosition.row + ")>th.k-header:not(.k-group-cell,.k-hierarchy-cell)");
			else ths = elements(that.lockedHeader, that.thead, "tr:eq(" + sourcePosition.row + ")>th.k-header:not(.k-group-cell,.k-hierarchy-cell)");
			var sourceLockedColumns = lockedColumns(sources).length;
			var targetLockedColumns = lockedColumns([target]).length;
			if (leafs.length) {
				if (sourceLockedColumns > 0 && targetLockedColumns === 0) {
					action = "prepend";
					moveCellsBetweenContainers(sources, target, leafs, that.columns, that.lockedHeader.find("thead"), that.thead, this._groups(), action);
				} else if (sourceLockedColumns === 0 && targetLockedColumns > 0) {
					action = destPosition.cell === 0 && sources[0].columns && !target.columns && !that._group ? "prepend" : "append";
					moveCellsBetweenContainers(sources, target, leafs, nonLockedColumns(that.columns), that.thead, that.lockedHeader.find("thead"), this._groups(), action);
				}
				if (target.columns || sourcePosition.cell - destPosition.cell > 1 || destPosition.cell - sourcePosition.cell > 1) {
					target = findReorderTarget(that.columns, target, sources[0], before, that.columns);
					if (target) if (sourceLockedColumns > 0 && targetLockedColumns === 0) that._reorderHeader(leafs, target, before, that.thead);
					else if (sourceLockedColumns === 0 && targetLockedColumns > 0) that._reorderHeader(leafs, target, before, that.lockedHead);
					else that._reorderHeader(leafs, target, before);
				}
			} else if (sourceLockedColumns !== targetLockedColumns) updateCellRowSpan(ths[sourcePosition.cell], that.columns, sourceLockedColumns);
			reorder(ths, sourcePosition.cell, destPosition.cell, before, sources.length);
		},
		_reorderContent: function(sources, destination, before) {
			var that = this;
			var lockedRows = $();
			var source = sources[0];
			var visibleSources = visibleColumns(sources);
			var sourceIndex = inArray(source, leafColumns(that.columns));
			var destIndex = inArray(destination, leafColumns(that.columns));
			var colSourceIndex = inArray(visibleSources[0], visibleLeafColumns(that.columns));
			var colDest = inArray(destination, visibleLeafColumns(that.columns));
			var lockedCount = lockedColumns(that.columns).length;
			var isLocked = !!destination.locked;
			var footer = that.footer || that.wrapper.find(".k-grid-footer");
			var headerCol = footerCol = colDest, footerCol, beforeVisibleColumn;
			if (destination.hidden) {
				var columnsArray = isLocked ? lockedColumns(that.columns) : nonLockedColumns(that.columns);
				if (visibleColumns(columnsArray).length > 0) {
					headerCol = footerCol = colDest = this._findClosestVisibleColumnIndex(columnsArray, destIndex);
					beforeVisibleColumn = visibleColumns(columnsArray.slice(destIndex)).length > 0;
				} else if (isLocked) {
					colDest = that.lockedTable.find("colgroup");
					headerCol = that.lockedHeader.find("colgroup");
					footerCol = $(that.lockedFooter).find(">table>colgroup");
				} else {
					colDest = that.tbody.prev();
					headerCol = that.thead.prev();
					footerCol = footer.find(".k-grid-footer-wrap").find(">table>colgroup");
				}
			}
			if (that._hasFilterRow()) reorder(that.wrapper.find(".k-filter-row td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex, before, sources.length);
			if (colSourceIndex >= 0) reorder(elements(that.lockedHeader, that.thead.prev(), COLGROUP), colSourceIndex, headerCol, beforeVisibleColumn ? beforeVisibleColumn : before, visibleSources.length);
			if (that.options.scrollable) {
				if (colSourceIndex >= 0 && !that._hasVirtualColumns()) reorder(elements(that.lockedTable, that.tbody.prev(), COLGROUP), colSourceIndex, colDest, beforeVisibleColumn ? beforeVisibleColumn : before, visibleSources.length);
			}
			if (footer && footer.length) {
				if (colSourceIndex >= 0) reorder(elements(that.lockedFooter, footer.find(".k-grid-footer-wrap"), ">table>colgroup>col:not(.k-group-col,.k-hierarchy-col)"), colSourceIndex, footerCol, beforeVisibleColumn ? beforeVisibleColumn : before, visibleSources.length);
				reorder(footer.find(".k-footer-template>td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex, before, sources.length);
			}
			var rows = that.tbody.children(":not(.k-grouping-row,.k-detail-row)");
			if (that.lockedTable) {
				if (lockedCount > destIndex) {
					if (lockedCount <= sourceIndex) updateColspan(that.lockedTable.find(">tbody>tr.k-grouping-row:not([hidden])"), that.table.find(">tbody>tr.k-grouping-row:not([hidden])"), sources.length);
				} else if (lockedCount > sourceIndex) updateColspan(that.table.find(">tbody>tr.k-grouping-row:not([hidden])"), that.lockedTable.find(">tbody>tr.k-grouping-row:not([hidden])"), sources.length);
				lockedRows = that.lockedTable.find(">tbody>tr:not(.k-grouping-row,.k-detail-row)");
			}
			for (var idx = 0, length = rows.length; idx < length; idx += 1) reorder(elements(lockedRows[idx], rows[idx], ">td:not(.k-group-cell,.k-hierarchy-cell)"), sourceIndex, destIndex, before, sources.length);
		},
		_findClosestVisibleColumnIndex: function(columns, columnIndex) {
			var closestVisibleColumn = visibleColumns(visibleColumns(columns.slice(columnIndex)).length > 0 ? columns.slice(columnIndex) : columns.slice(0, columnIndex + 1).reverse())[0];
			return inArray(closestVisibleColumn, visibleColumns(this.columns));
		},
		_autoFitLeafColumn: function(leafIndex) {
			this.autoFitColumn(leafColumns(this.columns)[leafIndex]);
		},
		_hasReorderableRows: function() {
			return this.options.reorderable && this.options.reorderable.rows;
		},
		_draggableRows: function() {
			var that = this, selectable = that._checkBoxSelection || that.options.selectable && !kendo.ui.Selectable.parseOptions(that.options.selectable).cell, clickMoveClick = false, isMobile = !!(that._isMobile || kendo.support.mobileOS);
			if (that._draggableRowsInstance) that._draggableRowsInstance.destroy();
			if (this.options.reorderable.rows.clickMoveClick !== false && this._hasDragHandleColumn) clickMoveClick = true;
			that._draggableRowsInstance = that.tbody.kendoDraggable({
				holdToDrag: isMobile,
				showHintOnHold: isMobile,
				preventOsHoldFeatures: isMobile,
				group: "row-draggable",
				autoScroll: true,
				filter: (selectable ? " > .k-selected" : " > tr:not(.k-grouping-row):not(.k-detail-row):not(.k-footer-template):not(.k-group-footer):visible") + (that._hasDragHandleColumn ? " > [ref-grid-drag-cell]" : ":not(:has([data-container-for]))"),
				hint: function(target) {
					var hint = $("<div class=\"k-reorder-clue k-drag-clue\">" + kendo.ui.icon({
						icon: "cancel",
						iconClass: "k-drag-status"
					}) + "</div>");
					if (selectable && that.select().length > 1 && that.lockedContent) hint.append("<span>" + that.select().length / 2 + " " + encode(that.options.messages.itemsSelected) + "</span>");
					else if (selectable && that.select().length > 1 && !that.lockedContent) hint.append("<span>" + that.select().length + " " + encode(that.options.messages.itemsSelected) + "</span>");
					else {
						var clone = target.closest(ITEMROW).clone();
						clone.find("td.k-command-cell").remove();
						clone.find("td").each(function(index, elm) {
							hint.append("<span>" + elm.innerText.replace(/<(\/?)script([^>]*)>/gi, "") + "&nbsp;</span>");
						});
					}
					return hint;
				},
				clickMoveClick,
				cursorOffset: {
					top: 0,
					left: 0
				}
			}).data("kendoDraggable");
		},
		_reorderableRows: function() {
			var that = this, selectable = that._checkBoxSelection || that.options.selectable && !kendo.ui.Selectable.parseOptions(that.options.selectable).cell;
			if (that.tbody.data("kendoReorderable")) that.tbody.data("kendoReorderable").destroy();
			that.tbody.kendoReorderable({
				smartPosition: false,
				draggable: that._draggableRowsInstance,
				dragOverContainers: function(sourceIndex, targetIndex) {
					var result = true, target = $(ITEMROW, that.content).eq(targetIndex);
					if (selectable) result = !target.is(".k-selected");
					return result;
				},
				inSameContainer: function(e) {
					if (selectable) return !$(e.target).is(".k-selected");
					return true;
				},
				dropFilter: "> tr:not(.k-grouping-row):not(.k-detail-row):not(.k-footer-template):not(.k-group-footer):visible",
				allowIcon: "insert-middle",
				orientation: "vertical",
				reorderDropCue: $("<div class=\"k-drop-hint k-drop-hint-h\"><div class=\"k-drop-hint-start\"></div><div class=\"k-drop-hint-line\"></div></div>"),
				positionDropCue: function(reorderDropCue, dropTarget) {
					var firstCellLeft = kendo.getOffset(dropTarget.children(DATA_CELL).eq(0)).left;
					reorderDropCue.css({
						transform: "translate(0,-50%)",
						left: firstCellLeft
					});
				},
				externalDraggable: function(e) {
					var draggable = e.draggable;
					if (draggable) return draggable;
				},
				change: function(e) {
					that._triggerRowRorder(e);
				}
			});
		},
		_triggerRowRorder: function(e) {
			var that = this, args = {
				newIndex: e.position === "after" ? e.newIndex + 1 : e.newIndex,
				oldIndex: e.oldIndex
			}, row = e.element, selectable = that._checkBoxSelection || that.options.selectable && !kendo.ui.Selectable.parseOptions(that.options.selectable).cell;
			if (selectable && that.select().length > 1) args = extend(args, { rows: that.select() });
			else args = extend(args, { row });
			if (!that.trigger(ROWREORDER, args)) that.reorderRows(selectable ? that.select() : row, args.newIndex);
		},
		reorderRowTo: function(row, index) {
			var that = this, item = that.dataItem(row), oldIndex = row.index();
			if (index < 0 || index === oldIndex) return;
			if (!that.trigger(ROWREORDER, {
				row,
				oldIndex: row.index(),
				newIndex: index
			})) {
				that._rowDropping = true;
				that.dataSource.pushMove(index, [item]);
				that._rowDropping = false;
				if (that._isPinnable()) that._renderPinnedRows();
			}
		},
		reorderRows: function(rows, index) {
			var that = this, dataSource = that.dataSource, rowsLength = that.tbody.children(ITEMROW).length, targetItem = that.dataItem(that.tbody.children(ITEMROW).eq(index)), items = rows.toArray().map(function(row) {
				let dataItem = that.dataItem(row);
				dataItem._isMoved = true;
				return dataItem;
			});
			if (!targetItem) {
				targetItem = that.dataItem(that.tbody.children(ITEMROW).eq(rowsLength - 1));
				index = dataSource.indexOf(targetItem) + 1;
			} else index = dataSource.indexOf(targetItem);
			if (index >= 0) {
				that._rowDropping = true;
				dataSource.pushMove(index, items);
				that._rowDropping = false;
				if (that._isPinnable()) that._renderPinnedRows();
			}
		},
		autoFitColumns: function(columns) {
			var that = this;
			columns = columns || that.columns;
			for (var i = 0; i < columns.length; i++) {
				var column = columns[i];
				if (column.columns) that.autoFitColumns(column.columns);
				else that.autoFitColumn(column);
			}
		},
		autoFitColumn: function(column) {
			var that = this, options = that.options, columns = that.columns, index, th, headerTable, leafCols, isLocked, visibleLocked = that.lockedHeader ? leafDataCells(that.lockedHeader.find(">table>thead")).filter(isCellVisible).length : 0, col, minWidth, contentDiv, scrollLeft, notGroupOrHierarchyCol = "col:not(.k-group-col):not(.k-hierarchy-col)", notGroupOrHierarchyVisibleCell = "td:visible:not(.k-group-cell):not(.k-hierarchy-cell)", thWidth;
			if (typeof column == "number") column = columns[column];
			else if (isPlainObject(column)) column = grep(flatColumns(columns), function(item) {
				return item === column;
			})[0];
			else column = grep(flatColumns(columns), function(item) {
				return item.field === column;
			})[0];
			if (!column || !isVisible(column)) return;
			leafCols = leafColumns(columns);
			minWidth = column.minResizableWidth;
			index = inArray(column, leafCols);
			isLocked = column.locked;
			if (isLocked) headerTable = that.lockedHeader.children("table");
			else headerTable = that.thead.parent();
			th = headerTable.find("[data-index='" + index + "']");
			th.find("a.k-grid-column-menu, a.k-grid-filter-menu");
			var contentTable = isLocked ? that.lockedTable : that.table, footer = that.footer || $();
			if (that.footer && that.lockedContent) footer = isLocked ? that.footer.children(".k-grid-footer-locked") : that.footer.children(".k-grid-footer-wrap");
			var footerTable = footer.find("table").first();
			if (that.lockedHeader && !isLocked) index -= visibleLocked;
			for (var j = 0; j < leafCols.length; j++) if (leafCols[j] === column) break;
			else if (leafCols[j].hidden) index--;
			if (options.scrollable) {
				col = headerTable.find(notGroupOrHierarchyCol).eq(index).add(contentTable.children("colgroup").find(notGroupOrHierarchyCol).eq(index)).add(footerTable.find("colgroup").find(notGroupOrHierarchyCol).eq(index));
				if (!isLocked) {
					contentDiv = contentTable.parent();
					scrollLeft = kendo.scrollLeft(contentDiv);
				}
				if (that._hasVirtualColumns()) index = inArray(column, that.virtualCols);
			} else col = contentTable.children("colgroup").find(notGroupOrHierarchyCol).eq(index);
			var tables = headerTable.add(contentTable).add(footerTable);
			if (browser.safari) th.css("white-space", "initial");
			var oldColumnWidth = outerWidth(th);
			col.width("");
			tables.css("table-layout", "fixed");
			col.width(AUTO);
			tables.addClass("k-autofitting");
			tables.css("table-layout", "");
			thWidth = outerWidth(th);
			var newColumnWidth = Math.ceil(Math.max(thWidth, outerWidth(contentTable.find("tr:not(.k-grouping-row)").eq(0).children(notGroupOrHierarchyVisibleCell).eq(index)), outerWidth(footerTable.find(TR).eq(0).children(notGroupOrHierarchyVisibleCell).eq(index)))) + 1;
			if (minWidth && minWidth > newColumnWidth) newColumnWidth = minWidth;
			col.width(newColumnWidth);
			column.width = newColumnWidth;
			if (browser.safari) th.css("white-space", "");
			if (options.scrollable) {
				var cols = headerTable.find("col"), colWidth, totalWidth = 0;
				for (var idx = 0, length = cols.length; idx < length; idx += 1) {
					colWidth = cols[idx].style.width;
					if (colWidth && colWidth.indexOf("%") == -1) totalWidth += parseInt(colWidth, 10);
					else if (cols.eq(idx).hasClass("k-group-col")) totalWidth += parseInt(cols.eq(idx).width(), 10);
					else {
						totalWidth = 0;
						break;
					}
				}
				if (totalWidth) tables.each(function() {
					this.style.width = totalWidth + PX;
				});
			}
			tables.removeClass("k-autofitting");
			if (scrollLeft) kendo.scrollLeft(contentDiv, scrollLeft);
			that.trigger(COLUMNRESIZE, {
				column,
				oldWidth: oldColumnWidth,
				newWidth: newColumnWidth
			});
			that._applyLockedContainersWidth();
			that._syncLockedContentHeight();
			that._syncLockedHeaderHeight();
			that._updateStickyColumns();
			that._syncStickyGroupColgroups();
		},
		reorderColumn: function(destIndex, column, before) {
			var that = this, parent = columnParent(column, that.columns), columns = parent ? parent.columns : that.columns, sourceIndex = inArray(column, columns), destColumn = columns[destIndex], virtualScroll = that.virtualScroll || {}, lockChanged, isLocked = !!destColumn.locked, lockedCount = lockedColumns(that.columns).length, groupHeaderColumnTemplateColumns = grep(leafColumns(that.columns), function(column) {
				return column.groupHeaderColumnTemplate;
			});
			if (that._isStackedMode()) return;
			if (sourceIndex === destIndex) return;
			if (!column.locked && isLocked && nonLockedColumns(that.columns).length == 1) return;
			if (column.locked && !isLocked && lockedCount == 1) return;
			that._hideResizeHandle();
			if (before === undefined) before = destIndex < sourceIndex;
			var sourceColumns = [column];
			that._reorderHeader(sourceColumns, destColumn, before);
			if (that.lockedHeader) {
				removeEmptyRows(that.thead);
				removeEmptyRows(that.lockedHeader);
			}
			if (destColumn.columns) {
				destColumn = leafColumns(destColumn.columns);
				destColumn = destColumn[before ? 0 : destColumn.length - 1];
			}
			if (column.columns) sourceColumns = leafColumns(column.columns);
			that._reorderContent(sourceColumns, destColumn, before);
			lockChanged = !!column.locked;
			lockChanged = lockChanged != isLocked;
			column.locked = isLocked;
			columns.splice(before ? destIndex : destIndex + 1, 0, column);
			columns.splice(sourceIndex < destIndex ? sourceIndex : sourceIndex + 1, 1);
			that._updateLockedCols();
			that._updateCols();
			that._templates();
			that._updateColumnCellIndex();
			that._updateColumnSorters();
			if (groupHeaderColumnTemplateColumns.length > 0) that._renderGroupRows();
			that._updateTablesWidth();
			that._applyLockedContainersWidth();
			that._syncLockedHeaderHeight();
			that._syncLockedContentHeight();
			that._updateFirstColumnClass();
			that._updateStickyColumns();
			that._syncStickyGroupColgroups();
			that._renderPinnedRows();
			if (virtualScroll.columns) that.refresh();
			if (!lockChanged) return;
			if (isLocked) that.trigger(COLUMNLOCK, { column });
			else that.trigger(COLUMNUNLOCK, { column });
		},
		_updateColumnCellIndex: function() {
			var header;
			var offset = 0;
			if (this.lockedHeader) {
				header = this.lockedHeader.find("thead");
				offset = updateCellIndex(header, lockedColumns(this.columns));
			}
			updateCellIndex(this.thead, nonLockedColumns(this.columns), offset);
		},
		lockColumn: function(column) {
			var columns = this.columns;
			if (typeof column == "number") column = columns[column];
			else column = grep(columns, function(item) {
				return item.field === column;
			})[0];
			if (!column || column.locked || column.hidden) return;
			if (column.sticky) this.unstickColumn(columns.indexOf(column));
			var index = lockedColumns(columns).length - 1;
			this.reorderColumn(index, column, false);
		},
		unlockColumn: function(column) {
			var columns = this.columns;
			if (typeof column == "number") column = columns[column];
			else column = grep(columns, function(item) {
				return item.field === column;
			})[0];
			if (!column || !column.locked || column.hidden) return;
			var index = lockedColumns(columns).length;
			this.reorderColumn(index, column, true);
		},
		stickColumn: function(column) {
			var columns = this.columns;
			if (typeof column == "number") column = columns[column];
			else column = grep(columns, function(item) {
				return item.field === column;
			})[0];
			if (!column || column.sticky || column.hidden) return;
			if (column.locked) {
				this.unlockColumn(columns.indexOf(column));
				if (column.locked) return;
			}
			column.sticky = true;
			this._updateStickyColumns();
		},
		unstickColumn: function(column) {
			var columns = this.columns;
			if (typeof column == "number") column = columns[column];
			else column = grep(columns, function(item) {
				return item.field === column;
			})[0];
			if (!column || !column.sticky || column.locked || column.hidden) return;
			this._removeStickyAttributes([column]);
			this._removeStickyStyles(stickyColumns(columns));
			column.sticky = false;
			this._updateStickyColumns();
			if (this._anyStickyColumns() === 0) {
				this._templates();
				if (this._hasFilterRow()) this._updateStickyFilterCells();
			}
		},
		cellIndex: function(td) {
			const that = this;
			var lockedColumnOffset = 0;
			const selector = that._isStackedMode() ? "div.k-grid-stack-cell" : "td";
			if (this.lockedTable && !$.contains(this.lockedTable[0], td[0])) lockedColumnOffset = leafColumns(lockedColumns(this.columns)).length;
			return $(td).parent().children(selector + ":not(.k-group-cell,.k-hierarchy-cell)").index(td) + lockedColumnOffset;
		},
		_modelForContainer: function(container) {
			container = $(container);
			if (!container.is(TR) && this._editMode() !== "popup") container = container.closest(TR);
			var id = container.attr(kendo.attr("uid")) || container.find("[ref='popup-edit-form']").attr(kendo.attr("uid"));
			return this.dataSource.getByUid(id);
		},
		_calculateColumnIndex: function(cell) {
			if (this._hasVirtualColumns()) {
				let virtualOffset = parseInt($(cell).closest(TR).find("td").first().attr("colspan"), 10);
				virtualOffset = virtualOffset > 1 ? virtualOffset - 1 : 0;
				const dataField = cell.data("field");
				return (this.thead?.find("th[data-field='" + dataField + "']")).data("index") ?? this.cellIndex(cell) + virtualOffset;
			}
			return this.cellIndex(cell);
		},
		_editable: function() {
			let that = this, editable = that.options.editable, handler = function() {
				let target = activeElement(), cell = that._editContainer;
				if (cell && cell[0] && !$.contains(cell[0], target) && cell[0] !== target && !$(target).closest(".k-animation-container").length) if (that.editable.end()) {
					that.closeCell();
					that._toggleToolbarEditingItemsVisibility();
				} else that._scrollVirtualWrapper();
			}, mobileOS = kendo.support.mobileOS, useDoubleTapEditing = !!(that._isMobile || mobileOS), userEventsPreventDefault = mobileOS && mobileOS.ios && (mobileOS.browser === "chrome" || mobileOS.browser === "edge");
			that._isEditableEnabled = that._isEditableEnabled !== undefined ? that._isEditableEnabled : !editable.readonly;
			const stacked = that._isStackedMode();
			if (!that._isEditableEnabled) that._removeEditableClickHandlers();
			if (editable && that._isEditableEnabled) {
				if (that._editMode() === "incell") {
					that.table.add(that.lockedTable).on("mousedown.kendoGrid", "tr:not(.k-footer-template):visible>:not(.k-group-cell):not(.k-detail-cell):not(.k-hierarchy-cell):visible", function(e) {
						let target = $(e.target);
						if (that._editMode() === "incell" && target.hasClass("k-checkbox") && target.prev().attr(kendo.attr("bind"))) e.preventDefault();
					});
					if (editable.update !== false) {
						if (isMac) that.wrapper.on("click.kendoGrid", ".k-edit-cell > input[type='checkbox']", function(e) {
							$(e.target).trigger("focus");
						}).on("click.kendoGrid", ".k-edit-cell", function(e) {
							if (!$(e.target).is("input")) $(e.currentTarget).find("input[type='checkbox']").trigger("focus");
						}).on("mousedown.kendoGrid", "tr:not(.k-grouping-row) > td", function(e) {
							var editContainer = that._editContainer;
							if (editContainer && editContainer[0] && ($.contains(editContainer[0], e.target) || editContainer[0] === e.target)) that._mousedownOnEditCell = true;
							else that._mousedownOnEditCell = false;
						});
						that.editableUserEvents = new kendo.UserEvents(that.wrapper, {
							filter: stacked ? ".k-grid-stack-row:not(.k-grouping-row) div.k-grid-stack-cell" : "tr:not(.k-grouping-row) > td",
							allowSelection: true,
							preventDefault: userEventsPreventDefault,
							supportDoubleTap: useDoubleTapEditing,
							fastTap: useDoubleTapEditing,
							[useDoubleTapEditing ? "doubleTap" : "tap"]: function(e) {
								var td = $(e.target), isLockedCell = that.lockedTable && td.closest("table")[0] === that.lockedTable[0];
								that._mousedownOnEditCell = false;
								if (td.hasClass("k-hierarchy-cell") || td.hasClass("k-detail-cell") || td.hasClass("k-group-cell") || td.hasClass("k-edit-cell") || td.hasClass("k-drag-cell") || td.hasClass("k-grid-stack-edit-cell") || td.has(".k-grid-remove-command").length || td.closest("tbody")[0] !== that.tbody[0] && !isLockedCell || $(e.target).is(":input")) return;
								let index;
								if (stacked) index = td.parent().children().index(td);
								if (that.editable) if (that.editable.end()) {
									$(activeElement()).trigger("blur");
									that.closeCell();
									if (!that._requestInProgress) that.editCell(td, index);
								} else that._scrollVirtualWrapper();
								else that.editCell(td, index);
							}
						});
						that.wrapper.on("focusin.kendoGrid", function() {
							if (!$.contains(this, activeElement())) {
								clearTimeout(that._timer);
								that._timer = null;
							}
						}).on("focusout.kendoGrid", function(e) {
							var shouldCloseCell = true;
							if (isMac && that._mousedownOnEditCell || that._virtualColScroll) shouldCloseCell = false;
							that._mousedownOnEditCell = false;
							if (shouldCloseCell) that._timer = setTimeout(function() {
								handler();
							}, 1);
						});
					}
				} else if (editable.update !== false) {
					that._editCommandClickHandler = that._editCommandClick.bind(that);
					that.wrapper.on("click.kendoGrid", "tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-edit-command", that._editCommandClickHandler);
					if (that._isVirtualInlineEditable()) that.wrapper.on("focusout.kendoGrid", "tr:not(.k-grouping-row) > td", function() {
						if (that.editable && !that.editable.end()) that._scrollVirtualWrapper();
					});
				}
				that._removeCommandClickHandler = that._removeCommandClick.bind(that);
				that.wrapper.on("click.kendoGrid", "tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-remove-command", that._removeCommandClickHandler);
			}
		},
		_getLastSelectedItemModel: function() {
			const that = this;
			if (!that.options.selectable) return;
			const selectedData = that.getSelectedData();
			if (!selectedData || !selectedData.length) return;
			const selected = selectedData[selectedData.length - 1];
			return that.dataSource.getByUid(selected.uid);
		},
		_removeToolbarClick: function(e) {
			if (e.event) {
				e.event.preventDefault();
				e.event.stopPropagation();
			} else e.preventDefault();
			const that = this;
			const model = this._getLastSelectedItemModel();
			if (!model) return;
			const row = that.tbody.children("[" + kendo.attr("uid") + "=" + model.uid + "]");
			that.removeRow(row);
		},
		_editToolbarClick: function(e) {
			if (e.event) {
				e.event.preventDefault();
				e.event.stopPropagation();
			} else e.preventDefault();
			const that = this;
			if (that._editMode() === "incell") return;
			const model = this._getLastSelectedItemModel();
			if (!model) return;
			that.editRow(model);
		},
		_editCommandClick: function(e) {
			let that = this, element = $(e.currentTarget);
			if (!that._belongsToGrid(element)) return;
			e.preventDefault();
			that.editRow(element.closest(TR));
		},
		_removeCommandClick: function(e) {
			let that = this, editable = that.options.editable, element = $(e.currentTarget);
			if (!that._belongsToGrid(element)) return;
			if (editable.destroy !== false) {
				e.preventDefault();
				e.stopPropagation();
				that.removeRow(element.closest(TR));
			} else {
				e.stopPropagation();
				if (!that._confirmation()) e.preventDefault();
			}
			that._toggleToolbarEditingItemsVisibility();
		},
		editCell: function(cell, index) {
			cell = $(cell);
			let that = this, colIndex = index === 0 || index ? index : that._calculateColumnIndex(cell), column = leafColumns(that.columns)[colIndex], model = that._modelForContainer(cell);
			const stacked = that._isStackedMode();
			const editClass = stacked ? "k-grid-stack-edit-cell" : "k-edit-cell";
			let isPinnedCell = cell.closest(".k-grid-pinned-container").length > 0;
			let pinnedUid, pinnedCellIndex;
			if (!isPinnedCell && cell[0] && !cell[0].isConnected && cell.closest("tr").data("uid")) isPinnedCell = true;
			if (isPinnedCell) {
				pinnedUid = cell.closest("tr").data("uid");
				pinnedCellIndex = cell.parent().children("td").index(cell);
			}
			that.closeCell();
			if (isPinnedCell && pinnedUid) {
				const pinnedRow = that.wrapper.find(".k-grid-pinned-container tr[data-uid='" + pinnedUid + "']");
				if (pinnedRow.length) cell = pinnedRow.children("td").eq(pinnedCellIndex);
			}
			const cellToEdit = stacked ? cell.find(".k-grid-stack-content") : cell;
			if (model && isColumnEditable(column, model) && !column.command) {
				if (that.trigger(BEFOREEDIT, { model })) return;
				that._attachModelChange(model);
				that._editContainer = cellToEdit;
				if (that._shouldClearEditableState) that._clearEditableState();
				cell.addClass(editClass);
				const skipFocus = (that._isVirtualIncellEditable() || that._hasVirtualColumns()) && that._editableState;
				that.editable = cellToEdit.kendoEditable({
					fields: editField(column, that._isAdaptive() ? "auto" : "none"),
					model,
					size: that.options.size,
					target: that,
					change: function(e) {
						if (that.trigger(SAVE, {
							values: e.values,
							container: cell,
							model
						})) e.preventDefault();
					},
					skipFocus
				}).data("kendoEditable");
				let tr = cell.closest(TR);
				if (!stacked) tr.addClass("k-grid-edit-row");
				if (model.new === true && !stacked) {
					tr.addClass("k-grid-add-row");
					delete model.new;
				}
				if (that.lockedContent) {
					adjustRowHeight(tr[0], that._relatedRow(tr).addClass("k-grid-edit-row")[0]);
					if (tr.hasClass("k-grid-add-row")) that._relatedRow(tr).addClass("k-grid-add-row");
					that._syncLockedScroll();
				}
				if (isPinnedCell && that.options.navigatable) that._setCurrent(cell, false, true);
				that.trigger(EDIT, {
					container: cell,
					model
				});
				that._toggleToolbarEditingItemsVisibility();
			}
		},
		enableEditing: function() {
			let that = this, toolbar = that.wrapper.find(".k-grid-toolbar");
			if (!that._isEditableEnabled) {
				that._isEditableEnabled = true;
				that._editable();
				let addButton = toolbar.find(".k-grid-add").getKendoButton();
				let editButton = toolbar.find(".k-grid-edit-command").getKendoButton();
				let removeButton = toolbar.find(".k-grid-remove-command").getKendoButton();
				let cancelChangesButton = toolbar.find(".k-grid-cancel-changes").getKendoButton();
				let saveChangesButton = toolbar.find(".k-grid-save-changes").getKendoButton();
				let cancelButton = toolbar.find(".k-grid-cancel-command").getKendoButton();
				let saveButton = toolbar.find(".k-grid-save-command").getKendoButton();
				if (addButton) addButton.bind("click", that._createClickHandler);
				if (editButton) editButton.bind("click", that._editClickHandler);
				if (removeButton) removeButton.bind("click", that._destroyClickHandler);
				if (cancelChangesButton) cancelChangesButton.bind("click", that._cancelClickHandler);
				if (saveChangesButton) saveChangesButton.bind("click", that._saveClickHandler);
				if (cancelButton) cancelButton.bind("click", that._editCancelClickHandler);
				if (saveButton) saveButton.bind("click", that._updateClickHandler);
			}
			that._toggleToolbarEditingItemsVisibility();
		},
		disableEditing: function() {
			let that = this;
			if (that._isEditableEnabled) {
				if (that._editMode() === "incell") that.closeCell();
				else that.cancelRow();
				that._clearEditableState();
				that._destroyEditable();
				if (that.editableUserEvents) {
					that.editableUserEvents.destroy();
					that.editableUserEvents = null;
				}
				that._removeEditableClickHandlers();
				that._isEditableEnabled = false;
				that._toggleToolbarEditingItemsVisibility();
			}
		},
		_removeEditableClickHandlers: function() {
			let that = this, toolbar = that.wrapper.find(".k-grid-toolbar");
			that.wrapper.off("click.kendoGrid", "tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-edit-command", that._editCommandClickHandler);
			that.wrapper.off("click.kendoGrid", "tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-remove-command", that._removeCommandClickHandler);
			toolbar.off("click.kendoGrid", ".k-grid-add", that._createClickHandler);
			toolbar.off("click.kendoGrid", ".k-grid-edit-command", that._editClickHandler);
			toolbar.off("click.kendoGrid", ".k-grid-remove-command", that._destroyClickHandler);
			let addButton = toolbar.find(".k-grid-add").getKendoButton();
			let editButton = toolbar.find(".k-grid-edit-command").getKendoButton();
			let removeButton = toolbar.find(".k-grid-remove-command").getKendoButton();
			let cancelChangesButton = toolbar.find(".k-grid-cancel-changes").getKendoButton();
			let saveChangesButton = toolbar.find(".k-grid-save-changes").getKendoButton();
			let cancelButton = toolbar.find(".k-grid-cancel-command").getKendoButton();
			let saveButton = toolbar.find(".k-grid-save-command").getKendoButton();
			if (addButton) addButton.unbind("click");
			if (editButton) editButton.unbind("click");
			if (removeButton) removeButton.unbind("click");
			if (cancelChangesButton) cancelChangesButton.unbind("click");
			if (saveChangesButton) saveChangesButton.unbind("click");
			if (cancelButton) cancelButton.unbind("click");
			if (saveButton) saveButton.unbind("click");
			that._editCommandClickHandler = null;
			that._removeCommandClickHandler = null;
		},
		_adjustLockedHorizontalScrollBar: function() {
			var table = this.table, content = table.parent();
			var scrollbar = table[0].offsetWidth > content[0].clientWidth ? kendo.support.scrollbar() : 0;
			this.lockedContent.height(content[0].offsetHeight - scrollbar);
		},
		_syncLockedScroll: function() {
			this.lockedContent[0].scrollTop = this.content[0].scrollTop;
			if (this.virtualScrollable) this.lockedContent[0].scrollTop = this.wrapper.find(".k-virtual-scrollable-wrap")[0].scrollTop;
		},
		_syncLockedContentHeight: function() {
			if (this.lockedTable) {
				if (!this.touchScroller) this._adjustLockedHorizontalScrollBar();
				this._adjustRowsHeight(this.table, this.lockedTable);
			}
		},
		_syncLockedHeaderHeight: function() {
			if (this.lockedHeader) {
				var lockedTable = this.lockedHeader.children("table");
				var table = this.thead.parent();
				this._adjustRowsHeight(lockedTable, table);
				syncTableHeight(lockedTable, table);
			}
		},
		_syncLockedFooterHeight: function() {
			if (this.lockedFooter && this.footer && this.footer.length) this._adjustRowsHeight(this.lockedFooter.children("table"), this.footer.find(".k-grid-footer-wrap > table"));
		},
		_destroyEditable: function() {
			let that = this;
			const component = that._editContainer && that._editContainer.length && that._editContainer.closest(".k-window").length ? "kendoWindow" : "kendoActionSheet";
			let destroy = function() {
				if (that.editable) {
					let container = that.editView ? that.editView.element : that._editContainer;
					let window = that._editContainer.data(component);
					if (container) {
						if (window) container = window.wrapper;
						container.off("click.kendoGrid", ".k-grid-cancel-command, button[ref-cancel-button], [ref-actionsheet-action-button]:not(.k-button-primary)", that._editCancelClickHandler);
						container.off("click.kendoGrid", ".k-grid-save-command, button[ref-update-button], [ref-actionsheet-action-button].k-button-primary", that._editUpdateClickHandler);
					}
					that._detachModelChange();
					that.editable.destroy();
					that.editable = null;
					if (window) window.destroy();
					that._editContainer = null;
					that._destroyEditView();
					that._editableIsClosing = null;
				}
			};
			if (that.editable) if (that._editMode() === "popup" && !that._isMobile) if (that._editableIsClosing) that._editContainer.data(component).bind("deactivate", destroy);
			else {
				that._editableIsClosing = true;
				that._editContainer.data(component).bind("deactivate", destroy).close();
			}
			else destroy();
			if (that._confirmDialog) {
				that._confirmDialog.close();
				that._confirmDialog.destroy();
				that._confirmDialog = null;
			}
		},
		_destroyEditView: function() {
			if (this.editView) {
				this.editView.purge();
				this.editView = null;
				this.pane.navigate("");
			}
		},
		_attachModelChange: function(model) {
			var that = this;
			that._modelChangeHandler = function(e) {
				that._modelChange({
					field: e.field,
					model: this
				});
			};
			model.bind("change", that._modelChangeHandler);
		},
		_detachModelChange: function() {
			var that = this, container = that._editContainer, model = that._modelForContainer(container);
			if (model) model.unbind(CHANGE, that._modelChangeHandler);
		},
		closeCell: function(isCancel) {
			let that = this, cell = that._editContainer, column, tr, model, errors;
			if (!cell) return;
			model = that._modelForContainer(cell);
			if (isCancel && that.trigger("cancel", {
				container: cell,
				model
			})) return;
			const stacked = that._isStackedMode();
			const editClass = stacked ? "k-grid-stack-edit-cell" : "k-edit-cell";
			const editCell = cell.closest(DOT + editClass);
			that.trigger(CELLCLOSE, {
				type: isCancel ? "cancel" : "save",
				model,
				container: cell
			});
			const index = stacked ? editCell.parent().children().index(editCell) : that._calculateColumnIndex(cell);
			if (stacked) editCell.removeClass(editClass);
			else cell.removeClass(editClass);
			column = leafColumns(that.columns)[index];
			errors = that.editable && that.editable.validatable && that.editable.validatable.errors();
			if (isCancel && model.dirtyFields && model.dirtyFields[column.field] && errors.length) delete model.dirtyFields[column.field];
			tr = cell.closest(TR).removeClass("k-grid-edit-row");
			if (tr.hasClass("k-grid-add-row")) tr.removeClass("k-grid-add-row");
			if (that.lockedContent) {
				const relatedTr = that._relatedRow(tr);
				relatedTr.removeClass("k-grid-edit-row");
				if (relatedTr.hasClass("k-grid-add-row")) relatedTr.removeClass("k-grid-add-row");
			}
			that._destroyEditable();
			that._displayCell(cell, column, model);
			if (that._shouldClearEditableState) that._clearEditableState();
			that.trigger("itemChange", {
				item: tr,
				data: model,
				ns: ui
			});
			if (that._activeStackedCell) that._setCurrentStackedCell(cell);
			if (that.lockedContent) {
				const rowUID = tr && tr.data("uid");
				const heightValue = (that._cachedRowsHeight && that._cachedRowsHeight[rowUID]) ?? "";
				adjustRowHeight(tr.css(HEIGHT, heightValue)[0], that._relatedRow(tr).css(HEIGHT, heightValue)[0]);
			}
			that._renderPinnedRows();
		},
		_displayCell: function(cell, column, dataItem) {
			var that = this, state = {
				storage: {},
				count: 0
			}, settings = extend({}, kendo.Template, that.options.templateSettings), tmpl = kendo.template(that._cellTmpl(column, state), settings);
			if (state.count > 0) tmpl = tmpl.bind(state.storage);
			cell.empty().html(tmpl(dataItem));
		},
		removeRow: function(row) {
			if (!this._confirmation(row)) return;
			this._removeRow(row);
		},
		_removeRow: function(row) {
			var that = this, model, modelId, key, schema, mode = that._editMode();
			if (mode !== "incell") that.cancelRow();
			row = $(row);
			if (that.lockedContent) row = row.add(that._relatedRow(row));
			row = row.hide();
			if (that.dataSource._isGroupPaged()) that._removeGroupIfEmpty(row);
			model = that._modelForContainer(row);
			if (model != undefined && model.hasOwnProperty("_isMoved")) delete model._isMoved;
			if (model && !that.trigger(REMOVE, {
				row,
				model
			})) {
				schema = that.dataSource.options.schema;
				if (that._selectedIds && schema && schema.model) {
					modelId = that._getSchemaIdField();
					key = model[modelId];
					delete that._selectedIds[key];
				}
				that.dataSource.remove(model);
				if (mode === "inline" || mode === "popup") that.dataSource.sync();
			} else if (mode === "incell") that._destroyEditable();
		},
		_editMode: function() {
			let mode = "incell", editable = this.options.editable;
			if (editable !== true) if (typeof editable == "string") mode = editable;
			else mode = editable.mode || mode;
			return mode;
		},
		editRow: function(row) {
			let model, that = this;
			if (row instanceof ObservableObject) model = row;
			else {
				row = $(row);
				model = that._modelForContainer(row);
			}
			let mode = that._editMode();
			let container;
			const isPinnedRow = row instanceof $ && row.closest(".k-grid-pinned-container").length > 0;
			that.cancelRow();
			if (model) {
				if (isPinnedRow) row = that.wrapper.find(".k-grid-pinned-container tr[" + kendo.attr("uid") + "=" + model.uid + "]");
				else row = that.tbody.children("[" + kendo.attr("uid") + "=" + model.uid + "]");
				that._attachModelChange(model);
				if (mode === "popup") that._createPopupEditor(model);
				else if (mode === "inline") that._createInlineEditor(row, model);
				else if (mode === "incell") {
					const stacked = that._isStackedMode();
					(stacked ? row.find(".k-grid-stack-cell:not(.k-command-cell):not(.k-drag-cell)") : row.children(DATA_CELL)).each(function() {
						let cell = $(this);
						const index = stacked ? cell.parent().children().index(cell) : that._calculateColumnIndex(cell);
						let column = leafColumns(that.columns)[index];
						model = that._modelForContainer(cell);
						if (model && (!model.editable || model.editable(column.field)) && column.field && !column.selectable && !column.draggable && !column.pinnable) {
							that.editCell(cell, index);
							return false;
						}
					});
				}
				if (that.editView) container = that.editView.element;
				else if (mode === "popup") container = that._editContainer?.parent();
				else container = that._editContainer;
				if (container) {
					if (!this._editCancelClickHandler) this._editCancelClickHandler = this._editCancelClick.bind(this);
					container.on("click.kendoGrid", ".k-grid-cancel-command, button[ref-cancel-button], [ref-actionsheet-action-button]:not(.k-button-primary)", this._editCancelClickHandler);
					if (!this._editUpdateClickHandler) this._editUpdateClickHandler = this._editUpdateClick.bind(this);
					container.on("click.kendoGrid", ".k-grid-save-command, button[ref-update-button], [ref-actionsheet-action-button].k-button-primary", this._editUpdateClickHandler);
				}
				that._toggleToolbarEditingItemsVisibility();
			}
		},
		_editUpdateClick: function(e) {
			e.preventDefault();
			if (e.stopPropagation) e.stopPropagation();
			else if (e.event && e.event.stopPropagation) e.event.stopPropagation();
			this.saveRow();
			this._preventOnCloseEditableChanges = this._editMode() === "popup";
			this.one(DATABOUND, () => {
				this._toggleToolbarEditingItemsVisibility();
			});
		},
		_editCancelClick: function(e) {
			var that = this;
			var navigatable = that.options.navigatable;
			var model = that.editable.options.model;
			var container = that.editView ? that.editView.element : that._editContainer;
			e.preventDefault();
			if (e.stopPropagation) e.stopPropagation();
			else if (e.event && e.event.stopPropagation) e.event.stopPropagation();
			if (that.trigger("cancel", {
				container,
				model
			})) return;
			var currentIndex = that.items().index($(that.current()).parent());
			that.cancelRow();
			if (navigatable) {
				that._setCurrent(that.items().eq(currentIndex).children().filter(NAVCELL).first());
				focusTable(that.table, true);
			}
			this._toggleToolbarEditingItemsVisibility();
		},
		_editFields: function(columns, model) {
			var fields = [];
			var column;
			for (var idx = 0; idx < columns.length; idx++) {
				column = columns[idx];
				if (column.selectable || column.command) continue;
				if (isColumnEditable(column, model)) fields.push(editField(column, this._isAdaptive() ? "auto" : "none"));
			}
			return fields;
		},
		_createPopupEditor: function(model) {
			var that = this;
			var html = "<div " + kendo.attr("uid") + "=\"" + model.uid + "\" ref=\"popup-edit-form\"><" + (that._isMobile ? "ul class=\"k-edit-form-container k-listgroup k-listgroup-flush\">" : "div class=\"k-edit-form-container\">");
			var column;
			var command;
			var idx;
			var length;
			var tmpl;
			var updateText;
			var cancelText;
			var updateIconClass;
			var cancelIconClass;
			var tempCommand;
			var columns = leafColumns(that.columns);
			var attr;
			var editMenuGuid = kendo.guid();
			var editable = that.options.editable;
			var template = editable.template;
			var options = isPlainObject(editable) ? editable.window : {};
			var settings = extend({}, kendo.Template, that.options.templateSettings);
			var state;
			var container;
			var buttonsHTML;
			const isAdaptive = that._isAdaptive();
			if (that.trigger(BEFOREEDIT, { model })) return;
			options = options || {};
			if (template) {
				if (typeof template === STRING) template = kendo.unescape(template);
				html += kendo.template(template, settings)(model);
				for (idx = 0, length = columns.length; idx < length; idx++) {
					column = columns[idx];
					if (column.command) {
						tempCommand = getCommand(column.command, "edit");
						if (tempCommand) command = tempCommand;
					}
				}
			} else for (idx = 0, length = columns.length; idx < length; idx++) {
				column = columns[idx];
				if (column.selectable) continue;
				if (!column.command) {
					if (that._isMobile) {
						html += "<li class=\"k-item k-listgroup-item\">";
						if (isColumnEditable(column, model)) {
							html += "<label class=\"k-label k-listgroup-form-row\">";
							html += "<span class=\"k-item-title k-listgroup-form-field-label\">" + (column.title && (that.options.encodeTitles ? htmlEncode(column.title, true) : column.title) || column.field || "") + "</span>";
							html += "<div class=\"k-listgroup-form-field-wrapper\" id=\"" + column.field + "_" + editMenuGuid + "\" " + kendo.attr("container-for") + "=\"" + column.field + "\"></div>";
							html += "</label>";
						} else {
							state = {
								storage: {},
								count: 0
							};
							tmpl = kendo.template(that._cellTmpl(column, state), settings);
							if (state.count > 0) tmpl = tmpl.bind(state.storage);
							html += "<label class=\"k-label k-listgroup-form-row k-no-click\">";
							html += "<span class=\"k-item-title k-listgroup-form-field-label\">" + (column.title && (that.options.encodeTitles ? htmlEncode(column.title, true) : column.title) || column.field || "") + "</span>";
							html += "<span class=\"k-no-editor k-listgroup-form-field-wrapper\">" + tmpl(model) + "</span>";
							html += "</label>";
						}
						html += "</li>";
					}
				} else if (column.command) {
					tempCommand = getCommand(column.command, "edit");
					if (tempCommand) command = tempCommand;
				}
			}
			if (command) {
				if (isPlainObject(command)) {
					if (isPlainObject(command.text)) {
						updateText = command.text.update;
						cancelText = command.text.cancel;
					}
					if (isPlainObject(command.iconClass)) {
						updateIconClass = command.iconClass.update;
						cancelIconClass = command.iconClass.cancel;
					}
					if (command.attr) attr = command.attr;
				}
			}
			if (!cancelText) cancelText = that.options.messages.commands.canceledit;
			if (!updateText) updateText = that.options.messages.commands.update;
			if (!that._isMobile) {
				let updateButton = $(that._createButton({
					name: "update",
					text: updateText,
					attr,
					iconClass: updateIconClass,
					skipCommandClass: true,
					skipTabIndex: true
				})).attr("ref-update-button", "");
				let cancelButton = $(that._createButton({
					name: "canceledit",
					text: cancelText,
					attr,
					iconClass: cancelIconClass,
					skipCommandClass: true,
					skipTabIndex: true
				})).attr("ref-cancel-button", "");
				html += "</div>";
				if (isAdaptive) {
					const editTitle = that.options.messages.commands.edit || "Edit record";
					const addTitle = that.options.messages.commands.add || "Add new record";
					container = that._editContainer = $(html).appendTo("body").eq(0).kendoActionSheet({
						modal: true,
						adaptive: true,
						title: model.id ? editTitle : addTitle,
						fullscreen: that.smallMQL.mediaQueryList.matches,
						closeButton: true,
						actionButtons: [{
							text: cancelText,
							icon: "cancel"
						}, {
							text: updateText,
							icon: "save",
							themeColor: "primary"
						}],
						open: function(e) {
							e.sender.element.removeClass("k-popup");
							that.editable.element.focus();
						},
						close: function(e) {
							e.sender.element.trigger("focus");
							if (!that._preventOnCloseEditableChanges) {
								if (that.trigger("cancel", {
									container,
									model
								})) {
									e.preventDefault();
									return;
								}
							}
							var currentIndex = that.items().index($(that.current()).parent());
							if (!that._preventOnCloseEditableChanges) {
								that._editableIsClosing = true;
								that.cancelRow(false, true);
							}
							if (that.options.navigatable) if (that._isStackedMode()) that._setCurrentStackedCell();
							else {
								that._setCurrent(that.items().eq(currentIndex).children().filter(NAVCELL).first());
								focusTable(that.table, true);
							}
							that._toggleToolbarEditingItemsVisibility();
						}
					});
				} else container = that._editContainer = $(html).appendTo(that.wrapper).eq(0).kendoWindow(extend({
					modal: true,
					resizable: false,
					draggable: true,
					title: that.options.messages.commands.edit || "Edit",
					_footerTemplate: () => `<div class="k-actions k-actions-start k-actions-horizontal k-window-actions">` + updateButton[0].outerHTML + cancelButton[0].outerHTML + `</div>`,
					visible: false,
					close: function(e) {
						if (e.userTriggered) {
							e.sender.element.trigger("focus");
							if (that.trigger("cancel", {
								container,
								model
							})) {
								e.preventDefault();
								return;
							}
							var currentIndex = that.items().index($(that.current()).parent());
							that._editableIsClosing = true;
							that.cancelRow();
							that._toggleToolbarEditingItemsVisibility();
							if (that._isStackedMode()) that._setCurrentStackedCell();
							else {
								that._setCurrent(that.items().eq(currentIndex).children().filter(NAVCELL).first());
								focusTable(that.table, true);
							}
						}
					}
				}, options));
			} else {
				html += "</ul></div>";
				let cancelText = encode(that.options.messages.commands.cancel);
				let updateText = encode(that.options.messages.commands.update);
				that.editView = that.pane.append("<div data-" + kendo.ns + "role=\"view\" class=\"k-grid-edit-form\"><div data-" + kendo.ns + " class=\"k-appbar k-appbar-primary\">" + kendo.html.renderButton(`<button class="k-header-cancel k-grid-cancel-command" title="${cancelText}" aria-label="${cancelText}"></button>`, {
					fillMode: "flat",
					icon: "chevron-left"
				}) + `<span class="k-spacer"></span><span>${encode(that.options.messages.commands.edit || "Edit")}</span><span class="k-spacer"></span>` + kendo.html.renderButton(`<button class="k-header-done k-grid-save-command" title="${updateText}" aria-label="${updateText}"></button>`, {
					fillMode: "flat",
					icon: "check"
				}) + "</div><div data-" + kendo.ns + "role=\"content\" class=\"" + classNames.content + "\">" + html + "</div></div>");
				container = that._editContainer = that.editView.element.find("[ref='popup-edit-form']");
			}
			if (!template && !that._isMobile) {
				that.editable = new ui.Form(that._editContainer.find(".k-edit-form-container"), {
					items: that._editFields(columns, model),
					buttonsTemplate: () => "",
					formData: model,
					size: that._isAdaptive() ? "large" : "medium"
				}).editable;
				that._editContainer.append(buttonsHTML);
				if (isAdaptive) that.editable.element.removeClass("k-edit-form-container");
			} else {
				that.editable = that._editContainer.kendoEditable({
					fields: that._isMobile && !template ? that._editFields(columns, model) : null,
					model,
					clearContainer: false,
					target: that,
					skipFocus: true
				}).data("kendoEditable");
				if (isAdaptive) that.editable.element.find(".k-edit-form-container").removeClass("k-edit-form-container");
			}
			that._openPopUpEditor(isAdaptive);
			that.trigger(EDIT, {
				container,
				model
			});
		},
		_openPopUpEditor: function(isAdaptive) {
			var that = this;
			const component = isAdaptive ? "kendoActionSheet" : "kendoWindow";
			var editor = that._editContainer ? that._editContainer.data(component) : null;
			var windowOptions = (that.options.editable || {}).window || {};
			if (!this._isMobile) {
				if (editor) {
					if (!isAdaptive && !windowOptions.position) editor.center();
					else if (isAdaptive) editor.fullscreen(that.smallMQL.mediaQueryList.matches);
					editor.open();
				}
			} else this.pane.navigate(this.editView, this._editAnimation);
		},
		_createInlineEditor: function(row, model) {
			var that = this;
			var column;
			var cell;
			var command;
			var fields = [];
			const stacked = that._isStackedMode();
			if (that.trigger(BEFOREEDIT, { model })) return;
			if (that.lockedContent) row = row.add(that._relatedRow(row));
			let cells = stacked ? row.children(":not(.k-group-cell,.k-hierarchy-cell)").find(".k-grid-stack-cell:not(.k-drag-cell)") : row.children(":not(.k-group-cell,.k-hierarchy-cell,.k-drag-cell)");
			const columns = stacked ? leafColumns(visibleColumns(that.columns)) : leafColumns(that.columns);
			if (that.options.navigatable && stacked) removeElementsFromTab(cells);
			cells.each(function() {
				cell = $(this);
				column = columns[that._calculateColumnIndex(cell)];
				if (!column) return;
				if (!column.command && isColumnEditable(column, model)) {
					fields.push(editField(column, that._isAdaptive() ? "auto" : "none"));
					let target = stacked ? cell.children(".k-grid-stack-content") : cell;
					target.attr(kendo.attr("container-for"), column.field);
					target.empty();
				} else if (column.command) {
					command = getCommand(column.command, "edit");
					if (command) {
						cell.empty();
						var updateText, cancelText, updateIconClass, cancelIconClass, attr;
						if (isPlainObject(command)) {
							if (isPlainObject(command.text)) {
								updateText = command.text.update;
								cancelText = command.text.cancel;
							}
							if (isPlainObject(command.iconClass)) {
								updateIconClass = command.iconClass.update;
								cancelIconClass = command.iconClass.cancel;
							}
							if (command.attr) attr = command.attr;
						}
						$(that._createButton({
							name: "update",
							text: updateText,
							attr,
							iconClass: updateIconClass,
							skipTabIndex: true
						}) + that._createButton({
							name: "canceledit",
							text: cancelText,
							attr,
							iconClass: cancelIconClass,
							skipTabIndex: true
						})).appendTo(cell);
					}
				}
			});
			that._editContainer = row;
			that._editContainer.addClass("k-grid-edit-row");
			if (model.new === true) {
				that._editContainer.addClass("k-grid-add-row");
				delete model.new;
			}
			if (that._shouldClearEditableState) that._clearEditableState();
			that.editable = new kendo.ui.Editable(that._editContainer, {
				target: that,
				fields,
				size: that.options.size,
				model,
				skipFocus: that._isVirtualInlineEditable() && that._editableState && (that._editableState.field ? true : false) || that._hasVirtualColumns(),
				clearContainer: false
			});
			if (row.length > 1) {
				adjustRowHeight(row[0], row[1]);
				that._applyLockedContainersWidth(true);
			}
			if (stacked) cells.addClass("k-grid-stack-edit-cell");
			that.trigger(EDIT, {
				container: row,
				model
			});
		},
		cancelRow: function(notify) {
			var that = this, container = that._editContainer, model;
			if (container) {
				model = that._modelForContainer(container);
				if (!model || notify && that.trigger("cancel", {
					container,
					model
				})) return;
				that._destroyEditable();
				that.dataSource.cancelChanges(model);
				that._clearEditableState();
				if (that._editMode() !== "popup") that._displayRow(container);
				else that._displayRow(that.tbody.find("[" + kendo.attr("uid") + "=" + model.uid + "]"));
				that._renderPinnedRows();
				that._aria();
			}
		},
		saveRow: function() {
			var that = this;
			var container = this._editContainer;
			var model = this._modelForContainer(container);
			var deferred = $.Deferred();
			var valid;
			if (!container || !this.editable) return deferred.resolve().promise();
			valid = that.editable && that.editable.end();
			if (!valid || this.trigger(SAVE, {
				container,
				model
			})) {
				if (!valid) that._scrollVirtualWrapper();
				return deferred.reject().promise();
			}
			if (this._editMode() === "popup") {
				const selector = "[ref-update-button]";
				let uploadButton = container.find(selector);
				if (!uploadButton?.length) uploadButton = container.parent().find(selector);
				if (uploadButton?.length) uploadButton?.addClass("k-disabled").attr("aria-disabled", true);
			}
			that._clearEditableState();
			return this.dataSource.sync();
		},
		_displayRow: function(row) {
			var that = this, model = that._modelForContainer(row), related, newRow, nextRow, isSelected = row.hasClass(SELECTED), isAlt = row.hasClass("k-table-alt-row");
			if (model) {
				if (that.lockedContent) {
					related = $((isAlt ? that.lockedAltRowTemplate : that.lockedRowTemplate)(model));
					kendo.applyStylesFromKendoAttributes(related, ["display"]);
					that._relatedRow(row.last()).replaceWith(related);
				}
				newRow = $((isAlt ? that.altRowTemplate : that.rowTemplate)(model));
				if (!row.is(":visible")) newRow.hide();
				kendo.applyStylesFromKendoAttributes(newRow, ["display"]);
				if (that._anyStickyColumns()) kendo.applyStylesFromKendoAttributes(newRow, ["left", "right"]);
				row.replaceWith(newRow);
				that.trigger("itemChange", {
					item: newRow,
					data: model,
					ns: ui
				});
				if (related && related.length) that.trigger("itemChange", {
					item: related,
					data: model,
					ns: ui
				});
				if (isSelected && (that.options.selectable || that._checkBoxSelection)) that.select(newRow.add(related));
				if (related) adjustRowHeight(newRow[0], related[0]);
				nextRow = newRow.next();
				if (nextRow.hasClass("k-detail-row") && nextRow.is(":visible")) if (that._isStackedMode()) {
					const expandBtn = newRow.find("[ref=\"expand-detail-button\"]");
					if (expandBtn.length) expandBtn.replaceWith($(kendo.html.renderButton(`<button tabindex='-1' ref="collapse-detail-button" aria-label="${COLLAPSE}">${kendo.htmlEncode(that.options.messages.details.collapse)}</button>`, {
						icon: "minus",
						fillMode: "flat",
						themeColor: "primary"
					})));
				} else {
					const iconEl = newRow.find(".k-hierarchy-cell .k-icon,.k-hierarchy-cell .k-svg-icon");
					if (iconEl.length) kendo.ui.icon(iconEl, { icon: "chevron-down" });
				}
			}
		},
		_showMessage: function(messages, row) {
			var that = this;
			if (!that._isMobile) return window.confirm(messages.title);
			(that._confirmDialog = new kendo.ui.Confirm($("<div />").appendTo(document.body), {
				modal: { preventScroll: true },
				closable: false,
				title: false,
				content: messages.title,
				messages: {
					okText: messages.confirmDelete,
					cancel: messages.cancelDelete
				},
				open: function() {
					if (that.content) {
						that.content.data(OVERFLOW, that.content.css(OVERFLOW));
						that.content.css(OVERFLOW, HIDDEN);
					}
				},
				close: function() {
					if (that.content) that.content.css(OVERFLOW, that.content.data(OVERFLOW));
				}
			})).result.done(function() {
				that._removeRow(row);
			}).fail(function() {
				var confirmDialog = that._confirmDialog;
				if (confirmDialog) {
					confirmDialog.close();
					confirmDialog.destroy();
				}
			});
			return false;
		},
		_confirmation: function(row) {
			var that = this, editable = that.options.editable, confirmation = editable === true || typeof editable === STRING ? that.options.messages.editable.confirmation : editable.confirmation;
			if (isPlainObject(editable) && typeof editable.mode === STRING && typeof confirmation !== FUNCTION && typeof confirmation !== STRING && confirmation !== false) confirmation = that.options.messages.editable.confirmation;
			if (confirmation !== false && confirmation != null) {
				if (typeof confirmation === FUNCTION) confirmation = confirmation(that._modelForContainer(row));
				return that._showMessage({
					confirmDelete: editable.confirmDelete || that.options.messages.editable.confirmDelete,
					cancelDelete: editable.cancelDelete || that.options.messages.editable.cancelDelete,
					title: confirmation === true ? that.options.messages.editable.confirmation : confirmation
				}, row);
			}
			return true;
		},
		cancelChanges: function() {
			var that = this;
			if (that._cachedRowsHeight) {
				that._mapCachedRowsHeight("getByUid", "id");
				that._shouldMapHights = true;
			}
			that.dataSource.cancelChanges();
			if (that._isVirtualEditable()) that._virtualPageToTop(function() {
				that.virtualScrollable.scrollToTop();
			});
		},
		saveChanges: function() {
			var that = this;
			var valid = that.editable && that.editable.end();
			if ((valid || !that.editable) && !that.trigger(SAVECHANGES)) that.dataSource.sync();
			else if (!valid) that._scrollVirtualWrapper();
		},
		addRow: function() {
			var that = this, index, dataSource = that.dataSource, mode = that._editMode(), createAt = that.options.editable.createAt || "", pageSize = dataSource.pageSize(), view = dataSource.view() || [];
			var createAtBottom = createAt.toLowerCase() === BOTTOM;
			var model;
			var virtualEditable = that._isVirtualEditable();
			if (that.editable && that.editable.end() || !that.editable) {
				if (mode != "incell") that.cancelRow();
				index = dataSource.indexOf(view[0]);
				if (createAtBottom) {
					index += view.length;
					if (pageSize && !dataSource.options.serverPaging && pageSize <= view.length) index -= 1;
				}
				if (index < 0) if (dataSource.page() > dataSource.totalPages()) index = (dataSource.page() - 1) * pageSize;
				else index = 0;
				if (that.options.navigatable && mode == "incell") that._removeCurrent();
				if (virtualEditable) that._virtualAddRow();
				else {
					model = dataSource.insert(index, {});
					model.new = true;
					that._editModel(model);
				}
			} else that._scrollVirtualWrapper();
		},
		_editModel: function(model) {
			var that = this;
			var createAt = that.options.editable.createAt || "";
			var mode = that._editMode();
			if (model) {
				var id = model.uid, row = (that.lockedContent ? that.lockedTable : that.table).find("tr[" + kendo.attr("uid") + "=" + id + "]");
				const stacked = that._isStackedMode();
				const cells = stacked ? row.find("td:not(.k-group-cell,.k-hierarchy-cell) .k-grid-stack-cell:not(.k-command-cell)") : row.children("td:not(.k-group-cell,.k-hierarchy-cell)");
				const index = that._firstEditableColumnIndex(row);
				const cell = cells.eq(index);
				if (mode === "inline" && row.length) that.editRow(row);
				else if (mode === "popup") that.editRow(model);
				else if (cell.length) that.editCell(cell, stacked && index);
				if (createAt.toLowerCase() == "bottom" && that.lockedContent) that.lockedContent[0].scrollTop = that.content[0].scrollTop = that.table[0].offsetHeight;
			}
		},
		_virtualAddRow: function() {
			var that = this;
			var createAtBottom = (that.options.editable.createAt || "").toLowerCase() === BOTTOM;
			that._clearEditableState();
			if (createAtBottom) that._virtualAddRowAtBottom();
			else that._virtualAddRowAtTop();
		},
		_virtualAddRowAtTop: function() {
			var that = this;
			var dataSource = that.dataSource;
			var virtualScrollable = that.virtualScrollable;
			var model;
			if (dataSource.page() === 1) {
				model = dataSource.insert(0, {});
				model.new = true;
				that._editModel(model);
				virtualScrollable.scrollToTop();
			} else that._virtualPageToTop(function() {
				model = dataSource.insert(0, {});
				model.new = true;
				that._editModel(model);
				virtualScrollable.scrollToTop();
			});
		},
		_virtualAddRowAtBottom: function() {
			var that = this;
			var dataSource = that.dataSource;
			var virtualScrollable = that.virtualScrollable;
			var index = dataSource.total();
			var model;
			if (dataSource.at(index - 1) instanceof ObservableObject) {
				model = dataSource.insert(index, {});
				model.new = true;
				that._virtualPageToBottom(function() {
					that._editModel(model);
					virtualScrollable.scrollToBottom();
				});
			} else that._virtualPageToBottom(function() {
				model = dataSource.insert(index, {});
				model.new = true;
				that._editModel(model);
				virtualScrollable.scrollToBottom();
			});
		},
		_virtualPageToTop: function(callback) {
			var that = this;
			that._virtualPage(0, that.dataSource.take(), function() {
				callback();
			});
		},
		_virtualPageToBottom: function(callback) {
			var that = this;
			var dataSource = that.dataSource;
			var take = dataSource.take();
			var total = dataSource.total();
			var skip = total > take ? total - take : 0;
			that._virtualPage(skip, take, function() {
				callback();
			});
		},
		_virtualPage: function(skip, take, callback) {
			var that = this;
			if (that._isVirtualEditable()) {
				that.virtualScrollable._preventScroll = true;
				that.virtualScrollable._page(skip, take, callback);
			}
		},
		_firstEditableColumnIndex: function(container) {
			var that = this, column, columns = leafColumns(that.columns), idx, length, model = that._modelForContainer(container);
			for (idx = 0, length = columns.length; idx < length; idx++) {
				column = columns[idx];
				if (model && (!model.editable || model.editable(column.field)) && !column.command && column.field && column.hidden !== true) return idx;
			}
			return -1;
		},
		_clickAdd: function(e) {
			if (e.preventDefault) e.preventDefault();
			this.addRow();
			this._toggleToolbarEditingItemsVisibility();
		},
		_clickCancel: function(e) {
			if (e.preventDefault) e.preventDefault();
			this.cancelChanges();
			this._toggleToolbarEditingItemsVisibility();
		},
		_clickExcel: function(e) {
			const that = this;
			if (e.preventDefault) e.preventDefault();
			that._exportExcel();
		},
		_exportExcel: function(doneCallback) {
			const that = this;
			const deferred = $.Deferred();
			that._isExport = true;
			that._progress(true);
			setTimeout(() => {
				that.saveAsExcel(deferred);
				deferred.always(() => {
					that._progress(false);
					that._isExport = false;
					if (doneCallback && typeof doneCallback === FUNCTION) doneCallback();
				});
			}, 1);
		},
		_clickPdf: function(e) {
			var that = this;
			if (e.preventDefault) e.preventDefault();
			that._exportPdf();
		},
		_exportPdf: function(doneCallback) {
			const that = this;
			that._isExport = true;
			that._pdfInitialized = true;
			that._progress(true);
			var promise = that.saveAsPDF();
			if (promise) promise.done(function() {
				that._progress(false);
				that._isExport = false;
				that._pdfInitialized = false;
				if (doneCallback && typeof doneCallback === FUNCTION) doneCallback();
			});
			else {
				that._progress(false);
				that._isExport = false;
				that._pdfInitialized = false;
			}
		},
		_clickCsv: function(e) {
			if (e.preventDefault) e.preventDefault();
			this._exportCsv();
		},
		_exportCsv: function(doneCallback) {
			const that = this;
			that._isExport = true;
			that._progress(true);
			setTimeout(() => {
				const csvColumns = that._getCSVColumnsInfo();
				that.toCSVString().then(function(csvString) {
					const eventData = { csv: csvString };
					if (!that.trigger("csvExport", eventData)) that._saveCSVToFile(eventData.csv, csvColumns.names);
					that._progress(false);
					that._isExport = false;
					if (doneCallback && typeof doneCallback === FUNCTION) doneCallback();
				});
			}, 1);
		},
		_clickSave: function(e) {
			if (e.preventDefault) e.preventDefault();
			this.saveChanges();
		},
		_searchInput: function(e) {
			var that = this, input = e.currentTarget;
			clearTimeout(that._searchTimeOut);
			that._searchTimeOut = setTimeout(function() {
				that._search(input.value);
			}, 300);
		},
		_pasteToolbarDropDown: function() {
			var that = this;
			if (that.wrapper.find(".k-grid-paste-action").length) that.pasteActionsDropDownList = that.wrapper.find(".k-grid-paste-action").kendoDropDownList({
				dataSource: [{
					value: "insert",
					text: "Paste (Insert)"
				}, {
					value: "replace",
					text: "Paste (Replace)"
				}],
				dataTextField: "text",
				dataValueField: "value",
				_allowFilterPaste: false
			}).data("kendoDropDownList");
		},
		_pushExpression: function(filters, field, value) {
			var that = this, isServerFiltering = that.dataSource.options.serverFiltering, defaultOperators = {
				string: "contains",
				number: "gte",
				date: "gte",
				enums: "eq",
				boolean: "eq"
			}, name = field.name || field, operator = field.operator, modelInfo = that.dataSource.reader.model && that.dataSource.reader.model.fields, fieldInfo = modelInfo && modelInfo[name], parseFn = fieldInfo && fieldInfo.parse, expression = {
				field: name,
				operator: operator || defaultOperators.string,
				value
			};
			if ((operator || isServerFiltering) && fieldInfo && kendo.isFunction(parseFn) && parseFn(value) !== null) extend(expression, {
				operator: operator || defaultOperators[fieldInfo.type],
				value: parseFn(value)
			});
			if (isServerFiltering && fieldInfo && kendo.isFunction(parseFn) && parseFn(value) === null) return;
			filters.push(expression);
		},
		_hasTool: function(selector) {
			const tool = this.wrapper.find(`.k-grid-toolbar ${selector}`);
			return {
				present: tool.length > 0,
				tool
			};
		},
		_initToolbarItemsPopups: function() {
			const that = this;
			const columnsToolbarButton = that._hasTool(".k-toolbar-button.k-grid-column-menu[ref-toolbar-tool]");
			const sortToolbarButton = that._hasTool(".k-toolbar-button.k-grid-sort-tool[ref-toolbar-tool]");
			const filterToolbarTool = that._hasTool(".k-toolbar-button.k-grid-filter-tool[ref-toolbar-tool]");
			const columnChooserTool = that._hasTool(".k-toolbar-button.k-grid-column-chooser[ref-toolbar-tool]");
			const groupToolbarTool = that._hasTool(".k-toolbar-button.k-grid-group-tool[ref-toolbar-tool]");
			const smartBoxTool = that._hasTool("[ref-grid-smartbox-input]");
			if (columnsToolbarButton.present) that._globalColumnsMenu(columnsToolbarButton.tool);
			if (columnChooserTool.present) that._columnChooserTool(columnChooserTool.tool);
			if (sortToolbarButton.present) {
				that._toggleBadge(sortToolbarButton.tool, that.dataSource.sort());
				that._sortToolbarTool(sortToolbarButton.tool);
			}
			if (filterToolbarTool.present) {
				that._toggleBadge(filterToolbarTool.tool, that.dataSource.filter());
				that._filterToolbarTool(filterToolbarTool.tool);
			}
			if (groupToolbarTool.present) {
				const groups = that.dataSource.group();
				that._toggleBadge(groupToolbarTool.tool, groups && groups.length);
				that._groupToolbarTool(groupToolbarTool.tool);
			}
			if (smartBoxTool.present) that._smartBoxTool(smartBoxTool.tool);
		},
		_unbindToolbarTools: function() {
			this.wrapper.find(`
                .k-toolbar-button.k-grid-column-menu[ref-toolbar-tool],
                .k-toolbar-button.k-grid-sort-tool[ref-toolbar-tool],
                .k-toolbar-button.k-grid-filter-tool[ref-toolbar-tool],
                .k-toolbar-button.k-grid-column-chooser[ref-toolbar-tool],
                .k-toolbar-button.k-grid-group-tool[ref-toolbar-tool],
                .k-toolbar-button.k-grid-ai-assistant-tool[ref-toolbar-tool]
            `).each(function() {
				$(this).off("click.kendoGrid");
			});
		},
		_ai: function() {
			const that = this;
			const hasAiTool = that._hasTool(".k-toolbar-button.k-grid-ai-assistant-tool[ref-toolbar-tool]");
			if (hasAiTool.present) that._initAiAssistantWindow(hasAiTool.tool);
		},
		_defaultSearch: function(value) {
			const that = this;
			const smartBoxOptions = that.options.smartBox || {};
			let searchFields = null;
			if (smartBoxOptions && smartBoxOptions.searchSettings && smartBoxOptions.searchSettings.enabled !== false) searchFields = smartBoxOptions.searchSettings.searchFields || null;
			that._search(value, searchFields);
		},
		_smartBoxTool: function(tool) {
			const that = this;
			const smartBoxOptions = that.options.smartBox || {};
			if (!tool.length) return;
			smartBoxOptions.size = that.options.size;
			smartBoxOptions.rounded = "full";
			if (smartBoxOptions.aiAssistantSettings) {
				let serviceOptions = smartBoxOptions.aiAssistantSettings?.service || that.options.ai?.service || null;
				smartBoxOptions.aiAssistantSettings.service = serviceOptions;
				if (serviceOptions) {
					serviceOptions = extend(true, { data: (prompt) => getDefaultAIRequestConfig(prompt, flatColumnsInDomOrder(that.columns)) }, serviceOptions);
					smartBoxOptions.aiAssistantSettings.service = serviceOptions;
					const userAiResponseHandler = smartBoxOptions.aiAssistantResponseSuccess;
					smartBoxOptions.aiAssistantResponseSuccess = function(e) {
						if (typeof userAiResponseHandler === "function") userAiResponseHandler.call(this, e);
						if (!e.isDefaultPrevented()) that.handleAIResponse(e.response);
					};
				}
			}
			const userSearchHandler = smartBoxOptions.search;
			smartBoxOptions.search = function(e) {
				if (typeof userSearchHandler === "function") userSearchHandler.call(this, e);
				if (!e.isDefaultPrevented()) that._defaultSearch(e.searchValue);
			};
			tool.kendoSmartBox(smartBoxOptions);
			const smartBox = tool.data("kendoSmartBox");
			if (smartBox) that._smartBox = smartBox;
		},
		_search: function(value, explicitFields) {
			const that = this;
			if (that._searchTimeOut) that._searchTimeOut = null;
			const options = that.options;
			const searchFields = explicitFields || (options.search ? options.search.fields : null);
			let expression = {
				filters: [],
				logic: "or"
			};
			let fields = searchFields;
			if (!fields) fields = getColumnsFields(options.columns);
			if (that.dataSource.options.endless) {
				that.dataSource.options.endless = null;
				that._endlessPageSize = that.dataSource.options.pageSize;
			}
			if (value) for (let i = 0; i < fields.length; i++) that._pushExpression(expression.filters, fields[i], value);
			else expression = {};
			that.dataSource.filter(expression);
		},
		_toolbar: function() {
			var that = this, wrapper = that.wrapper, toolbar = that.options.toolbar, container, items;
			if (toolbar) {
				that._createClickHandler = that._addClickHandler = that._clickAdd.bind(that);
				that._editClickHandler = that._editToolbarClick.bind(that);
				that._destroyClickHandler = that._removeToolbarClick.bind(that);
				that._editCancelClickHandler = that._editCancelClick.bind(that);
				that._updateClickHandler = that._editUpdateClick.bind(that);
				that._cancelClickHandler = that._clickCancel.bind(that);
				that._saveClickHandler = that._clickSave.bind(that);
				that._excelClickHandler = that._clickExcel.bind(that);
				that._pdfClickHandler = that._clickPdf.bind(that);
				that._csvClickHandler = that._clickCsv.bind(that);
				that._serachHandler = that._searchInput.bind(that);
				container = that.wrapper.find(".k-grid-toolbar");
				if (!container.length) {
					container = $("<div class=\"k-grid-toolbar k-toolbar\" />").prependTo(wrapper);
					if (typeof toolbar === STRING || isFunction(toolbar)) {
						if (typeof toolbar === STRING) toolbar = kendo.template(toolbar).bind(that);
						container.html(toolbar({ grid: that }));
						that._attachToolbarClicks();
					} else if (isArray(toolbar)) {
						items = that._processItems(toolbar);
						container.kendoToolBar({
							navigateOnTab: !that.options.navigatable,
							size: that.options.size,
							items
						});
					} else if (isPlainObject(toolbar)) container.kendoToolBar({
						navigateOnTab: !that.options.navigatable,
						size: that.options.size,
						items: that._processItems(toolbar.items || []),
						overflow: toolbar.overflow
					});
				} else that._attachToolbarClicks();
				if (that._checkBoxSelection) container.on("click.kendoGrid", ".k-select-checkbox", that._headerCheckboxClick.bind(that));
				container.on("input.kendoGrid", ".k-grid-search input", this._serachHandler);
				if (toolbar.overflow && toolbar.overflow.mode === "section") {
					const toolbarElement = container.data("kendoToolBar");
					toolbarElement.bind("overflowOpen", function() {
						const itemsToCheck = toolbarElement.overflowSection.element.find(".k-toolbar-items-list");
						that._toggleToolbarEditingItemsVisibility(itemsToCheck?.children(editableToolbarItemsSelector));
					});
				}
				that._toggleToolbarEditingItemsVisibility();
			}
		},
		_toggleToolbarEditingItemsVisibility: function(itemsToCheck) {
			const that = this;
			const hasChanges = that.dataSource.hasChanges() || that.element.find(".k-dirty").length > 0;
			const toolbar = that.wrapper.find(".k-grid-toolbar");
			if (!toolbar.length) return;
			let toolbarItems;
			if (that.options.toolbar && that.options.toolbar.overflow && that.options.toolbar.overflow.mode === "scroll") toolbarItems = toolbar.find(".k-toolbar-items").children(editableToolbarItemsSelector);
			else if (itemsToCheck) toolbarItems = itemsToCheck;
			else toolbarItems = toolbar.children(editableToolbarItemsSelector);
			const showInactive = that.options.toolbar && that.options.toolbar.showInactiveTools || false;
			const mode = that._editMode();
			const editContainerVisible = that._editContainer && that._editContainer.length > 0 && that._editContainer.is(":visible");
			const method = showInactive ? (element) => {
				element.attr("aria-disabled", true);
				element.addClass("k-disabled");
			} : (element) => element.hide();
			const selected = that.select();
			const hasSelected = selected.length > 0;
			const differentSelectionThanEditing = hasSelected && that._editContainer && that._editContainer.length > 0 && that._editContainer.is(":visible") && that._editContainer[0].getAttribute(kendo.attr("uid")) !== selected[selected.length - 1].getAttribute(kendo.attr("uid"));
			const regex = getToolbarRegex({
				mode,
				hasSelected,
				hasChanges,
				editContainerVisible,
				differentSelectionThanEditing: !differentSelectionThanEditing && differentSelectionThanEditing !== false ? true : differentSelectionThanEditing,
				_editableIsClosing: that._editableIsClosing || false,
				_isEditableEnabled: that._isEditableEnabled || false,
				options: that.options
			});
			toolbarItems.show().removeClass("k-disabled").attr("aria-disabled", "false");
			toolbarItems.each(function() {
				const element = $(this);
				const identifier = element.attr("class");
				if (element.hasClass(FOCUSED)) {
					element.removeClass(FOCUSED);
					focusTable(that.table, true);
				}
				if (identifier && regex && regex.test(identifier)) method(element);
			});
		},
		_attachToolbarClicks: function() {
			var editable = this.options.editable, container = this.wrapper.find(".k-grid-toolbar");
			if (editable && editable.create !== false) container.on("click.kendoGrid", ".k-grid-add", this._createClickHandler).on("click.kendoGrid", ".k-grid-cancel-changes", this._cancelClickHandler).on("click.kendoGrid", ".k-grid-save-changes", this._saveClickHandler);
			if (editable && editable.update !== false) container.on("click.kendoGrid", ".k-grid-edit-command", this._editClickHandler).on("click.kendoGrid", ".k-grid-save-command", this._updateClickHandler).on("click.kendoGrid", ".k-grid-cancel-command", this._editCancelClickHandler);
			if (editable.destroy !== false) container.on("click.kendoGrid", ".k-grid-remove-command", this._destroyClickHandler);
			container.on("click.kendoGrid", ".k-grid-excel", this._excelClickHandler);
			container.on("click.kendoGrid", ".k-grid-pdf", this._pdfClickHandler);
			container.on("click.kendoGrid", ".k-grid-csv", this._csvClickHandler);
		},
		_processItems: function(tools) {
			var that = this, options = that.options, items = [], messages = this.options.messages.commands, itemsCollectionHasSpacer = false;
			tools.map((t) => {
				var command, searchText, icon, className, inputSize, template = "";
				if (typeof t === "string") {
					command = t.toLowerCase();
					t = {};
					if (command !== "aiassistant") t.text = messages[command] || command;
				} else {
					command = t.name || t.text || "";
					let isPredefinedTool = defaultCommands[command.toLowerCase()];
					if (isPredefinedTool) command = command.toLowerCase();
					if (command !== "aiassistant") {
						const defaultText = isPredefinedTool ? resolveCommandText(t, messages) : t.text || command;
						t.text = t.text === "" ? "" : defaultText;
					}
				}
				if (!itemsCollectionHasSpacer && (command === "search" || command === "columns")) {
					itemsCollectionHasSpacer = true;
					items.push({ type: "spacer" });
				}
				if (command.toLowerCase() === "selectall") {
					const label = t.text;
					t.template = t.template || kendo.template(SELECTCOLUMNHEADERTMPL)({
						size: kendo.getValidCssClass("k-checkbox-", "size", that.options.size),
						label
					});
				}
				if (command === "search") {
					searchText = htmlEncode(t.text || messages.search, true);
					icon = t.icon || t.iconClass || "search";
					inputSize = kendo.getValidCssClass("k-input-", "size", that.options.size);
					template += `<span class='k-searchbox k-input ${inputSize} k-grid-search'>`;
					template += kendo.ui.icon({
						icon,
						iconClass: "k-input-icon"
					});
					template += "<input autocomplete='off' placeholder='" + searchText + "' title='" + searchText + "' aria-label='" + searchText + "' class='k-input-inner' />";
					template += "</span>";
					items.push({
						name: "search",
						overflow: "never",
						template
					});
				} else if (command === "smartbox") items.push(extend({}, defaultCommands.smartbox, t, {}));
				else if (command === "paste" && options.allowPaste) items.push({ template: "<input class='k-grid-paste-action' />" });
				else {
					if (!command && !(isPlainObject(t) && t.template)) throw new Error("Custom commands should have name specified");
					t = extend({ type: "button" }, defaultCommands[command], t);
					className = t.className || "k-grid-" + (command || "").replace(/\s/g, "");
					t.attributes = that._processAttr(t.attr);
					delete t.attr;
					if (!!className) {
						if (t.attributes["class"] === undefined) t.attributes["class"] = "";
						t.attributes["class"] += " " + className;
					}
					if (t.template) delete t.type;
					if (!!that["_" + command + "ClickHandler"]) t.click = that["_" + command + "ClickHandler"];
					if (command === "canceledit") t.click = that._editCancelClickHandler;
					items.push(t);
				}
			});
			return items;
		},
		_processAttr: function(attr) {
			var attributes = {}, attrArray;
			if (typeof attr === STRING && attr.length > 0) {
				attrArray = attr.split(" ");
				attrArray.map((a) => {
					var keyValue = a.split("=");
					if (keyValue.length === 2) attributes[keyValue[0]] = keyValue[1].replaceAll("\"", "").replaceAll("'", "");
				});
			} else if (isPlainObject(attr)) attributes = attr;
			return attributes;
		},
		_createButton: function(command) {
			var button, template = command.template || COMMANDBUTTONTMPL, commandName = typeof command === STRING ? command : command.name || command.text, className = defaultCommands[commandName] ? defaultCommands[commandName].className : "k-grid-" + (commandName || "").replace(/\s/g, ""), options = {
				className: command.skipCommandClass ? "" : className,
				text: commandName,
				attr: command.skipTabIndex ? "" : "tabindex=-1",
				iconClass: "",
				size: command.size || this.options.size
			}, messages = this.options.messages.commands, attributeClassMatch;
			if (!commandName && !(isPlainObject(command) && command.template)) throw new Error("Custom commands should have name specified");
			if (isPlainObject(command)) {
				command = extend(true, {}, command);
				command.text = resolveCommandText(command, messages);
				if (command.className && inArray(options.className, command.className.split(" ")) < 0) command.className += " " + options.className;
				else if (command.className === undefined) command.className = options.className;
				if (command.className.indexOf("k-primary") > -1) {
					command.className = command.className.replace("k-primary", "");
					command.themeColor = "primary";
				}
				if (commandName === "edit") {
					command = extend(true, {}, command);
					command.text = isPlainObject(command.text) ? command.text.edit : command.text;
					command.iconClass = isPlainObject(command.iconClass) ? command.iconClass.edit : command.iconClass;
				}
				if (command.attr) {
					if (isPlainObject(command.attr)) command.attr = stringifyAttributes(command.attr);
					if (command.attr instanceof Function) {
						let compiledAttributes = command.attr(command);
						command.attr = stringifyAttributes(compiledAttributes);
					}
					if (typeof command.attr === STRING) {
						attributeClassMatch = command.attr.match(/class="(.+?)"/);
						if (attributeClassMatch && inArray(attributeClassMatch[1], command.className.split(" ")) < 0) command.className += " " + attributeClassMatch[1];
					}
				}
				const additionalOptions = {};
				if (commandName === "edit" || commandName === "update") additionalOptions.themeColor = "primary";
				if (commandName === "destroy") additionalOptions.iconClass = "k-i-x";
				options = extend(true, options, defaultCommands[commandName], additionalOptions, command);
			} else {
				const additionalOptions = { text: messages[commandName] };
				if (commandName === "edit" || commandName === "update") additionalOptions.themeColor = "primary";
				if (commandName === "destroy") additionalOptions.iconClass = "k-i-x";
				options = extend(true, options, defaultCommands[commandName], additionalOptions);
			}
			button = kendo.template(template)(options);
			if (!command.template) return kendo.html.renderButton($(button), options);
			else return button;
		},
		_hasFooters: function() {
			return !!this.footerTemplate || !!this.groupFooterTemplate || this.footer && this.footer.length > 0 || this.wrapper.find(".k-grid-footer").length > 0;
		},
		_groupable: function() {
			var that = this;
			if (that._groupableClickHandler) that.table.add(that.lockedTable).off("click.kendoGrid", that._groupableClickHandler);
			else that._groupableClickHandler = function(e) {
				var element = $(this), groupRow = element.closest(TR);
				var group = that._groupRows ? that._groupRows[that.wrapper.find(".k-grouping-row").index(groupRow)] : {};
				if (element.is(CARET_ALT_DOWN)) {
					if (!that.trigger("groupCollapse", {
						group,
						element: groupRow
					})) that.collapseGroup(groupRow);
				} else if (!that.trigger("groupExpand", {
					group,
					element: groupRow
				})) that.expandGroup(groupRow);
				e.preventDefault();
				e.stopPropagation();
			};
			if (that._isLocked()) that.lockedTable.on("click.kendoGrid", ".k-grouping-row " + CARET_ALT_RIGHT + ", .k-grouping-row a[class*='-i-chevron-down']", that._groupableClickHandler);
			else that.table.on("click.kendoGrid", ".k-grouping-row " + CARET_ALT_RIGHT + ", .k-grouping-row a[class*='-i-chevron-down']", that._groupableClickHandler);
			that._attachGroupable();
		},
		_toggleGroupableHeader: function(condition) {
			const that = this;
			const groupable = that.options.groupable;
			const header = that.wrapper.find("div.k-grouping-header");
			if (!(groupable && groupable.enabled !== false) || !header.length) return;
			if (condition) header.removeClass("k-hidden");
			else header.addClass("k-hidden");
		},
		_attachGroupable: function() {
			var that = this, wrapper = that.wrapper, groupable = that.options.groupable, draggables = "th.k-header:not(.k-group-cell):not(.k-hierarchy-cell)[" + kendo.attr("field") + "]", filter = that.content ? ".k-grid-header:first " + draggables : "table:first>.k-grid-header " + draggables;
			const stacked = that._isStackedMode();
			if (stacked) filter = ".k-grid-stack-cell[data-field]";
			if (groupable && groupable.enabled !== false) {
				if (!wrapper.has("div.k-grouping-header")[0]) $("<div/>").addClass("k-grouping-header").prependTo(wrapper);
				if (that.groupable) that._destroyGroupable();
				if (browser.chrome) {
					wrapper.find("div.k-grouping-header").css("touch-action", NONE);
					wrapper.find(filter).css("touch-action", NONE);
				}
				that.wrapper.children(".k-grid-header").addClass("k-grid-draggable-header");
				that.groupable = new ui.Groupable(wrapper, extend({}, groupable, {
					draggable: !stacked ? that._draggableInstance : false,
					groupContainer: ">div.k-grouping-header",
					dataSource: that.dataSource,
					draggableElements: !stacked ? filter : false,
					filter,
					size: that.options.size,
					allowDrag: that.options.reorderable,
					enableContextMenu: !!that.options.contextMenu,
					removeGroup: function(e) {
						that._showUngroupedColumn(e);
					},
					change: function(e) {
						if (that.trigger("group", { groups: e.groups })) e.preventDefault();
						else {
							that._clearEditableState();
							that._hideGroupedColumns(e.groups);
							if (that.dataSource.options.endless) that._resetEndless();
						}
					},
					_groupableFieldsData: stacked && that.columns
				}));
				if (stacked) that._toggleGroupableHeader(that.dataSource.group().length > 0);
				that._addGroupableOptionsToHeader();
			}
		},
		_showUngroupedColumn: function(group) {
			var columns = leafColumns(this.columns);
			var i;
			for (i = 0; i < columns.length; i++) if (columns[i].uid == group.colID && columns[i].hideOnGroup) this.showColumn(columns[i]);
		},
		_hideGroupedColumns: function(groups) {
			if (!groups) return;
			var columns = leafColumns(this.columns);
			var fields = [];
			var i;
			for (i = 0; i < groups.length; i++) if (groups[i].colID) fields.push(groups[i].colID);
			for (i = 0; i < columns.length; i++) if (fields.indexOf(columns[i].uid) >= 0 && columns[i].hideOnGroup) this.hideColumn(columns[i]);
		},
		_resetEndless: function() {
			var that = this;
			that.dataSource.options.endless = null;
			that._endlessPageSize = that.dataSource.options.pageSize;
			that.dataSource._skip = 0;
			that.dataSource._pageSize = that.dataSource._take = that._endlessPageSize;
			that.dataSource._page = 1;
		},
		_addGroupableOptionsToHeader: function() {
			var that = this;
			var columns = flatColumns(that.columns);
			var columnFieldMap = {};
			var headerCells = that._headerCells();
			var cellFieldAttr = "";
			var headerCell;
			var columnOptions;
			var i;
			for (i = 0; i < columns.length; i++) {
				columns[i].field;
				columnFieldMap[columns[i].field] = columns[i];
			}
			for (i = 0; i < headerCells.length; i++) {
				headerCell = headerCells.eq(i);
				cellFieldAttr = headerCell.attr(kendo.attr(FIELD));
				columnOptions = columnFieldMap[cellFieldAttr];
				if (columnOptions && columnOptions.groupable && columnOptions.groupable.sort) headerCell.data(GROUP_SORT, columnOptions.groupable.sort);
			}
		},
		_destroyGroupable: function() {
			var that = this;
			if (that.groupable && that.groupable.element) that.groupable.element.kendoGroupable("destroy");
			that.groupable = null;
			that._removeGroupableOptionsFromHeader();
		},
		_hasStickyGroupHeaders: function() {
			const groupable = this.options.groupable;
			return groupable && groupable.stickyHeaders && this.options.scrollable;
		},
		_hasStickyGroupFooters: function() {
			const groupable = this.options.groupable;
			return groupable && groupable.stickyFooters && this.options.scrollable;
		},
		_isPinnable: function() {
			return !!this.options.pinnable && !!this.options.scrollable;
		},
		_getPinnableRowLocation: function() {
			const p = this.options.pinnable;
			return isPlainObject(p) && p.pinRowLocation || "both";
		},
		_getIsRowPinnable: function() {
			const p = this.options.pinnable;
			return isPlainObject(p) && typeof p.isRowPinnable === FUNCTION ? p.isRowPinnable : null;
		},
		_checkRowPinnable: function(dataItem) {
			const fn = this._getIsRowPinnable();
			if (!fn) return true;
			return !!fn({ dataItem });
		},
		_initPinColumn: function() {
			const that = this;
			const messages = that.options.messages.commands;
			const pinRowLocation = that._getPinnableRowLocation();
			if (pinRowLocation !== "both") {
				that.wrapper.on("click.kendoGrid", ".k-pin-cell", function(e) {
					const uid = $(e.target).closest(".k-pin-cell").closest("tr").data("uid");
					const dataItem = uid ? that.dataSource.getByUid(uid) : null;
					if (!dataItem || !that._checkRowPinnable(dataItem)) return;
					if (that._getRowPinPosition(dataItem) !== "none") that.unpinRows(dataItem);
					else that.pinRows(dataItem, pinRowLocation);
				});
				return;
			}
			that._pinMenu = new kendo.ui.ContextMenu($("<ul></ul>"), {
				target: that.wrapper,
				filter: ".k-pin-cell",
				showOn: "click",
				copyAnchorStyles: false,
				open: function(e) {
					const uid = $(e.target).closest(".k-pin-cell").closest("tr").data("uid");
					const dataItem = uid ? that.dataSource.getByUid(uid) : null;
					if (!dataItem || !that._checkRowPinnable(dataItem)) {
						e.preventDefault();
						return;
					}
					const position = that._getRowPinPosition(dataItem);
					const items = that._pinMenu.element.find(".k-menu-item");
					items.filter("[data-action=top]").toggle(position !== "top");
					items.filter("[data-action=bottom]").toggle(position !== "bottom");
					items.filter("[data-action=unpin]").toggle(position !== "none");
				},
				select: function(e) {
					const action = $(e.item).data("action");
					const uid = $(e.target).closest(".k-pin-cell").closest("tr").data("uid");
					const dataItem = uid ? that.dataSource.getByUid(uid) : null;
					if (!dataItem) return;
					if (action === "unpin") that.unpinRows(dataItem);
					else if (action === "top") that.pinRows(dataItem, "top");
					else if (action === "bottom") that.pinRows(dataItem, "bottom");
				}
			});
			that._pinMenu.append([
				{
					text: messages.pinTop,
					icon: "pin-top",
					attr: { "data-action": "top" }
				},
				{
					text: messages.pinBottom,
					icon: "pin-bottom",
					attr: { "data-action": "bottom" }
				},
				{
					text: messages.unpin,
					icon: "unpin",
					attr: { "data-action": "unpin" }
				}
			]);
		},
		_updatePinColumnIcons: function() {
			const that = this;
			if (!that._isPinnable()) return;
			that.wrapper.find(`.${PINCELLCLASS}`).each((_, el) => {
				const span = $(el);
				const row = span.closest("tr");
				const dataItem = that.dataItem(row);
				if (!dataItem) return;
				if (!that._checkRowPinnable(dataItem)) {
					span.empty();
					return;
				}
				const position = that._getRowPinPosition(dataItem);
				const iconName = position !== "none" ? "unpin" : "pin";
				const iconClass = position !== "none" ? "k-action-icon" : "";
				span.empty().append(kendo.ui.icon({
					icon: iconName,
					iconClass
				}));
			});
		},
		pinRows: function(targets, position) {
			const that = this;
			if (!arguments.length) return {
				top: (that._pinnedTopRows || []).slice(),
				bottom: (that._pinnedBottomRows || []).slice()
			};
			const items = Array.isArray(targets) ? targets : [targets];
			for (let i = 0; i < items.length; i++) {
				let dataItem = items[i];
				if (!(dataItem instanceof kendo.data.ObservableObject)) dataItem = that.dataItem($(dataItem));
				if (dataItem) that._changePinState(dataItem, position || "bottom", ROWPIN);
			}
		},
		unpinRows: function(targets) {
			const that = this;
			const items = Array.isArray(targets) ? targets : [targets];
			for (let i = 0; i < items.length; i++) {
				let dataItem = items[i];
				if (!(dataItem instanceof kendo.data.ObservableObject)) dataItem = that.dataItem($(dataItem));
				if (dataItem) that._changePinState(dataItem, null, ROWUNPIN);
			}
		},
		_getRowPinPosition: function(dataItem) {
			return getRowPinPosition(dataItem, this._pinnedTopRows, this._pinnedBottomRows, this._getSchemaIdField());
		},
		_changePinState: function(dataItem, position, eventName) {
			const that = this;
			const idField = that._getSchemaIdField();
			if (!idField || !dataItem || !that._isPinnable()) return;
			if (dataItem.rowType && dataItem.rowType !== "data") return;
			if (position && !that._checkRowPinnable(dataItem)) return;
			if (position && that._getRowPinPosition(dataItem) === position) return;
			const key = dataItem[idField];
			let newTop = (that._pinnedTopRows || []).filter((r) => r[idField] !== key);
			let newBottom = (that._pinnedBottomRows || []).filter((r) => r[idField] !== key);
			if (position === "top") newTop = [...newTop, dataItem];
			if (position === "bottom") newBottom = [...newBottom, dataItem];
			const e = {
				pinnedTopRows: newTop,
				pinnedBottomRows: newBottom,
				dataItem,
				position
			};
			if (!that.trigger(eventName, e)) {
				that._pinnedTopRows = newTop;
				that._pinnedBottomRows = newBottom;
				that._renderPinnedRows();
			}
		},
		_initPinnedRows: function() {
			const that = this;
			if (!that._isPinnable()) return;
			if (!that._pinnedTopRows && !that._pinnedBottomRows) {
				that._pinnedTopRows = [];
				that._pinnedBottomRows = [];
				that._resolveInitialPinnedRows();
			}
		},
		_resolveInitialPinnedRows: function() {
			const that = this;
			const result = resolveInitialPinnedRows(that.options.pinnable, that._getSchemaIdField(), (id) => that.dataSource.get(id));
			if (result) {
				that._pinnedTopRows = result.top;
				that._pinnedBottomRows = result.bottom;
			}
		},
		_refreshPinnedReferences: function() {
			const that = this;
			const result = refreshPinnedReferences(that._pinnedTopRows, that._pinnedBottomRows, that.dataSource.data(), that._getSchemaIdField());
			that._pinnedTopRows = result.top;
			that._pinnedBottomRows = result.bottom;
		},
		_createPinnedContainer: function(position) {
			const els = createPinnedContainer(position, kendo.getValidCssClass("k-table-", "size", this.options.size));
			return {
				container: $(els.container),
				wrap: $(els.wrap),
				table: $(els.table),
				tbody: $(els.tbody)
			};
		},
		_createPinnedLockedContent: function() {
			const els = createPinnedLockedContent(kendo.getValidCssClass("k-table-", "size", this.options.size));
			return {
				container: $(els.container),
				table: $(els.table),
				tbody: $(els.tbody)
			};
		},
		_ensurePinnedContainers: function() {
			const that = this;
			const gridContainer = that.content ? that.content.parent() : null;
			if (!gridContainer || !gridContainer.length || !that._isPinnable()) return;
			if (!that._pinnedScrollbarSynced) {
				that._pinnedScrollbarSynced = true;
				that._pinnedScrollbarPadding = kendo.support.scrollbar() + "px";
			}
			const containers = ["_pinnedTop", "_pinnedBottom"];
			[{
				prop: containers[0],
				position: "top",
				insert: "insertBefore"
			}, {
				prop: containers[1],
				position: "bottom",
				insert: "insertAfter"
			}].forEach((cfg) => {
				if (!that[cfg.prop]) {
					const p = that._createPinnedContainer(cfg.position);
					that[cfg.prop] = p;
					p.container[0].style.paddingInlineEnd = that._pinnedScrollbarPadding;
					p.wrap[0].style.boxSizing = "content-box";
					p.container[cfg.insert](gridContainer);
					p.wrap.on("scroll.kendoGrid", (e) => {
						that._syncPinnedScroll(e.target);
					});
					if (that._hasDetails()) that._bindPinnedDetailHandler(p.table);
				}
			});
			if (that._isLocked() && that.lockedContent) containers.forEach((p) => {
				const reference = that[p];
				if (!reference.locked) {
					const content = $("<div class=\"k-grid-content\"/>");
					reference.table.before(content);
					content.append(reference.table);
					reference.content = content;
					const locked = that._createPinnedLockedContent();
					reference.locked = locked.container;
					reference.lockedTable = locked.table;
					reference.lockedTbody = locked.tbody;
					reference.content.before(reference.locked);
					reference.content.on("scroll.kendoGrid", (e) => {
						that._syncPinnedScroll(e.target);
					});
				}
			});
		},
		_bindPinnedDetailHandler: function(table) {
			const that = this;
			const selector = `.k-hierarchy-cell ${CARET_ALT_RIGHT}, .k-hierarchy-cell ${CARET_ALT_DOWN}`;
			table.on("click.kendoGrid", selector, function(e) {
				that._toggleDetails($(this));
				e.preventDefault();
				return false;
			});
		},
		_syncPinnedScroll: function(source) {
			const that = this;
			if (that._isSyncingPinnedScroll) return;
			const top = that._pinnedTop;
			const bottom = that._pinnedBottom;
			that._isSyncingPinnedScroll = true;
			syncPinnedScroll(source, that.content ? that.content[0] : null, (top && (top.content || top.wrap) || $())[0] || null, (bottom && (bottom.content || bottom.wrap) || $())[0] || null);
			that._isSyncingPinnedScroll = false;
		},
		_syncPinnedColgroups: function() {
			const that = this;
			const top = that._pinnedTop;
			const bottom = that._pinnedBottom;
			syncPinnedColgroups(that.table ? that.table[0] : null, top && top.table ? top.table[0] : null, bottom && bottom.table ? bottom.table[0] : null);
			if (that._isLocked() && that.lockedTable) syncPinnedColgroups(that.lockedTable[0], top && top.lockedTable ? top.lockedTable[0] : null, bottom && bottom.lockedTable ? bottom.lockedTable[0] : null);
		},
		_syncPinnedTableWidths: function() {
			const that = this;
			const top = that._pinnedTop;
			const bottom = that._pinnedBottom;
			syncPinnedTableWidths(that.table ? that.table[0] : null, top && top.table ? top.table[0] : null, bottom && bottom.table ? bottom.table[0] : null);
			if (that._isLocked() && that.lockedTable) syncPinnedTableWidths(that.lockedTable[0], top && top.lockedTable ? top.lockedTable[0] : null, bottom && bottom.lockedTable ? bottom.lockedTable[0] : null);
		},
		_syncPinnedLockedWidths: function() {
			const that = this;
			if (!that._isLocked() || !that.lockedContent) return;
			const top = that._pinnedTop;
			const bottom = that._pinnedBottom;
			syncPinnedLockedWidths(that.lockedContent[0].style.width || that.lockedContent[0].offsetWidth + "px", that.content[0].clientWidth + "px", top && top.locked ? top.locked[0] : null, bottom && bottom.locked ? bottom.locked[0] : null, top && top.content ? top.content[0] : null, bottom && bottom.content ? bottom.content[0] : null);
		},
		_buildPinnedRowHtml: function(dataItem, locked, alt) {
			const that = this;
			if (!dataItem || !dataItem.uid) return "";
			let row;
			if (locked) {
				const lockedRowTemplate = that.lockedRowTemplate ? that.lockedRowTemplate(dataItem) : that.rowTemplate(dataItem);
				row = alt && that.lockedAltRowTemplate ? that.lockedAltRowTemplate(dataItem) : lockedRowTemplate;
			} else row = alt && that.altRowTemplate ? that.altRowTemplate(dataItem) : that.rowTemplate(dataItem);
			if (typeof that.options.pinnedRowTemplate === "function") return that.options.pinnedRowTemplate({
				dataItem,
				row
			});
			return row;
		},
		_stripPinnedDetailToggles: function(tbody) {
			if (!tbody || !tbody.length || !this._hasDetails()) return;
			tbody.find(".k-hierarchy-cell").empty().removeAttr("aria-expanded").attr("aria-hidden", "true");
		},
		_stripPinnedDragHandles: function(tbody) {
			if (!tbody || !tbody.length || !this._hasReorderableRows()) return;
			tbody.find(".k-drag-cell").empty().removeAttr("aria-label").removeAttr("ref-grid-drag-cell").removeClass("k-drag-cell").css("cursor", "");
		},
		_getSortedPinnedRows: function(rows) {
			const sort = this.dataSource.sort();
			if (!sort || !sort.length) return rows;
			return new kendo.data.Query(rows).sort(sort).toArray();
		},
		_renderPinnedRows: function() {
			const that = this;
			if (!that._isPinnable()) return;
			that._refreshPinnedReferences();
			const hasTopRows = that._pinnedTopRows.length > 0;
			const hasBottomRows = that._pinnedBottomRows.length > 0;
			const hasPinnedRows = hasTopRows || hasBottomRows;
			if (!hasPinnedRows && !that._pinnedTop && !that._pinnedBottom) return;
			that._ensurePinnedContainers();
			const top = that._pinnedTop;
			const bottom = that._pinnedBottom;
			if (!top || !bottom) return;
			that._syncPinnedColgroups();
			that._syncPinnedTableWidths();
			that._markPinnedSourceRows();
			let pinnedNavInfo = null;
			if (hasPinnedRows && that.options.navigatable && that._current && that._current.length) {
				const cur = that._current;
				if (that._isPinnedContainer(cur)) pinnedNavInfo = {
					uid: cur.closest("tr").data("uid"),
					cellIndex: cur.parent().children("td").index(cur),
					position: that._pinnedPosition(cur)
				};
			}
			that._pinnedNavInfo = pinnedNavInfo;
			const topRows = hasTopRows ? that._getSortedPinnedRows(that._pinnedTopRows) : [];
			const bottomRows = hasBottomRows ? that._getSortedPinnedRows(that._pinnedBottomRows) : [];
			const topHtml = hasTopRows ? flattenRowSpans(topRows.map((item, idx) => that._buildPinnedRowHtml(item, false, idx % 2 === 1)).join("")) : "";
			const bottomHtml = hasBottomRows ? flattenRowSpans(bottomRows.map((item, idx) => that._buildPinnedRowHtml(item, false, idx % 2 === 1)).join("")) : "";
			top.tbody.html(topHtml);
			bottom.tbody.html(bottomHtml);
			that._stripPinnedDetailToggles(top.tbody);
			that._stripPinnedDetailToggles(bottom.tbody);
			that._stripPinnedDragHandles(top.tbody);
			that._stripPinnedDragHandles(bottom.tbody);
			kendo.applyStylesFromKendoAttributes(top.tbody, [
				"display",
				"left",
				"right"
			]);
			kendo.applyStylesFromKendoAttributes(bottom.tbody, [
				"display",
				"left",
				"right"
			]);
			if (that._isStackedMode()) {
				const layoutSettings = that._getStackedLayoutSettings();
				const pinnedStackedRows = top.tbody.find(".k-grid-stack-row").add(bottom.tbody.find(".k-grid-stack-row"));
				if (layoutSettings.colClass) pinnedStackedRows.addClass(layoutSettings.colClass);
				else if (layoutSettings.colsConfig) pinnedStackedRows.css("grid-template-columns", layoutSettings.colsConfig);
			}
			if (that._isLocked() && that.lockedRowTemplate) {
				const topLockedHtml = flattenRowSpans(topRows.map((item, idx) => that._buildPinnedRowHtml(item, true, idx % 2 === 1)).join(""));
				const bottomLockedHtml = flattenRowSpans(bottomRows.map((item, idx) => that._buildPinnedRowHtml(item, true, idx % 2 === 1)).join(""));
				top.lockedTbody.html(topLockedHtml);
				bottom.lockedTbody.html(bottomLockedHtml);
				that._stripPinnedDetailToggles(top.lockedTbody);
				that._stripPinnedDetailToggles(bottom.lockedTbody);
				that._stripPinnedDragHandles(top.lockedTbody);
				that._stripPinnedDragHandles(bottom.lockedTbody);
				that._syncPinnedLockedWidths();
			}
			top.container.toggleClass("k-hidden", that._pinnedTopRows.length === 0);
			bottom.container.toggleClass("k-hidden", that._pinnedBottomRows.length === 0);
			that._setContentHeight();
			that._syncLockedContentHeight();
			if (that.content && that.content[0]) that._syncPinnedScroll(that.content[0]);
			that._updatePinColumnIcons();
			if (hasPinnedRows) {
				that._syncPinnedSelection();
				that._syncPinnedHighlight();
				that._syncPinnedNavigation();
				that._applyPinnedAria();
			}
		},
		_syncPinnedSelection: function() {
			const that = this;
			if (!that._isPinnable() || !that.options.selectable) return;
			const isCell = kendo.ui.Selectable.parseOptions(that.options.selectable).cell;
			const selected = that.select();
			const selectedKeys = {};
			selected.each((_, el) => {
				const $el = $(el);
				const uid = isCell ? $el.closest("tr").data("uid") : $el.data("uid");
				if (uid) selectedKeys[isCell ? `${uid}_${$el.index()}` : uid] = true;
			});
			const selector = isCell ? "tr > td" : "tr";
			const toggle = (el) => {
				const $el = $(el);
				const uid = isCell ? $el.closest("tr").data("uid") : $el.data("uid");
				$el.toggleClass(SELECTED, !!selectedKeys[isCell ? `${uid}_${$el.index()}` : uid]);
			};
			[that._pinnedTop, that._pinnedBottom].forEach((p) => {
				if (!p || !p.tbody) return;
				p.tbody.find(selector).each((_, el) => toggle(el));
				if (p.lockedTbody) p.lockedTbody.find(selector).each((_, el) => toggle(el));
			});
		},
		_syncPinnedHighlight: function() {
			const that = this;
			if (!that._isPinnable()) return;
			const highlighted = that._highlightedItems;
			const highlightedUids = {};
			const highlightedCells = {};
			if (highlighted && highlighted.length) highlighted.each((_, el) => {
				const $el = $(el);
				if ($el.is("tr")) {
					const uid = $el.data("uid");
					if (uid) highlightedUids[uid] = true;
				} else if ($el.is("td")) {
					const uid = $el.closest("tr").data("uid");
					if (uid) {
						if (!highlightedCells[uid]) highlightedCells[uid] = [];
						highlightedCells[uid].push($el.index());
					}
				}
			});
			const applyRowHighlight = (el) => {
				const row = $(el);
				const uid = row.data("uid");
				row.toggleClass(HIGHLIGHTED, !!highlightedUids[uid]);
				row.children("td").removeClass(HIGHLIGHTED);
				if (highlightedCells[uid]) highlightedCells[uid].forEach((idx) => {
					row.children("td").eq(idx).addClass(HIGHLIGHTED);
				});
			};
			const applyHighlight = (tbody) => {
				tbody.children("tr").each((_, el) => applyRowHighlight(el));
			};
			[that._pinnedTop, that._pinnedBottom].forEach((p) => {
				if (!p || !p.tbody) return;
				applyHighlight(p.tbody);
				if (p.lockedTbody) applyHighlight(p.lockedTbody);
			});
		},
		_isPinnedContainer: function(cell) {
			return cell && cell.closest(".k-grid-pinned-container").length > 0;
		},
		_applyPinnedAria: function() {
			const that = this;
			applyPinnedAria([that._pinnedTop, that._pinnedBottom], (uid) => that.dataSource.getByUid(uid), that._getSchemaIdField());
		},
		_syncPinnedNavigation: function() {
			const that = this;
			const current = that._current;
			const info = that._pinnedNavInfo;
			if (!info || !current || !current.length || !that.options.navigatable) return;
			that._pinnedNavInfo = null;
			const p = info.position === "top" ? that._pinnedTop : that._pinnedBottom;
			if (!p || !p.tbody) return;
			const newRow = p.tbody.children(`tr[data-uid='${info.uid}']`);
			if (newRow.length) {
				const newCell = newRow.children("td").eq(info.cellIndex);
				if (newCell.length) that._updateCurrentAttr(current, newCell);
			}
		},
		_pinnedPosition: function(cell) {
			return cell.closest(`.${PINNED_CONTAINER_CLASS}`).hasClass("k-pos-bottom") ? "bottom" : "top";
		},
		_pinnedVerticalCell: function(current, down) {
			const row = current.parent();
			const rows = row.parent().children(NAVROW);
			const rowIndex = rows.index(row);
			const cellIndex = Math.max(row.children(DATA_CELL_HIDDENINCLUDED).index(current), this._lastCellIndex || 0);
			let nextRow;
			if (down) nextRow = rowIndex < rows.length - 1 ? rows.eq(rowIndex + 1) : $();
			else nextRow = rowIndex > 0 ? rows.eq(rowIndex - 1) : $();
			if (!nextRow.length) return $();
			const cells = nextRow.children(DATA_CELL_HIDDENINCLUDED);
			return cells.length > cellIndex ? cells.eq(cellIndex) : cells.last();
		},
		_pinnedBoundaryCellByPosition: function(position, first, cellIndex, navCellIndex) {
			const p = position === "top" ? this._pinnedTop : this._pinnedBottom;
			if (!p || !p.tbody) return $();
			const rows = p.tbody.children(NAVROW);
			if (!rows.length) return $();
			const row = first ? rows.first() : rows.last();
			if (navCellIndex) {
				const navCells = row.children(NAVCELL);
				return navCells.length > cellIndex ? navCells.eq(cellIndex) : navCells.last();
			}
			const cells = row.children(DATA_CELL_HIDDENINCLUDED);
			let cell = cells.length > cellIndex ? cells.eq(cellIndex) : cells.last();
			if (cell.is(".k-group-cell")) cell = cell.nextAll("td").not(".k-group-cell").not(":hidden").first();
			return cell;
		},
		_initPinnedRowEditing: function() {
			const that = this;
			if (!that._isPinnable() || !that.options.editable) return;
			if (that._editMode() === "incell") that.wrapper.on("mousedown.kendoGrid", ".k-grid-pinned-container td", function(e) {
				if ($(e.target).closest(".k-pin-cell").length || $(e.target).is(":input")) return;
				const pinnedCell = $(this);
				const uid = pinnedCell.closest("tr").data("uid");
				if (!uid || pinnedCell.hasClass("k-edit-cell")) return;
				e.preventDefault();
				clearTimeout(that._timer);
				that._timer = null;
				if (that.options.selectable) {
					const selOpts = kendo.ui.Selectable.parseOptions(that.options.selectable);
					const sourceRow = that.tbody.children("tr[data-uid=\"" + uid + "\"]");
					if (sourceRow.length) {
						let source;
						if (selOpts.cell) source = sourceRow.children("td").eq(pinnedCell.index());
						else source = sourceRow;
						if (source.length) {
							if (selOpts.multiple && !e.ctrlKey && !e.metaKey && !e.shiftKey) that.clearSelection();
							that.select(source);
							that._syncPinnedSelection();
						}
					}
				}
				if (that.editable) {
					if (!that.editable.end()) return;
				}
				that.editCell(pinnedCell);
			});
		},
		_initPinnedRowSelection: function() {
			const that = this;
			if (!that._isPinnable() || !that.options.selectable) return;
			const selectableOptions = kendo.ui.Selectable.parseOptions(that.options.selectable);
			const selector = selectableOptions.cell ? ".k-grid-pinned-container td" : ".k-grid-pinned-container tr";
			that.wrapper.on("click.kendoGrid", selector, function(e) {
				if ($(e.target).closest(".k-pin-cell").length) return;
				const pinnedTarget = $(this);
				const uid = (selectableOptions.cell ? pinnedTarget.closest("tr") : pinnedTarget).data("uid");
				if (!uid) return;
				const sourceRow = that.tbody.children(`tr[data-uid="${uid}"]`);
				if (!sourceRow.length) return;
				let source;
				if (selectableOptions.cell) source = sourceRow.children("td").eq(pinnedTarget.index());
				else source = sourceRow;
				if (!source.length) return;
				const toggleOff = source.hasClass(SELECTED) && (e.ctrlKey || e.metaKey);
				if (selectableOptions.multiple && !e.ctrlKey && !e.metaKey && !e.shiftKey) that.clearSelection();
				if (toggleOff) {
					let target = source;
					if (that._isLocked()) target = target.add(selectableOptions.cell ? that._relatedCell(source) : that._relatedRow(source));
					that.selectable._unselect(target);
					that.selectable.trigger(CHANGE);
				} else that.select(source);
				that._syncPinnedSelection();
			});
		},
		_markPinnedSourceRows: function() {
			const that = this;
			if (!that._isPinnable() || !that.tbody) return;
			const idField = that._getSchemaIdField();
			markPinnedSourceRows(that.tbody[0], that._pinnedTopRows, that._pinnedBottomRows, idField);
			if (that._isLocked() && that.lockedTable) markPinnedSourceRows(that.lockedTable.children("tbody")[0], that._pinnedTopRows, that._pinnedBottomRows, idField);
		},
		_destroyPinnedRows: function() {
			const that = this;
			if (that._pinnedTop) {
				if (that._pinnedTop.wrap) that._pinnedTop.wrap.off("scroll.kendoGrid");
				if (that._pinnedTop.content) that._pinnedTop.content.off("scroll.kendoGrid");
				destroyPinnedContainer(null, that._pinnedTop.container);
			}
			that._pinnedTop = null;
			if (that._pinnedBottom) {
				if (that._pinnedBottom.wrap) that._pinnedBottom.wrap.off("scroll.kendoGrid");
				if (that._pinnedBottom.content) that._pinnedBottom.content.off("scroll.kendoGrid");
				destroyPinnedContainer(null, that._pinnedBottom.container);
			}
			that._pinnedBottom = null;
			if (that._pinMenu) {
				that._pinMenu.destroy();
				that._pinMenu = null;
			}
		},
		_createStickyGroupContainer: function(tableClass, extraClasses) {
			const result = createStickyGroupContainer(tableClass, kendo.getValidCssClass("k-table-", "size", this.options.size), extraClasses);
			return {
				container: $(result.container),
				table: $(result.table)
			};
		},
		_removeStickyGroupContainer: function(containerProp, tableProp, unbindClick) {
			if (this[containerProp]) {
				if (unbindClick) this[containerProp].off("click.kendoGrid");
				this[containerProp].remove();
				this[containerProp] = null;
				this[tableProp] = null;
			}
		},
		_syncStickyColgroup: function(targetTable, sourceTable) {
			syncColgroup(targetTable ? targetTable[0] : null, sourceTable[0]);
		},
		_initStickyGroups: function() {
			const that = this;
			const gridContainer = that.content ? that.content.parent() : null;
			if (!gridContainer || !gridContainer.length) return;
			if (that._hasStickyGroupHeaders() && !that._stickyGroupHeaderContainer) {
				const { container, table } = that._createStickyGroupContainer(STICKY_GROUP_HEADER_TABLE_CLASS);
				that._stickyGroupHeaderContainer = container;
				that._stickyGroupHeaderTable = table;
				container.addClass("k-hidden").prependTo(gridContainer);
			}
			if (that._hasStickyGroupFooters() && !that._stickyGroupFooterContainer) {
				const { container, table } = that._createStickyGroupContainer(STICKY_GROUP_FOOTER_TABLE_CLASS, "k-pos-bottom");
				that._stickyGroupFooterContainer = container;
				that._stickyGroupFooterTable = table;
				container.addClass("k-hidden").appendTo(gridContainer);
			}
			that._stickyHeaderItems = [];
			that._stickyFooterItems = [];
			if (!that._stickyGroupsScrollHandler) that._stickyGroupsScrollHandler = () => {
				that._updateStickyGroups();
			};
			if (!that._stickyGroupsClickHandler) that._stickyGroupsClickHandler = (e) => {
				const target = $(e.target).closest(`${CARET_ALT_DOWN}, ${CARET_ALT_RIGHT}`);
				if (!target.length) return;
				const stickyRow = target.closest("tr.k-grouping-row");
				if (!stickyRow.length) return;
				const rowIndex = stickyRow.parent().children().index(stickyRow);
				const items = that._stickyHeaderItems;
				if (!items || rowIndex >= items.length) return;
				const resolved = that._resolveStickyGroupRows(items[rowIndex].headerIndex);
				if (!resolved) return;
				that._toggleStickyGroup(target, resolved.groupRow);
				that._scrollToStickyGroupRow(resolved.originalRow);
				e.preventDefault();
				e.stopPropagation();
			};
			if (that._stickyGroupHeaderContainer) that._stickyGroupHeaderContainer.off("click.kendoGrid").on("click.kendoGrid", that._stickyGroupsClickHandler);
			if (!that._stickyGroupResizeHandler) {
				that._stickyGroupResizeHandler = () => {
					that.resize();
				};
				$(window).on("resize.kendoGrid", that._stickyGroupResizeHandler);
			}
			that._initStickyGroupKeyboardNav();
		},
		_initStickyGroupKeyboardNav: function() {
			const that = this;
			if (!that.options.navigatable) return;
			const containers = [];
			if (that._stickyGroupHeaderContainer) containers.push({
				el: that._stickyGroupHeaderContainer,
				type: "header"
			});
			if (that._stickyGroupFooterContainer) containers.push({
				el: that._stickyGroupFooterContainer,
				type: "footer"
			});
			for (const { el, type } of containers) {
				el.off("mousedown.kendoGridnav");
				el.removeAttr("tabindex");
				el.on("mousedown.kendoGridnav", "td", (e) => {
					if ($(e.target).closest(`${CARET_ALT_DOWN}, ${CARET_ALT_RIGHT}`).length) return;
					const cell = $(e.currentTarget);
					const row = cell.closest("tr");
					that._focusStickyRow(row, type, cell);
					e.preventDefault();
				});
			}
		},
		_stickyGroupVirtualOffset: function() {
			return this.virtualScrollable ? this.virtualScrollable._rangeStart || this.dataSource.skip() || 0 : 0;
		},
		_resolveStickyGroupRows: function(headerIndex) {
			const that = this;
			const skipOffset = that._stickyGroupVirtualOffset();
			const originalRow = $(that.tbody[0].children[headerIndex - skipOffset]);
			if (!originalRow.length) return null;
			let groupRow = originalRow;
			if (that._isLocked() && that.lockedTable) groupRow = $(that.lockedTable.find(">tbody")[0].children[headerIndex - skipOffset]);
			return {
				originalRow,
				groupRow
			};
		},
		_toggleStickyGroup: function(toggle, groupRow) {
			const that = this;
			const isCollapse = toggle.is(CARET_ALT_DOWN);
			const eventName = isCollapse ? "groupCollapse" : "groupExpand";
			const group = that._groupRows ? that._groupRows[that.wrapper.find(".k-grouping-row").index(groupRow)] : {};
			if (!that.trigger(eventName, {
				group,
				element: groupRow
			})) if (isCollapse) that.collapseGroup(groupRow);
			else that.expandGroup(groupRow);
		},
		_scrollToStickyGroupRow: function(originalRow) {
			const that = this;
			that._stickyHeaderItems = [];
			that._stickyFooterItems = [];
			const scrollContainer = that.content[0];
			if (scrollContainer) scrollContainer.scrollTop = originalRow[0].offsetTop;
			that._updateStickyGroups();
			const hc = that._stickyGroupHeaderContainer;
			if (hc && !hc.hasClass("k-hidden") && scrollContainer) {
				const stickyH = hc[0].offsetHeight;
				if (stickyH > 0) {
					scrollContainer.scrollTop = originalRow[0].offsetTop - stickyH;
					if (that._isLocked() && that.lockedContent) that.lockedContent[0].scrollTop = scrollContainer.scrollTop;
					that._updateStickyGroups();
				}
			}
		},
		_navigateStickyHorizontal: function(focusedRow, focusedCell, containerType, keyCode) {
			const that = this;
			const navCells = focusedRow.children(NAVCELL);
			const cellIdx = navCells.index(focusedCell);
			const nextIdx = keyCode === keys.RIGHT ? cellIdx + 1 : cellIdx - 1;
			if (nextIdx >= 0 && nextIdx < navCells.length) {
				focusedCell.removeClass(FOCUSED);
				const nextCell = navCells.eq(nextIdx);
				nextCell.addClass(FOCUSED);
				that._stickyGroupFocusState.cell = nextCell;
			} else if (that.lockedTable) {
				const container = containerType === "header" ? that._stickyGroupHeaderContainer : that._stickyGroupFooterContainer;
				const lockedWrapper = container.children(".k-grid-content-locked");
				const nonLockedClass = containerType === "header" ? ".k-grid-header-wrap" : ".k-grid-footer-wrap";
				const nonLockedWrapper = container.children(nonLockedClass);
				const isInLocked = focusedRow.closest(".k-grid-content-locked").length > 0;
				const rowIdx = focusedRow.parent().children("tr").index(focusedRow);
				let targetRow, targetCell;
				if (keyCode === keys.RIGHT && isInLocked) {
					targetRow = nonLockedWrapper.find("tbody > tr").eq(rowIdx);
					targetCell = targetRow.children(NAVCELL).first();
				} else if (keyCode === keys.LEFT && !isInLocked) {
					targetRow = lockedWrapper.find("tbody > tr").eq(rowIdx);
					targetCell = targetRow.children(NAVCELL).last();
				}
				if (targetCell && targetCell.length) {
					focusedCell.removeClass(FOCUSED);
					targetCell.addClass(FOCUSED);
					that._stickyGroupFocusState.cell = targetCell;
					that._stickyGroupFocusState.row = targetRow;
				}
			}
		},
		_handleStickyGroupKeyDown: function(e) {
			const isVertical = e.keyCode === keys.DOWN || e.keyCode === keys.UP;
			const isHorizontal = e.keyCode === keys.LEFT || e.keyCode === keys.RIGHT;
			const isEnter = e.keyCode === keys.ENTER;
			if (!isVertical && !isHorizontal && !isEnter) return false;
			const that = this;
			const focusState = that._stickyGroupFocusState;
			if (!focusState) return false;
			let focusedCell = focusState.cell;
			let focusedRow = focusState.row;
			let containerType = focusState.type;
			if (isEnter && containerType === "header") {
				const toggle = focusedRow.find(`${CARET_ALT_DOWN}, ${CARET_ALT_RIGHT}`);
				if (toggle.length) {
					const rowIdx = focusedRow.parent().children("tr").index(focusedRow);
					const cellIdx = focusedRow.children(NAVCELL).index(focusedCell);
					const items = that._stickyHeaderItems;
					if (items && rowIdx < items.length) {
						const resolved = that._resolveStickyGroupRows(items[rowIdx].headerIndex);
						if (resolved) {
							that._toggleStickyGroup(toggle, resolved.groupRow);
							that._scrollToStickyGroupRow(resolved.originalRow);
							that._clearStickyGroupFocus();
							const targetCell = resolved.groupRow.children(NAVCELL).eq(Math.min(cellIdx, resolved.groupRow.children(NAVCELL).length - 1));
							that._setCurrent(targetCell.length ? targetCell : resolved.groupRow.children(DATA_CELL).first());
						}
					}
				}
				e.preventDefault();
				e.stopImmediatePropagation();
				return true;
			}
			if (isHorizontal) {
				that._navigateStickyHorizontal(focusedRow, focusedCell, containerType, e.keyCode);
				e.preventDefault();
				e.stopImmediatePropagation();
				return true;
			}
			if (e.keyCode === keys.DOWN) that._exitStickyContainer(focusedRow, containerType, "down");
			else that._exitStickyContainer(focusedRow, containerType, "up");
			e.preventDefault();
			e.stopImmediatePropagation();
			return true;
		},
		_focusStickyRow: function(row, type, targetCell) {
			const that = this;
			if (!(type === "header" ? that._stickyGroupHeaderContainer : that._stickyGroupFooterContainer)) return;
			that._clearStickyGroupFocus();
			let cell;
			if (targetCell && targetCell.length && targetCell.is(NAVCELL)) cell = targetCell;
			else {
				cell = row.children("td[colspan]");
				if (!cell.length) cell = row.children(NAVCELL).first();
			}
			if (cell.length) {
				cell.addClass(FOCUSED);
				that._stickyGroupFocusState = {
					cell,
					row,
					type
				};
			}
			that._removeCurrent();
			that._stickyGroupFocusing = true;
			focusTable(that.table, true);
			that._stickyGroupFocusing = false;
		},
		_clearStickyGroupFocus: function() {
			if (this._stickyGroupHeaderContainer) this._stickyGroupHeaderContainer.find(".k-focus").removeClass(FOCUSED);
			if (this._stickyGroupFooterContainer) this._stickyGroupFooterContainer.find(".k-focus").removeClass(FOCUSED);
			this._stickyGroupFocusState = null;
		},
		_scrollStickyExitRow: function(targetRow, isLocked, scrollContainer, type) {
			const scrollRow = this._stickyScrollRow(targetRow, isLocked);
			if (!scrollRow) return;
			if (type === "header") {
				const stickyH = this._stickyGroupHeaderContainer ? this._stickyGroupHeaderContainer[0].offsetHeight : 0;
				scrollContainer.scrollTop = Math.max(scrollRow.offsetTop - stickyH, 0);
			} else {
				const stickyH = this._stickyGroupFooterContainer ? this._stickyGroupFooterContainer[0].offsetHeight : 0;
				scrollContainer.scrollTop = Math.max(scrollRow.offsetTop + scrollRow.offsetHeight - scrollContainer.clientHeight + stickyH, 0);
			}
		},
		_stickyScrollRow: function(targetRow, isLocked) {
			if (!isLocked) return targetRow;
			const idx = Array.prototype.indexOf.call(targetRow.parentNode.children, targetRow);
			return this.tbody[0] && this.tbody[0].children[idx];
		},
		_exitStickyFocusToCell: function(targetRow, scrollIntoView) {
			const that = this;
			const focusState = that._stickyGroupFocusState;
			let cellIdx = 0;
			if (scrollIntoView && focusState && focusState.cell) cellIdx = focusState.row.children(NAVCELL).index(focusState.cell);
			that._clearStickyGroupFocus();
			if (that._stickyGroupsScrollHandler) that._stickyGroupsScrollHandler();
			const navCells = $(targetRow).children(NAVCELL);
			const cell = navCells.eq(Math.min(cellIdx, navCells.length - 1));
			if (cell.length) {
				that._setCurrent(cell, false, true);
				if (scrollIntoView) targetRow.scrollIntoView({ block: "nearest" });
			}
		},
		_scrollToStickyGroup: function(stickyRow, type) {
			const that = this;
			const items = type === "header" ? that._stickyHeaderItems : that._stickyFooterItems;
			const rowIdx = stickyRow.parent().children("tr").index(stickyRow);
			if (!items || rowIdx < 0 || rowIdx >= items.length) return;
			const range = items[rowIdx];
			const skipOffset = that._stickyGroupVirtualOffset();
			const scrollContainer = that._stickyScrollContainer();
			if (!scrollContainer) return;
			const realRowIndex = range.headerIndex;
			const tbody = that.lockedTable && stickyRow.closest(".k-grid-content-locked").length > 0 ? that.lockedTable.find(">tbody")[0] : that.tbody[0];
			const realRow = tbody && tbody.children[realRowIndex - skipOffset];
			if (!realRow) return;
			const parentPadding = stickyParentPadding(stickyRow.parent().children("tr"), items, range);
			scrollContainer.scrollTop = Math.max(realRow.offsetTop - parentPadding, 0);
			that._clearStickyGroupFocus();
			if (that._stickyGroupsScrollHandler) that._stickyGroupsScrollHandler();
			let targetRow = null;
			for (let i = range.firstChildIndex; i <= range.lastChildIndex; i++) {
				const row = tbody.children[i - skipOffset];
				if (row && !row.classList.contains("k-grouping-row")) {
					targetRow = row;
					break;
				}
			}
			if (!targetRow) targetRow = tbody.children[range.firstChildIndex - skipOffset] || realRow;
			const cell = $(targetRow).children(NAVCELL).first();
			if (cell.length) that._setCurrent(cell, false, true);
		},
		_redirectToStickyContainer: function(next) {
			this._ensureStickyNavTargetVisible(next);
			return false;
		},
		_ensureStickyNavTargetVisible: function(next) {
			const that = this;
			if (!next || !next.length) return;
			const row = next.parent();
			const skipOffset = that._stickyGroupVirtualOffset();
			const rowGlobalIndex = row.index() + skipOffset;
			if (row.hasClass(GROUPING_ROW) && that._stickyHeaderItems && that._stickyHeaderItems.length) {
				for (let i = 0; i < that._stickyHeaderItems.length; i++) if (that._stickyHeaderItems[i].headerIndex === rowGlobalIndex) {
					if (that._isRowObscuredByStickyHeader(row)) that._scrollRealGroupRowIntoView(row, that._stickyHeaderItems[i], "header");
					return;
				}
			}
			if (row.hasClass("k-group-footer") && that._stickyFooterItems && that._stickyFooterItems.length) {
				for (let i = 0; i < that._stickyFooterItems.length; i++) if (that._stickyFooterItems[i].footerIndex === rowGlobalIndex) {
					if (that._isRowObscuredByStickyFooter(row)) that._scrollRealGroupRowIntoView(row, that._stickyFooterItems[i], "footer");
					return;
				}
			}
		},
		_scrollRealGroupRowIntoView: function(row, range, type) {
			const that = this;
			const scrollContainer = that._stickyScrollContainer();
			if (!scrollContainer) return;
			const container = type === "header" ? that._stickyGroupHeaderContainer : that._stickyGroupFooterContainer;
			if (!container) return;
			const items = type === "header" ? that._stickyHeaderItems : that._stickyFooterItems;
			const parentPadding = stickyParentPadding(container.find("tbody > tr"), items, range);
			const rowEl = row[0];
			if (type === "header") scrollContainer.scrollTop = Math.max(rowEl.offsetTop - parentPadding, 0);
			else {
				const viewportH = scrollContainer.clientHeight;
				scrollContainer.scrollTop = Math.max(rowEl.offsetTop + rowEl.offsetHeight - viewportH + parentPadding, 0);
			}
			that._updateStickyGroups();
			const targetProp = type === "header" ? "headerIndex" : "footerIndex";
			const targetValue = range[targetProp];
			let safety = 4;
			while (safety-- > 0) {
				if (!(type === "header" ? that._stickyHeaderItems : that._stickyFooterItems).some(function(it) {
					return it[targetProp] === targetValue;
				})) break;
				if (type === "header") scrollContainer.scrollTop = Math.max(Math.floor(scrollContainer.scrollTop) - 1, 0);
				else scrollContainer.scrollTop = Math.floor(scrollContainer.scrollTop) + 1;
				that._updateStickyGroups();
			}
		},
		_isRowObscuredByStickyHeader: function(row) {
			const stickyEl = this._stickyGroupHeaderContainer && this._stickyGroupHeaderContainer[0];
			if (!stickyEl || this._stickyGroupHeaderContainer.hasClass("k-hidden")) return false;
			const rowRect = row[0].getBoundingClientRect();
			const stickyRect = stickyEl.getBoundingClientRect();
			return rowRect.top < stickyRect.bottom;
		},
		_isRowObscuredByStickyFooter: function(row) {
			const stickyEl = this._stickyGroupFooterContainer && this._stickyGroupFooterContainer[0];
			if (!stickyEl || this._stickyGroupFooterContainer.hasClass("k-hidden")) return false;
			const rowRect = row[0].getBoundingClientRect();
			const stickyRect = stickyEl.getBoundingClientRect();
			return rowRect.bottom > stickyRect.top;
		},
		_exitStickyContainer: function(focusedRow, type, direction) {
			const that = this;
			const items = type === "header" ? that._stickyHeaderItems : that._stickyFooterItems;
			const rowIdx = focusedRow.parent().children("tr").index(focusedRow);
			if (!items || rowIdx < 0 || rowIdx >= items.length) return;
			const range = items[rowIdx];
			const skipOffset = that._stickyGroupVirtualOffset();
			const scrollContainer = that._stickyScrollContainer();
			if (!scrollContainer) return;
			const isLocked = that.lockedTable && focusedRow.closest(".k-grid-content-locked").length > 0;
			const tbody = isLocked ? that.lockedTable.find(">tbody")[0] : that.tbody[0];
			let realRowIndex;
			if (type === "header") realRowIndex = range.headerIndex;
			else realRowIndex = range.footerIndex !== null ? range.footerIndex : range.lastChildIndex;
			const realRow = tbody && tbody.children[realRowIndex - skipOffset];
			if (!realRow) return;
			let targetRow = realRow;
			if (type === "header" && direction === "down") {
				targetRow = findStickyChildRow(tbody, range, skipOffset, false) || realRow;
				const parentPadding = stickyParentPadding(focusedRow.parent().children("tr"), items, range);
				const hDownIdx = realRowIndex - skipOffset;
				const hDownRow = isLocked ? that.tbody[0] && that.tbody[0].children[hDownIdx] : realRow;
				if (hDownRow) {
					scrollContainer.scrollTop = Math.max(hDownRow.offsetTop - parentPadding, 0);
					that._updateStickyGroups();
					let safety = 4;
					while (safety-- > 0 && that._stickyHeaderItems.some(function(it) {
						return it.headerIndex === range.headerIndex;
					})) {
						scrollContainer.scrollTop = Math.max(Math.floor(scrollContainer.scrollTop) - 1, 0);
						that._updateStickyGroups();
					}
				}
			} else if (type === "header" && direction === "up") {
				const prevIdx = range.headerIndex - 1 - skipOffset;
				if (prevIdx >= 0 && tbody.children[prevIdx]) {
					targetRow = tbody.children[prevIdx];
					that._scrollStickyExitRow(targetRow, isLocked, scrollContainer, "header");
					that._updateStickyGroups();
					const hc = that._stickyGroupHeaderContainer;
					if (hc && !hc.hasClass("k-hidden")) {
						const scrollRow = that._stickyScrollRow(targetRow, isLocked);
						if (scrollRow) {
							const newStickyH = hc[0].offsetHeight;
							scrollContainer.scrollTop = Math.max(scrollRow.offsetTop - newStickyH, 0);
						}
					}
				} else {
					if (that._isStackedMode()) return;
					if (that._isPinnable() && that._pinnedTopRows && that._pinnedTopRows.length) {
						const focusState = that._stickyGroupFocusState;
						let cellIdx = 0;
						if (focusState && focusState.cell) cellIdx = focusState.row.children(NAVCELL).index(focusState.cell);
						scrollContainer.scrollTop = 0;
						that._clearStickyGroupFocus();
						const pinnedCell = that._pinnedBoundaryCellByPosition("top", false, cellIdx, true);
						if (pinnedCell.length) {
							focusTable(that.table, true);
							that._setCurrent(pinnedCell);
							return;
						}
					}
					scrollContainer.scrollTop = 0;
					const theadEl = isLocked ? that.lockedHeader && that.lockedHeader.find("thead") : that.thead;
					if (theadEl && theadEl.length) that._exitStickyFocusToCell(theadEl.find("tr:last > th").filter(NAVCELL).parent()[0]);
					return;
				}
			} else if (type === "footer" && direction === "up") {
				targetRow = findStickyChildRow(tbody, range, skipOffset, true) || realRow;
				that._scrollStickyExitRow(targetRow, isLocked, scrollContainer, "footer");
			} else if (type === "footer" && direction === "down") {
				const nextIdx = (range.footerIndex !== null ? range.footerIndex : range.lastChildIndex) + 1 - skipOffset;
				if (nextIdx < tbody.children.length && tbody.children[nextIdx]) targetRow = tbody.children[nextIdx];
				else if (that._isPinnable() && that._pinnedBottomRows && that._pinnedBottomRows.length) {
					const focusState = that._stickyGroupFocusState;
					let cellIdx = 0;
					if (focusState && focusState.cell) cellIdx = focusState.row.children(NAVCELL).index(focusState.cell);
					that._clearStickyGroupFocus();
					const pinnedCell = that._pinnedBoundaryCellByPosition("bottom", true, cellIdx, true);
					if (pinnedCell.length) {
						focusTable(that.table, true);
						that._setCurrent(pinnedCell);
						if (isLocked && that.lockedContent) that.lockedContent[0].scrollTop = scrollContainer.scrollTop;
						return;
					}
				}
			}
			if (isLocked && that.lockedContent) that.lockedContent[0].scrollTop = scrollContainer.scrollTop;
			that._exitStickyFocusToCell(targetRow, type !== "header");
		},
		_destroyStickyGroups: function() {
			const that = this;
			that._removeStickyGroupContainer("_stickyGroupLockedHeaderContainer", "_stickyGroupLockedHeaderTable", true);
			that._removeStickyGroupContainer("_stickyGroupLockedFooterContainer", "_stickyGroupLockedFooterTable");
			that._removeStickyGroupContainer("_stickyGroupHeaderContainer", "_stickyGroupHeaderTable", true);
			that._removeStickyGroupContainer("_stickyGroupFooterContainer", "_stickyGroupFooterTable");
			that._stickyHeaderItems = [];
			that._stickyFooterItems = [];
			that._stickyGroupsScrollHandler = null;
			that._stickyGroupsClickHandler = null;
			if (that._stickyGroupResizeHandler) {
				$(window).off("resize.kendoGrid", that._stickyGroupResizeHandler);
				that._stickyGroupResizeHandler = null;
			}
			if (that.content && that.content[0]) {
				that.content[0].style.scrollPaddingTop = "";
				that.content[0].style.scrollPaddingBottom = "";
			}
		},
		_syncStickyGroupColgroups: function() {
			const that = this;
			that._syncStickyColgroup(that._stickyGroupHeaderTable, that.table);
			that._syncStickyColgroup(that._stickyGroupFooterTable, that.table);
			if (that.lockedTable) {
				that._syncStickyColgroup(that._stickyGroupLockedHeaderTable, that.lockedTable);
				that._syncStickyColgroup(that._stickyGroupLockedFooterTable, that.lockedTable);
			}
			if (that._stickyHeaderItems && that._stickyHeaderItems.length) that._renderStickyHeaderRows(that._stickyHeaderItems);
			if (that._stickyFooterItems && that._stickyFooterItems.length) that._renderStickyFooterRows(that._stickyFooterItems);
			if (that.lockedTable) {
				if (that._stickyGroupHeaderTable && that._stickyGroupLockedHeaderTable) that._adjustRowsHeight(that._stickyGroupHeaderTable, that._stickyGroupLockedHeaderTable);
				if (that._stickyGroupFooterTable && that._stickyGroupLockedFooterTable) that._adjustRowsHeight(that._stickyGroupFooterTable, that._stickyGroupLockedFooterTable);
			}
		},
		_initStickyGroupLockedContainers: function() {
			const that = this;
			if (!that._isLocked() || !that.lockedContent) return;
			that._initStickyGroupLockedContainer("header");
			that._initStickyGroupLockedContainer("footer");
		},
		_initStickyGroupLockedContainer: function(type) {
			const that = this;
			const isHeader = type === "header";
			const hasItems = isHeader ? that._hasStickyGroupHeaders() : that._hasStickyGroupFooters();
			const container = isHeader ? that._stickyGroupHeaderContainer : that._stickyGroupFooterContainer;
			const lockedContainer = isHeader ? that._stickyGroupLockedHeaderContainer : that._stickyGroupLockedFooterContainer;
			if (!hasItems || !container || lockedContainer) return;
			const tableClass = isHeader ? STICKY_GROUP_HEADER_TABLE_CLASS : STICKY_GROUP_FOOTER_TABLE_CLASS;
			const wrapClass = isHeader ? "k-grid-header-wrap" : "k-grid-footer-wrap";
			const mainTable = isHeader ? that._stickyGroupHeaderTable : that._stickyGroupFooterTable;
			const result = createStickyGroupLockedContainer(tableClass, kendo.getValidCssClass("k-table-", "size", that.options.size));
			const lockedDiv = $(result.container);
			const lockedTable = $(result.table);
			mainTable.wrap(`<div class="${wrapClass}"></div>`);
			container.prepend(lockedDiv);
			if (isHeader) {
				that._stickyGroupLockedHeaderContainer = lockedDiv;
				that._stickyGroupLockedHeaderTable = lockedTable;
				lockedDiv.on("click.kendoGrid", that._stickyGroupsClickHandler);
			} else {
				that._stickyGroupLockedFooterContainer = lockedDiv;
				that._stickyGroupLockedFooterTable = lockedTable;
			}
		},
		_stickyScrollContainer: function() {
			if (this.virtualScrollable) return this.virtualScrollable.wrapper[0];
			return this.content ? this.content[0] : null;
		},
		_applyStickyGroupVisibility: function(type, items, pushOffsets) {
			const that = this;
			const isHeader = type === "header";
			const $container = isHeader ? that._stickyGroupHeaderContainer : that._stickyGroupFooterContainer;
			const $lockedContainer = isHeader ? that._stickyGroupLockedHeaderContainer : that._stickyGroupLockedFooterContainer;
			const container = $container[0];
			const lockedContainer = $lockedContainer ? $lockedContainer[0] : null;
			const changed = stickyItemsChanged(items, isHeader ? that._stickyHeaderItems || [] : that._stickyFooterItems || []);
			const shouldShow = items.length > 0;
			const applyTransforms = isHeader ? applyHeaderPushTransforms : applyFooterPushTransforms;
			if (!shouldShow) {
				$container.addClass("k-hidden");
				resetStickyTransforms(container);
				if (lockedContainer) {
					$lockedContainer.addClass("k-hidden");
					resetStickyTransforms(lockedContainer);
				}
			} else if (!changed) {
				$container.removeClass("k-hidden");
				applyTransforms(container, pushOffsets);
				if (lockedContainer) {
					$lockedContainer.removeClass("k-hidden");
					applyTransforms(lockedContainer, pushOffsets);
				}
			} else {
				if (isHeader) that._stickyHeaderItems = items;
				else that._stickyFooterItems = items;
				$container.removeClass("k-hidden");
				if (lockedContainer) $lockedContainer.removeClass("k-hidden");
				that._syncStickyGroupColgroups();
				applyTransforms(container, pushOffsets);
				if (lockedContainer) applyTransforms(lockedContainer, pushOffsets);
			}
		},
		_syncStickyGroupScrollState: function(scrollContainer) {
			const that = this;
			if (!scrollContainer) return;
			if (that.lockedContent) {
				const lockedWidthPx = that.lockedContent[0].style.width;
				if (lockedWidthPx && that._stickyGroupLockedHeaderContainer) that._stickyGroupLockedHeaderContainer[0].style.width = lockedWidthPx;
				if (lockedWidthPx && that._stickyGroupLockedFooterContainer) that._stickyGroupLockedFooterContainer[0].style.width = lockedWidthPx;
			}
			const contentScrollLeft = scrollContainer.scrollLeft;
			let headerScrollTarget = null;
			if (that._stickyGroupHeaderContainer) headerScrollTarget = that.lockedContent ? that._stickyGroupHeaderContainer.children(".k-grid-header-wrap")[0] : that._stickyGroupHeaderContainer[0];
			let footerScrollTarget = null;
			if (that._stickyGroupFooterContainer) footerScrollTarget = that.lockedContent ? that._stickyGroupFooterContainer.children(".k-grid-footer-wrap")[0] : that._stickyGroupFooterContainer[0];
			if (headerScrollTarget && headerScrollTarget.scrollLeft !== contentScrollLeft) headerScrollTarget.scrollLeft = contentScrollLeft;
			if (footerScrollTarget && footerScrollTarget.scrollLeft !== contentScrollLeft) footerScrollTarget.scrollLeft = contentScrollLeft;
			const scrollbarMargin = kendo.support.scrollbar() + "px";
			if (that._stickyGroupHeaderContainer) that._stickyGroupHeaderContainer[0].style.marginInlineEnd = `var(--kendo-scrollbar-width, ${scrollbarMargin})`;
			if (that._stickyGroupFooterContainer) that._stickyGroupFooterContainer[0].style.marginInlineEnd = `var(--kendo-scrollbar-width, ${scrollbarMargin})`;
			const headerH = that._stickyGroupHeaderContainer ? that._stickyGroupHeaderContainer[0].offsetHeight : 0;
			const footerH = that._stickyGroupFooterContainer ? that._stickyGroupFooterContainer[0].offsetHeight : 0;
			scrollContainer.style.scrollPaddingTop = headerH > 0 ? `${headerH}px` : "";
			scrollContainer.style.scrollPaddingBottom = footerH > 0 ? `${footerH}px` : "";
		},
		_updateStickyGroups: function() {
			const that = this;
			const isHeaderEnabled = that._hasStickyGroupHeaders();
			const isFooterEnabled = that._hasStickyGroupFooters();
			if (!isHeaderEnabled && !isFooterEnabled) return;
			if (that._isLocked() && that.lockedContent && !that._stickyGroupLockedHeaderContainer) {
				that._initStickyGroupLockedContainers();
				that._syncStickyGroupColgroups();
			}
			const tbody = that.tbody ? that.tbody[0] : null;
			const scrollContainer = that._stickyScrollContainer();
			if (!tbody || !scrollContainer || !tbody.children.length) return;
			if (that._groups() <= 0) {
				if (that._stickyGroupHeaderContainer) that._stickyGroupHeaderContainer.addClass("k-hidden");
				if (that._stickyGroupFooterContainer) that._stickyGroupFooterContainer.addClass("k-hidden");
				if (that._stickyGroupLockedHeaderContainer) that._stickyGroupLockedHeaderContainer.addClass("k-hidden");
				if (that._stickyGroupLockedFooterContainer) that._stickyGroupLockedFooterContainer.addClass("k-hidden");
				return;
			}
			const skipOffset = that._stickyGroupVirtualOffset();
			const groupRanges = buildGroupRangeMap(tbody, skipOffset, that.lockedTable ? that.lockedTable.find(">tbody")[0] : null);
			if (!Object.keys(groupRanges).length) return;
			const metrics = buildStickyRowMetrics(tbody, skipOffset);
			const scrollTop = scrollContainer.scrollTop;
			const viewportHeight = scrollContainer.clientHeight;
			if (isHeaderEnabled && that._stickyGroupHeaderContainer) {
				const { items, pushOffsets } = convergeStickyHeaders(metrics, groupRanges, scrollTop);
				that._applyStickyGroupVisibility("header", items, pushOffsets);
			}
			if (isFooterEnabled && that._stickyGroupFooterContainer) {
				const { items, pushOffsets } = convergeStickyFooters(metrics, groupRanges, scrollTop, viewportHeight);
				that._applyStickyGroupVisibility("footer", items, pushOffsets);
				const hasHorizontalScrollbar = scrollContainer.scrollWidth > scrollContainer.clientWidth;
				that._stickyGroupFooterContainer[0].style.marginBlockEnd = hasHorizontalScrollbar ? `var(--kendo-scrollbar-width, ${kendo.support.scrollbar()}px)` : "";
			}
			that._syncStickyGroupScrollState(scrollContainer);
		},
		_renderStickyRows: function(items, indexProp, table, lockedTable) {
			const that = this;
			const tbody = table.find(">tbody");
			const skipOffset = that._stickyGroupVirtualOffset();
			tbody.html(renderStickyRowsHtml(that.tbody[0].children, items, indexProp, skipOffset));
			kendo.applyStylesFromKendoAttributes(tbody, [
				"display",
				"left",
				"right"
			]);
			if (lockedTable && that.lockedTable) {
				const lockedTbody = lockedTable.find(">tbody");
				const lockedRows = that.lockedTable.find(">tbody")[0].children;
				lockedTbody.html(renderStickyRowsHtml(lockedRows, items, indexProp, skipOffset));
				kendo.applyStylesFromKendoAttributes(lockedTbody, [
					"display",
					"left",
					"right"
				]);
			}
		},
		_renderStickyHeaderRows: function(stickyHeaders) {
			this._renderStickyRows(stickyHeaders, "headerIndex", this._stickyGroupHeaderTable, this._stickyGroupLockedHeaderTable);
		},
		_renderStickyFooterRows: function(stickyFooters) {
			this._renderStickyRows(stickyFooters, "footerIndex", this._stickyGroupFooterTable, this._stickyGroupLockedFooterTable);
		},
		_removeGroupableOptionsFromHeader: function() {
			var headerCells = this._headerCells();
			for (var i = 0; i < headerCells.length; i++) headerCells.eq(i).removeData(GROUP_SORT);
		},
		_continuousItems: function(filter, cell) {
			if (!this.lockedContent) return;
			var that = this;
			var elements = that.table.add(that.lockedTable);
			var lockedItems = $(filter, elements[0]);
			var nonLockedItems = $(filter, elements[1]);
			var columns = cell ? lockedColumns(leafColumns(that.columns)).length : 1;
			var nonLockedColumns = cell ? leafColumns(that.columns).length - columns : 1;
			var result = [];
			for (var idx = 0; idx < lockedItems.length; idx += columns) {
				push.apply(result, lockedItems.slice(idx, idx + columns));
				push.apply(result, [].splice.call(nonLockedItems, 0, nonLockedColumns));
			}
			return result;
		},
		_selectable: function() {
			var that = this, multi, cell, notString = [], isLocked = that._isLocked(), selectable = that.options.selectable, hasSkeletonLoader = that.options.loaderType === "skeleton";
			if (selectable && !selectable.checkboxSelection) {
				if (that.selectable) that.selectable.destroy();
				that._selectedIds = {};
				selectable = kendo.ui.Selectable.parseOptions(selectable);
				multi = selectable.multiple;
				cell = selectable.cell;
				if (that._hasDetails()) notString[notString.length] = ".k-detail-row";
				if (that.options.groupable || that._hasFooters() || that._groups()) notString[notString.length] = ".k-grouping-row,.k-group-footer";
				if (hasSkeletonLoader) notString[notString.length] = "[data-skeleton-row]";
				notString = notString.join(",");
				if (notString !== "") notString = ":not(" + notString + ")";
				var elements = that.table;
				if (isLocked) elements = elements.add(that.lockedTable);
				const cellSelector = that._isStackedMode() ? STACKED_CELL_SELECTOR : SELECTION_CELL_SELECTOR;
				var filter = ">" + (cell ? cellSelector : "tbody>tr" + notString);
				that.selectable = new kendo.ui.Selectable(elements, {
					allowPaste: that.options.allowPaste,
					filter,
					aria: true,
					multiple: multi,
					holdToDrag: !!(that._isMobile || kendo.support.mobileOS),
					toggleable: !!(that._isMobile || kendo.support.mobileOS),
					dragToSelect: that.options.selectable && that.options.selectable.dragToSelect,
					changing: function(e) {
						if (that.trigger(CHANGING, {
							target: e.target,
							originalEvent: e.originalEvent
						})) e.preventDefault();
					},
					change: function(e) {
						var selectedValues;
						if (!cell) that._persistSelectedRows();
						if (that._checkBoxSelection) {
							selectedValues = that.selectable.value();
							that._uncheckCheckBoxes();
							that._checkRows(selectedValues);
							if (selectedValues.length && selectedValues.length === that.items().length) that._toggleHeaderCheckState(true);
							else that._toggleHeaderCheckState(false);
						}
						that._calculateAggregatesForSelected();
						if (that._editMode() !== "incell") that._toggleToolbarEditingItemsVisibility();
						that._syncPinnedSelection();
						if (e.event) that.trigger(CHANGE, { cellAggregates: that._cellAggregates });
					},
					useAllItems: isLocked && multi && cell,
					relatedTarget: function(items) {
						if (cell || !isLocked) return;
						var related;
						var result = $();
						for (var idx = 0, length = items.length; idx < length; idx++) {
							related = that._relatedRow(items[idx]);
							if (inArray(related[0], items) < 0) result = result.add(related);
						}
						return result;
					},
					continuousItems: function() {
						return that._continuousItems(filter, cell);
					},
					ignoreOverlapped: that.options.selectable && that.options.selectable.ignoreOverlapped,
					addIdToRanges: true
				});
				if (that.options.navigatable) elements.on("keydown.kendoGrid", function(e) {
					var current = that.current();
					var target = e.target;
					var eventObject = { event: e };
					var triggerChange;
					var triggerChanging;
					var lastSelection;
					if (!current) return;
					if (e.keyCode === keys.SPACEBAR && !e.shiftKey && $.inArray(target, elements) > -1 && !current.is(".k-grid-stack-edit-cell,.k-edit-cell,.k-header") && current.parent().is(":not(.k-grouping-row,.k-detail-row,.k-group-footer)")) {
						e.preventDefault();
						e.stopPropagation();
						current = cell ? current : current.parent();
						triggerChange = !current.hasClass(SELECTED) || that.selectable.value().length > 1;
						triggerChanging = triggerChange || multi && current.hasClass(SELECTED) && e.ctrlKey;
						if (triggerChanging && that.trigger(CHANGING, {
							target: current,
							originalEvent: e
						})) return;
						if (isLocked && !cell) current = current.add(that._relatedRow(current));
						if (multi) {
							if (!e.ctrlKey) that.selectable.clear();
							else if (current.hasClass(SELECTED)) {
								that._deselectCheckRows(current);
								that._calculateAggregatesForSelected();
								that.trigger(CHANGE, { cellAggregates: that._cellAggregates });
								return;
							}
						} else that.selectable.clear();
						if (!cell) that.selectable._lastActive = current;
						that.selectable.value(current);
						if (triggerChange) {
							that._calculateAggregatesForSelected();
							that.trigger(CHANGE, { cellAggregates: that._cellAggregates });
						}
						that._syncPinnedSelection();
					} else if (!cell && ($(target).is("td") || $(target).is("table") && inArray(target, this._navigatableTables)) && (e.shiftKey && e.keyCode == keys.LEFT || e.shiftKey && e.keyCode == keys.RIGHT || e.shiftKey && e.keyCode == keys.UP || e.shiftKey && e.keyCode == keys.DOWN || e.keyCode === keys.SPACEBAR && e.shiftKey)) {
						e.preventDefault();
						e.stopPropagation();
						current = current.parent();
						if (that.trigger(CHANGING, {
							target: current,
							originalEvent: e
						})) return;
						lastSelection = that.selectable.value();
						if (isLocked) current = current.add(that._relatedRow(current));
						if (multi) {
							if (!that.selectable._lastActive) that.selectable._lastActive = current;
							that.selectable.selectRange(that.selectable._firstSelectee(), current);
							if (!compareElements(lastSelection, that.selectable.value())) that.trigger(CHANGE, eventObject);
						} else if (!current.hasClass(SELECTED)) {
							that.selectable.clear();
							that.selectable.value(current);
							that._calculateAggregatesForSelected();
							that.trigger(CHANGE, { cellAggregates: that._cellAggregates });
						}
						that._syncPinnedSelection();
					}
				});
			}
		},
		_pasteReplaceHandler: function(plain) {
			var that = this, rows, current, currentRow, currentRowUid, currentField, uids = [];
			current = that.select().first();
			if (that._isStackedMode() || !current.length) return;
			if (current.is(TR)) current = current.children(TD).first();
			rows = plain.split("\n").filter((f) => f);
			currentRow = current.closest("tr");
			currentField = that.thead.find("th:eq(" + current.index() + ")").data("field");
			currentRowUid = currentRow.data("uid");
			uids.push(currentRowUid);
			currentRow.nextAll(ITEMROW).slice(0, rows.length - 1).each((i, item) => {
				uids.push($(item).data("uid"));
			});
			that._executePaste(rows, uids, null, currentField);
		},
		_pasteInsertHandler: function(plain) {
			var that = this, dataSource = that.dataSource, rows, current = that.select().first(), currentRow, dataItemIndex, dataItem;
			if (!current.length) return;
			if (current.is(TR)) current = current.children(TD).first();
			rows = plain.split("\n").filter((f) => f);
			currentRow = current.closest("tr");
			dataItem = that.dataItem(currentRow);
			dataItemIndex = dataSource.indexOf(dataItem) + 1;
			that._executePaste(rows, null, dataItemIndex, null);
		},
		_executePaste: function(rows, uids, index, currentField) {
			var that = this, dataSource = that.dataSource, update = uids || false, dataItem, row, cells, cell, column, field, selectedUids = that._getSelectedRowUids(), selectedColumnFields = that._getSelectedColumnFields(), changedItems = [], visibleColumns = visibleLeafColumns(that.columns).filter((col) => !col.selectable && !col.draggable & !col.command), startingIndex = currentField && visibleColumns.map((c) => c.field).indexOf(currentField);
			if (rows.length === 1 && rows[0].split("	").length === 1 && update) for (let j = 0; j < selectedUids.length; j++) {
				const uid = selectedUids[j];
				dataItem = dataSource.getByUid(uid);
				cell = rows[0].split("	")[0];
				for (let j = 0; j < selectedColumnFields.length; j++) {
					field = selectedColumnFields[j];
					if (dataItem && cell) dataItem.set(field, cell);
				}
				if (dataItem && dataItem.dirty) changedItems.push(dataItem);
			}
			else for (let i = 0; i < rows.length; i++) {
				row = rows[i];
				cells = row.split("	");
				dataItem = update ? dataSource.getByUid(uids[i]) : dataSource.insert(index + i, {});
				for (let j = 0; j < cells.length; j++) {
					cell = cells[j].replace(/\r/, "");
					column = visibleColumns[j + startingIndex || 0];
					if (column && dataItem && cell) {
						field = column.field;
						dataItem.set(field, cell);
					}
				}
				if (dataItem && dataItem.dirty) changedItems.push(dataItem);
			}
			that.trigger(PASTE, {
				items: changedItems,
				type: update ? "replace" : "insert"
			});
		},
		_pasteKeyboardHandler: function(e) {
			var that = this, current = that.current(), clipBoardData = e.originalEvent.clipboardData, operation = that.pasteActionsDropDownList && that.pasteActionsDropDownList.value() || "insert", rowUid, cellIndex, plain;
			if ($(e.target).is(".k-grid-stack-edit-cell input:visible,.k-edit-cell input:visible")) return;
			if (clipBoardData) {
				e.preventDefault();
				plain = clipBoardData.getData("text").trimEnd();
				if (isEmptyString(plain)) plain = " ";
				if (current && current.length) {
					cellIndex = current.index();
					rowUid = current.closest(TR).data("uid");
				}
				if (operation === "replace") that._pasteReplaceHandler(plain);
				if (operation === "insert") that._pasteInsertHandler(plain);
				if (cellIndex && rowUid) {
					that._currentRowIndex = that.wrapper.find("tr[data-uid='" + rowUid + "']").index();
					that._restoreCurrent(cellIndex);
				}
			}
		},
		_paste: function() {
			var that = this, options = that.options, selectable = options.selectable;
			if (options.allowPaste && selectable) {
				that.pasteHandler = that._pasteKeyboardHandler.bind(that);
				(that.content || that.table).on("paste.kendoGrid", that.pasteHandler);
				if (that.options.toolbar) that._pasteToolbarDropDown();
			}
		},
		_clipboard: function() {
			var options = this.options;
			if (options.selectable && options.allowCopy) {
				var grid = this;
				if (!options.navigatable) {
					grid.table.attr(TABINDEX, 0);
					grid.table.add(grid.lockedTable).on("mousedown.kendoGrid keydown.kendoGrid", ".k-detail-cell", function(e) {
						if (e.target !== e.currentTarget) e.stopImmediatePropagation();
					}).on("mousedown.kendoGrid", "tr:not(.k-footer-template):visible>:not(.k-group-cell):not(.k-detail-cell):not(.k-hierarchy-cell):visible", tableClick.bind(grid));
				}
				grid.copyHandler = grid.copySelection.bind(grid);
				grid.updateClipBoardState = function() {
					if (grid.areaClipBoard) grid.areaClipBoard.val(grid.getTSV()).trigger("focus").select();
				};
				const container = grid.content || grid.table;
				grid.bind("change", grid.updateClipBoardState);
				container.on("keydown", grid.copyHandler);
				grid.clearAreaHandler = grid.clearArea.bind(grid);
				container.on("keyup", grid.clearAreaHandler);
			}
		},
		copySelectionToClipboard: function(includeHeaders) {
			this._createAreaClipBoard();
			this.areaClipBoard.val(this.getTSV(includeHeaders)).trigger("focus").select();
			document.execCommand("copy");
		},
		copySelection: function(e) {
			if (e instanceof jQuery.Event && !(e.ctrlKey || e.metaKey) || !(e.keyCode === 67 && (e.ctrlKey || e.metaKey)) || $(e.target).is("input:visible,textarea:visible") || window.getSelection && window.getSelection().toString() || document.selection && document.selection.createRange().text) return;
			this._createAreaClipBoard();
			this.areaClipBoard.val(this.getTSV()).trigger("focus").select();
		},
		_createAreaClipBoard: function() {
			if (!this.areaClipBoard) this.areaClipBoard = $("<textarea />").css({
				position: "fixed",
				top: "50%",
				left: "50%",
				opacity: 0,
				width: 0,
				height: 0
			}).appendTo(this.wrapper);
		},
		getTSV: function(includeHeaders) {
			var grid = this;
			var selected = grid.select();
			var delimeter = "	";
			var allowCopy = grid.options.allowCopy;
			var onlyVisible = true;
			var hasLockedCols = grid._isLocked() && lockedColumns(grid.columns).length;
			if ($.isPlainObject(allowCopy) && allowCopy.delimeter) delimeter = allowCopy.delimeter;
			var text = "";
			if (selected.length) {
				if (selected.eq(0).is(TR)) selected = selected.find("td:not(.k-group-cell)");
				if (onlyVisible) selected.filter(":visible");
				var result = [];
				var cellsOffset = this.columns.length;
				var lockedCols = grid._isLocked() && lockedColumns(grid.columns).length;
				var inLockedArea = true;
				var fields = [];
				var field;
				var columns = visibleLeafColumns(this.columns);
				$.each(selected, function(idx, cell) {
					cell = $(cell);
					field = grid._getCellField(cell, hasLockedCols);
					if (columns.findIndex((c) => c.field === field) === -1) return;
					var rowIndex = cell.closest(TR).index();
					var cellIndex = cell.index();
					if (onlyVisible) cellIndex -= cell.prevAll(":hidden").length;
					if (lockedCols && inLockedArea) inLockedArea = $.contains(grid.lockedTable[0], cell[0]);
					if (grid._groups() && inLockedArea) cellIndex -= grid._groups();
					cellIndex = inLockedArea ? cellIndex : cellIndex + lockedCols;
					if (field) fields[cellIndex] = field;
					if (cellsOffset > cellIndex) cellsOffset = cellIndex;
					var cellText = cell.text();
					if (!result[rowIndex]) result[rowIndex] = [];
					result[rowIndex][cellIndex] = cellText;
				});
				var rowsOffset = result.length;
				result = $.each(result, function(idx, val) {
					if (val) {
						result[idx] = val.slice(cellsOffset);
						if (rowsOffset > idx) rowsOffset = idx;
					}
				});
				if (includeHeaders && fields.length) {
					result.splice(rowsOffset, 0, fields.map(function(field) {
						return getTitle(field, columns);
					}));
					var headerIndex = result.findIndex(function(el) {
						return el !== undefined;
					});
					result[headerIndex] = result[headerIndex].slice(cellsOffset);
				}
				$.each(result.slice(rowsOffset), function(idx, val) {
					if (val) text += val.join(delimeter) + "\r\n";
					else text += "\r\n";
				});
			}
			return text;
		},
		clearArea: function(e) {
			if (this.areaClipBoard && e && e.target === this.areaClipBoard[0]) focusTable(this.table, true);
			if (this.areaClipBoard) {
				this.areaClipBoard.remove();
				this.areaClipBoard = null;
			}
		},
		_adaptiveColumns: function() {
			var that = this;
			if (that._anyColumnHasMediaQuery()) {
				that._setColumnsMediaVisibility(that.columns);
				that._attachColumnMediaResizeHandler();
			}
		},
		_anyColumnHasMediaQuery: function() {
			return this._columnsWithMediaQuery().length;
		},
		_columnsWithMediaQuery: function() {
			return columnsWithMedia(this.columns);
		},
		_attachColumnMediaResizeHandler: function() {
			var that = this;
			that._detachColumnMediaResizeHandler();
			that._columnMediaResizeHandler = that._onColumnMediaResize.bind(that);
			$(window).on("resize.kendoGrid", that._columnMediaResizeHandler);
		},
		_detachColumnMediaResizeHandler: function() {
			var that = this;
			if (that._columnMediaResizeHandler) $(window).off("resize.kendoGrid", that._columnMediaResizeHandler);
		},
		_onColumnMediaResize: function() {
			var that = this;
			that._setColumnsMediaVisibility(that.columns);
			that._setContentMediaWidth();
		},
		_setColumnsMediaVisibility: function(columns) {
			var cols = columns || [];
			for (var i = 0; i < cols.length; i++) this._setColumnMediaVisibility(cols[i]);
		},
		_setColumnMediaVisibility: function(column) {
			var that = this;
			if (isUndefined(column.media)) that._setColumnsMediaVisibility(column.columns);
			else if (columnMatchesMedia(column)) {
				that._showColumnByMedia(column);
				if (!column.hidden) that._setColumnsMediaVisibility(column.columns);
			} else that._hideColumnByMedia(column);
		},
		_showColumnByMedia: function(column) {
			if (!column.hidden) this.showColumn(column);
			setColumnMatchesMedia(column);
		},
		_hideColumnByMedia: function(column) {
			var initiallyHidden = column.hidden;
			if (!initiallyHidden) {
				column._hideByMedia = true;
				this.hideColumn(column);
				column._hideByMedia = false;
				column.hidden = initiallyHidden;
			}
			setColumnMatchesMedia(column);
		},
		_setContentMediaWidth: function() {
			var that = this;
			var options = that.options;
			var isLocked = that._isLocked();
			var footer;
			if (options.scrollable && (options.resizable === true || options.resizable && options.resizable.columns === true)) {
				if (isLocked && that.lockedFooter) footer = that.lockedFooter.children("table");
				else if (that.footer) footer = that.footer.find(">.k-grid-footer-wrap>table");
				if (!footer || !footer[0]) footer = $();
				var header = isLocked ? that.wrapper.find(".k-grid-header-locked").find("table") : that.wrapper.find(".k-grid-header").find("table");
				var contentTable = isLocked ? that.lockedTable : that.table;
				var headerColumns = header.find("th");
				var headerColgroup = header.find("colgroup");
				var headerColumnsCount = headerColumns.length;
				var visibleHeaderColumnsCount = headerColumns.filter(isCellVisible).length;
				var hiddenHeaderColumnsCount = headerColumns.length - visibleHeaderColumnsCount;
				var totalHeaderWidth = 0;
				if (header[0].style.width !== "" && parseFloat(header[0].style.width) !== totalHeaderWidth) {
					var currentHeaderWidth = header.css(WIDTH);
					for (var i = 0; i < headerColumnsCount; i++) if (isElementVisible(headerColumns[i])) {
						var columnWidth;
						var cellIndex = Math.max(i, i - hiddenHeaderColumnsCount);
						var colgroupChild = headerColgroup.children()[cellIndex];
						var columnStyleWidth = colgroupChild ? colgroupChild.style.width : "";
						if (columnStyleWidth !== "") columnWidth = parseFloat(columnStyleWidth);
						else {
							header.css(WIDTH, AUTO);
							columnWidth = outerWidth(headerColumns.eq(i));
							header.css(WIDTH, currentHeaderWidth);
						}
						totalHeaderWidth += columnWidth;
					}
					contentTable.css("width", totalHeaderWidth - 1);
					header.css("width", totalHeaderWidth);
					footer.css("width", totalHeaderWidth);
					that._updateStickyColumns();
				}
			}
		},
		_minScreenSupport: function() {
			if (this.hideMinScreenCols()) {
				this.minScreenResizeHandler = this.hideMinScreenCols.bind(this);
				$(window).on("resize", this.minScreenResizeHandler);
			}
		},
		hideMinScreenCols: function() {
			var cols = this.columns, screenWidth = window.innerWidth > 0 ? window.innerWidth : screen.width;
			return this._iterateMinScreenCols(cols, screenWidth);
		},
		_iterateMinScreenCols: function(cols, screenWidth) {
			var any = false;
			for (var i = 0; i < cols.length; i++) {
				var col = cols[i];
				var minWidth = col.minScreenWidth;
				if (minWidth !== undefined && minWidth !== null) {
					any = true;
					if (minWidth > screenWidth) this.hideColumn(col);
					else this.showColumn(col);
				}
				if (!col.hidden && col.columns) any = this._iterateMinScreenCols(col.columns, screenWidth) || any;
			}
			return any;
		},
		_stickyColumns: function() {
			var that = this;
			if (that._anyStickyColumns()) that._setStickyColumns(false);
		},
		_updateStickyColumns: function() {
			var that = this;
			var groupHeaderColumnTemplateColumns = grep(leafColumns(that.columns), function(column) {
				return column.groupHeaderColumnTemplate;
			});
			if (that._anyStickyColumns()) {
				that._setStickyColumns(true);
				that._templates();
				if (groupHeaderColumnTemplateColumns.length > 0) that._renderGroupRows();
				if (that._hasFilterRow()) that._updateStickyFilterCells();
			}
			if (that._hasStickyGroupHeaders() || that._hasStickyGroupFooters()) that._syncStickyGroupColgroups();
		},
		_updateStickyFilterCells: function() {
			var filterCells = this.thead.find(".k-filter-row").find("td:not(.k-group-cell,.k-hierarchy-cell)");
			if (filterCells.length) filterCells.each(function() {
				var th = $(this);
				var column = th.data("column");
				if (column.sticky) {
					if (isPlainObject(column.stickyStyle)) th.css({
						left: column.stickyStyle.left || "",
						right: column.stickyStyle.right || ""
					});
					th.addClass(STICKY_HEADER_CLASS);
				} else {
					th.css({
						left: "",
						right: ""
					});
					th.removeClass(STICKY_HEADER_CLASS);
				}
			});
		},
		_anyStickyColumns: function() {
			var that = this;
			if (!that.tbody) return false;
			return !that._isStackedMode() && stickyColumns(that.columns).length;
		},
		_setStickyColumns: function(updateStyles) {
			var that = this;
			var columns = stickyColumns(that.columns);
			var visibleColumns = visibleStickyColumns(that.columns);
			var stickyWidths = that._calculateStickyWidths(visibleColumns);
			that._removeStickyAttributes(columns);
			that._setStickyClassAttributes(columns);
			that._setStickyStyleAttributes(visibleColumns, stickyWidths, updateStyles);
			if (updateStyles) that._setStickyStyles(visibleColumns, stickyWidths);
		},
		_calculateStickyWidths: function(columns, initialLeftWidth, initialRightWidth) {
			var that = this;
			var i;
			var column;
			var columnWidth;
			var nextColumnLeft;
			var nextColumnRight;
			var left = isRtl ? "right" : "left";
			var right = isRtl ? "left" : "right";
			var stickyWidths = {
				left: new Array(columns.length).fill(initialLeftWidth ? initialLeftWidth : 0),
				right: new Array(columns.length).fill(initialRightWidth ? initialRightWidth : 0)
			};
			for (i = 0; i < columns.length - 1; i++) {
				column = columns[i];
				columnWidth = that._sumColumnWidth(column);
				nextColumnLeft = columnWidth + stickyWidths[left][i];
				stickyWidths[left][i + 1] = nextColumnLeft;
			}
			for (i = columns.length - 1; i > 0; i--) {
				column = columns[i];
				columnWidth = that._sumColumnWidth(column);
				nextColumnRight = columnWidth + stickyWidths[right][i];
				stickyWidths[right][i - 1] = nextColumnRight;
			}
			return stickyWidths;
		},
		_setStickyClassAttributes: function(columns, masterIndex) {
			var that = this;
			var i;
			var column;
			for (i = 0; i < columns.length; i++) {
				column = columns[i];
				if (column.columns) {
					if (!masterIndex && i) masterIndex = i;
					that._setStickyClassAttributes(childColumns([column]), masterIndex);
				}
				if (masterIndex) addColumnAttribute(column, "headerAttributes", "class", STICKY_HEADER_NO_BORDER_CLASS);
				addColumnAttribute(column, "attributes", "class", STICKY_CELL_CLASS);
				addColumnAttribute(column, "headerAttributes", "class", STICKY_HEADER_CLASS);
				addColumnAttribute(column, "footerAttributes", "class", STICKY_FOOTER_CLASS);
			}
		},
		_setStickyStyleAttributes: function(columns, stickyWidths, updateStyles) {
			var that = this;
			var i;
			var column;
			var stickyLeft;
			var stickyRight;
			var stickyStyle;
			var childCols;
			var childStickyWidths;
			for (i = 0; i < columns.length; i++) {
				column = columns[i];
				stickyLeft = stickyWidths.left[i];
				stickyRight = stickyWidths.right[i];
				stickyStyle = {
					left: stickyLeft + "px",
					right: stickyRight + "px"
				};
				if (column.columns) {
					childCols = visibleChildColumns([column]);
					childStickyWidths = that._calculateStickyWidths(childCols, stickyLeft, stickyRight);
					that._setStickyStyleAttributes(childCols, childStickyWidths, updateStyles);
					if (updateStyles) that._setStickyStyles(childCols, childStickyWidths);
				}
				addColumnAttribute(column, "attributes", kendo.attr("style-left"), stickyStyle.left);
				addColumnAttribute(column, "attributes", kendo.attr("style-right"), stickyStyle.right);
				addColumnAttribute(column, "headerAttributes", kendo.attr("style-left"), stickyStyle.left);
				addColumnAttribute(column, "headerAttributes", kendo.attr("style-right"), stickyStyle.right);
				addColumnAttribute(column, "footerAttributes", kendo.attr("style-left"), stickyStyle.left);
				addColumnAttribute(column, "footerAttributes", kendo.attr("style-right"), stickyStyle.right);
				column.stickyStyle = stickyStyle;
			}
		},
		_removeStickyAttributes: function(columns) {
			var that = this;
			var i;
			var cellClassRegExp = /* @__PURE__ */ new RegExp("(\\s*k-grid-content-sticky)*", "ig");
			var headerClassRegExp = /* @__PURE__ */ new RegExp("(\\s*k-grid-header-sticky)*", "ig");
			var footerClassRegExp = /* @__PURE__ */ new RegExp("(\\s*k-grid-footer-sticky)*", "ig");
			var headerClassNoBorderRegExp = /* @__PURE__ */ new RegExp("(\\s*k-grid-no-left-border)*", "ig");
			var column;
			for (i = 0; i < columns.length; i++) {
				column = columns[i];
				if (column.columns) that._removeStickyAttributes(childColumns([column]));
				removeColumnAttribute(column, "attributes", "class", cellClassRegExp);
				removeColumnAttribute(column, "attributes", kendo.attr("style-left"), "", true);
				removeColumnAttribute(column, "attributes", kendo.attr("style-right"), "", true);
				removeColumnAttribute(column, "headerAttributes", "class", headerClassRegExp);
				removeColumnAttribute(column, "headerAttributes", "class", headerClassNoBorderRegExp);
				removeColumnAttribute(column, "headerAttributes", kendo.attr("style-left"), "", true);
				removeColumnAttribute(column, "headerAttributes", kendo.attr("style-right"), "", true);
				removeColumnAttribute(column, "footerAttributes", "class", footerClassRegExp);
				removeColumnAttribute(column, "footerAttributes", kendo.attr("style-left"), "", true);
				removeColumnAttribute(column, "footerAttributes", kendo.attr("style-right"), "", true);
			}
		},
		_setStickyStyles: function(columns, stickyWidths) {
			var that = this;
			var i;
			var j;
			var leafsCols = leafColumns(nonLockedColumns(that.columns));
			var rows = that.tbody.children(":not(.k-detail-row)");
			var row;
			var column;
			var columnIndex;
			var left;
			var right;
			var header;
			var footer;
			var groupHeader;
			var cell;
			for (i = 0; i < columns.length; i++) {
				column = columns[i];
				left = stickyWidths.left[i];
				right = stickyWidths.right[i];
				columnIndex = leafsCols.indexOf(column);
				header = that._getColumnHeader(column);
				header.addClass(STICKY_HEADER_CLASS);
				if (column.headerAttributes["class"] && column.headerAttributes["class"].indexOf(STICKY_HEADER_NO_BORDER_CLASS) !== -1) header.addClass(STICKY_HEADER_NO_BORDER_CLASS);
				setLeftAndRightStyles(header, left, right);
				if (column.columns) continue;
				if (that.footer) {
					footer = that.footer.find(".k-grid-footer-wrap tr.k-footer-template").children().filter(":not(.k-group-cell,.k-hierarchy-cell)").eq(columnIndex);
					footer.addClass(STICKY_FOOTER_CLASS);
					setLeftAndRightStyles(footer, left, right);
				}
				for (j = 0; j < rows.length; j++) {
					row = $(rows[j]);
					if (row.hasClass(GROUPING_ROW)) {
						groupHeader = row.find("." + column.groupHeaderColumnTemplateClass);
						groupHeader.addClass(STICKY_CELL_CLASS);
						setLeftAndRightStyles(groupHeader, left, right);
					} else {
						cell = row.children().filter(":not(.k-group-cell,.k-hierarchy-cell)").eq(columnIndex);
						cell.addClass(STICKY_CELL_CLASS);
						setLeftAndRightStyles(cell, left, right);
					}
				}
			}
		},
		_removeStickyStyles: function(columns) {
			var that = this;
			var i;
			var j;
			var leafsCols = leafColumns(nonLockedColumns(that.columns));
			var rows = that.tbody.children(":not(.k-detail-row)");
			var row;
			var column;
			var columnIndex;
			var header;
			var footer;
			var groupHeader;
			var cell;
			for (i = 0; i < columns.length; i++) {
				column = columns[i];
				columnIndex = leafsCols.indexOf(column);
				header = that._getColumnHeader(column);
				header.removeClass(STICKY_HEADER_CLASS);
				header.removeClass(STICKY_HEADER_NO_BORDER_CLASS);
				setLeftAndRightStyles(header, "", "");
				if (column.columns) {
					that._removeStickyStyles(column.columns);
					continue;
				}
				if (column.footerTemplate && that.footer) {
					footer = that.footer.find(".k-grid-footer-wrap tr.k-footer-template").children().filter(":not(.k-group-cell,.k-hierarchy-cell)").eq(columnIndex);
					footer.removeClass(STICKY_FOOTER_CLASS);
					setLeftAndRightStyles(footer, "", "");
				}
				for (j = 0; j < rows.length; j++) {
					row = $(rows[j]);
					if (row.hasClass(GROUPING_ROW)) {
						groupHeader = row.find("." + column.groupHeaderColumnTemplateClass);
						groupHeader.removeClass(STICKY_CELL_CLASS);
						setLeftAndRightStyles(groupHeader, "", "");
					} else {
						cell = row.children().filter(":not(.k-group-cell,.k-hierarchy-cell)").eq(columnIndex);
						cell.removeClass(STICKY_CELL_CLASS);
						setLeftAndRightStyles(cell, "", "");
					}
				}
			}
		},
		_getColumnHeader: function(column) {
			var that = this;
			return $("#" + column.headerAttributes.id).length ? $("#" + column.headerAttributes.id) : $("#" + that._cellId);
		},
		_sumColumnWidth: function(column) {
			var that = this;
			var width = 0;
			if (column.columns) width = that._sumCurrentWidths(leafColumns([column]));
			else width = that._sumCurrentWidths([column]);
			return width;
		},
		_sumCurrentWidths: function(cols) {
			var that = this;
			var width = 0;
			var colWidth = 0;
			var col;
			var header;
			var i;
			var length = cols.length;
			for (i = 0; i < length; i++) {
				col = cols[i];
				header = that._getColumnHeader(col);
				if (!col.hidden && columnMatchesMedia(col)) {
					colWidth = header.is(":visible") ? header.outerWidth() : col.width;
					width += colWidth ? parseInt(colWidth, 10) : 0;
				}
			}
			return width;
		},
		_belongsToGrid: function(element) {
			return this.wrapper[0] === element.closest(WRAPPER)[0];
		},
		getSelectedData: function() {
			const that = this;
			const selectable = that.selectable;
			const checkboxSelection = that._checkBoxSelection;
			const visibleColumns = visibleLeafColumns(that.columns);
			let result = [];
			if (selectable) {
				const selectedRanges = that.selectable?.selectedRanges();
				const selectedRangeNames = Object.keys(selectedRanges);
				const selectedSingleItems = that.selectable?.selectedSingleItems();
				for (var idx = 0; idx < selectedRangeNames.length; idx++) result = result.concat(that._mapSelectionToData(selectedRanges[selectedRangeNames[idx]], visibleColumns, null, true));
				if (selectedSingleItems.length) result = result.concat(that._mapSelectionToData(selectedSingleItems, visibleColumns, null, true));
				return result;
			}
			if (checkboxSelection) {
				const selectedRows = that.tbody.find("tr.k-selected").toArray().map(function(elem) {
					return $(elem);
				});
				if (selectedRows.length) result = that._mapSelectionToData(selectedRows, visibleColumns, null, true);
			}
			return result;
		},
		getSelectedDataByKeys: function() {
			var that = this, dataSource = that.dataSource, keys = that.selectedKeyNames(), visibleColumns = visibleLeafColumns(that.columns), key, dataItem, result = {};
			var columnMapHandler = function(col) {
				var result = {};
				if (!col.field) return;
				result[col.field] = dataItem[col.field];
				return result;
			};
			for (let i = 0; i < keys.length; i++) {
				key = keys[i];
				dataItem = dataSource.get(key);
				if (dataItem) result[dataItem.uid] = $.extend.apply({}, visibleColumns.map(columnMapHandler));
			}
			return Object.keys(result).map(function(id) {
				return result[id];
			});
		},
		exportSelectedToExcel: function(includeHeaders) {
			if (!kendo.excel || !kendo.ooxml) throw new Error("The excel export functionality depends on both kendo.excel.js and kendo.ooxml.js scripts, please make sure they are included.");
			var that = this;
			var excel = this.options.excel || {};
			var visibleColumns = visibleLeafColumns(that.columns);
			var exporter = new kendo.excel.ExcelExporter({});
			var columnHandler = function() {
				return { autoWidth: true };
			};
			var book = { sheets: [{
				columns: Array.apply(0, Array(visibleColumns.length)).map(columnHandler),
				rows: [],
				freezePane: {},
				filter: false
			}] };
			var selectedRanges = that.selectable.selectedRanges();
			var selectedRangeNames = Object.keys(selectedRanges);
			var selectedSingleItems = that.selectable.selectedSingleItems();
			var idx;
			var exportData = [];
			var hasLockedCols = that._isLocked() && lockedColumns(that.columns).length;
			var sortHandler = exportDataSort.bind(that);
			for (idx = 0; idx < selectedRangeNames.length; idx++) exportData = exportData.concat(that._mapSelectionToData(selectedRanges[selectedRangeNames[idx]], visibleColumns, isExcelExportableColumn));
			if (exportData.length) that._addRangeSelectionRows(book, exporter, exportData, includeHeaders);
			exportData = selectedSingleItems.length ? that._mapSelectionToData(selectedSingleItems, visibleColumns, isExcelExportableColumn) : [];
			if (exportData.length) {
				if (hasLockedCols) exportData = exportData.sort(sortHandler);
				that._addSingleSelectionRows(book, exporter, exportData, includeHeaders);
			}
			if (book.sheets[0].rows.length) {
				var workbook = new kendo.ooxml.Workbook(book);
				if (!workbook.options) workbook.options = {};
				workbook.options.skipCustomHeight = true;
				workbook.toDataURLAsync().then(function(dataURI) {
					kendo.saveAs({
						dataURI,
						fileName: book.fileName || excel.fileName,
						proxyURL: excel.proxyURL,
						forceProxy: excel.forceProxy
					});
				});
			}
		},
		exportSelectedToCSV: function(includeHeaders) {
			const selectedItems = this._getSelectedRowData();
			if (selectedItems.length) return this._saveCSVData(selectedItems, includeHeaders);
		},
		_getSelectedRowData: function() {
			if (!this.selectable && !this._checkBoxSelection) return [];
			const that = this;
			const selected = this.select();
			const seen = {};
			const items = [];
			const selectedKeys = that.options.persistSelection ? that.selectedKeyNames() : [];
			if (selected.length && !selected.eq(0).is(TR)) return [];
			if (selectedKeys.length) {
				for (let idx = 0; idx < selectedKeys.length; idx++) {
					const item = that.dataSource.get(selectedKeys[idx]);
					if (item && !seen[item.uid]) {
						seen[item.uid] = true;
						items.push(item.toJSON());
					}
				}
				return items;
			}
			selected.each(function() {
				const row = this.nodeName === "TR" ? $(this) : $(this).closest("tr");
				const item = that.dataItem(row);
				if (item && !seen[item.uid]) {
					seen[item.uid] = true;
					items.push(item.toJSON());
				}
			});
			return items;
		},
		_addSingleSelectionRows: function(book, exporter, data, includeHeaders) {
			var idx = 0;
			var visibleColumns = visibleLeafExportColumns(this.columns);
			var item;
			const exporterInstance = exporter._instance ?? exporter;
			for (idx = 0; idx < data.length; idx++) {
				item = data[idx];
				exporter.data = [item];
				this._setExporterColumns(exporterInstance, visibleColumns, item);
				this._createExportRows(book, exporterInstance, includeHeaders);
			}
		},
		_addRangeSelectionRows: function(book, exporter, data, includeHeaders) {
			var visibleColumns = visibleLeafExportColumns(this.columns);
			const exporterInstance = exporter._instance ?? exporter;
			exporter.data = data;
			this._setExporterColumns(exporterInstance, visibleColumns, data[0]);
			this._createExportRows(book, exporterInstance, includeHeaders);
		},
		_createExportRows: function(book, exporter, includeHeaders) {
			const exporterInstance = exporter._instance ?? exporter;
			book.sheets[0].rows = book.sheets[0].rows.concat(includeHeaders ? exporterInstance._rows() : exporterInstance._dataRows(exporterInstance.data, 0));
		},
		_setExporterColumns: function(exporter, columns, item) {
			const exporterInstance = exporter._instance ?? exporter;
			exporterInstance.columns = exporterInstance.options.columns = $.map(columns.filter(function(col) {
				return Object.keys(item).indexOf(col.field) >= 0;
			}), exporterInstance._prepareColumn);
		},
		_mapSelectionToData: function(elements, visibleColumns, columnsFilter, ignoreOffset) {
			var that = this;
			var isRowSelection = elements[0][0].nodeName === "TR";
			var dataItem;
			var result = {};
			var element;
			var curr;
			var field;
			var columnMapHandler = function(col) {
				var result = {};
				if (!col.field || columnsFilter && !columnsFilter(col)) return;
				result[col.field] = dataItem[col.field];
				return result;
			};
			var hasLockedCols = that._isLocked() && lockedColumns(that.columns).length;
			var column;
			for (var i = 0; i < elements.length; i++) {
				element = elements[i];
				dataItem = that.dataItem(isRowSelection ? element : element.closest(TR));
				if (isRowSelection) result[dataItem.uid] = $.extend.apply({}, visibleColumns.map(columnMapHandler));
				else {
					field = that._getCellField(element, hasLockedCols, ignoreOffset);
					if (!field) continue;
					curr = result[dataItem.uid];
					if (!curr) curr = result[dataItem.uid] = {};
					column = findColumnByField(visibleColumns, field);
					if (!column || columnsFilter && !columnsFilter(column)) continue;
					curr[field] = dataItem[field];
				}
			}
			return Object.keys(result).map(function(id) {
				result[id].uid = id;
				return result[id];
			});
		},
		_getCellField: function(cell, hasLockedCols, ignoreOffset) {
			const grid = this;
			const inLockedArea = hasLockedCols && $.contains(grid.lockedTable[0], cell[0]);
			const fieldAttr = kendo.attr("field");
			const index = kendo.attr("index");
			const lockedOffset = inLockedArea ? 0 : hasLockedCols;
			let indexOffset = 0;
			if (ignoreOffset) indexOffset = grid._trailingColumns();
			if (grid._isStackedMode()) return grid.table.find("div.k-grid-stack-cell[" + index + "='" + (cell.index() - indexOffset) + "']").attr(fieldAttr);
			else if (hasLockedCols) return grid.element.find(".k-grid-header-" + (inLockedArea ? "locked" : "wrap") + " th[" + index + "='" + (cell.index() + lockedOffset) + "']").attr(fieldAttr);
			else return grid.thead && grid.thead.find("th[" + index + "='" + (cell.index() - indexOffset) + "']").attr(fieldAttr);
		},
		_relatedRow: function(row) {
			var lockedTable = this.lockedTable;
			row = $(row);
			if (!lockedTable) return row;
			var table = row.closest(this.table.add(this.lockedTable));
			var index = table.find(">tbody>tr").index(row);
			table = table[0] === this.table[0] ? lockedTable : this.table;
			return table.find(">tbody>tr").eq(index);
		},
		_relatedCell: function(cell) {
			var lockedTable = this.lockedTable;
			cell = $(cell);
			if (!lockedTable) return cell;
			var table = cell.closest(this.table.add(this.lockedTable));
			var index = table.find(">tbody>tr>td").index(cell);
			table = table[0] === this.table[0] ? lockedTable : this.table;
			return table.find(">tbody>tr>td").index(index);
		},
		clearSelection: function() {
			var that = this;
			if (that.selectable && !that._checkBoxSelection) that.selectable.clear();
			if (that._hasAISelection) delete that._hasAISelection;
			if (that._checkBoxSelection) {
				that._deselectCheckRows(that.select());
				return;
			}
			if (that.options.persistSelection) that._persistSelectedRows();
			else that._selectedIds = {};
		},
		clearHighlight: function() {
			const that = this;
			if (that._highlightDescriptors) delete that._highlightDescriptors;
			if (that._highlightedItems && that._highlightedItems.length) {
				that._highlightedItems.removeClass(HIGHLIGHTED);
				delete that._highlightedItems;
			}
			if (that._hasAIHighlight) delete that._hasAIHighlight;
			that._syncPinnedHighlight();
		},
		_getElementsToProcess: function(data) {
			const that = this;
			const columns = that.columns;
			const lockedColumns = [];
			const nonLockedCols = [];
			const elements = [];
			const rowSelector = (uid) => `tr[data-uid='${uid}']`;
			const getColIndex = (cols, field) => cols.length && cols.findIndex((col) => col.field === field);
			grep(columns, (col) => {
				if (col.locked) lockedColumns.push(col);
				else nonLockedCols.push(col);
			});
			Object.entries(data).forEach(([key, value]) => {
				if (!key) return;
				const item = that.dataSource.get(key);
				if (!item) return;
				let row = that.tbody.find(rowSelector(item.uid));
				if (value === true) elements.push(row);
				else if (typeof value === "object") Object.keys(value).forEach((field) => {
					if (!field && !value[field]) return;
					let isLockedCell = false;
					let columnIndex = getColIndex(nonLockedCols, field);
					if (columnIndex === -1) {
						columnIndex = getColIndex(lockedColumns, field);
						isLockedCell = true;
					}
					if (isLockedCell) row = that._relatedRow(row);
					const td = row.find(`td:eq(${columnIndex})`);
					if (td.length) {
						elements.push(td);
						row = that.tbody.find(rowSelector(item.uid));
					}
				});
			});
			return elements;
		},
		_getElementsToHighlight: function(data) {
			return this._getElementsToProcess(data);
		},
		_getElementsToSelect: function(data) {
			return this._getElementsToProcess(data);
		},
		_restoreHighlight: function() {
			const that = this;
			const highlightDescriptors = that._highlightDescriptors;
			if (!highlightDescriptors || !Object.keys(highlightDescriptors).length) return;
			that.highlight(highlightDescriptors);
		},
		_persistHighlight: function(element) {
			const that = this;
			const highlightDescriptor = {};
			const isRow = element.is("tr");
			const idField = that.options.dataSource?.schema?.model?.id || "id";
			const uid = (isRow ? element : element.closest("tr")).data("uid");
			const item = that.dataSource.getByUid(uid);
			if (!item) return;
			const key = item[idField];
			if (isRow) highlightDescriptor[key] = true;
			else {
				const field = that._getCellField(element, that._isLocked(), true);
				if (!field) return;
				highlightDescriptor[key] = field;
			}
			if (that._highlightDescriptors) that._highlightDescriptors = that._mergeHighlightDescriptor(that._highlightDescriptors, highlightDescriptor);
			else that._highlightDescriptors = highlightDescriptor;
		},
		_mergeHighlightDescriptor: function(existingDescriptors, newDescriptor) {
			const merged = {};
			let key, existingValue, newValue;
			for (key in existingDescriptors) merged[key] = existingDescriptors[key];
			for (key in newDescriptor) {
				existingValue = merged[key];
				newValue = newDescriptor[key];
				if (existingValue === undefined) merged[key] = newValue;
				else if (newValue === true) merged[key] = true;
				else if (typeof newValue === "object" && newValue !== null && typeof existingValue === "object" && existingValue !== null) merged[key] = extend({}, existingValue, newValue);
				else merged[key] = existingValue;
			}
			return merged;
		},
		highlight: function(data) {
			const that = this;
			const lockedContainer = that.lockedTable;
			const isEmptyArray = Array.isArray(data) && !data.length;
			if (!data && !isEmptyArray) return that._highlightedItems || $();
			if (isEmptyArray) {
				that.clearHighlight();
				return;
			}
			const keys = typeof data === "object" && !Array.isArray(data) ? Object.keys(data) : [];
			const isHighlightDescriptor = keys.length && that.dataSource.get(keys[0]);
			let itemsToHighlight;
			if (isHighlightDescriptor) {
				that._highlightDescriptors = that._mergeHighlightDescriptor(that._highlightDescriptors || {}, data);
				itemsToHighlight = that._getElementsToHighlight(data);
			} else itemsToHighlight = Array.isArray(data) ? data : [data];
			const highlightRelatedRow = (item) => {
				const targetIsRow = item.is("tr");
				let relatedItem;
				if (!item) return;
				if (targetIsRow && lockedContainer?.length) {
					relatedItem = that._relatedRow(item);
					item = item.add(relatedItem);
				}
				return item;
			};
			itemsToHighlight.forEach(function(item) {
				const target = highlightRelatedRow($(item));
				if (target && !isHighlightDescriptor) that._persistHighlight(target);
				if (target?.length) {
					if (that._highlightedItems && that._highlightedItems.index(target) !== -1) return;
					target.addClass(HIGHLIGHTED);
					if (that._highlightedItems) that._highlightedItems = that._highlightedItems.add(target);
					else that._highlightedItems = target;
				}
			});
			that._syncPinnedHighlight();
		},
		select: function(items) {
			var that = this, selectable = that.selectable, cell = kendo.ui.Selectable.parseOptions(this.options.selectable).cell;
			items = that.table.add(that.lockedTable).find(items);
			if (items.length) {
				if (selectable && !selectable.options.multiple) {
					selectable.clear();
					items = items.first();
				}
				if (that._isLocked()) items = items.add(items.map(function() {
					if (cell) return that._relatedCell(this);
					else return that._relatedRow(this);
				}));
				if (selectable && !that._checkBoxSelection) selectable.value(items);
				else {
					that._checkRows(items);
					if (that.select().length === that.items().length) that._toggleHeaderCheckState(true);
				}
				if (!cell) that._persistSelectedRows();
				that._syncPinnedSelection();
				return;
			}
			return selectable ? selectable.value() : that.items().filter(".k-selected");
		},
		_initSelectableAggregates: function() {
			var that = this;
			if (!that.options.selectable) return;
			if (!that._selectableAggregatesOptions) that._selectableAggregatesOptions = that._parseSelectableAggregatesOptions();
			if (that._selectableAggregatesOptions.count) that._cellAggregates = { count: 0 };
		},
		_calculateAggregatesForSelected: function() {
			var that = this, options = that.options, selectedData = that.getSelectedDataByKeys(), selectable = that.options.selectable, cellAggregates = selectable.cellAggregates, cellsLength = visibleLeafColumns(that.columns).filter((col) => !col.selectable && !col.draggable & !col.command).length, columnFields = getColumnsFields(options.columns), isCellSelection = kendo.ui.Selectable.parseOptions(selectable).cell, dataItem, type, value, numberAggregates = [], dateAggregates = [], booleanAggregates = [], count, min, max, sum, average, earliest, latest, isTrue, isFalse;
			if (!cellAggregates) return;
			if (isCellSelection) selectedData = that.getSelectedData();
			cellAggregates = that._selectableAggregatesOptions;
			for (let i = 0; i < selectedData.length; i++) {
				dataItem = selectedData[i];
				for (let j = 0; j < columnFields.length; j++) {
					value = dataItem[columnFields[j]];
					type = getType(value);
					switch (type) {
						case "number":
							numberAggregates.push(value);
							break;
						case "date":
							dateAggregates.push(value);
							break;
						case "boolean":
							booleanAggregates.push(value);
							break;
						default: break;
					}
				}
			}
			if (cellAggregates.count) count = isCellSelection ? cellsExcludingSpecialColumns(that.select()).length : selectedData.length * cellsLength;
			if (numberAggregates.length) {
				max = cellAggregates.max ? numberAggregates.reduce((acc, current) => Math.max(acc, current)) : null;
				min = cellAggregates.min ? numberAggregates.reduce((acc, current) => Math.min(acc, current)) : null;
				sum = cellAggregates.sum ? numberAggregates.reduce((acc, current) => acc + current) : null;
				average = cellAggregates.average ? numberAggregates.reduce((acc, current) => acc + current) / numberAggregates.length : null;
			}
			if (dateAggregates.length) {
				earliest = cellAggregates.earliest ? dateAggregates.reduce((acc, current) => new Date(Math.min(acc, current))) : null;
				latest = cellAggregates.latest ? dateAggregates.reduce((acc, current) => new Date(Math.max(acc, current))) : null;
			}
			if (booleanAggregates.length) {
				isTrue = cellAggregates.isTrue ? booleanAggregates.filter((b) => b === true).length : null;
				isFalse = cellAggregates.isFalse ? booleanAggregates.filter((b) => b === false).length : null;
			}
			that._cellAggregates = {
				count,
				max,
				min,
				sum,
				average,
				earliest,
				latest,
				isTrue,
				isFalse
			};
			if (that.statusBar) that._statusBar();
		},
		_parseSelectableAggregatesOptions: function() {
			var cellAggregates = this.options.selectable.cellAggregates, result = {};
			if (isArray(cellAggregates)) {
				for (let i = 0; i < cellAggregates.length; i++) result[cellAggregates[i]] = true;
				return result;
			}
			return {
				count: true,
				min: true,
				max: true,
				sum: true,
				average: true,
				earliest: true,
				latest: true,
				isTrue: true,
				isFalse: true
			};
		},
		_toggleHeaderCheckState: function(checked) {
			const that = this;
			const stacked = that._isStackedMode();
			const toolbar = that.wrapper.find(".k-toolbar");
			const selectAllTool = toolbar.find(CHECKBOXINPUT);
			let container = stacked ? toolbar : that.thead.add(that.lockedHeader);
			let checkboxSelector = stacked ? CHECKBOXINPUT : "tr input[data-role='checkbox'].k-select-checkbox.k-checkbox";
			const toggleState = (element, state) => {
				if (element.length) {
					const ariaLabel = state ? "Deselect all rows" : "Select all rows";
					element.prop("checked", state).attr(ARIA_CHECKED, state).attr(ARIA_LABEL, ariaLabel);
				}
			};
			if (stacked) toggleState(selectAllTool, checked);
			else {
				toggleState(container.find(checkboxSelector), checked);
				toggleState(selectAllTool, checked);
			}
		},
		_uncheckCheckBoxes: function() {
			var that = this;
			that.table.add(that.lockedTable).find("tbody input[data-role='checkbox'].k-select-checkbox.k-checkbox").attr(ARIA_CHECKED, false).prop("checked", false).attr(ARIA_LABEL, "Select row");
		},
		_deselectCheckRows: function(items) {
			var that = this, rangeSelectedAttr = kendo.attr("range-selected");
			items = that.table.add(that.lockedTable).find(items);
			if (that._isLocked()) items = items.add(items.map(function() {
				return that._relatedRow(this);
			}));
			items.each(function() {
				$(this).removeClass(SELECTED).removeAttr(rangeSelectedAttr).find(CHECKBOXINPUT).attr(ARIA_CHECKED, false).prop("checked", false).attr(ARIA_LABEL, "Select row");
			});
			that._toggleHeaderCheckState(false);
			that._persistSelectedRows();
		},
		_checkRows: function(items) {
			items.each(function() {
				$(this).addClass(SELECTED).find(CHECKBOXINPUT).prop("checked", true).attr(ARIA_LABEL, "Deselect row").attr(ARIA_CHECKED, true);
			});
		},
		_persistSelectedRows: function() {
			var that = this, key, dataItem, allRows = that.items(), schema = that.dataSource.options.schema, modelId, selectedViewIds = {};
			if (!schema || !schema.model || !that._data) return;
			modelId = that._getSchemaIdField();
			if (!modelId) return;
			if (!kendo.ui.Selectable.parseOptions(that.options.selectable).multiple && !that._checkBoxSelection) that._selectedIds = {};
			that.select().each(function() {
				dataItem = that.dataItem(this);
				selectedViewIds[dataItem[modelId]] = true;
			});
			for (var i = 0; i < allRows.length; i++) {
				dataItem = that.dataItem(allRows[i]);
				key = dataItem[modelId];
				if (selectedViewIds[key]) that._selectedIds[key] = true;
				else delete that._selectedIds[key];
			}
		},
		selectedKeyNames: function() {
			var that = this, ids = [];
			for (var property in that._selectedIds) ids.push(property);
			ids.sort();
			return ids;
		},
		_updateCurrentAttr: function(current, next, skipFocus) {
			var headerId = $(current).data("headerId");
			var nextId;
			var descId;
			$(current).removeClass(FOCUSED);
			this.table.removeAttr(ARIA_ACTIVEDESCENDANT);
			if (headerId) {
				headerId = headerId.replace(this._cellId, "");
				$(current).attr(ID, headerId);
			} else $(current).removeAttr(ID);
			nextId = next.attr(ID);
			if (nextId != this._cellId) next.data("headerId", nextId);
			if (!!nextId) descId = nextId;
			else next.attr(ID, this._cellId);
			if (!skipFocus) next.addClass(FOCUSED);
			this.table.attr(ARIA_ACTIVEDESCENDANT, descId || this._cellId);
			this._current = next;
		},
		_scrollCurrent: function() {
			const current = this._current;
			const scrollable = this.options.scrollable;
			if (!current || !scrollable) return;
			var row = current.parent();
			var tableContainer = row.closest("table").parent();
			var isInLockedContainer = tableContainer.is(".k-grid-content-locked,.k-grid-header-locked");
			var isInContent = tableContainer.is(".k-grid-content-locked,.k-grid-content,.k-virtual-scrollable-wrap");
			var scrollableContainer = $(this.content).find(">.k-virtual-scrollable-wrap").addBack().last()[0];
			if (isInContent) if (this.virtualScroll) {
				var rowIndex = Math.max(inArray(row[0], this._items(row.parent())), 0);
				if (this.virtualScroll.rows) {
					this._rowVirtualIndex = this.virtualScrollable.itemIndex(rowIndex);
					this.virtualScrollable.scrollIntoView(row);
				} else {
					this._rowVirtualIndex = rowIndex;
					this._scrollTo(this._relatedRow(row)[0], scrollableContainer);
				}
			} else this._scrollTo(this._relatedRow(row)[0], scrollableContainer);
			if (this.lockedContent) this.lockedContent[0].scrollTop = scrollableContainer.scrollTop;
			if (!isInLockedContainer) this._scrollTo(current[0], scrollableContainer);
		},
		_findGroupedItem: function(data, id, idField) {
			const that = this;
			let item;
			for (let i = 0; i < data.length; i++) {
				const group = data[i];
				if (group.field === idField) {
					if (group.value === id) if (group.items[0].uid && group.items[0][idField]) item = group.items[0];
					else item = that._findGroupedItem(group.items, id, idField);
				} else if (group.items[0].uid && group.items[0][idField]) item = group.items.find((item) => item[idField] === id);
				else item = that._findGroupedItem(group.items, id, idField);
				if (item) return item;
			}
		},
		_findClosestGroupingRow: function(htmlRow, rowsCount, hiddenGroupingRows) {
			const that = this;
			const hasGroupingRows = that.options.groupable && that.dataSource.group().length > 0;
			let i = rowsCount ? rowsCount : 0;
			let j = hiddenGroupingRows ? hiddenGroupingRows : 0;
			if (!hasGroupingRows || !htmlRow) return {
				targetRow: htmlRow,
				rowsToTarget: i,
				hiddenGroupingRows: j
			};
			const prevSibling = $(htmlRow.previousSibling);
			if (prevSibling && prevSibling.hasClass(GROUPING_ROW)) {
				let result = {
					targetRow: prevSibling,
					rowsToTarget: i,
					hiddenGroupingRows: j
				};
				if (prevSibling.css("display") === "none") result = that._findClosestGroupingRow(prevSibling[0], i, j + 1);
				return result;
			} else {
				const result = that._findClosestGroupingRow(prevSibling[0], i + 1, j);
				if (result) return {
					targetRow: result.targetRow,
					rowsToTarget: result.rowsToTarget,
					hiddenGroupingRows: j
				};
			}
		},
		_checkItemAlreadyLoaded: function(id, idField) {
			const ranges = this.dataSource._ranges;
			let item;
			for (let i = 0; i < ranges.length; i++) {
				item = ranges[i].data.find((item) => item[idField] === id);
				if (item) return {
					loadedItem: item,
					page: i
				};
			}
		},
		scrollToItem: function(id, callback) {
			const that = this, options = that.options, dataSource = that.dataSource, groups = dataSource.group(), pageSize = dataSource.pageSize(), idField = that._getSchemaIdField(), scrollable = options.scrollable, scrollableContainer = that.wrapper.find(".k-grid-content.k-auto-scrollable"), rowHeight = kendo._outerHeight(that.tbody.find(`tr:not(.${GROUPING_ROW})`)), isVirtual = scrollable && scrollable.virtual && (scrollable.virtual === "rows" || scrollable.virtual === true);
			let rootGroupingRow = options.groupable && that.wrapper.find(`.${GROUPING_ROW}:first-child`);
			let previouslyScrolledItems = {};
			let targetRowIsHidden = false;
			let groupingRowHeight = 0;
			if (that._scrolledItems) previouslyScrolledItems = that._scrolledItems;
			if (isVirtual && groups.length === 0) that.virtualScrollable._alwaysScrollTop = true;
			if (!id || !idField) return;
			const currentView = dataSource.view();
			let item = currentView.find((item) => item[idField] == id);
			if (groups.length > 0) {
				item = that._findGroupedItem(currentView, id, idField);
				groupingRowHeight = kendo._outerHeight(rootGroupingRow[0]);
			}
			if (!item && isVirtual) {
				if (!that.virtualScrollable._programmaticallyScrolling || that.virtualScrollable._programmaticallyScrolling.state() === "resolved") that.virtualScrollable._programmaticallyScrolling = $.Deferred();
				callback && typeof callback === "function" && callback({ success: (index) => {
					let itemIndex = index;
					itemIndex = typeof itemIndex !== "number" ? Number(itemIndex) : itemIndex;
					if (isNaN(itemIndex)) return;
					const serverPaging = dataSource.options.serverPaging;
					let page = math.floor(itemIndex / pageSize);
					if (serverPaging) {
						const itemLoaded = that._checkItemAlreadyLoaded(id, idField);
						if (itemLoaded) page = itemLoaded.page;
					}
					const allRows = scrollableContainer.find(TR);
					const lastRowOffsetTop = allRows[allRows.length - 1].offsetTop;
					const pageEndOffset = pageSize / allRows.length * lastRowOffsetTop;
					const itemsToTarget = itemIndex - page * pageSize - 1;
					const scrollPosition = page * pageEndOffset + itemsToTarget * rowHeight;
					that._scrollingUp = that.virtualScrollable.verticalScrollbar.scrollTop() > scrollPosition;
					that._scrollOffset = scrollPosition;
					that.virtualScrollable._scrollTo(scrollPosition);
					that.virtualScrollable.verticalScrollbar.trigger(SCROLL);
					return that.virtualScrollable._programmaticallyScrolling.done(() => {
						that.scrollToItem(id);
					});
				} });
				return;
			}
			const uid = item && item.uid;
			if (!uid) return;
			let element = $(`[data-uid=${uid}]`);
			let { targetRow, rowsToTarget, hiddenGroupingRows } = that._findClosestGroupingRow(element[0]);
			if (!element || !targetRow) return;
			else if (groups.length > 0 && element.css("display") === "none") {
				element = targetRow;
				targetRowIsHidden = true;
			}
			const hiddenGroupingRowsOffset = targetRowIsHidden ? hiddenGroupingRows * groupingRowHeight + rowHeight : 0;
			let wrapperPosition = element[0].offsetTop - ((rowsToTarget + groups.length) * groupingRowHeight - hiddenGroupingRowsOffset);
			let scrollPosition = wrapperPosition;
			if (isVirtual) {
				if (previouslyScrolledItems && previouslyScrolledItems[id]) {
					const scrollerPosition = Math.floor(that.virtualScrollable.verticalScrollbar.scrollTop());
					const prevScrollDown = Math.floor(previouslyScrolledItems[id].scrollingDownOffset);
					const prevScrollUp = Math.floor(previouslyScrolledItems[id].scrollingUpOffset);
					if (scrollerPosition === prevScrollUp || scrollerPosition === prevScrollDown) return;
					that._scrollingUp = scrollerPosition > scrollPosition;
					if (that._scrollingUp) scrollPosition = prevScrollUp ?? scrollPosition;
					else scrollPosition = prevScrollDown ?? scrollPosition;
				}
				if (that._scrollOffset) if (!that._scrollingUp) scrollPosition += that._scrollOffset;
				else {
					if (that._scrollOffset === wrapperPosition) that._scrollOffset += rowHeight;
					scrollPosition = wrapperPosition - that.virtualScrollable._scrollTop + that._scrollOffset;
				}
				that.virtualScrollable._scrollTo(wrapperPosition, scrollPosition);
				if (!previouslyScrolledItems[id]) previouslyScrolledItems[id] = {};
				if (that._scrollingUp) {
					if (previouslyScrolledItems[id] && !previouslyScrolledItems[id].scrollingUpOffset) previouslyScrolledItems[id].scrollingUpOffset = scrollPosition;
				} else if (previouslyScrolledItems[id] && !previouslyScrolledItems[id].scrollingDownOffset) previouslyScrolledItems[id].scrollingDownOffset = scrollPosition;
			} else scrollableContainer.scrollTop(scrollPosition);
			if (that.virtualScrollable) {
				if (that._scrollOffset) delete that._scrollOffset;
				if (that._scrollingUp) delete that._scrollingUp;
			}
			that._scrolledItems = previouslyScrolledItems;
		},
		current: function(next) {
			return this._setCurrent(next, true);
		},
		_setCurrent: function(next, preventTrigger, preventScroll, skipFocus) {
			var current = this._current;
			next = $(next);
			if (current && next && current.length && next.length && current.closest(".k-filter-row").length > 0 && next.closest(".k-filter-row").length === 0) this._filterFocusable().attr(TABINDEX, -1);
			if (next.length) {
				if (!current || current[0] !== next[0]) {
					var parent = next.parent();
					parent.children(DATA_CELL);
					var colspan = parseInt(parent.children().first().attr("colspan"), 10);
					if (this._hasVirtualColumns()) {
						var virtualSiblings = parent.children(DATA_CELL_HIDDENINCLUDED).not(".k-group-cell");
						this._virtualCellIndex = (colspan > 1 ? colspan : 0) + virtualSiblings.index(next);
					}
					this._updateCurrentAttr(current, next, skipFocus);
					if (!preventScroll) this._scrollCurrent();
					if (!preventTrigger) this.trigger(NAVIGATE, { element: next });
				}
			}
			if (next && next.length) this._lastCellIndex = next.parent().children(".k-group-cell, .k-grid-stack-content," + DATA_CELL_HIDDENINCLUDED).index(next);
			this._updateSelctCheckbox(current, next);
			return this._current;
		},
		_removeCurrent: function() {
			if (this._current) {
				this._current.removeClass(FOCUSED);
				this._current = null;
			}
		},
		_updateSelctCheckbox: function(current, next) {
			var nextCheckbox;
			if (next && next.length) {
				nextCheckbox = next.find(".k-select-checkbox");
				if (nextCheckbox.length > 0) nextCheckbox.trigger("focus");
				else if (current && current.find(".k-select-checkbox").length > 0) focusTable(this.table, true);
			}
		},
		_stickyScrollInsets: function() {
			return {
				top: this._stickyGroupHeaderContainer ? Math.ceil(this._stickyGroupHeaderContainer[0].getBoundingClientRect().height) : 0,
				bottom: this._stickyGroupFooterContainer ? Math.ceil(this._stickyGroupFooterContainer[0].getBoundingClientRect().height) : 0
			};
		},
		_scrollTo: function(element, container) {
			var elementToLowercase = element.tagName.toLowerCase();
			var isHorizontal = elementToLowercase === "td" || elementToLowercase === "th";
			var table = $(element).closest("table")[0];
			var elementOffsetDir = element[isHorizontal ? "offsetWidth" : "offsetHeight"];
			var containerScroll = container[isHorizontal ? "scrollLeft" : "scrollTop"];
			var containerOffsetDir = container[isHorizontal ? "clientWidth" : "clientHeight"];
			var elementOffset = $(element).css("position") === "relative" && isRtl && isHorizontal ? Math.abs(table.offsetLeft - element.offsetLeft) : element[isHorizontal ? "offsetLeft" : "offsetTop"];
			var bottomDistance = elementOffset + elementOffsetDir;
			var result = 0;
			var ieCorrection = 0;
			var firefoxCorrection = 0;
			var stickyTopOffset = 0;
			var stickyBottomOffset = 0;
			if (!isHorizontal) {
				const insets = this._stickyScrollInsets();
				stickyTopOffset = insets.top;
				stickyBottomOffset = insets.bottom;
			}
			if (isRtl && isHorizontal) {
				if (browser.msie || browser.edge) ieCorrection = table.offsetLeft;
				else if (browser.mozilla || browser.webkit && browser.version > 85) firefoxCorrection = table.offsetLeft - kendo.support.scrollbar();
			}
			containerScroll = Math.abs(containerScroll + ieCorrection - firefoxCorrection);
			if (containerScroll + stickyTopOffset > elementOffset) result = elementOffset - stickyTopOffset;
			else if (bottomDistance > containerScroll + containerOffsetDir - stickyBottomOffset) if (elementOffsetDir <= containerOffsetDir) result = bottomDistance - containerOffsetDir + stickyBottomOffset;
			else result = elementOffset - stickyTopOffset;
			else result = containerScroll;
			result = Math.max(0, Math.abs(result + ieCorrection) + firefoxCorrection);
			const scrollProp = isHorizontal ? "scrollLeft" : "scrollTop";
			const previous = container[scrollProp];
			container[scrollProp] = result;
			if (!isHorizontal && previous !== result && (this._hasStickyGroupHeaders() || this._hasStickyGroupFooters())) {
				this._updateStickyGroups();
				const updated = this._stickyScrollInsets();
				if (updated.top !== stickyTopOffset || updated.bottom !== stickyBottomOffset) {
					if (containerScroll + updated.top > elementOffset) result = elementOffset - updated.top;
					else if (bottomDistance > result + containerOffsetDir - updated.bottom) {
						if (elementOffsetDir <= containerOffsetDir) result = bottomDistance - containerOffsetDir + updated.bottom;
					}
					container[scrollProp] = Math.max(0, result);
				}
			}
		},
		_navigatable: function() {
			var that = this;
			if (!that.options.navigatable) return;
			const stacked = that._isStackedMode();
			var dataTables = that.table.add(that.lockedTable);
			var headerTables = !stacked && that.thead.parent().add($(">table", that.lockedHeader));
			var tables = dataTables;
			if (that.options.scrollable) tables = tables.add(headerTables);
			this._navigatableTables = tables;
			if (headerTables) this._headertables = headerTables;
			tables.off("mousedown.kendoGrid focus.kendoGrid focusout.kendoGrid keydown.kendoGrid");
			if (headerTables) headerTables.find("a.k-link").attr("tabIndex", -1);
			dataTables.on("keydown.kendoGrid", ".k-detail-cell", function(e) {
				if (e.target !== e.currentTarget) e.stopImmediatePropagation();
			});
			tables.on(kendo.support.touch ? "touchstart.kendoGrid" : "mousedown.kendoGrid", "tr:not(.k-footer-template):visible>:not(.k-group-cell):not(.k-detail-cell):not(.k-hierarchy-cell):visible", tableClick.bind(that)).on("focus.kendoGrid", that._tableFocus.bind(that)).on("focusout.kendoGrid", that._tableBlur.bind(that)).on("keydown.kendoGrid", that, that._tableKeyDown.bind(that));
			that.wrapper.on(kendo.support.touch ? "touchstart.kendoGrid" : "mousedown.kendoGrid", ".k-grid-pinned-container tr:not(.k-footer-template):visible>:not(.k-group-cell):not(.k-detail-cell):not(.k-hierarchy-cell):visible", tableClick.bind(that));
			that.wrapper.on("keydown.kendoGrid", ".k-grid-pinned-container", that._tableKeyDown.bind(that));
			that._filterFocusable().on("focus", that._filterFocus.bind(that));
		},
		_filterFocus: function(e) {
			var header = e.target.closest("th");
			this._filterFocusable().attr(TABINDEX, 0);
			this._setCurrent(header);
			$(header).removeClass(FOCUSED);
		},
		_tableFocus: function() {
			var current = this.current();
			var table = this.lockedTable ? this.lockedTable : this.table;
			if (!this._stickyGroupFocusing) {
				this._clearStickyGroupFocus();
				if (current && current.is(":visible")) current.addClass(FOCUSED);
				else if (this._virtualColScroll) this._setCurrent(table.find(NAVROW).first().children(NAVCELL).first(), true, true);
				else this._setCurrent(table.find(NAVROW).first().children(NAVCELL).first());
			}
			this.table.attr(TABINDEX, 0);
		},
		_tableBlur: function() {
			var current = this.current();
			if (current) current.removeClass(FOCUSED);
			this._clearStickyGroupFocus();
		},
		_findCellIndex: function(columns, startIndex, reversed) {
			var cellIndex;
			var i;
			if (reversed) for (i = startIndex; i >= 0; i--) {
				cellIndex = i;
				if (!columns[i].hidden) break;
			}
			else for (i = startIndex; i < columns.length; i++) {
				cellIndex = i;
				if (!columns[i].hidden) break;
			}
			return cellIndex;
		},
		_scrollToColumn: function(key, e) {
			if (this._virtualCellIndex === undefined) return false;
			var that = this;
			var cellIndex = that._virtualCellIndex;
			var leafsCols = leafColumns(nonLockedColumns(that.columns));
			var scrollWidth = 0;
			if (key == (isRtl ? keys.LEFT : keys.RIGHT) && cellIndex !== leafsCols.length - 1) cellIndex = that._findCellIndex(leafsCols, cellIndex + 1);
			else if (key == (isRtl ? keys.RIGHT : keys.LEFT) && cellIndex) cellIndex = that._findCellIndex(leafsCols, cellIndex - 1, true);
			else if (key == keys.HOME) cellIndex = that._findCellIndex(leafsCols, 0);
			else if (key == keys.END) cellIndex = that._findCellIndex(leafsCols, leafsCols.length - 1, true);
			for (var i = 0; i < cellIndex; i++) scrollWidth += leafsCols[i].width;
			that._virtualCellIndex = cellIndex;
			if (e) {
				e.preventDefault();
				e.stopPropagation();
			}
			let scrollable = that.content;
			if (that._hasVirtualRows() && that._hasVirtualColumns()) scrollable = that.virtualScrollable.wrapper;
			kendo.scrollLeft(scrollable, scrollWidth);
			return true;
		},
		_tableKeyDown: function(e) {
			if (this._handleStickyGroupKeyDown(e)) return;
			let current = this.current(), currentTable = current && current.closest(".k-grid-table")[0], virtualScroll = this.virtualScroll || {}, requestInProgress = this.virtualScrollable && this.virtualScrollable.fetching(), target = $(e.target), canHandle = !e.isDefaultPrevented() && (!target.is(":button,a,:input:not(.k-select-checkbox),a>.k-icon,a>.k-svg-icon") || this._isFocusableGridElement(current) && !target.closest(".k-grid-stack-edit-cell").length);
			if (e.altKey && e.keyCode == keys.DOWN) {
				this.current().find(".k-grid-filter-menu, .k-grid-column-menu").click();
				e.stopImmediatePropagation();
				return;
			}
			if (requestInProgress) {
				e.preventDefault();
				return;
			}
			if (virtualScroll.columns && (!current || !document.body.contains(current[0])) && this._scrollToColumn(e.keyCode, e)) return;
			if (!current) current = $(this.lockedTable).add(this.options.scrollable ? this.table : this.tbody).find(NAVROW).first().children(NAVCELL).first();
			if (!current.length) return;
			var handled = false;
			if (!e.isDefaultPrevented() && e.keyCode === keys.F10) handled = this._focusToolbar();
			if (canHandle && e.keyCode == keys.UP) handled = this._moveUp(current, e.shiftKey, e.ctrlKey);
			if (canHandle && e.keyCode == keys.DOWN) handled = this._moveDown(current, e.shiftKey, e.ctrlKey);
			if (canHandle && e.keyCode == (isRtl ? keys.LEFT : keys.RIGHT)) handled = this._moveRight(current, e.altKey, e.shiftKey, e.ctrlKey, currentTable);
			if (canHandle && e.keyCode == (isRtl ? keys.RIGHT : keys.LEFT)) handled = this._moveLeft(current, e.altKey, e.shiftKey, e.ctrlKey, currentTable);
			if (canHandle && e.keyCode == keys.PAGEDOWN) handled = this._handlePageDown();
			if (canHandle && e.keyCode == keys.PAGEUP) handled = this._handlePageUp();
			if (canHandle && e.keyCode == keys.HOME) handled = this._handleHome(current, e.ctrlKey);
			if (canHandle && e.keyCode == keys.END) handled = this._handleEnd(current, e.ctrlKey);
			if (canHandle && e.keyCode == keys.SPACEBAR) handled = this._handleSpaceKey(current, e.ctrlKey);
			if (e.keyCode == keys.ENTER || e.keyCode == keys.F2) handled = this._handleEnterKey(current, currentTable, target, e.keyCode == keys.F2);
			if (e.keyCode == keys.ESC) handled = this._handleEscKey(current);
			if (e.keyCode == keys.TAB) handled = this._handleTabKey(current, currentTable, e.shiftKey, target);
			if (e.keyCode === keys.DELETE || e.keyCode === keys.BACKSPACE) handled = this._handleDeletion(e);
			if (handled) {
				e.preventDefault();
				e.stopPropagation();
			}
		},
		_focusToolbar: function() {
			var focusable = this.wrapper.find(".k-grid-toolbar [tabindex=0]");
			if (focusable.length > 0) {
				focusable.first().addClass(".k-focus").trigger("focus");
				return true;
			}
			return false;
		},
		_focusFocusable: function(current, next, preventTrigger, preventScroll, eventData) {
			this._ensureStickyNavTargetVisible(next);
			let focusable = this._isFocusableGridElement(next) && next.find(FOCUSABLE);
			if (!focusable || !focusable.length) {
				if (next.is(FOCUSABLE)) focusable = next;
			}
			if (this._containerHasActiveElement(current) && current.find(FOCUSABLE).length !== 1 || !next.length) return;
			focusTable(this.table, true);
			this._setCurrent(next, preventTrigger, preventScroll);
			if (focusable.length === 1 && !focusable.is("table")) {
				focusable.trigger("focus");
				eventData?.preventDefault();
			}
		},
		_containerHasActiveElement: function(container) {
			return container.find(activeElement()).length;
		},
		_isFocusableGridElement: function(element) {
			if (!element) return false;
			return element.is(FOCUSABLE_GRID_ELEMENT_SELECTORS) || element.has(FOCUSABLE_GRID_ELEMENT_SELECTORS).length;
		},
		_moveLeft: function(current, altKey, shiftKey, ctrlKey, currentTable) {
			var next, index;
			var row = current.parent();
			var container = row.parent();
			if (altKey) if (row.hasClass(GROUPING_ROW)) this.collapseGroup(row);
			else this.collapseRow(row);
			else if (ctrlKey && current.is(".k-header") && this.options.reorderable) this._moveColumn(current, true);
			else {
				index = container.find(NAVROW).index(row);
				next = this._prevHorizontalCell(container, current, index);
				if (!next[0] && !this._containerHasActiveElement(current)) if (shiftKey) if (this.lockedTable) {
					next = this._relatedRow(row);
					if ($.contains(this.lockedTable[0], row[0])) next = next.prevAll(ITEMROW).first();
					next = next.children(DATA_CELL).last();
				} else next = this._tabNext(current, currentTable, true);
				else {
					container = this._horizontalContainer(container);
					next = this._prevHorizontalCell(container, current, index);
					if (next[0] !== current[0]) focusTable(this.table, true);
				}
				this._focusFocusable(current, next);
			}
			return true;
		},
		_moveRight: function(current, altKey, shiftKey, ctrlKey, currentTable) {
			var next, index;
			var row = current.parent();
			var container = row.parent();
			if (altKey) if (row.hasClass(GROUPING_ROW)) this.expandGroup(row);
			else this.expandRow(row);
			else if (ctrlKey && current.is(".k-header") && this.options.reorderable) this._moveColumn(current, false);
			else {
				index = container.find(NAVROW).index(row);
				next = this._nextHorizontalCell(container, current, index);
				if ((!next[0] || next[0] === current[0]) && !this._containerHasActiveElement(current)) if (shiftKey) if (this.lockedTable) {
					next = this._relatedRow(row);
					if ($.contains(this.table[0], row[0])) next = next.nextAll(ITEMROW).first();
					next = next.children(DATA_CELL).first();
				} else next = this._tabNext(current, currentTable, false);
				else {
					container = this._horizontalContainer(container, true);
					next = this._nextHorizontalCell(container, current, index);
					if (next[0] !== current[0]) focusTable(this.table, true);
				}
				this._focusFocusable(current, next);
			}
			return true;
		},
		_moveUp: function(current, shiftKey, ctrlKey) {
			var container = current.parent().parent();
			var next, cellIndex, index, oldIndex;
			if (this._isPinnedContainer(current)) {
				const pos = this._pinnedPosition(current);
				const ci = Math.max(current.parent().children(DATA_CELL_HIDDENINCLUDED).index(current), this._lastCellIndex || 0);
				next = this._pinnedVerticalCell(current, false);
				if (!next.length) {
					if (pos === "top") {
						const theadContainer = this.thead;
						if (theadContainer && theadContainer.length) {
							if (theadContainer.children(NAVROW).last().length) {
								const groupOffset = current.parent().children(".k-group-cell").length;
								next = leafDataCells(theadContainer).eq(ci - groupOffset);
							}
						}
					} else if (pos === "bottom") {
						const bodyRows = this.tbody.children(NAVROW);
						if (bodyRows.length) {
							const cells = bodyRows.last().children(DATA_CELL_HIDDENINCLUDED);
							next = cells.length > ci ? cells.eq(ci) : cells.last();
						}
					}
				}
				if (next && next.length) {
					const tmp = this._lastCellIndex || 0;
					focusTable(this.table, true);
					this._setCurrent(this._findVisibleCell(next));
					this._lastCellIndex = tmp;
				}
				return true;
			}
			if (shiftKey) {
				next = current.parent();
				next = next.prevAll(ITEMROW).first();
				next = current.parent().is(ITEMROW) ? next.children().eq(current.index()) : next.children(DATA_CELL).last();
			} else if (ctrlKey && current.parent().is(ITEMROW) && this._hasReorderableRows()) {
				cellIndex = current.index();
				next = current.parent();
				next = next.prevAll(ITEMROW).first();
				index = this.tbody.children(ITEMROW).index(next);
				oldIndex = this.tbody.children(ITEMROW).index(current.parent());
				if (index >= 0 && !this.trigger(ROWREORDER, {
					oldIndex,
					newIndex: index,
					row: current.parent()
				})) {
					this.reorderRows(current.parent(), index);
					next = this.tbody.children(ITEMROW).eq(index).children().eq(cellIndex);
				}
			} else {
				next = this._prevVerticalCell(container, current);
				if (!next[0]) {
					const isInTbody = container[0] === this.tbody[0];
					if (this._isPinnable() && isInTbody && this._pinnedTopRows && this._pinnedTopRows.length) {
						const ci = Math.max(current.parent().children(DATA_CELL_HIDDENINCLUDED).index(current), this._lastCellIndex || 0);
						next = this._pinnedBoundaryCellByPosition("top", false, ci);
					}
					if (!next || !next[0]) {
						this._lastCellIndex = 0;
						container = this._verticalContainer(container, true);
						next = this._prevVerticalCell(container, current);
						if (next.is(":hidden")) next = next.nextAll().not(":hidden").first();
						if (next[0]) focusTable(this.table, true);
					}
				}
			}
			var tmp = this._lastCellIndex || 0;
			if (!this._isStackedMode()) this._focusFocusable(current, next);
			else {
				focusTable(this.table, true);
				this._setCurrent(next);
			}
			this._lastCellIndex = tmp;
			return true;
		},
		_moveDown: function(current, shiftKey, ctrlKey) {
			var container = current.parent().parent();
			var next, cellIndex, index, oldIndex;
			if (this._isPinnedContainer(current)) {
				const pos = this._pinnedPosition(current);
				const ci = Math.max(current.parent().children(DATA_CELL_HIDDENINCLUDED).index(current), this._lastCellIndex || 0);
				next = this._pinnedVerticalCell(current, true);
				if (!next.length) {
					if (pos === "top") {
						const bodyRows = this.tbody.children(NAVROW);
						if (bodyRows.length) {
							const cells = bodyRows.first().children(DATA_CELL_HIDDENINCLUDED);
							next = cells.length > ci ? cells.eq(ci) : cells.last();
						}
					}
				}
				if (next && next.length) {
					const tmp = this._lastCellIndex || 0;
					focusTable(this.table, true);
					this._setCurrent(this._findVisibleCell(next));
					this._lastCellIndex = tmp;
				}
				return true;
			}
			if (shiftKey) {
				next = current.parent();
				next = next.nextAll(ITEMROW).first();
				next = current.parent().is(ITEMROW) ? next.children().eq(current.index()) : next.children(DATA_CELL).first();
			} else if (ctrlKey && current.parent().is(ITEMROW) && this._hasReorderableRows()) {
				cellIndex = current.index();
				next = current.parent();
				next = next.nextAll(ITEMROW).first();
				index = this.tbody.children(ITEMROW).index(next);
				oldIndex = this.tbody.children(ITEMROW).index(current.parent());
				if (index >= 0 && !this.trigger(ROWREORDER, {
					oldIndex,
					newIndex: index,
					row: current.parent()
				})) {
					this.reorderRows(current.parent(), index + 1);
					next = this.tbody.children(ITEMROW).eq(index).children().eq(cellIndex);
				}
			} else {
				next = this._nextVerticalCell(container, current);
				if (!next[0]) {
					const isInThead = container.is("thead");
					const isInTbody = container[0] === this.tbody[0];
					if (this._isPinnable() && isInThead && this._pinnedTopRows && this._pinnedTopRows.length) {
						const ci = Math.max(current.parent().children(DATA_CELL_HIDDENINCLUDED).index(current), this._lastCellIndex || 0);
						next = this._pinnedBoundaryCellByPosition("top", true, ci);
					} else if (this._isPinnable() && isInTbody) {
						const ci = Math.max(current.parent().children(DATA_CELL_HIDDENINCLUDED).index(current), this._lastCellIndex || 0);
						next = this._pinnedBoundaryCellByPosition("bottom", true, ci);
					}
					if (!next || !next[0]) {
						this._lastCellIndex = 0;
						container = this._verticalContainer(container);
						next = this._nextVerticalCell(container, current);
						if (next[0]) focusTable(this.table, true);
					}
				}
			}
			var tmp = this._lastCellIndex || 0;
			if (!this._isStackedMode()) this._focusFocusable(current, this._findVisibleCell(next));
			else {
				focusTable(this.table, true);
				this._setCurrent(this._findVisibleCell(next));
			}
			this._lastCellIndex = tmp;
			return true;
		},
		_moveColumn: function(current, isLeft) {
			var elements = this.wrapper.data().kendoReorderable.element.find(this._draggableInstance.options.filter + ":visible");
			var columns = visibleColumns(flatColumnsInDomOrder(this.columns));
			var oldIndex = elements.index($(current));
			var offset = isLeft ? -1 : 1;
			var column = columns[oldIndex];
			var newIndex = targetParentContainerIndex(columns, this.columns, oldIndex, oldIndex + offset);
			if (newIndex >= 0) {
				this.reorderColumn(newIndex, column, isLeft);
				this.trigger(COLUMNREORDER, {
					newIndex,
					oldIndex,
					column
				});
			}
		},
		_handleHome: function(current, ctrl) {
			const that = this;
			let row = current.parent();
			const rowContainer = row.parent();
			const isInLockedTable = that.lockedTable && that.lockedTable.children("tbody")[0] === rowContainer[0];
			const isInBody = rowContainer[0] === that.tbody[0];
			let prev;
			const hasVirtualColumns = that._hasVirtualColumns();
			const hasVirtualRows = that._hasVirtualRows();
			const isScrolledToStart = (hasVirtualRows ? that.virtualScrollable.wrapper : that.content).scrollLeft() === 0;
			if (hasVirtualColumns && hasVirtualRows && ctrl) {
				that._focusVirtualCell(true, hasVirtualColumns && !isScrolledToStart);
				return true;
			}
			if (hasVirtualColumns) {
				if (isScrolledToStart) that._setCurrent(that.table.find(ITEMROW).first().children(NAVCELL).first());
				else that._forceScrollVirtualColumn(keys.HOME, ctrl);
				return true;
			}
			if (hasVirtualRows && ctrl) {
				that._focusVirtualCell(true);
				return true;
			}
			if (ctrl) if (that._isPinnable() && that._pinnedTopRows && that._pinnedTopRows.length) prev = that._pinnedBoundaryCellByPosition("top", true, 0);
			else if (that.lockedTable) prev = that.lockedTable.find(ITEMROW).first().children(NAVCELL).first();
			else prev = that.table.find(ITEMROW).first().children(NAVCELL).first();
			else if (isInBody || isInLockedTable) {
				if (isInBody && this.lockedTable) row = that._relatedRow(row);
				prev = row.children(DATA_CELL).first();
			} else if (that._isPinnedContainer(current)) prev = row.children(DATA_CELL).first();
			if (prev && prev.length) {
				that._setCurrent(prev);
				return true;
			}
		},
		_setCurrentVirtualCell: function(focusFirst) {
			const that = this;
			if (focusFirst) this._setCurrent(that.table.find(ITEMROW).first().children(NAVCELL).first());
			else this._setCurrent(that.table.find(ITEMROW).last().children(NAVCELL).last());
		},
		_forceScrollVirtualColumn: function(key, condition, setboth) {
			const that = this;
			if (setboth) {
				that._shouldFocusInLastRow = !condition;
				that._shouldFocusInFirstRow = condition;
			} else if (key === keys.HOME) that._shouldFocusInFirstRow = condition;
			else that._shouldFocusInLastRow = condition;
			that.one(DATABOUND, function() {
				if (setboth) that.one(DATABOUND, function() {
					that._setCurrentVirtualCell(condition);
				});
				if (that._shouldFocusInLastRow) {
					that.content.scrollTop(that.content[0].scrollHeight);
					delete that._shouldFocusInLastRow;
				}
				if (that._shouldFocusInFirstRow) {
					that.content.scrollTop(0);
					delete that._shouldFocusInFirstRow;
				}
			});
			that._scrollToColumn(key);
		},
		_focusVirtualCell: function(first, scrollColumn) {
			const that = this;
			const scrollbar = this.virtualScrollable.verticalScrollbar;
			const isScrolledToBottom = Math.ceil(scrollbar.scrollTop() + scrollbar.innerHeight()) >= scrollbar[0].scrollHeight;
			const isScrollToTop = this.virtualScrollable.verticalScrollbar.scrollTop() === 0;
			const scrollbarCondition = first ? isScrollToTop : isScrolledToBottom;
			const key = first ? keys.HOME : keys.END;
			if (scrollbarCondition) if (scrollColumn) that._forceScrollVirtualColumn(key, first, true);
			else that._setCurrentVirtualCell(first);
			else {
				if (that.dataSource.group().length === 0) that.virtualScrollable._alwaysScrollTop = true;
				const scrollPosition = first ? 0 : scrollbar[0].scrollHeight;
				that.one(DATABOUND, function() {
					if (scrollColumn) that._forceScrollVirtualColumn(key, first, true);
					else {
						that._setCurrentVirtualCell(first);
						if (that._shouldFocusInLastRow) delete that._shouldFocusInLastRow;
						if (that._shouldFocusInFirstRow) delete that._shouldFocusInFirstRow;
					}
				});
				scrollbar.scrollTop(scrollPosition);
				scrollbar.trigger(SCROLL);
			}
		},
		_handleEnd: function(current, ctrl) {
			const that = this;
			let row = current.parent();
			const rowContainer = row.parent();
			const isInLockedTable = that.lockedTable && that.lockedTable.children("tbody")[0] === rowContainer[0];
			const isInBody = rowContainer[0] === that.tbody[0];
			let next;
			const hasVirtualColumns = that._hasVirtualColumns();
			const hasVirtualRows = that._hasVirtualRows();
			const scrollable = hasVirtualRows ? that.virtualScrollable.wrapper : that.content;
			const isScrolledToEnd = scrollable.scrollLeft() + scrollable.innerWidth() >= scrollable[0].scrollWidth;
			if (hasVirtualColumns && hasVirtualRows && ctrl) {
				that._focusVirtualCell(false, hasVirtualColumns && !isScrolledToEnd);
				return true;
			}
			if (hasVirtualColumns) {
				if (isScrolledToEnd) that._setCurrent(that.table.find(ITEMROW).last().children(NAVCELL).last());
				else that._forceScrollVirtualColumn(keys.END, ctrl);
				return true;
			}
			if (hasVirtualRows && ctrl) {
				that._focusVirtualCell(false);
				return true;
			}
			if (ctrl) if (that._isPinnable() && that._pinnedBottomRows && that._pinnedBottomRows.length) next = that._pinnedBottom.tbody.children(NAVROW).last().children(NAVCELL).last();
			else next = that.table.find(ITEMROW).last().children(NAVCELL).last();
			else if (isInBody || isInLockedTable) {
				if (!isInBody && this.lockedTable) row = that._relatedRow(row);
				next = row.children(DATA_CELL).last();
			} else if (that._isPinnedContainer(current)) next = row.children(DATA_CELL).last();
			if (next && next.length) {
				that._setCurrent(next);
				return true;
			}
		},
		_handlePageDown: function() {
			if (!this.options.pageable) return false;
			this.dataSource.page(this.dataSource.page() + 1);
			return true;
		},
		_handlePageUp: function() {
			if (!this.options.pageable) return false;
			this.dataSource.page(this.dataSource.page() - 1);
			return true;
		},
		_handleTabKey: function(current, currentTable, shiftKey, target) {
			const isInCell = this.options.editable && this._editMode() == "incell";
			let cell = $(activeElement()).closest(".k-grid-stack-edit-cell,.k-edit-cell");
			let filterFocusable;
			const stacked = this._isStackedMode();
			const isTargetFocusable = target.is(FOCUSABLE);
			const isTargetNotTable = !target.is("table");
			const focusStackedCell = stacked && target.is(".k-grid-stack-cell");
			const stackedParent = !focusStackedCell && target.closest(".k-grid-stack-cell");
			const isCurrentFocusableGridElement = this._isFocusableGridElement(current);
			const isNotInEditMode = !isInEdit(current);
			const initialTarget = target;
			let hasMultipleFocusableElements;
			if (stacked) {
				if (focusStackedCell) hasMultipleFocusableElements = target.parent().find(".k-grid-stack-cell").length > 1;
				else if (stackedParent && stackedParent.length) hasMultipleFocusableElements = stackedParent.find(FOCUSABLE).length > 1;
			} else hasMultipleFocusableElements = current.find(FOCUSABLE).length > 1;
			if (!cell[0] && isTargetFocusable && isTargetNotTable && hasMultipleFocusableElements && isCurrentFocusableGridElement && isNotInEditMode) {
				if (!stacked && (target.is(":last-child") && !shiftKey || target.is(":first-child") && shiftKey)) {
					focusTable(this.table, true);
					return true;
				}
				if (shiftKey) target = target.prev(FOCUSABLE);
				else target = target.next(FOCUSABLE);
				if (focusStackedCell && !target.length) {
					const focusableSiblings = initialTarget.parent().find(".k-grid-stack-cell");
					if (shiftKey && initialTarget.is(":first-child")) target = focusableSiblings.last();
					if (!shiftKey && initialTarget.is(":last-child")) target = focusableSiblings.first();
				} else if (!target.length && !focusStackedCell) {
					const closestStackedCell = initialTarget.closest(".k-grid-stack-cell");
					if (shiftKey && initialTarget.is(":first-child")) target = closestStackedCell;
					if (!shiftKey && initialTarget.is(":last-child")) target = closestStackedCell.find(FOCUSABLE).first();
				}
				if (stacked && target.is(".k-grid-stack-cell")) this._currentStackedCell(target, true);
				else target.trigger("focus");
				return true;
			}
			if (this.options.editable && this._editMode() === "inline" && this._editContainer && this.lockedTable) {
				const currentEditCell = $(activeElement()).closest("td,th");
				const currentRow = currentEditCell.closest(TR);
				if (currentEditCell.length && this._editContainer.filter(currentRow).length) {
					const adjacentRow = this._relatedRow(currentRow);
					const editCells = ($.contains(this.lockedTable[0], currentRow[0]) ? currentRow.add(adjacentRow) : adjacentRow.add(currentRow)).children(":not(.k-group-cell,.k-hierarchy-cell,.k-drag-cell)");
					const adjacentCellIndex = editCells.index(currentEditCell) + (shiftKey ? -1 : 1);
					if (adjacentCellIndex < 0 || adjacentCellIndex >= editCells.length) return false;
					const adjacentCell = editCells.eq(adjacentCellIndex);
					if (adjacentCell.length && adjacentCell.closest("table")[0] !== currentEditCell.closest("table")[0]) {
						const focusable = adjacentCell.find(FOCUSABLE).filter(":visible").not("button,[tabindex='-1']");
						this._setCurrent(adjacentCell);
						focusTable(this.table, true);
						if (focusable.length) focusable.eq(shiftKey ? focusable.length - 1 : 0).trigger("focus");
						else adjacentCell.trigger("focus");
						return true;
					}
				}
			}
			if (stacked && !focusStackedCell && !cell[0] || !isInCell || current.is("th") || (this.options.scrollable ? this._headertables && this._headertables.filter(currentTable).length : this.thead && this.thead.filter(target).length)) {
				if (current.parent().hasClass("k-filter-row")) {
					filterFocusable = this._filterFocusable();
					if (!shiftKey && filterFocusable[filterFocusable.length - 1] === document.activeElement) {
						filterFocusable.first().trigger("focus");
						return true;
					} else if (shiftKey && filterFocusable[0] === document.activeElement) {
						filterFocusable.last().trigger("focus");
						return true;
					}
				}
				return false;
			}
			if (cell[0] && cell[0] !== current[0]) current = cell;
			cell = this._tabNext(current, currentTable, shiftKey);
			if (cell[0] === current[0]) return false;
			if (cell.length && this._isEditableEnabled) {
				if (this._isPinnedContainer(cell)) {
					const uid = cell.closest("tr").data("uid");
					const ci = cell.parent().children("td").index(cell);
					if (this.editable) {
						var active = $(activeElement());
						if (this._editContainer && $.contains(this._editContainer[0], active[0])) active.trigger("blur");
						if (this.editable && this.editable.end()) this.closeCell();
					}
					const pinnedRow = this.wrapper.find(".k-grid-pinned-container tr[data-uid='" + uid + "']");
					if (pinnedRow.length) cell = pinnedRow.children("td").eq(ci);
					this._setCurrent(cell);
					focusTable(this.table, true);
					this.editCell(cell);
					return true;
				}
				this._handleEditing(current, cell, cell.closest("table"));
				return true;
			}
			return false;
		},
		_handleDeletion: function(e) {
			const that = this;
			const current = that.current();
			const activeElementInstance = $(activeElement());
			const stackedCondition = !that._isStackedMode() ? !activeElementInstance.is("table") : !that._currentStackedCell();
			if (!(that._isEditableEnabled !== undefined ? that._isEditableEnabled && that.options.editable : that.options.editable) || isInEdit(current) || stackedCondition || !current.closest("tbody").length || !current.closest(".k-master-row").length) return false;
			that._removeCommandClick({
				currentTarget: current,
				preventDefault: e.preventDefault,
				stopPropagation: e.stopPropagation
			});
			return true;
		},
		_handleEscKey: function(current) {
			const active = activeElement();
			const isInCell = this._editMode() == "incell";
			const stacked = this._isStackedMode();
			const activeElementInstance = $(active);
			const stackedParent = !activeElementInstance.is(".k-grid-stack-cell") ? activeElementInstance.closest(".k-grid-stack-cell") : $();
			const cell = stacked ? activeElementInstance : current;
			const targetIsInsideStackedCell = stackedParent && stackedParent.length > 0;
			const isStackedCell = stacked && cell.is(".k-grid-stack-cell");
			if (!isInEdit(stackedParent.length ? stackedParent : cell)) {
				if (isStackedCell || !targetIsInsideStackedCell && current.has(active).length && !(this._isFocusableGridElement(current) && current.find(FOCUSABLE).length === 1)) {
					focusTable(this.table, true);
					delete this._activeStackedCell;
					return true;
				}
				if (targetIsInsideStackedCell) {
					stackedParent.trigger("focus");
					return true;
				}
				if (current.parent().hasClass("k-filter-row")) {
					this._filterFocusable().attr(TABINDEX, -1);
					focusTable(this.table, true);
					return true;
				}
				return false;
			}
			if (isInCell) {
				this.closeCell(true);
				if (stacked && targetIsInsideStackedCell) {
					addElementsToTab(stackedParent.parent().children(".k-grid-stack-cell"));
					this._currentStackedCell(stackedParent, true);
				}
			} else {
				var currentIndex = $(current).closest(TR).index();
				if (active) active.blur();
				this.cancelRow(true);
				if (currentIndex >= 0) {
					const lastActiveStackedCell = this._currentStackedCell();
					if (stacked && lastActiveStackedCell) {
						const cell = this.tbody.find(TR).eq(currentIndex).find(".k-grid-stack-cell").eq(lastActiveStackedCell.index);
						this._setCurrentStackedCell(cell);
					} else {
						const target = this.items().eq(currentIndex).children(NAVCELL).first();
						this._setCurrent(target);
					}
				}
			}
			if (!stacked) focusTable(this.table, true);
			return !stacked;
		},
		_toggleCurrent: function(current, editable, hasDetails) {
			const that = this;
			const row = current.parent();
			if (current.is(".k-command-cell")) return false;
			if (row.is(".k-filter-row")) return false;
			const pinCell = current.find(".k-pin-cell");
			if (pinCell.length) {
				const isPinned = that._isPinnedContainer(current);
				const pinnedPosition = isPinned ? that._pinnedPosition(current) : null;
				const pinnedRowIndex = isPinned ? current.parent().index() : -1;
				if (that._pinMenu) {
					const uid = row.data("uid");
					const cellIndex = current.index();
					that._pinMenu.one("close", function() {
						setTimeout(function() {
							that._restorePinFocus(isPinned, pinnedPosition, pinnedRowIndex, uid, cellIndex);
						});
					});
					that._pinMenu.open(pinCell[0], undefined, true);
				} else {
					const uid = row.data("uid");
					const cellIndex = current.index();
					pinCell.trigger("click");
					that._restorePinFocus(isPinned, pinnedPosition, pinnedRowIndex, uid, cellIndex);
				}
				return true;
			}
			if (row.is(".k-grouping-row")) {
				row.find(".k-icon,.k-svg-icon").first().click();
				return true;
			}
			if (!editable && hasDetails) {
				const selector = that._isStackedMode() ? `[ref="expand-detail-button"], [ref="collapse-detail-button"]` : ".k-icon,.k-svg-icon";
				row.find(selector).first().click();
				return true;
			}
			return false;
		},
		_restorePinFocus: function(isPinned, position, rowIndex, uid, cellIndex) {
			const that = this;
			let cell;
			if (!isPinned) {
				const newRow = that.tbody.find("tr[data-uid='" + uid + "']");
				cell = newRow.length ? newRow.children("td").eq(cellIndex) : that._current;
			} else if (position === "top") {
				const container = that._pinnedTop;
				const rows = container && container.tbody ? container.tbody.children("tr") : $();
				if (rows.length > 0) {
					let targetIndex = Math.min(rowIndex, rows.length - 1);
					if (rowIndex > 0) targetIndex = rowIndex - 1;
					cell = rows.eq(targetIndex).children("td").first();
				} else cell = that.tbody.children(ITEMROW).first().children("td").first();
			} else {
				const container = that._pinnedBottom;
				const rows = container && container.tbody ? container.tbody.children("tr") : $();
				if (rows.length > 0) {
					let targetIndex = Math.min(rowIndex, rows.length - 1);
					cell = rows.eq(targetIndex).children("td").first();
				} else cell = that.tbody.children(ITEMROW).last().children("td").first();
			}
			if (cell && cell.length) {
				that.table.trigger("focus");
				that._setCurrent(cell);
			}
		},
		_handleSpaceKey: function(current, ctrlKey) {
			var that = this;
			if (!ctrlKey || !that.groupable || !current.hasClass(HEADER_CLASS)) return;
			var descriptors = that.groupable.descriptors();
			var field = current.attr(kendo.attr("field"));
			var aggregates = that.groupable.aggregates();
			if (that.groupable._canDrag(current)) descriptors.push({
				field,
				dir: "asc",
				aggregates: aggregates || []
			});
			else descriptors = $.grep(descriptors, function(item) {
				return item.field !== field;
			});
			if (that.trigger("group", { groups: descriptors })) return;
			that.dataSource.group(descriptors);
			return true;
		},
		_hasStackedModeDetailsButton: function(cell) {
			return cell.find(`[ref="expand-detail-button"], [ref="collapse-detail-button"]`).length > 0;
		},
		_currentStackedCell: function(current, triggerFocus) {
			const that = this;
			if (!current || !current.length) return that._activeStackedCell;
			that._activeStackedCell = {
				cell: current,
				index: current.index()
			};
			if (triggerFocus && current.length) current.trigger("focus");
		},
		_handleEnterKey: function(current, currentTable, target, isF2) {
			const that = this;
			var editable = this.options.editable && this.options.editable.update !== false;
			var container = target.closest("td");
			var hasDetails = this._hasDetails();
			var link, filterFocusable;
			const stacked = that._isStackedMode();
			const skipToEditing = stacked && isF2 && that._editMode() !== "incell";
			const editClass = stacked ? "k-grid-stack-edit-cell" : "k-edit-cell";
			const focusIsInStackedCell = stacked && (target.is(".k-grid-stack-cell") || target.parent().is(".k-grid-stack-cell") || target.closest(".k-grid-stack-cell").length > 0);
			if (!target.is("table") && !$.contains(current[0], target[0])) current = container;
			if (current.is("th")) {
				link = current.find(".k-link");
				if (current.has($(activeElement())).length > 0) return false;
				else if (link.length) link.click();
				else if (current.parent().hasClass("k-filter-row")) {
					filterFocusable = this._filterFocusable();
					filterFocusable.attr(TABINDEX, 0);
					current.find(":kendoFocusable").first().trigger("focus");
				} else current.find(CHECKBOXINPUT).trigger("focus");
				return true;
			}
			if (!skipToEditing) {
				if (!focusIsInStackedCell && this._toggleCurrent(current, editable, hasDetails)) return true;
				if (focusIsInStackedCell && that._hasStackedModeDetailsButton(target)) {
					this._toggleCurrent(current, false, true);
					return true;
				}
				let focusableSelector = ":kendoFocusable";
				if (stacked && !focusIsInStackedCell) addElementsToTab(current.find(".k-grid-stack-cell"));
				var focusable = (focusIsInStackedCell ? target : current).find(focusableSelector).first();
				if (focusable[0] && (focusIsInStackedCell || !current.hasClass(editClass) && current.hasClass("k-focus"))) {
					if (!focusIsInStackedCell) that._currentStackedCell(focusable, true);
					else focusable.trigger("focus");
					return true;
				}
			}
			if (editable && !target.is(":button,.k-button,textarea")) {
				if (!container[0]) container = current;
				if (stacked && target.is(".k-grid-stack-cell")) that._currentStackedCell(target);
				if (focusIsInStackedCell) if (!target.is(".k-grid-stack-cell")) container = target.closest(".k-grid-stack-cell");
				else container = target;
				if (this._isEditableEnabled) this._handleEditing(container, false, currentTable);
				return true;
			}
			return false;
		},
		_nextHorizontalCell: function(table, current, originalIndex) {
			if (!current.nextAll(DATA_CELL).length) {
				var rows = table.find(NAVROW);
				if (rows.index(current.parent()) == -1) {
					if (current.hasClass(HEADER_CLASS)) {
						var headerRows = [];
						mapColumnToCellRows([lockedColumns(this.columns)[0]], childColumnsCells(rows.eq(0).children(":visible").first()), headerRows, 0, 0);
						if (headerRows[originalIndex]) return headerRows[originalIndex][0];
						return current;
					}
					if (current.parent().hasClass("k-filter-row")) return rows.last().children(DATA_CELL).first();
					return this._findVisibleCell(rows.eq(originalIndex).children(DATA_CELL + ",[hidden]").first());
				}
			}
			return this._findVisibleCell(current.nextAll(DATA_CELL + ",[hidden]").eq(0));
		},
		_prevHorizontalCell: function(table, current, originalIndex) {
			var cells = current.prevAll(DATA_CELL);
			if (!cells.length) {
				var rows = table.find(NAVROW);
				if (rows.index(current.parent()) == -1) {
					if (current.hasClass(HEADER_CLASS)) {
						var headerRows = [];
						var columns = lockedColumns(this.columns);
						mapColumnToCellRows([columns[columns.length - 1]], childColumnsCells(rows.eq(0).children().last()), headerRows, 0, 0);
						if (headerRows[originalIndex]) return headerRows[originalIndex][0];
						return current;
					}
					if (current.parent().hasClass("k-filter-row")) return rows.last().children(DATA_CELL).last();
					return rows.eq(originalIndex).children(DATA_CELL).last();
				}
			}
			cells = current.prevAll(DATA_CELL + ",[hidden]");
			let cellToFocus = this._findVisibleCell(cells.first());
			if (cellToFocus.is(".k-group-cell")) return cellToFocus.next(DATA_CELL);
			return cellToFocus;
		},
		_currentDataIndex: function(table, current) {
			var index = current.attr("data-index");
			if (!index) return undefined;
			var lockedColumnsCount = lockedColumns(this.columns).length;
			if (lockedColumnsCount && !table.closest(DIV).hasClass("k-grid-content-locked")[0]) return index - lockedColumnsCount;
			return index;
		},
		_findVisibleCell: function($cell) {
			var col = $cell.index();
			var row = $cell.closest("tr").index();
			var $newFocus;
			if ($cell.is("[hidden]")) {
				$newFocus = $cell.prevAll(":not([hidden])").first();
				var hiddenCount = $cell.prevUntil(":not([hidden])", "[hidden]").length;
				if (!$newFocus.attr("colspan") || $newFocus.attr("colspan") > 1 && $newFocus.attr("colspan") <= hiddenCount + 1) {
					$newFocus = $cell.prevAll("[hidden]").last();
					if ($newFocus.length === 0) return $cell.closest("tr").prevAll().find(`td:nth-of-type(${col + 1}):visible`).last();
					while (!$newFocus.attr("rowspan") && !($newFocus.attr("rowspan") > 1 && $newFocus.attr("rowspan") != row - $newFocus.closest("tr").index()) && Math.abs($newFocus.index() - col) != $newFocus.attr("colspan")) {
						$newFocus = $newFocus.closest("tr").prevAll().find("td, th").eq(col).first();
						if ($newFocus.length === 0) {
							$newFocus = $cell;
							break;
						}
					}
				}
			} else $newFocus = $cell;
			return $newFocus;
		},
		_prevVerticalCell: function(container, current) {
			var cells;
			var row = current.parent();
			var rows = container.children(NAVROW);
			var rowIndex = rows.index(row);
			var index = this._currentDataIndex(container, current);
			if (index || current.hasClass(HEADER_CLASS)) {
				cells = parentColumnsCells(current);
				return cells.eq(cells.length - 2);
			}
			index = Math.max(row.children(DATA_CELL_HIDDENINCLUDED).index(current), this._lastCellIndex || 0);
			if (row.hasClass("k-filter-row")) {
				let offset = rows.last().children(".k-group-cell").length;
				return leafDataCells(container).filter(isCellVisible).eq(Math.max(0, index - offset));
			}
			if (rowIndex == -1) {
				if (this._hasVirtualColumns()) index = this._virtualCellIndex;
				row = container.find("tr.k-filter-row:visible");
				if (!row[0]) {
					if ((this._hasDetails() || current.parent().find(".k-hierarchy-cell").length) && index) index--;
					let offset = 0;
					if (current.parent().is(".k-table-group-row")) offset = rows.last().children(".k-group-cell").length;
					return leafDataCells(container).eq(Math.max(0, index - offset));
				} else if (this._hasDetails()) index--;
			} else row = rowIndex === 0 ? $() : rows.eq(rowIndex - 1);
			cells = row.children(DATA_CELL_HIDDENINCLUDED);
			if (cells.length > index) {
				let nextCell = cells.eq(index);
				if (nextCell.is(".k-group-cell")) nextCell = nextCell.nextAll("td").not(".k-group-cell").not(":hidden").first();
				return this._findVisibleCell(nextCell);
			}
			return cells.eq(0);
		},
		_nextVerticalCell: function(container, current) {
			var cells;
			var originalRow;
			var row = originalRow = current.closest(TR);
			var rows = container.children(NAVROW);
			var rowIndex = rows.index(row);
			var index = this._currentDataIndex(container, current);
			var virtualScroll = this.virtualScroll || {};
			var colspan;
			if (rowIndex != -1 && index === undefined && current.hasClass(HEADER_CLASS)) return childColumnsCells(current).eq(1);
			index = index ? parseInt(index, 10) : row.children(DATA_CELL_HIDDENINCLUDED).index(current);
			index = Math.max(index, this._lastCellIndex || 0);
			if (rowIndex == -1) {
				row = rows.eq(0);
				if (virtualScroll.columns) {
					colspan = parseInt(row.children().first().attr("colspan"), 10);
					index = this._virtualCellIndex - (colspan > 1 ? colspan : 0);
				}
				if (this._hasDetails() || row.find(".k-hierarchy-cell").length) index++;
				if (row.hasClass("k-table-group-row")) index += originalRow.children(".k-group-cell").length;
			} else row = rows.eq(rowIndex + current[0].rowSpan);
			cells = row.children(".k-group-cell," + DATA_CELL_HIDDENINCLUDED);
			let cellToFocus = cells.eq(0);
			if (cells.length > index) cellToFocus = cells.eq(index);
			if (cellToFocus.is(".k-group-cell")) cellToFocus = cellToFocus.nextAll("td").not(".k-group-cell").not(":hidden").first();
			return cellToFocus;
		},
		_verticalContainer: function(container, up) {
			var table = container.parent();
			var length = this._navigatableTables.length;
			var step = Math.floor(length / 2);
			var index = inArray(table[0], this._navigatableTables);
			if (up) step *= -1;
			index += step;
			if (index >= 0 || index < length) table = this._navigatableTables.eq(index);
			return table.find(up ? ">thead" : ">tbody");
		},
		_filterFocusable: function() {
			return this.wrapper.find(".k-filter-row").find(".k-dropdownlist, .k-input .k-input-inner:visible, input[type='radio']:visible, input[type='checkbox']:visible");
		},
		_horizontalContainer: function(container, right) {
			var length = this._navigatableTables.length;
			if (length <= 2) return container;
			var table = container.parent();
			var index = inArray(table[0], this._navigatableTables);
			index += right ? 1 : -1;
			if (right && (index == 2 || index == length)) return container;
			if (!right && (index == 1 || index < 0)) return container;
			return this._navigatableTables.eq(index).find("thead, tbody");
		},
		_tabNext: function(current, currentTable, back) {
			var switchRow = true;
			var next = back ? current.prevAll(DATA_CELL).first() : current.nextAll(":visible").first();
			if (!next.length) {
				next = current.parent();
				if (this.lockedTable) {
					switchRow = back && currentTable == this.lockedTable[0] || !back && currentTable == this.table[0];
					next = this._relatedRow(next);
				}
				if (switchRow) {
					if (this._hasVirtualColumns()) return current;
					next = next[back ? "prevAll" : "nextAll"]("tr:not(.k-grouping-row):not(.k-detail-row):visible").first();
				}
				if (!next.length && this._isPinnable()) {
					next = this._tabNextPinnedBoundary(current, back);
					if (next && next.length) return next;
				}
				if (back) next = next.children(DATA_CELL).last();
				else next = next.children(DATA_CELL).first();
			}
			return next;
		},
		_tabNextPinnedBoundary: function(current, back) {
			if (this._isPinnedContainer(current)) {
				const pos = this._pinnedPosition(current);
				if (!back) {
					if (pos === "top") {
						const bodyRows = this.tbody.children(NAVROW);
						if (bodyRows.length) return bodyRows.first().children(DATA_CELL).first();
					}
				} else if (pos === "top") {
					if ((this.thead ? this.thead.children(NAVROW) : $()).length) return leafDataCells(this.thead).last();
				} else if (pos === "bottom") {
					const bodyRows = this.tbody.children(NAVROW);
					if (bodyRows.length) return bodyRows.last().children(DATA_CELL).last();
				}
			} else if (current.closest("tbody")[0] === this.tbody[0]) {
				if (!back && this._pinnedBottomRows && this._pinnedBottomRows.length) return this._pinnedBoundaryCellByPosition("bottom", true, 0);
				else if (back && this._pinnedTopRows && this._pinnedTopRows.length) {
					const p = this._pinnedTop;
					if (p && p.tbody) {
						const lastRow = p.tbody.children(NAVROW).last();
						if (lastRow.length) return lastRow.children(DATA_CELL).last();
					}
				}
			}
			return $();
		},
		_handleEditing: function(current, next, table) {
			var that = this, active = $(activeElement()), mode = that._editMode(), isIE = browser.msie, editContainer = that._editContainer, focusable, editable = that.options.editable && that.options.editable.update !== false, isEdited;
			const stacked = that._isStackedMode();
			const isStackedCell = current.is(".k-grid-stack-cell");
			table = $(table);
			if (mode == "incell") {
				const editClass = stacked ? "k-grid-stack-edit-cell" : "k-edit-cell";
				isEdited = current.hasClass(editClass);
			} else isEdited = isStackedCell ? current.hasClass("k-grid-stack-edit-cell") : current.parent().hasClass("k-grid-edit-row");
			if (that.editable) {
				if ($.contains(editContainer[0], active[0])) {
					active.trigger("blur");
					if (isIE) active.trigger("blur");
				}
				if (!that.editable && !isStackedCell) {
					focusTable(that.table);
					return;
				}
				if (that.editable.end()) if (mode == "incell") that.closeCell();
				else {
					that.saveRow();
					isEdited = true;
				}
				else {
					if (stacked && isStackedCell) return that._setCurrentStackedCell(current);
					if (mode == "incell") that._setCurrent(editContainer);
					else that._setCurrent(editContainer.children().filter(DATA_CELL).first());
					focusable = editContainer.find(":kendoFocusable").first()[0];
					if (focusable) focusable.focus();
					return;
				}
			}
			if (next) that._setCurrent(next);
			if (!isStackedCell) focusTable(that.table, true);
			if (!editable) return;
			if (!isEdited && !next || next) if (mode === INCELL) {
				if (!(stacked ? current.find("[ref='expand-detail-button'], [ref='collapse-detail-button']").length : $(that.current()).hasClass(HIERARCHY_CELL_CLASS))) {
					const stacked = that._isStackedMode();
					const cell = stacked ? current : that.current();
					if (stacked && cell.is(".k-command-cell")) return;
					that.editCell(cell, stacked && cell.parent().children().index(cell));
				}
			} else that.editRow(that.current().parent());
			that._toggleToolbarEditingItemsVisibility();
		},
		_wrapper: function() {
			var that = this, table = that.table, height = that.options.height, width = that.options.width, wrapper = that.element;
			if (!wrapper.is(DIV)) wrapper = wrapper.wrap("<div/>").parent();
			that.wrapper = wrapper.addClass("k-grid " + kendo.getValidCssClass("k-grid-", "size", that.options.size));
			if (that._isStackedMode()) that.wrapper.addClass(STACKED);
			else that.wrapper.removeClass(STACKED);
			if (height) {
				that.wrapper.css(HEIGHT, height);
				table.css(HEIGHT, AUTO);
			}
			if (width) that.wrapper.css(WIDTH, width);
			that._initMobile();
		},
		_initContextMenu: function() {
			var that = this, options = that.options, groupsContextMenu = isPlainObject(options.contextMenu) && isArray(options.contextMenu.groups) ? { items: options.contextMenu.groups } : { items: defaultGroupsContextMenu }, tbodyContextMenu = isPlainObject(options.contextMenu) && isArray(options.contextMenu.body) ? { items: options.contextMenu.body } : { items: defaultBodyContextMenu }, theadContextMenu = isPlainObject(options.contextMenu) && isArray(options.contextMenu.head) ? { items: options.contextMenu.head } : { items: defaultHeadContextMenu };
			var mainOptions = isPlainObject(options.contextMenu) ? options.contextMenu : {};
			tbodyContextMenu = extend({}, {
				messages: options.messages,
				target: that.wrapper,
				filter: ".k-grid-content .k-table-td, .k-grid-content-locked .k-table-td, .k-grid-pinned-container .k-table-td, .k-table-tbody .k-table-td",
				copyAnchorStyles: false,
				action: that._action.bind(that),
				states: that._buildStates()
			}, mainOptions, tbodyContextMenu);
			theadContextMenu = extend({}, {
				messages: options.messages,
				target: that.thead,
				filter: ".k-table-th",
				action: that._action.bind(that),
				states: that._buildStates()
			}, mainOptions, theadContextMenu);
			if (that.groupable) groupsContextMenu = extend({}, {
				showOn: "click",
				target: that.groupable.groupContainer,
				filter: ".k-groupable-context-menu",
				messages: options.messages,
				action: that._action.bind(that),
				states: that._buildStates()
			}, mainOptions, groupsContextMenu);
			that.tbodyContextMenu = new ui.grid.ContextMenu("<ul></ul>", tbodyContextMenu);
			that.theadContextMenu = new ui.grid.ContextMenu("<ul></ul>", theadContextMenu);
			that.groupsContextMenu = !!that.groupable && new ui.grid.ContextMenu("<ul></ul>", groupsContextMenu);
			if (that._isPinnable() && that.tbodyContextMenu) {
				const menu = that.tbodyContextMenu;
				const origOnOpen = menu._onOpen.bind(menu);
				const pinRowLocation = that._getPinnableRowLocation();
				let pinnedMenuContext = false;
				menu.unbind("open");
				menu.bind("open", function(ev) {
					origOnOpen(ev);
					if (!ev.event || ev.isDefaultPrevented && ev.isDefaultPrevented()) {
						if (pinnedMenuContext) {
							const reorderItem = menu.element.find("[data-command=ReorderRowCommand]").closest(".k-menu-group").closest(".k-menu-item");
							if (reorderItem.length) menu.enable(reorderItem, false);
						}
						return;
					}
					const row = $(ev.event.target).closest("tr");
					const dataItem = row.length ? that.dataItem(row) : null;
					const position = dataItem ? that._getRowPinPosition(dataItem) : "none";
					pinnedMenuContext = row.closest(".k-grid-pinned-container").length > 0;
					togglePinMenuItems(menu.element[0], position, pinRowLocation, that.options.messages.commands);
					const pinItems = menu.element.find("[data-command=PinTopCommand],[data-command=PinBottomCommand],[data-command=UnpinCommand]").closest(".k-menu-item");
					const parentItem = pinItems.closest(".k-menu-group").closest(".k-menu-item");
					const rowPinnable = !dataItem || that._checkRowPinnable(dataItem);
					menu.enable(pinItems, rowPinnable);
					if (parentItem.length) menu.enable(parentItem, rowPinnable);
					const reorderItem = menu.element.find("[data-command=ReorderRowCommand]").closest(".k-menu-group").closest(".k-menu-item");
					if (reorderItem.length && pinnedMenuContext) menu.enable(reorderItem, false);
				});
				if (pinRowLocation !== "both") menu.bind("select", function(ev) {
					const cmd = $(ev.item).data("command");
					if (cmd === "PinTopCommand" || cmd === "PinBottomCommand" || cmd === "UnpinCommand") menu.close();
				});
			}
		},
		_buildStates: function() {
			var that = this;
			return {
				isEditable: that.options.editable,
				isSelectable: that.options.selectable,
				isSortable: that.options.sortable,
				isRowReorderable: isPlainObject(that.options.reorderable) ? that.options.reorderable.rows : that.options.reorderable,
				isGroupable: that.options.groupable,
				isPinnable: that._isPinnable(),
				allowPaste: that.options.allowPaste,
				alwaysDisabled: false,
				hasSelection: () => this.select() ? this.select().length > 0 : false,
				isSorted: () => !(this.dataSource.sort() ? this.dataSource.sort().length > 0 : false),
				canMoveGroupPrev: (target) => {
					return $(target).closest(".k-chip").index() > 0;
				},
				canMoveGroupNext: (target) => {
					var length = $(target).closest(".k-chip-list").children().length - 1;
					return $(target).closest(".k-chip").index() < length;
				}
			};
		},
		_action: function(args) {
			var commandName = args.command, commandOptions = extend({ grid: this }, args.options);
			return new ui.grid.commands[commandName](commandOptions).exec();
		},
		_initMobile: function() {
			var options = this.options;
			var that = this;
			this._isMobile = that.options.adaptiveMode !== "auto" && options.mobile === true && kendo.support.mobileOS || options.mobile === "phone" || options.mobile === "tablet";
			if (kendo.support.mobileOS) this.wrapper.addClass("k-grid-mobile");
			if (this._isMobile) {
				var html = this.wrapper.wrap("<div data-" + kendo.ns + "stretch=\"true\" data-" + kendo.ns + "role=\"view\" data-" + kendo.ns + "init-widgets=\"false\"></div>").parent();
				this.pane = this._createPane(html);
				this.view = this.pane.view();
				if (options.height) this.pane.element.parent().css(HEIGHT, options.height);
				else this.pane.element.parent().css(HEIGHT, this.wrapper[0].style.height);
				this._editAnimation = "slide";
				that.wrapper.on("transitionend.kendoGrid", function(e) {
					e.stopPropagation();
				});
				that.wrapper.on("contextmenu.kendoGrid", "th a", function(e) {
					e.preventDefault();
					return false;
				});
				this.view.bind("showStart", function() {
					if (that._isLocked()) {
						that._updateTablesWidth();
						that._applyLockedContainersWidth();
						that._syncLockedContentHeight();
						that._syncLockedHeaderHeight();
						that._syncLockedFooterHeight();
					}
				});
			}
		},
		_createPane: function(html) {
			return kendo.Pane.wrap(html, { viewEngine: { viewOptions: {
				renderOnInit: true,
				wrap: false,
				wrapInSections: true,
				detachOnHide: false,
				detachOnDestroy: false
			} } });
		},
		_initLoader: function(options) {
			var that = this, defaultOptions = {
				size: "medium",
				messages: { exporting: "Exporting..." }
			};
			defaultOptions = $.extend({}, defaultOptions, options);
			that.loader = $("<div></div>").kendoLoader(defaultOptions).data("kendoLoader");
			that._loaderContainer = require_loaderContainer.useLoaderContainer.bind(that);
		},
		_tbody: function() {
			var that = this, table = that.table, tbody = table.find(">tbody");
			if (!tbody.length) tbody = $("<tbody/>").appendTo(table);
			tbody.addClass("k-table-tbody");
			that.tbody = tbody;
		},
		_scrollable: function() {
			var that = this, header, table, options = that.options, scrollable = options.scrollable, virtualScroll = scrollable !== true && scrollable.virtual ? parseVirtualSettings(scrollable.virtual) : null, scrollbar = !kendo.support.kineticScrollNeeded || virtualScroll && virtualScroll.rows ? kendo.support.scrollbar() : 0, headerWrap;
			const stacked = that._isStackedMode();
			if (scrollable) {
				if (!stacked) {
					header = that.wrapper.children(".k-grid-header");
					if (!header[0]) header = $("<div class=\"k-grid-header\" />").insertBefore(that.table);
					header.css(isRtl ? "padding-left" : "padding-right", scrollable.virtual ? scrollbar + 1 : scrollbar);
					table = $("<table role=\"none\" class=\"k-grid-header-table k-table\"/>");
					table.addClass(kendo.getValidCssClass("k-table-", "size", options.size));
					table.width(that.table[0].style.width);
				}
				if (!stacked) {
					table.append(that.thead);
					header.empty().append($("<div class=\"k-grid-header-wrap k-auto-scrollable\" />").append(table));
				}
				that.content = that.table.parent();
				that.virtualScroll = virtualScroll;
				if (that.content.is(".k-virtual-scrollable-wrap, ." + classNames.scrollContainer)) that.content = that.content.parent();
				if (!that.content.is(".k-grid-content, .k-virtual-scrollable-wrap")) that.content = that.table.wrap("<div class=\"k-grid-content k-auto-scrollable\" />").parent();
				if (!that.content.parent().hasClass("k-grid-container")) that.content.wrap("<div class='k-grid-container' />").parent();
				if (virtualScroll && virtualScroll.rows && !that.virtualScrollable) that._createVirtualScrollable();
				if (virtualScroll && virtualScroll.columns) that.table.add(that.thead.parent()).css({ width: sumWidths(visibleLeafColumns(visibleNonLockedColumns(that.columns))) });
				if (!stacked) {
					headerWrap = header.children(".k-grid-header-wrap");
					that.scrollables = headerWrap.add(that.content);
				} else that.scrollables = that.content;
				var footer = that.wrapper.find(".k-grid-footer");
				if (footer.length) that.scrollables = that.scrollables.add(footer.children(".k-grid-footer-wrap"));
				if (!stacked) headerWrap.off("scroll.kendoGrid").on("scroll.kendoGrid", function(e) {
					if (that._scrollLeft !== this.scrollLeft) kendo.scrollLeft(that.scrollables.not(e.currentTarget), this.scrollLeft);
				});
				if (virtualScroll && virtualScroll.rows) that.content.find(">.k-virtual-scrollable-wrap").off("scroll.kendoGrid").on("scroll.kendoGrid", function() {
					var isScrollingLeft = this.scrollLeft != that._scrollLeft;
					that._scrollLeft = this.scrollLeft;
					kendo.scrollLeft(that.scrollables, this.scrollLeft);
					if (that.lockedContent) that.lockedContent[0].scrollTop = this.scrollTop;
					if (virtualScroll.columns && isScrollingLeft) that.refresh();
					if (that._stickyGroupsScrollHandler) that._stickyGroupsScrollHandler();
					if (that._isPinnable()) that._syncPinnedScroll(this);
				});
				else {
					var endless = scrollable.endless;
					var originalPageSize = that.dataSource.options.pageSize;
					if (endless) that._endlessPageSize = originalPageSize;
					that.content.off("scroll.kendoGrid").on("scroll.kendoGrid", function(e) {
						var isScrollingLeft = this.scrollLeft != that._scrollLeft;
						that._scrollLeft = this.scrollLeft;
						kendo.scrollLeft(that.scrollables.not(e.currentTarget), that._scrollLeft);
						if (that.lockedContent && e.currentTarget == that.content[0]) that.lockedContent[0].scrollTop = this.scrollTop;
						if (endless) {
							if (this.scrollTop + this.clientHeight - this.scrollHeight >= -10 && !that._endlessFetchInProgress && that._endlessPageSize < that.dataSource.total()) {
								that._skipRerenderItemsCount = that._endlessPageSize;
								that._endlessPageSize = that._endlessPageSize + originalPageSize;
								that.dataSource.options.endless = true;
								that._endlessFetchInProgress = true;
								that.dataSource.pageSize(that._endlessPageSize);
							}
						}
						if (virtualScroll && virtualScroll.columns && isScrollingLeft) {
							that._virtualColScroll = true;
							that._cacheEditableState();
							that.refresh();
							that._restoreEditableState();
							that._virtualColScroll = false;
						}
						if (that.rowResizer) that.rowResizer.css("left", e.currentTarget.scrollLeft + "px");
						if (that._stickyGroupsScrollHandler) that._stickyGroupsScrollHandler();
						if (that._isPinnable()) that._syncPinnedScroll(e.currentTarget);
					});
					var touchScroller = that.content.data("kendoTouchScroller");
					if (touchScroller) touchScroller.destroy();
					touchScroller = kendo.touchScroller(that.content);
					if (touchScroller && touchScroller.movable) {
						that.touchScroller = touchScroller;
						touchScroller.movable.bind("change", function(e) {
							kendo.scrollLeft(that.scrollables, -e.sender.x);
							if (that.lockedContent) that.lockedContent.scrollTop(-e.sender.y);
						});
						that.one(DATABOUND, function(e) {
							e.sender.wrapper.addClass("k-grid-backface");
						});
					}
				}
				that._initStickyGroups();
			}
		},
		_createVirtualScrollable: function() {
			var that = this;
			if (that.virtualScrollable) that.virtualScrollable.destroy();
			that.virtualScrollable = new VirtualScrollable(that.content, {
				dataSource: that.dataSource,
				itemHeight: function() {
					return that._averageRowHeight();
				},
				page: function() {
					that._restoreEditableState();
				},
				scroll: function() {
					that._focusEditable();
				},
				loadStart: function() {
					that._progress(true);
				},
				loadEnd: function() {
					that._progress(false);
				}
			});
			that.virtualScrollable.bind(PAGING, that._onVirtualPaging.bind(that));
		},
		_onVirtualPaging: function() {
			var that = this;
			that._cacheEditableState();
			if (that._isVirtualIncellEditable()) {
				that._shouldClearEditableState = false;
				that.closeCell();
				that._shouldClearEditableState = true;
			}
		},
		_isVirtualEditable: function() {
			return this._isVirtualIncellEditable() || this._isVirtualInlineEditable() || this._isVirtualPopupEditable();
		},
		_isVirtualInlineEditable: function() {
			return this.virtualScrollable && this._editMode() === INLINE;
		},
		_isVirtualIncellEditable: function() {
			return this.virtualScrollable && this._editMode() === INCELL;
		},
		_isVirtualPopupEditable: function() {
			return this.virtualScrollable && this._editMode() === "popup";
		},
		_hasVirtualColumns: function() {
			return (this.virtualScroll || {}).columns ? true : false;
		},
		_hasVirtualRows: function() {
			return (this.virtualScroll || {}).rows ? true : false;
		},
		_scrollVirtualWrapper: function() {
			var that = this;
			var scrollable = that.virtualScrollable;
			if (that._isVirtualInlineEditable() || that._isVirtualIncellEditable()) {
				if (scrollable._isScrolledToBottom()) scrollable._scrollWrapperToBottom();
				else if (scrollable._isScrolledToTop()) scrollable._scrollWrapperToTop();
			}
		},
		_scrollVirtualWrapperOnColumnResize: function() {
			var virtualScrollable = this.virtualScrollable;
			if (virtualScrollable) virtualScrollable._scrollWrapperOnColumnResize();
		},
		_restoreEditableState: function() {
			var that = this;
			var editableState = that._editableState || {};
			var editedModel = editableState.model;
			var dataSource = that.dataSource;
			var inlineMode = that._isVirtualInlineEditable();
			var incellMode = that._isVirtualIncellEditable();
			var virtualColumns = that._hasVirtualColumns();
			var row;
			var cell;
			if ((inlineMode || incellMode || virtualColumns) && editedModel && dataSource._getByUid(editedModel.uid, dataSource.view())) {
				if (that._editMode() === INLINE) {
					that._shouldClearEditableState = false;
					that.editRow(editedModel);
					if (!virtualColumns) that._focusEditable();
				} else if (that._editMode() === INCELL) {
					const stacked = that._isStackedMode();
					row = that.tbody.children(attrEquals(UNIQUE_ID, editedModel.uid));
					cell = stacked ? $(row).find(".k-grid-stack-cell:not(.k-drag-cell):not(.k-command-cell)" + attrEquals(FIELD, editableState.field)) : $(row).children(attrEquals(FIELD, editableState.field));
					if (cell[0]) {
						that._shouldClearEditableState = false;
						that.editCell(cell, stacked && cell.parent().children().index(cell));
						if (!virtualColumns) that._focusEditable();
					}
				}
			}
			that._shouldClearEditableState = true;
		},
		_focusEditable: function() {
			var that = this;
			var editedField = (that._editableState || {}).field;
			var editContainer = that._editContainer;
			if (editContainer && editContainer.length && !contains(editContainer[0], activeElement()) && that._canFocusEditable()) {
				if (that._isVirtualInlineEditable() || that._hasVirtualColumns()) editContainer.find(attrEquals(CONTAINER_FOR, editedField)).find(FOCUSABLE).eq(0).trigger("focus");
				else if (that._isVirtualIncellEditable() || that._hasVirtualColumns()) editContainer.find(FOCUSABLE).eq(0).trigger("focus");
			}
		},
		_canFocusEditable: function() {
			var that = this;
			return (that._isVirtualIncellEditable() || that._isVirtualInlineEditable() || that._hasVirtualColumns()) && (isElementVisibleInWrapper((that.virtualScrollable || {}).wrapper, that._editContainer) || isElementVisibleInWrapper(that.content, that._editContainer));
		},
		_cacheEditableState: function() {
			var that = this;
			var editContainer = that._editContainer;
			var editedModel = editContainer ? that._modelForContainer(editContainer) : null;
			var inlineMode = that._isVirtualInlineEditable();
			var incellMode = that._isVirtualIncellEditable();
			var virtualColumns = that._hasVirtualColumns();
			var active;
			var widget;
			var value;
			if ((inlineMode || incellMode || virtualColumns) && editedModel) {
				that._clearEditableState();
				active = $(activeElement());
				if (editContainer && active[0] && contains(editContainer[0], active[0])) {
					active.change();
					widget = kendo.widgetInstance(active, kendo.ui);
					if (widget && isFunction(widget.value) && active.is(INPUT)) {
						value = active.val();
						if (active.is("[type='checkbox'")) value = active.is(":checked");
						widget.value(value);
						widget.trigger(CHANGE);
					}
				}
				if (that._editMode() === INLINE) that._editableState = {
					model: editedModel,
					field: active.closest("[" + kendo.attr(CONTAINER_FOR) + "]").attr(kendo.attr(CONTAINER_FOR))
				};
				else if (that._editMode() === INCELL) that._editableState = {
					model: editedModel,
					field: editContainer.attr(kendo.attr(FIELD))
				};
			}
		},
		_clearSortClasses: function() {
			var that = this, content = that.content || that.table, lockedContent = that.lockedContent;
			if (content) content.find(COLGROUP).removeClass(SORTED_CLASS);
			if (lockedContent) lockedContent.find(COLGROUP).removeClass(SORTED_CLASS);
		},
		_clearEditableState: function() {
			var that = this;
			if (that.virtualScrollable || that.virtualScroll && that._hasVirtualColumns()) that._editableState = null;
		},
		_destroyVirtualScrollable: function() {
			var that = this;
			that._clearEditableState();
			if (that.virtualScrollable && that.virtualScrollable.element) that.virtualScrollable.destroy();
			that.virtualScrollable = null;
		},
		_destroyRowResizing: function() {
			if (this.rowResizing) {
				this.rowResizing.destroy();
				this.rowResizing = null;
			}
			if (this.rowResizer) {
				this._detachRowResizerEvents();
				this.rowResizer.off("dblclick.kendoGrid");
				this.rowResizer = null;
				this._clearCachedRowsHeight();
			}
		},
		_renderNoRecordsContent: function() {
			var that = this;
			if (that.options.noRecords) {
				var noRecordsElement = that.table.parent().children(".k-grid-norecords");
				if (noRecordsElement.length) noRecordsElement.remove();
				if (!that.dataSource || !that.dataSource.view().length) {
					noRecordsElement = $(that.noRecordsTemplate({ grid: that }));
					kendo.applyStylesFromKendoAttributes(noRecordsElement, ["margin", "position"]);
					noRecordsElement.insertAfter(that.table);
				}
			}
		},
		_setContentWidth: function(scrollLeft) {
			var that = this, hiddenDivClass = "k-grid-content-expander", hiddenDiv = "<div class=\"" + hiddenDivClass + "\"></div>", resizable = that.resizable, expander;
			if (that.options.scrollable && that.wrapper.is(":visible")) {
				expander = that.table.parent().children("." + hiddenDivClass);
				that._setContentWidthHandler = that._setContentWidth.bind(that);
				if (!that.dataSource || !that.dataSource.view().length) {
					if (!expander[0]) {
						expander = $(hiddenDiv).appendTo(that.table.parent());
						if (resizable) resizable.bind("resize", that._setContentWidthHandler);
					}
					if (that.thead) {
						expander.width(that.thead.width());
						if (!isNaN(parseFloat(scrollLeft, 10))) kendo.scrollLeft(that.content, scrollLeft);
					}
				} else if (expander[0]) {
					expander.remove();
					if (resizable) resizable.unbind("resize", that._setContentWidthHandler);
				}
				that._applyLockedContainersWidth(true);
				that._syncLockedContentHeight();
				if (that.lockedHeader && that.table[0].clientWidth === 0) that.table[0].style.width = "1px";
			}
		},
		_applyLockedContainersWidth: function(calculateGroupWidth) {
			if (this.options.scrollable && this.lockedHeader) {
				let headerTable = this.thead.parent(), headerWrap = headerTable.parent(), contentWidth = this.wrapper[0].clientWidth, groups = this._groups(), scrollbar = kendo.support.scrollbar(), cols = this.lockedHeader.find(">table>colgroup>col:not(.k-group-col, .k-hierarchy-col)"), nonLockedCols = headerTable.find(">colgroup>col:not(.k-group-col, .k-hierarchy-col)"), width = columnsWidth(cols), nonLockedColsWidth = columnsWidth(nonLockedCols), footerWrap;
				if (groups > 0 && calculateGroupWidth) width += outerWidth(this.lockedHeader.find(".k-group-cell").first()) * groups;
				if (width >= contentWidth) width = contentWidth - 3 * scrollbar;
				this.lockedHeader.add(this.lockedContent).width(width);
				headerWrap[0].style.width = headerWrap.parent().width() - width - 2 + PX;
				headerTable.add(this.table).width(nonLockedColsWidth);
				if (this.virtualScrollable && !isIE11) contentWidth -= scrollbar;
				this.content[0].style.width = contentWidth - width - 1 + PX;
				if (this.lockedFooter && this.lockedFooter.length) {
					this.lockedFooter.width(width);
					footerWrap = this.footer.find(".k-grid-footer-wrap");
					footerWrap[0].style.width = headerWrap[0].clientWidth + PX;
					footerWrap.children().first().width(nonLockedColsWidth);
				}
			}
		},
		_setContentHeight: function() {
			var that = this, options = that.options, height, header = that.wrapper.children(".k-grid-header"), scrollbar = kendo.support.scrollbar();
			var scrollableHeight = (options.scrollable || {}).height;
			if (options.scrollable && that.wrapper.is(":visible")) {
				if (scrollableHeight && that.content[0].style.height === "" && !that._scrollableHeightApplied) that.content[0].style.height = scrollableHeight;
				height = that.wrapper.innerHeight();
				height -= outerHeight(header);
				if (that.pager && that.pager.element.is(":visible")) height -= outerHeight(that.pager.element);
				if (options.groupable) height -= outerHeight(that.wrapper.children(".k-grouping-header:not(.k-hidden)"));
				if (options.toolbar) height -= outerHeight(that.wrapper.children(".k-grid-toolbar"));
				if (that.footerTemplate) height -= outerHeight(that.wrapper.children(".k-grid-footer"));
				if (that.statusBar) height -= outerHeight(that.wrapper.children(".k-selection-aggregates"));
				that.wrapper.find(".k-grid-pinned-container:not(.k-hidden)").each(function() {
					height -= outerHeight($(this).find(".k-grid-table")[0] || this);
				});
				var isGridHeightSet = function(el) {
					var initialHeight, newHeight;
					if (el[0].style.height) return true;
					else initialHeight = el.height();
					el.height(AUTO);
					newHeight = el.height();
					if (initialHeight != newHeight) {
						el.height("");
						return true;
					}
					el.height("");
					return false;
				};
				if (isGridHeightSet(that.wrapper)) if (height > scrollbar * 2) {
					if (that.lockedContent) {
						scrollbar = that.table[0].offsetWidth > that.table.parent()[0].clientWidth ? scrollbar : 0;
						that.lockedContent.height(height - scrollbar);
					}
					that.content.height(height);
				} else that.content.height(scrollbar * 2 + 1);
			}
		},
		_averageRowHeight: function() {
			var that = this, itemsCount = that._items(that.tbody, true).length, rowHeight = that._rowHeight;
			if (itemsCount === 0) return rowHeight;
			if (!that._rowHeight) {
				that._rowHeight = rowHeight = outerHeight(that.table) / itemsCount;
				that._sum = rowHeight;
				that._measures = 1;
			}
			var currentRowHeight = outerHeight(that.table) / itemsCount;
			if (rowHeight !== currentRowHeight) {
				that._measures++;
				that._sum += currentRowHeight;
				that._rowHeight = that._sum / that._measures;
			}
			return rowHeight;
		},
		_dataSource: function() {
			var that = this, options = that.options, pageable, dataSource = options.dataSource;
			dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
			if (isPlainObject(dataSource)) {
				extend(dataSource, {
					table: that.table,
					fields: that.columns
				});
				pageable = options.pageable;
				if (isPlainObject(pageable) && pageable.pageSize !== undefined) dataSource.pageSize = pageable.pageSize;
			}
			if (that.dataSource && that._refreshHandler) that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(PROGRESS, that._progressHandler).unbind(REQUESTEND, that._requestEndHandler).unbind(ERROR, that._errorHandler).unbind(SORT, that._sortHandler);
			else {
				that._refreshHandler = that.refresh.bind(that);
				that._progressHandler = that._requestStart.bind(that);
				that._requestEndHandler = that._requestEnd.bind(that);
				that._errorHandler = that._error.bind(that);
				that._sortHandler = that._clearSortClasses.bind(that);
			}
			that.dataSource = DataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(PROGRESS, that._progressHandler).bind(REQUESTEND, that._requestEndHandler).bind(ERROR, that._errorHandler).bind(SORT, that._sortHandler);
		},
		_error: function() {
			this._progress(false);
			this._requestInProgress = false;
		},
		_requestStart: function() {
			this._progress(true);
			this._requestInProgress = true;
		},
		_requestEnd: function() {
			this._requestInProgress = false;
		},
		_modelChange: function(e) {
			var that = this, tbody = that.tbody, model = e.model, row = that.tbody.find("tr[" + kendo.attr("uid") + "=" + model.uid + "]"), relatedRow, cell, column, isAlt = row.hasClass("k-table-alt-row"), tmp, idx = that._items(tbody).index(row), isLocked = that.lockedContent, selectable, selectableRow, childCells, originalCells, length;
			const stacked = that._isStackedMode();
			if (isLocked) relatedRow = that._relatedRow(row);
			if ((stacked ? row.add(relatedRow).find(".k-grid-stack-cell:not(.k-drag-cell):not(.k-command-cell)") : row.add(relatedRow).children(".k-edit-cell")).length && !that.options.rowTemplate) (stacked ? row.add(relatedRow).children(":not(.k-group-cell,.k-hierarchy-cell)").find(".k-grid-stack-cell:not(.k-drag-cell):not(.k-command-cell)") : row.add(relatedRow).children(":not(.k-group-cell,.k-hierarchy-cell)")).each(function() {
				cell = $(this);
				const isDetailCell = cell.find("[ref='collapse-detail-button'],[ref='expand-detail-button']").length > 0;
				if (stacked && (cell.hasClass("k-command-cell") || isDetailCell)) return;
				column = leafColumns(that.columns)[that._calculateColumnIndex(cell)];
				if (column?.field === e.field) {
					const editClass = stacked ? "k-grid-stack-edit-cell" : "k-edit-cell";
					if (!cell.hasClass(editClass)) {
						const target = stacked ? cell.find(".k-grid-stack-content") : cell;
						that._displayCell(target, column, model);
					} else cell.addClass("k-dirty-cell");
				}
			});
			else if (!row.hasClass("k-grid-edit-row")) {
				selectableRow = $().add(row);
				if (isLocked) {
					tmp = (isAlt ? that.lockedAltRowTemplate : that.lockedRowTemplate)(model);
					selectableRow = selectableRow.add(relatedRow);
					relatedRow.replaceWith(tmp);
				}
				tmp = (isAlt ? that.altRowTemplate : that.rowTemplate)(model);
				let tmpResult = $(tmp);
				kendo.applyStylesFromKendoAttributes(tmpResult, ["display"]);
				row.replaceWith(tmpResult);
				tmp = that._items(tbody).eq(idx);
				if (isLocked) {
					row = row.add(relatedRow);
					relatedRow = that._relatedRow(tmp)[0];
					adjustRowHeight(tmp[0], relatedRow);
					tmp = tmp.add(relatedRow);
				}
				selectable = that.options.selectable;
				if ((selectable || that._checkBoxSelection) && row.hasClass(SELECTED)) that.select(tmp);
				originalCells = selectableRow.children(":not(.k-group-cell,.k-hierarchy-cell)");
				childCells = tmp.children(":not(.k-group-cell,.k-hierarchy-cell)");
				for (idx = 0, length = that.columns.length; idx < length; idx++) {
					column = that.columns[idx];
					cell = stacked ? childCells.find().eq(idx) : childCells.eq(idx);
					if (selectable && originalCells.eq(idx).hasClass(SELECTED)) cell.addClass(SELECTED);
				}
				that.trigger("itemChange", {
					item: tmp,
					data: model,
					ns: ui
				});
			}
			if (!that._editContainer || !that._editContainer.closest(".k-grid-pinned-container").length) that._renderPinnedRows();
			that._toggleToolbarEditingItemsVisibility();
		},
		_pageable: function() {
			var that = this, pagerWrap, pageable = that.options.pageable, size = that.options.size, navigatable = that.options.navigatable;
			if (pageable) {
				pagerWrap = that.wrapper.children("div.k-grid-pager");
				if (!pagerWrap.length) pagerWrap = $("<div class=\"k-pager k-grid-pager\"/>");
				if (pageable.position === "top") pagerWrap.prependTo(that.wrapper).addClass("k-grid-pager-top");
				else pagerWrap.appendTo(that.wrapper);
				if (that.pager) that.pager.destroy();
				let adaptive = that.options.adaptiveMode;
				if (that.options.adaptiveMode === "auto") {
					if (pageable && typeof pageable === "object" && pageable.adaptiveMode) adaptive = pageable.adaptiveMode;
				}
				if (typeof pageable === "object" && pageable instanceof kendo.ui.Pager) that.pager = pageable;
				else if (that.dataSource._groupPaging) that.pager = new GroupsPager(pagerWrap, extend({}, pageable, {
					dataSource: that.dataSource,
					navigatable,
					size,
					adaptiveMode: adaptive
				}));
				else that.pager = new kendo.ui.Pager(pagerWrap, extend({}, pageable, {
					dataSource: that.dataSource,
					navigatable,
					size,
					adaptiveMode: adaptive
				}));
				that.pager.bind("pageChange", function(e) {
					if (that.trigger("page", { page: e.index })) e.preventDefault();
				});
				that._togglePagerVisibility();
			}
		},
		_statusBar: function() {
			var that = this, options = that.options, wrapper = that.wrapper, statusBarTemplate = options.statusBarTemplate, content = "";
			if (statusBarTemplate) if (!that.statusBar) {
				content += "<div class=\"k-selection-aggregates k-grid-selection-aggregates\">";
				content += statusBarTemplate({ aggregates: that._cellAggregates });
				content += "</div>";
				if (options.scrollable) that.statusBar = $(content).insertAfter(wrapper.find(".k-grid-container"));
				else that.statusBar = $(content).insertAfter(wrapper.find(".k-grid-table"));
			} else that.statusBar.html(statusBarTemplate({ aggregates: that._cellAggregates }));
		},
		_footer: function() {
			var that = this, aggregates = that.dataSource.aggregates(), html = "", footerTemplate = that.footerTemplate, options = that.options, footerWrap, footer = that.footer || that.wrapper.find(".k-grid-footer");
			if (footerTemplate) {
				html = $(that._wrapFooter(footerTemplate(aggregates)));
				kendo.applyStylesFromKendoAttributes(html, [
					"display",
					"left",
					"right"
				]);
				if (footer.length) {
					var tmp = html;
					footer.replaceWith(tmp);
					footer = that.footer = tmp;
				} else if (options.scrollable) {
					if (that.statusBar) that.footer = html.insertBefore(that.statusBar);
					else if (options.pageable && options.pageable.position !== "top") that.footer = html.insertBefore(that.wrapper.children("div.k-grid-pager"));
					else that.footer = html.appendTo(that.wrapper);
					footer = that.footer;
				} else footer = that.footer = html.insertAfter(that.tbody);
			} else if (footer && !that.footer) that.footer = footer;
			if (footer.length) {
				if (options.scrollable) {
					footerWrap = footer.attr(TABINDEX, -1).children(".k-grid-footer-wrap");
					that.scrollables = $(that.scrollables.filter(function() {
						return !$(this).is(".k-grid-footer-wrap");
					}).toArray()).add(footerWrap);
				}
				if (that._footerWidth) footer.find("table").css("width", that._footerWidth);
				if (that.thead && !that.lockedContent) syncFooterColsWidthsWithHeader(that.thead, footer);
				if (footerWrap) {
					var offset = kendo.scrollLeft(that.content);
					if (options.scrollable !== true && that.virtualScroll && that.virtualScroll.rows) offset = kendo.scrollLeft(that.wrapper.find(".k-virtual-scrollable-wrap"));
					kendo.scrollLeft(footerWrap, offset);
				}
			}
			if (that.lockedContent) {
				that._appendLockedColumnFooter();
				that._applyLockedContainersWidth();
				that._syncLockedFooterHeight();
			}
		},
		_wrapFooter: function(footerRow) {
			var that = this, html = "", table, scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0;
			if (that.options.scrollable) {
				html = $("<div class=\"k-grid-footer\"><div class=\"k-grid-footer-wrap\"><table class=\"k-table k-grid-footer-table\"><tfoot class=\"k-table-tfoot\">" + footerRow + "</tfoot></table></div></div>");
				table = html.find("table");
				table.addClass(kendo.getValidCssClass("k-table-", "size", that.options.size));
				that._appendCols(table);
				html.css(isRtl ? "padding-left" : "padding-right", scrollbar);
				return html;
			}
			return "<tfoot class=\"k-grid-footer k-table-tfoot\">" + footerRow + "</tfoot>";
		},
		_actionsheetFooterActionsTemplate: function(buttonsConfig) {
			let buttonsHtml = "";
			buttonsConfig.forEach((buttonOptions) => {
				const command = buttonOptions.command;
				const text = buttonOptions.text;
				buttonsHtml += kendo.html.renderButton(`<button data-command=${command}>${text}</button>`, buttonOptions);
			});
			return buttonsHtml;
		},
		_isAdaptive: function() {
			const that = this;
			return that.options.adaptiveMode === "auto" && (that.smallMQL.mediaQueryList.matches || that.mediumMQL.mediaQueryList.matches);
		},
		_isStackedMode: function() {
			return this.options.dataLayoutMode === "stacked";
		},
		_toolPopup: function(cell) {
			return cell.popup && cell.popup.wrapper;
		},
		_filterToolbarTool: function(cell) {
			const that = this;
			const columns = leafColumns(that.columns);
			const options = that.options;
			const clearFilterButton = (cell) => {
				const clearFilterSelector = that._isAdaptive() ? "[ref-actionsheet-action-button]" : "[ref='clear-filter']";
				return that._toolPopup(cell)?.find(clearFilterSelector);
			};
			const toolbarFilterOptions = that._toolbarOptionsForTool("filter");
			function content({ isAdaptive }) {
				let content = "";
				for (let i = 0; i < columns.length; i++) {
					const column = columns[i];
					if (!(options.filterable && column.filterable !== false) || column.command || column.draggable || column.selectable || column.pinnable) continue;
					content += `<div class="k-columnmenu-item-wrapper">`;
					content += `<div class="k-columnmenu-item-content k-columns-item" ref="filter" tabindex="0" data-field="${column.field}" data-index=${column.index || i || 0}></div>`;
					content += `</div>`;
				}
				if ((!toolbarFilterOptions || toolbarFilterOptions.clearButton) && !isAdaptive) {
					content += `<div class="k-actions k-actions-stretched k-actions-horizontal k-column-menu-footer">`;
					content += kendo.html.renderButton(`<button ref="clear-filter">${defaultActionSheetFooterButtons(that.options.messages).filter[0].text}</button>`, { icon: "filter-clear" });
					content += "</div>";
				}
				return content;
			}
			const menu = cell.data("kendoColumnMenu");
			if (menu) {
				menu.wrapper.off("click.kendoGrid");
				menu.element.off("click.kendoGrid");
				menu.destroy();
			}
			const filterable = options.filterable && that.options.columnMenu.filterable !== false ? extend(true, { pane: that.pane }, options.filterable) : false;
			if (!filterable) return;
			const menuOptions = {
				dataSource: that.dataSource,
				columns: false,
				sortable: false,
				filterable,
				hideAutoSizeColumn: true,
				owner: that,
				adaptiveMode: that.options.adaptiveMode,
				encodeTitles: that.options.encodeTitles,
				componentType: "modern",
				_actionsheet: {
					actionButtons: defaultActionSheetFooterButtons(that.options.messages).filter,
					title: "Filter by",
					closeButton: true,
					ref: "filter-view"
				},
				closeCallback: function(e) {
					e.removeClass(SELECTED);
					const popupElement = that._toolPopup(cell);
					if (popupElement.length) popupElement.find(".k-focus").removeClass("k-focus");
					cell.element.focus();
				},
				init: function(e) {
					cell.wrapper.attr("ref", "filter-tool");
					if (!that._showAdaptiveView) for (var idx = 0, length = columns.length; idx < length; idx++) {
						const column = columns[idx];
						const field = column.field;
						if (!(options.filterable && column.filterable !== false && !column.draggable && !column.selectable && !column.command && !column.pinnable)) continue;
						const element = cell.wrapper.find(`.k-columns-item[data-field="${field}"]`);
						that._initFilterMenuForColumn(column, element);
					}
				},
				contentTemplate: content,
				filtering: function(e) {
					if (that.trigger("filter", {
						filter: e.filter,
						field: e.field
					})) e.preventDefault();
					else {
						that._clearEditableState();
						if (that.dataSource.options.endless) that._resetEndless();
					}
				}
			};
			cell = cell.kendoColumnMenu(menuOptions).data("kendoColumnMenu");
			cell.element.bind("click.kendoGrid", function(e) {
				if (cell.popup && cell.popup._closing) return;
				$(e.currentTarget).addClass(SELECTED);
				clearFilterButton(cell)?.toggleClass("k-disabled", !cell.dataSource._filter);
			});
			const filterToolHandler = function(e) {
				const cell = e.sender.wrapper && e.sender.wrapper.find(".k-grid-filter-tool")?.data("kendoColumnMenu");
				const filters = cell.dataSource._filter;
				const field = e.field;
				let isRemoveFilter = !e.filter;
				let condition;
				const filteredFields = filters && filters.filters;
				if (isRemoveFilter && filteredFields && filteredFields.length && !filteredFields.find((descriptor) => descriptor.filters && descriptor.filters.length ? descriptor.filters[0].field === field : descriptor.field === field)) return;
				const expansionPanel = cell.wrapper.find(`.k-columns-item[data-field='${field}']`);
				const headerElement = expansionPanel?.closest(".k-expander")?.find(".k-columnmenu-item");
				if (filters && filters.filters.length) if (isRemoveFilter) condition = filteredFields.filter((descriptor) => descriptor.field !== field).length > 0;
				else condition = !isRemoveFilter;
				else if (!isRemoveFilter) condition = true;
				if (expansionPanel.length) expansionPanel.data("kendoExpansionPanel").toggle(false);
				clearFilterButton(cell)?.toggleClass("k-disabled", !condition);
				that._toggleColumnMenuFilterIndicator(headerElement, !isRemoveFilter);
				that._toggleBadge(cell.element, condition);
			};
			that.unbind("filter", filterToolHandler);
			that.bind("filter", filterToolHandler);
			cell.bind("open", function() {
				const popupElement = that._toolPopup(cell);
				if (popupElement.length) {
					popupElement.focus();
					const clearFilterBtn = clearFilterButton(cell);
					const filters = cell.dataSource._filter && cell.dataSource._filter.filters;
					if (filters && filters.length) filters.forEach((descriptor) => {
						const field = descriptor.field || descriptor.filters[0].field;
						const headerElement = cell.wrapper.find(`.k-columns-item[data-field='${field}']`)?.closest(".k-expander")?.find(".k-columnmenu-item");
						that._toggleColumnMenuFilterIndicator(headerElement, true);
					});
					if (clearFilterBtn.length) {
						clearFilterBtn.off("mousedown.kendoGrid");
						clearFilterBtn.on("mousedown.kendoGrid", function() {
							that.dataSource.filter([]);
							that._toggleBadge(cell.element, false);
							cell.wrapper.find(".k-columnmenu-indicators").remove();
							clearFilterBtn.toggleClass("k-disabled", true);
							cell.popup.close();
						});
					}
				}
			});
		},
		_initFilterMenuForColumn: function(column, element) {
			const that = this;
			let filterable = that._hasFilterMenu();
			let filterMenu;
			if (element.length) {
				filterMenu = element.data("kendoFilterMenu");
				if (filterMenu) filterMenu.destroy();
				filterMenu = element.data("kendoFilterMultiCheck");
				if (filterMenu) filterMenu.destroy();
				var columnFilterable = column.filterable;
				var options = extend({}, filterable, columnFilterable, {
					appendToElement: true,
					componentType: "modern",
					dataSource: that.dataSource,
					values: column.values,
					format: column.format,
					title: column.title || column.field,
					pane: that.pane,
					adaptiveMode: that.options.adaptiveMode,
					change: function(e) {
						if (that.trigger("filter", {
							filter: e.filter,
							field: e.field
						})) e.preventDefault();
						else {
							that._clearEditableState();
							if (that.dataSource.options.endless) that._resetEndless();
						}
					}
				});
				if (columnFilterable && columnFilterable.messages) options.messages = extend(true, {}, filterable.messages, columnFilterable.messages);
				if (columnFilterable && columnFilterable.dataSource) {
					options.forceUnique = false;
					options.checkSource = columnFilterable.dataSource;
				}
				if (columnFilterable && columnFilterable.multi) return element.kendoFilterMultiCheck(options).data("kendoFilterMultiCheck");
				else return element.kendoFilterMenu(options).data("kendoFilterMenu");
			}
		},
		_isRowSelection: function() {
			const that = this;
			const selectable = that.options.selectable;
			if (!selectable && !that._checkBoxSelection) return false;
			if (selectable === true || selectable === "multiple" || selectable === "single" || selectable?.mode === "multiple" || selectable?.mode === "single" || that._checkBoxSelection) return true;
			if (isPlainObject(selectable)) {
				const mode = selectable.mode;
				return mode ? typeof mode === "string" && mode.includes("row") : false;
			}
			if (typeof selectable === "string") return selectable.includes("row");
			return false;
		},
		_isSingleSelectionEnabled: function() {
			const selectable = this.options.selectable;
			if (!selectable) return false;
			if (selectable === true || selectable === "single" || selectable?.mode === "single" || selectable === "row" || selectable?.mode === "row" || selectable === "cell" || selectable?.mode === "cell") return true;
			if (isPlainObject(selectable)) {
				const mode = selectable.mode;
				return mode ? typeof mode === "string" && mode.includes("single") : false;
			}
			if (typeof selectable === "string") return selectable.includes("single");
			return false;
		},
		_initAiPrompt: function() {
			const that = this;
			const aiOptions = that.options.ai;
			const promptConfig = aiOptions?.aiAssistant || {};
			const messages = that.options.messages?.ai;
			let aiService = aiOptions?.service;
			const defaults = {
				data: (prompt) => getDefaultAIRequestConfig(prompt, flatColumnsInDomOrder(that.columns)),
				outputGetter: (response) => aiPromptDefaultOutputGetter(response, messages, that._isRowSelection())
			};
			if (aiOptions?.service) {
				const serviceIsString = typeof aiOptions.service === "string";
				const url = serviceIsString ? aiOptions.service : aiOptions.service.url;
				if (url) aiService = {
					...defaults,
					...!serviceIsString ? aiOptions.service : {},
					url
				};
			}
			const skeletonID = "skeleton" + kendo.guid();
			const aiAssistant = that._aiAssistant = $("<div></div>").kendoAIPrompt($.extend(true, {
				service: aiService,
				speechToText: true,
				promptTextArea: {
					resize: "none",
					rows: 3,
					placeholder: messages?.promptPlaceholder || "",
					fillMode: "outline",
					maxLength: 1e3
				},
				outputTemplate: (data) => {
					return data.output.output?.split("/n").map((line) => aiPromptOutputTemplate(line, messages, flatColumnsInDomOrder(that.columns))).join("");
				},
				views: [{
					type: "prompt",
					buttonIcon: "sparkles",
					themeColor: "primary",
					footerTemplate: () => `<div class="k-actions k-actions-start k-actions-horizontal k-prompt-actions">${kendo.html.renderButton(`<button ref-ai-apply-button>Apply</button>`, {
						themeColor: "primary",
						icon: "table-wizard",
						rounded: "full"
					})}</div>`
				}, {
					type: "output",
					themeColor: "primary",
					isStreaming: true,
					isLoading: true
				}],
				promptRequest: (e) => {
					if (!aiAssistant.options.service) return;
					aiAssistant.activeView("output");
					aiAssistant.removePromptOutput(placeholderId);
					aiAssistant.addPromptOutput({
						id: skeletonID,
						prompt: e.prompt,
						output: "",
						isLoading: true,
						isStreaming: true
					});
					aiAssistant._requestInProgress = true;
					aiAssistant.transport.options.requestStart = null;
				},
				promptResponse: (e) => {
					e.preventDefault();
					const output = e;
					handleAIResponseOutput(output, aiAssistant, skeletonID);
					if (output && output.response) that.handleAIResponse(output.response);
					if (aiOptions.autoClose === false) return;
					that._aiAssistantWindow?.close();
				},
				promptRequestCancel: () => {
					aiAssistant.removePromptOutput(skeletonID);
					if (!aiAssistant.outputObjects.size) aiAssistant.addPromptOutput(promptPlaceholderOptions(messages));
					aiAssistant._requestInProgress = false;
				}
			}, promptConfig)).data("kendoAIPrompt");
			if (aiAssistant?.transport) aiAssistant.transport.options.error = function(res) {
				res.response.abort();
				handleAIResponseOutput({
					output: res.output,
					outputId: res.id
				}, aiAssistant, skeletonID);
			};
			that._aiAssistantWindow.element.append(that._aiAssistant.element);
			that._aiAssistantWindow.wrapper.addClass("k-grid-assistant-window");
		},
		handleAIResponse: function(response) {
			const that = this;
			const fields = that.dataSource.options.schema?.model?.fields;
			const multipleSortable = that.options.sortable?.mode === "multiple" || that.options.sortable?.mode === "mixed";
			const commands = prepareAICommands(response?.commands, multipleSortable);
			const allColumns = flatColumnsInDomOrder(that.columns);
			const enabled = getEnabledAICommands(that.options, that.dataSource, that._checkBoxSelection);
			if (commands) for (let i = 0; i < commands.length; i++) {
				const command = commands[i];
				const type = command?.type;
				const commandKey = supportedAIDataSourceCommands[type] || supportedAIGridCommands[type];
				const isPageCommand = commandKey === "page" || commandKey === "pageSize";
				let isClearValue = !command[commandKey];
				let value;
				if (supportedAIDataSourceCommands[type]) {
					const hasPreviousValue = that.dataSource[commandKey]();
					if (isPageCommand) value = command[commandKey];
					else {
						value = isClearValue ? [] : command[commandKey];
						value = Array.isArray(value) ? value : [value];
					}
					if (!(isClearValue || !isClearValue && !hasInvalidDescriptor(value, commandKey))) continue;
					if (isClearValue && !hasPreviousValue) continue;
					if (enabled[type]) {
						if (!isClearValue && commandKey === "filter") value = parseDate(value, fields);
						if (commandKey === "sort" && !multipleSortable) {
							that.dataSource[commandKey](value);
							continue;
						}
						if (hasPreviousValue && !isClearValue && !isPageCommand) {
							const previousValue = Array.isArray(hasPreviousValue) ? hasPreviousValue : [hasPreviousValue];
							if (commandKey === "filter") value = previousValue.map((v) => ({
								...v,
								filters: [...v.filters, value[0]]
							}));
							else {
								const config = getMergedAIDescriptors(previousValue, value, commandKey);
								if (config.shouldContinue) continue;
								value = config.descriptors;
							}
						}
						that.dataSource[commandKey](value);
					}
				}
				if (supportedAIGridCommands[type]) {
					value = isClearValue ? [] : command[commandKey];
					value = Array.isArray(value) ? value : [value];
					if (commandKey === "highlight") {
						that.clearHighlight();
						if (!isClearValue) {
							that._hasAIHighlight = value;
							that._applyAIHighlight(value);
						}
						continue;
					}
					if (commandKey === "select" && enabled[type] && !command.skipCommand) {
						that.clearSelection();
						if (!isClearValue) {
							that._hasAISelection = value;
							that._applyAISelection(value);
						}
						continue;
					}
					if (commandKey === "_exportPdf" || commandKey === "_exportExcel" || commandKey === "_exportCsv") {
						const responseWithRemainingCommands = { commands: commands.slice(i + 1) };
						const customFileName = command.fileName;
						that._changeAIExportFileName(commandKey, customFileName, true);
						that[commandKey](() => {
							that._changeAIExportFileName(commandKey, customFileName, false);
							that.handleAIResponse(responseWithRemainingCommands);
						});
						break;
					}
					if (enabled[type]) {
						const identifier = command.id;
						const width = kendo.parseFloat(command.size);
						const position = command.position ?? command.index;
						const useIndex = commandKey === "lockColumn" || commandKey === "unlockColumn";
						let colIndex;
						const col = allColumns.find((col, idx) => {
							if (col.uid === identifier) {
								colIndex = idx;
								return col;
							}
						});
						const column = useIndex ? colIndex : col;
						const isMultiColumnHeader = allColumns.length > that.columns.length;
						if (column) {
							let firstArgument = position ?? column;
							let secondArgument = position !== undefined ? column : width;
							let reorderPosition;
							if (isMultiColumnHeader && commandKey === "reorderColumn") {
								firstArgument = targetParentContainerIndex(allColumns, that.columns, colIndex, position);
								reorderPosition = colIndex > position ? "before" : "after";
							}
							that[commandKey](firstArgument, secondArgument, reorderPosition);
						}
					}
				}
			}
		},
		getAIRequest: function(prompt) {
			return getDefaultAIRequestConfig(prompt, flatColumnsInDomOrder(this.columns));
		},
		_changeAIExportFileName: function(commandKey, customFileName, toggle) {
			const that = this;
			let exportConfig;
			if (commandKey === "_exportPdf") exportConfig = {
				optionName: "pdf",
				variableName: "_aiPdfFilename"
			};
			else if (commandKey === "_exportCsv") exportConfig = {
				optionName: "csv",
				variableName: "_aiCsvFilename"
			};
			else exportConfig = {
				optionName: "excel",
				variableName: "_aiExcelFilename"
			};
			if (!toggle) {
				if (commandKey !== "_exportPdf" && commandKey !== "_exportExcel" && commandKey !== "_exportCsv") return;
				that.options[exportConfig.optionName].fileName = that[exportConfig.variableName];
				delete that[exportConfig.variableName];
				return;
			}
			let originalName = that.options?.[exportConfig.optionName]?.fileName;
			if (customFileName) {
				that[exportConfig.variableName] = originalName;
				that.options[exportConfig.optionName].fileName = customFileName;
			}
		},
		_bindAIPrompt: function() {
			const that = this;
			const aiOptions = that.options.ai;
			const aiAssistant = that._aiAssistant;
			const getGenerateOutputButton = (element) => element.find("[ref-ai-apply-button]");
			aiAssistant.element.on("input.kendoGrid", "[ref-prompt-input]", (e) => {
				const button = getGenerateOutputButton(aiAssistant.element);
				const value = $(e.currentTarget).val();
				button.toggleClass("k-disabled", !value || value.trim() === "");
			});
			if (aiOptions?.aiAssistant?.promptSuggestions) aiAssistant.element.on("click.kendoGrid", ".k-suggestion", (e) => {
				getGenerateOutputButton(aiAssistant.element).toggleClass("k-disabled", false);
			});
			aiAssistant.element.on("click.kendoGrid", "[ref-ai-apply-button]", () => {
				const prompt = aiAssistant.element.find("[ref-prompt-input]").getKendoTextArea().value();
				const promptView = aiAssistant._selectedView;
				const eventArgs = {
					prompt,
					isRetry: false,
					history: []
				};
				if (promptView?.service) eventArgs.service = promptView.service;
				aiAssistant.trigger("promptRequest", eventArgs);
				if (aiAssistant.transport) aiAssistant.transport.read({
					prompt: eventArgs.prompt,
					history: eventArgs.history,
					isRetry: false,
					service: promptView?.service
				});
			});
			if (aiAssistant?.speechToTextButton) aiAssistant.speechToTextButton.bind("result", function() {
				getGenerateOutputButton(aiAssistant.element).toggleClass("k-disabled", false);
			});
		},
		_unbindAIPrompt: function() {
			const aiAssistant = this._aiAssistant;
			aiAssistant?.element?.off("input.kendoGrid");
			aiAssistant?.element?.off("click.kendoGrid");
		},
		_prepareAIDataForSelectOrHighlight: function(value) {
			const that = this;
			const schema = that.dataSource.options.schema;
			const idField = schema?.model?.id || "id";
			const fields = schema?.model?.fields;
			value = parseDate(value, fields);
			return that.dataSource.parseHighlightDescriptors(value, idField);
		},
		_applyAIHighlight: function(value) {
			const that = this;
			const data = that._prepareAIDataForSelectOrHighlight(value);
			that.highlight(data);
		},
		_applyAISelection: function(value) {
			const that = this;
			const data = that._prepareAIDataForSelectOrHighlight(value);
			that._getElementsToSelect(data).forEach((item) => that.select(item));
		},
		_initAiAssistantWindow: function(cell) {
			const that = this;
			const aiOptions = that.options.ai;
			const skipInitialization = aiOptions && aiOptions === true || aiOptions === false;
			const userConfig = aiOptions?.aiAssistantWindow || {};
			const customContent = userConfig?.content;
			const windowOptions = {
				modal: false,
				resizable: false,
				title: "AI Assistant",
				visible: false,
				actions: [
					"Minimize",
					"Maximize",
					"Close"
				],
				position: {
					top: outerHeight(cell) + cell.offset().top,
					left: cell.offset().left + cell.outerWidth() / 2
				},
				width: 437,
				...userConfig,
				open: function(e) {
					if (userConfig.open) userConfig.open(e);
					if (e._defaultPrevented) return;
					const ai = that._aiAssistant;
					if (ai && ai.element) {
						const messages = that.options.messages?.ai;
						if (!ai.outputObjects.size) ai.addPromptOutput(promptPlaceholderOptions(messages));
						ai.activeView("prompt");
						const button = ai.element.find("[ref-ai-apply-button]");
						const value = ai.element.find("[ref-prompt-input]").val();
						if (that._aiAssistant) that._bindAIPrompt();
						button.toggleClass("k-disabled", !value || value.trim() === "");
					}
				},
				close: function(e) {
					if (userConfig.close) userConfig.close(e);
					if (e._defaultPrevented) return;
					if (e.userTriggered) {
						e.sender.element.trigger("focus");
						var currentIndex = that.items().index($(that.current()).parent());
						that._toggleToolbarEditingItemsVisibility();
						if (that.options.navigatable && that.current()) {
							that._setCurrent(that.items().eq(currentIndex).children().filter(NAVCELL).first());
							focusTable(that.table, true);
						}
					}
					if (that._aiAssistant) {
						if (!aiOptions?.keepOutputHistory) that._aiAssistant?.clearOutput();
						that._unbindAIPrompt();
					}
				}
			};
			if (!skipInitialization) {
				that._aiAssistantWindow = $("<div></div>").kendoWindow(windowOptions).data("kendoWindow");
				if (!customContent) that._initAiPrompt();
			}
			cell.bind("click.kendoGrid", function(e) {
				if (!that._aiAssistantWindow) return;
				if (that._aiAssistantWindow.element.is(":visible")) that._aiAssistantWindow.close();
				else that._aiAssistantWindow.open();
			});
		},
		_toolbarOptionsForTool: function(toolName) {
			const options = this.options;
			let toolbarItems = [];
			if (Array.isArray(options.toolbar)) toolbarItems = options.toolbar;
			else if (options.toolbar && options.toolbar.items) toolbarItems = options.toolbar.items;
			return extend({}, defaultCommands[toolName], toolbarItems.find((item) => item.name === toolName));
		},
		_groupToolbarTool: function(cell) {
			const that = this;
			const options = that.options;
			const clearGroupButton = (cell) => {
				const clearGroupSelector = that._isAdaptive() ? "[ref-actionsheet-action-button]:not('.k-button-primary')" : "[ref='clear-group']";
				return that._toolPopup(cell)?.find(clearGroupSelector);
			};
			let menu;
			let menuOptions;
			if (!(options.groupable && options.groupable.enabled !== false || that.dataSource._groupPaging)) return;
			menu = cell.data("kendoColumnMenu");
			if (menu) {
				menu.wrapper.off("click.kendoGrid");
				menu.element.off("click.kendoGrid");
				menu.destroy();
			}
			const toolbarGroupOptions = that._toolbarOptionsForTool("group");
			toolbarGroupOptions.reorderButtons = true;
			const indicator = ({ isAdaptive, isFirstItem, isLastItem }) => `<span class="k-group-menu-item-actions">
                ${toolbarGroupOptions.reorderButtons ? `<span class="k-group-menu-item-action k-group-menu-item-up-action ${isFirstItem ? "k-disabled" : ""}" ${isFirstItem ? "aria-disabled=true" : "aria-disabled=false"}>
                    ${kendo.ui.icon($("<span></span>"), {
				icon: "chevron-up",
				size: isAdaptive ? "large" : "medium"
			})}
                </span>
                <span class="k-group-menu-item-action k-group-menu-item-down-action ${isLastItem ? "k-disabled" : ""}" ${isLastItem ? "aria-disabled=true" : "aria-disabled=false"}>
                    ${kendo.ui.icon($("<span></span>"), {
				icon: "chevron-down",
				size: isAdaptive ? "large" : "medium"
			})}
                </span>` : `<span class="k-group-menu-item-action k-group-menu-item-drag-action">
                    ${kendo.ui.icon($("<span></span>"), {
				icon: "handle-drag-dots",
				size: isAdaptive ? "large" : "medium"
			})}
                    </span>`}
                </span>`;
			const itemTemplate = (column, action, options) => {
				const isAdaptive = that._isAdaptive();
				const title = column.title || column.field || "";
				return `<div class="k-group-menu-item" data-field="${column.field}" data-index="${options && options.index || column.index || "0"}" tabindex="0">
                ${options && options.renderIndicator ? indicator({
					isAdaptive,
					isFirstItem: options.isFirstItem,
					isLastItem: options.isLastItem
				}) : ""}
                        <span class="k-group-item-text">${title}</span>
                        <span class="k-spacer"></span>
                        <span class="k-group-menu-item-actions">
                            <span class="k-group-menu-item-action ${action.actionClass}">
                                ${kendo.ui.icon($("<span></span>"), {
					...action,
					size: isAdaptive ? "large" : "medium"
				})}
                            </span>
                        </span>
                    </div>`;
			};
			const content = ({ columns }) => {
				const isAdaptive = that._isAdaptive();
				let content = `<div class="k-group-menu"><div class='k-group-menu-item-wrap'>`;
				columns.forEach((column) => {
					if (!isColumnGroupable(that, column) || column.command || column._originalObject.selectable) return;
					content += itemTemplate(column, {
						icon: "plus-circle",
						actionClass: "k-group-menu-item-add-action"
					});
				});
				content += `</div>`;
				if ((!toolbarGroupOptions || toolbarGroupOptions.clearButton) && !isAdaptive) content += `<div class="k-actions k-actions-stretched k-actions-horizontal k-column-menu-footer">
                                     ${kendo.html.renderButton(`<button ref="clear-group">${defaultActionSheetFooterButtons(that.options.messages).group[0].text}</button>`, { icon: "x" })}
                                </div>`;
				content += `</div>`;
				return content;
			};
			menuOptions = {
				dataSource: that.dataSource,
				columns: false,
				sortable: false,
				filterable: false,
				hideAutoSizeColumn: false,
				owner: that,
				adaptiveMode: that.options.adaptiveMode,
				encodeTitles: that.options.encodeTitles,
				componentType: "modern",
				_actionsheet: {
					actionButtons: defaultActionSheetFooterButtons(that.options.messages).group,
					title: "Group by",
					ref: "group-view",
					closeButton: {
						icon: "check",
						themeColor: "primary"
					}
				},
				init: function(e) {
					const element = cell.wrapper.find(".k-group-menu");
					element.attr("ref", "group-tool");
					if (cell._showAdaptiveView) {
						element.unwrap();
						element.removeClass("k-group-menu-md").addClass("k-group-menu-lg");
					} else cell.wrapper.removeClass("k-column-menu");
					element.find(".k-group-menu-item").on("click.kendoGrid", `.k-group-menu-item-remove-action, .k-group-menu-item-add-action`, function(e) {
						that._groupItemClickHandler(e, cell, {
							itemTemplate,
							indicator
						});
					});
					that._syncGroupingTool(e, cell, that.dataSource.group(), {
						itemTemplate,
						indicator
					});
				},
				closeCallback: function(e) {
					e.removeClass(SELECTED);
					const popupElement = that._toolPopup(cell);
					if (popupElement.length) popupElement.find(".k-focus").removeClass("k-focus");
					cell.element.focus();
				},
				contentTemplate: content
			};
			let oldGroups = that.dataSource.group() || [];
			cell = cell.kendoColumnMenu(menuOptions).data("kendoColumnMenu");
			const groupToolHandler = function(e) {
				const that = this;
				const cell = e.sender.wrapper && e.sender.wrapper.find(".k-grid-group-tool")?.data("kendoColumnMenu");
				const groups = e.groups || that.dataSource.group() || [];
				const clearButton = clearGroupButton(cell);
				const groupedData = oldGroups.length > groups.length ? oldGroups : groups;
				that._syncGroupingTool(e, cell, groupedData, {
					itemTemplate,
					indicator
				});
				that._toggleBadge(cell.element, groups.length > 0);
				clearButton?.toggleClass("k-disabled", !groups.length);
				oldGroups = groups;
			};
			that.unbind("group", groupToolHandler);
			that.bind("group", groupToolHandler);
			cell.element.on("click.kendoGrid", function(e) {
				if (cell.popup && cell.popup._closing) return;
				$(e.currentTarget).addClass(SELECTED);
			});
			cell.bind("open", function() {
				const clearButton = clearGroupButton(cell);
				const currentGroups = that.dataSource.group() || [];
				const getWrapper = () => cell._showAdaptiveView ? that._toolPopup(cell) : cell.wrapper;
				const wrapper = getWrapper();
				const groupContainer = wrapper?.find("[ref='group-container']");
				if (wrapper && !currentGroups.length && groupContainer && groupContainer.length && groupContainer.children().length) {
					const ungroupedContainer = wrapper.find(".k-group-menu-item-wrap").not("[ref='group-container']");
					groupContainer.children(".k-group-menu-item").each(function() {
						const item = $(this);
						item.find(".k-group-menu-item-up-action, .k-group-menu-item-down-action").parent().remove();
						const removeAction = item.find(".k-group-menu-item-remove-action");
						if (removeAction.length) {
							removeAction.removeClass("k-group-menu-item-remove-action").addClass("k-group-menu-item-add-action");
							const icon = removeAction.find("[class*='k-svg-i-']");
							if (icon.length) icon.replaceWith(kendo.ui.icon($("<span></span>"), {
								icon: "plus-circle",
								size: that._isAdaptive() ? "large" : "medium"
							}));
						}
						ungroupedContainer.append(item);
					});
					if (groupContainer.data("kendoReorderable")) groupContainer.data("kendoReorderable").destroy();
					groupContainer.remove();
					oldGroups = [];
				}
				if (clearButton.length) {
					clearButton.off("mousedown.kendoGrid");
					clearButton.on("mousedown.kendoGrid", function() {
						that.dataSource.group([]);
						that.groupable.trigger(CHANGE, { groups: [] });
						that._toggleBadge(cell.element, false);
						clearButton?.toggleClass("k-disabled", true);
						cell.popup.close();
					});
					clearButton.toggleClass("k-disabled", !currentGroups.length);
				}
			});
		},
		_toggleReorderButtonsDisabledState: function(item, isFirstItem, isLastItem) {
			const disabled = "k-disabled";
			if (!item.length) return;
			const firstReorderButton = item.find(".k-group-menu-item-actions .k-group-menu-item-up-action");
			if (firstReorderButton.length) {
				if (isFirstItem && !firstReorderButton.hasClass(disabled)) {
					firstReorderButton.addClass(disabled);
					firstReorderButton.attr("aria-disabled", true);
				} else if (!isFirstItem && firstReorderButton.hasClass(disabled)) {
					firstReorderButton.removeClass(disabled);
					firstReorderButton.attr("aria-disabled", false);
				}
				const prevItem = firstReorderButton.next();
				if (prevItem.length) {
					if (isLastItem && !prevItem.hasClass(disabled)) {
						prevItem.addClass(disabled);
						prevItem.attr("aria-disabled", true);
					} else if (!isLastItem && prevItem.hasClass(disabled)) {
						prevItem.removeClass(disabled);
						prevItem.attr("aria-disabled", false);
					}
				}
			}
		},
		_syncGroupingTool: function(ev, cell, groupedData, templates) {
			const that = this;
			const groups = ev.groups || that.dataSource.group() || [];
			const getWrapper = () => cell._showAdaptiveView ? that._toolPopup(cell) : cell.wrapper;
			const containerClass = "k-group-menu-item-wrap";
			const findContainers = () => getWrapper()?.find(`.${containerClass}`);
			const wrapper = getWrapper();
			let containers = findContainers();
			if (cell._draggableInstance && cell._triggeredReordering) {
				delete cell._triggeredReordering;
				return;
			}
			if (containers && containers.length) {
				groupedData.forEach((group, i) => {
					const item = wrapper.find(`.k-group-menu-item[data-field='${group.field}']`);
					const index = item.data("index");
					const action = item.find(".k-group-menu-item-add-action").length ? "remove" : "add";
					const shouldAdd = action === "remove";
					const actionClass = `k-group-menu-item-${action}-action`;
					const parentContainer = item.parent();
					containers = findContainers();
					let isFirstItem = groups && groups.length ? groups[0]?.field === group.field : false;
					let isLastItem = groups && groups.length ? groups[groups.length - 1].field === group.field : false;
					that._toggleReorderButtonsDisabledState(item, isFirstItem, isLastItem);
					const isRemoveAction = !groups.find((descriptor) => descriptor.field === group.field);
					if (!isRemoveAction && parentContainer.is("[ref='group-container']")) return;
					let container;
					if (containers && containers.length === 2) container = shouldAdd ? containers.first() : containers.last();
					else {
						container = $(`<div class="${containerClass}" ${shouldAdd ? "ref=\"group-container" : ""}"></div>`);
						const method = shouldAdd ? "prepend" : "append";
						const containersWrapper = containers.first().parent();
						const clearBtn = containersWrapper.find("[ref='clear-group']");
						if (clearBtn.length && !shouldAdd) clearBtn.parent().before(container);
						else if (containersWrapper.length) containersWrapper[method](container);
					}
					const column = leafColumns(that.columns)[index];
					const shouldAddDragIndicator = !isRemoveAction && groups.length > 1;
					const templateOptions = {
						renderIndicator: shouldAddDragIndicator,
						index,
						isFirstItem,
						isLastItem
					};
					const groupedItem = $(templates.itemTemplate(column, {
						icon: shouldAdd ? "x-circle" : "plus-circle",
						actionClass
					}, templateOptions));
					item.remove();
					if (containers.length === 2 && !parentContainer.children().length) {
						if (parentContainer.data("kendoReorderable")) parentContainer.data("kendoReorderable")?.destroy();
						parentContainer.remove();
					}
					if (shouldAdd) {
						if (shouldAddDragIndicator) {
							const firstItem = container.children().first();
							if (firstItem.find(".k-group-menu-item-actions")?.length === 1) firstItem.prepend(templates.indicator({
								isAdaptive: that._isAdaptive(),
								isFirstItem: true,
								isLastItem: false
							}));
						}
						container.append(groupedItem);
					} else {
						const nextItem = container.find(`.k-group-menu-item[data-index="${index + 1}"]`);
						if (groups.length === 1) parentContainer.children().first().find(".k-group-menu-item-actions:first-of-type").remove();
						if (nextItem.length) nextItem.before(groupedItem);
						else container.append(groupedItem);
					}
					container.find(groupedItem).on("click.kendoGrid", `.k-group-menu-item-remove-action, .k-group-menu-item-add-action`, function(e) {
						that._groupItemClickHandler(e, cell);
					});
				});
				containers = findContainers();
				if (containers && containers.length) {
					const dragIndicators = containers.first().find(".k-group-menu-item-drag-action");
					const reorderButtons = containers.first().find(".k-group-menu-item-up-action, .k-group-menu-item-down-action");
					const container = containers.first();
					const hasContainer = container.is("[ref='group-container']");
					const initDragInstance = dragIndicators.length;
					const initButtonsReordering = reorderButtons.length;
					if (hasContainer && initButtonsReordering) reorderButtons.each(function() {
						const button = $(this);
						button.unbind("click.kendoGrid");
						button.bind("click.kendoGrid", (e) => that._buttonClickReorderHandler(e, cell));
					});
					else if (hasContainer && initDragInstance) {
						that._groupToolDraggableInstance(cell, container);
						that._groupToolReorderableInstance(cell, container);
					}
				}
			}
		},
		_buttonClickReorderHandler: function(e, cell) {
			e.preventDefault();
			e.stopPropagation();
			const that = this;
			const itemSelector = ".k-group-menu-item";
			const clicked = $(e.currentTarget);
			const element = clicked.closest(itemSelector);
			const position = clicked.hasClass("k-group-menu-item-up-action") ? "before" : "after";
			const evData = {
				element,
				target: position === "before" ? element.prev(itemSelector) : element.next(itemSelector),
				position
			};
			that._handleGroupReordering(evData, cell);
		},
		_groupToolDraggableInstance: function(cell, container) {
			const isMobile = this._isMobile;
			cell._draggableInstance = container.kendoDraggable({
				holdToDrag: isMobile,
				showHintOnHold: isMobile,
				preventOsHoldFeatures: isMobile,
				group: "group-item-draggable",
				autoScroll: true,
				filter: ".k-group-menu-item-drag-action",
				hint: function(target) {
					return $("<div class=\"k-reorder-clue k-drag-clue\">" + kendo.ui.icon({
						icon: "cancel",
						iconClass: "k-drag-status"
					}) + "</div>");
				},
				clickMoveClick: false,
				cursorOffset: {
					top: 0,
					left: 0
				}
			}).data("kendoDraggable");
		},
		_groupToolReorderableInstance: function(cell, container) {
			const that = this;
			const itemSelector = ".k-group-menu-item";
			if (container && container.data("kendoReorderable")) container.data("kendoReorderable").destroy();
			container.kendoReorderable({
				smartPosition: false,
				draggable: cell._draggableInstance,
				dragOverContainers: function(sourceIndex, targetIndex) {
					var result = true;
					$(itemSelector).eq(targetIndex);
					return result;
				},
				dropFilter: "> .k-group-menu-item",
				allowIcon: "insert-middle",
				orientation: "vertical",
				reorderDropCue: $("<div class=\"k-drop-hint k-drop-hint-h\"><div class=\"k-drop-hint-start\"></div><div class=\"k-drop-hint-line\"></div></div>"),
				positionDropCue: function(reorderDropCue) {
					reorderDropCue.css({ transform: "translate(0,-50%)" });
				},
				externalDraggable: function(e) {
					var draggable = e.draggable;
					if (draggable) return draggable;
				},
				change: function(e) {
					that._handleGroupReordering(e, cell);
				}
			});
		},
		_handleGroupReordering: function(e, cell) {
			const element = e.element;
			const newSibling = e.target;
			const method = e.position === "before" ? "insertBefore" : "insertAfter";
			const descriptors = this.groupable.descriptors();
			element[method](newSibling);
			const items = element.parent().children();
			const newDescriptors = [];
			for (let i = 0; i < items.length; i++) {
				const field = $(items[i]).data("field");
				const descriptor = descriptors.find((d) => d.field === field);
				if (descriptor) newDescriptors.push(descriptor);
			}
			cell._triggeredReordering = true;
			this.dataSource.group(newDescriptors);
			this.groupable.trigger(CHANGE, { groups: newDescriptors });
		},
		_groupItemClickHandler: function(e, cell) {
			const that = this;
			const item = $(e.currentTarget).closest(".k-group-menu-item");
			const shouldAdd = ($(e.currentTarget).find(".k-icon")?.attr("class").includes("plus-circle") ? "remove" : "add") === "remove";
			const index = item.data("index");
			let descriptors = that.groupable.descriptors();
			if (!descriptors.length) descriptors = that.dataSource.group();
			const column = leafColumns(that.columns)[index];
			if (shouldAdd) descriptors.push({
				field: column.field,
				dir: "asc",
				aggregates: that.groupable.aggregates() || [],
				colID: column.uid,
				compare: column.sortable && column.sortable.compare || that.groupable.sort && that.groupable.sort.compare
			});
			else descriptors = descriptors.filter((descriptor) => descriptor.field !== column.field);
			that.dataSource.group(descriptors);
			that.groupable.trigger(CHANGE, { groups: descriptors });
		},
		_sortToolbarTool: function(cell) {
			const that = this;
			const options = that.options;
			const selectorClass = "k-columnmenu-indicators";
			const isAdaptive = that._isAdaptive();
			const clearSortButton = (cell) => {
				const clearSortSelector = that._isAdaptive() ? "[ref-actionsheet-action-button]:not('.k-button-primary')" : "[ref='clear-sort']";
				return that._toolPopup(cell)?.find(clearSortSelector);
			};
			let menu;
			let menuOptions;
			const toolbarSortOptions = that._toolbarOptionsForTool("sort");
			menu = cell.data("kendoColumnMenu");
			if (menu) {
				menu.wrapper.off("click.kendoGrid");
				menu.element.off("click.kendoGrid");
				menu.destroy();
			}
			function content({ columns, isAdaptive }) {
				let content = `<div class="k-columnmenu-item-wrapper">`;
				columns.forEach((column) => {
					if (column.sortable === false || column.command || column._originalObject.draggable || column._originalObject.selectable) return;
					content += `<div class="k-columnmenu-item" tabindex="0" data-field="${column.field}">${column.title || column.field || ""}</div>`;
				});
				content += "</div>";
				if ((!toolbarSortOptions || toolbarSortOptions.clearButton) && !isAdaptive) {
					content += `<div class="k-actions k-actions-stretched k-actions-horizontal k-column-menu-footer">`;
					content += kendo.html.renderButton(`<button ref="clear-sort">${defaultActionSheetFooterButtons(that.options.messages).sort[0].text}</button>`, { icon: "x" });
					content += "</div>";
				}
				return content;
			}
			const sortable = options.columnMenu.sortable !== false && options.sortable !== false ? extend({}, options.sortable, { allowUnsort: false }) : false;
			if (!sortable) return;
			menuOptions = {
				dataSource: that.dataSource,
				columns: false,
				sortable,
				filterable: false,
				hideAutoSizeColumn: true,
				owner: that,
				adaptiveMode: that.options.adaptiveMode,
				encodeTitles: that.options.encodeTitles,
				componentType: "modern",
				_actionsheet: {
					actionButtons: defaultActionSheetFooterButtons(that.options.messages).sort,
					title: "Sort by",
					ref: "sort-view",
					closeButton: {
						icon: "check",
						themeColor: "primary"
					}
				},
				init: function(e) {
					cell.wrapper.attr("ref", "sort-tool");
				},
				closeCallback: function(e) {
					e.removeClass(SELECTED);
					const popupElement = that._toolPopup(cell);
					if (popupElement.length) popupElement.find(".k-focus").removeClass("k-focus");
					cell.element.focus();
				},
				sort: function(e) {
					const preventClose = e.preventClose;
					cell._preventClose = that.options.sortable.mode === "multiple" || preventClose;
					if (that.trigger("sort", { sort: e.sort })) e.preventDefault();
					else {
						that._clearEditableState();
						if (that.dataSource.options.endless) that._resetEndless();
					}
				},
				contentTemplate: content
			};
			cell = cell.kendoColumnMenu(menuOptions).data("kendoColumnMenu");
			const sortToolHandler = function(e) {
				const cell = e.sender.wrapper && e.sender.wrapper.find(".k-grid-sort-tool")?.data("kendoColumnMenu");
				const sortFields = e.sender.dataSource._sortFields;
				const isArray = Array.isArray(e.sort);
				const field = e.sort.field;
				const isMultiSortingEnabled = e.sender.options.sortable && (e.sender.options.sortable.mode === "multiple" || e.sender.options.sortable.mode === "mixed");
				let condition;
				let isUnsortEvent = e.sort.dir === undefined;
				if (isArray) condition = e.sort.length;
				else if (sortFields && Object.keys(sortFields).length) condition = !isUnsortEvent || isUnsortEvent && Object.keys(sortFields).length - 1 > 0;
				else condition = !isUnsortEvent;
				that._toggleBadge(cell.element, condition);
				const element = cell.wrapper.find(`.k-columnmenu-item[data-field='${field}']`);
				let indicatorsWrapper = element.find(".k-columnmenu-indicators");
				if (isUnsortEvent) indicatorsWrapper.remove();
				else {
					if (!isMultiSortingEnabled) indicatorsWrapper = cell.wrapper.find(".k-columnmenu-indicators");
					if (indicatorsWrapper.length) indicatorsWrapper.remove();
					element.append(that._renderIndicator(`sort-${e.sort.dir}-small`));
				}
				clearSortButton(cell)?.toggleClass("k-disabled", !condition);
			};
			that.unbind("sort", sortToolHandler);
			that.bind("sort", sortToolHandler);
			cell.bind("open", function() {
				const popupElement = that._toolPopup(cell);
				if (popupElement.length) {
					popupElement.focus();
					const clearSortBtn = clearSortButton(cell);
					const items = popupElement.find(".k-columnmenu-item");
					if (clearSortBtn.length) {
						clearSortBtn.off("mousedown.kendoGrid");
						clearSortBtn.on("mousedown.kendoGrid", function() {
							that.dataSource.sort([]);
							that._toggleBadge(cell.element, false);
							clearSortButton(cell)?.toggleClass("k-disabled", true);
							cell.wrapper.find(".k-columnmenu-indicators").remove();
							cell.close();
						});
					}
					if (items.length) {
						popupElement.off("click.kendoGrid");
						popupElement.on("click.kendoGrid", ".k-columnmenu-item", function(e) {
							const currentTarget = $(e.currentTarget);
							const field = currentTarget.data("field");
							const unsort = that._toggleColumnMenuSortIndicator(currentTarget);
							const isMixed = that.options.sortable && that.options.sortable.mode === "mixed";
							const hasCtrlKey = e.originalEvent.ctrlKey;
							cell.field = field;
							cell._sortHandler({
								item: currentTarget,
								allowUnsort: unsort,
								allowSelectedState: false,
								isMixed,
								hasCtrlKey
							});
							that._toggleSortIndexes(that.dataSource._sortFields && Object.keys(that.dataSource._sortFields).length > 1, cell, selectorClass);
						});
					}
				}
			});
			cell.element.on("click.kendoGrid", function(e) {
				if (cell.popup && cell.popup._closing) return;
				$(e.currentTarget).addClass(SELECTED);
				const sortFields = cell.dataSource._sortFields;
				const condition = sortFields && Object.keys(sortFields).length;
				const shouldRenderIndexes = Object.keys(sortFields).length > 1;
				if (condition) Object.keys(sortFields).forEach((sortField) => {
					const element = cell.wrapper.find(`.k-columnmenu-item[data-field='${sortField}']`);
					const indicator = element.find(".k-columnmenu-indicators");
					if (indicator.length) indicator.remove();
					element.append(that._renderIndicator(`sort-${sortFields[sortField].dir}-small`));
				});
				that._toggleSortIndexes(shouldRenderIndexes, cell, selectorClass, isAdaptive);
				clearSortButton(cell)?.toggleClass("k-disabled", !condition);
			});
		},
		_toggleSortIndexes: function(condition, cell, selector) {
			const that = this;
			if (!(that.options.sortable && that.options.sortable.showIndexes)) return;
			const indicators = cell.wrapper.find(`.${selector}`);
			let sortFields = cell.dataSource && cell.dataSource._sortFields;
			if (!indicators.length) return;
			if (condition) indicators.each((index, indicator) => {
				const field = $(indicator).closest(".k-columnmenu-item").data("field");
				const sortField = sortFields[field];
				const sortIndex = $(indicator).parent().find(".k-sort-index");
				if (!sortIndex.length) if (sortField.index) $(indicator).append(`<span class='k-sort-index'>${sortField.index}</span>`);
				else $(indicator).append(`<span class='k-sort-index'>${index + 1}</span>`);
				else sortIndex.text(sortField.index);
			});
			else cell.wrapper.find(".k-sort-index").remove();
		},
		_columnChooserTool: function(cell) {
			const that = this;
			const templateRef = "column-chooser";
			function content(config) {
				const template = config._defaultContents()[templateRef];
				return template(config);
			}
			const menu = cell.data("kendoColumnMenu");
			if (menu) {
				menu.wrapper.off("click.kendoGrid");
				menu.element.off("click.kendoGrid");
				menu.destroy();
			}
			const menuOptions = {
				dataSource: that.dataSource,
				columns: true,
				sortable: false,
				filterable: false,
				hideAutoSizeColumn: true,
				owner: that,
				adaptiveMode: that.options.adaptiveMode,
				encodeTitles: that.options.encodeTitles,
				messages: {
					reset: that.options.messages.clearButtons ? that.options.messages.clearButtons.columnChooserReset : "Reset",
					apply: that.options.messages.applyButtons ? that.options.messages.applyButtons.columnChooserApply : "Apply"
				},
				componentType: "modern",
				contentTemplate: content,
				_actionsheet: {
					actionButtons: defaultActionSheetFooterButtons(that.options.messages)[templateRef],
					title: "Column visibility",
					subtitle: "Selected fields are visible",
					closeButton: true,
					ref: `${templateRef}-view`
				},
				init: function(e) {
					cell.wrapper.attr("ref", "column-chooser-tool");
					if (cell._showAdaptiveView) cell.popup._content.find(`[ref='${templateRef}']`).unwrap();
					else cell.wrapper.removeClass("k-column-menu");
				},
				closeCallback: function(e) {
					e.removeClass(SELECTED);
					const popupElement = that._toolPopup(cell);
					if (popupElement.length) popupElement.find(".k-focus").removeClass("k-focus");
					if (cell && cell._showAdaptiveView) cell._applyColumnVisibility();
					cell.element.focus();
				}
			};
			cell = cell.kendoColumnMenu(menuOptions).data("kendoColumnMenu");
			cell.element.bind("click", function(e) {
				if (cell.popup && cell.popup._closing) return;
				$(e.currentTarget).addClass(SELECTED);
			});
			cell.bind("open", function() {
				const popupElement = that._toolPopup(cell);
				if (popupElement.length) popupElement.find(".k-checkbox")?.first()?.focus();
			});
		},
		_toggleBadge: function(cell, condition) {
			const badgeContainer = cell.closest(`.k-badge-container`);
			const badge = badgeContainer.length && badgeContainer.find(".k-badge");
			const overflowProp = cell.data("overflow");
			if (!badge.length && condition) cell.wrap(`<div class='k-badge-container' ${overflowProp ? "data-overflow=" + overflowProp : ""}></div>`).parent().append($("<span></span>").kendoBadge({
				round: "full",
				position: "edge",
				align: "top end",
				themeColor: "primary",
				cutoutBorder: true
			}));
			else if (!condition && badge.length) {
				badge.remove();
				cell.unwrap(".k-badge-container");
			}
		},
		_toggleColumnMenuFilterIndicator: function(element, condition) {
			const that = this;
			const indicatorWrapper = element.find(".k-columnmenu-indicators");
			const spacer = element.find(".k-spacer");
			if (condition && spacer.length) {
				if (indicatorWrapper && indicatorWrapper.length) return;
				spacer.before(that._renderIndicator("filter"));
			} else if (indicatorWrapper && indicatorWrapper.length) indicatorWrapper.remove();
		},
		_toggleColumnMenuSortIndicator: function(element) {
			const that = this;
			const indicatorWrapper = element.find(".k-columnmenu-indicators");
			const sortIndex = element.find(".k-sort-index");
			let unsort;
			let action = !sortIndex.length ? "append" : "before";
			if (indicatorWrapper && indicatorWrapper.length) {
				const indicator = indicatorWrapper.find(".k-icon");
				if (indicator.length && indicatorWrapper.attr("data-sort-dir") === "desc") {
					indicatorWrapper.remove();
					unsort = true;
				} else {
					indicator.remove();
					indicatorWrapper[action](that._renderIndicator("sort-desc-small"));
					unsort = false;
				}
			} else {
				element[action](that._renderIndicator("sort-asc-small"));
				unsort = false;
			}
			return unsort;
		},
		_renderIndicator: function(icon) {
			let sortDir = "";
			if (icon === "sort-asc-small") sortDir = "asc";
			else if (icon === "sort-desc-small") sortDir = "desc";
			const indicator = $("<span></span>");
			if (sortDir) indicator.attr("ref", `sort-${sortDir}`);
			return $("<span class='k-columnmenu-indicators'></span>").attr("data-sort-dir", sortDir).append(kendo.ui.icon(indicator, { icon }));
		},
		_globalColumnsMenu: function(cell) {
			var that = this, menu, columns = leafColumns(that.columns), columnMenu = that.options.columnMenu, menuOptions, initCallback = function(e) {
				if (that._isAdaptive()) e.sender.popup.wrapper.find(".k-expanded [ref=columns-visibility]").data("handler").toggle(false);
				that.trigger(COLUMNMENUINIT, {
					field: e.field,
					container: e.container
				});
			}, openCallback = function(e) {
				that.trigger(COLUMNMENUOPEN, {
					field: e.field,
					container: e.container
				});
			}, closeCallback = function() {
				cell.trigger("focus");
			};
			if (columnMenu) {
				if (typeof columnMenu == "boolean") columnMenu = {};
				that._setColumnsMediaVisibility(columns);
				let toggleable = !!(columnMenu.autoSize || columnMenu.clearAllFilters);
				menu = cell.data("kendoColumnMenu");
				if (menu) menu.destroy();
				let columnsExpanderOptions = {
					toggleable,
					expanded: columnMenu.expanded || true,
					animation: false,
					hideExpanderIndicator: !toggleable
				};
				menuOptions = {
					dataSource: that.dataSource,
					columns: columnMenu.columns,
					sortable: false,
					filterable: false,
					clearAllFilters: columnMenu.clearAllFilters,
					messages: columnMenu.messages,
					hideAutoSizeColumn: true,
					owner: that,
					closeCallback,
					init: initCallback,
					open: openCallback,
					pane: that.options.adaptiveMode !== "auto" && that.pane,
					autoSize: columnMenu.autoSize,
					encodeTitles: that.options.encodeTitles,
					componentType: "modern",
					adaptiveMode: "auto",
					columnsExpanderOptions
				};
				cell.kendoColumnMenu(menuOptions);
			}
		},
		_columnMenu: function() {
			var that = this, menu, columns = leafColumns(that.columns), column, options = that.options, columnMenu = options.columnMenu, menuOptions, sortable, filterable, cells, hasMultiColumnHeaders = grep(that.columns, function(item) {
				return item.columns !== undefined;
			}).length > 0, hasLockableColumns = grep(that.columns, function(item) {
				return item.lockable !== false;
			}).length > 0, hasStickableColumns = grep(that.columns, function(item) {
				return item.stickable === true;
			}).length > 0, isMobile = this._isMobile, initCallback = function(e) {
				that.trigger(COLUMNMENUINIT, {
					field: e.field,
					container: e.container
				});
			}, openCallback = function(e) {
				that.trigger(COLUMNMENUOPEN, {
					field: e.field,
					container: e.container
				});
			}, closeCallback = function() {
				focusTable(that.table, true);
			}, stickCallback = function(e) {
				that.trigger(COLUMNSTICK, { column: e.column });
			}, unstickCallback = function(e) {
				that.trigger(COLUMNUNSTICK, { column: e.column });
			}, sortHandler = function(e) {
				if (that.trigger("sort", { sort: e.sort })) e.preventDefault();
				else {
					that._clearEditableState();
					if (that.dataSource.options.endless) that._resetEndless();
				}
			}, filterHandler = function(e) {
				if (that.trigger("filter", {
					filter: e.filter,
					field: e.field
				})) e.preventDefault();
				else {
					that._clearEditableState();
					if (that.dataSource.options.endless) that._resetEndless();
				}
			};
			if (columnMenu && !that._isStackedMode()) {
				if (typeof columnMenu == "boolean") columnMenu = {};
				that._setColumnsMediaVisibility(columns);
				cells = leafDataCells(that.thead);
				for (var idx = 0, length = cells.length; idx < length; idx++) {
					column = columns[idx];
					var cell = cells.eq(idx);
					if (column.columnMenu !== false && !column.command && (column.field || cell.attr("data-" + kendo.ns + "field"))) {
						menu = cell.data("kendoColumnMenu");
						if (menu) menu.destroy();
						sortable = column.sortable !== false && columnMenu.sortable !== false && options.sortable !== false ? extend({}, options.sortable, { compare: (column.sortable || {}).compare }) : false;
						filterable = options.filterable && column.filterable !== false && columnMenu.filterable !== false ? extend(true, { pane: that.pane }, options.filterable, column.filterable) : false;
						if (column.filterable && column.filterable.dataSource) {
							filterable.forceUnique = false;
							filterable.checkSource = column.filterable.dataSource;
						}
						if (filterable) filterable.format = column.format;
						const isAdaptive = that.options.adaptiveMode !== "auto" ? columnMenu.adaptiveMode || that.options.adaptiveMode : that.options.adaptiveMode;
						menuOptions = {
							dataSource: that.dataSource,
							values: column.values,
							columns: columnMenu.columns,
							sortable,
							filterable,
							messages: columnMenu.messages,
							owner: that,
							adaptiveMode: isAdaptive,
							adaptiveTitle: column.title || column.field,
							closeCallback,
							init: initCallback,
							open: openCallback,
							stick: stickCallback,
							unstick: unstickCallback,
							pane: that.options.adaptiveMode !== "auto" && that.pane,
							sort: sortHandler,
							filtering: filterHandler,
							filter: isMobile ? ":not(.k-column-active)" : "",
							autoSize: columnMenu.autoSize,
							hasLockableColumns: lockedColumns(columns).length > 0 && hasLockableColumns && !hasMultiColumnHeaders,
							hasStickableColumns: hasStickableColumns && !hasMultiColumnHeaders,
							encodeTitles: that.options.encodeTitles,
							componentType: columnMenu.componentType,
							appendTo: DOT + classNames.headerCellInner,
							reorderable: options.reorderable === true || options.reorderable && options.reorderable.columns,
							groupable: that.options.groupable && that.options.groupable.enabled !== false && column.groupable !== false
						};
						cell.kendoColumnMenu(menuOptions);
					}
				}
			}
		},
		_headerCells: function() {
			return $(this.thead).find("th").filter(function() {
				var th = $(this);
				return !th.hasClass("k-group-cell") && !th.hasClass("k-hierarchy-cell");
			});
		},
		_hasFilterMenu: function() {
			var filterable = this.options.filterable;
			if (filterable && typeof filterable.mode == STRING && filterable.mode.indexOf("menu") == -1) return false;
			return filterable;
		},
		_filterable: function() {
			var that = this, columns = leafColumns(that.columns), filterMenu, cells, cell, filterInit = function(e) {
				that.trigger(FILTERMENUINIT, {
					field: e.field,
					container: e.container
				});
			}, closeCallback = function() {
				focusTable(that.table, true);
			}, filterHandler = function(e) {
				if (that.trigger("filter", {
					filter: e.filter,
					field: e.field
				})) e.preventDefault();
				else {
					that._clearEditableState();
					if (that.dataSource.options.endless) that._resetEndless();
				}
			}, filterOpen = function(e) {
				that.trigger(FILTERMENUOPEN, {
					field: e.field,
					container: e.container
				});
			}, filterable = that._hasFilterMenu();
			if (filterable && !that.options.columnMenu && !that._isStackedMode()) {
				cells = leafDataCells(that.thead);
				for (var idx = 0, length = cells.length; idx < length; idx++) {
					cell = cells.eq(idx);
					if (columns[idx].filterable !== false && !columns[idx].command && (columns[idx].field || cell.attr("data-" + kendo.ns + "field"))) {
						filterMenu = cell.data("kendoFilterMenu");
						if (filterMenu) filterMenu.destroy();
						filterMenu = cell.data("kendoFilterMultiCheck");
						if (filterMenu) filterMenu.destroy();
						var columnFilterable = columns[idx].filterable;
						var options = extend({}, filterable, columnFilterable, {
							dataSource: that.dataSource,
							values: columns[idx].values,
							format: columns[idx].format,
							closeCallback,
							title: columns[idx].title || columns[idx].field,
							init: filterInit,
							open: filterOpen,
							pane: that.pane,
							change: filterHandler,
							appendTo: DOT + classNames.headerCellInner,
							adaptiveMode: that.options.adaptiveMode
						});
						if (columnFilterable && columnFilterable.messages) options.messages = extend(true, {}, filterable.messages, columnFilterable.messages);
						if (columnFilterable && columnFilterable.dataSource) {
							options.forceUnique = false;
							options.checkSource = columnFilterable.dataSource;
						}
						if (columnFilterable && columnFilterable.multi) cell.kendoFilterMultiCheck(options);
						else cell.kendoFilterMenu(options);
					}
				}
			}
		},
		_filterRow: function() {
			var that = this;
			if (!that._hasFilterRow()) return;
			var settings;
			var columns = leafColumns(that.columns), filterable = that.options.filterable, rowheader = that.thead.find(".k-filter-row"), filterHandler = function(e) {
				if (that.trigger("filter", {
					filter: e.filter,
					field: e.field
				})) e.preventDefault();
				else {
					that._clearEditableState();
					if (that.dataSource.options.endless) that._resetEndless();
				}
			};
			this._updateHeader(that._groups());
			for (var i = 0; i < columns.length; i++) {
				var suggestDataSource, col = columns[i], operators = that.options.filterable.operators, customDataSource = false, td = $("<td class='k-table-td' title='" + that.options.messages.filterCellTitle + "'/>"), field = col.field;
				if (col.hidden) td.hide();
				rowheader.append(td);
				if (field && col.filterable !== false) {
					var cellOptions = col.filterable && col.filterable.cell || {};
					suggestDataSource = that.options.dataSource;
					if (suggestDataSource instanceof DataSource) suggestDataSource = that.options.dataSource.options;
					var messages = extend(true, {}, filterable.messages);
					if (col.filterable) extend(true, messages, col.filterable.messages);
					if (cellOptions.enabled === false) {
						td.html("&nbsp;");
						continue;
					}
					if (cellOptions.dataSource) {
						suggestDataSource = cellOptions.dataSource;
						customDataSource = true;
					}
					if (col.filterable && col.filterable.operators) operators = col.filterable.operators;
					settings = {
						column: col,
						dataSource: that.dataSource,
						suggestDataSource,
						customDataSource,
						field,
						messages,
						size: that.options.size,
						values: col.values,
						template: cellOptions.template,
						delay: cellOptions.delay,
						inputWidth: cellOptions.inputWidth,
						suggestionOperator: cellOptions.suggestionOperator,
						minLength: cellOptions.minLength,
						dataTextField: cellOptions.dataTextField,
						operator: cellOptions.operator,
						operators,
						showOperators: cellOptions.showOperators,
						change: filterHandler,
						adaptiveMode: that.options.adaptiveMode
					};
					$("<span/>").attr(kendo.attr("field"), field).appendTo(td).kendoFilterCell(settings);
				} else td.html("&nbsp;");
				td.data("column", col);
			}
			this._filterFocusable().attr(TABINDEX, -1);
		},
		_sortable: function() {
			var that = this, columns = leafColumns(that.columns), column, sorterInstance, cell, sortable = that.options.sortable, sortHandler = function(e) {
				if (that.trigger("sort", { sort: e.sort })) e.preventDefault();
				else that._clearEditableState();
			};
			if (sortable && !that._isStackedMode()) {
				var cells = leafDataCells(that.thead);
				for (var idx = 0, length = cells.length; idx < length; idx++) {
					column = columns[idx];
					if (column.sortable !== false && !column.command && column.field) {
						cell = cells.eq(idx);
						sorterInstance = cell.data("kendoColumnSorter");
						if (sorterInstance) sorterInstance.destroy();
						cell.attr("data-" + kendo.ns + "field", column.field).kendoColumnSorter(extend({}, sortable, column.sortable, {
							dataSource: that.dataSource,
							aria: true,
							filter: ":not(.k-column-active)",
							change: sortHandler
						}));
					}
				}
				cells = null;
			}
		},
		_columns: function(columns) {
			var that = this, table = that.table, encoded, cols = table.find("col"), lockedCols, headerRows = that.element.find("thead tr"), dataSource = that.options.dataSource, draggableColumns;
			columns = columns.length ? columns : map(table.find("th:not(.k-group-cell):not(.k-hierarchy-cell)"), function(th, idx) {
				th = $(th);
				var sortable = th.attr(kendo.attr("sortable")), filterable = th.attr(kendo.attr("filterable")), type = th.attr(kendo.attr("type")), groupable = th.attr(kendo.attr("groupable")), field = th.attr(kendo.attr("field")), title = th.attr(kendo.attr("title")), columnMenu = th.attr(kendo.attr("column-menu")), menu = th.attr(kendo.attr("menu"));
				if (!field) field = th.text().replace(/\s|[^A-z0-9]/g, "");
				return {
					field,
					type,
					title,
					sortable: sortable !== "false",
					filterable: filterable !== "false",
					groupable: groupable !== "false",
					menu: menu !== "false",
					columnMenu: columnMenu !== "false",
					template: th.attr(kendo.attr("template")),
					width: cols.eq(idx).css(WIDTH)
				};
			});
			encoded = !(that.table.find("tbody tr").length > 0 && (!dataSource || !dataSource.transport));
			if (that.options.scrollable && !that._isStackedMode()) {
				var initialColumns = columns;
				lockedCols = lockedColumns(columns);
				columns = nonLockedColumns(columns);
				if (lockedCols.length > 0 && columns.length === 0) throw new Error("There should be at least one non locked column");
				normalizeHeaderCells(that.element.find("tr:has(th)").first(), initialColumns);
				columns = lockedCols.concat(columns);
			}
			if (headerRows.length && columns.length) that._updateColumnIDs(columns, headerRows.first());
			that.columns = normalizeColumns(columns, encoded);
			if ($.grep(leafColumns(that.columns), function(col) {
				return col.selectable;
			}).length) {
				that._selectedIds = {};
				that._checkBoxSelection = true;
				that.wrapper.on("click.kendoGrid", "tbody > tr input[data-role='checkbox'].k-select-checkbox.k-checkbox", that._checkboxClick.bind(that));
				that.wrapper.on("click.kendoGrid", "thead > tr input[data-role='checkbox'].k-select-checkbox.k-checkbox", that._headerCheckboxClick.bind(that));
			}
			draggableColumns = $.grep(leafColumns(that.columns), function(col) {
				return col.draggable;
			});
			if (draggableColumns.length) {
				that._hasDragHandleColumn = true;
				for (var i = 0; i < draggableColumns.length; i++) draggableColumns[i].headerAttributes = $.extend({ "aria-label": that.options.messages.dragHandleLabel }, draggableColumns[i].headerAttributes);
			}
			if ($.grep(leafColumns(that.columns), function(col) {
				return col.pinnable;
			}).length) that._initPinColumn();
			that._foreignKeyBindings(flatColumns(that.columns));
		},
		_foreignKeyBindings: function(columns) {
			var that = this;
			var length = columns.length;
			var column;
			for (var i = 0; i < length; i++) {
				column = columns[i];
				if (column.dataSource) that._fetchForeignKeyValues(column);
			}
		},
		_fetchForeignKeyValues: function(column) {
			var that = this;
			var promise = $.Deferred();
			that._hasBoundForeignKey = true;
			column.dataSource = DataSource.create(column.dataSource);
			if (!that._foreignKeyPromises) that._foreignKeyPromises = [];
			that._foreignKeyPromises.push(promise);
			column.dataSource.fetch().then(function() {
				column.values = column.dataSource.data().map(function(item) {
					return {
						value: item[column.dataValueField],
						text: item[column.dataTextField]
					};
				});
				promise.resolve();
			});
		},
		_updateColumnIDs: function(columns, tr) {
			if (!columns.length) return;
			var ths = tr.find("th:not(.k-group-cell):not(.k-hierarchy-cell)");
			var id;
			for (var i = 0; i < columns.length; i++) {
				id = ths.eq(i).attr(ID);
				if (id) columns[i].headerAttributes = extend(columns[i].headerAttributes, { id });
			}
			this._updateColumnIDs(childColumns(columns), tr.next());
		},
		_headerCheckboxClick: function(e) {
			var that = this, checkBox = $(e.target), checked = checkBox.prop("checked");
			if (!that._belongsToGrid(checkBox)) return;
			if (that.trigger(CHANGING, {
				target: checkBox,
				originalEvent: e
			})) {
				e.preventDefault();
				return;
			}
			if (checked) that.select(that.items());
			else that.clearSelection();
			that._calculateAggregatesForSelected();
			that.trigger(CHANGE, { cellAggregates: that._cellAggregates });
		},
		_checkboxClick: function(e) {
			var that = this, row = $(e.target).closest(TR), isSelecting = !row.hasClass(SELECTED);
			if (!that._belongsToGrid(row)) return;
			if (that.trigger(CHANGING, {
				target: row,
				originalEvent: e
			})) {
				e.preventDefault();
				return;
			}
			if (isSelecting) that.select(row);
			else that._deselectCheckRows(row);
			that._calculateAggregatesForSelected();
			that.trigger(CHANGE, { cellAggregates: that._cellAggregates });
		},
		_groups: function() {
			var group = this.dataSource.group();
			return group ? group.length : 0;
		},
		_getStackedLayoutSettings: function() {
			const layoutSetting = this.options.stackedLayoutSettings;
			const stackedColsConfig = layoutSetting && layoutSetting.cols;
			let config = [];
			let colClass = "";
			if (stackedColsConfig) {
				if (Array.isArray(stackedColsConfig)) {
					for (let i = 0; i < stackedColsConfig.length; i++) if (typeof stackedColsConfig[i] === "object") {
						const width = stackedColsConfig[i].width;
						if (!width && width !== 0) continue;
						if (typeof width === "number") config.push(`${width}px`);
						if (typeof width === "string") config.push(width);
					} else if (typeof stackedColsConfig[i] === "number") config.push(`${stackedColsConfig[i]}px`);
					else if (typeof stackedColsConfig[i] === "string") config.push(stackedColsConfig[i]);
				} else if (typeof stackedColsConfig === "number") colClass = `k-grid-cols-${stackedColsConfig}`;
			}
			return {
				colClass,
				colsConfig: config.join(" ")
			};
		},
		_tmpl: function(rowTemplate, columns, alt, skipGroupCells) {
			var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), idx, length = columns.length, state = {
				storage: {},
				count: 0
			}, column, hasDetails = that._hasDetails(), groups = that._groups();
			var fieldAttr = kendo.attr("field");
			var field;
			var classAttribute;
			var compiledAttributes;
			let rowTemplateFunc;
			if (!rowTemplate) {
				rowTemplateFunc = (data) => {
					var uid = length ? ` ${kendo.attr("uid")}="${kendo.getter("uid")(data)}"` : "";
					var rowTemplateResult = `<tr class="${alt ? "k-table-row k-table-alt-row " : "k-table-row "}k-master-row"${uid}>`;
					if (groups > 0 && !skipGroupCells) rowTemplateResult += groupCells(groups);
					if (hasDetails) rowTemplateResult += "<td class=\"k-hierarchy-cell k-table-td\" aria-expanded=\"false\">" + kendo.ui.icon($(`<a ref-grid-expand-detail href="#" ${ARIA_LABEL}="${EXPAND}" tabindex="-1"></a>`), { icon: `chevron-${isRtl ? "left" : "right"}` }) + "</td>";
					for (idx = 0; idx < length; idx++) {
						column = columns[idx];
						column.template;
						field = column.field;
						compiledAttributes = {};
						let dirtyCellTemplate;
						if (that._editMode() && field) {
							column.attributes = column.attributes || {};
							if (that.virtualScroll) column.attributes[fieldAttr] = field;
							dirtyCellTemplate = that._dirtyCellTemplate(field)(data);
						}
						if (column.colSpan && column.colSpan > 0 && hasHiddenStyle(column.attributes)) column.attributes = removeHiddenStyle(column.attributes);
						else if (!column.colSpan && column.hidden) column.attributes = addHiddenStyle(column.attributes);
						if (column.command) {
							column.attributes = column.attributes || {};
							classAttribute = column.attributes["class"];
							if (typeof classAttribute !== "undefined") {
								if (classAttribute.indexOf("k-command-cell") < 0) column.attributes["class"] += " k-command-cell ";
							} else column.attributes["class"] = " k-command-cell ";
						}
						if (column.draggable) {
							column.attributes = column.attributes || {};
							if (typeof column.attributes["class"] !== "undefined") {
								if (column.attributes["class"].indexOf("k-drag-cell") < 0) column.attributes["class"] += " k-drag-cell ";
							} else column.attributes["class"] = " k-drag-cell ";
							if (!column.attributes["ref-grid-drag-cell"]) column.attributes["ref-grid-drag-cell"] = true;
							if (typeof column.attributes[ARIA_LABEL] === "undefined") column.attributes[ARIA_LABEL] = that.options.messages.dragHandleLabel;
							if (typeof column.attributes.style !== "undefined") {
								if (column.attributes.style.indexOf("cursor: move;") < 0) column.attributes.style += " cursor: move;";
							} else column.attributes.style = "cursor: move;";
						}
						if (column._attributesFunction) compiledAttributes = column._attributesFunction(data);
						let attributes = extend({}, column.attributes, compiledAttributes);
						if (dirtyCellTemplate) {
							attributes["class"] = attributes["class"] || "";
							attributes["class"] += dirtyCellTemplate;
						}
						let columnAttributes = stringifyAttributes(attributes);
						let colSpanAttributes = "";
						if (column.colSpan) {
							if (column.colSpan > 1) colSpanAttributes += " " + kendo.attr("virtual");
							colSpanAttributes += ` colSpan="${column.colSpan}"`;
						}
						rowTemplateResult += decorateCellWithClass(`<td${columnAttributes}${colSpanAttributes}>`);
						rowTemplateResult += column.selectable ? kendo.template(SELECTCOLUMNTMPL)({ size: kendo.getValidCssClass("k-checkbox-", "size", that.options.size) }) : that._cellTmpl(column, state)(data);
						rowTemplateResult += "</td>";
					}
					rowTemplateResult += "</tr>";
					return rowTemplateResult;
				};
				if (that._isStackedMode()) rowTemplateFunc = (data) => {
					columns = leafColumns(visibleColumns(columns));
					length = columns.length;
					const uid = length ? ` ${kendo.attr("uid")}="${kendo.getter("uid")(data)}"` : "";
					let rowTemplateResult = `<tr class="${alt ? "k-table-row k-table-alt-row " : "k-table-row "}k-master-row"${uid}>`;
					if (groups > 0 && !skipGroupCells) rowTemplateResult += groupCells(groups);
					rowTemplateResult += `<td class="k-table-td">
                            <div class="k-grid-stack-row">
                        `;
					for (idx = 0; idx < length; idx++) {
						column = columns[idx];
						column.template;
						field = column.field;
						compiledAttributes = {};
						const showHideColumnOnGroup = groups > 0 && that.dataSource.group().find((g) => g.field === field) && column.hideOnGroup;
						let dirtyCellTemplate;
						if (showHideColumnOnGroup) continue;
						if (that._editMode() && field) {
							column.attributes = column.attributes || {};
							if (that.virtualScroll) column.attributes[fieldAttr] = field;
							dirtyCellTemplate = that._dirtyCellTemplate(field)(data);
						}
						if (column.colSpan && column.colSpan > 0 && hasHiddenStyle(column.attributes)) column.attributes = removeHiddenStyle(column.attributes);
						else if (!column.colSpan && column.hidden) column.attributes = addHiddenStyle(column.attributes);
						column.attributes["class"] = "k-grid-stack-cell";
						if (column.title) column.attributes[kendo.attr("title")] = column.title;
						if (column.aggregates) column.attributes[kendo.attr("aggregates")] = column.aggregates;
						let shouldRenderStackedContent = !column.selectable;
						let shouldRenderStackedHeader = !column.selectable;
						if (column.command) {
							column.attributes = column.attributes || {};
							classAttribute = column.attributes["class"];
							if (typeof classAttribute !== "undefined") {
								if (classAttribute.indexOf("k-command-cell") < 0) column.attributes["class"] += " k-command-cell";
							} else column.attributes["class"] = "k-command-cell";
							shouldRenderStackedHeader = column.title && column.title !== "&nbsp;";
							shouldRenderStackedContent = true;
						}
						if (column.draggable) {
							shouldRenderStackedHeader = false;
							shouldRenderStackedContent = true;
							column.attributes = column.attributes || {};
							if (typeof column.attributes["class"] !== "undefined") {
								if (column.attributes["class"].indexOf("k-drag-cell") < 0) column.attributes["class"] += " k-drag-cell";
							} else column.attributes["class"] = "k-drag-cell";
							if (!column.attributes["ref-grid-drag-cell"]) column.attributes["ref-grid-drag-cell"] = true;
							if (typeof column.attributes[ARIA_LABEL] === "undefined") column.attributes[ARIA_LABEL] = that.options.messages.dragHandleLabel;
							if (typeof column.attributes.style !== "undefined") {
								if (column.attributes.style.indexOf("cursor: move;") < 0) column.attributes.style += " cursor: move;";
							} else column.attributes.style = "cursor: move;";
						}
						if (column._attributesFunction) compiledAttributes = column._attributesFunction(data);
						let attributes = extend({}, column.attributes, compiledAttributes);
						if (dirtyCellTemplate) {
							attributes["class"] = attributes["class"] || "";
							attributes["class"] += dirtyCellTemplate;
						}
						let columnAttributes = stringifyAttributes(attributes);
						let colSpanAttributes = "";
						if (column.colSpan) {
							if (column.colSpan > 1) colSpanAttributes += " " + kendo.attr("virtual");
							colSpanAttributes += ` colSpan="${column.colSpan}"`;
						}
						const title = column.parentIds ? createMultiHeaderTitle(that, column) : column.title || column.field || "";
						rowTemplateResult += decorateCellWithClass(`<div${columnAttributes}${colSpanAttributes} ${column.field ? `data-field=${column.field}` : ""} data-index=${idx}>`, true);
						if (shouldRenderStackedHeader) rowTemplateResult += `<div class="k-grid-stack-header">${title}</div>`;
						if (shouldRenderStackedContent) rowTemplateResult += `<div class="k-grid-stack-content">`;
						rowTemplateResult += column.selectable ? kendo.template(SELECTCOLUMNTMPL)({ size: kendo.getValidCssClass("k-checkbox-", "size", that.options.size) }) : that._cellTmpl(column, state)(data);
						if (shouldRenderStackedContent) rowTemplateResult += `</div>`;
						rowTemplateResult += "</div>";
					}
					if (hasDetails) rowTemplateResult += `<div class="k-grid-stack-cell">
                                <div class="k-grid-stack-content">
                                    ${kendo.html.renderButton(`<button tabindex='-1' ref="expand-detail-button">${that.options.messages.details.expand}</button>`, {
						icon: "plus",
						fillMode: "flat",
						themeColor: "primary"
					})}
                                </div>
                            </div>`;
					rowTemplateResult += "</div></td></tr>";
					return rowTemplateResult;
				};
			}
			rowTemplate = kendo.template(rowTemplate || rowTemplateFunc, settings);
			if (state.count > 0) return rowTemplate.bind(state.storage);
			return rowTemplate;
		},
		_dirtyCellTemplate: function(field) {
			return (data) => {
				if (field && data && data.dirty && data.dirtyFields) return (field.charAt(0) === "[" ? kendo.getter(field)(data.dirtyFields) : data.dirtyFields[field]) ? " k-dirty-cell" : "";
				return "";
			};
		},
		_headerCellText: function(column) {
			var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), template = column.headerTemplate, type = typeof template, text = column.title && (that.options.encodeTitles ? htmlEncode(column.title) : column.title) || htmlEncode(column.field || "");
			if (type === FUNCTION) text = kendo.template(template, settings)({});
			else if (type === STRING) text = template;
			return text;
		},
		_cellTmpl: function(column, state) {
			var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), template = column.template, field = column.field, idx, length, format = column.format, type = typeof template, columnValues = column.values;
			if (column.command) {
				if (isArray(column.command)) return (data) => {
					let html = "";
					for (idx = 0, length = column.command.length; idx < length; idx++) if (column.command[idx].visible) html += column.command[idx].visible(data) ? that._createButton(column.command[idx]) : "";
					else html += that._createButton(column.command[idx]);
					return html;
				};
				return () => that._createButton(column.command);
			}
			if (column.selectable) return SELECTCOLUMNTMPL;
			if (column.draggable) return DRAGHANDLECOLUMNTMPL;
			if (column.pinnable) {
				const isRowPinnableFn = that._getIsRowPinnable();
				if (isRowPinnableFn) return (data) => {
					if (!isRowPinnableFn({ dataItem: data })) return `<span class="${PINCELLCLASS}"></span>`;
					return PINNABLECOLUMNTMPL();
				};
				return PINNABLECOLUMNTMPL;
			}
			return (data) => {
				let html = that._dirtyIndicatorTemplate(field)(data);
				if (type === FUNCTION) {
					state.storage["tmpl" + state.count] = template;
					html += template(data);
					state.count++;
				} else if (type === STRING) html += kendo.template(template, settings)(data);
				else if (columnValues && columnValues.length && isPlainObject(columnValues[0]) && "value" in columnValues[0] && field) {
					var f = convertToObject(columnValues)[settings.useWithBlock ? kendo.getter(field)(data) : field];
					html += encode(f != null ? f : "");
				} else {
					let fieldValue = "";
					if (field) {
						field = kendo.getter(field)(data);
						fieldValue = field == null ? "" : field;
					}
					if (format) fieldValue = kendo.format(format.replace(formatRegExp, "$1"), fieldValue);
					html += column.encoded ? encode(fieldValue) : fieldValue;
				}
				return html;
			};
		},
		_dirtyIndicatorTemplate: function(field) {
			return (data) => {
				if (field && data && data.dirty && data.dirtyFields) return (field.charAt(0) === "[" ? kendo.getter(field)(data.dirtyFields) : data.dirtyFields[field]) ? "<span class=\"k-dirty\"></span>" : "";
				return "";
			};
		},
		_virtualCols: function(columns) {
			var that = this;
			var visibleColumns = $.grep(columns, function(c) {
				return !c.hidden;
			});
			var widths = $.map(visibleColumns, function(c) {
				return parseInt(c.width, 10);
			});
			var scrollLeft = that.virtualScrollable ? kendo.scrollLeft(that.content.find(">.k-virtual-scrollable-wrap")) : kendo.scrollLeft(that.content);
			var tableWidth = outerWidth(that.content);
			var sumOfWidths = sumWidths(visibleColumns);
			var colsToRender = [];
			var firstColspan = 0;
			var lastColspan = 0;
			var hiddenColumns = 0;
			var idx = 0;
			var widthOfHiddenColumns = 0;
			var considerNext;
			for (idx = 0; idx < visibleColumns.length; idx++) {
				considerNext = idx < widths.length - 1 ? widths[idx + 1] : 0;
				if (widthOfHiddenColumns + widths[idx] + 2 * considerNext < scrollLeft) {
					if (widths[idx]) hiddenColumns++;
					widthOfHiddenColumns += widths[idx];
				} else {
					firstColspan = 1 + hiddenColumns;
					break;
				}
			}
			hiddenColumns = 0;
			widthOfHiddenColumns = 0;
			for (var i = visibleColumns.length - 1; i >= 0; i--) if (widthOfHiddenColumns + 3 * widths[i] < sumOfWidths - tableWidth - scrollLeft) {
				if (widths[i]) hiddenColumns++;
				widthOfHiddenColumns += widths[i];
			} else {
				lastColspan = 1 + hiddenColumns;
				for (var j = idx; j <= i; j++) {
					if (visibleColumns[j].locked) continue;
					colsToRender.push(visibleColumns[j]);
					if (visibleColumns[j].colSpan) delete visibleColumns[j].colSpan;
				}
				colsToRender[0].colSpan = firstColspan;
				colsToRender[colsToRender.length - 1].colSpan = lastColspan;
				break;
			}
			that.virtualCols = colsToRender;
			return colsToRender;
		},
		_templates: function() {
			var that = this, options = that.options, dataSource = that.dataSource, groups = dataSource.group(), footer = that.footer || that.wrapper.find(".k-grid-footer"), aggregates = dataSource.aggregate(), stacked = that._isStackedMode(), columnLeafs = leafColumns(that.columns), columnsLocked = leafColumns(lockedColumns(that.columns)), leafsCols = options.scrollable ? leafColumns(nonLockedColumns(that.columns)) : columnLeafs, columns = (that.virtualScroll || {}).columns ? that._virtualCols(leafsCols) : leafsCols, groupHeaderColumnTemplateLockedColumns = grep(visibleColumns(columnsLocked), function(column, index) {
				return column.groupHeaderColumnTemplate && index !== 0;
			}), groupHeaderColumnTemplateNonLockedColumns = grep(visibleColumns(columns), function(column) {
				return column.groupHeaderColumnTemplate;
			});
			if (options.scrollable && columnsLocked.length && !stacked) {
				if (options.rowTemplate || options.altRowTemplate) throw new Error("Having both row template and locked columns is not supported");
				that.rowTemplate = that._tmpl(options.rowTemplate, columns, false, true);
				that.altRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, columns, true, true);
				that.lockedRowTemplate = that._tmpl(options.rowTemplate, columnsLocked);
				that.lockedAltRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, columnsLocked, true);
			} else {
				that.rowTemplate = that._tmpl(options.rowTemplate, stacked ? that.columns : columns);
				that.altRowTemplate = that._tmpl(options.altRowTemplate || options.rowTemplate, stacked ? that.columns : columns, true);
			}
			if (that._hasDetails()) that.detailTemplate = that._detailTmpl(options.detailTemplate || (() => ""));
			if (that._group && !isEmptyObject(aggregates) || !isEmptyObject(aggregates) && !footer.length || grep(columnLeafs, function(column) {
				return column.footerTemplate;
			}).length) that.footerTemplate = that._footerTmpl(columnLeafs, aggregates, "footerTemplate", "k-footer-template k-table-row");
			if (groups && grep(columnLeafs, function(column) {
				return column.groupFooterTemplate;
			}).length) {
				aggregates = $.map(groups, function(g) {
					return g.aggregates;
				});
				that.groupFooterTemplate = that._footerTmpl(columns, aggregates, "groupFooterTemplate", "k-group-footer k-table-row", columnsLocked.length);
				if (options.scrollable && columnsLocked.length) that.lockedGroupFooterTemplate = that._footerTmpl(columnsLocked, aggregates, "groupFooterTemplate", "k-group-footer k-table-row");
			}
			if (groups && (groupHeaderColumnTemplateLockedColumns.length || groupHeaderColumnTemplateNonLockedColumns.length)) {
				aggregates = $.map(groups, function(g) {
					return g.aggregates;
				});
				that.groupHeaderColumnTemplate = that._groupHeaderTmpl(visibleColumns(columns), aggregates, "groupHeaderColumnTemplate", "k-table-group-row k-grouping-row k-table-row", columnsLocked.length, groupHeaderColumnTemplateNonLockedColumns);
				if (options.scrollable && columnsLocked.length) that.lockedGroupHeaderColumnTemplate = that._groupHeaderTmpl(visibleColumns(columnsLocked), aggregates, "groupHeaderColumnTemplate", "k-table-group-row k-grouping-row k-table-row", 0, groupHeaderColumnTemplateLockedColumns);
			} else {
				that.groupHeaderColumnTemplate = null;
				that.lockedGroupHeaderColumnTemplate = null;
			}
			if (that.options.noRecords) that.noRecordsTemplate = that._noRecordsTmpl();
		},
		_noRecordsTmpl: function() {
			var wrapper = "<div class=\"{0}\">{1}</div>";
			var defaultTemplate = "<div class=\"k-grid-norecords-template\"{1}>{0}</div>";
			var scrollableNoGridHeightStyles = this.options.scrollable && !this.wrapper[0].style.height ? ` ${kendo.attr("style-margin")}="0 auto" ${kendo.attr("style-position")}="static"` : "";
			var state = {
				storage: {},
				count: 0
			};
			var settings = $.extend({}, kendo.Template, this.options.templateSettings);
			settings.paramName;
			var template;
			var type;
			var tmpl;
			let resultTemplate;
			if (this.options.noRecords.template) template = this.options.noRecords.template;
			else template = kendo.format(defaultTemplate, this.options.messages.noRecords, scrollableNoGridHeightStyles);
			type = typeof template;
			if (type === "function") {
				let currentCustomTemplate = state.storage["tmpl" + state.count] = template;
				state.count++;
				resultTemplate = (data) => kendo.format(wrapper, NORECORDSCLASS, currentCustomTemplate(data));
			} else if (type === "string") resultTemplate = this.options.noRecords.template ? kendo.format(wrapper, NORECORDSCLASS, template) : () => kendo.format(wrapper, NORECORDSCLASS, template);
			tmpl = kendo.template(resultTemplate, settings);
			if (state.count > 0) tmpl = tmpl.bind(state.storage);
			return tmpl;
		},
		_footerTmpl: function(columns, aggregates, templateName, rowClass, skipGroupCells) {
			var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), paramName = settings.paramName, idx, length, template, type, storage = {}, count = 0, scope = {}, groups = that._groups(), fieldsMap = that.dataSource._emptyAggregates(aggregates), column;
			const stacked = that._isStackedMode();
			let footerTemplateFunction = (data) => {
				let html = "<tr class=\"" + rowClass + "\">";
				if (groups > 0 && !skipGroupCells) html += groupCells(groups);
				if (that._hasDetails() && !stacked) html += "<td class=\"k-hierarchy-cell k-table-td\">&nbsp;</td>";
				const columnsToCheck = columns;
				if (stacked) {
					html += decorateCellWithClass("<td>");
					html += `<div class="${STACKED_TEMPLATE_WRAPPER_CLASS}">`;
					for (idx = 0, length = columnsToCheck.length; idx < length; idx++) {
						column = columnsToCheck[idx];
						template = column[templateName];
						type = typeof template;
						if (template) {
							if (type !== FUNCTION) {
								scope = fieldsMap[column.field] ? extend({}, settings, { paramName: paramName + "['" + column.field + "']" }) : {};
								template = kendo.template(template, scope);
							}
							storage["tmpl" + count] = template;
							html += template(data);
							count++;
						}
					}
					html += "</div></td>";
				} else for (idx = 0, length = columnsToCheck.length; idx < length; idx++) {
					column = columnsToCheck[idx];
					template = column[templateName];
					type = typeof template;
					html += decorateCellWithClass("<td" + stringifyAttributes(column.footerAttributes) + ">");
					if (template) {
						if (type !== FUNCTION) {
							scope = fieldsMap[column.field] ? extend({}, settings, { paramName: paramName + "['" + column.field + "']" }) : {};
							template = kendo.template(template, scope);
						}
						storage["tmpl" + count] = template;
						html += template(data);
						count++;
					} else html += "&nbsp;";
					html += "</td>";
				}
				html += "</tr>";
				return html;
			};
			let resultTemplate = kendo.template(footerTemplateFunction, settings);
			if (count > 0) return resultTemplate.bind(storage);
			return resultTemplate;
		},
		_groupHeaderTmpl: function(columns, aggregates, templateName, rowClass, skipGroupCells, groupHeaderColumnTemplateColumns) {
			var that = this, settings = extend({}, kendo.Template, that.options.templateSettings), paramName = settings.paramName, html = "", idx, length, template, type, storage = {}, count = 0, scope = {}, fieldsMap = that.dataSource._emptyAggregates(aggregates), column, headerTemplateIndex = groupHeaderColumnTemplateColumns.length ? inArray(groupHeaderColumnTemplateColumns[0], columns) : -1, groupHeaderColumnTemplateClass;
			if (headerTemplateIndex < 0) return;
			const stacked = that._isStackedMode();
			var groupHeaderTemplFunc = (data) => {
				let aggregatesHTML = "";
				var resultHtml = "<tr data-group-uid=\"" + data.uid + "\" class=\"" + rowClass + "\">";
				if (!skipGroupCells) for (var i = 0; i < data.groupCells; i++) resultHtml += "<td class=\"k-table-td k-group-cell\">&nbsp;</td>";
				const columnsToCheck = columns;
				if (stacked) {
					aggregatesHTML += `<div class="${STACKED_TEMPLATE_WRAPPER_CLASS}">`;
					for (idx = headerTemplateIndex, length = columnsToCheck.length; idx < length; idx++) {
						column = columnsToCheck[idx];
						template = column[templateName];
						type = typeof template;
						if (template) {
							if (type !== FUNCTION) {
								scope = fieldsMap[column.field] ? extend({}, settings, { paramName: paramName + "['" + column.field + "']" }) : {};
								template = kendo.template(template, scope);
							}
							storage["tmpl" + count] = template;
							aggregatesHTML += storage["tmpl" + count](data);
							count++;
						}
					}
					aggregatesHTML += "</div>";
				}
				if (that._hasDetails() && !stacked) resultHtml += "<td class=\"k-table-td k-hierarchy-cell\">&nbsp;</td>";
				if (headerTemplateIndex < MINCOLSPANVALUE && groupHeaderColumnTemplateColumns.length <= 1 && !skipGroupCells) {
					resultHtml += !skipGroupCells ? groupCellBuilder(columnsToCheck.length, stacked, aggregatesHTML)(data) : "";
					return resultHtml;
				}
				if (headerTemplateIndex < MINCOLSPANVALUE) {
					headerTemplateIndex = !skipGroupCells ? 1 : 0;
					resultHtml += !skipGroupCells ? groupCellBuilder(headerTemplateIndex, stacked, aggregatesHTML)(data) : "";
				} else resultHtml += !skipGroupCells ? groupCellBuilder(headerTemplateIndex, stacked, aggregatesHTML)(data) : groupCellLockedContentBuilder(headerTemplateIndex);
				if (!stacked) for (idx = headerTemplateIndex, length = columnsToCheck.length; idx < length; idx++) {
					column = columnsToCheck[idx];
					template = column[templateName];
					type = typeof template;
					if (column.sticky) {
						let stickyAttributes = "";
						groupHeaderColumnTemplateClass = column.groupHeaderColumnTemplateClass || "";
						if (!groupHeaderColumnTemplateClass) groupHeaderColumnTemplateClass = column.groupHeaderColumnTemplateClass = "group-header-column-template-" + kendo.guid();
						if (isPlainObject(column.stickyStyle)) stickyAttributes = `${column.stickyStyle.left ? `${kendo.attr("style-left")}="${column.stickyStyle.left}"` : ""} ${column.stickyStyle.right ? `${kendo.attr("style-right")}="${column.stickyStyle.right}"` : ""}`;
						resultHtml += `<td class="k-table-td ${STICKY_CELL_CLASS} ${groupHeaderColumnTemplateClass}" ${stickyAttributes}>`;
					} else resultHtml += "<td class='k-table-td'>";
					if (template) {
						if (type !== FUNCTION) {
							scope = fieldsMap[column.field] ? extend({}, settings, { paramName: paramName + "['" + column.field + "']" }) : {};
							template = kendo.template(template, scope);
						}
						storage["tmpl" + count] = template;
						resultHtml += storage["tmpl" + count](data);
						count++;
					} else resultHtml += "&nbsp;";
					resultHtml += "</td>";
				}
				resultHtml += "</tr>";
				return resultHtml;
			};
			html = kendo.template(groupHeaderTemplFunc, settings);
			if (count > 0) return html.bind(storage);
			return html;
		},
		_detailTmpl: function(template) {
			var that = this, settings = extend({}, kendo.Template, that.options.templateSettings);
			settings.paramName;
			var templateFunctionStorage = {}, templateFunctionCount = 0, groups = that._groups(), stacked = that._isStackedMode(), colspan = stacked ? that.table.find("col").length : visibleColumns(leafColumns(that.columns)).length, type = typeof template;
			let detailTemplateFunction = (data) => {
				let html = "<tr role=\"row\" class=\"k-detail-row k-table-row\">";
				if (groups > 0) html += groupCells(groups);
				if (!stacked) html += `<td role="gridcell" class="k-hierarchy-cell k-table-td"></td>`;
				html += `<td role="gridcell" class="k-detail-cell k-table-td" ${colspan ? ` colspan="${colspan}"` : ""}>`;
				if (type === FUNCTION) {
					templateFunctionStorage["tmpl" + templateFunctionCount] = template;
					html += template(data);
					templateFunctionCount++;
				} else html += kendo.template(template, settings)(data);
				html += "</td></tr>";
				return html;
			};
			let resultTemplate = kendo.template(detailTemplateFunction, settings);
			if (templateFunctionCount > 0) return resultTemplate.bind(templateFunctionStorage);
			return resultTemplate;
		},
		_hasDetails: function() {
			var that = this;
			return that.options.detailTemplate !== null || (that._events[DETAILINIT] || []).length;
		},
		_hasFilterRow: function() {
			var filterable = this.options.filterable;
			var hasFiltering = filterable && typeof filterable.mode == STRING && filterable.mode.indexOf(ROW) != -1;
			var columns = this.columns;
			var columnsWithoutFiltering = $.grep(columns, function(col) {
				return col.filterable === false;
			});
			if (this._isStackedMode() || columns.length && columnsWithoutFiltering.length == columns.length) hasFiltering = false;
			return hasFiltering;
		},
		_details: function() {
			var that = this;
			if (that.options.scrollable && that._hasDetails() && lockedColumns(that.columns).length) throw new Error("Having both detail template and locked columns is not supported");
			const stacked = that._isStackedMode();
			const selector = stacked ? `[ref="expand-detail-button"], [ref="collapse-detail-button"]` : ".k-hierarchy-cell " + CARET_ALT_RIGHT + ", .k-hierarchy-cell a[class*='-i-chevron-down']";
			that.table.on("click.kendoGrid", selector, function(e) {
				var button = $(this);
				if (stacked) that._toggleStackedDetails(button);
				else that._toggleDetails(button);
				e.preventDefault();
				return false;
			});
		},
		_setCurrentStackedCell: function(cell) {
			const that = this;
			const currentIndex = cell ? $(cell).closest(TR).index() : that._currentRowIndex;
			const currentStackedCell = that._currentStackedCell();
			if (!currentStackedCell || !that.options.navigatable) return;
			if (currentIndex >= 0) {
				let target;
				let stackedCellIndex = currentStackedCell && currentStackedCell.index;
				target = that.table.find(`${TR}:eq(${currentIndex}) .k-grid-stack-cell`).eq(stackedCellIndex);
				if (target.length) {
					if (target.attr("tabindex") === "-1" || !target.attr("tabindex")) addElementsToTab(target.parent().children());
					that._currentStackedCell(target, true);
				}
			}
		},
		_toggleStackedDetails: function(button, omitAnimation) {
			const that = this;
			const cell = button.closest(".k-grid-stack-cell");
			const content = cell.find(".k-grid-stack-content");
			const expanding = button.is(`[ref="expand-detail-button"]`);
			const masterRow = button.closest("tr.k-master-row");
			const ariaLabelText = expanding ? COLLAPSE : EXPAND;
			button.remove();
			let buttonInstance;
			if (!expanding) buttonInstance = kendo.html.renderButton(`<button tabindex='-1' ref="expand-detail-button" aria-label=${ariaLabelText}>${kendo.htmlEncode(that.options.messages.details.expand)}</button>`, {
				icon: "plus",
				fillMode: "flat",
				themeColor: "primary"
			});
			else buttonInstance = kendo.html.renderButton(`<button tabindex='-1' ref="collapse-detail-button" aria-label=${ariaLabelText}>${kendo.htmlEncode(that.options.messages.details.collapse)}</button>`, {
				icon: `minus`,
				fillMode: "flat",
				themeColor: "primary"
			});
			content.append(buttonInstance);
			that._addDetailRow(masterRow, expanding, omitAnimation);
			that._setCurrentStackedCell(cell);
		},
		_addDetailRow: function(masterRow, expanding, omitAnimation) {
			const that = this;
			const detailTemplate = that.detailTemplate;
			const masterRowIndex = masterRow.attr(ARIA_ROWINDEX);
			const hasDetails = that._hasDetails();
			let detailRow = masterRow.next();
			let data;
			if (detailRow.hasClass("k-hidden")) detailRow.removeClass("k-hidden");
			if (hasDetails && !detailRow.hasClass("k-detail-row")) {
				data = that.dataItem(masterRow);
				detailRow = $(detailTemplate(data)).addClass(masterRow.hasClass("k-table-alt-row") ? "k-table-alt-row" : "").insertAfter(masterRow);
				if (masterRowIndex || masterRowIndex === 0) detailRow.attr(ARIA_ROWINDEX, Number(masterRowIndex) + 1);
				that.trigger(DETAILINIT, {
					masterRow,
					detailRow,
					data,
					detailCell: detailRow.find(".k-detail-cell")
				});
			}
			that.trigger(expanding ? DETAILEXPAND : DETAILCOLLAPSE, {
				masterRow,
				detailRow
			});
			if (omitAnimation) toggleRow(detailRow, expanding);
			else detailRow.toggle(expanding);
		},
		_toggleDetails: function(button, omitAnimation) {
			var that = this, cell = button.closest("td.k-hierarchy-cell"), expanding = button.is(CARET_ALT_RIGHT), masterRow = button.closest("tr.k-master-row"), ariaLabelText = expanding ? COLLAPSE : EXPAND, ariaExpandText = expanding ? "true" : "false";
			if (!expanding) {
				kendo.ui.icon(button, { icon: `chevron-${isRtl ? "left" : "right"}` });
				button.removeAttr("ref-grid-collapse-detail").attr("ref-grid-expand-detail", true);
			} else {
				kendo.ui.icon(button, { icon: "chevron-down" });
				button.removeAttr("ref-grid-expand-detail").attr("ref-grid-collapse-detail", true);
			}
			button.attr(ARIA_LABEL, ariaLabelText);
			cell.attr(ARIA_EXPANDED, ariaExpandText);
			that._addDetailRow(masterRow, expanding, omitAnimation);
		},
		dataItem: function(tr) {
			tr = $(tr)[0];
			if (!tr) return null;
			if ($(tr).closest(".k-grid-pinned-container").length) {
				const uid = $(tr).attr(kendo.attr("uid"));
				if (uid) return this.dataSource.getByUid(uid);
				return null;
			}
			const rows = this.tbody.children(), classesRegEx = /k-grouping-row|k-detail-row|k-group-footer/, idx = tr.sectionRowIndex;
			let j, correctIdx;
			correctIdx = idx;
			for (j = 0; j < idx; j++) if (classesRegEx.test(rows[j].className)) correctIdx--;
			return this._data[correctIdx];
		},
		expandRow: function(tr, omitAnimation) {
			var button = $(tr).find("> td " + CARET_ALT_RIGHT);
			if (button.length) this._toggleDetails(button, omitAnimation);
		},
		collapseRow: function(tr, omitAnimation) {
			var button = $(tr).find("> td a[class*='-i-chevron-down']");
			if (button.length) this._toggleDetails(button, omitAnimation);
		},
		_createHeaderCells: function(columns, rowSpan) {
			var that = this, idx, th, text, html = "", length, title, columnMenu = that.options.columnMenu, sortable = that.options.sortable, filterable = that._hasFilterMenu(), messages = that.options.messages, leafs = leafColumns(that.columns), groups = that.dataSource.group(), field;
			for (idx = 0, length = columns.length; idx < length; idx++) {
				th = columns[idx].column || columns[idx];
				text = that._headerCellText(th);
				title = th.title;
				field = "";
				let index = inArray(th, leafs);
				let currentTh = "";
				if (th.selectable) {
					currentTh += "<th scope='col'" + stringifyAttributes(th.headerAttributes);
					if (rowSpan && !columns[idx].colSpan) currentTh += " rowspan='" + rowSpan + "'";
					if (index > -1) currentTh += kendo.attr("index") + "='" + index + "'";
					text = th.headerTemplate ? text : kendo.template(SELECTCOLUMNHEADERTMPL)({ size: kendo.getValidCssClass("k-checkbox-", "size", that.options.size) });
					currentTh += ">" + text + "</th>";
				} else if (th.draggable) {
					currentTh += "<th class='k-header' ref-grid-drag-cell scope='col'" + stringifyAttributes(th.headerAttributes);
					if (rowSpan && !columns[idx].colSpan) currentTh += " rowspan='" + rowSpan + "'";
					if (index > -1) currentTh += kendo.attr("index") + "='" + index + "'";
					text = th.headerTemplate ? text : "";
					currentTh += ">" + text + "</th>";
				} else if (th.command) {
					currentTh += "<th scope='col'" + stringifyAttributes(th.headerAttributes);
					if (rowSpan && !columns[idx].colSpan) currentTh += " rowspan='" + rowSpan + "'";
					if (index > -1) currentTh += kendo.attr("index") + "='" + index + "'";
					currentTh += ">" + (!text || text === "&nbsp;" ? text : kendo.template(DEFAULTHEADERTEMPLATE)({ text })) + "</th>";
				} else {
					if (th.field) field = kendo.attr("field") + "='" + th.field + "' ";
					currentTh += "<th scope='col' " + field;
					if (columnMenu && th.field && th.menu !== false) currentTh += " aria-haspopup='menu'";
					else if (filterable && th.filterable !== false && !th.command) currentTh += " aria-haspopup='dialog'";
					if (rowSpan && !columns[idx].colSpan) currentTh += " rowspan='" + rowSpan + "'";
					if (columns[idx].colSpan > 1) {
						currentTh += "colspan=\"" + (columns[idx].colSpan - hiddenLeafColumnsCount(th.columns)) + "\" ";
						currentTh += kendo.attr("colspan") + "='" + columns[idx].colSpan + "'";
					} else if (columns[idx].colSpan === 1) currentTh += kendo.attr("colspan") + "='" + columns[idx].colSpan + "'";
					if (title) {
						title = title && (that.options.encodeTitles ? htmlEncode(title, true) : title);
						currentTh += kendo.attr("title") + "=\"" + title + "\" ";
					}
					if (th.groupable !== undefined) currentTh += kendo.attr("groupable") + "='" + th.groupable + "' ";
					if (isColumnGroupable(that, th) && (!th.headerAttributes || !th.headerAttributes.title)) {
						currentTh += "title='";
						currentTh += isGroupedBy(groups, th.field) ? messages.ungroupHeader : messages.groupHeader;
						currentTh += "' ";
					}
					if (th.aggregates && th.aggregates.length) currentTh += kendo.attr("aggregates") + "='" + th.aggregates + "'";
					if (index > -1) currentTh += kendo.attr("index") + "='" + index + "'";
					currentTh += stringifyAttributes(th.headerAttributes);
					text = kendo.template(DEFAULTHEADERTEMPLATE)({ text });
					currentTh += ">" + text + "</th>";
				}
				var thClasses = "k-table-th" + (sortable && th.sortable !== false && !th.command && !th.selectable && !th.draggable && th.field ? " k-sortable" : "");
				if (that.options.resizable) html += $(currentTh).attr("data-resizable", (th.resizable !== false).toString()).addClass(thClasses)[0].outerHTML;
				else html += $(currentTh).addClass(thClasses)[0].outerHTML;
			}
			return html;
		},
		_appendLockedColumnContent: function() {
			var columns = this.columns, idx, colgroup = this.table.find("colgroup"), cols = colgroup.find(COLGROUP), length, lockedCols = $(), skipHiddenCount = 0, container, colSpan, spanIdx, colOffset = 0;
			for (idx = 0, length = columns.length; idx < length; idx++) if (columns[idx].locked) if (isVisible(columns[idx])) {
				colSpan = 1;
				if (columns[idx].columns) colSpan = leafColumns(columns[idx].columns).length - hiddenLeafColumnsCount(columns[idx].columns);
				colSpan = colSpan || 1;
				for (spanIdx = 0; spanIdx < colSpan; spanIdx++) lockedCols = lockedCols.add(cols.eq(idx + colOffset + spanIdx - skipHiddenCount));
				colOffset += colSpan - 1;
			} else skipHiddenCount++;
			container = $("<div class=\"k-grid-content-locked\"><table class=\"k-grid-table k-table\"><colgroup></colgroup><tbody class=\"k-table-tbody\"></tbody></table></div>");
			colgroup.detach();
			container.find("colgroup").append(lockedCols);
			colgroup.insertBefore(this.table.find("tbody"));
			this.lockedContent = container.insertBefore(this.content);
			this.lockedTable = container.children("table");
			this.lockedTable.addClass(kendo.getValidCssClass("k-table-", "size", this.options.size));
		},
		_appendLockedColumnFooter: function() {
			var that = this;
			var footer = that.footer;
			var cells = footer.find(".k-footer-template>td");
			var cols = footer.find(".k-grid-footer-wrap>table>colgroup>col");
			var html = $("<div class=\"k-grid-footer-locked\"><table class=\"k-grid-footer-table k-table\"><colgroup></colgroup><tfoot class=\"k-table-tfoot\"><tr class=\"k-footer-template k-table-row\"></tr></tfoot></table></div>");
			var idx, length;
			var groups = that._groups();
			var lockedCells = $(), lockedCols = $();
			html.find("table").addClass(kendo.getValidCssClass("k-table-", "size", this.options.size));
			lockedCells = lockedCells.add(cells.filter(".k-group-cell"));
			for (idx = 0, length = leafColumns(lockedColumns(that.columns)).length; idx < length; idx++) lockedCells = lockedCells.add(cells.eq(idx + groups));
			lockedCols = lockedCols.add(cols.filter(".k-group-col"));
			for (idx = 0, length = visibleColumns(leafColumns(visibleLockedColumns(that.columns))).length; idx < length; idx++) lockedCols = lockedCols.add(cols.eq(idx + groups));
			lockedCells.appendTo(html.find(TR));
			lockedCols.appendTo(html.find("colgroup"));
			that.lockedFooter = html.prependTo(footer);
		},
		_appendLockedColumnHeader: function(container) {
			var that = this, columns = this.columns, idx, html, length, colgroup, tr, trFilter, table, header, filtercellCells, rows = [], skipHiddenCount = 0, cols = $(), hasFilterRow = that._hasFilterRow(), filterCellOffset = 0, filterCells = $(), cell, leafColumnsCount = 0, cells = $();
			colgroup = that.thead.prev().find(COLGROUP);
			header = that.thead.find(TR).first().find(".k-header:not(.k-group-cell,.k-hierarchy-cell)");
			filtercellCells = that.thead.find(".k-filter-row").find("td:not(.k-group-cell,.k-hierarchy-cell)");
			var colOffset = 0;
			for (idx = 0, length = columns.length; idx < length; idx++) {
				if (columns[idx].locked) {
					cell = header.eq(idx);
					leafColumnsCount = leafColumns(columns[idx].columns || []).length;
					if (isVisible(columns[idx])) {
						var colSpan = null;
						if (columns[idx].columns) colSpan = leafColumnsCount - hiddenLeafColumnsCount(columns[idx].columns);
						colSpan = colSpan || 1;
						for (var spanIdx = 0; spanIdx < colSpan; spanIdx++) cols = cols.add(colgroup.eq(idx + colOffset + spanIdx - skipHiddenCount));
						colOffset += colSpan - 1;
					}
					mapColumnToCellRows([columns[idx]], childColumnsCells(cell), rows, 0, 0);
					leafColumnsCount = leafColumnsCount || 1;
					for (var j = 0; j < leafColumnsCount; j++) filterCells = filterCells.add(filtercellCells.eq(filterCellOffset + j));
					filterCellOffset += leafColumnsCount;
				}
				if (columns[idx].columns) skipHiddenCount += hiddenLeafColumnsCount(columns[idx].columns);
				if (!isVisible(columns[idx])) skipHiddenCount++;
			}
			if (rows.length) {
				html = "<div class=\"k-grid-header-locked\"><table class=\"k-grid-header-table k-table\"><colgroup></colgroup><thead class=\"k-table-thead\">";
				html += new Array(rows.length + 1).join("<tr class='k-table-row'></tr>");
				html += (hasFilterRow ? "<tr class=\"k-filter-row k-table-row\"></tr>" : "") + "</thead></table></div>";
				table = $(html);
				table.find(".k-grid-header-locked").css("width", "1px");
				table.find("table").addClass(kendo.getValidCssClass("k-table-", "size", that.options.size));
				colgroup = table.find("colgroup");
				colgroup.append(that.thead.prev().find("col.k-group-col").add(cols));
				tr = table.find("thead tr:not(.k-filter-row)");
				for (idx = 0, length = rows.length; idx < length; idx++) {
					cells = toJQuery(rows[idx]);
					tr.eq(idx).append(that.thead.find(TR).eq(idx).find(".k-group-cell").add(cells));
				}
				var count = removeEmptyRows(this.thead);
				if (rows.length < count) removeRowSpanValue(table, count - rows.length);
				trFilter = table.find(".k-filter-row");
				trFilter.append(that.thead.find(".k-filter-row .k-group-cell").add(filterCells));
				this.lockedHeader = table.prependTo(container);
				this.thead.find(".k-group-cell").remove();
				return true;
			}
			return false;
		},
		_removeLockedContainers: function() {
			var elements = this.lockedHeader.add(this.lockedContent).add(this.lockedFooter);
			kendo.destroy(elements);
			elements.off(NS).remove();
			this.lockedHeader = this.lockedContent = this.lockedFooter = null;
			this.selectable = null;
		},
		_thead: function() {
			var that = this, columns = that.columns, hasDetails = that._hasDetails() && columns.length, hasFilterRow = that._hasFilterRow(), idx, html = "", thead = that.table.find(">thead"), hasTHead = that.element.find("thead").first().length > 0, headerContent = that.options.messages.expandCollapseColumnHeader, tr;
			const isStacked = that._isStackedMode();
			if (!isStacked) {
				if (!thead.length) thead = $("<thead/>").insertBefore(that.tbody);
				thead.addClass("k-table-thead");
				if (that.lockedHeader && that.thead) {
					tr = that.thead.find("tr:has(th):not(.k-filter-row)").html("");
					tr.remove();
					tr = $();
					that._removeLockedContainers();
				} else if (hasTHead) tr = that.element.find("thead").first().find("tr:has(th):not(.k-filter-row)");
				else tr = that.element.find("tr:has(th)").first();
				if (!tr.length) {
					tr = thead.children().first();
					if (!tr.length) {
						var rows = [{
							rowSpan: 1,
							cells: [],
							index: 0
						}];
						that._prepareColumns(rows, columns);
						for (idx = 0; idx < rows.length; idx++) {
							html += "<tr class='k-table-row'>";
							if (hasDetails) html += "<th class=\"k-hierarchy-cell k-table-th\" scope=\"col\">" + headerContent + "</th>";
							html += that._createHeaderCells(rows[idx].cells, rows[idx].rowSpan);
							html += "</tr>";
						}
						tr = $(html);
						kendo.applyStylesFromKendoAttributes(tr, [
							"display",
							"left",
							"right"
						]);
					}
				} else {
					for (idx = 0; idx < columns.length; idx++) {
						let columnIndex = inArray(columns[idx], leafColumns(columns));
						let cell = leafDataCells(tr.parent()).filter("th:not(.k-group-cell):not(.k-hierarchy-cell)").eq(columnIndex);
						cell.addClass("k-table-th");
						const cellElement = cell[0];
						const cellChildNodes = cellElement?.childNodes || [];
						if (cellChildNodes.length === 1 && cellChildNodes[0].nodeType === 3) cell.html(DEFAULTHEADERTEMPLATE({ text: htmlEncode(cell.text()) }));
						if (columns[idx].hidden && columnIndex >= 0) {
							if (cellElement) cellElement.style.display = NONE;
						}
					}
					that._updateHeadersAttr(childColumns(columns));
				}
				if (hasFilterRow) {
					var filterRow = $("<tr/>");
					filterRow.addClass("k-filter-row k-table-row");
					if (hasDetails || tr.find(".k-hierarchy-cell").length) filterRow.prepend("<td class=\"k-table-td k-hierarchy-cell\">&nbsp;</td>");
					var existingFilterRow = (that.thead || thead).find(".k-filter-row");
					if (existingFilterRow.length) {
						kendo.destroy(existingFilterRow);
						existingFilterRow.remove();
					}
					thead.append(filterRow);
				}
				if (!tr.children().length) {
					html = "";
					if (hasDetails) html += "<th class=\"k-hierarchy-cell k-table-th\" scope=\"col\">&nbsp;</th>";
					html += that._createHeaderCells(columns);
					tr.html(html);
				} else if (hasDetails && !tr.find(".k-hierarchy-cell")[0]) tr.prepend("<th class=\"k-hierarchy-cell k-table-th\" scope=\"col\">" + (headerContent ? headerContent : "&nbsp;") + "</th>");
				const th = tr.find(TH);
				th.addClass(HEADER_CLASS);
				if (th.length > 0) thead.attr(ROLE, ROWGROUP);
				if (!that.options.scrollable) thead.addClass("k-grid-header");
				tr.find("script").remove().end().prependTo(thead);
				if (that.thead) that._destroyColumnAttachments();
				that.thead = thead;
			}
			that._sortable();
			that._filterable();
			that._filterRow();
			that._scrollable();
			that._columnMenu();
			var syncHeight;
			var hasLockedColumns = this.options.scrollable && lockedColumns(this.columns).length;
			if (hasLockedColumns && !isStacked) {
				syncHeight = that._appendLockedColumnHeader(that.thead.closest(".k-grid-header"));
				that._appendLockedColumnContent();
				that.lockedContent.on("DOMMouseScroll.kendoGrid mousewheel.kendoGrid", that._wheelScroll.bind(that));
				if (kendo.support.touch) that._lockedContentUserEvents = new kendo.UserEvents(that.lockedContent, { move: function(e) {
					that.content.scrollTop(that.content.scrollTop() + -e.y.delta);
					e.preventDefault();
				} });
				that._updateLockedCols();
			}
			that._updateCols();
			if (!isStacked) {
				that._updateColumnCellIndex();
				that._updateFirstColumnClass();
			}
			that._resizable();
			that._draggable();
			that._reorderable();
			if (!isStacked) {
				that._updateHeader(that._groups());
				that._updateStickyColumns();
			}
			if (hasLockedColumns && !isStacked) {
				if (syncHeight) that._syncLockedHeaderHeight();
				that._applyLockedContainersWidth();
			}
		},
		_retrieveFirstColumn: function(columns, rows) {
			var result = $();
			if (rows.length && columns[0]) {
				var column = columns[0];
				while (column.columns && column.columns.length) {
					column = column.columns[0];
					rows = rows.filter(":not(:first)");
				}
				result = result.add(rows);
			}
			return result;
		},
		_updateFirstColumnClass: function() {
			var that = this, columns = that.columns || [];
			if (!(that._hasDetails() && columns.length) && !that._groups()) {
				var tr = that.thead.find(">tr:not(.k-filter-row):not(:first)");
				columns = nonLockedColumns(columns);
				var rows = that._retrieveFirstColumn(columns, tr);
				if (that._isLocked()) {
					tr = that.lockedHeader.find("thead>tr:not(.k-filter-row):not(:first)");
					columns = lockedColumns(that.columns);
					rows = rows.add(that._retrieveFirstColumn(columns, tr));
				}
				rows.each(function() {
					var ths = $(this).find("th");
					ths.removeClass("k-first");
					ths.eq(0).addClass("k-first");
				});
			}
		},
		_prepareColumns: function(rows, columns, parentCell, parentRow) {
			var row = parentRow || rows[rows.length - 1];
			var childRow = rows[row.index + 1];
			var totalColSpan = 0;
			for (var idx = 0; idx < columns.length; idx++) {
				var cell = {
					column: columns[idx],
					colSpan: 0
				};
				row.cells.push(cell);
				if (columns[idx].columns && columns[idx].columns.length) {
					if (!childRow) {
						childRow = {
							rowSpan: 0,
							cells: [],
							index: rows.length
						};
						rows.push(childRow);
					}
					cell.colSpan = columns[idx].columns.length;
					this._prepareColumns(rows, columns[idx].columns, cell, childRow);
					totalColSpan += cell.colSpan - 1;
					row.rowSpan = rows.length - row.index;
				}
			}
			if (parentCell) parentCell.colSpan += totalColSpan;
		},
		_wheelScroll: function(e) {
			if (e.ctrlKey) return;
			var content = this.content;
			if (this.virtualScroll && this.virtualScroll.rows) content = this.virtualScrollable.verticalScrollbar;
			var scrollTop = content.scrollTop(), delta = kendo.wheelDeltaY(e);
			if (delta) {
				if (content[0].scrollHeight > content[0].clientHeight && (content[0].scrollTop < content[0].scrollHeight - content[0].clientHeight && delta < 0 || content[0].scrollTop > 0 && delta > 0)) e.preventDefault();
				content.scrollTop(scrollTop + -delta);
			}
		},
		_isLocked: function() {
			return this.lockedHeader != null;
		},
		_updateHeaderCols: function() {
			var table = this.thead.parent().add(this.table);
			if (this._isLocked()) normalizeCols(table, visibleLeafColumns(visibleNonLockedColumns(this.columns)), this._hasDetails(), 0);
			else normalizeCols(table, visibleLeafColumns(visibleColumns(this.columns)), this._hasDetails(), 0);
		},
		_updateColumnSorters: function() {
			var that = this;
			var cells = leafDataCells(that.thead);
			var columns = leafColumns(that.columns);
			var column;
			var cell;
			var sorterInstance;
			if (!that.options.sortable) return;
			for (var idx = 0, length = cells.length; idx < length; idx++) {
				column = columns[idx];
				if (column.sortable !== false && !column.command && column.field) {
					cell = cells.eq(idx);
					sorterInstance = cell.data("kendoColumnSorter");
					if (sorterInstance) sorterInstance.refresh();
				}
			}
		},
		_updateHeadersAttr: function(columns) {
			if (!columns.length) return;
			var that = this;
			for (var i = 0; i < columns.length; i++) if (columns[i].headerAttributes) that.element.find("[id='" + columns[i].headerAttributes.id + "']").attr("headers", columns[i].headerAttributes.headers);
			that._updateHeadersAttr(childColumns(columns));
		},
		_updateCols: function(table) {
			const that = this;
			const defaultTable = that._isStackedMode() ? that.table : that.thead.parent().add(that.table);
			table = table || defaultTable;
			this._appendCols(table, this._isLocked());
		},
		_updateLockedCols: function(table) {
			if (this._isLocked()) {
				table = table || this.lockedHeader.find("table").add(this.lockedTable);
				normalizeCols(table, visibleLeafColumns(visibleLockedColumns(this.columns)), this._hasDetails(), this._groups());
			}
		},
		_appendCols: function(table, locked) {
			if (locked) normalizeCols(table, visibleLeafColumns(visibleNonLockedColumns(this.columns)), this._hasDetails(), 0);
			else normalizeCols(table, visibleLeafColumns(visibleColumns(this.columns)), this._hasDetails(), this._groups(), this._isStackedMode());
		},
		_autoColumns: function(schema) {
			if (schema && schema.toJSON) {
				var that = this, field, encoded;
				schema = schema.toJSON();
				encoded = !(that.table.find("tbody tr").length > 0 && (!that.dataSource || !that.dataSource.transport));
				for (field in schema) that.columns.push({
					field,
					encoded,
					headerAttributes: { id: kendo.guid() }
				});
				that._thead();
				that._templates();
			}
		},
		_setRowCachedHeight: function(row, uid) {
			var cachedHeight = this._cachedRowsHeight[uid], $row;
			if (cachedHeight) {
				$row = $(row);
				$row[0].style.height = cachedHeight + "px";
				row = $row.prop("outerHTML");
			}
			return row;
		},
		_rowsHtml: function(data, templates) {
			var that = this, html = "", idx, rowTemplate = templates.rowTemplate, altRowTemplate = templates.altRowTemplate, cachedHeights = that._cachedRowsHeight, length, row;
			for (idx = 0, length = data.length; idx < length; idx++) {
				if (that._skipRerenderItemsCount > 0) that._skipRerenderItemsCount--;
				else {
					if (idx % 2) row = altRowTemplate(data[idx]);
					else row = rowTemplate(data[idx]);
					if (cachedHeights) row = that._setRowCachedHeight(row, data[idx].uid);
					html += row;
				}
				that._data.push(data[idx]);
			}
			return html;
		},
		_groupData: function(group, skipFooter, firstColumn) {
			var footerDefaults = this._groupAggregatesDefaultObject || {}, groupItems = group.items, aggregates = extend({}, footerDefaults, group.aggregates), headerData = extend({}, {
				field: group.field,
				value: group.value,
				items: groupItems,
				aggregates
			}, group.aggregates[firstColumn ? firstColumn.field : group.field]), footerData = {};
			if (!skipFooter) for (var aggregate in aggregates) footerData[aggregate] = extend({}, aggregates[aggregate], { group: {
				field: group.field,
				value: group.value,
				items: groupItems
			} });
			return extend({}, footerData, headerData);
		},
		_removeGroupIfEmpty: function(row) {
			var that = this, itemsCount, subgroupsCount, length = that.dataSource._group.length;
			for (var i = 0; i < length; i++) {
				row = row.prev();
				itemsCount = +row.attr("data-group-item-count");
				subgroupsCount = +row.attr("data-sub-group-count");
				if (itemsCount == 1 || subgroupsCount == 1) row.hide();
			}
		},
		_groupRowHtml: function(group, colspan, level, groupHeaderBuilder, templates, skipColspan, skipLastGroup, isLockedTable) {
			var that = this, html = "", idx, length, isLocked = that.lockedContent != null, field = group.field, column = grep(leafColumns(that.columns), function(column) {
				return column.field == field;
			})[0] || {}, firstColumn = visibleColumns(that.columns)[0], firstVisibleColumnGroupHeaderTemplate = firstColumn ? firstColumn.groupHeaderColumnTemplate : null, template = column.groupHeaderTemplate ? column.groupHeaderTemplate : firstVisibleColumnGroupHeaderTemplate, text = (column.title && (that.options.encodeTitles ? htmlEncode(column.title, true) : column.title) || htmlEncode(field, true)) + ": " + formatGroupValue(group.value, column.format, column.values, column.encoded), groupItems = group.currentItems || group.items, groups = that._groups(), groupFooterTemplate = templates.groupFooterTemplate, groupHeaderColumnTemplate = templates.groupHeaderColumnTemplate, groupData, isGroupPaged = that.dataSource._isGroupPaged(), expanded = isGroupPaged ? that.dataSource._groupsState[group.uid] : true;
			if (that.options.editable && group.items && group.items[0] && group.items[0].isNew && group.items[0].isNew()) expanded = true;
			if (templates.groupFooterTemplate || templates.groupHeaderColumnTemplate || column.groupHeaderTemplate || that._isStackedMode()) groupData = that._groupData(group, false, !column.groupHeaderTemplate && visibleColumns(that.columns)[0].groupHeaderColumnTemplate ? visibleColumns(that.columns)[0] : false);
			if (template && !skipColspan) text = typeof template === FUNCTION ? template(groupData) : kendo.template(template)(groupData);
			if (!that._skipRerenderItemsCount) if (!group.excludeHeader) html += groupHtmlBuilder({
				groupHeaderColumnTemplate,
				groupHeaderBuilder,
				colspan,
				templateColspan: groups - level,
				groupData,
				level,
				text,
				expanded,
				group,
				isGroupPaged,
				stacked: that._isStackedMode()
			});
			else if (isLocked) group.excludeHeader = isLockedTable ? false : true;
			else group.excludeHeader = false;
			else groupHeaderBuilder(colspan, level, text, expanded, group.uid, isGroupPaged, that._isStackedMode());
			if (expanded) if (group.hasSubgroups) for (idx = 0, length = groupItems.length; idx < length; idx++) html += that._groupRowHtml(groupItems[idx], skipColspan ? colspan : colspan - 1, level + 1, groupHeaderBuilder, templates, skipColspan, skipLastGroup && idx === groupItems.length - 1, isLockedTable);
			else html += that._rowsHtml(groupItems, templates);
			if (groupFooterTemplate) if (skipLastGroup) {
				if (!inArray(group.value, that._skippedGroups)) that._skippedGroups.push(group.value);
			} else {
				if (that._skippedGroups.length && that._skippedGroups[0] === group.value) that._skippedGroups.shift();
				if (!that._skipRerenderItemsCount) html += groupFooterTemplate(groupData);
			}
			return html;
		},
		collapseGroup: function(group) {
			var level, that = this, groupToCollapse = group, footerCount = this.options.groupable.showFooter ? 0 : 1, offset, relatedGroup = $(), idx, length, tr;
			group = $(group);
			level = group.find(".k-group-cell").length;
			if (this.dataSource._isGroupPaged()) {
				var groupUid = group.attr("data-group-uid");
				var groupObject = that.dataSource._getGroupByUid(groupUid);
				var currentGroupCount = that.dataSource._calculateGroupsTotal([groupObject], true);
				var groupCountAfterCollapse;
				that.dataSource._groupsState[groupUid] = false;
				groupCountAfterCollapse = that.dataSource._calculateGroupsTotal([groupObject], true);
				that.dataSource._serverGroupsTotal -= currentGroupCount - groupCountAfterCollapse;
				that._progress(true);
				that.dataSource.range(that.dataSource._currentRangeStart, that.dataSource.take(), function() {
					that._progress(false);
				}, "collapseGroup");
				return;
			}
			if (this._isLocked()) if (!group.closest(DIV).hasClass("k-grid-content-locked")) {
				relatedGroup = group.nextAll(TR);
				group = this.lockedTable.find(">tbody>tr").eq(group.index());
			} else relatedGroup = this.tbody.children(TR).eq(group.index()).nextAll(TR);
			if (group.find(CARET_ALT_DOWN).length) kendo.ui.icon(group.find(CARET_ALT_DOWN), { icon: `chevron-${isRtl ? "left" : "right"}` });
			group.find("td[aria-expanded='true']").first().attr(ARIA_EXPANDED, false).find("a").attr(ARIA_LABEL, EXPAND);
			group = group.nextAll(TR);
			var toHide = [];
			for (idx = 0, length = group.length; idx < length; idx++) {
				tr = group.eq(idx);
				offset = tr.find(".k-group-cell").length;
				if (tr.hasClass(GROUPING_ROW)) footerCount++;
				else if (tr.hasClass("k-group-footer")) footerCount--;
				if (offset <= level || tr.hasClass("k-group-footer") && footerCount < 0) break;
				if (relatedGroup.length) toHide.push(relatedGroup[idx]);
				toHide.push(tr[0]);
			}
			$(toHide).hide();
			if (this.options.scrollable.endless && this.content) {
				clearTimeout(that._collapseGroupsTimeOut);
				that._collapseGroupsTimeOut = setTimeout(function() {
					that.content.scroll();
					that._groupToCollapse = groupToCollapse;
				});
			}
			if (that._hasStickyGroupHeaders() || that._hasStickyGroupFooters()) that._updateStickyGroups();
		},
		expandGroup: function(group) {
			group = $(group);
			var that = this, showFooter = that.options.groupable.showFooter, level, tr, offset, relatedGroup = $(), idx, length, footersVisibility = [], groupsCount = 1;
			level = group.find(".k-group-cell").length;
			if (this.dataSource._isGroupPaged()) {
				var groupUid = group.attr("data-group-uid");
				var groupObject = that.dataSource._getGroupByUid(groupUid);
				var groupCount = that.dataSource._calculateGroupsTotal([groupObject], true);
				var groupCountAfterExpand;
				that.dataSource._groupsState[groupUid] = true;
				if (groupObject.items && groupObject.items.length) {
					groupCountAfterExpand = that.dataSource._calculateGroupsTotal([groupObject], true);
					that.dataSource._serverGroupsTotal += groupCountAfterExpand - groupCount;
				}
				that._progress(true);
				that.dataSource.range(that.dataSource._currentRangeStart, that.dataSource.take(), function() {
					that._progress(false);
				}, "expandGroup");
				return;
			}
			if (this._isLocked()) if (!group.closest(DIV).hasClass("k-grid-content-locked")) {
				relatedGroup = group.nextAll(TR);
				group = this.lockedTable.find(">tbody>tr").eq(group.index());
			} else relatedGroup = this.tbody.children(TR).eq(group.index()).nextAll(TR);
			if (group.find(CARET_ALT_RIGHT).length) kendo.ui.icon(group.find(CARET_ALT_RIGHT), { icon: "chevron-down" });
			group.find("td[aria-expanded='false']").first().attr(ARIA_EXPANDED, true).find("a").attr(ARIA_LABEL, COLLAPSE);
			group = group.nextAll(TR);
			for (idx = 0, length = group.length; idx < length; idx++) {
				tr = group.eq(idx);
				offset = tr.find(".k-group-cell").length;
				if (offset <= level) break;
				if (offset == level + 1 && !tr.hasClass("k-detail-row")) {
					tr.show();
					relatedGroup.eq(idx).show();
					if (tr.hasClass(GROUPING_ROW) && tr.find(".k-icon,.k-svg-icon").is(CARET_ALT_DOWN)) that.expandGroup(tr);
					if (tr.hasClass("k-master-row") && tr.find(".k-icon,.k-svg-icon").is(CARET_ALT_DOWN)) {
						tr.next().show();
						relatedGroup.eq(idx + 1).show();
					}
				}
				if (tr.hasClass(GROUPING_ROW)) {
					if (showFooter) footersVisibility.push(tr.is(":visible"));
					groupsCount++;
				}
				if (tr.hasClass("k-group-footer")) {
					if (showFooter) {
						var toggleVisibility = footersVisibility.pop();
						tr.toggle(toggleVisibility);
						relatedGroup.eq(idx).toggle(toggleVisibility);
					}
					if (groupsCount == 1) {
						tr.show();
						relatedGroup.eq(idx).show();
					} else groupsCount--;
				}
			}
			if (level === 0 && that.options.scrollable.endless && this._isLocked() || !that.options.scrollable.endless && this._isLocked()) that._syncLockedContentHeight();
			if (that._hasStickyGroupHeaders() || that._hasStickyGroupFooters()) that._updateStickyGroups();
		},
		_updateHeader: function(groups) {
			var that = this, container = that._isLocked() ? that.lockedHeader.find("thead") : that.thead, filterCells = container.find("tr.k-filter-row").find("td.k-group-cell").length, length = container.find(TR).first().find("th.k-group-cell").length, rows = container.children("tr:not(:first)").filter(function() {
				return !$(this).children(":visible").length;
			});
			if (groups > length) {
				$(new Array(groups - length + 1).join("<th class=\"k-group-cell k-header k-table-th\" scope=\"col\">" + encode(that.options.messages.expandCollapseColumnHeader) + "</th>")).prependTo(container.children("tr:not(.k-filter-row)"));
				if (that.element.is(":visible")) rows.find("th.k-group-cell").hide();
			} else if (groups < length) container.find(TR).each(function() {
				$(this).find(".k-group-cell").eq(groups).remove();
				$(this).find(".k-group-cell").slice(groups).remove();
			});
			if (groups > filterCells) $(new Array(groups - filterCells + 1).join("<td class=\"k-group-cell k-table-group-td k-table-td\">&nbsp;</td>")).prependTo(container.find(".k-filter-row"));
		},
		_firstDataItem: function(data, grouped) {
			if (data && grouped) if (data.hasSubgroups) data = this._firstDataItem(data.items[0], grouped);
			else data = data.items[0];
			return data;
		},
		_updateTablesWidth: function() {
			var that = this, tables;
			if (!that._isLocked()) return;
			tables = $(">.k-grid-footer>.k-grid-footer-wrap>table", that.wrapper).add(that.thead.parent()).add(that.table);
			that._footerWidth = tableWidth(tables.eq(0));
			tables.width(that._footerWidth);
			tables = $(">.k-grid-footer>.k-grid-footer-locked>table", that.wrapper).add(that.lockedHeader.find(">table")).add(that.lockedTable);
			tables.width(tableWidth(tables.eq(0)));
		},
		hideColumn: function(column) {
			var that = this, cell, tables, idx, cols, colWidth, position, width = 0, headerCellIndex, length, footer = that.footer || that.wrapper.find(".k-grid-footer"), virtualScroll = that.virtualScroll || {}, columns = that.columns, visibleLocked = that.lockedHeader ? leafDataCells(that.lockedHeader.find(">table>thead")).filter(isCellVisible).length : 0, columnIndex, groupHeaderColumnTemplateColumns, columnsToHide;
			if (!Array.isArray(column)) columnsToHide = [column];
			else columnsToHide = column;
			columnsToHide.forEach((column) => {
				groupHeaderColumnTemplateColumns = grep(leafColumns(that.columns), function(column) {
					return column.groupHeaderColumnTemplate;
				});
				if (typeof column == "number") column = columns[column];
				else if (isPlainObject(column)) column = grep(flatColumns(columns), function(item) {
					return item === column;
				})[0];
				else column = grep(flatColumns(columns), function(item) {
					return item.field === column;
				})[0];
				if (!column || !isVisible(column)) return;
				if (that._isStackedMode()) {
					that._stackedColumnVisibility(column, "hide");
					that.trigger(COLUMNHIDE, { column });
					return;
				}
				var setColumnVisibility = that._columnVisibilitySetter(column);
				if (column.columns && column.columns.length) {
					position = columnVisiblePosition(column, columns);
					setColumnVisibility(column, false);
					setCellVisibility(elements($(">table>thead", that.lockedHeader), that.thead, ">tr:eq(" + position.row + ")>th"), position.cell, false);
					for (idx = 0; idx < column.columns.length; idx++) this.hideColumn(column.columns[idx]);
					that._ariaAddHiddenColIndex();
					that.trigger(COLUMNHIDE, { column });
					return;
				}
				columnIndex = inArray(column, visibleColumns(leafColumns(columns)));
				setColumnVisibility(column, false);
				that._setParentsVisibility(column, false);
				that._templates();
				that._updateCols();
				that._updateLockedCols();
				var container = that.thead;
				headerCellIndex = columnIndex;
				if (that.lockedHeader && visibleLocked > columnIndex) container = that.lockedHeader.find(">table>thead");
				else headerCellIndex -= visibleLocked;
				cell = leafDataCells(container).filter(isCellVisible).eq(headerCellIndex);
				cell[0].style.display = NONE;
				setCellVisibility(elements($(">table>thead", that.lockedHeader), that.thead, ">tr.k-filter-row>td"), columnIndex, false);
				if (footer[0]) {
					that._updateCols(footer.find(">.k-grid-footer-wrap>table"));
					that._updateLockedCols(footer.find(">.k-grid-footer-locked>table"));
					setCellVisibility(footer.find(".k-footer-template>td"), columnIndex, false);
				}
				if (virtualScroll.columns && !column.locked) {
					that._updateContentWidth();
					that._renderPinnedRows();
					that.trigger(COLUMNHIDE, { column });
					return;
				}
				if (that.lockedTable && visibleLocked > columnIndex) hideColumnCells(that.lockedTable.find(">tbody>tr"), columnIndex);
				else hideColumnCells(that.tbody.children(), columnIndex - visibleLocked);
				if (that.lockedTable) {
					that._updateTablesWidth();
					that._applyLockedContainersWidth();
					that._syncLockedContentHeight();
					that._syncLockedHeaderHeight();
					that._syncLockedFooterHeight();
				} else {
					cols = that.thead ? that.thead.prev().find("col") : [];
					for (idx = 0, length = cols.length; idx < length; idx += 1) {
						colWidth = cols[idx].style.width;
						if (cols[idx].className.indexOf("k-hierarchy-col") > -1) {
							width += outerWidth(cols[idx]);
							continue;
						}
						if (cols[idx].className.indexOf("k-group-col") > -1) {
							width += outerWidth(cols[idx]);
							continue;
						}
						if (colWidth && colWidth.indexOf("%") == -1) width += parseInt(colWidth, 10);
						else {
							width = 0;
							break;
						}
					}
					tables = that.wrapper.find(">.k-grid-header table").first().add(that.wrapper.find(">.k-grid-footer table").first()).add(that.table);
					that._footerWidth = null;
					if (width) {
						tables.each(function() {
							this.style.width = width + PX;
						});
						that._footerWidth = width;
						that._setContentWidth();
					}
				}
				that._updateFirstColumnClass();
				that._updateStickyColumns();
				that._ariaAddHiddenColIndex();
				if (groupHeaderColumnTemplateColumns.length > 0) that._renderGroupRows();
				that._renderPinnedRows();
				if (that._hasStickyGroupHeaders() || that._hasStickyGroupFooters()) that._syncStickyGroupColgroups();
				that.trigger(COLUMNHIDE, { column });
			});
		},
		_stackedColumnVisibility: function(column, method) {
			const that = this;
			const field = column.field;
			let index;
			if (that.columns.length) {
				for (let i = 0; i < that.columns.length; i++) if (that.columns[i].field === field) {
					index = i;
					break;
				}
				if (index || index === 0) {
					that.table.find(".k-grid-stack-cell[data-index='" + index + "']").each(function() {
						const cell = $(this);
						if (!cell.closest(".k-detail-row").length) cell[method]();
					});
					setColumnVisibility(column, method === "show");
				}
			}
		},
		_setParentsVisibility: function(column, visible) {
			var that = this;
			var columns = that.columns;
			var idx;
			var parents = [];
			var parent;
			var position;
			var cell;
			var colSpan;
			var setColumnVisibility = that._columnVisibilitySetter(column);
			var predicate = visible ? function(p) {
				return visibleColumns(p.columns).length && p.hidden;
			} : function(p) {
				return !visibleColumns(p.columns).length && !p.hidden;
			};
			if (columnParents(column, columns, parents) && parents.length) for (idx = parents.length - 1; idx >= 0; idx--) {
				parent = parents[idx];
				position = columnPosition(parent, columns);
				cell = elements($(">table>thead", this.lockedHeader), this.thead, ">tr:eq(" + position.row + ")>th:not(.k-group-cell):not(.k-hierarchy-cell)").eq(position.cell);
				if (predicate(parent)) {
					setColumnVisibility(parent, visible);
					cell[0].style.display = visible ? "" : NONE;
				}
				if (cell.filter("[" + kendo.attr("colspan") + "]").length) {
					colSpan = parseInt(cell.attr(kendo.attr("colspan")), 10);
					cell[0].colSpan = colSpan - hiddenLeafColumnsCount(parent.columns) || 1;
				}
			}
		},
		_updateContentWidth: function() {
			const that = this;
			that.table.add(that.thead ? that.thead.parent() : $()).css({ width: sumWidths(visibleLeafColumns(visibleNonLockedColumns(that.columns))) });
			that.refresh();
		},
		showColumn: function(column) {
			var that = this, idx, length, cell, tables, width, headerCellIndex, position, colWidth, cols, columns = that.columns, virtualScroll = that.virtualScroll || {}, footer = that.footer || that.wrapper.find(".k-grid-footer"), lockedColumnsCount = that.lockedHeader ? leafDataCells(that.lockedHeader.find(">table>thead")).length : 0, columnIndex, originalColumn, columnLeafIndex, groupHeaderColumnTemplateColumns, columnsToShow;
			if (!Array.isArray(column)) columnsToShow = [column];
			else columnsToShow = column;
			columnsToShow.forEach((column) => {
				groupHeaderColumnTemplateColumns = grep(leafColumns(that.columns), function(column) {
					return column.groupHeaderColumnTemplate;
				});
				if (typeof column == "number") {
					columnIndex = column;
					column = columns[column];
				} else if (isPlainObject(column)) $.each(flatColumns(columns), function(index, item) {
					if (item === column) {
						column = item;
						columnIndex = index;
						return false;
					}
				});
				else $.each(flatColumns(columns), function(index, item) {
					if (item.field === column) {
						column = item;
						columnIndex = index;
						return false;
					}
				});
				if (!column || isVisible(column)) return;
				if (that._isStackedMode()) {
					that._stackedColumnVisibility(column, "show");
					that.trigger(COLUMNSHOW, { column });
					return;
				}
				var setColumnVisibility = that._columnVisibilitySetter(column);
				if (column.columns && column.columns.length) {
					position = columnPosition(column, columns);
					originalColumn = flatColumns(that.options.columns)[columnIndex];
					setColumnVisibility(column, true);
					setCellVisibility(elements($(">table>thead", that.lockedHeader), that.thead, ">tr:eq(" + position.row + ")>th"), position.cell, true);
					for (idx = 0; idx < column.columns.length; idx++) if (!originalColumn.columns[idx].hidden) this.showColumn(column.columns[idx]);
					that._ariaRemoveHiddenColIndex();
					that.trigger(COLUMNSHOW, { column });
					return;
				}
				columnLeafIndex = inArray(column, leafColumns(columns));
				setColumnVisibility(column, true);
				that._setParentsVisibility(column, true);
				that._templates();
				that._updateCols();
				that._updateLockedCols();
				var container = that.thead;
				headerCellIndex = columnLeafIndex;
				if (that.lockedHeader && lockedColumnsCount > columnLeafIndex) container = that.lockedHeader.find(">table>thead");
				else headerCellIndex -= lockedColumnsCount;
				cell = leafDataCells(container).eq(headerCellIndex);
				cell[0].style.display = "";
				cell[0].classList.remove("k-hidden");
				setCellVisibility(elements($(">table>thead", that.lockedHeader), that.thead, ">tr.k-filter-row>td"), columnLeafIndex, true);
				if (footer[0]) {
					that._updateCols(footer.find(">.k-grid-footer-wrap>table"));
					that._updateLockedCols(footer.find(">.k-grid-footer-locked>table"));
					setCellVisibility(footer.find(".k-footer-template>td"), columnLeafIndex, true);
				}
				if (virtualScroll.columns && !column.locked) {
					that._updateContentWidth();
					that._renderPinnedRows();
					that.trigger(COLUMNSHOW, { column });
					return;
				}
				if (that.lockedTable && lockedColumnsCount > columnLeafIndex) showColumnCells(that.lockedTable.find(">tbody>tr"), columnLeafIndex);
				else showColumnCells(that.tbody.children(), columnLeafIndex - lockedColumnsCount);
				if (that.lockedTable) {
					that._updateTablesWidth();
					that._applyLockedContainersWidth();
					that._syncLockedContentHeight();
					that._syncLockedHeaderHeight();
				} else {
					tables = that.wrapper.find(">.k-grid-header table").first().add(that.wrapper.find(">.k-grid-footer table").first()).add(that.table);
					if (!column.width) tables.width("");
					else {
						width = 0;
						cols = that.thead.prev().find("col");
						for (idx = 0, length = cols.length; idx < length; idx += 1) {
							colWidth = cols[idx].style.width;
							if (cols[idx].className.indexOf("k-hierarchy-col") > -1) {
								width += outerWidth(cols[idx]);
								continue;
							}
							if (cols[idx].className.indexOf("k-group-col") > -1) {
								width += outerWidth(cols[idx]);
								continue;
							}
							if (colWidth.indexOf("%") > -1) {
								width = 0;
								break;
							}
							width += parseInt(colWidth, 10);
						}
						that._footerWidth = null;
						if (width) {
							tables.each(function() {
								this.style.width = width + PX;
							});
							that._footerWidth = width;
							that._setContentWidth();
						}
					}
				}
				that._updateFirstColumnClass();
				that._updateStickyColumns();
				if (groupHeaderColumnTemplateColumns.length > 0) that._renderGroupRows();
				that._renderPinnedRows();
				if (that._hasStickyGroupHeaders() || that._hasStickyGroupFooters()) that._syncStickyGroupColgroups();
				that._ariaRemoveHiddenColIndex();
				that.trigger(COLUMNSHOW, { column });
			});
		},
		_columnVisibilitySetter: function(column) {
			if (isUndefined((column || {}).media)) return setColumnVisibility;
			else return setColumnMediaVisibility;
		},
		_buildSkeleton: function() {
			const visibleColumns = this.virtualCols ? this.virtualCols : visibleLeafColumns(this.columns);
			const pageSize = this.dataSource.pageSize() || this.dataSource.total();
			const groups = this._groups();
			const stacked = this._isStackedMode();
			let loaderHTML = "";
			let colspan;
			let columnsCount = stacked ? 1 : visibleColumns.length + groups;
			if (this._hasDetails() && !stacked) columnsCount++;
			if (this._hasVirtualColumns()) colspan = parseInt(this.content.find(TR).first().find("td").first().attr("colspan"), 10);
			for (var i = 0; i < pageSize; i++) {
				loaderHTML += "<tr class='k-table-row' data-skeleton-row>";
				for (var j = 0; j < columnsCount; j++) if (colspan && !j) loaderHTML += "<td colspan='" + colspan + "'><span class='k-skeleton k-skeleton-text k-skeleton-pulse k-table-td'></span></td>";
				else loaderHTML += "<td><span class='k-skeleton k-skeleton-text k-skeleton-pulse k-table-td'></span></td>";
				loaderHTML += "</tr>";
			}
			return loaderHTML;
		},
		_progress: function(toggle) {
			var element = this.element;
			var endless = this.options.scrollable && this.options.scrollable.endless;
			var loaderType = this.options.loaderType;
			var isVirtualization = this.options.scrollable && this.options.scrollable.virtual;
			var skeleton;
			if (isVirtualization) element = this.content;
			else if (this._editContainer && this._editMode() === "popup") element = this._editContainer;
			else if (this.lockedContent || endless) element = this.wrapper;
			else if (this.element.is("table")) element = this.element.parent();
			else if (this.content && this.content.length) element = this.content;
			if (loaderType == "skeleton" && !this._isExport) {
				const tbody = element.find("tbody");
				if (toggle) {
					skeleton = this._buildSkeleton();
					if (endless) this._currentEndlessRows = tbody.children(":not([data-skeleton-row])");
					tbody.empty().append(skeleton);
				} else if (endless) {
					element.find("[data-skeleton-row]").remove();
					tbody.prepend(this._currentEndlessRows);
				} else element.find(".k-skeleton").closest("tbody").empty();
			} else if (this._isExport) this._loaderContainer(toggle, { message: this.options.messages.loader.exporting });
			else if (endless && toggle) kendo.ui.progress(element, toggle, {
				height: this.content.height(),
				top: this.content.parent()[0].offsetTop,
				opacity: true
			});
			else kendo.ui.progress(element, toggle);
		},
		_resize: function(size, force) {
			this._syncLockedContentHeight();
			this._syncLockedHeaderHeight();
			if (this.content) {
				this._setContentWidth();
				this._setContentHeight();
			}
			if (this.lockedTable) this._syncLockedScroll();
			if (this.virtualScrollable && (force || this._rowHeight)) {
				if (force) this._rowHeight = null;
				this.virtualScrollable.repaintScrollbar();
			}
			if (this.pager && this.pager.element) this.pager.resize(force);
			if (this._anyStickyColumns()) this._updateStickyColumns(false);
			if (this._hasStickyGroupHeaders() || this._hasStickyGroupFooters()) {
				this._syncStickyGroupColgroups();
				this._syncStickyGroupScrollState(this.content[0]);
			}
			if (this._isPinnable() && this.content && this.content[0]) {
				this._syncPinnedLockedWidths();
				this._syncPinnedScroll(this.content[0]);
			}
		},
		_isActiveInTable: function() {
			var active = activeElement();
			if (!active) return false;
			return this.table[0] === active || $.contains(this.table[0], active) || this._isLocked() && (this.lockedTable[0] === active || $.contains(this.lockedTable[0], active));
		},
		refresh: function(e) {
			var that = this, data = that.dataSource.view(), navigatable = that.options.navigatable, virtualScroll = that.virtualScroll || {}, currentIndex, current = $(that.current()), isCurrentInHeader = false, groups = that._groups(), colspan = groups + visibleLeafColumns(visibleColumns(that.columns)).length, hasMultiColumnHeaders = grep(that.columns, function(item) {
				return item.columns !== undefined;
			}).length > 0, contentScrollLeft, cachedItemsToSkip, multiColumnHeaderIndex = 0, hasGroups = that.dataSource.group() && that.dataSource.group().length > 0;
			const stacked = that._isStackedMode();
			if (e && e.action === "itemchange" && (that.editable || that.options.scrollable.endless)) {
				if (this._editMode() != "popup" || this._editMode() === "popup" && !that._editableIsClosing) return;
			}
			if (that._shouldMapHights) {
				that._mapCachedRowsHeight("get", "uid");
				that._shouldMapHights = false;
			}
			if (virtualScroll.columns) that._templates();
			if (e && e.action === "remove" && that.editable && that.editable.options.model && inArray(that.editable.options.model, e.items) > -1) that.editable.options.model.unbind(CHANGE, that._modelChangeHandler);
			e = e || {};
			if (that.trigger("dataBinding", {
				action: e.action || "rebind",
				index: e.index,
				items: e.items
			})) return;
			if (e.action === SYNC && that._isVirtualEditable()) {
				that._destroyEditable();
				that._clearEditableState();
			}
			if (!that._endlessFetchInProgress) {
				const component = that._isAdaptive() ? "kendoActionSheet" : "kendoWindow";
				if (navigatable && (that._isActiveInTable() || that._editContainer && that._editContainer.data(component))) {
					isCurrentInHeader = current.is("th");
					currentIndex = isCurrentInHeader ? current.parent().children(":not(.k-group-cell)").index(current[0]) : Math.max(that.cellIndex(current), 0);
					if (isCurrentInHeader && hasMultiColumnHeaders) multiColumnHeaderIndex = current.parent().index();
				}
				that._destroyEditable();
			}
			if (that.options.scrollable && that.options.scrollable.endless && !that._pdfInitialized) {
				clearTimeout(that._progressTimeOut);
				that._progressTimeOut = setTimeout(function() {
					if (!that._endlessFetchInProgress) {
						that._progress(false);
						if (stacked) that._toggleGroupableHeader(hasGroups);
					}
				}, 250);
			} else if (!that._isExport) {
				that._progress(false);
				if (stacked) that._toggleGroupableHeader(hasGroups);
			}
			if (current.length && current.closest(that.wrapper).length) that._currentRowIndex = current.parent().index();
			that._hideResizeHandle();
			that._data = [];
			if (!that.columns.length) {
				that._autoColumns(that._firstDataItem(data[0], groups));
				colspan = groups + that.columns.length;
			}
			that._group = groups > 0 || that._group;
			if (that._group) {
				that._templates();
				that._updateCols();
				that._updateLockedCols();
				if (!that._virtualColScroll && !stacked) that._updateHeader(groups);
				that._group = groups > 0;
				that._groupRows = groupRows(data);
			}
			if (that.content) contentScrollLeft = kendo.scrollLeft(that.content);
			const hasGroupAggregateTemplate = that.dataSource.group()?.some((g) => g.aggregates) && that.columns?.some((c) => c.groupFooterTemplate || c.groupHeaderTemplate);
			if (that.options.loaderType !== "skeleton" && e && e.action === "sync" && e.partialUpdate && e.changedItems && e.changedItems.length && !hasGroupAggregateTemplate) {
				that._data = that.dataSource.flatView();
				e.changedItems.forEach((changedItem) => {
					const row = that.tbody.find("[" + kendo.attr("uid") + "=" + changedItem.uid + "]");
					if (that._editMode() === INCELL && that.dataSource.options.autoSync && that.dataSource.isLocalTransport()) that.trigger(CELLCLOSE, {
						type: "save",
						model: e.changedItems[0],
						container: row.find(".k-edit-cell")
					});
					that._displayRow(row);
				});
				that._progress(false);
				if (stacked) that._toggleGroupableHeader(hasGroups);
				that._destroyEditable();
			} else {
				cachedItemsToSkip = that._skipRerenderItemsCount;
				that._renderContent(data, colspan, groups);
				if (that.options.scrollable && that.options.scrollable.endless && this.lockedContent) that._skipRerenderItemsCount = cachedItemsToSkip;
				that._renderLockedContent(data, colspan, groups);
			}
			if (!that._virtualColScroll) {
				that._footer();
				that._renderNoRecordsContent();
				that._togglePagerVisibility();
				if (!that._isPinnable()) that._setContentHeight();
				that._setContentWidth(that.content && contentScrollLeft);
			}
			if (that.lockedTable) if (virtualScroll.rows) that.content.find(">.k-virtual-scrollable-wrap").trigger("scroll");
			else if (that.touchScroller) that.touchScroller.movable.trigger("change");
			else {
				that.wrapper.one("scroll", function(e) {
					e.stopPropagation();
				});
				that.content.trigger("scroll");
			}
			if (!that._endlessFetchInProgress && !that._rowDropping) if (stacked && that.options.navigatable) that._setCurrentStackedCell();
			else {
				const currentIsLockedHeader = that._isLocked() && current.closest(".k-grid-header-locked").length && isCurrentInHeader;
				that._restoreCurrent(currentIndex, isCurrentInHeader, multiColumnHeaderIndex, currentIsLockedHeader);
			}
			if (that.touchScroller) that.touchScroller.contentResized();
			if (that.selectable) that.selectable.resetTouchEvents();
			if (that._checkBoxSelection) that._toggleHeaderCheckState(false);
			if (that.options.persistSelection && (that.selectable && !kendo.ui.Selectable.parseOptions(that.options.selectable).cell || that._checkBoxSelection) && (that.items().length || that.dataSource._isGroupPaged())) that._restoreSelection();
			that._restoreHighlight();
			if (that._hasAIHighlight) that._applyAIHighlight(that._hasAIHighlight);
			if (that._hasAISelection && !that._isSingleSelectionEnabled()) that._applyAISelection(that._hasAISelection);
			if (!that.options.persistSelection) that._selectedIds = {};
			if (that._hasReorderableRows()) {
				that._draggableRows();
				that._reorderableRows();
			}
			if (that.options.selectable && that.options.selectable.cellAggregates) that._calculateAggregatesForSelected();
			if (stacked) {
				const layoutSettings = that._getStackedLayoutSettings();
				const stackedRows = that.tbody.find(".k-grid-stack-row");
				if (layoutSettings.colClass) stackedRows.addClass(layoutSettings.colClass);
				else if (layoutSettings.colsConfig) stackedRows.css("grid-template-columns", layoutSettings.colsConfig);
			}
			that._toggleToolbarEditingItemsVisibility();
			that._aria();
			if (that._hasStickyGroupHeaders() || that._hasStickyGroupFooters()) {
				that._stickyHeaderItems = [];
				that._stickyFooterItems = [];
				that._syncStickyGroupColgroups();
				that._updateStickyGroups();
			}
			if (that._isPinnable() && !that._rowDropping) {
				that._initPinnedRows();
				that._renderPinnedRows();
			}
			that.trigger(DATABOUND);
		},
		_getSchemaIdField: function() {
			const model = this.dataSource.options.schema.model;
			return isFunction(model) ? model.fn.idField : model && model.id;
		},
		_restoreCurrent: function(currentIndex, isCurrentInHeader, multiColumnHeaderIndex, isLocked) {
			if (currentIndex === undefined || currentIndex < 0) return;
			this._removeCurrent();
			if (isCurrentInHeader) {
				const container = isLocked ? this.lockedHeader : this.thead;
				this._setCurrent(container.find(`tr:eq(${multiColumnHeaderIndex}) th:not(.k-group-cell)`).eq(currentIndex), false, this._hasVirtualColumns());
			} else {
				var rowIndex = 0;
				var virtualScroll = this.virtualScroll || {};
				if (this._rowVirtualIndex) if (virtualScroll.rows) rowIndex = this.virtualScrollable.position(this._rowVirtualIndex);
				else rowIndex = this._rowVirtualIndex;
				else if (this._currentRowIndex) rowIndex = this._currentRowIndex;
				else currentIndex = 0;
				var row = $();
				var colspan;
				if (this.lockedTable) if (this._shouldFocusInLastRow) row = this.lockedTable.find(">tbody>tr").last();
				else if (this._shouldFocusInFirstRow) row = this.lockedTable.find(">tbody>tr").first();
				else row = this.lockedTable.find(">tbody>tr").eq(rowIndex);
				let nonLockedRow;
				if (this._shouldFocusInLastRow) nonLockedRow = this.tbody.children().last();
				else if (this._shouldFocusInFirstRow) nonLockedRow = this.tbody.children().first();
				else nonLockedRow = this.tbody.children().eq(rowIndex);
				row = row.add(nonLockedRow);
				if (this._hasVirtualColumns()) {
					colspan = parseInt(row.find("td").first().attr("colspan"), 10);
					currentIndex = this._virtualCellIndex - (colspan > 1 ? colspan - 1 : 0);
				}
				var td = row.find(">td:not(.k-group-cell):not(.k-hierarchy-cell)").eq(currentIndex);
				if (!td.length || currentIndex < 0) return;
				if (this._hasVirtualColumns()) this._setCurrent(td, true, true);
				else this._setCurrent(td);
			}
			if (this._current) focusTable(this.table, true);
		},
		_restoreSelection: function() {
			var that = this, allRows = that.items(), selectedRows, id = that._getSchemaIdField();
			selectedRows = grep(allRows, function(row) {
				var dataItemKey = that.dataItem(row)[id];
				if (that._selectedIds[dataItemKey]) return row;
			});
			that.select(selectedRows);
		},
		_getSelectedRowUids: function() {
			var that = this, selected = that.select(), row, uid, result = [];
			for (let i = 0; i < selected.length; i++) {
				row = $(selected[i]);
				if (kendo.ui.Selectable.parseOptions(that.options.selectable).cell) row = row.closest(TR);
				uid = row.data("uid");
				if (result.indexOf(uid) === -1) result.push(uid);
			}
			return result;
		},
		_getSelectedColumnFields: function() {
			var that = this, selected = that.select(), field, index, visibleColumns = visibleLeafColumns(that.columns).filter((col) => !col.selectable && !col.draggable & !col.command), result = [];
			if (!kendo.ui.Selectable.parseOptions(that.options.selectable).cell) return visibleColumns.map((vc) => vc.field);
			for (let i = 0; i < selected.length; i++) {
				index = $(selected[i]).index();
				field = that.thead.find("th:eq(" + index + ")").data("field");
				if (result.indexOf(field) === -1) result.push(field);
			}
			return result;
		},
		_cleanupDetailItems: function() {
			var that = this;
			if (that._hasDetails()) that.tbody.find(".k-detail-cell").empty();
		},
		_renderContent: function(data, colspan, groups) {
			var that = this, idx, length, html = "", isLocked = that.lockedContent != null, endlessAppend = null, skipLastGroup, flatViewLength, scrollable = that.options.scrollable, templates = {
				rowTemplate: that.rowTemplate,
				altRowTemplate: that.altRowTemplate,
				groupFooterTemplate: that.groupFooterTemplate,
				groupHeaderColumnTemplate: that.groupHeaderColumnTemplate
			};
			const stacked = that._isStackedMode();
			if (scrollable && scrollable.endless && !that.dataSource.options.endless) {
				that._skipRerenderItemsCount = 0;
				if (that.content) that.content[0].scrollTop = 0;
			}
			endlessAppend = that._skipRerenderItemsCount > 0;
			colspan = isLocked ? colspan - visibleLeafColumns(visibleLockedColumns(that.columns)).length : colspan;
			if (stacked) colspan = that.table.find("col").length;
			if (groups > 0) {
				colspan = isLocked ? colspan - groups : colspan;
				if (that.detailTemplate && !stacked) colspan++;
				if (that.groupFooterTemplate) that._groupAggregatesDefaultObject = that.dataSource.aggregates();
				if (that.options.scrollable.endless) flatViewLength = that.dataSource.flatView().length;
				for (idx = 0, length = data.length; idx < length; idx++) {
					if (!that._skippedGroups) that._skippedGroups = [];
					skipLastGroup = flatViewLength && idx === data.length - 1 && flatViewLength !== that.dataSource.total();
					html += that._groupRowHtml(data[idx], colspan, 0, isLocked ? groupRowLockedContentBuilder : groupRowBuilder, templates, isLocked, skipLastGroup, false);
				}
			} else html += that._rowsHtml(data, templates);
			if (endlessAppend) {
				that.tbody.append(html);
				kendo.applyStylesFromKendoAttributes(that.tbody, [
					"display",
					"left",
					"right"
				]);
				clearTimeout(that._endlessFetchTimeOut);
				that._endlessFetchTimeOut = setTimeout(function() {
					if (that._groupToCollapse) {
						that.collapseGroup(that._groupToCollapse);
						that._groupToCollapse = null;
					}
				});
				that._endlessFetchInProgress = null;
			} else that.tbody = appendContent(that.tbody, that.table, html, this.options.size);
		},
		_renderGroupRows: function() {
			var that = this, data = that._groupRows, groupRows = that.wrapper.find(".k-grouping-row"), groups = that._groups(), groupRowBuilderFunc, isLocked = that.lockedContent != null, columns, colspan, group, field, column, template, text, groupHeaderData, tableContainer, isInLockedContainer, prevElement, newGroupRowElement, currentRow, level, groupHeaderColumnTemplate, firstColumnGroupData;
			groupRows.each(function(index, row) {
				currentRow = $(row);
				tableContainer = currentRow.closest("table").parent();
				isInLockedContainer = tableContainer.is(".k-grid-content-locked");
				columns = isInLockedContainer ? visibleLeafColumns(visibleColumns(lockedColumns(that.columns))) : visibleLeafColumns(visibleColumns(nonLockedColumns(that.columns)));
				level = currentRow.find(".k-group-cell").length;
				if (isLocked) {
					groupRowBuilderFunc = isInLockedContainer ? groupRowBuilder : groupRowLockedContentBuilder;
					colspan = isInLockedContainer ? columns.length + groups - level : columns.length;
				} else {
					groupRowBuilderFunc = groupRowBuilder;
					colspan = columns.length + groups - level;
				}
				group = index >= data.length ? data[index - data.length] : data[index];
				field = group.field;
				column = grep(leafColumns(that.columns), function(column) {
					return column.field == field;
				})[0] || {};
				firstColumnGroupData = !column.groupHeaderTemplate && visibleColumns(that.columns)[0].groupHeaderColumnTemplate ? visibleColumns(that.columns)[0] : false;
				template = column.groupHeaderTemplate ? column.groupHeaderTemplate : visibleColumns(that.columns)[0].groupHeaderColumnTemplate;
				text = (column.title && (that.options.encodeTitles ? htmlEncode(column.title, true) : column.title) || htmlEncode(field, true)) + ": " + formatGroupValue(group.value, column.format, column.values, column.encoded);
				groups = groups;
				groupHeaderData = that._groupData(group, false, firstColumnGroupData);
				groupHeaderColumnTemplate = isInLockedContainer ? that.lockedGroupHeaderColumnTemplate : that.groupHeaderColumnTemplate;
				if (template) text = typeof template === FUNCTION ? template(groupHeaderData) : kendo.template(template)(groupHeaderData);
				prevElement = currentRow.prev().length ? currentRow.prev() : currentRow.parent();
				newGroupRowElement = $(groupHeaderColumnTemplate ? groupHeaderColumnTemplate(extend({}, groupHeaderData, {
					groupCells: level,
					colspan: groups - level,
					text
				})) : groupRowBuilderFunc(colspan, level, text, null, null, null, isRtl, that._isStackedMode()));
				kendo.applyStylesFromKendoAttributes(newGroupRowElement, [
					"display",
					"left",
					"right"
				]);
				if (prevElement.is("tbody")) prevElement.prepend(newGroupRowElement);
				else prevElement.after(newGroupRowElement);
				currentRow.remove();
			});
		},
		_renderLockedContent: function(data, colspan, groups) {
			var html = "", idx, length, skipLastGroup, endlessAppend = null, flatViewLength, templates = {
				rowTemplate: this.lockedRowTemplate,
				altRowTemplate: this.lockedAltRowTemplate,
				groupFooterTemplate: this.lockedGroupFooterTemplate,
				groupHeaderColumnTemplate: this.lockedGroupHeaderColumnTemplate
			};
			if (this.lockedContent) {
				var table = this.lockedTable;
				endlessAppend = this._skipRerenderItemsCount > 0;
				if (groups > 0) {
					colspan = colspan - visibleColumns(leafColumns(nonLockedColumns(this.columns))).length;
					if (this.options.scrollable.endless) flatViewLength = this.dataSource.flatView().length;
					for (idx = 0, length = data.length; idx < length; idx++) {
						skipLastGroup = flatViewLength && idx === data.length - 1 && flatViewLength !== this.dataSource.total();
						html += this._groupRowHtml(data[idx], colspan, 0, groupRowBuilder, templates, false, skipLastGroup, true);
					}
				} else html = this._rowsHtml(data, templates);
				if (endlessAppend) table.children("tbody").append(html);
				else appendContent(table.children("tbody"), table, html, this.options.size);
				this._syncLockedContentHeight();
			}
		},
		_togglePagerVisibility: function() {
			if (this.options.pageable.alwaysVisible === false) this.wrapper.find(".k-grid-pager").toggle(this.dataSource.total() >= this.dataSource.pageSize());
		},
		_adjustRowsHeight: function(table1, table2) {
			var rows = table1[0].rows, length = rows.length, idx, rows2 = table2[0].rows, containers = table1.add(table2), containersLength = containers.length, heights = [];
			for (idx = 0; idx < length; idx++) {
				if (!rows2[idx]) break;
				if (rows[idx].style.height) rows[idx].style.height = rows2[idx].style.height = "";
			}
			for (idx = 0; idx < length; idx++) {
				if (!rows2[idx]) break;
				var offsetHeight1 = rows[idx].getBoundingClientRect().height;
				var offsetHeight2 = rows2[idx].getBoundingClientRect().height;
				var height = 0;
				if (offsetHeight1 > offsetHeight2) height = offsetHeight1;
				else if (offsetHeight1 < offsetHeight2) height = offsetHeight2;
				heights.push(height);
			}
			for (idx = 0; idx < containersLength; idx++) containers[idx].style.display = NONE;
			for (idx = 0; idx < length; idx++) if (heights[idx]) rows[idx].style.height = rows2[idx].style.height = heights[idx] + PX;
			for (idx = 0; idx < containersLength; idx++) containers[idx].style.display = "";
		}
	});
	if (kendo.ExcelMixin) kendo.ExcelMixin.extend(Grid.prototype);
	if (kendo.CSVMixin) kendo.CSVMixin.extend(Grid.prototype);
	if (kendo.PDFMixin) {
		kendo.PDFMixin.extend(Grid.prototype);
		Grid.prototype._drawPDF_autoPageBreak = function(progress) {
			var grid = this;
			var result = new $.Deferred();
			var dataSource = grid.dataSource;
			var allPages = grid.options.pdf.allPages;
			var origBody = grid.wrapper.find("> table > tbody, .k-grid-content > table > tbody").first();
			var cont = $("<div>").css({
				position: "absolute",
				left: -1e4,
				top: -1e4
			});
			var clone;
			grid.toggleUnexportableColumns(grid.columns);
			clone = grid.wrapper.clone().css({
				height: AUTO,
				width: AUTO
			}).appendTo(cont);
			clone.find(".k-grid-content").css({
				height: AUTO,
				width: AUTO,
				overflow: "visible"
			});
			clone.find("> table, .k-grid-header table, .k-grid-content > table, .k-grid-footer table").css({
				height: AUTO,
				width: "100%",
				overflow: "visible"
			});
			clone.find(".k-grid-pager, .k-grid-toolbar, .k-grouping-header").remove();
			clone.find(".k-grid-header, .k-grid-footer, .k-auto-scrollable").css({ paddingRight: 0 });
			var body = clone.find("> table > tbody, .k-grid-content > table > tbody").first().empty();
			var startingPage = dataSource.page();
			function resolve() {
				if (allPages && startingPage !== undefined) {
					dataSource.one("change", draw);
					dataSource.page(startingPage);
				} else {
					grid.refresh();
					draw();
				}
			}
			function draw() {
				cont.appendTo(document.body);
				var options = $.extend({}, grid.options.pdf, {
					_destructive: true,
					progress: function(p) {
						progress.notify({
							page: p.page,
							pageNumber: p.pageNum,
							progress: .5 + p.pageNum / p.totalPages / 2,
							totalPages: p.totalPages
						});
					}
				});
				kendo.drawing.drawDOM(clone, options).always(function() {
					cont.remove();
				}).then(function(group) {
					result.resolve(group);
					grid.toggleUnexportableColumns(grid.columns, true);
				}).fail(function(err) {
					result.reject(err);
				});
			}
			function renderPage() {
				var pageNum = dataSource.page();
				var totalPages = allPages ? dataSource.totalPages() : 1;
				body.append(origBody.children("tr:not(.k-detail-row)"));
				if (pageNum < totalPages) dataSource.page(pageNum + 1);
				else {
					dataSource.unbind("change", renderPage);
					resolve();
				}
			}
			if (allPages) {
				dataSource.bind("change", renderPage);
				dataSource.page(1);
			} else renderPage();
			return result.promise();
		};
		Grid.prototype.toggleUnexportableColumns = function(columns, restore) {
			var length = columns.length;
			var column;
			var exportable;
			var visibleInExport;
			var visibleInExportOnly;
			for (var i = 0; i < length; i++) {
				column = columns[i];
				exportable = column.exportable;
				if (!restore) {
					if (typeof column.exportable === "object") exportable = column.exportable.pdf;
					visibleInExport = !column.hidden && exportable !== false;
					visibleInExportOnly = column.hidden && exportable === true;
					exportable = visibleInExport || visibleInExportOnly;
					if (!exportable && !column.hidden) {
						column._toggledDuringExport = true;
						this.hideColumn(column);
					} else if (exportable && column.hidden) {
						column._toggledDuringExport = true;
						this.showColumn(column);
					} else if (exportable && column.columns) this.toggleUnexportableColumns(column.columns);
				} else if (column._toggledDuringExport) {
					column._toggledDuringExport = false;
					if (column.hidden) this.showColumn(column);
					else this.hideColumn(column);
				} else if (column.columns) this.toggleUnexportableColumns(column.columns, restore);
			}
		};
		Grid.prototype._drawPDF = function(progress) {
			var grid = this;
			if (grid.options.pdf.paperSize && grid.options.pdf.paperSize != AUTO) return grid._drawPDF_autoPageBreak(progress);
			var result = new $.Deferred();
			var dataSource = grid.dataSource;
			var allPages = grid.options.pdf.allPages;
			var doc = new kendo.drawing.Group();
			var startingPage = dataSource.page();
			function resolve() {
				if (allPages && startingPage !== undefined) {
					dataSource.unbind("change", exportPage);
					dataSource.one("change", function() {
						result.resolve(doc);
					});
					dataSource.page(startingPage);
				} else result.resolve(doc);
			}
			function exportPage() {
				grid.toggleUnexportableColumns(grid.columns);
				grid._drawPDFShadow({ width: grid.wrapper.width() }, { avoidLinks: grid.options.pdf.avoidLinks }).done(function(group) {
					var pageNum = dataSource.page();
					var totalPages = allPages ? dataSource.totalPages() : 1;
					var args = {
						page: group,
						pageNumber: pageNum,
						progress: pageNum / totalPages,
						totalPages
					};
					grid.toggleUnexportableColumns(grid.columns, true);
					progress.notify(args);
					doc.append(args.page);
					if (pageNum < totalPages) dataSource.page(pageNum + 1);
					else resolve();
				}).fail(function(err) {
					result.reject(err);
				});
			}
			if (allPages) {
				dataSource.bind("change", exportPage);
				dataSource.page(1);
			} else exportPage();
			return result.promise();
		};
	}
	function syncTableHeight(table1, table2) {
		table1 = table1[0];
		table2 = table2[0];
		if (table1.rows.length !== table2.rows.length) {
			var lockedHeigth = table1.offsetHeight;
			var tableHeigth = table2.offsetHeight;
			var row;
			var diff;
			if (lockedHeigth > tableHeigth) {
				row = table2.rows[table2.rows.length - 1];
				if (filterRowRegExp.test(row.className)) row = table2.rows[table2.rows.length - 2];
				diff = lockedHeigth - tableHeigth;
			} else {
				row = table1.rows[table1.rows.length - 1];
				if (filterRowRegExp.test(row.className)) row = table1.rows[table1.rows.length - 2];
				diff = tableHeigth - lockedHeigth;
			}
			row.style.height = row.offsetHeight + diff + PX;
		}
	}
	function adjustRowHeight(row1, row2) {
		var height;
		var offsetHeight1 = row1.offsetHeight;
		var offsetHeight2 = row2.offsetHeight;
		if (offsetHeight1 > offsetHeight2) height = offsetHeight1 + PX;
		else if (offsetHeight1 < offsetHeight2) height = offsetHeight2 + PX;
		if (height) row1.style.height = row2.style.height = height;
	}
	function getCommand(commands, name) {
		var idx, length, command;
		if (typeof commands === STRING && commands === name) return commands;
		if (isPlainObject(commands) && commands.name === name) return commands;
		if (isArray(commands)) for (idx = 0, length = commands.length; idx < length; idx++) {
			command = commands[idx];
			if (typeof command === STRING && command === name || command.name === name) return command;
		}
		return null;
	}
	function compareElements(element, toCompare) {
		if (element.length !== toCompare.length) return false;
		for (var i = 0; i < element.length; i++) if (element[i] !== toCompare[i]) return false;
		return true;
	}
	function focusTable(table, direct) {
		if (!table || table.length === 0) return;
		if (direct === true) {
			table = $(table);
			var scrollLeft = kendo.scrollLeft(table.parent());
			kendo.focusElement(table);
			kendo.scrollLeft(table.parent(), scrollLeft);
		} else $(table).one("focusin", function(e) {
			e.preventDefault();
		}).trigger("focus");
	}
	function isColumnGroupable(grid, column) {
		return grid.options.groupable && (column.groupable || column.groupable === undefined);
	}
	function isGroupedBy(groups, field) {
		return !!$.grep(groups, function(item) {
			return item.field === field;
		}).length;
	}
	function isColumnEditable(column, model) {
		if (!column.field || column.selectable) return false;
		if (model.editable && !model.editable(column.field)) return false;
		if (column.editable && !column.editable(model)) return false;
		return true;
	}
	function isInputElement(element) {
		return $(element).is(INPUT_SELECTORS) || $(element).is("div[contenteditable=true]");
	}
	function tableClick(e) {
		var that = this, currentTarget = $(e.currentTarget), isHeader = currentTarget.is("th"), table = this.table.add(this.lockedTable), headerTable = this.thead && this.thead.parent().add($(">table", this.lockedHeader)), isInput = isInputElement(e.target), preventScroll = $(e.target).is(".k-checkbox"), target = $(e.target), currentTable = currentTarget.closest("table")[0];
		if (!that._isStackedMode() && isInput && currentTarget.find(kendo.roleSelector("filtercell")).length) {
			this._setCurrent(currentTarget, null, null, true);
			return;
		}
		if (currentTable !== table[0] && currentTable !== table[1] && currentTable !== headerTable[0] && currentTable !== headerTable[1]) {
			if (!currentTarget.closest(".k-grid-pinned-container").length) return;
		}
		if (target.is(CARET_ALT_RIGHT + ",a[class*='-i-chevron-down'],[ref='expand-detail-button'], [ref='collapse-detail-button']")) return;
		if (this.options.navigatable) this._setCurrent(currentTarget, false, preventScroll);
		if (isHeader || !isInput) setTimeout(function() {
			var activeEl = $(kendo._activeElement());
			if ((activeEl.hasClass("k-widget") || activeEl.hasClass("k-dropdownlist") || activeEl.is(".k-upload .k-upload-button")) && !activeEl.hasClass("k-grid-pager") || activeEl.hasClass("k-select-checkbox")) return;
			if (that.table && (activeEl.is(CHECKBOXINPUT) || !isInputElement(kendo._activeElement()) || !$.contains(currentTable, kendo._activeElement()))) focusTable(that.table[0], true);
		});
		if (isHeader && !kendo.support.touch) e.preventDefault();
	}
	function leftMostPosition(element, rtl) {
		if (!rtl) return 0;
		var result = 0;
		if (kendo.support.browser.webkit) result = element.width();
		return result;
	}
	function parseVirtualSettings(options) {
		var asLowerString;
		if (typeof options === "string") {
			asLowerString = options.toLowerCase();
			if (asLowerString === "true") return { rows: true };
			else return {
				rows: asLowerString.indexOf("rows") > -1,
				columns: asLowerString.indexOf("columns") > -1
			};
		} else if (options === true) return { rows: true };
	}
	function isElementVisibleInWrapper(wrapper, element) {
		var offsetTop;
		var halfHeight;
		if (!wrapper) return false;
		element = $(element);
		if (element[0] && contains(wrapper[0], element[0])) {
			offsetTop = element.offset().top - wrapper.offset().top;
			halfHeight = element.outerHeight() / 2;
			if ((offsetTop >= 0 || math.abs(offsetTop) <= halfHeight) && math.floor(offsetTop + halfHeight) <= wrapper.height()) return true;
		}
		return false;
	}
	function isInEdit(cell) {
		return cell && (cell.hasClass("k-edit-cell") || cell.parent().hasClass("k-grid-edit-row") || cell.hasClass("k-grid-stack-edit-cell"));
	}
	function groupHtmlBuilder({ groupHeaderColumnTemplate, groupHeaderBuilder, colspan, templateColspan, groupData, level, text, expanded, group, isGroupPaged, stacked }) {
		var html;
		if (groupHeaderColumnTemplate) html = groupHeaderColumnTemplate(extend({}, groupData, {
			groupCells: level,
			colspan: templateColspan,
			text,
			expanded,
			isRtl,
			uid: group.uid
		}));
		else html = groupHeaderBuilder(colspan, level, text, expanded, group.uid, isGroupPaged, isRtl, stacked);
		return html;
	}
	function groupCellBuilder(headerTemplateIndex, stacked, aggregates) {
		return ({ colspan, text, expanded, isRtl }) => {
			let length = colspan + headerTemplateIndex > 0 ? colspan + headerTemplateIndex : 0;
			let collapsedClass = `chevron-${isRtl ? "left" : "right"}`;
			return `<td class="k-table-td" colspan="${colspan + headerTemplateIndex}"><p class="k-reset">` + kendo.ui.icon($(`<a href="\\#" tabindex="-1" ${ARIA_LABEL}="${expanded ? COLLAPSE : EXPAND}"></a>`), { icon: expanded ? "chevron-down" : collapsedClass }) + text + `</p>${aggregates || ""}</td>${!stacked ? new Array(length).join("<td hidden group-header-spanned-hidden></td>") : ""}`;
		};
	}
	function groupCellLockedContentBuilder(headerTemplateIndex) {
		return "<td class=\"k-table-td\" colspan=\"" + headerTemplateIndex + `"><p class="k-reset">&nbsp;</p></td>${new Array(headerTemplateIndex).join("<td hidden group-header-spanned-hidden></td>")}`;
	}
	function groupRowBuilder(colspan, level, text, expanded, uid, includeAdditionalData, isRtl, stacked, aggregates) {
		const chevronDirectionIcon = `chevron-${isRtl ? "left" : "right"}`;
		return "<tr " + (includeAdditionalData ? "data-group-uid=\"" + uid + "\"" : "") + "class=\"k-table-group-row k-grouping-row k-table-row\">" + groupCells(level) + "<td class=\"k-table-td\" colspan=\"" + colspan + "\" aria-expanded=\"" + !!expanded + "\"><p class=\"k-reset\">" + kendo.ui.icon($("<a href=\"#\" tabindex=\"-1\" aria-label=\"" + (expanded ? COLLAPSE : EXPAND) + "\"></a>"), { icon: expanded ? "chevron-down" : chevronDirectionIcon }) + text + `</p>${aggregates || ""}</td>${!stacked ? new Array(colspan).join("<td hidden group-header-spanned-hidden></td>") : ""}</tr>`;
	}
	function groupRowLockedContentBuilder(colspan) {
		return "<tr class=\"k-table-group-row k-grouping-row k-table-row\"><td class=\"k-table-td\" colspan=\"" + colspan + `" aria-expanded="true"><p class="k-reset">&nbsp;</p></td>${new Array(colspan).join("<td hidden group-header-spanned-hidden></td>")}</tr>`;
	}
	function toggleRow(row, visible) {
		row = $(row)[0];
		if (visible) row.style.display = "";
		else row.style.display = NONE;
	}
	function htmlEncode(value, backslashEscapeQuotes) {
		return ("" + value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, function(match) {
			if (backslashEscapeQuotes) return "\\" + match;
			return "&quot;";
		}).replace(/'/g, "&#39;");
	}
	function isEmptyString(value) {
		return !/\S/.test(value);
	}
	function getTitle(field, columns) {
		return columns.filter(function(col) {
			return col.field === field;
		})[0].title || field;
	}
	function exportDataSort(a, b) {
		return this.dataSource.indexOf(this.dataSource.getByUid(a.uid)) - this.dataSource.indexOf(this.dataSource.getByUid(b.uid));
	}
	function isExcelExportableColumn(column) {
		return !(column.exportable === false || column.exportable && column.exportable.excel === false);
	}
	ui.plugin(Grid);
	ui.plugin(VirtualScrollable);
	extend(kendo.ui.grid, {
		defaultBodyContextMenu,
		defaultHeadContextMenu,
		defaultGroupsContextMenu
	});
})(window.kendo.jQuery);
var kendo_grid_default = kendo;
//#endregion
Object.defineProperty(exports, "__meta__", {
	enumerable: true,
	get: function() {
		return __meta__;
	}
});
Object.defineProperty(exports, "kendo_grid_default", {
	enumerable: true,
	get: function() {
		return kendo_grid_default;
	}
});