{"version":3,"file":"kendo.grid.min.js","sources":["kendo.grid.js"],"sourcesContent":["(function(f, define) {\n define('kendo.grid',[\n \"kendo.data\",\n \"kendo.columnsorter\",\n \"kendo.editable\",\n \"kendo.window\",\n \"kendo.filtermenu\",\n \"kendo.filtercell\",\n \"kendo.columnmenu\",\n \"kendo.groupable\",\n \"kendo.pager\",\n \"kendo.selectable\",\n \"kendo.sortable\",\n \"kendo.reorderable\",\n \"kendo.resizable\",\n \"kendo.ooxml\",\n \"kendo.excel\",\n \"kendo.pane\",\n \"kendo.progressbar\",\n \"kendo.pdf\",\n \"kendo.dialog\",\n \"kendo.pane\",\n \"kendo.switch\",\n \"kendo.html.button\",\n \"kendo.textbox\",\n \"kendo.form\"\n ], f);\n})(function() {\n\nvar __meta__ = {\n id: \"grid\",\n name: \"Grid\",\n category: \"web\",\n description: \"The Grid widget displays tabular data and offers rich support for interacting with data,including paging, sorting, grouping, and selection.\",\n depends: [ \"data\", \"columnsorter\", \"sortable\", \"html.button\" ],\n features: [ {\n id: \"grid-editing\",\n name: \"Editing\",\n description: \"Support for record editing\",\n depends: [ \"editable\", \"window\", \"textbox\", \"form\" ]\n }, {\n id: \"grid-filtering\",\n name: \"Filtering\",\n description: \"Support for record filtering\",\n depends: [ \"filtermenu\" ]\n }, {\n id: \"grid-columnmenu\",\n name: \"Column menu\",\n description: \"Support for header column menu\",\n depends: [ \"columnmenu\" ]\n }, {\n id: \"grid-grouping\",\n name: \"Grouping\",\n description: \"Support for grid grouping\",\n depends: [ \"groupable\" ]\n }, {\n id: \"grid-filtercell\",\n name: \"Row filter\",\n description: \"Support for grid header filtering\",\n depends: [ \"filtercell\" ]\n }, {\n id: \"grid-paging\",\n name: \"Paging\",\n description: \"Support for grid paging\",\n depends: [ \"pager\" ]\n }, {\n id: \"grid-selection\",\n name: \"Selection\",\n description: \"Support for row selection\",\n depends: [ \"selectable\" ]\n }, {\n id: \"grid-column-reorder\",\n name: \"Column reordering\",\n description: \"Support for column reordering\",\n depends: [ \"reorderable\" ]\n }, {\n id: \"grid-column-resize\",\n name: \"Column resizing\",\n description: \"Support for column resizing\",\n depends: [ \"resizable\" ]\n }, {\n id: \"grid-mobile\",\n name: \"Grid adaptive rendering\",\n description: \"Support for adaptive rendering\",\n depends: [ \"dialog\", \"pane\", \"switch\" ]\n }, {\n id: \"grid-excel-export\",\n name: \"Excel export\",\n description: \"Export grid data as Excel spreadsheet\",\n depends: [ \"excel\" ]\n }, {\n id: \"grid-pdf-export\",\n name: \"PDF export\",\n description: \"Export grid data as PDF\",\n depends: [ \"pdf\", \"drawing\", \"progressbar\" ]\n } ]\n};\n\n\n(function($, undefined) {\n var kendo = window.kendo,\n ui = kendo.ui,\n DataSource = kendo.data.DataSource,\n ObservableObject = kendo.data.ObservableObject,\n tbodySupportsInnerHtml = kendo.support.tbodyInnerHtml,\n activeElement = kendo._activeElement,\n Widget = ui.Widget,\n outerWidth = kendo._outerWidth,\n outerHeight = kendo._outerHeight,\n keys = kendo.keys,\n\n isPlainObject = $.isPlainObject,\n extend = $.extend,\n map = $.map,\n grep = $.grep,\n isArray = Array.isArray,\n inArray = $.inArray,\n push = Array.prototype.push,\n isFunction = kendo.isFunction,\n isEmptyObject = $.isEmptyObject,\n contains = $.contains,\n math = Math,\n\n DOT = \".\",\n PROGRESS = \"progress\",\n ERROR = \"error\",\n HIERARCHY_CELL_CLASS = \"k-hierarchy-cell\",\n DATA_CELL = \":not(.k-group-cell):not([\" + kendo.attr(\"virtual\") + \"]):not(.k-hierarchy-cell:not(:has(.k-icon.k-i-collapse,.k-icon.k-i-expand))):visible\",\n SELECTION_CELL_SELECTOR = \"tbody>tr:not(.k-grouping-row):not(.k-detail-row):not(.k-group-footer) > td:not(.k-group-cell):not(.k-hierarchy-cell)\",\n NAVROW = \"tr:not(.k-footer-template):visible\",\n NAVCELL = \":not(.k-group-cell):not(.k-detail-cell):not(.k-hierarchy-cell):visible\",\n ITEMROW = \"tr:not(.k-grouping-row):not(.k-detail-row):not(.k-footer-template):not(.k-group-footer):visible\",\n COLGROUP = \"col:not(.k-group-col, .k-hierarchy-col)\",\n HEADERCELLS = \"th.k-header:not(.k-group-cell):not(.k-hierarchy-cell)\",\n WRAPPER = \".k-grid.k-widget\",\n NS = \".kendoGrid\",\n CONTENTRLOCKEDCONTAINER = \"k-grid-content-locked\",\n GROUPCELLCLASS = \"k-group-cell\",\n\n EDIT = \"edit\",\n BEFOREEDIT = \"beforeEdit\",\n SAVE = \"save\",\n REMOVE = \"remove\",\n DETAILINIT = \"detailInit\",\n FILTERMENUINIT = \"filterMenuInit\",\n COLUMNMENUINIT = \"columnMenuInit\",\n FILTERMENUOPEN = \"filterMenuOpen\",\n COLUMNMENUOPEN = \"columnMenuOpen\",\n CELLCLOSE = \"cellClose\",\n CHANGE = \"change\",\n COLUMNHIDE = \"columnHide\",\n COLUMNSHOW = \"columnShow\",\n SAVECHANGES = \"saveChanges\",\n DATABOUND = \"dataBound\",\n DETAILEXPAND = \"detailExpand\",\n DETAILCOLLAPSE = \"detailCollapse\",\n ITEM_CHANGE = \"itemchange\",\n PAGE = \"page\",\n PAGING = \"paging\",\n SCROLL = \"scroll\",\n SYNC = \"sync\",\n LOAD_START = \"loadStart\",\n LOAD_END = \"loadEnd\",\n\n FOCUSED = \"k-focus\",\n FOCUSABLE = \":kendoFocusable\",\n SELECTED = \"k-selected\",\n CHECKBOX = \"k-checkbox\",\n CHECKBOXINPUT = \"input[data-role='checkbox'].\" + CHECKBOX,\n NORECORDSCLASS = \"k-grid-norecords\",\n LINK_CLASS = \"k-link\",\n ICON_CLASS = \"k-icon\",\n ORDER_CLASS = \"k-sort-order\",\n SORTED_CLASS = \"k-sorted\",\n HEADER_COLUMN_MENU_CLASS = \"k-header-column-menu\",\n FILTER_MENU_CLASS = \"k-grid-filter\",\n STICKY_CELL_CLASS = \"k-grid-content-sticky\",\n STICKY_HEADER_CLASS = \"k-grid-header-sticky\",\n STICKY_FOOTER_CLASS = \"k-grid-footer-sticky\",\n STICKY_HEADER_NO_BORDER_CLASS = \"k-grid-no-left-border\",\n RESIZE = \"resize\",\n COLUMNRESIZE = \"columnResize\",\n COLUMNREORDER = \"columnReorder\",\n COLUMNLOCK = \"columnLock\",\n COLUMNUNLOCK = \"columnUnlock\",\n COLUMNSTICK = \"columnStick\",\n COLUMNUNSTICK = \"columnUnstick\",\n ROWREORDER = \"rowReorder\",\n NAVIGATE = \"navigate\",\n CLICK = \"click\",\n MOUSEDOWN = \"mousedown\",\n HEIGHT = \"height\",\n TABINDEX = \"tabIndex\",\n FUNCTION = \"function\",\n STRING = \"string\",\n BOTTOM = \"bottom\",\n CONTAINER_FOR = \"container-for\",\n FIELD = \"field\",\n INPUT = \"input\",\n INCELL = \"incell\",\n INLINE = \"inline\",\n UNIQUE_ID = \"uid\",\n MINCOLSPANVALUE = 1,\n COLSPAN = \"colSpan\",\n OVERFLOW = \"overflow\",\n HIDDEN = \"hidden\",\n SORT = \"sort\",\n GROUP_SORT = \"group-sort\",\n DELETECONFIRM = \"Are you sure you want to delete this record?\",\n NORECORDS = \"No records available.\",\n CONFIRMDELETE = \"Delete\",\n CANCELDELETE = \"Cancel\",\n COLLAPSE = \"Collapse\",\n EXPAND = \"Expand\",\n ARIALABEL = \"aria-label\",\n formatRegExp = /(\\}|\\#)/ig,\n templateHashRegExp = /#/ig,\n whitespaceRegExp = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",\n leftRegExp = new RegExp(\"(\\\\s*left\\\\s*:\\\\s*\\\\d*px;?)*\", \"ig\"),\n rightRegExp = new RegExp(\"(\\\\s*right\\\\s*:\\\\s*\\\\d*px;?)*\", \"ig\"),\n nonDataCellsRegExp = new RegExp(\"(^|\" + whitespaceRegExp + \")\" + \"(k-group-cell|k-hierarchy-cell)\" + \"(\" + whitespaceRegExp + \"|$)\"),\n filterRowRegExp = new RegExp(\"(^|\" + whitespaceRegExp + \")\" + \"(k-filter-row)\" + \"(\" + whitespaceRegExp + \"|$)\"),\n COMMANDBUTTONTMPL = '',\n SELECTCOLUMNTMPL = '',\n SELECTCOLUMNHEADERTMPL = '',\n DRAGHANDLECOLUMNTMPL = '',\n SORTHEADERTMPL = '#=text#',\n isRtl = false,\n browser = kendo.support.browser;\n\n var isIE11 = browser.msie && browser.version === 11;\n var isMac = /Mac OS/.test(navigator.userAgent);\n var classNames = {\n content: \"k-content\",\n widget: \"k-widget\",\n scrollContainer: \"k-scroll-container\",\n headerCellInner: \"k-cell-inner\"\n };\n var GroupsPager;\n\n if (ui.Pager) {\n GroupsPager = ui.Pager.extend({\n init: function(element, options) {\n ui.Pager.fn.init.call(this, element, extend(true, {}, options));\n this.dataSource.options.useRanges = true;\n this.dataSource._omitPrefetch = true;\n },\n options: {\n name: \"GroupsPager\"\n },\n\n totalPages: function() {\n var that = this;\n\n return Math.ceil((that._collapsedTotal() || 0) / (that.pageSize() || 1));\n },\n _collapsedTotal: function() {\n var dataSource = this.dataSource;\n return dataSource ? (dataSource.groupsTotal(true) || 0) : 0;\n }\n });\n }\n\n var VirtualScrollable = Widget.extend({\n init: function(element, options) {\n var that = this;\n\n Widget.fn.init.call(that, element, options);\n that._refreshHandler = that.refresh.bind(that);\n that.setDataSource(options.dataSource);\n that.wrap();\n },\n\n setDataSource: function(dataSource) {\n var that = this;\n if (that.dataSource) {\n that.dataSource.unbind(CHANGE, that._refreshHandler);\n }\n that.dataSource = dataSource;\n that.dataSource.bind(CHANGE, that._refreshHandler);\n that.dataSource.options.useRanges = true;\n that.dataSource.options.virtual = true;\n },\n\n options: {\n name: \"VirtualScrollable\",\n itemHeight: $.noop,\n prefetch: true,\n maxScrollHeight: 250000\n },\n\n events: [\n PAGING,\n PAGE,\n SCROLL,\n LOAD_START,\n LOAD_END\n ],\n\n destroy: function() {\n var that = this;\n\n Widget.fn.destroy.call(that);\n\n that.dataSource.unbind(CHANGE, that._refreshHandler);\n that.wrapper.add(that.verticalScrollbar).off(NS);\n\n clearTimeout(that._timeout);\n if (that._scrollingTimeout) {\n clearTimeout(that._scrollingTimeout);\n }\n\n if (that.drag) {\n that.drag.destroy();\n that.drag = null;\n }\n that.wrapper = that.element = that.verticalScrollbar = null;\n that._refreshHandler = null;\n },\n\n wrap: function() {\n var that = this,\n // workaround for IE issue where scroll is not raised if container is same width as the scrollbar\n scrollbar = kendo.support.scrollbar() + 1,\n element = that.element,\n wrapper;\n\n element.css( {\n width: \"auto\",\n overflow: \"hidden\"\n }).css((isRtl ? \"padding-left\" : \"padding-right\"), scrollbar);\n that.content = element.children().first();\n wrapper = that.wrapper = that.content.wrap('
')\n .parent()\n .on(\"DOMMouseScroll\" + NS + \" mousewheel\" + NS, that._wheelScroll.bind(that));\n that._wrapper();\n\n if (kendo.support.kineticScrollNeeded || kendo.support.touch) {\n that.wrapper.css(\"touch-action\", \"none\");\n that.drag = new kendo.UserEvents(that.wrapper, {\n global: true,\n allowSelection: true,\n start: function(e) {\n e.sender.capture();\n },\n move: function(e) {\n that.verticalScrollbar.scrollTop(that.verticalScrollbar.scrollTop() - e.y.delta);\n kendo.scrollLeft(wrapper, kendo.scrollLeft(wrapper) - e.x.delta);\n e.preventDefault();\n }\n });\n }\n\n that.verticalScrollbar = $('
')\n .css({\n width: scrollbar\n }).appendTo(element)\n .on(\"scroll\" + NS, that._scroll.bind(that));\n },\n\n _wrapper: function() {\n var that = this;\n\n if (isIE11) {\n //scrolling the virtual scrollbar to the bottom and then\n //scrolling the horizontal content scrollbar does not fire the \"scroll\" event\n //seems like a problem in IE 11 only (after version 11.0.9600.18860)\n //https://github.com/telerik/kendo-ui-core/issues/3779\n that.wrapper.css({\n \"overflow-y\": SCROLL\n });\n\n //hide the wrapper behind the virtual scrollbar\n that.element.css((isRtl ? \"padding-left\" : \"padding-right\"), 0);\n }\n },\n\n _wheelScroll: function(e) {\n if (e.ctrlKey) {\n return;\n }\n\n var scrollbar = this.verticalScrollbar,\n scrollTop = scrollbar.scrollTop(),\n delta = kendo.wheelDeltaY(e);\n\n if (delta && !(delta > 0 && scrollTop === 0) && !(delta < 0 && scrollTop + scrollbar[0].clientHeight == scrollbar[0].scrollHeight)) {\n e.preventDefault();\n this.verticalScrollbar.scrollTop(scrollTop + (-delta));\n }\n },\n\n _scroll: function(e) {\n var that = this,\n delayLoading = !that.options.prefetch,\n scrollTop = e.currentTarget.scrollTop,\n dataSource = that.dataSource,\n rowHeight = that.itemHeight,\n skip = dataSource.skip() || 0,\n start = that._rangeStart || skip,\n height = that.element.innerHeight(),\n isScrollingUp = !!(that._scrollbarTop && that._scrollbarTop > scrollTop),\n firstItemIndex = math.max(math.floor(scrollTop / rowHeight), 0),\n lastItemOffset = isScrollingUp ? math.ceil(height / rowHeight) : math.floor(height / rowHeight),\n lastItemIndex = math.max(firstItemIndex + lastItemOffset, 0);\n\n if (that._preventScroll) {\n that._preventScroll = false;\n return;\n }\n that._prevScrollTop = that._scrollTop;\n that._scrollTop = scrollTop - (start * rowHeight);\n that._scrollbarTop = scrollTop;\n\n that._scrolling = delayLoading;\n\n if (!that._fetch(firstItemIndex, lastItemIndex, isScrollingUp)) {\n that.wrapper[0].scrollTop = that._scrollTop;\n }\n\n that.trigger(SCROLL);\n\n if (delayLoading) {\n if (that._scrollingTimeout) {\n clearTimeout(that._scrollingTimeout);\n }\n\n that._scrollingTimeout = setTimeout(function() {\n that._scrolling = false;\n that._page(that._rangeStart, that.dataSource.take());\n }, 100);\n }\n },\n\n scrollToTop: function() {\n this._scrollTo(0);\n },\n\n scrollToBottom: function() {\n var scrollbar = this.verticalScrollbar;\n this._scrollTo(scrollbar[0].scrollHeight - scrollbar.height());\n },\n\n _scrollWrapperToTop: function() {\n this.wrapper.scrollTop(0);\n },\n\n _scrollWrapperToBottom: function() {\n this.wrapper.scrollTop(this.wrapper[0].scrollHeight);\n },\n\n _scrollWrapperOnColumnResize: function() {\n var that = this;\n var wrapper = this.wrapper;\n var initialScrollTop = wrapper.scrollTop();\n\n if (wrapper[0].scrollWidth > wrapper[0].clientWidth) {\n if ((!that._wrapperScrolled && initialScrollTop) || that._isScrolledToBottom()) {\n wrapper.scrollTop(initialScrollTop + kendo.support.scrollbar());\n that._scrollTop = wrapper.scrollTop();\n that._wrapperScrolled = true;\n }\n } else if (that._wrapperScrolled) {\n if (!that._isWrapperScrolledToBottom()) {\n wrapper.scrollTop(initialScrollTop - kendo.support.scrollbar());\n that._scrollTop = wrapper.scrollTop();\n }\n\n that._wrapperScrolled = false;\n }\n },\n\n _scrollTo: function(scrollTop) {\n var that = this;\n var scrollbar = that.verticalScrollbar;\n\n if (scrollbar.scrollTop() !== scrollTop) {\n that._preventScroll = true;\n }\n\n that.wrapper.scrollTop(scrollTop);\n that._scrollTop = that.wrapper.scrollTop();\n\n scrollbar.scrollTop(scrollTop);\n that._scrollbarTop = scrollbar.scrollTop();\n },\n\n _isScrolledToTop: function() {\n return this.verticalScrollbar.scrollTop() === 0;\n },\n\n _isScrolledToBottom: function() {\n var scrollbar = this.verticalScrollbar;\n var scrollTop = scrollbar.scrollTop();\n\n return (scrollTop > 0 && scrollTop >= parseInt(scrollbar[0].scrollHeight - scrollbar.height(), 10));\n },\n\n _isWrapperScrolledToBottom: function() {\n var wrapper = this.wrapper;\n\n return (wrapper.scrollTop() >= parseInt(wrapper[0].scrollHeight - wrapper.height(), 10));\n },\n\n itemIndex: function(rowIndex) {\n var rangeStart = this._rangeStart || this.dataSource.skip() || 0;\n\n return rangeStart + rowIndex;\n },\n\n position: function(index) {\n var rangeStart = this._rangeStart || this.dataSource.skip() || 0;\n var pageSize = this.dataSource.pageSize();\n var result;\n\n if (index > rangeStart) {\n result = index - rangeStart;\n } else {\n result = rangeStart - index - 1;\n }\n\n return result > pageSize ? pageSize : result;\n },\n\n scrollIntoView: function(row) {\n var container = this.wrapper[0];\n var containerHeight = container.clientHeight;\n var containerScroll = !this._isScrolledToBottom() ? (this._scrollTop || container.scrollTop) : container.scrollTop;\n var elementOffset = row[0].offsetTop;\n var elementHeight = row[0].offsetHeight;\n\n if (containerScroll > elementOffset) {\n this.verticalScrollbar[0].scrollTop -= containerHeight / 2;\n } else if (elementOffset + elementHeight >= containerScroll + containerHeight) {\n this.verticalScrollbar[0].scrollTop += containerHeight / 2;\n }\n },\n\n _fetch: function(firstItemIndex, lastItemIndex, scrollingUp) {\n var that = this,\n dataSource = that.dataSource,\n itemHeight = that.itemHeight,\n take = dataSource.take(),\n rangeStart = that._rangeStart || dataSource.skip() || 0,\n currentSkip = math.floor(firstItemIndex / take) * take,\n fetching = false,\n prefetchAt = 0.33;\n var scrollbar = that.verticalScrollbar;\n var webkitCorrection = browser.webkit ? 1 : 0;\n var total = dataSource._isGroupPaged() ? dataSource.groupsTotal(true) : dataSource.total();\n\n if (firstItemIndex < rangeStart) {\n\n fetching = true;\n rangeStart = math.max(0, lastItemIndex - take);\n that._scrollTop = scrollbar.scrollTop() - (rangeStart * itemHeight);\n that._page(rangeStart, take);\n\n } else if (lastItemIndex >= rangeStart + take && !scrollingUp) {\n\n fetching = true;\n rangeStart = math.min(firstItemIndex, total - take);\n\n //ensure the scrollbar can be scrolled to bottom with mouse drag\n if (scrollbar.scrollTop() >= scrollbar[0].scrollHeight - scrollbar[0].offsetHeight - webkitCorrection) {\n that._scrollTop = that.wrapper[0].scrollHeight - that.wrapper[0].offsetHeight;\n } else if (that.dataSource._isGroupPaged() && firstItemIndex >= total - take) {\n that._scrollTop = that.wrapper[0].scrollHeight - that.wrapper[0].offsetHeight - (that._scrollTop - that._prevScrollTop);\n } else {\n that._scrollTop = itemHeight;\n }\n\n that._page(rangeStart, take);\n\n } else if (!that._fetching && that.options.prefetch) {\n\n if (firstItemIndex < (currentSkip + take) - take * prefetchAt && firstItemIndex > take) {\n dataSource.prefetch(currentSkip - take, take, $.noop);\n }\n if (lastItemIndex > currentSkip + take * prefetchAt) {\n dataSource.prefetch(currentSkip + take, take, $.noop);\n }\n\n }\n return fetching;\n },\n\n fetching: function() {\n return this._fetching;\n },\n\n _page: function(skip, take, callback) {\n var that = this,\n delayLoading = !that.options.prefetch,\n dataSource = that.dataSource,\n isGroupPaged = dataSource._isGroupPaged();\n callback = isFunction(callback) ? callback : $.noop;\n\n if (that.trigger(PAGING, { skip: skip, take: take })) {\n return;\n }\n\n clearTimeout(that._timeout);\n that._fetching = true;\n that._rangeStart = skip;\n\n if ((isGroupPaged && dataSource._groupRangeExists(skip, skip + take)) || (!isGroupPaged && dataSource.inRange(skip, take))) {\n that.trigger(LOAD_START);\n\n dataSource.range(skip, take, function() {\n that.trigger(LOAD_END);\n callback();\n that.trigger(PAGE);\n }, \"page\");\n } else {\n if (!delayLoading) {\n that.trigger(LOAD_START);\n }\n\n that._timeout = setTimeout(function() {\n if (!that._scrolling) {\n\n if (delayLoading) {\n that.trigger(LOAD_START);\n }\n\n dataSource.range(skip, take, function() {\n that.trigger(LOAD_END);\n callback();\n that.trigger(PAGE);\n });\n }\n }, 100);\n }\n },\n\n repaintScrollbar: function(shouldScrollWrapper) {\n var that = this,\n html = \"\",\n maxHeight = that.options.maxScrollHeight,\n dataSource = that.dataSource,\n scrollbar = !kendo.support.kineticScrollNeeded ? kendo.support.scrollbar() : 0,\n wrapperElement = that.wrapper[0],\n totalHeight,\n idx,\n itemHeight;\n var wasScrolledToBottom = that._isScrolledToBottom();\n\n itemHeight = that.itemHeight = that.options.itemHeight() || 0;\n\n var addScrollBarHeight = (wrapperElement.scrollWidth > wrapperElement.offsetWidth) ? scrollbar : 0;\n\n totalHeight = (dataSource._isGroupPaged() ? dataSource.groupsTotal(true) : dataSource.total()) * itemHeight + addScrollBarHeight;\n\n for (idx = 0; idx < math.floor(totalHeight / maxHeight); idx++) {\n html += '
';\n }\n\n if (totalHeight % maxHeight) {\n html += '
';\n }\n\n that.verticalScrollbar.html(html);\n\n if (wasScrolledToBottom && !that._isScrolledToBottom() && !that.dataSource._isGroupPaged()) {\n that.scrollToBottom();\n }\n\n if (typeof(that._scrollTop) !== \"undefined\" && !!shouldScrollWrapper) {\n wrapperElement.scrollTop = that._scrollTop;\n that._scrollWrapperOnColumnResize();\n }\n },\n\n refresh: function(e) {\n var that = this,\n dataSource = that.dataSource,\n rangeStart = that._rangeStart;\n var action = (e || {}).action;\n var shouldScrollWrapper = that._isScrolledToBottom() || !action || (action !== ITEM_CHANGE && action !== REMOVE && action !== SYNC);\n\n that.trigger(LOAD_END);\n clearTimeout(that._timeout);\n\n that.repaintScrollbar(shouldScrollWrapper);\n\n if (that.drag) {\n that.drag.cancel();\n }\n\n if (typeof(rangeStart) !== \"undefined\" && !that._fetching) { // we are rebound from outside local range should be reset\n if (!action || (action !== SYNC && action !== ITEM_CHANGE && action !== \"expandGroup\")) {\n that._rangeStart = dataSource.skip();\n }\n\n if (dataSource.page() === 1 && (!action || (action !== SYNC && action !== ITEM_CHANGE && action !== \"expandGroup\" && action !== \"collapseGroup\"))) {\n // reset the scrollbar position if datasource is filtered\n that.verticalScrollbar[0].scrollTop = 0;\n }\n }\n\n that._fetching = false;\n }\n });\n\n function attrEquals(attrName, attrValue) {\n return \"[\" + kendo.attr(attrName) + \"=\" + attrValue + \"]\";\n }\n\n function groupCells(count) {\n return new Array(count + 1).join(' ');\n }\n\n function stringifyAttributes(attributes) {\n var attr,\n result = \" \";\n\n if (attributes) {\n if (typeof attributes === STRING) {\n return attributes;\n }\n\n for (attr in attributes) {\n if (attributes[attr] !== '') {\n result += attr + '=\"' + attributes[attr] + '\"';\n }\n }\n }\n return result;\n }\n\n var defaultCommands = {\n create: {\n text: \"Add new record\",\n className: \"k-grid-add\",\n iconClass: \"k-icon k-i-plus\"\n },\n cancel: {\n text: \"Cancel changes\",\n className: \"k-grid-cancel-changes\",\n iconClass: \"k-icon k-i-cancel\"\n },\n save: {\n text: \"Save changes\",\n className: \"k-grid-save-changes\",\n iconClass: \"k-icon k-i-check\"\n },\n destroy: {\n text: \"Delete\",\n className: \"k-grid-delete\",\n iconClass: \"k-icon k-i-close\"\n },\n edit: {\n text: \"Edit\",\n className: \"k-grid-edit\",\n iconClass: \"k-icon k-i-edit\"\n },\n update: {\n text: \"Update\",\n className: \"k-grid-update\",\n iconClass: \"k-icon k-i-check\",\n themeColor: \"primary\"\n },\n canceledit: {\n text: \"Cancel\",\n className: \"k-grid-cancel\",\n iconClass: \"k-icon k-i-cancel\"\n },\n excel: {\n text: \"Export to Excel\",\n className: \"k-grid-excel\",\n iconClass: \"k-icon k-i-file-excel\"\n },\n pdf: {\n text: \"Export to PDF\",\n className: \"k-grid-pdf\",\n iconClass: \"k-icon k-i-file-pdf\"\n },\n search: {\n text: \"Search...\",\n className: \"k-grid-search\"\n }\n };\n\n function cursor(context, value) {\n $('th, th .k-grid-filter, th .k-link', context)\n .add(document.body)\n .css('cursor', value);\n }\n\n function reorder(selector, source, dest, before, count) {\n var sourceIndex = source;\n source = $();\n count = count || 1;\n for (var idx = 0; idx < count; idx++) {\n source = source.add(selector.eq(sourceIndex + idx));\n }\n\n if (typeof dest == \"number\") {\n source[before ? \"insertBefore\" : \"insertAfter\"](selector.eq(dest));\n } else {\n source.appendTo(dest);\n }\n }\n\n function elements(lockedContent, content, filter) {\n return $(lockedContent).add(content).find(filter);\n }\n\n function attachCustomCommandEvent(context, container, commands) {\n var idx,\n length,\n command,\n commandName;\n\n commands = !isArray(commands) ? [commands] : commands;\n\n for (idx = 0, length = commands.length; idx < length; idx++) {\n command = commands[idx];\n\n if (isPlainObject(command) && command.click) {\n commandName = command.name || command.text;\n container.on(CLICK + NS, \".k-grid-\" + (commandName || \"\").replace(/\\s/g, \"\"), { commandName: commandName }, command.click.bind(context));\n }\n }\n }\n\n function normalizeColumns(columns, encoded, hide, locked, parentIds) {\n return map(columns, function(column) {\n column = typeof column === STRING ? { field: column } : column;\n\n var hidden;\n column.parentIds = parentIds;\n\n if (!isVisible(column) || hide) {\n column.attributes = addHiddenStyle(column.attributes);\n column.footerAttributes = addHiddenStyle(column.footerAttributes);\n column.headerAttributes = addHiddenStyle(column.headerAttributes);\n hidden = true;\n }\n\n var uid = kendo.guid();\n if (locked && !column.locked) {\n column.locked = locked;\n }\n\n column.headerAttributes = extend({ headers: parentIds }, column.headerAttributes);\n if (!column.headerAttributes.id) {\n column.headerAttributes = extend({ id: uid }, column.headerAttributes);\n column.uid = uid;\n } else {\n column.uid = uid = column.headerAttributes.id;\n }\n\n if (column.columns) {\n column.columns = normalizeColumns(column.columns, encoded, hidden, column.locked, parentIds ? (parentIds + \" \" + uid) : uid);\n }\n return extend({ encoded: encoded, hidden: hidden, locked: locked }, column);\n });\n }\n\n function columnParent(column, columns) {\n var parents = [];\n columnParents(column, columns, parents);\n return parents[parents.length - 1];\n }\n\n function columnParents(column, columns, parents) {\n parents = parents || [];\n\n for (var idx = 0; idx < columns.length; idx++) {\n if (column === columns[idx]) {\n return true;\n } else if (columns[idx].columns) {\n var inserted = parents.length;\n parents.push(columns[idx]);\n if (!columnParents(column, columns[idx].columns, parents)) {\n parents.splice(inserted, parents.length - inserted);\n } else {\n return true;\n }\n }\n }\n return false;\n }\n\n function setColumnVisibility(column, visible) {\n setVisibility(column, visible, visible);\n }\n\n function setVisibility(column, visible, show) {\n var method = show ? removeHiddenStyle : addHiddenStyle;\n column.hidden = !visible;\n column.attributes = method(column.attributes);\n column.footerAttributes = method(column.footerAttributes);\n column.headerAttributes = method(column.headerAttributes);\n }\n\n\n function setColumnMediaVisibility(column, visible) {\n setColumnMatchesMedia(column);\n var hideByMedia = column._hideByMedia;\n setVisibility(column, visible, hideByMedia ? column.matchesMedia : visible);\n }\n\n function setColumnMatchesMedia(column) {\n column.matchesMedia = columnMatchesMedia(column);\n }\n\n function columnMatchesMedia(column) {\n return column && (isUndefined(column.media) || (!isUndefined(column.media) && kendo.matchesMedia(column.media)));\n }\n\n function isCellVisible() {\n return this.style.display !== \"none\";\n }\n\n function isElementVisible(element) {\n return $(element)[0].style.display !== \"none\";\n }\n\n function isVisible(column) {\n return visibleColumns([column]).length > 0;\n }\n\n function visibleColumns(columns) {\n return grep(columns, function(column) {\n var result = !column.hidden && column.matchesMedia !== false;\n\n if (result && column.columns) {\n result = visibleColumns(column.columns).length > 0;\n }\n return result;\n });\n }\n\n function columnsWithMedia(columns) {\n var result = [];\n var column;\n\n for (var i = 0; i < columns.length; i++) {\n column = columns[i];\n\n if (!isUndefined(column.media)) {\n if (!isUndefined(column.minScreenWidth)) {\n throw new Error(\"Using 'media' and 'minScreenWidth' options at the same time is not supported.\");\n }\n\n result.push(column);\n }\n\n if (column.columns) {\n result = result.concat(columnsWithMedia(column.columns));\n }\n }\n\n return result;\n }\n\n function isUndefined(value) {\n return typeof value === \"undefined\";\n }\n\n function toJQuery(elements) {\n return $(elements).map(function() { return this.toArray(); });\n }\n\n function updateCellRowSpan(cell, columns, sourceLockedColumnsCount) {\n var lockedColumnDepth = depth(lockedColumns(columns));\n var nonLockedColumnDepth = depth(nonLockedColumns(columns));\n\n var rowSpan = cell.rowSpan;\n if (sourceLockedColumnsCount) {\n if (lockedColumnDepth > nonLockedColumnDepth) {\n cell.rowSpan = (rowSpan - (lockedColumnDepth - nonLockedColumnDepth)) || 1;\n } else {\n cell.rowSpan = rowSpan + (nonLockedColumnDepth - lockedColumnDepth);\n }\n } else {\n if (lockedColumnDepth > nonLockedColumnDepth) {\n cell.rowSpan = rowSpan + (lockedColumnDepth - nonLockedColumnDepth);\n } else {\n cell.rowSpan = (rowSpan - (nonLockedColumnDepth - lockedColumnDepth)) || 1;\n }\n }\n }\n\n function findColumnByField(columns, field) {\n for (var i = 0; i < columns.length; i++) {\n if (columns[i].field == field) {\n return columns[i];\n }\n }\n }\n\n function moveCellsBetweenContainers(sources, target, leafs, columns, container, destination, groups, action) {\n var sourcesDepth = depth(sources);\n var targetDepth = depth([target]);\n\n if (sourcesDepth > targetDepth) {\n var groupCells = new Array(groups + 1).join(' ');\n var rows = destination.children(\":not(.k-filter-row)\");\n $(new Array((sourcesDepth - targetDepth) + 1).join(\"\" + groupCells + \"\")).insertAfter(rows.last());\n }\n\n addRowSpanValue(destination, sourcesDepth - targetDepth);\n\n moveCells(leafs, columns, container, destination, action);\n }\n\n function updateCellIndex(thead, columns, offset) {\n offset = offset || 0;\n\n var position;\n var cell;\n var allColumns = columns;\n columns = leafColumns(columns);\n\n var cells = {};\n var rows = thead.find(\">tr:not(.k-filter-row)\");\n\n var filter = function() {\n var el = $(this);\n return !el.hasClass(\"k-group-cell\") && !el.hasClass(\"k-hierarchy-cell\");\n };\n\n for (var idx = 0, length = columns.length; idx < length; idx++) {\n position = columnPosition(columns[idx], allColumns);\n\n if (!cells[position.row]) {\n cells[position.row] = rows.eq(position.row)\n .find(\".k-header\")\n .filter(filter);\n }\n\n cell = cells[position.row].eq(position.cell);\n cell.attr(kendo.attr(\"index\"), offset + idx);\n }\n\n\n return columns.length;\n }\n\n function depth(columns) {\n var result = 1;\n var max = 0;\n\n for (var idx = 0; idx < columns.length; idx++) {\n if (columns[idx].columns) {\n var temp = depth(columns[idx].columns);\n if (temp > max) {\n max = temp;\n }\n }\n }\n return result + max;\n }\n\n function moveCells(leafs, columns, container, destination, action) {\n var sourcePosition = columnVisiblePosition(leafs[0], columns);\n\n var ths = container.find(\">tr:not(.k-filter-row)\").eq(sourcePosition.row).children(\"th.k-header\");\n\n var t = $();\n var sourceIndex = sourcePosition.cell;\n var idx;\n\n for (idx = 0; idx < leafs.length; idx++) {\n t = t.add(ths.eq(sourceIndex + idx));\n }\n\n destination.find(\">tr:not(.k-filter-row)\").eq(sourcePosition.row)[action](t);\n\n var children = [];\n for (idx = 0; idx < leafs.length; idx++) {\n if (leafs[idx].columns) {\n children = children.concat(leafs[idx].columns);\n }\n }\n\n if (children.length) {\n moveCells(children, columns, container, destination, action);\n }\n }\n\n function columnPosition(column, columns, row, cellCounts) {\n var result;\n var idx;\n\n row = row || 0;\n cellCounts = cellCounts || {};\n cellCounts[row] = cellCounts[row] || 0;\n\n for (idx = 0; idx < columns.length; idx++) {\n if (columns[idx] == column) {\n result = { cell: cellCounts[row], row: row };\n break;\n } else if (columns[idx].columns) {\n result = columnPosition(column, columns[idx].columns, row + 1, cellCounts);\n if (result) {\n break;\n }\n }\n\n cellCounts[row]++;\n }\n return result;\n }\n function findParentColumnWithChildren(columns, index, source, rtl) {\n var target;\n var locked = !!source.locked;\n var targetLocked;\n\n do {\n target = columns[index];\n index += rtl ? 1 : -1;\n targetLocked = !!target.locked;\n } while (target && index > -1 && index < columns.length && target != source && !target.columns && targetLocked === locked);\n\n return target;\n }\n\n function findReorderTarget(columns, target, source, before, masterColumns) {\n if (target.columns) {\n target = target.columns;\n return target[before ? 0 : target.length - 1];\n } else {\n var parent = columnParent(target, columns);\n var parentColumns;\n\n if (parent) {\n parentColumns = parent.columns;\n } else {\n parentColumns = columns;\n }\n\n var index = inArray(target, parentColumns);\n if (index === 0 && before) {\n index++;\n } else if ((index == parentColumns.length - 1 && !before) || (!source.locked && !target.columns && !before)) {\n index--;\n } else if (index > 0 || (index === 0 && !before)) {\n index++;\n }\n\n var sourceIndex = inArray(source, parentColumns);\n target = findParentColumnWithChildren(parentColumns, index, source, sourceIndex > index);\n var targetIndex = inArray(target, masterColumns);\n if (target.columns && (!targetIndex || targetIndex === parentColumns.length - 1)) {\n return null;\n }\n\n if (target && target != source && target.columns) {\n return findReorderTarget(columns, target, source, before, masterColumns);\n }\n }\n return null;\n }\n\n function columnVisiblePosition(column, columns, row, cellCounts) {\n var result;\n var idx;\n\n row = row || 0;\n cellCounts = cellCounts || {};\n cellCounts[row] = cellCounts[row] || 0;\n\n for (idx = 0; idx < columns.length; idx++) {\n if (columns[idx] == column) {\n result = { cell: cellCounts[row], row: row };\n break;\n } else if (columns[idx].columns) {\n result = columnVisiblePosition(column, columns[idx].columns, row + 1, cellCounts);\n if (result) {\n break;\n }\n }\n\n if (!columns[idx].hidden) {\n cellCounts[row]++;\n }\n }\n return result;\n }\n\n function flatColumnsInDomOrder(columns) {\n var result = flatColumns(lockedColumns(columns));\n return result.concat(flatColumns(nonLockedColumns(columns)));\n }\n\n function targetParentContainerIndex(flatColumns, columns, sourceIndex, targetIndex) {\n var column = flatColumns[sourceIndex];\n var target = flatColumns[targetIndex];\n\n var parent = columnParent(column, columns);\n columns = parent ? parent.columns : columns;\n\n return inArray(target, columns);\n }\n\n function flatColumns(columns) {\n var result = [];\n var children = [];\n for (var idx = 0; idx < columns.length; idx++) {\n result.push(columns[idx]);\n if (columns[idx].columns) {\n children = children.concat(columns[idx].columns);\n }\n\n }\n if (children.length) {\n result = result.concat(flatColumns(children));\n }\n return result;\n }\n\n function hiddenLeafColumnsCount(columns) {\n var counter = 0;\n var column;\n\n for (var idx = 0; idx < columns.length; idx++) {\n column = columns[idx];\n\n if (column.columns) {\n counter += hiddenLeafColumnsCount(column.columns);\n } else if (column.hidden) {\n counter++;\n }\n }\n return counter;\n }\n\n function sumWidths(cols) {\n var width = 0;\n\n for (var idx = 0, length = cols.length; idx < length; idx++) {\n if (!cols[idx].hidden) {\n width += parseInt(cols[idx].width, 10);\n }\n }\n\n return width;\n }\n\n function columnsWidth(cols) {\n var colWidth, width = 0;\n\n for (var idx = 0, length = cols.length; idx < length; idx++) {\n colWidth = cols[idx].style.width;\n if (colWidth && colWidth.indexOf(\"%\") == -1) {\n width += parseInt(colWidth, 10);\n }\n }\n\n return width;\n }\n\n function removeRowSpanValue(container, count) {\n var cells = container.find(\"tr:not(.k-filter-row) th:not(.k-group-cell,.k-hierarchy-cell)\");\n\n var rowSpan;\n for (var idx = 0; idx < cells.length; idx++) {\n rowSpan = cells[idx].rowSpan;\n if (rowSpan > 1) {\n cells[idx].rowSpan = (rowSpan - count) || 1;\n }\n }\n }\n\n function addRowSpanValue(container, count) {\n var cells = container.find(\"tr:not(.k-filter-row) th:not(.k-group-cell,.k-hierarchy-cell)\");\n\n for (var idx = 0; idx < cells.length; idx++) {\n cells[idx].rowSpan += count;\n }\n }\n\n function removeEmptyRows(container) {\n var rows = container.find(\"tr:not(.k-filter-row)\");\n\n var emptyRowsCount = rows.filter(function() {\n return !$(this).children().length;\n }).remove().length;\n\n var cells = rows.find(\"th:not(.k-group-cell,.k-hierarchy-cell)\");\n\n for (var idx = 0; idx < cells.length; idx++) {\n if (cells[idx].rowSpan > 1) {\n cells[idx].rowSpan -= emptyRowsCount;\n }\n }\n return rows.length - emptyRowsCount;\n }\n\n function mapColumnToCellRows(columns, cells, rows, rowIndex, offset) {\n var idx, row, length, children = [];\n\n for (idx = 0, length = columns.length; idx < length; idx++) {\n row = rows[rowIndex] || [];\n row.push(cells.eq(offset + idx));\n rows[rowIndex] = row;\n\n if (columns[idx].columns) {\n children = children.concat(columns[idx].columns);\n }\n }\n\n if (children.length) {\n mapColumnToCellRows(children, cells, rows, rowIndex + 1, offset + columns.length);\n }\n }\n\n function setLeftAndRightStyles(element, left, right) {\n element.css({\n \"left\": left,\n \"right\": right\n });\n }\n\n function createColumnAttribute(column, attribute, property) {\n column[attribute] = column[attribute] || {};\n column[attribute][property] = column[attribute][property] || \"\";\n }\n\n function addColumnAttribute(column, attribute, property, value) {\n createColumnAttribute(column, attribute, property);\n\n if (column[attribute][property] !== \"\") {\n column[attribute][property] += \" \" + value;\n } else {\n column[attribute][property] = value;\n }\n }\n\n function removeColumnAttribute(column, attribute, property, value) {\n createColumnAttribute(column, attribute, property);\n\n column[attribute][property] = column[attribute][property].replace(value, \"\");\n }\n\n function lockedColumns(columns) {\n return grep(columns, function(column) {\n return column.locked;\n });\n }\n\n function nonLockedColumns(columns) {\n return grep(columns, function(column) {\n return !column.locked;\n });\n }\n\n function stickyColumns(columns) {\n return grep(columns, function(column) {\n return column.sticky && !column.locked;\n });\n }\n\n function visibleStickyColumns(columns) {\n return grep(columns, function(column) {\n return column.sticky && !column.locked && isVisible(column);\n });\n }\n\n function visibleNonLockedColumns(columns) {\n return grep(columns, function(column) {\n return !column.locked && isVisible(column);\n });\n }\n\n function visibleLockedColumns(columns) {\n return grep(columns, function(column) {\n return column.locked && isVisible(column);\n });\n }\n\n function visibleLeafColumns(columns) {\n var result = [];\n\n for (var idx = 0; idx < columns.length; idx++) {\n if (columns[idx].hidden) {\n continue;\n }\n\n if (columns[idx].columns) {\n result = result.concat(visibleLeafColumns(columns[idx].columns));\n } else {\n result.push(columns[idx]);\n }\n }\n\n return result;\n }\n\n function visibleLeafExportColumns(columns) {\n var result = [];\n\n for (var idx = 0; idx < columns.length; idx++) {\n if (columns[idx].hidden) {\n continue;\n }\n\n if (columns[idx].columns) {\n result = result.concat(visibleLeafColumns(columns[idx].columns));\n } else {\n result.push({\n field: columns[idx].field,\n width: columns[idx].width,\n values: columns[idx].values\n });\n }\n }\n\n return result;\n }\n\n function childColumns(columns) {\n var result = [];\n\n for (var idx = 0; idx < columns.length; idx++) {\n if (columns[idx].columns) {\n result = result.concat(columns[idx].columns);\n }\n }\n\n return result;\n }\n\n function visibleChildColumns(columns) {\n var result = childColumns(columns);\n\n result = result.filter(function(e) {\n return !e.hidden;\n });\n\n return result;\n }\n\n function leafColumns(columns) {\n var result = [];\n\n for (var idx = 0; idx < columns.length; idx++) {\n if (!columns[idx].columns) {\n result.push(columns[idx]);\n continue;\n }\n result = result.concat(leafColumns(columns[idx].columns));\n }\n\n return result;\n }\n\n function getColumnsFields(columns) {\n var result = [];\n columns = leafColumns(columns);\n\n for (var idx = 0; idx < columns.length; idx++) {\n if (typeof columns[idx] === \"string\") {\n result.push(columns[idx]);\n } else if (columns[idx].field) {\n result.push(columns[idx].field);\n }\n }\n return result;\n }\n\n function editField(column) {\n return {\n field: column.field,\n title: column.title,\n format: column.format,\n editor: column.editor,\n values: column.values,\n editorOptions: extend(true, { format: column.format }, column.editorOptions),\n label: column.title || column.field || \"\"\n };\n }\n\n function leafDataCells(container) {\n var rows = container.find(\">tr:not(.k-filter-row)\");\n\n var filter = function() {\n var el = $(this);\n return !el.hasClass(\"k-group-cell\") && !el.hasClass(\"k-hierarchy-cell\");\n };\n\n var cells = $();\n if (rows.length > 1) {\n cells = rows.find(\"th\")\n .filter(filter)\n .filter(function() { return this.rowSpan > 1; });\n }\n\n cells = cells.add(rows.last().find(\"th\").filter(filter));\n\n var indexAttr = kendo.attr(\"index\");\n cells.sort(function(a, b) {\n a = $(a);\n b = $(b);\n\n var indexA = a.attr(indexAttr);\n var indexB = b.attr(indexAttr);\n\n if (indexA === undefined) {\n indexA = $(a).index();\n }\n if (indexB === undefined) {\n indexB = $(b).index();\n }\n\n indexA = parseInt(indexA, 10);\n indexB = parseInt(indexB, 10);\n return indexA > indexB ? 1 : (indexA < indexB ? -1 : 0);\n });\n\n return cells;\n }\n\n function parentColumnsCells(cell) {\n var container = cell.closest(\"table\");\n var result = $().add(cell);\n\n var row = cell.closest(\"tr\");\n var headerRows = container.find(\"tr:not(.k-filter-row)\");\n var level = headerRows.index(row);\n if (level > 0) {\n var parent = headerRows.eq(level - 1);\n var parentCellsWithChildren = parent.find(\"th:not(.k-group-cell,.k-hierarchy-cell)\").filter(function() {\n return !$(this).attr(\"rowspan\");\n });\n\n var offset = 0;\n var index = row.find(\"th:not(.k-group-cell,.k-hierarchy-cell)\").index(cell);\n\n var prevCells = cell.prevAll(\":not(.k-group-cell,.k-hierarchy-cell)\").filter(function() {\n return this.colSpan > 1;\n });\n\n for (var idx = 0; idx < prevCells.length; idx++) {\n offset += prevCells[idx].colSpan || 1;\n }\n\n index += Math.max(offset - 1, 0);\n\n offset = 0;\n for (idx = 0; idx < parentCellsWithChildren.length; idx++) {\n var parentCell = parentCellsWithChildren.eq(idx);\n if (parentCell.attr(\"data-colspan\")) {\n offset += parentCell[0].getAttribute(\"data-colspan\");\n } else {\n offset += 1;\n }\n if (index >= idx && index < offset) {\n result = parentColumnsCells(parentCell).add(result);\n break;\n }\n }\n }\n return result;\n }\n\n function childColumnsCells(cell) {\n var container = cell.closest(\"thead\");\n var result = $().add(cell);\n\n var row = cell.closest(\"tr\");\n var headerRows = container.find(\"tr:not(.k-filter-row)\");\n var level = headerRows.index(row) + cell[0].rowSpan;\n var colSpanAttr = kendo.attr(\"colspan\");\n\n if (level <= headerRows.length - 1) {\n var child = row.next();\n var prevCells = cell.prevAll(\":not(.k-group-cell,.k-hierarchy-cell)\");\n\n var idx;\n\n prevCells = prevCells.filter(function() {\n return !this.rowSpan || this.rowSpan === 1;\n });\n\n var offset = 0;\n\n for (idx = 0; idx < prevCells.length; idx++) {\n offset += parseInt(prevCells.eq(idx).attr(colSpanAttr), 10) || 1;\n }\n\n var cells = child.find(\"th:not(.k-group-cell,.k-hierarchy-cell)\");\n var colSpan = parseInt(cell.attr(colSpanAttr), 10) || 1;\n\n idx = 0;\n\n while (idx < colSpan) {\n child = cells.eq(idx + offset);\n result = result.add(childColumnsCells(child));\n var value = parseInt(child.attr(colSpanAttr), 10);\n if (value > 1) {\n colSpan -= value - 1;\n }\n idx++;\n }\n }\n\n return result;\n }\n\n function appendContent(tbody, table, html, empty) {\n var placeholder,\n tmp = tbody;\n\n // necessary for AngularJS to cleanup its guts.\n if (empty) {\n tbody.empty();\n }\n\n if (tbodySupportsInnerHtml) {\n tbody[0].innerHTML = html;\n } else {\n placeholder = document.createElement(\"div\");\n placeholder.innerHTML = \"\" + html + \"
\";\n tbody = placeholder.firstChild.firstChild;\n table[0].replaceChild(tbody, tmp[0]);\n tbody = $(tbody);\n }\n return tbody;\n }\n\n function addHiddenStyle(attr) {\n attr = attr || {};\n var style = attr.style;\n\n if (!style) {\n style = \"display:none\";\n } else {\n style = style.replace(/display:[^;]*/i, \"display:none\");\n if (!style.match(/display:/i)) {\n style = style.replace(/(.*)?/i, \"display:none;$1\");\n }\n }\n\n return extend({}, attr, { style: style });\n }\n\n function hasHiddenStyle(attr) {\n attr = attr || {};\n var style = attr.style || \"\";\n\n return style.indexOf(\"display:none\") !== -1;\n }\n\n function removeHiddenStyle(attr) {\n attr = attr || {};\n var style = attr.style;\n\n if (style) {\n attr.style = style.replace(/(display\\s*:\\s*none\\s*;?)*/ig, \"\");\n }\n\n return attr;\n }\n\n function normalizeCols(table, visibleColumns, hasDetails, groups) {\n var colgroup = table.find(\">colgroup\"),\n width,\n cols = map(visibleColumns, function(column) {\n width = column.width;\n if (width && parseInt(width, 10) !== 0) {\n return kendo.format('', typeof width === STRING ? width : width + \"px\");\n }\n\n return \"\";\n });\n\n if (hasDetails || colgroup.find(\".k-hierarchy-col\").length) {\n cols.splice(0, 0, '');\n }\n\n if (colgroup.length) {\n colgroup.remove();\n }\n\n colgroup = $(new Array(groups + 1).join('') + cols.join(\"\"));\n if (!colgroup.is(\"colgroup\")) {\n colgroup = $(\"\").append(colgroup);\n }\n\n table.prepend(colgroup);\n }\n\n function normalizeHeaderCells(container, columns) {\n var lastIndex = 0;\n var idx , len;\n var th = container.find(\"th:not(.k-group-cell)\");\n\n for (idx = 0, len = columns.length; idx < len; idx ++) {\n if (columns[idx].locked) {\n th.eq(idx).insertBefore(th.eq(lastIndex));\n th = container.find(\"th:not(.k-group-cell)\");\n lastIndex ++;\n }\n }\n }\n\n function convertToObject(array) {\n var result = {},\n item,\n idx,\n length;\n\n for (idx = 0, length = array.length; idx < length; idx++) {\n item = array[idx];\n result[item.value] = item.text;\n }\n\n return result;\n }\n\n function formatGroupValue(value, format, columnValues, encoded) {\n var isForeignKey = columnValues && columnValues.length && isPlainObject(columnValues[0]) && \"value\" in columnValues[0],\n groupValue = isForeignKey ? convertToObject(columnValues)[value] : value;\n\n groupValue = groupValue != null ? groupValue : \"\";\n\n return format ? kendo.format(format, groupValue) : (encoded === false ? groupValue : kendo.htmlEncode(groupValue));\n }\n\n function setCellVisibility(cells, index, visible) {\n var pad = 0,\n state,\n cell = cells[pad];\n\n while (cell) {\n state = visible ? true : cell.style.display !== \"none\";\n\n if (state && !nonDataCellsRegExp.test(cell.className) && --index < 0) {\n cell.style.display = visible ? \"\" : \"none\";\n break;\n }\n\n cell = cells[++pad];\n }\n }\n\n function hideColumnCells(rows, columnIndex) {\n var idx = 0,\n length = rows.length,\n cell, row;\n\n for ( ; idx < length; idx += 1) {\n row = rows.eq(idx);\n if (row.is(\".k-grouping-row,.k-detail-row\")) {\n cell = row.children(\":not(.k-group-cell):first,.k-detail-cell\").last();\n cell.attr(\"colspan\", parseInt(cell.attr(\"colspan\"), 10) - 1);\n } else {\n if (row.hasClass(\"k-grid-edit-row\") && (cell = row.children(\".k-edit-container\")[0])) {\n cell = $(cell);\n cell.attr(\"colspan\", parseInt(cell.attr(\"colspan\"), 10) - 1);\n cell.find(\"col\").eq(columnIndex).remove();\n row = cell.find(\"tr\").first();\n }\n\n setCellVisibility(row[0].cells, columnIndex, false);\n }\n }\n }\n\n function groupRows(data) {\n var result = [];\n var item;\n\n for (var idx = 0; idx < data.length; idx++) {\n item = data[idx];\n if (!(\"field\" in item && \"value\" in item && \"items\" in item)) {\n break;\n }\n\n result.push(item);\n\n if (item.hasSubgroups) {\n result = result.concat(groupRows(item.items));\n }\n }\n\n return result;\n }\n\n function groupFooters(data) {\n var result = [];\n var item;\n\n for (var idx = 0; idx < data.length; idx++) {\n item = data[idx];\n if (!(\"field\" in item && \"value\" in item && \"items\" in item)) {\n break;\n }\n\n if (item.hasSubgroups) {\n result = result.concat(groupFooters(item.items));\n }\n\n result.push(item.aggregates);\n }\n\n return result;\n }\n\n function showColumnCells(rows, columnIndex) {\n var idx = 0,\n length = rows.length,\n cell, row, columns;\n\n for ( ; idx < length; idx += 1) {\n row = rows.eq(idx);\n if (row.is(\".k-grouping-row,.k-detail-row\")) {\n cell = row.children(\":not(.k-group-cell):first,.k-detail-cell\").last();\n cell.attr(\"colspan\", parseInt(cell.attr(\"colspan\"), 10) + 1);\n } else {\n if (row.hasClass(\"k-grid-edit-row\") && (cell = row.children(\".k-edit-container\")[0])) {\n cell = $(cell);\n cell.attr(\"colspan\", parseInt(cell.attr(\"colspan\"), 10) + 1);\n normalizeCols(cell.find(\">form>table\"), visibleColumns(columns), false, 0);\n row = cell.find(\"tr\").first();\n }\n\n setCellVisibility(row[0].cells, columnIndex, true);\n }\n }\n }\n\n function updateColspan(toAdd, toRemove, num) {\n num = num || 1;\n\n var item, idx, length;\n for (idx = 0, length = toAdd.length; idx < length; idx++) {\n item = toAdd.eq(idx).children().last();\n item.attr(\"colspan\", parseInt(item.attr(\"colspan\"), 10) + num);\n\n item = toRemove.eq(idx).children().last();\n item.attr(\"colspan\", parseInt(item.attr(\"colspan\"), 10) - num);\n }\n }\n\n function tableWidth(table) {\n var idx, length, width = 0;\n var cols = table.find(\">colgroup>col\");\n\n for (idx = 0, length = cols.length; idx < length; idx += 1) {\n width += parseInt(cols[idx].style.width, 10);\n }\n\n return width;\n }\n\n var Grid = kendo.ui.DataBoundWidget.extend({\n init: function(element, options, events) {\n var that = this;\n\n options = isArray(options) ? { dataSource: options } : options;\n\n Widget.fn.init.call(that, element, options);\n\n if (events) {\n that._events = events;\n }\n\n isRtl = kendo.support.isRtl(element);\n\n that._element();\n\n that._ariaId();\n\n that._columns($.extend(true, [], that.options.columns));\n\n if (that._foreignKeyPromises) {\n $.when.apply(null, that._foreignKeyPromises)\n .then(function() {\n that._foreignKeyPromises = null;\n that._continueInit();\n });\n } else {\n that._continueInit();\n }\n },\n\n _continueInit: function() {\n var that = this;\n\n that._dataSource();\n\n that._stickyColumns();\n\n that._tbody();\n\n that._thead();\n\n that._groupable();\n\n that._toolbar();\n\n that._pageable();\n\n that._setContentHeight();\n\n that._templates();\n\n that._navigatable();\n\n that._selectable();\n\n that._clipboard();\n\n that._details();\n\n that._editable();\n\n that._attachCustomCommandsEvent();\n\n that._adaptiveColumns();\n\n that._minScreenSupport();\n\n if (that.options.autoBind) {\n that.dataSource.fetch();\n } else {\n that._group = that._groups() > 0;\n that._footer();\n }\n\n if (that.lockedContent) {\n that.wrapper.addClass(\"k-grid-lockedcolumns\");\n that._resizeHandler = function() {\n that.resize();\n };\n $(window).on(\"resize\" + NS, that._resizeHandler);\n }\n\n kendo.notify(that);\n },\n\n events: [\n CHANGE,\n \"dataBinding\",\n \"cancel\",\n DATABOUND,\n DETAILEXPAND,\n DETAILCOLLAPSE,\n DETAILINIT,\n FILTERMENUINIT,\n FILTERMENUOPEN,\n COLUMNMENUINIT,\n COLUMNMENUOPEN,\n EDIT,\n BEFOREEDIT,\n SAVE,\n REMOVE,\n SAVECHANGES,\n CELLCLOSE,\n COLUMNRESIZE,\n COLUMNREORDER,\n COLUMNSHOW,\n COLUMNHIDE,\n COLUMNLOCK,\n COLUMNUNLOCK,\n COLUMNSTICK,\n COLUMNUNSTICK,\n ROWREORDER,\n NAVIGATE,\n \"page\",\n \"sort\",\n \"filter\",\n \"group\",\n \"groupExpand\",\n \"groupCollapse\",\n \"kendoKeydown\"\n ],\n\n setDataSource: function(dataSource) {\n var that = this;\n var scrollable = that.options.scrollable;\n var scrollableContent;\n\n that.options.dataSource = dataSource;\n\n that._dataSource();\n\n that._pageable();\n\n that._thead();\n\n if (scrollable) {\n if (scrollable.virtual) {\n scrollableContent = that.content.find(\">.k-virtual-scrollable-wrap\");\n kendo.scrollLeft(scrollableContent, leftMostPosition(scrollableContent, isRtl));\n } else {\n scrollableContent = that.tbody;\n kendo.scrollLeft(that.content, leftMostPosition(scrollableContent, isRtl));\n }\n }\n\n if (that.options.groupable) {\n that._groupable();\n }\n\n if (that.virtualScrollable) {\n that.virtualScrollable.setDataSource(that.options.dataSource);\n }\n\n if (that.options.navigatable) {\n that._navigatable();\n }\n\n if (that.options.selectable) {\n that._selectable();\n }\n\n if (that.options.autoBind) {\n that.dataSource.fetch();\n } else {\n that._footer();\n }\n },\n\n options: {\n name: \"Grid\",\n columns: [],\n toolbar: null,\n autoBind: true,\n filterable: false,\n scrollable: true,\n sortable: false,\n selectable: false,\n allowCopy: false,\n navigatable: false,\n pageable: false,\n persistSelection: false,\n editable: false,\n encodeTitles: false,\n groupable: false,\n rowTemplate: \"\",\n altRowTemplate: \"\",\n search: false,\n noRecords: false,\n dataSource: {},\n height: null,\n resizable: false,\n reorderable: false,\n columnMenu: false,\n detailTemplate: null,\n columnResizeHandleWidth: 3,\n mobile: \"\",\n loaderType: \"loadingPanel\",\n messages: {\n editable: {\n cancelDelete: CANCELDELETE,\n confirmation: DELETECONFIRM,\n confirmDelete: CONFIRMDELETE\n },\n commands: {\n create: defaultCommands.create.text,\n cancel: defaultCommands.cancel.text,\n save: defaultCommands.save.text,\n destroy: defaultCommands.destroy.text,\n edit: defaultCommands.edit.text,\n update: defaultCommands.update.text,\n canceledit: defaultCommands.canceledit.text,\n excel: defaultCommands.excel.text,\n pdf: defaultCommands.pdf.text,\n search: defaultCommands.search.text\n },\n noRecords: NORECORDS,\n expandCollapseColumnHeader: \"\",\n groupHeader: \"Press ctrl + space to group\",\n ungroupHeader: \"Press ctrl + space to ungroup\",\n itemsSelected: \"items selected\",\n dragHandleLabel: \"Drag row\",\n toolbarLabel: \"grid toolbar\",\n groupingHeaderLabel: \"grid grouping header\",\n filterCellTitle: \"filter cell\"\n },\n width: null\n },\n\n destroy: function() {\n var that = this,\n element;\n\n that._angularItems(\"cleanup\");\n that._destroyColumnAttachments();\n\n Widget.fn.destroy.call(that);\n\n if (this._navigatableTables) {\n this._navigatableTables.off(NS);\n this._navigatableTables = null;\n this._headertables = null;\n }\n\n if (that._resizeHandler) {\n $(window).off(\"resize\" + NS, that._resizeHandler);\n }\n\n if (that.pager && that.pager.element) {\n that.pager.destroy();\n }\n\n if (that.timer) {\n clearTimeout(that.timer);\n }\n\n if (that._progressTimeOut) {\n clearTimeout(that._progressTimeOut);\n }\n\n if (that._collapseGroupsTimeOut) {\n clearTimeout(that._collapseGroupsTimeOut);\n }\n\n if (that._endlessFetchTimeOut) {\n clearTimeout(that._endlessFetchTimeOut);\n }\n\n that.pager = null;\n\n that._destroyGroupable();\n\n if (that.options.reorderable === true || (that.options.reorderable && that.options.reorderable.columns)) {\n that.wrapper.data(\"kendoReorderable\").destroy();\n }\n\n if (that._hasReorderableRows()) {\n that.tbody.data(\"kendoReorderable\").destroy();\n }\n\n if (that.selectable && that.selectable.element) {\n that.selectable.destroy();\n\n that.clearArea();\n that._selectedIds = null;\n\n if (that.copyHandler) {\n that.wrapper.off(\"keydown\", that.copyHandler);\n that.unbind(that.copyHandler);\n }\n if (that.updateClipBoardState) {\n that.unbind(that.updateClipBoardState);\n that.updateClipBoardState = null;\n }\n if (that.clearAreaHandler) {\n that.wrapper.off(\"keyup\", that.clearAreaHandler);\n }\n }\n\n that.selectable = null;\n\n if (that.resizable) {\n that.resizable.destroy();\n\n if (that._resizeUserEvents) {\n if (that._resizeHandleDocumentClickHandler) {\n $(document).off(\"click\", that._resizeHandleDocumentClickHandler);\n }\n that._resizeUserEvents.destroy();\n that._resizeUserEvents = null;\n }\n that.resizable = null;\n }\n\n that._destroyVirtualScrollable();\n\n if (that.editableUserEvents) {\n that.editableUserEvents.destroy();\n that.editableUserEvents = null;\n }\n\n if (that._lockedContentUserEvents) {\n that._lockedContentUserEvents.destroy();\n that._lockedContentUserEvents = null;\n }\n\n that._destroyEditable();\n\n if (that.dataSource) {\n that.dataSource.unbind(CHANGE, that._refreshHandler)\n .unbind(PROGRESS, that._progressHandler)\n .unbind(ERROR, that._errorHandler)\n .unbind(SORT, that._clearSortClasses);\n\n that._refreshHandler = that._progressHandler = that._errorHandler = that._sortHandler = null;\n }\n\n element = that.element\n .add(that.wrapper)\n .add(that.table)\n .add(that.thead)\n .add(that.wrapper.find(\">.k-grid-toolbar\"));\n\n if (that.content) {\n element = element\n .add(that.content)\n .add(that.content.find(\">.k-virtual-scrollable-wrap\"));\n }\n\n if (that.lockedHeader) {\n that._removeLockedContainers();\n }\n\n if (that.pane) {\n that.pane.destroy();\n }\n\n if (that._isMobile) {\n that.wrapper.off(\"transitionend\" + NS);\n that.wrapper.off(\"contextmenu\" + NS);\n }\n\n if (that.minScreenResizeHandler) {\n $(window).off(\"resize\", that.minScreenResizeHandler);\n }\n\n that._detachColumnMediaResizeHandler();\n\n if (that._draggableInstance && that._draggableInstance.element) {\n that._draggableInstance.destroy();\n }\n\n that._draggableInstance = null;\n\n if (that._draggableRowsInstance && that._draggableRowsInstance.element) {\n that._draggableRowsInstance.destroy();\n }\n\n that._draggableRowsInstance = null;\n\n element.off(NS);\n\n kendo.destroy(that.wrapper);\n\n that.rowTemplate =\n that.altRowTemplate =\n that.lockedRowTemplate =\n that.lockedAltRowTemplate =\n that.detailTemplate =\n that.footerTemplate =\n that.groupFooterTemplate =\n that.lockedGroupFooterTemplate =\n that.noRecordsTemplate = null;\n\n that.scrollables =\n that.thead =\n that.tbody =\n that.element =\n that.table =\n that.content =\n that.footer =\n that.wrapper =\n that.lockedTable =\n that.lockedContent =\n that.lockedHeader =\n that.lockedFooter =\n that._groupableClickHandler =\n that._groupRows =\n that._setContentWidthHandler = null;\n },\n\n getOptions: function() {\n var options = this.options;\n options.dataSource = null;\n\n var result = extend(true, {}, this.options);\n result.columns = kendo.deepExtend([], this.columns);\n\n var dataSource = this.dataSource;\n\n var initialData = dataSource.options.data && dataSource._data;\n dataSource.options.data = null;\n\n result.dataSource = $.extend(true, {}, dataSource.options);\n\n dataSource.options.data = initialData;\n\n result.dataSource.data = initialData;\n result.dataSource.page = dataSource.page();\n result.dataSource.filter = $.extend(true, {}, dataSource.filter());\n result.dataSource.pageSize = dataSource.pageSize();\n result.dataSource.sort = dataSource.sort();\n result.dataSource.group = dataSource.group();\n result.dataSource.aggregate = dataSource.aggregate();\n\n if (result.dataSource.transport) {\n result.dataSource.transport.dataSource = null;\n }\n\n if (result.pageable && result.pageable.pageSize) {\n result.pageable.pageSize = dataSource.pageSize();\n }\n\n result.$angular = undefined;\n\n return result;\n },\n\n setOptions: function(options) {\n var currentOptions = this.getOptions();\n kendo.deepExtend(currentOptions, options);\n if (!options.dataSource) {\n currentOptions.dataSource = this.dataSource;\n } else {\n if (options.dataSource.filter) {\n currentOptions.dataSource.filter = options.dataSource.filter;\n }\n }\n var wrapper = this.wrapper;\n var events = this._events;\n var element = this.element;\n\n this.destroy();\n this.options = null;\n if (this._isMobile) {\n var mobileWrapper = wrapper.closest(kendo.roleSelector(\"pane\")).parent();\n mobileWrapper.after(wrapper);\n mobileWrapper.remove();\n wrapper.removeClass(\"k-grid-mobile\");\n }\n if (wrapper[0] !== element[0]) {\n wrapper.before(element);\n wrapper.remove();\n }\n element.empty();\n\n this.init(element, currentOptions, events);\n this._setEvents(currentOptions);\n },\n\n items: function() {\n if (this.lockedContent) {\n return this._items(this.tbody).add(this._items(this.lockedTable.children(\"tbody\")));\n } else {\n return this._items(this.tbody);\n }\n },\n\n _items: function(container, includeGroupRows) {\n return container.children().filter(function() {\n var tr = $(this);\n return (includeGroupRows ? !tr.hasClass(\"k-detail-row\") : !tr.hasClass(\"k-grouping-row\")) && !tr.hasClass(\"k-detail-row\") && !tr.hasClass(\"k-group-footer\");\n });\n },\n\n dataItems: function() {\n var dataItems = kendo.ui.DataBoundWidget.fn.dataItems.call(this);\n if (this.lockedContent) {\n var n = dataItems.length, tmp = new Array(2 * n);\n for (var i = n; --i >= 0;) {\n tmp[i] = tmp[i + n] = dataItems[i];\n }\n dataItems = tmp;\n }\n\n return dataItems;\n },\n\n _destroyColumnAttachments: function() {\n var that = this;\n\n that.resizeHandle = null;\n\n if (!that.thead) {\n return;\n }\n\n this.angular(\"cleanup\", function() {\n return { elements: that.thead.get() };\n });\n\n that.thead.add(that.lockedHeader).find(\"th\").each(function() {\n var th = $(this),\n filterMenu = th.data(\"kendoFilterMenu\"),\n sortable = th.data(\"kendoColumnSorter\"),\n columnMenu = th.data(\"kendoColumnMenu\");\n\n if (filterMenu) {\n filterMenu.destroy();\n }\n\n if (sortable) {\n sortable.destroy();\n }\n\n if (columnMenu) {\n columnMenu.destroy();\n }\n });\n },\n\n _attachCustomCommandsEvent: function() {\n var that = this,\n columns = leafColumns(that.columns || []),\n command,\n idx,\n length;\n\n for (idx = 0, length = columns.length; idx < length; idx++) {\n command = columns[idx].command;\n\n if (command) {\n attachCustomCommandEvent(that, that.wrapper, command);\n }\n }\n },\n\n _aria: function() {\n var wrapper = this.wrapper,\n gridRole = this._hasDetails() ? \"treegrid\" : \"grid\",\n table = this.table,\n toolbar = wrapper.find(\".k-grid-toolbar\"),\n groupingHeader = wrapper.find(\".k-grouping-header\"),\n gridId = table.attr(\"id\"),\n tableTabindex = table.attr(\"tabindex\"),\n tbodyId, headerGroupId, footerGroupId, tableOwned,\n numberOfFixedRows = this.thead.find(\"tr\").length + this.wrapper.find(\".k-grid-footer-wrap table tr\").length,\n trailingColumns = this._trailingColumns(),\n virtual = this.virtualScroll,\n pageable = this.options.pageable,\n rowsCount;\n\n table.attr({\n role: gridRole,\n tabindex: tableTabindex >= 0 ? tableTabindex : 0\n });\n\n table.find(\"tbody, thead, tfoot\").attr(\"role\", \"rowgroup\");\n table.find(\"tr\").attr(\"role\", \"row\");\n table.find(\"th\").attr(\"role\", \"columnheader\");\n table.find(\"td\").attr(\"role\", \"gridcell\");\n\n if ((pageable && this.dataSource.totalPages() > 1) || (virtual && virtual.rows)) {\n if (this.dataSource.group().length > 0) {\n rowsCount = -1;\n } else if (this._hasDetails()) {\n rowsCount = numberOfFixedRows + (this.dataSource.total() * 2);\n } else {\n rowsCount = numberOfFixedRows + this.dataSource.total();\n }\n\n table.attr(\"aria-rowcount\", rowsCount);\n } else if (this._hasDetails()) {\n if (this.dataSource.group().length > 0) {\n rowsCount = -1;\n } else {\n rowsCount = numberOfFixedRows + (this.dataSource.total() * 2);\n }\n\n table.attr(\"aria-rowcount\", rowsCount);\n }\n\n if (rowsCount && rowsCount > 0) {\n this._ariaRowIndex();\n }\n\n if ((virtual && virtual.columns) ||\n (!table.attr(\"aria-colcount\") &&\n (table.find(\"td:hidden\").length > 0 ||\n wrapper.find(\".k-grid-content-locked td:hidden\").length > 0))) {\n table.attr(\"aria-colcount\", trailingColumns + leafColumns(this.columns).length);\n this._ariaColumnIndex();\n }\n\n if (!gridId) {\n gridId = kendo.guid();\n table.attr(\"id\", gridId);\n }\n\n if (this.pager) {\n this.pager.element.attr(\"aria-controls\", gridId);\n }\n\n toolbar.attr({\n role: \"toolbar\",\n \"aria-label\": this.options.messages.toolbarLabel,\n \"aria-controls\": gridId\n });\n\n groupingHeader.attr({\n role: \"toolbar\",\n \"aria-label\": this.options.messages.groupingHeaderLabel,\n \"aria-controls\": gridId\n });\n\n headerGroupId = this._ariaHeaderFooter(\"header\", \"thead\", \"th, td\", \"columnheader\");\n footerGroupId = this._ariaHeaderFooter(\"footer\", \"tfoot\", \"td\", \"gridcell\");\n\n if (wrapper.find(\".k-grid-content-locked\").length > 0) {\n this._ariaLockedContent();\n }\n\n if (!!headerGroupId || !!footerGroupId) {\n tbodyId = this.tbody.attr(\"id\") || kendo.guid();\n tableOwned = [headerGroupId, tbodyId, footerGroupId].join(\" \");\n\n this.tbody.attr(\"id\", tbodyId);\n table.attr(\"aria-owns\", tableOwned);\n }\n },\n\n _ariaColumnIndex: function() {\n var trailingColumns = this._trailingColumns(),\n dataVirtual = this.tbody.find(\">tr\").last().find(\"> td[data-virtual]\"),\n headerRows = this.thead.find(\">tr\").not(\".k-filter-row\"),\n lockedHeaderRows = this.wrapper.find(\".k-grid-header-locked thead > tr\").not(\".k-filter-row\"),\n firstIndex = Number.MAX_VALUE,\n lastIndex = 0,\n lockedLastIndex = 0,\n previousVirtual = 0,\n nextVirtual = 0,\n previousIndex, i, cells, dataIndex, cellsIndex,\n eachHeaderCell = function(j, cell) {\n var current = cell.getAttribute(\"data-index\"),\n currentIndex = Number(current),\n lockedParent = $(cell).closest(\".k-grid-header-locked\");\n\n if (lockedParent.length === 0 && currentIndex < firstIndex) {\n firstIndex = currentIndex;\n }\n\n if (lockedParent.length > 0 && lockedLastIndex < currentIndex) {\n lockedLastIndex = currentIndex;\n }\n\n if (lockedParent.length === 0 && lastIndex < currentIndex) {\n lastIndex = currentIndex;\n }\n\n if (current !== null) {\n cell.setAttribute(\"aria-colindex\", Number(currentIndex) + 1);\n previousIndex = Number(currentIndex) + 1 + cell.getAttribute(\"colspan\");\n } else {\n cell.setAttribute(\"aria-colindex\", previousIndex + 1);\n previousIndex = previousIndex + cell.getAttribute(\"colspan\");\n }\n };\n\n if (dataVirtual.length === 2) {\n previousVirtual = Number(dataVirtual[0].getAttribute(\"colspan\"));\n nextVirtual = Number(dataVirtual[1].getAttribute(\"colspan\"));\n } else if (dataVirtual.length === 1 && dataVirtual.prev().length === 0) {\n previousVirtual = Number(dataVirtual[0].getAttribute(\"colspan\"));\n } else if (dataVirtual.length === 1 && dataVirtual.prev().length === 1) {\n nextVirtual = Number(dataVirtual[0].getAttribute(\"colspan\"));\n }\n\n for (i = 0; i < lockedHeaderRows.length; i++) {\n previousIndex = 0;\n lockedHeaderRows.eq(i).find(\"th\").each(eachHeaderCell);\n }\n\n for (i = 0; i < headerRows.length; i++) {\n previousIndex = 0;\n headerRows.eq(i).find(\"th\").each(eachHeaderCell);\n }\n\n for (i = 0; i <= lockedLastIndex; i++) {\n dataIndex = i + trailingColumns;\n cells = this.wrapper.find(\".k-grid-content-locked tbody > tr > td:nth-child(\" + (i + 1) + \")\");\n cells.attr(\"aria-colindex\", dataIndex + 1);\n }\n\n for (i = previousVirtual; i <= lastIndex - firstIndex - nextVirtual; i++) {\n if (previousVirtual === 0 ) {\n cellsIndex = i + 1;\n } else {\n cellsIndex = i - previousVirtual + 2;\n }\n\n dataIndex = firstIndex + i + trailingColumns;\n\n cells = this.tbody.find(\"> tr > td:nth-child(\" + cellsIndex + \")\");\n cells.attr(\"aria-colindex\", dataIndex + 1);\n }\n },\n\n _ariaHeaderFooter: function(type, group, el, role) {\n var that = this,\n wrapper = that.wrapper,\n table = wrapper.find(\".k-grid-\" + type + \" .k-grid-\" + type + \"-wrap table\"),\n lockedTable = wrapper.find(\".k-grid-\" + type + \" .k-grid-\" + type + \"-locked table\"),\n groupId = \"\",\n rowGroup;\n\n if (table.length > 0) {\n rowGroup = table.find(group + \", tbody\");\n groupId = rowGroup.attr(\"id\") || kendo.guid();\n\n table.attr(\"role\", \"none\");\n table.find(\"tr\").attr(\"role\", \"row\");\n table.find(el).attr(\"role\", role);\n rowGroup.attr({\n role: \"rowgroup\",\n id: groupId\n });\n }\n\n if (lockedTable.length > 0) {\n that._ariaLocked(type, group, el, role);\n }\n\n lockedTable.find(\"td\").attr(\"role\", \"gridcell\");\n table.find(\"td\").attr(\"role\", \"gridcell\");\n\n return groupId;\n },\n\n _ariaId: function() {\n var id = this.element.attr(\"id\") || \"aria\";\n\n if (id) {\n this._cellId = id + \"_active_cell\";\n }\n },\n\n _ariaLocked: function(type, group, el, role) {\n var that = this,\n wrapper = that.wrapper,\n table = wrapper.find(\".k-grid-\" + type + \" .k-grid-\" + type + \"-wrap table\"),\n lockedTable = wrapper.find(\".k-grid-\" + type + \" .k-grid-\" + type + \"-locked table\"),\n rows = table.find(\"tr\"),\n lockedRows = lockedTable.find(\"tr\");\n\n lockedTable.attr(\"role\", \"none\");\n lockedTable.find(group + \", tbody\").attr(\"role\", \"none\");\n lockedRows.attr(\"role\", \"none\");\n\n lockedTable.find(el).attr(\"role\", role);\n\n rows.each(function(i, row) {\n var ownedCells = [];\n\n ownedCells = that._cellsIds(lockedRows.eq(i).find(el), \"locked_\" + type, i);\n ownedCells = ownedCells.concat(that._cellsIds($(row).find(el), type, i));\n\n row.setAttribute(\"aria-owns\", ownedCells.join(\" \"));\n });\n },\n\n _ariaLockedContent: function() {\n var that = this,\n table = that.table,\n tableRows = table.find(\"tr\"),\n lockedTable = that.wrapper.find(\".k-grid-content-locked table\"),\n lockedRows = lockedTable.find(\"tr\");\n\n lockedTable.attr(\"role\", \"none\");\n lockedTable.find(\"tbody\").attr(\"role\", \"none\");\n lockedRows.attr(\"role\", \"none\");\n lockedTable.find(\"td\").attr(\"role\", \"gridcell\");\n\n tableRows.each(function(i, row) {\n var ownedCells = [];\n\n ownedCells = that._cellsIds(lockedRows.eq(i).find(\"td\"), \"locked_datacell\", i);\n ownedCells = ownedCells.concat(that._cellsIds($(row).find(\"td\"), \"datacell\", i));\n\n row.setAttribute(\"aria-owns\", ownedCells.join(\" \"));\n });\n },\n\n _ariaAddHiddenColIndex: function() {\n var virtualScroll = this.virtualScroll || {},\n columns = this.columns,\n table = this.table,\n leafColsCount = leafColumns(columns).length;\n\n if (!virtualScroll.columns && !table.attr(\"aria-colcount\")) {\n this._ariaColumnIndex();\n\n table.attr(\"aria-colcount\", leafColsCount);\n }\n },\n\n _ariaRemoveHiddenColIndex: function() {\n var virtualScroll = this.virtualScroll || {},\n columns = this.columns,\n leafColsCount = leafColumns(columns).length;\n\n if (!virtualScroll.columns && (leafColsCount === visibleLeafColumns(this.columns).length)) {\n this.wrapper.find(\"td, th\").removeAttr(\"aria-colindex\");\n\n this.table.removeAttr(\"aria-colcount\");\n }\n },\n\n _ariaRowIndex: function() {\n var headerRows = this.thead.find(\">tr\"),\n numberOfHeaderRows = headerRows.length,\n bodyRows = this.tbody.find(\">tr\"),\n footerRows = this.wrapper.find(\".k-grid-footer-wrap tfoot > tr\"),\n totalNumberOfItems = this.dataSource.total(),\n previousItems = this.dataSource.skip() || 0,\n currentIndex = 1,\n previousMaster = false,\n i, currentRow;\n\n if (this._hasDetails()) {\n totalNumberOfItems = totalNumberOfItems * 2;\n previousItems = previousItems * 2;\n }\n\n for (i = 0; i < numberOfHeaderRows; i++) {\n headerRows.eq(i).attr(\"aria-rowindex\", currentIndex + i);\n }\n\n currentIndex = numberOfHeaderRows + previousItems;\n\n for (i = 0; i < bodyRows.length; i++) {\n currentRow = bodyRows.eq(i);\n\n if (this._hasDetails() && currentRow.hasClass(\"k-master-row\")) {\n if (previousMaster) {\n currentIndex = currentIndex + 2;\n } else {\n currentIndex = currentIndex + 1;\n }\n\n previousMaster = true;\n } else {\n currentIndex = currentIndex + 1;\n previousMaster = false;\n }\n\n currentRow.attr(\"aria-rowindex\", currentIndex);\n }\n\n currentIndex = numberOfHeaderRows + totalNumberOfItems + 1;\n\n for (i = 0; i < footerRows.length; i++) {\n footerRows.eq(i).attr(\"aria-rowindex\", currentIndex + i);\n }\n },\n\n _cellsIds: function(elements, prefix, i) {\n var ownedCells = [];\n\n elements.each(function(j, cell) {\n var id = cell.getAttribute(\"id\") || prefix + \"_\" + i + \"_\" + j;\n\n cell.setAttribute(\"id\", id);\n\n ownedCells.push(id);\n });\n\n return ownedCells;\n },\n\n _trailingColumns: function() {\n return this.dataSource.group().length + (this._hasDetails() ? 1 : 0);\n },\n\n _element: function() {\n var that = this,\n table = that.element;\n\n if (!table.is(\"table\")) {\n if (that.options.scrollable) {\n table = that.element.find(\"> .k-grid-content > table\");\n } else {\n table = that.element.children(\"table\");\n }\n\n if (!table.length) {\n table = $(\"\").appendTo(that.element);\n }\n }\n\n that.table = table;\n\n that._wrapper();\n },\n\n _createResizeHandle: function(container, th) {\n var that = this;\n var indicatorWidth = that.options.columnResizeHandleWidth;\n var scrollable = that.options.scrollable;\n var resizeHandle = that.resizeHandle;\n var halfResizeHandle = (indicatorWidth * 3) / 2;\n var rtlCorrection = 0;\n var headerWrap;\n var ieCorrection;\n var webkitCorrection;\n var firefoxCorrection;\n var leftMargin;\n var invisibleSpace;\n var leftBorderWidth;\n var scrollLeft;\n var left;\n var top;\n\n if (resizeHandle && that.lockedContent && resizeHandle.data(\"th\")[0] !== th[0]) {\n resizeHandle.off(NS).remove();\n resizeHandle = null;\n }\n\n if (!resizeHandle) {\n resizeHandle = that.resizeHandle = $('
');\n container.append(resizeHandle);\n }\n\n scrollLeft = kendo.scrollLeft(container);\n\n if (isRtl && (browser.mozilla || (browser.webkit && browser.version >= 85))) {\n scrollLeft = scrollLeft * -1;\n }\n\n leftBorderWidth = parseFloat(container.css(\"borderLeftWidth\"));\n\n left = th.offset().left + scrollLeft - parseFloat(th.css(\"marginLeft\")) - (container.offset().left + leftBorderWidth);\n\n if (!isRtl) {\n left += th[0].offsetWidth;\n } else {\n if (scrollable) {\n rtlCorrection = (left <= scrollLeft ? halfResizeHandle : 0);// when shown on first column headers are misaligned due to the width of the resize handler\n headerWrap = th.closest(\".k-grid-header-wrap, .k-grid-header-locked\");\n invisibleSpace = headerWrap[0].scrollWidth - headerWrap[0].offsetWidth; // the difference between the entire width and the visible area\n leftMargin = parseFloat(headerWrap.css(\"marginLeft\"));\n ieCorrection = browser.msie ? 2 * kendo.scrollLeft(headerWrap) + leftBorderWidth - leftMargin - rtlCorrection : 0;\n webkitCorrection = -rtlCorrection;\n firefoxCorrection = browser.mozilla ? leftBorderWidth - leftMargin - rtlCorrection : 0;\n\n left -= webkitCorrection + firefoxCorrection + ieCorrection;\n }\n }\n\n top = th.offset().top - parseFloat(th.css(\"marginTop\")) - (container.offset().top + parseFloat(container.css(\"borderTopWidth\")));\n\n resizeHandle.css({\n top: top, //scrollable ? 0 : heightAboveHeader(that.wrapper),\n left: left - halfResizeHandle,\n height: outerHeight(th),\n width: indicatorWidth * 3 - rtlCorrection\n })\n .data(\"th\", th)\n .show();\n\n resizeHandle.off(\"dblclick\" + NS).on(\"dblclick\" + NS, function() {\n that._autoFitLeafColumn(parseInt(th.attr(kendo.attr(\"index\")), 10));\n });\n },\n\n _positionColumnResizeHandle: function() {\n var that = this,\n lockedHead = that.lockedHeader ? that.lockedHeader.find(\"thead\").first() : $();\n\n that.thead.add(lockedHead).on(\"mousemove\" + NS, \"tr:not(.k-filter-row) > th\", function(e) {\n var button = typeof e.buttons !== \"undefined\" ? e.buttons : (e.which || e.button);\n\n var th = $(this);\n if (th.hasClass(\"k-group-cell\") || th.hasClass(\"k-hierarchy-cell\")) {\n return;\n }\n\n if (typeof button !== \"undefined\" && button !== 0) {\n //do not create a new resize handle if a mouse button is still pressed\n //this happens during resizing or before UserEvents trigger \"start\"\n return;\n }\n\n if (th[0].hasAttribute(kendo.attr(COLSPAN))) {\n // resizing multi-column headers is not supported\n return;\n }\n\n that._createResizeHandle(th.closest(\"div\"), th);\n });\n },\n\n _resizeHandleDocumentClick: function(e) {\n if ($(e.target).closest(\".k-column-active\").length) {\n return;\n }\n\n $(document).off(e);\n\n this._resetResizeHandleHeader();\n this._hideResizeHandle();\n },\n\n _resetResizeHandleHeader: function() {\n var th;\n\n if (!this.resizeHandle) {\n return;\n }\n\n th = $(this.resizeHandle).data(\"th\");\n\n if (th) {\n th.find(DOT + LINK_CLASS).find(DOT + ICON_CLASS).show();\n th.find(DOT + ORDER_CLASS).show();\n th.find(DOT + HEADER_COLUMN_MENU_CLASS).show();\n th.find(DOT + FILTER_MENU_CLASS).show();\n }\n },\n\n _hideResizeHandle: function() {\n if (this.resizeHandle) {\n this.resizeHandle.data(\"th\")\n .removeClass(\"k-column-active\");\n\n if (this.lockedContent && !this._isMobile) {\n this.resizeHandle.off(NS).remove();\n this.resizeHandle = null;\n } else {\n this.resizeHandle.hide();\n }\n }\n },\n\n _positionColumnResizeHandleTouch: function() {\n var that = this,\n lockedHead = that.lockedHeader ? that.lockedHeader.find(\"thead\").first() : $();\n\n that._resizeUserEvents = new kendo.UserEvents(lockedHead.add(that.thead), {\n filter: \"th:not(.k-group-cell):not(.k-hierarchy-cell)\",\n threshold: 10,\n minHold: 500,\n hold: function(e) {\n var th = $(e.target);\n\n e.preventDefault();\n\n if (that.resizeHandle) {\n that.resizeHandle.data(\"th\")\n .removeClass(\"k-column-active\");\n that._resetResizeHandleHeader();\n }\n\n th.addClass(\"k-column-active\");\n\n th.find(DOT + LINK_CLASS).find(DOT + ICON_CLASS).hide();\n th.find(DOT + ORDER_CLASS).hide();\n th.find(DOT + HEADER_COLUMN_MENU_CLASS).hide();\n th.find(DOT + FILTER_MENU_CLASS).hide();\n\n that._createResizeHandle(th.closest(\"div\"), th);\n\n if (!that._resizeHandleDocumentClickHandler) {\n that._resizeHandleDocumentClickHandler = that._resizeHandleDocumentClick.bind(that);\n }\n\n $(document).on(\"click\", that._resizeHandleDocumentClickHandler);\n }\n });\n },\n\n resizeColumn: function(column, columnWidth) {\n var that = this;\n var isLocked = !!column.locked;\n var isHidden = !!column.hidden;\n var options = this.options;\n var scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0;\n var index = isLocked ? inArray(column, visibleLockedColumns(visibleLeafColumns(that.columns))) : inArray(column, visibleNonLockedColumns(visibleLeafColumns(that.columns)));\n var contentTable = isLocked ? that.lockedTable : that.table;\n var footer = that.footer || $();\n var header = isLocked ? that.lockedHeader.find(\"table\") : that.thead.closest(\"table\");\n var columnMinWidth = column.minResizableWidth || 10;\n var gridWidth = isLocked ? outerWidth(contentTable.find(\"tbody\")) : outerWidth(that.tbody); // IE returns 0 if grid is empty and scrolling is enabled\n var col;\n\n if (isHidden) {\n column.width = columnWidth > columnMinWidth ? columnWidth : columnMinWidth;\n return;\n }\n\n if (that.footer && that.lockedContent) {\n footer = isLocked ? that.footer.children(\".k-grid-footer-locked\") : that.footer.children(\".k-grid-footer-wrap\");\n }\n\n if (options.scrollable) {\n\n col = header.find(\"col:not(.k-group-col,.k-hierarchy-col)\").eq(index)\n .add(contentTable.children(\"colgroup\").find(\"col:not(.k-group-col):not(.k-hierarchy-col)\").eq(index))\n .add(footer.find(\"colgroup\").find(\"col:not(.k-group-col):not(.k-hierarchy-col)\").eq(index));\n } else {\n col = contentTable.find(\"colgroup\").find(\"col:not(.k-group-col):not(.k-hierarchy-col)\").eq(index);\n }\n\n if (options.scrollable) {\n var constrain = false;\n var totalWidth = that.wrapper.width() - scrollbar;\n var width = columnWidth = columnWidth > columnMinWidth ? columnWidth : columnMinWidth;\n\n if (isLocked && gridWidth - columnWidth + width > totalWidth) {\n width = columnWidth + (totalWidth - gridWidth - scrollbar * 2);\n if (width < 0) {\n width = columnWidth;\n }\n constrain = true;\n }\n\n if (width > 10 && width >= columnMinWidth) {\n col.css('width', width);\n\n if (gridWidth) {\n if (constrain) {\n width = totalWidth - scrollbar * 2;\n } else {\n width = gridWidth + (columnWidth - column.width);\n }\n\n contentTable\n .add(header)\n .add(footer)\n .css('width', width);\n\n if (!isLocked) {\n that._footerWidth = width;\n }\n }\n }\n\n that._scrollVirtualWrapperOnColumnResize();\n } else if (columnWidth > 10 && columnWidth >= columnMinWidth) {\n col.css('width', columnWidth);\n }\n\n column.width = columnWidth;\n\n that._applyLockedContainersWidth();\n that._syncLockedContentHeight();\n that._syncLockedHeaderHeight();\n that._updateStickyColumns();\n },\n\n _resizable: function() {\n var that = this,\n options = that.options,\n container,\n columnStart,\n columnWidth,\n columnMinWidth,\n gridWidth,\n isMobile = this._isMobile,\n scrollbar = !kendo.support.mobileOS ? kendo.support.scrollbar() : 0,\n isLocked,\n col, th;\n\n if (options.resizable) {\n container = options.scrollable ? that.wrapper.find(\".k-grid-header-wrap\").first() : that.wrapper;\n\n if (isMobile) {\n that._positionColumnResizeHandleTouch(container);\n } else {\n that._positionColumnResizeHandle(container);\n }\n\n if (that.resizable) {\n that.resizable.destroy();\n }\n\n that.resizable = new ui.Resizable(container.add(that.lockedHeader), {\n handle: (!!options.scrollable ? \"\" : \">\") + \".k-resize-handle\",\n hint: function(handle) {\n return $('
').css({\n height: outerHeight(handle.data(\"th\")) + that.tbody.attr(\"clientHeight\")\n });\n },\n start: function(e) {\n th = $(e.currentTarget).data(\"th\");\n\n if (isMobile) {\n that._hideResizeHandle();\n }\n\n var header = th.closest(\"table\"),\n index = $.inArray(th[0], leafDataCells(th.closest(\"thead\")).filter(\":visible\"));\n\n isLocked = header.parent().hasClass(\"k-grid-header-locked\");\n\n var contentTable = isLocked ? that.lockedTable : that.table,\n footer = that.footer || $();\n\n if (that.footer && that.lockedContent) {\n footer = isLocked ? that.footer.children(\".k-grid-footer-locked\") : that.footer.children(\".k-grid-footer-wrap\");\n }\n\n cursor(that.wrapper, 'col-resize');\n\n if (options.scrollable) {\n col = header.find(\"col:not(.k-group-col):not(.k-hierarchy-col)\").eq(index)\n .add(contentTable.children(\"colgroup\").find(\"col:not(.k-group-col):not(.k-hierarchy-col)\").eq(index))\n .add(footer.find(\"colgroup\").find(\"col:not(.k-group-col):not(.k-hierarchy-col)\").eq(index));\n } else {\n col = contentTable.children(\"colgroup\").find(\"col:not(.k-group-col):not(.k-hierarchy-col)\").eq(index);\n }\n\n var columns = $.map(that.columns, function(a) {\n return !a.hidden && ((isLocked && a.locked) || ((!isLocked && !a.locked))) ? a : null;\n });\n\n columnStart = e.x.location;\n columnWidth = outerWidth(th);\n columnMinWidth = leafColumns(columns)[index].minResizableWidth || 10;\n gridWidth = isLocked ? outerWidth(contentTable.children(\"tbody\")) : outerWidth(that.tbody); // IE returns 0 if grid is empty and scrolling is enabled\n\n // fix broken UI in Chrome38+\n if (browser.webkit) {\n that.wrapper.addClass(\"k-grid-column-resizing\");\n }\n },\n resize: function(e) {\n var rtlMultiplier = isRtl ? -1 : 1,\n currentWidth = columnWidth + (e.x.location * rtlMultiplier) - (columnStart * rtlMultiplier);\n\n if (options.scrollable) {\n var footer;\n if (isLocked && that.lockedFooter) {\n footer = that.lockedFooter.children(\"table\");\n } else if (that.footer) {\n footer = that.footer.find(\">.k-grid-footer-wrap>table\");\n }\n if (!footer || !footer[0]) {\n footer = $();\n }\n var header = th.closest(\"table\");\n var contentTable = isLocked ? that.lockedTable : that.table;\n var constrain = false;\n var totalWidth = that.wrapper.width() - scrollbar;\n var width = currentWidth;\n\n if (isLocked && gridWidth - columnWidth + width > totalWidth) {\n width = columnWidth + (totalWidth - gridWidth - scrollbar * 2);\n if (width < 0) {\n width = currentWidth;\n }\n constrain = true;\n }\n\n if (width > 10 && width >= columnMinWidth) {\n col.css('width', width);\n\n if (gridWidth) {\n if (constrain) {\n width = totalWidth - scrollbar * 2;\n } else {\n width = gridWidth + (e.x.location * rtlMultiplier) - (columnStart * rtlMultiplier);\n }\n\n contentTable\n .add(header)\n .add(footer)\n .css('width', width);\n\n if (!isLocked) {\n that._footerWidth = width;\n }\n }\n }\n\n that._scrollVirtualWrapperOnColumnResize();\n } else if (currentWidth > 10 && currentWidth >= columnMinWidth) {\n col.css('width', currentWidth);\n }\n },\n resizeend: function() {\n var newWidth = outerWidth(th),\n column,\n header;\n\n cursor(that.wrapper, \"\");\n\n if (browser.webkit) {\n that.wrapper.removeClass(\"k-grid-column-resizing\");\n }\n\n if (columnWidth != newWidth) {\n header = that.lockedHeader ? that.lockedHeader.find(\"thead\").first().find(\"tr\").first().add(that.thead.find(\"tr\").first()) : th.parent();\n\n var index = th.attr(kendo.attr(\"index\"));\n if (!index) {\n index = header.find(\"th:not(.k-group-cell):not(.k-hierarchy-cell)\").index(th);\n }\n column = leafColumns(that.columns)[index];\n\n column.width = newWidth;\n\n that.trigger(COLUMNRESIZE, {\n column: column,\n oldWidth: columnWidth,\n newWidth: newWidth\n });\n\n that._applyLockedContainersWidth();\n that._syncLockedContentHeight();\n that._syncLockedHeaderHeight();\n that._updateStickyColumns();\n }\n\n that._resetResizeHandleHeader();\n that._hideResizeHandle();\n th = null;\n }\n });\n\n }\n },\n\n _draggable: function() {\n var that = this,\n reorderable = that.options.reorderable;\n\n if (reorderable === true || (reorderable && reorderable.columns)) {\n\n if (that._draggableInstance) {\n that._draggableInstance.destroy();\n }\n\n var header = that.wrapper.children(\".k-grid-header\");\n\n that._draggableInstance = that.wrapper.kendoDraggable({\n group: kendo.guid(),\n autoScroll: true,\n filter: that.content ? \".k-grid-header:first \" + HEADERCELLS : \"table:first>.k-grid-header \" + HEADERCELLS,\n dragstart: function() {\n header.children(\".k-grid-header-wrap\").off(\"scroll\" + NS + \"scrolling\").on(\"scroll\" + NS + \"scrolling\", function(e) {\n if (that.virtualScrollable) {\n kendo.scrollLeft(that.content.find(\">.k-virtual-scrollable-wrap\"), this.scrollLeft);\n } else {\n kendo.scrollLeft(that.scrollables.not(e.currentTarget), this.scrollLeft);\n }\n });\n },\n dragend: function() {\n that._resetResizeHandleHeader();\n header.children(\".k-grid-header-wrap\").off(\"scroll\" + NS + \"scrolling\");\n },\n drag: function() {\n that._hideResizeHandle();\n },\n hint: function(target) {\n var title = target.attr(kendo.attr(\"title\"));\n if (title) {\n title = kendo.htmlEncode(title);\n }\n return $('
')\n .html(title || target.attr(kendo.attr(\"field\")) || target.text())\n .prepend('');\n }\n }).data(\"kendoDraggable\");\n }\n },\n\n _reorderable: function() {\n var that = this,\n reorderable = that.options.reorderable;\n\n if (reorderable === true || (reorderable && reorderable.columns)) {\n if (that.wrapper.data(\"kendoReorderable\")) {\n that.wrapper.data(\"kendoReorderable\").destroy();\n }\n\n that.wrapper.kendoReorderable({\n draggable: that._draggableInstance,\n dropFilter: HEADERCELLS,\n dragOverContainers: function(sourceIndex, targetIndex) {\n var columns = flatColumnsInDomOrder(that.columns);\n return columns[sourceIndex].lockable !== false && targetParentContainerIndex(columns, that.columns, sourceIndex, targetIndex) > -1;\n },\n inSameContainer: function(e) {\n return $(e.source).parent()[0] === $(e.target).parent()[0] && targetParentContainerIndex(flatColumnsInDomOrder(that.columns), that.columns, e.sourceIndex, e.targetIndex) > -1;\n },\n change: function(e) {\n var columns = flatColumnsInDomOrder(that.columns);\n var column = columns[e.oldIndex];\n var newIndex = targetParentContainerIndex(columns, that.columns, e.oldIndex, e.newIndex);\n\n that.trigger(COLUMNREORDER, {\n newIndex: newIndex,\n oldIndex: inArray(column, columns),\n column: column\n });\n\n that.reorderColumn(newIndex, column, e.position === \"before\");\n }\n });\n }\n },\n\n _reorderHeader: function(sources, target, before, container) {\n var that = this;\n var sourcePosition = columnPosition(sources[0], that.columns);\n var destPosition = columnPosition(target, that.columns);\n var action;\n var ths;\n\n var leafs = [];\n for (var idx = 0; idx < sources.length; idx++) {\n if (sources[idx].columns) {\n leafs = leafs.concat(sources[idx].columns);\n }\n }\n if (container) {\n ths = elements(container, container, \"tr:eq(\" + sourcePosition.row + \")>th.k-header:not(.k-group-cell,.k-hierarchy-cell)\");\n } else {\n ths = elements(that.lockedHeader, that.thead, \"tr:eq(\" + sourcePosition.row + \")>th.k-header:not(.k-group-cell,.k-hierarchy-cell)\");\n }\n\n var sourceLockedColumns = lockedColumns(sources).length;\n var targetLockedColumns = lockedColumns([target]).length;\n\n if (leafs.length) {\n if (sourceLockedColumns > 0 && targetLockedColumns === 0) {\n action = \"prepend\";\n moveCellsBetweenContainers(sources, target, leafs, that.columns, that.lockedHeader.find(\"thead\"), that.thead, this._groups(), action);\n } else if (sourceLockedColumns === 0 && targetLockedColumns > 0) {\n action = destPosition.cell === 0 && sources[0].columns && !target.columns && !that._group ? \"prepend\" : \"append\";\n moveCellsBetweenContainers(sources, target, leafs, nonLockedColumns(that.columns), that.thead, that.lockedHeader.find(\"thead\"), this._groups(), action);\n }\n\n if (target.columns || sourcePosition.cell - destPosition.cell > 1 || destPosition.cell - sourcePosition.cell > 1) {\n target = findReorderTarget(that.columns, target, sources[0], before, that.columns);\n if (target) {\n if (sourceLockedColumns > 0 && targetLockedColumns === 0) {\n that._reorderHeader(leafs, target, before, that.thead);\n } else if (sourceLockedColumns === 0 && targetLockedColumns > 0) {\n that._reorderHeader(leafs, target, before, that.lockedHead);\n } else {\n that._reorderHeader(leafs, target, before);\n }\n }\n }\n } else if (sourceLockedColumns !== targetLockedColumns) { // move between containers\n updateCellRowSpan(ths[sourcePosition.cell], that.columns, sourceLockedColumns);\n }\n\n reorder(ths, sourcePosition.cell, destPosition.cell, before, sources.length);\n },\n\n _reorderContent: function(sources, destination, before) {\n var that = this;\n var lockedRows = $();\n var source = sources[0];\n var visibleSources = visibleColumns(sources);\n var sourceIndex = inArray(source, leafColumns(that.columns));\n var destIndex = inArray(destination, leafColumns(that.columns));\n\n var colSourceIndex = inArray(visibleSources[0], visibleLeafColumns(that.columns));\n var colDest = inArray(destination, visibleLeafColumns(that.columns));\n var lockedCount = lockedColumns(that.columns).length;\n var isLocked = !!destination.locked;\n var footer = that.footer || that.wrapper.find(\".k-grid-footer\");\n\n var headerCol, footerCol, beforeVisibleColumn;\n headerCol = footerCol = colDest;\n\n if (destination.hidden) {\n var columnsArray = isLocked ? lockedColumns(that.columns) : nonLockedColumns(that.columns);\n\n if (visibleColumns(columnsArray).length > 0) {\n headerCol = footerCol = colDest = this._findClosestVisibleColumnIndex(columnsArray, destIndex);\n beforeVisibleColumn = visibleColumns(columnsArray.slice(destIndex)).length > 0;\n }\n else {\n if (isLocked) {\n colDest = that.lockedTable.find(\"colgroup\");\n headerCol = that.lockedHeader.find(\"colgroup\");\n footerCol = $(that.lockedFooter).find(\">table>colgroup\");\n } else {\n colDest = that.tbody.prev();\n headerCol = that.thead.prev();\n footerCol = footer.find(\".k-grid-footer-wrap\").find(\">table>colgroup\");\n }\n }\n }\n\n if (that._hasFilterRow()) {\n reorder(that.wrapper.find(\".k-filter-row td:not(.k-group-cell,.k-hierarchy-cell)\"), sourceIndex, destIndex, before, sources.length);\n }\n\n if (colSourceIndex >= 0) {\n reorder(elements(that.lockedHeader, that.thead.prev(), COLGROUP), colSourceIndex, headerCol, beforeVisibleColumn ? beforeVisibleColumn : before, visibleSources.length);\n }\n\n if (that.options.scrollable) {\n if (colSourceIndex >= 0 && !that._hasVirtualColumns()) {\n reorder(elements(that.lockedTable, that.tbody.prev(), COLGROUP), colSourceIndex, colDest, beforeVisibleColumn ? beforeVisibleColumn : before, visibleSources.length);\n }\n }\n\n if (footer && footer.length) {\n if (colSourceIndex >= 0) {\n 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);\n }\n reorder(footer.find(\".k-footer-template>td:not(.k-group-cell,.k-hierarchy-cell)\"), sourceIndex, destIndex, before, sources.length);\n }\n\n var rows = that.tbody.children(\":not(.k-grouping-row,.k-detail-row)\");\n if (that.lockedTable) {\n if (lockedCount > destIndex) {\n if (lockedCount <= sourceIndex) {\n updateColspan(\n that.lockedTable.find(\">tbody>tr.k-grouping-row\"),\n that.table.find(\">tbody>tr.k-grouping-row\"),\n sources.length\n );\n }\n } else if (lockedCount > sourceIndex) {\n updateColspan(\n that.table.find(\">tbody>tr.k-grouping-row\"),\n that.lockedTable.find(\">tbody>tr.k-grouping-row\"),\n sources.length\n );\n }\n\n lockedRows = that.lockedTable.find(\">tbody>tr:not(.k-grouping-row,.k-detail-row)\");\n }\n\n for (var idx = 0, length = rows.length; idx < length; idx += 1) {\n reorder(elements(lockedRows[idx], rows[idx], \">td:not(.k-group-cell,.k-hierarchy-cell)\"), sourceIndex, destIndex, before, sources.length);\n }\n },\n\n _findClosestVisibleColumnIndex: function(columns, columnIndex) {\n var columnsArray = visibleColumns(columns.slice(columnIndex)).length > 0 ? columns.slice(columnIndex) : columns.slice(0, columnIndex + 1).reverse(),\n closestVisibleColumn = visibleColumns(columnsArray)[0];\n\n return inArray(closestVisibleColumn, visibleColumns(this.columns));\n },\n\n _autoFitLeafColumn: function(leafIndex) {\n this.autoFitColumn(leafColumns(this.columns)[leafIndex]);\n },\n\n _hasReorderableRows: function() {\n return this._hasDragHandleColumn || (this.options.reorderable && this.options.reorderable.rows);\n },\n\n _draggableRows: function() {\n var that = this,\n selectable = that._checkBoxSelection ||\n (that.options.selectable && !kendo.ui.Selectable.parseOptions(that.options.selectable).cell);\n\n if (that._draggableRowsInstance) {\n that._draggableRowsInstance.destroy();\n }\n\n that._draggableRowsInstance = that.tbody.kendoDraggable({\n group: \"row-draggable\",\n autoScroll: true,\n filter: (selectable ? \" > .k-selected\" : \" > \" + ITEMROW) + (that._hasDragHandleColumn ? \" > .k-drag-cell\" : \"\"),\n hint: function(target) {\n var hint = $('
');\n\n if (selectable && that.select().length > 1) {\n hint.append(\"\" + that.select().length + \" \" + that.options.messages.itemsSelected + \"\");\n } else {\n var clone = target.closest(ITEMROW).clone();\n clone.find(\"td.k-command-cell\").remove();\n clone.find(\"td\").each(function(index, elm) {\n hint.append(\"\" + elm.innerText + \" \");\n });\n }\n\n return hint;\n }\n }).data(\"kendoDraggable\");\n },\n\n _reorderableRows: function() {\n var that = this,\n selectable = that._checkBoxSelection ||\n (that.options.selectable && !kendo.ui.Selectable.parseOptions(that.options.selectable).cell);\n\n if (that.tbody.data(\"kendoReorderable\")) {\n that.tbody.data(\"kendoReorderable\").destroy();\n }\n\n that.tbody.kendoReorderable({\n smartPosition: false,\n draggable: that._draggableRowsInstance,\n dragOverContainers: function(sourceIndex, targetIndex) {\n var result = true,\n target = $(ITEMROW, that.content).eq(targetIndex);\n\n if (selectable) {\n result = !target.is(\".k-selected\");\n }\n\n return result;\n },\n inSameContainer: function(e) {\n if (selectable) {\n return !$(e.target).is(\".k-selected\");\n }\n\n return true;\n },\n dropFilter: \"> \" + ITEMROW,\n allowIcon: \"k-i-insert-middle\",\n orientation: \"vertical\",\n reorderDropCue: $('
'),\n positionDropCue: function(reorderDropCue, dropTarget) {\n var firstCellLeft = kendo.getOffset(dropTarget.children(DATA_CELL).eq(0)).left;\n reorderDropCue.css({\n transform: \"translate(0,-50%)\",\n left: firstCellLeft\n });\n },\n externalDraggable: function(e) {\n var draggable = e.draggable;\n\n if (draggable) {\n return draggable;\n }\n },\n change: function(e) {\n that._triggerRowRorder(e);\n }\n });\n },\n\n _triggerRowRorder: function(e) {\n var that = this,\n args = {\n newIndex: e.position === \"after\" ? e.newIndex + 1 : e.newIndex,\n oldIndex: e.oldIndex\n },\n row = e.element,\n selectable = that._checkBoxSelection ||\n (that.options.selectable && !kendo.ui.Selectable.parseOptions(that.options.selectable).cell);\n\n if (selectable && that.select().length > 1) {\n args = extend(args, {\n rows: that.select()\n });\n } else {\n args = extend(args, {\n row: row\n });\n }\n\n if (!that.trigger(ROWREORDER, args)) {\n that.reorderRows(selectable ? that.select() : row, args.newIndex);\n }\n },\n\n reorderRows: function(rows, index) {\n var that = this,\n dataSource = that.dataSource,\n rowsLength = that.tbody.children(ITEMROW).length,\n targetItem = that.dataItem(that.tbody.children(ITEMROW).eq(index)),\n items = rows.toArray().map(function(row) {\n return that.dataItem(row);\n });\n\n if (!targetItem) {\n // If index is after last row dataItem wouldn't exist\n targetItem = that.dataItem(that.tbody.children(ITEMROW).eq(rowsLength - 1));\n index = dataSource.indexOf(targetItem) + 1;\n } else {\n index = dataSource.indexOf(targetItem);\n }\n\n if (index >= 0) {\n that._rowDropping = true;\n dataSource.pushMove(index, items);\n that._rowDropping = false;\n }\n },\n\n autoFitColumns: function(columns) {\n var that = this;\n\n columns = columns || that.columns;\n\n for (var i = 0; i < columns.length; i++) {\n var column = columns[i];\n\n if (column.columns) {\n that.autoFitColumns(column.columns);\n } else {\n that.autoFitColumn(column);\n }\n }\n },\n\n autoFitColumn: function(column) {\n var that = this,\n options = that.options,\n columns = that.columns,\n index,\n th,\n headerTable,\n leafCols,\n isLocked,\n visibleLocked = that.lockedHeader ? leafDataCells(that.lockedHeader.find(\">table>thead\")).filter(isCellVisible).length : 0,\n col,\n minWidth,\n contentDiv, scrollLeft,\n notGroupOrHierarchyCol = \"col:not(.k-group-col):not(.k-hierarchy-col)\",\n notGroupOrHierarchyVisibleCell = \"td:visible:not(.k-group-cell):not(.k-hierarchy-cell)\",\n menu,\n thWidth;\n\n // retrieve the column object, depending on the method argument\n if (typeof column == \"number\") {\n column = columns[column];\n } else if (isPlainObject(column)) {\n column = grep(flatColumns(columns), function(item) {\n return item === column;\n })[0];\n } else {\n column = grep(flatColumns(columns), function(item) {\n return item.field === column;\n })[0];\n }\n\n if (!column || !isVisible(column)) {\n return;\n }\n\n leafCols = leafColumns(columns);\n minWidth = column.minResizableWidth;\n index = inArray(column, leafCols);\n isLocked = column.locked;\n\n if (isLocked) {\n headerTable = that.lockedHeader.children(\"table\");\n } else {\n headerTable = that.thead.parent();\n }\n\n th = headerTable.find(\"[data-index='\" + index + \"']\");\n menu = th.find('a.k-header-column-menu, a.k-grid-filter');\n\n var contentTable = isLocked ? that.lockedTable : that.table,\n footer = that.footer || $();\n\n if (that.footer && that.lockedContent) {\n footer = isLocked ? that.footer.children(\".k-grid-footer-locked\") : that.footer.children(\".k-grid-footer-wrap\");\n }\n\n var footerTable = footer.find(\"table\").first();\n\n if (that.lockedHeader && !isLocked) {\n index -= visibleLocked;\n }\n\n // adjust column index, depending on previous hidden columns\n for (var j = 0; j < leafCols.length; j++) {\n if (leafCols[j] === column) {\n break;\n } else {\n if (leafCols[j].hidden) {\n index--;\n }\n }\n }\n\n // get col elements\n if (options.scrollable) {\n col = headerTable.find(notGroupOrHierarchyCol).eq(index)\n .add(contentTable.children(\"colgroup\").find(notGroupOrHierarchyCol).eq(index))\n .add(footerTable.find(\"colgroup\").find(notGroupOrHierarchyCol).eq(index));\n\n if (!isLocked) {\n contentDiv = contentTable.parent();\n scrollLeft = kendo.scrollLeft(contentDiv);\n }\n } else {\n col = contentTable.children(\"colgroup\").find(notGroupOrHierarchyCol).eq(index);\n }\n\n var tables = headerTable.add(contentTable).add(footerTable);\n\n if (browser.safari) {\n th.css(\"white-space\", \"initial\");\n }\n\n var oldColumnWidth = outerWidth(th);\n\n // reset the table and autofitted column widths\n // if scrolling is disabled, we need some additional repainting of the table\n col.width(\"\");\n tables.css(\"table-layout\", \"fixed\");\n col.width(\"auto\");\n tables.addClass(\"k-autofitting\");\n tables.css(\"table-layout\", \"\");\n\n thWidth = menu.length ? outerWidth(menu) + outerWidth(th) : outerWidth(th);\n\n // +1 is required by IE, regardless of the border widths, otherwise unexpected wrapping may occur with hyphenated text\n var newColumnWidth = Math.ceil(Math.max(\n thWidth,\n outerWidth(contentTable.find(\"tr:not(.k-grouping-row)\").eq(0).children(notGroupOrHierarchyVisibleCell).eq(index)),\n outerWidth(footerTable.find(\"tr\").eq(0).children(notGroupOrHierarchyVisibleCell).eq(index))\n )) + 1;\n\n if (minWidth && minWidth > newColumnWidth) {\n newColumnWidth = minWidth;\n }\n\n col.width(newColumnWidth);\n column.width = newColumnWidth;\n\n if (browser.safari) {\n th.css(\"white-space\", \"\");\n }\n\n // if all visible columns have widths, the table needs a pixel width as well\n if (options.scrollable) {\n var cols = headerTable.find(\"col\"),\n colWidth,\n totalWidth = 0;\n for (var idx = 0, length = cols.length; idx < length; idx += 1) {\n colWidth = cols[idx].style.width;\n if (colWidth && colWidth.indexOf(\"%\") == -1) {\n totalWidth += parseInt(colWidth, 10);\n } else if (cols.eq(idx).hasClass(\"k-group-col\")) {\n totalWidth += parseInt(cols.eq(idx).width(), 10);\n } else {\n totalWidth = 0;\n break;\n }\n }\n\n if (totalWidth) {\n tables.each(function() {\n this.style.width = totalWidth + \"px\";\n });\n }\n }\n\n tables.removeClass(\"k-autofitting\");\n\n if (scrollLeft) {\n kendo.scrollLeft(contentDiv, scrollLeft);\n }\n\n that.trigger(COLUMNRESIZE, {\n column: column,\n oldWidth: oldColumnWidth,\n newWidth: newColumnWidth\n });\n\n that._applyLockedContainersWidth();\n that._syncLockedContentHeight();\n that._syncLockedHeaderHeight();\n that._updateStickyColumns();\n },\n\n reorderColumn: function(destIndex, column, before) {\n var that = this,\n parent = columnParent(column, that.columns),\n columns = parent ? parent.columns : that.columns,\n sourceIndex = inArray(column, columns),\n destColumn = columns[destIndex],\n virtualScroll = that.virtualScroll || {},\n lockChanged,\n isLocked = !!destColumn.locked,\n lockedCount = lockedColumns(that.columns).length,\n groupHeaderColumnTemplateColumns = grep(leafColumns(that.columns), function(column) { return column.groupHeaderColumnTemplate; });\n\n if (sourceIndex === destIndex) {\n return;\n }\n\n if (!column.locked && isLocked && nonLockedColumns(that.columns).length == 1) {\n return;\n }\n\n if (column.locked && !isLocked && lockedCount == 1) {\n return;\n }\n\n that._hideResizeHandle();\n\n if (before === undefined) {\n before = destIndex < sourceIndex;\n }\n\n var sourceColumns = [column];\n\n that._reorderHeader(sourceColumns, destColumn, before);\n\n if (that.lockedHeader) {\n removeEmptyRows(that.thead);\n removeEmptyRows(that.lockedHeader);\n }\n\n if (destColumn.columns) {\n destColumn = leafColumns(destColumn.columns);\n destColumn = destColumn[before ? 0 : destColumn.length - 1];\n }\n\n if (column.columns) {\n sourceColumns = leafColumns(column.columns);\n }\n\n that._reorderContent(sourceColumns, destColumn, before);\n\n lockChanged = !!column.locked;\n lockChanged = lockChanged != isLocked;\n column.locked = isLocked;\n\n columns.splice(before ? destIndex : destIndex + 1, 0, column);\n columns.splice(sourceIndex < destIndex ? sourceIndex : sourceIndex + 1, 1);\n\n that._updateLockedCols();\n that._updateCols();\n that._templates();\n\n that._updateColumnCellIndex();\n that._updateColumnSorters();\n\n if (groupHeaderColumnTemplateColumns.length > 0) {\n that._renderGroupRows();\n }\n that._updateTablesWidth();\n that._applyLockedContainersWidth();\n that._syncLockedHeaderHeight();\n that._syncLockedContentHeight();\n that._updateFirstColumnClass();\n that._updateStickyColumns();\n\n if (virtualScroll.columns) {\n that.refresh();\n }\n\n if (!lockChanged) {\n return;\n }\n\n if (isLocked) {\n that.trigger(COLUMNLOCK, {\n column: column\n });\n } else {\n that.trigger(COLUMNUNLOCK, {\n column: column\n });\n }\n },\n\n _updateColumnCellIndex: function() {\n var header;\n var offset = 0;\n\n if (this.lockedHeader) {\n header = this.lockedHeader.find(\"thead\");\n offset = updateCellIndex(header, lockedColumns(this.columns));\n }\n updateCellIndex(this.thead, nonLockedColumns(this.columns), offset);\n },\n\n lockColumn: function(column) {\n var columns = this.columns;\n\n if (typeof column == \"number\") {\n column = columns[column];\n } else {\n column = grep(columns, function(item) {\n return item.field === column;\n })[0];\n }\n\n if (!column || column.locked || column.hidden) {\n return;\n }\n\n if (column.sticky) {\n this.unstickColumn(columns.indexOf(column));\n }\n\n var index = lockedColumns(columns).length - 1;\n this.reorderColumn(index, column, false);\n },\n\n unlockColumn: function(column) {\n var columns = this.columns;\n\n if (typeof column == \"number\") {\n column = columns[column];\n } else {\n column = grep(columns, function(item) {\n return item.field === column;\n })[0];\n }\n\n if (!column || !column.locked || column.hidden) {\n return;\n }\n\n var index = lockedColumns(columns).length;\n this.reorderColumn(index, column, true);\n },\n\n stickColumn: function(column) {\n var columns = this.columns;\n\n if (typeof column == \"number\") {\n column = columns[column];\n } else {\n column = grep(columns, function(item) {\n return item.field === column;\n })[0];\n }\n\n if (!column || column.sticky || column.hidden) {\n return;\n }\n\n if (column.locked) {\n this.unlockColumn(columns.indexOf(column));\n\n if (column.locked) {\n return;\n }\n }\n\n column.sticky = true;\n this._updateStickyColumns();\n },\n\n unstickColumn: function(column) {\n var columns = this.columns;\n\n if (typeof column == \"number\") {\n column = columns[column];\n } else {\n column = grep(columns, function(item) {\n return item.field === column;\n })[0];\n }\n\n if (!column || !column.sticky || column.locked || column.hidden) {\n return;\n }\n\n this._removeStickyAttributes([column]);\n this._removeStickyStyles(stickyColumns(columns));\n\n column.sticky = false;\n this._updateStickyColumns();\n\n if (this._anyStickyColumns() === 0) {\n this._templates();\n if (this._hasFilterRow()) {\n this._updateStickyFilterCells();\n }\n }\n },\n\n cellIndex: function(td) {\n var lockedColumnOffset = 0;\n\n if (this.lockedTable && !$.contains(this.lockedTable[0], td[0])) {\n lockedColumnOffset = leafColumns(lockedColumns(this.columns)).length;\n }\n\n return $(td).parent().children('td:not(.k-group-cell,.k-hierarchy-cell)').index(td) + lockedColumnOffset;\n },\n\n _modelForContainer: function(container) {\n container = $(container);\n\n if (!container.is(\"tr\") && this._editMode() !== \"popup\") {\n container = container.closest(\"tr\");\n }\n\n var id = container.attr(kendo.attr(\"uid\")) || container.find(\".k-popup-edit-form\").attr(kendo.attr(\"uid\"));\n\n return this.dataSource.getByUid(id);\n },\n\n _calculateColumnIndex: function(cell) {\n var cellIndex = this.cellIndex(cell);\n var virtualOffset = 0;\n\n if (this._hasVirtualColumns()) {\n virtualOffset = parseInt($(cell).closest(\"tr\").find(\"td\").first().attr(\"colspan\"), 10);\n virtualOffset = (virtualOffset > 1 ? virtualOffset - 1 : 0);\n }\n\n return cellIndex + virtualOffset;\n },\n\n _editable: function() {\n var that = this,\n editable = that.options.editable,\n handler = function() {\n var target = activeElement(),\n cell = that._editContainer;\n\n if (cell && cell[0] && !$.contains(cell[0], target) && cell[0] !== target && !$(target).closest(\".k-animation-container\").length) {\n if (that.editable.end()) {\n that.closeCell();\n } else {\n that._scrollVirtualWrapper();\n }\n }\n };\n\n if (editable) {\n this.wrapper.addClass(\"k-editable\");\n\n var mode = that._editMode();\n if (mode === \"incell\") {\n that.table.add(that.lockedTable)\n .on(\"mousedown\" + NS, NAVROW + \">\" + NAVCELL, function(e) {\n var target = $(e.target);\n if (that._editMode() === \"incell\" && target.hasClass(\"k-checkbox\") && target.prev().attr(kendo.attr(\"bind\"))) {\n e.preventDefault();\n }\n });\n\n if (editable.update !== false) {\n if (isMac) {\n that.wrapper\n .on(CLICK + NS, \".k-edit-cell > input[type='checkbox']\", function(e) {\n // checking /unchecking a checkbox does not change the document.activeElement to be the checkbox\n // this is necessary for the \"focusout\" event to be fired\n $(e.target).trigger(\"focus\");\n })\n .on(CLICK + NS, \".k-edit-cell\", function(e) {\n if (!$(e.target).is(\"input\")) {\n $(e.currentTarget).find(\"input[type='checkbox']\").trigger(\"focus\");\n }\n })\n .on(MOUSEDOWN + NS, \"tr:not(.k-grouping-row) > td\", function(e) {\n var editContainer = that._editContainer;\n\n if (editContainer && editContainer[0] && ($.contains(editContainer[0], e.target) || editContainer[0] === e.target)) {\n that._mousedownOnEditCell = true;\n } else {\n that._mousedownOnEditCell = false;\n }\n });\n }\n\n that.editableUserEvents = new kendo.UserEvents(that.wrapper, {\n filter: \"tr:not(.k-grouping-row) > td\",\n allowSelection: true,\n tap: function(e) {\n var td = $(e.target),\n isLockedCell = that.lockedTable && td.closest(\"table\")[0] === that.lockedTable[0];\n\n that._mousedownOnEditCell = false;\n\n if (td.hasClass(\"k-hierarchy-cell\") ||\n td.hasClass(\"k-detail-cell\") ||\n td.hasClass(\"k-group-cell\") ||\n td.hasClass(\"k-edit-cell\") ||\n td.has(\".k-grid-delete\").length ||\n (td.closest(\"tbody\")[0] !== that.tbody[0] && !isLockedCell) ||\n $(e.target).is(\":input\")) {\n return;\n }\n\n if (that.editable) {\n if (that.editable.end()) {\n $(activeElement()).trigger(\"blur\");\n that.closeCell();\n that.editCell(td);\n } else {\n that._scrollVirtualWrapper();\n }\n } else {\n that.editCell(td);\n }\n }\n });\n\n that.wrapper.on(\"focusin\" + NS, function() {\n // fix focus issue in IE\n if (!$.contains(this, activeElement())) {\n clearTimeout(that.timer);\n that.timer = null;\n }\n })\n .on(\"focusout\" + NS, function(e) {\n var shouldCloseCell = true;\n\n if ((isMac && that._mousedownOnEditCell) || that._virtualColScroll) {\n shouldCloseCell = false;\n }\n\n that._mousedownOnEditCell = false;\n\n if (shouldCloseCell) {\n that.timer = setTimeout(function() {\n handler(e);\n }, 1);\n }\n });\n }\n } else {\n if (editable.update !== false) {\n that.wrapper.on(CLICK + NS, \"tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-edit\", function(e) {\n var element = $(this);\n if (!that._belongsToGrid(element)) {\n return;\n }\n e.preventDefault();\n that.editRow(element.closest(\"tr\"));\n });\n\n if (that._isVirtualInlineEditable()) {\n that.wrapper.on(\"focusout\" + NS, \"tr:not(.k-grouping-row) > td\", function() {\n if (that.editable && !that.editable.end()) {\n that._scrollVirtualWrapper();\n }\n });\n }\n }\n }\n\n if (editable.destroy !== false) {\n that.wrapper.on(CLICK + NS, \"tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-delete\", function(e) {\n var element = $(this);\n if (!that._belongsToGrid(element)) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n that.removeRow(element.closest(\"tr\"));\n });\n } else {\n //Required for the MVC server wrapper delete button\n that.wrapper.on(CLICK + NS, \"tbody>tr:not(.k-detail-row,.k-grouping-row):visible .k-grid-delete\", function(e) {\n if (!that._belongsToGrid($(this))) {\n return;\n }\n e.stopPropagation();\n\n if (!that._confirmation()) {\n e.preventDefault();\n }\n });\n }\n }\n },\n\n editCell: function(cell) {\n cell = $(cell);\n\n var that = this,\n column = leafColumns(that.columns)[that._calculateColumnIndex(cell)],\n model = that._modelForContainer(cell);\n\n that.closeCell();\n\n if (model && isColumnEditable(column, model) && !column.command) {\n if (that.trigger(BEFOREEDIT, { model: model })) {\n return;\n }\n\n that._attachModelChange(model);\n\n that._editContainer = cell;\n\n if (that._shouldClearEditableState) {\n that._clearEditableState();\n }\n that.editable = cell.addClass(\"k-edit-cell\")\n .kendoEditable({\n fields: editField(column),\n model: model,\n target: that,\n change: function(e) {\n if (that.trigger(SAVE, { values: e.values, container: cell, model: model } )) {\n e.preventDefault();\n }\n },\n skipFocus: (that._isVirtualIncellEditable() || that._hasVirtualColumns()) && that._editableState ? true : false\n\n }).data(\"kendoEditable\");\n\n var tr = cell.parent().addClass(\"k-grid-edit-row\");\n\n if (that.lockedContent) {\n adjustRowHeight(tr[0], that._relatedRow(tr).addClass(\"k-grid-edit-row\")[0]);\n that._syncLockedScroll();\n }\n\n that.trigger(EDIT, { container: cell, model: model });\n }\n },\n\n _adjustLockedHorizontalScrollBar: function() {\n var table = this.table,\n content = table.parent();\n\n var scrollbar = table[0].offsetWidth > content[0].clientWidth ? kendo.support.scrollbar() : 0;\n this.lockedContent.height(content[0].offsetHeight - scrollbar);\n },\n\n _syncLockedScroll: function() {\n this.lockedContent[0].scrollTop = this.content[0].scrollTop;\n if (this.virtualScrollable) {\n this.lockedContent[0].scrollTop = this.wrapper.find(\".k-virtual-scrollable-wrap\")[0].scrollTop;\n }\n },\n\n _syncLockedContentHeight: function() {\n if (this.lockedTable) {\n if (!this.touchScroller) {\n this._adjustLockedHorizontalScrollBar();\n }\n this._adjustRowsHeight(this.table, this.lockedTable);\n }\n },\n\n _syncLockedHeaderHeight: function() {\n if (this.lockedHeader) {\n var lockedTable = this.lockedHeader.children(\"table\");\n var table = this.thead.parent();\n\n this._adjustRowsHeight(lockedTable, table);\n\n syncTableHeight(lockedTable, table);\n }\n },\n\n _syncLockedFooterHeight: function() {\n if (this.lockedFooter && this.footer && this.footer.length) {\n this._adjustRowsHeight(this.lockedFooter.children(\"table\"), this.footer.find(\".k-grid-footer-wrap > table\"));\n }\n },\n\n _destroyEditable: function() {\n var that = this;\n\n var destroy = function() {\n if (that.editable) {\n\n var container = that.editView ? that.editView.element : that._editContainer;\n var window = that._editContainer.data(\"kendoWindow\");\n\n if (container) {\n container.off(CLICK + NS, \".k-grid-cancel\", that._editCancelClickHandler);\n container.off(CLICK + NS, \".k-grid-update\", that._editUpdateClickHandler);\n }\n\n that._detachModelChange();\n that.editable.destroy();\n that.editable = null;\n if (window) {\n window.destroy();\n }\n that._editContainer = null;\n that._destroyEditView();\n that._editableIsClosing = null;\n }\n };\n\n if (that.editable) {\n if (that._editMode() === \"popup\" && !that._isMobile) {\n if (that._editableIsClosing) {\n that._editContainer.data(\"kendoWindow\").bind(\"deactivate\", destroy);\n }\n else {\n that._editableIsClosing = true;\n that._editContainer.data(\"kendoWindow\").bind(\"deactivate\", destroy).close();\n }\n } else {\n destroy();\n }\n }\n if (that._confirmDialog) {\n that._confirmDialog.close();\n that._confirmDialog.destroy();\n that._confirmDialog = null;\n }\n },\n\n _destroyEditView: function() {\n if (this.editView) {\n this.editView.purge();\n this.editView = null;\n this.pane.navigate(\"\");\n }\n },\n\n _attachModelChange: function(model) {\n var that = this;\n\n that._modelChangeHandler = function(e) {\n that._modelChange({ field: e.field, model: this });\n };\n\n model.bind(\"change\", that._modelChangeHandler);\n },\n\n _detachModelChange: function() {\n var that = this,\n container = that._editContainer,\n model = that._modelForContainer(container);\n\n if (model) {\n model.unbind(CHANGE, that._modelChangeHandler);\n }\n },\n\n closeCell: function(isCancel) {\n var that = this,\n cell = that._editContainer,\n column,\n tr,\n model;\n\n if (!cell) {\n return;\n }\n\n model = that._modelForContainer(cell);\n\n if (isCancel && that.trigger(\"cancel\", { container: cell, model: model })) {\n return;\n }\n\n that.trigger(CELLCLOSE, { type: isCancel ? \"cancel\" : \"save\", model: model, container: cell });\n\n cell.removeClass(\"k-edit-cell\");\n column = leafColumns(that.columns)[that._calculateColumnIndex(cell)];\n\n tr = cell.parent().removeClass(\"k-grid-edit-row\");\n\n if (that.lockedContent) {\n that._relatedRow(tr).removeClass(\"k-grid-edit-row\");\n }\n\n that._destroyEditable(); // editable should be destroyed before content of the container is changed\n\n that._displayCell(cell, column, model);\n\n if (that._shouldClearEditableState) {\n that._clearEditableState();\n }\n\n that.trigger(\"itemChange\", { item: tr, data: model, ns: ui });\n\n if (that.lockedContent) {\n adjustRowHeight(tr.css(\"height\", \"\")[0], that._relatedRow(tr).css(\"height\", \"\")[0]);\n }\n },\n\n _displayCell: function(cell, column, dataItem) {\n var that = this,\n state = { storage: {}, count: 0 },\n settings = extend({}, kendo.Template, that.options.templateSettings),\n tmpl = kendo.template(that._cellTmpl(column, state), settings);\n\n if (state.count > 0) {\n tmpl = tmpl.bind(state.storage);\n }\n\n cell.empty().html(tmpl(dataItem));\n\n that.angular(\"compile\", function() {\n return {\n elements: cell,\n data: [ { dataItem: dataItem } ]\n };\n });\n },\n\n removeRow: function(row) {\n if (!this._confirmation(row)) {\n return;\n }\n\n this._removeRow(row);\n },\n\n _removeRow: function(row) {\n var that = this,\n model,\n modelId,\n key,\n schema,\n mode = that._editMode();\n\n if (mode !== \"incell\") {\n that.cancelRow();\n }\n\n row = $(row);\n\n if (that.lockedContent) {\n row = row.add(that._relatedRow(row));\n }\n\n row = row.hide();\n if (that.dataSource._isGroupPaged()) {\n that._removeGroupIfEmpty(row);\n }\n\n model = that._modelForContainer(row);\n\n if (model && !that.trigger(REMOVE, { row: row, model: model })) {\n schema = that.dataSource.options.schema;\n if (that._selectedIds && schema && schema.model) {\n modelId = isFunction(that.dataSource.options.schema.model) ? that.dataSource.options.schema.model.fn.idField : that.dataSource.options.schema.model.id;\n key = model[modelId];\n delete that._selectedIds[key];\n }\n\n that.dataSource.remove(model);\n\n if (mode === \"inline\" || mode === \"popup\") {\n that.dataSource.sync();\n }\n } else if (mode === \"incell\") {\n that._destroyEditable();\n }\n },\n\n _editMode: function() {\n var mode = \"incell\",\n editable = this.options.editable;\n\n if (editable !== true) {\n if (typeof editable == \"string\") {\n mode = editable;\n } else {\n mode = editable.mode || mode;\n }\n }\n\n return mode;\n },\n\n editRow: function(row) {\n var model;\n var that = this;\n\n if (row instanceof ObservableObject) {\n model = row;\n } else {\n row = $(row);\n model = that._modelForContainer(row);\n }\n\n var mode = that._editMode();\n var container;\n\n that.cancelRow();\n\n if (model) {\n row = that.tbody.children(\"[\" + kendo.attr(\"uid\") + \"=\" + model.uid + \"]\");\n that._attachModelChange(model);\n\n if (mode === \"popup\") {\n that._createPopupEditor(model);\n } else if (mode === \"inline\") {\n that._createInlineEditor(row, model);\n } else if (mode === \"incell\") {\n $(row).children(DATA_CELL).each(function() {\n var cell = $(this);\n var column = leafColumns(that.columns)[that._calculateColumnIndex(cell)];\n\n model = that._modelForContainer(cell);\n\n if (model && (!model.editable || model.editable(column.field)) && column.field && !column.selectable) {\n that.editCell(cell);\n return false;\n }\n });\n }\n\n\n container = that.editView ? that.editView.element : that._editContainer;\n\n if (container) {\n if (!this._editCancelClickHandler) {\n this._editCancelClickHandler = this._editCancelClick.bind(this);\n }\n\n container.on(CLICK + NS, \".k-grid-cancel\", this._editCancelClickHandler);\n\n if (!this._editUpdateClickHandler) {\n this._editUpdateClickHandler = this._editUpdateClick.bind(this);\n }\n\n container.on(CLICK + NS, \".k-grid-update\", this._editUpdateClickHandler);\n }\n }\n },\n\n _editUpdateClick: function(e) {\n e.preventDefault();\n e.stopPropagation();\n\n this.saveRow();\n },\n\n _editCancelClick: function(e) {\n var that = this;\n var navigatable = that.options.navigatable;\n var model = that.editable.options.model;\n var container = that.editView ? that.editView.element : that._editContainer;\n\n e.preventDefault();\n e.stopPropagation();\n\n if (that.trigger(\"cancel\", { container: container, model: model })) {\n return;\n }\n\n var currentIndex = that.items().index($(that.current()).parent());\n\n that.cancelRow();\n\n if (navigatable) {\n that._setCurrent(that.items().eq(currentIndex).children().filter(NAVCELL).first());\n focusTable(that.table, true);\n }\n },\n\n _editFields: function(columns, model) {\n var fields = [];\n var column;\n\n for (var idx = 0; idx < columns.length; idx++) {\n column = columns[idx];\n if (column.selectable || column.command) {\n continue;\n }\n if (isColumnEditable(column, model)) {\n fields.push(editField(column));\n }\n }\n return fields;\n },\n\n _createPopupEditor: function(model) {\n var that = this;\n var html = '
<' + (that._isMobile ? 'ul class=\"k-edit-form-container k-listgroup k-listgroup-flush\">' : 'div class=\"k-edit-form-container\">');\n var column;\n var command;\n var idx;\n var length;\n var tmpl;\n var updateText;\n var cancelText;\n var updateIconClass;\n var cancelIconClass;\n var tempCommand;\n var columns = leafColumns(that.columns);\n var attr;\n var editMenuGuid = kendo.guid();\n var editable = that.options.editable;\n var template = editable.template;\n var options = isPlainObject(editable) ? editable.window : {};\n var settings = extend({}, kendo.Template, that.options.templateSettings);\n var state;\n var container;\n var buttonsHTML;\n\n if (that.trigger(BEFOREEDIT, { model: model })) {\n return;\n }\n\n options = options || {};\n\n if (template) {\n if (typeof template === STRING) {\n template = kendo.unescape(template);\n }\n\n html += (kendo.template(template, settings))(model);\n\n for (idx = 0, length = columns.length; idx < length; idx++) {\n column = columns[idx];\n if (column.command) {\n tempCommand = getCommand(column.command, \"edit\");\n if (tempCommand) {\n command = tempCommand;\n }\n }\n }\n } else {\n for (idx = 0, length = columns.length; idx < length; idx++) {\n column = columns[idx];\n if (column.selectable) {\n continue;\n }\n if (!column.command) {\n if (that._isMobile) {\n html += '
  • ';\n\n if (isColumnEditable(column, model)) {\n html += '';\n } else {\n state = { storage: {}, count: 0 };\n\n tmpl = kendo.template(that._cellTmpl(column, state), settings);\n\n if (state.count > 0) {\n tmpl = tmpl.bind(state.storage);\n }\n\n html += '';\n }\n\n html += \"
  • \";\n }\n } else if (column.command) {\n tempCommand = getCommand(column.command, \"edit\");\n if (tempCommand) {\n command = tempCommand;\n }\n }\n }\n }\n\n if (command) {\n if (isPlainObject(command)) {\n if (isPlainObject(command.text)) {\n updateText = command.text.update;\n cancelText = command.text.cancel;\n }\n if (isPlainObject(command.iconClass)) {\n updateIconClass = command.iconClass.update;\n cancelIconClass = command.iconClass.cancel;\n }\n\n if (command.attr) {\n attr = command.attr;\n }\n }\n }\n\n if (!that._isMobile) {\n buttonsHTML = '
    ';\n buttonsHTML += that._createButton({ name: \"update\", text: updateText, attr: attr, iconClass: updateIconClass }) + that._createButton({ name: \"canceledit\", text: cancelText, attr: attr, iconClass: cancelIconClass });\n buttonsHTML += '
    ';\n\n html += template ? buttonsHTML + '
    ' : '
    ';\n\n container = that._editContainer = $(html)\n .appendTo(that.wrapper).eq(0)\n .kendoWindow(extend({\n modal: true,\n resizable: false,\n draggable: true,\n title: that.options.messages.commands.edit || \"Edit\",\n visible: false,\n close: function(e) {\n if (e.userTriggered) {\n //The bellow line is required due to: draggable window in IE, change event will be triggered while the window is closing\n e.sender.element.trigger(\"focus\");\n if (that.trigger(\"cancel\", { container: container, model: model })) {\n e.preventDefault();\n return;\n }\n\n var currentIndex = that.items().index($(that.current()).parent());\n\n that._editableIsClosing = true;\n that.cancelRow();\n if (that.options.navigatable) {\n that._setCurrent(that.items().eq(currentIndex).children().filter(NAVCELL).first());\n focusTable(that.table, true);\n }\n }\n }\n }, options));\n } else {\n html += \"
    \";\n that.editView = that.pane.append(\n '
    ' +\n '
    ' +\n '' +\n (that.options.messages.commands.edit || \"Edit\") +\n '' +\n '
    ' +\n '
    ' +\n html +\n '
    ' +\n '
    ');\n container = that._editContainer = that.editView.element.find(\".k-popup-edit-form\");\n }\n\n if (!template && !that._isMobile) {\n that.editable = new ui.Form(that._editContainer.find(\".k-edit-form-container\"), {\n items: that._editFields(columns, model),\n buttonsTemplate: \"\",\n formData: model\n }).editable;\n that._editContainer.append(buttonsHTML);\n } else {\n that.editable = that._editContainer\n .kendoEditable({\n fields: that._isMobile ? that._editFields(columns, model) : null,\n model: model,\n clearContainer: false,\n target: that\n }).data(\"kendoEditable\");\n }\n\n that._openPopUpEditor();\n\n that.trigger(EDIT, { container: container, model: model });\n },\n\n _openPopUpEditor: function() {\n var that = this;\n var windowEditor = that._editContainer ? that._editContainer.data(\"kendoWindow\") : null;\n var windowOptions = (that.options.editable || {}).window || {};\n\n if (!this._isMobile) {\n if (windowEditor) {\n if (!windowOptions.position) {\n windowEditor.center();\n }\n\n windowEditor.open();\n }\n } else {\n this.pane.navigate(this.editView, this._editAnimation);\n }\n },\n\n _createInlineEditor: function(row, model) {\n var that = this;\n var column;\n var cell;\n var command;\n var fields = [];\n\n if (that.trigger(BEFOREEDIT, { model: model })) {\n return;\n }\n\n if (that.lockedContent) {\n row = row.add(that._relatedRow(row));\n }\n\n row.children(\":not(.k-group-cell,.k-hierarchy-cell)\").each(function() {\n cell = $(this);\n column = leafColumns(that.columns)[that._calculateColumnIndex(cell)];\n\n if (!column.command && isColumnEditable(column, model)) {\n fields.push(editField(column));\n cell.attr(kendo.attr(\"container-for\"), column.field);\n cell.empty();\n } else if (column.command) {\n command = getCommand(column.command, \"edit\");\n if (command) {\n cell.empty();\n\n var updateText,\n cancelText,\n updateIconClass,\n cancelIconClass,\n attr;\n\n if (isPlainObject(command)) {\n if (isPlainObject(command.text)) {\n updateText = command.text.update;\n cancelText = command.text.cancel;\n }\n if (isPlainObject(command.iconClass)) {\n updateIconClass = command.iconClass.update;\n cancelIconClass = command.iconClass.cancel;\n }\n\n if (command.attr) {\n attr = command.attr;\n }\n }\n\n $(that._createButton({ name: \"update\", text: updateText, attr: attr, iconClass: updateIconClass }) +\n that._createButton({ name: \"canceledit\", text: cancelText, attr: attr, iconClass: cancelIconClass })).appendTo(cell);\n }\n }\n });\n\n that._editContainer = row;\n that._editContainer.addClass(\"k-grid-edit-row\");\n\n if (that._shouldClearEditableState) {\n that._clearEditableState();\n }\n\n that.editable = new kendo.ui.Editable(that._editContainer, {\n target: that,\n fields: fields,\n model: model,\n skipFocus: (that._isVirtualInlineEditable() && that._editableState && (that._editableState.field ? true : false)) || that._hasVirtualColumns(),\n clearContainer: false\n });\n\n if (row.length > 1) {\n\n adjustRowHeight(row[0], row[1]);\n that._applyLockedContainersWidth();\n }\n\n that.trigger(EDIT, { container: row, model: model });\n },\n\n cancelRow: function(notify) {\n var that = this,\n container = that._editContainer,\n model;\n\n if (container) {\n model = that._modelForContainer(container);\n\n if (!model || (notify && that.trigger(\"cancel\", { container: container, model: model }))) {\n return;\n }\n\n that._destroyEditable();\n\n that.dataSource.cancelChanges(model);\n\n that._clearEditableState();\n\n if (that._editMode() !== \"popup\") {\n that._displayRow(container);\n } else {\n that._displayRow(that.tbody.find(\"[\" + kendo.attr(\"uid\") + \"=\" + model.uid + \"]\"));\n }\n }\n },\n\n saveRow: function() {\n var that = this;\n var container = this._editContainer;\n var model = this._modelForContainer(container);\n var deferred = $.Deferred();\n var valid;\n\n if (!container || !this.editable) {\n return deferred.resolve().promise();\n }\n\n valid = that.editable && that.editable.end();\n\n if (!valid || this.trigger(SAVE, { container: container, model: model })) {\n if (!valid) {\n that._scrollVirtualWrapper();\n }\n\n return deferred.reject().promise();\n }\n\n that._clearEditableState();\n\n return this.dataSource.sync();\n },\n\n _displayRow: function(row) {\n var that = this,\n model = that._modelForContainer(row),\n related,\n newRow,\n nextRow,\n isSelected = row.hasClass(\"k-selected\"),\n isAlt = row.hasClass(\"k-alt\");\n\n if (model) {\n\n if (that.lockedContent) {\n related = $((isAlt ? that.lockedAltRowTemplate : that.lockedRowTemplate)(model));\n that._relatedRow(row.last()).replaceWith(related);\n }\n\n that.angular(\"cleanup\", function() { return { elements: row.get() }; });\n\n newRow = $((isAlt ? that.altRowTemplate : that.rowTemplate)(model));\n if (!row.is(\":visible\")) {\n newRow.hide();\n }\n row.replaceWith(newRow);\n\n that.trigger(\"itemChange\", { item: newRow, data: model, ns: ui });\n\n if (related && related.length) {\n that.trigger(\"itemChange\", { item: related, data: model, ns: ui });\n }\n\n var angularElements = newRow;\n var angularData = [{ dataItem: model }];\n\n if (related && related.length) {\n angularElements = newRow.add(related);\n angularData.push({ dataItem: model });\n }\n\n that.angular(\"compile\", function() {\n return {\n elements: angularElements.get(),\n data: angularData\n };\n });\n\n\n if (isSelected && (that.options.selectable || that._checkBoxSelection)) {\n that.select(newRow.add(related));\n }\n\n if (related) {\n adjustRowHeight(newRow[0], related[0]);\n }\n\n nextRow = newRow.next();\n if (nextRow.hasClass(\"k-detail-row\") && nextRow.is(\":visible\")) {\n newRow.find(\".k-hierarchy-cell .k-icon\")\n .removeClass(\"k-i-expand\")\n .addClass(\"k-i-collapse\");\n }\n }\n },\n\n _showMessage: function(messages, row) {\n var that = this;\n\n if (!that._isMobile) {\n // eslint-disable-next-line no-alert\n return window.confirm(messages.title);\n }\n\n var confirmDialog = that._confirmDialog = new kendo.ui.Confirm($(\"
    \").appendTo(document.body), {\n modal: {\n preventScroll: true\n },\n closable: false,\n title: false,\n content: messages.title,\n messages: {\n okText: messages.confirmDelete,\n cancel: messages.cancelDelete\n },\n open: function() {\n if (that.content) {\n that.content.data(OVERFLOW, that.content.css(OVERFLOW));\n that.content.css(OVERFLOW, HIDDEN);\n }\n },\n close: function() {\n if (that.content) {\n that.content.css(OVERFLOW, that.content.data(OVERFLOW));\n }\n }\n });\n\n confirmDialog.result\n .done(function() {\n that._removeRow(row);\n })\n .fail(function() {\n var confirmDialog = that._confirmDialog;\n\n if (confirmDialog) {\n confirmDialog.close();\n confirmDialog.destroy();\n }\n });\n\n return false;\n },\n\n _confirmation: function(row) {\n var that = this,\n editable = that.options.editable,\n confirmation = (editable === true || typeof editable === STRING) ? that.options.messages.editable.confirmation : editable.confirmation;\n\n if (isPlainObject(editable) && typeof editable.mode === STRING && typeof confirmation !== FUNCTION && typeof confirmation !== STRING && confirmation !== false) {\n confirmation = that.options.messages.editable.confirmation;\n }\n\n if (confirmation !== false && confirmation != null) {\n\n if (typeof confirmation === FUNCTION) {\n confirmation = confirmation(that._modelForContainer(row));\n }\n\n return that._showMessage({\n confirmDelete: editable.confirmDelete || that.options.messages.editable.confirmDelete,\n cancelDelete: editable.cancelDelete || that.options.messages.editable.cancelDelete,\n title: confirmation === true ? that.options.messages.editable.confirmation : confirmation\n }, row);\n }\n\n return true;\n },\n\n cancelChanges: function() {\n var that = this;\n\n that.dataSource.cancelChanges();\n\n if (that._isVirtualEditable()) {\n that._virtualPageToTop(function() {\n that.virtualScrollable.scrollToTop();\n });\n }\n },\n\n saveChanges: function() {\n var that = this;\n var valid = that.editable && that.editable.end();\n\n if ((valid || !that.editable) && !that.trigger(SAVECHANGES)) {\n that.dataSource.sync();\n } else if (!valid) {\n that._scrollVirtualWrapper();\n }\n },\n\n addRow: function() {\n var that = this,\n index,\n dataSource = that.dataSource,\n mode = that._editMode(),\n createAt = that.options.editable.createAt || \"\",\n pageSize = dataSource.pageSize(),\n view = dataSource.view() || [];\n var createAtBottom = createAt.toLowerCase() === BOTTOM;\n var model;\n var virtualEditable = that._isVirtualEditable();\n\n if ((that.editable && that.editable.end()) || !that.editable) {\n if (mode != \"incell\") {\n that.cancelRow();\n }\n\n index = dataSource.indexOf(view[0]);\n\n if (createAtBottom) {\n index += view.length;\n\n if (pageSize && !dataSource.options.serverPaging && pageSize <= view.length) {\n index -= 1;\n }\n }\n\n if (index < 0) {\n if (dataSource.page() > dataSource.totalPages()) {\n index = (dataSource.page() - 1) * pageSize;\n } else {\n index = 0;\n }\n }\n\n if (that.options.navigatable && mode == \"incell\") {\n that._removeCurrent();\n }\n\n if (virtualEditable) {\n that._virtualAddRow();\n } else {\n model = dataSource.insert(index, {});\n that._editModel(model);\n }\n } else {\n that._scrollVirtualWrapper();\n }\n },\n\n _editModel: function(model) {\n var that = this;\n var createAt = that.options.editable.createAt || \"\";\n var mode = that._editMode();\n\n if (model) {\n var id = model.uid,\n table = that.lockedContent ? that.lockedTable : that.table,\n row = table.find(\"tr[\" + kendo.attr(\"uid\") + \"=\" + id + \"]\"),\n cell = row.children(\"td:not(.k-group-cell,.k-hierarchy-cell)\").eq(that._firstEditableColumnIndex(row));\n\n if (mode === \"inline\" && row.length) {\n that.editRow(row);\n } else if (mode === \"popup\") {\n that.editRow(model);\n } else if (cell.length) {\n that.editCell(cell);\n }\n\n if (createAt.toLowerCase() == \"bottom\" && that.lockedContent) {\n //scroll the containers to the bottom\n that.lockedContent[0].scrollTop = that.content[0].scrollTop = that.table[0].offsetHeight;\n }\n }\n },\n\n _virtualAddRow: function() {\n var that = this;\n var createAtBottom = (that.options.editable.createAt || \"\").toLowerCase() === BOTTOM;\n\n that._clearEditableState();\n\n if (createAtBottom) {\n that._virtualAddRowAtBottom();\n } else {\n that._virtualAddRowAtTop();\n }\n },\n\n _virtualAddRowAtTop: function() {\n var that = this;\n var dataSource = that.dataSource;\n var virtualScrollable = that.virtualScrollable;\n var model;\n\n if (dataSource.page() === 1) {\n model = dataSource.insert(0, {});\n that._editModel(model);\n virtualScrollable.scrollToTop();\n } else {\n that._virtualPageToTop(function() {\n model = dataSource.insert(0, {});\n that._editModel(model);\n virtualScrollable.scrollToTop();\n });\n }\n },\n\n _virtualAddRowAtBottom: function() {\n var that = this;\n var dataSource = that.dataSource;\n var virtualScrollable = that.virtualScrollable;\n var index = dataSource.total();\n var model;\n\n if (dataSource.at(index - 1) instanceof ObservableObject) {\n model = dataSource.insert(index, {});\n\n that._virtualPageToBottom(function() {\n that._editModel(model);\n virtualScrollable.scrollToBottom();\n });\n } else {\n that._virtualPageToBottom(function() {\n model = dataSource.insert(index, {});\n that._editModel(model);\n virtualScrollable.scrollToBottom();\n });\n }\n },\n\n _virtualPageToTop: function(callback) {\n var that = this;\n\n that._virtualPage(0, that.dataSource.take(), function() {\n callback();\n });\n },\n\n _virtualPageToBottom: function(callback) {\n var that = this;\n var dataSource = that.dataSource;\n var take = dataSource.take();\n var total = dataSource.total();\n var skip = total > take ? (total - take) : 0;\n\n that._virtualPage(skip, take, function() {\n callback();\n });\n },\n\n _virtualPage: function(skip, take, callback) {\n var that = this;\n\n if (that._isVirtualEditable()) {\n that.virtualScrollable._preventScroll = true;\n that.virtualScrollable._page(skip, take, callback);\n }\n },\n\n _firstEditableColumnIndex: function(container) {\n var that = this,\n column,\n columns = leafColumns(that.columns),\n idx,\n length,\n model = that._modelForContainer(container);\n\n for (idx = 0, length = columns.length; idx < length; idx++) {\n column = columns[idx];\n\n if (model && (!model.editable || model.editable(column.field)) && !column.command && column.field && column.hidden !== true) {\n return idx;\n }\n }\n return -1;\n },\n\n _toolbar: function() {\n var that = this,\n wrapper = that.wrapper,\n toolbar = that.options.toolbar,\n editable = that.options.editable,\n container;\n\n if (toolbar) {\n container = that.wrapper.find(\".k-grid-toolbar\");\n\n if (!container.length) {\n if (!isFunction(toolbar)) {\n toolbar = (typeof toolbar === STRING ? toolbar : that._toolbarTmpl(toolbar).replace(templateHashRegExp, \"\\\\#\"));\n toolbar = kendo.template(toolbar).bind(that);\n }\n\n container = $('
    ')\n .html(toolbar({}))\n .prependTo(wrapper);\n\n that.angular(\"compile\", function() {\n return { elements: container.get() };\n });\n }\n\n if (editable && editable.create !== false) {\n container.on(CLICK + NS, \".k-grid-add\", function(e) { e.preventDefault(); that.addRow(); })\n .on(CLICK + NS, \".k-grid-cancel-changes\", function(e) { e.preventDefault(); that.cancelChanges(); })\n .on(CLICK + NS, \".k-grid-save-changes\", function(e) { e.preventDefault(); that.saveChanges(); });\n }\n\n container.on(CLICK + NS, \".k-grid-excel\", function(e) {\n e.preventDefault();\n\n that.saveAsExcel();\n });\n\n container.on(CLICK + NS, \".k-grid-pdf\", function(e) {\n e.preventDefault();\n\n var promise = that.saveAsPDF();\n if (promise) {\n promise.done(function() {\n that._pdfInitialized = false;\n });\n }\n });\n\n container.on(INPUT + NS, \".k-grid-search input\", function(e) {\n var input = e.currentTarget;\n clearTimeout(that._searchTimeOut);\n that._searchTimeOut = setTimeout(function() {\n that._searchTimeOut = null;\n var options = that.options;\n var searchFields = options.search ? options.search.fields : null;\n var expression = { filters: [], logic: \"or\" };\n var value = input.value;\n\n if (!searchFields) {\n searchFields = getColumnsFields(options.columns);\n }\n\n if (that.dataSource.options.endless) {\n that.dataSource.options.endless = null;\n that._endlessPageSize = that.dataSource.options.pageSize;\n }\n\n if (value) {\n for (var i = 0; i < searchFields.length; i++) {\n that._pushExpression(expression.filters, searchFields[i], value);\n }\n } else {\n expression = {};\n }\n\n that.dataSource.filter(expression);\n\n }, 300);\n });\n }\n },\n\n _pushExpression: function(filters, field, value) {\n var that = this,\n isServerFiltering = that.dataSource.options.serverFiltering,\n defaultOperators = {\n string: \"contains\",\n number: \"gte\",\n date: \"gte\",\n enums: \"eq\",\n boolean: \"eq\"\n },\n name = field.name || field,\n operator = field.operator,\n modelInfo = that.dataSource.reader.model && that.dataSource.reader.model.fields,\n fieldInfo = modelInfo && modelInfo[name],\n parseFn = fieldInfo && fieldInfo.parse,\n expression = {\n field: name,\n operator: operator || defaultOperators.string,\n value: value\n };\n\n if ((operator || isServerFiltering) && fieldInfo && kendo.isFunction(parseFn) && parseFn(value) !== null) {\n extend(expression, {\n operator: operator || defaultOperators[fieldInfo.type],\n value: parseFn(value)\n });\n }\n\n if (isServerFiltering && fieldInfo && kendo.isFunction(parseFn) && parseFn(value) === null) {\n return;\n }\n\n filters.push(expression);\n },\n\n _toolbarTmpl: function(commands) {\n var that = this,\n idx,\n length,\n html = \"\",\n command,\n searchText = \"\",\n messages = that.options.messages.commands;\n\n if (isArray(commands)) {\n for (idx = 0, length = commands.length; idx < length; idx++) {\n command = typeof commands[idx] === 'string' ? commands[idx].toLowerCase() : (commands[idx].name || \"\").toLowerCase();\n if (command === \"search\") {\n if (typeof commands[idx] !== 'string') {\n searchText = commands[idx].text;\n }\n searchText = searchText || messages.search;\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n html += \"\";\n } else {\n html += that._createButton(commands[idx]);\n }\n }\n }\n\n return html;\n },\n\n _createButton: function(command) {\n var button,\n template = command.template || COMMANDBUTTONTMPL,\n commandName = typeof command === STRING ? command : command.name || command.text,\n className = defaultCommands[commandName] ? defaultCommands[commandName].className : \"k-grid-\" + (commandName || \"\").replace(/\\s/g, \"\"),\n options = { className: className, text: commandName, attr: \"\", iconClass: \"\" },\n messages = this.options.messages.commands,\n attributeClassMatch;\n\n if (!commandName && !(isPlainObject(command) && command.template)) {\n throw new Error(\"Custom commands should have name specified\");\n }\n\n if (isPlainObject(command)) {\n command = extend(true, {}, command);\n\n if (command.className && inArray(options.className, command.className.split(\" \")) < 0) {\n command.className += \" \" + options.className;\n } else if (command.className === undefined) {\n command.className = options.className;\n }\n\n if (command.className.indexOf(\"k-primary\") > -1) {\n command.className = command.className.replace(\"k-primary\", \"\");\n command.themeColor = \"primary\";\n }\n\n if (commandName === \"edit\") {\n command = extend(true, {}, command);\n command.text = isPlainObject(command.text) ? command.text.edit : command.text;\n command.iconClass = isPlainObject(command.iconClass) ? command.iconClass.edit : command.iconClass;\n }\n\n if (command.attr) {\n if (isPlainObject(command.attr)) {\n command.attr = stringifyAttributes(command.attr);\n }\n\n if (typeof command.attr === STRING) {\n attributeClassMatch = command.attr.match(/class=\"(.+?)\"/);\n\n if (attributeClassMatch && inArray(attributeClassMatch[1], command.className.split(\" \")) < 0) {\n command.className += \" \" + attributeClassMatch[1];\n }\n }\n }\n\n options = extend(true, options, defaultCommands[commandName], { text: messages[commandName] }, command);\n } else {\n options = extend(true, options, defaultCommands[commandName], { text: messages[commandName] });\n }\n\n button = kendo.template(template)(options);\n\n if (!command.template) {\n return kendo.html.renderButton($(button), options);\n } else {\n return button;\n }\n },\n\n _hasFooters: function() {\n return !!this.footerTemplate ||\n !!this.groupFooterTemplate ||\n (this.footer && this.footer.length > 0) ||\n this.wrapper.find(\".k-grid-footer\").length > 0;\n },\n\n _groupable: function() {\n var that = this;\n\n if (that._groupableClickHandler) {\n that.table.add(that.lockedTable).off(CLICK + NS, that._groupableClickHandler);\n } else {\n that._groupableClickHandler = function(e) {\n var element = $(this),\n groupRow = element.closest(\"tr\");\n\n var group = that._groupRows ? that._groupRows[that.wrapper.find(\".k-grouping-row\").index(groupRow)] : { };\n\n if (element.hasClass('k-i-collapse')) {\n if (!that.trigger(\"groupCollapse\", { group: group, element: groupRow })) {\n that.collapseGroup(groupRow);\n }\n } else {\n if (!that.trigger(\"groupExpand\", { group: group, element: groupRow })) {\n that.expandGroup(groupRow);\n }\n }\n e.preventDefault();\n e.stopPropagation();\n };\n }\n\n if (that._isLocked()) {\n that.lockedTable.on(CLICK + NS, \".k-grouping-row .k-i-expand, .k-grouping-row .k-i-collapse\", that._groupableClickHandler);\n } else {\n that.table.on(CLICK + NS, \".k-grouping-row .k-i-expand, .k-grouping-row .k-i-collapse\", that._groupableClickHandler);\n }\n\n that._attachGroupable();\n },\n\n _attachGroupable: function() {\n var that = this,\n wrapper = that.wrapper,\n groupable = that.options.groupable,\n draggables = HEADERCELLS + \"[\" + kendo.attr(\"field\") + \"]\",\n filter = that.content ? \".k-grid-header:first \" + draggables : \"table:first>.k-grid-header \" + draggables;\n\n if (groupable && groupable.enabled !== false) {\n if (!wrapper.has(\"div.k-grouping-header\")[0]) {\n $(\"
     
    \").addClass(\"k-grouping-header\").prependTo(wrapper);\n }\n\n if (that.groupable) {\n that._destroyGroupable();\n }\n\n if (browser.chrome) {\n wrapper.find(\"div.k-grouping-header\").css(\"touch-action\", \"none\");\n wrapper.find(filter).css(\"touch-action\", \"none\");\n }\n\n that.groupable = new ui.Groupable(wrapper, extend({}, groupable, {\n draggable: that._draggableInstance,\n groupContainer: \">div.k-grouping-header\",\n dataSource: that.dataSource,\n draggableElements: filter,\n filter: filter,\n allowDrag: that.options.reorderable,\n removeGroup: function(e) {\n that._showUngroupedColumn(e);\n },\n change: function(e) {\n if (that.trigger(\"group\", { groups: e.groups })) {\n e.preventDefault();\n } else {\n that._clearEditableState();\n that._hideGroupedColumns(e.groups);\n if (that.dataSource.options.endless) {\n that._resetEndless();\n }\n }\n }\n }));\n\n that._addGroupableOptionsToHeader();\n }\n },\n\n _showUngroupedColumn: function(group) {\n var columns = leafColumns(this.columns);\n var i;\n\n for (i = 0; i < columns.length; i++) {\n if (columns[i].uid == group.colID && columns[i].hideOnGroup) {\n this.showColumn(columns[i]);\n }\n }\n },\n\n _hideGroupedColumns: function(groups) {\n if (!groups) {\n return;\n }\n var columns = leafColumns(this.columns);\n var fields = [];\n var i;\n\n for (i = 0; i < groups.length; i++) {\n if (groups[i].colID) {\n fields.push(groups[i].colID);\n }\n }\n\n for (i = 0; i < columns.length; i++) {\n if (fields.indexOf(columns[i].uid) >= 0 && columns[i].hideOnGroup) {\n this.hideColumn(columns[i]);\n }\n }\n },\n\n _resetEndless: function() {\n var that = this;\n that.dataSource.options.endless = null;\n that._endlessPageSize = that.dataSource.options.pageSize;\n that.dataSource._skip = 0;\n that.dataSource._pageSize = that.dataSource._take = that._endlessPageSize;\n that.dataSource._page = 1;\n },\n\n _addGroupableOptionsToHeader: function() {\n var that = this;\n var columns = flatColumns(that.columns);\n var columnFieldMap = {};\n var field = \"\";\n var headerCells = that._headerCells();\n var cellFieldAttr = \"\";\n var headerCell;\n var columnOptions;\n var i;\n\n for (i = 0; i < columns.length; i++) {\n field = columns[i].field;\n columnFieldMap[columns[i].field] = columns[i];\n }\n\n for (i = 0; i < headerCells.length; i++) {\n headerCell = headerCells.eq(i);\n cellFieldAttr = headerCell.attr(kendo.attr(FIELD));\n columnOptions = columnFieldMap[cellFieldAttr];\n\n if (columnOptions && columnOptions.groupable && columnOptions.groupable.sort) {\n headerCell.data(GROUP_SORT, columnOptions.groupable.sort);\n }\n }\n },\n\n _destroyGroupable: function() {\n var that = this;\n\n if (that.groupable && that.groupable.element) {\n that.groupable.element.kendoGroupable(\"destroy\");\n }\n\n that.groupable = null;\n\n that._removeGroupableOptionsFromHeader();\n },\n\n _removeGroupableOptionsFromHeader: function() {\n var that = this;\n var headerCells = that._headerCells();\n\n for (var i = 0; i < headerCells.length; i++) {\n headerCells.eq(i).removeData(GROUP_SORT);\n }\n },\n\n _continuousItems: function(filter, cell) {\n if (!this.lockedContent) {\n return;\n }\n\n var that = this;\n\n var elements = that.table.add(that.lockedTable);\n\n var lockedItems = $(filter, elements[0]);\n var nonLockedItems = $(filter, elements[1]);\n var columns = cell ? lockedColumns(that.columns).length : 1;\n var nonLockedColumns = cell ? that.columns.length - columns : 1;\n var result = [];\n\n for (var idx = 0; idx < lockedItems.length; idx += columns) {\n push.apply(result, lockedItems.slice(idx, idx + columns));\n push.apply(result, nonLockedItems.splice(0, nonLockedColumns));\n }\n\n return result;\n },\n\n _selectable: function() {\n var that = this,\n multi,\n cell,\n notString = [],\n isLocked = that._isLocked(),\n selectable = that.options.selectable;\n\n if (selectable) {\n\n if (that.selectable) {\n that.selectable.destroy();\n }\n\n that._selectedIds = {};\n\n selectable = kendo.ui.Selectable.parseOptions(selectable);\n\n multi = selectable.multiple;\n cell = selectable.cell;\n\n if (that._hasDetails()) {\n notString[notString.length] = \".k-detail-row\";\n }\n if (that.options.groupable || that._hasFooters() || that._groups()) {\n notString[notString.length] = \".k-grouping-row,.k-group-footer\";\n }\n\n notString = notString.join(\",\");\n\n if (notString !== \"\") {\n notString = \":not(\" + notString + \")\";\n }\n\n var elements = that.table;\n if (isLocked) {\n elements = elements.add(that.lockedTable);\n }\n\n var filter = \">\" + (cell ? SELECTION_CELL_SELECTOR : \"tbody>tr\" + notString);\n that.selectable = new kendo.ui.Selectable(elements, {\n filter: filter,\n aria: true,\n multiple: multi,\n dragToSelect: that.options.selectable && that.options.selectable.dragToSelect,\n change: function(e) {\n var selectedValues;\n if (!cell) {\n that._persistSelectedRows();\n }\n\n if (that._checkBoxSelection) {\n selectedValues = that.selectable.value();\n that._uncheckCheckBoxes();\n that._checkRows(selectedValues);\n if (selectedValues.length && selectedValues.length === that.items().length) {\n that._toggleHeaderCheckState(true);\n } else {\n that._toggleHeaderCheckState(false);\n }\n }\n\n if (e.event) {\n that.trigger(CHANGE);\n }\n },\n useAllItems: isLocked && multi && cell,\n relatedTarget: function(items) {\n if (cell || !isLocked) {\n return;\n }\n\n var related;\n var result = $();\n for (var idx = 0, length = items.length; idx < length; idx ++) {\n related = that._relatedRow(items[idx]);\n\n if (inArray(related[0], items) < 0) {\n result = result.add(related);\n }\n }\n\n return result;\n },\n continuousItems: function() {\n return that._continuousItems(filter, cell);\n },\n ignoreOverlapped: that.options.selectable && that.options.selectable.ignoreOverlapped,\n addIdToRanges: true\n });\n\n if (that.options.navigatable) {\n elements.on(\"keydown\" + NS, function(e) {\n var current = that.current();\n var target = e.target;\n var eventObject = { event: e };\n var triggerChange;\n var lastSelection;\n if (!current) {\n return;\n }\n if (e.keyCode === keys.SPACEBAR && !e.shiftKey && $.inArray(target, elements) > -1 &&\n !current.is(\".k-edit-cell,.k-header\") &&\n current.parent().is(\":not(.k-grouping-row,.k-detail-row,.k-group-footer)\")) {\n e.preventDefault();\n e.stopPropagation();\n current = cell ? current : current.parent();\n triggerChange = !current.hasClass(SELECTED) || that.selectable.value().length > 1;\n\n if (isLocked && !cell) {\n current = current.add(that._relatedRow(current));\n }\n\n if (multi) {\n if (!e.ctrlKey) {\n that.selectable.clear();\n } else {\n if (current.hasClass(SELECTED)) {\n that._deselectCheckRows(current);\n return;\n }\n }\n } else {\n that.selectable.clear();\n }\n if (!cell) {\n that.selectable._lastActive = current;\n }\n that.selectable.value(current);\n if (triggerChange) {\n that.trigger(CHANGE);\n }\n } else if (!cell &&\n ($(target).is(\"td\") || ($(target).is(\"table\") && inArray(target, this._navigatableTables))) &&\n ((e.shiftKey && e.keyCode == keys.LEFT) ||\n (e.shiftKey && e.keyCode == keys.RIGHT) ||\n (e.shiftKey && e.keyCode == keys.UP) ||\n (e.shiftKey && e.keyCode == keys.DOWN) ||\n (e.keyCode === keys.SPACEBAR && e.shiftKey))) {\n e.preventDefault();\n e.stopPropagation();\n current = current.parent();\n\n lastSelection = that.selectable.value();\n\n if (isLocked) {\n current = current.add(that._relatedRow(current));\n }\n\n if (multi) {\n if (!that.selectable._lastActive) {\n that.selectable._lastActive = current;\n }\n that.selectable.selectRange(that.selectable._firstSelectee(), current);\n\n if (!compareElements(lastSelection, that.selectable.value())) {\n that.trigger(CHANGE, eventObject);\n }\n } else if (!current.hasClass(\"k-selected\")) {\n that.selectable.clear();\n that.selectable.value(current);\n that.trigger(CHANGE);\n }\n }\n });\n }\n }\n },\n\n _clipboard: function() {\n var options = this.options;\n var selectable = options.selectable;\n if (selectable && options.allowCopy) {\n var grid = this;\n if (!options.navigatable) {\n grid.table.attr(\"tabindex\", 0);\n\n grid.table.add(grid.lockedTable)\n .on(\"mousedown\" + NS + \" keydown\" + NS, \".k-detail-cell\", function(e) {\n if (e.target !== e.currentTarget) {\n e.stopImmediatePropagation();\n }\n })\n .on(\"mousedown\" + NS, NAVROW + \">\" + NAVCELL, tableClick.bind(grid));\n }\n grid.copyHandler = grid.copySelection.bind(grid);\n grid.updateClipBoardState = function() {\n if (grid.areaClipBoard) {\n grid.areaClipBoard.val(grid.getTSV()).trigger(\"focus\").select();\n }\n };\n grid.bind(\"change\",grid.updateClipBoardState);\n grid.wrapper.on(\"keydown\", grid.copyHandler);\n grid.clearAreaHandler = grid.clearArea.bind(grid);\n grid.wrapper.on(\"keyup\", grid.clearAreaHandler);\n }\n },\n\n copySelectionToClipboard: function(includeHeaders) {\n this._createAreaClipBoard();\n this.areaClipBoard.val(this.getTSV(includeHeaders)).trigger(\"focus\").select();\n document.execCommand('copy');\n },\n\n copySelection: function(e) {\n if ((e instanceof jQuery.Event && !(e.ctrlKey || e.metaKey)) ||\n $(e.target).is(\"input:visible,textarea:visible\") ||\n (window.getSelection && window.getSelection().toString()) ||\n (document.selection && document.selection.createRange().text) ) {\n return;\n }\n\n this._createAreaClipBoard();\n this.areaClipBoard.val(this.getTSV()).trigger(\"focus\").select();\n\n },\n\n _createAreaClipBoard: function() {\n if (!this.areaClipBoard) {\n this.areaClipBoard =\n $(\"