{"version":3,"file":"kendo.treelist.min.js","sources":["kendo.treelist.js"],"sourcesContent":["(function(f, define) {\n define('kendo.treelist',[\n \"kendo.dom\",\n \"kendo.data\",\n \"kendo.columnsorter\",\n \"kendo.editable\",\n \"kendo.window\",\n \"kendo.filtermenu\",\n \"kendo.columnmenu\",\n \"kendo.selectable\",\n \"kendo.resizable\",\n \"kendo.treeview.draganddrop\",\n \"kendo.pager\",\n 'kendo.filtercell',\n 'kendo.textbox',\n 'kendo.form'\n ], f);\n})(function() {\n\nvar __meta__ = {\n id: \"treelist\",\n name: \"TreeList\",\n category: \"web\",\n description: \"The TreeList widget displays self-referencing data and offers rich support for interacting with data, sorting, filtering, and selection.\",\n depends: [ \"dom\", \"data\", \"pager\" ],\n features: [ {\n id: \"treelist-sorting\",\n name: \"Sorting\",\n description: \"Support for column sorting\",\n depends: [ \"columnsorter\" ]\n }, {\n id: \"treelist-filtering\",\n name: \"Filtering\",\n description: \"Support for record filtering\",\n depends: [ \"filtermenu\" ]\n }, {\n id: \"treelist-columnmenu\",\n name: \"Column menu\",\n description: \"Support for header column menu\",\n depends: [ \"columnmenu\" ]\n }, {\n id: \"treelist-editing\",\n name: \"Editing\",\n description: \"Support for record editing\",\n depends: [ \"editable\", \"window\", \"textbox\", \"form\" ]\n }, {\n id: \"treelist-selection\",\n name: \"Selection\",\n description: \"Support for row selection\",\n depends: [ \"selectable\" ]\n }, {\n id: \"treelist-column-resize\",\n name: \"Column resizing\",\n description: \"Support for column resizing\",\n depends: [ \"resizable\" ]\n }, {\n id: \"treelist-dragging\",\n name: \"Drag & Drop\",\n description: \"Support for drag & drop of rows\",\n depends: [ \"treeview.draganddrop\" ]\n }, {\n id: \"treelist-excel-export\",\n name: \"Excel export\",\n description: \"Export data as Excel spreadsheet\",\n depends: [ \"excel\" ]\n }, {\n id: \"treelist-pdf-export\",\n name: \"PDF export\",\n description: \"Export data as PDF\",\n depends: [ \"pdf\", \"drawing\" ]\n }, {\n id: \"treelist-paging\",\n name: \"Paging\",\n description: \"Support for treelist paging\",\n depends: [ \"pager\" ]\n } ]\n};\n\n(function($, undefined) {\n var data = kendo.data;\n var kendoDom = kendo.dom;\n var kendoDomElement = kendoDom.element;\n var kendoTextElement = kendoDom.text;\n var kendoHtmlElement = kendoDom.html;\n var outerWidth = kendo._outerWidth;\n var keys = kendo.keys;\n var outerHeight = kendo._outerHeight;\n var ui = kendo.ui;\n var DataBoundWidget = ui.DataBoundWidget;\n var DataSource = data.DataSource;\n var ObservableArray = data.ObservableArray;\n var Query = data.Query;\n var Model = data.Model;\n var browser = kendo.support.browser;\n var kendoTemplate = kendo.template;\n var activeElement = kendo._activeElement;\n var touchDevice = kendo.support.touch;\n\n var isArray = Array.isArray;\n var extend = $.extend;\n var map = $.map;\n var grep = $.grep;\n var inArray = $.inArray;\n var isPlainObject = $.isPlainObject;\n\n var push = Array.prototype.push;\n\n var STRING = \"string\";\n var CHANGE = \"change\";\n var ITEM_CHANGE = \"itemChange\";\n var ERROR = \"error\";\n var PROGRESS = \"progress\";\n var DOT = \".\";\n var NS = \".kendoTreeList\";\n var CLICK = \"click\";\n var INPUT = \"input\";\n var BEFORE_EDIT = \"beforeEdit\";\n var EDIT = \"edit\";\n var PAGE = \"page\";\n var PAGE_CHANGE = \"pageChange\";\n var SAVE = \"save\";\n var SAVE_CHANGES = \"saveChanges\";\n var SORT = \"sort\";\n var EXPAND = \"expand\";\n var COLLAPSE = \"collapse\";\n var CELL_CLOSE = \"cellClose\";\n var REMOVE = \"remove\";\n var DATA_CELL = \"td:not(.k-group-cell):not(.k-hierarchy-cell):visible\";\n var FILTER_CELL = \".k-filter-row th:not(.k-group-cell):not(.k-hierarchy-cell):visible\";\n var DATABINDING = \"dataBinding\";\n var DATABOUND = \"dataBound\";\n var CANCEL = \"cancel\";\n var TABINDEX = \"tabIndex\";\n var FILTERMENUINIT = \"filterMenuInit\";\n var FILTERMENUOPEN = \"filterMenuOpen\";\n var COLUMNHIDE = \"columnHide\";\n var COLUMNSHOW = \"columnShow\";\n var HEADERCELLS = \"th.k-header\";\n var COLUMNREORDER = \"columnReorder\";\n var COLUMNRESIZE = \"columnResize\";\n var COLUMNMENUINIT = \"columnMenuInit\";\n var COLUMNMENUOPEN = \"columnMenuOpen\";\n var COLUMNLOCK = \"columnLock\";\n var COLUMNUNLOCK = \"columnUnlock\";\n var PARENTIDFIELD = \"parentId\";\n var DRAGSTART = \"dragstart\";\n var DRAG = \"drag\";\n var DROP = \"drop\";\n var DRAGEND = \"dragend\";\n var NAVROW = \"tr:visible\";\n var NAVCELL = \"td:visible\";\n var NAVHEADER = \"th:visible\";\n var NORECORDSCLASS = \"k-grid-norecords\";\n var ITEMROW = \"tr:not(.k-footer-template):visible\";\n var isRtl = false;\n var HEIGHT = \"height\";\n var INCELL = \"incell\";\n var INLINE = \"inline\";\n var POPUP = \"popup\";\n var TABLE = \"table\";\n var CHECKBOX = \"k-checkbox\";\n var CHECKBOXINPUT = \"input[data-role='checkbox'].\" + CHECKBOX;\n var SELECTCOLUMNTMPL = '';\n var SELECTCOLUMNHEADERTMPL = '';\n var SELECTED = \"k-selected\";\n var whitespaceRegExp = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\";\n var filterRowRegExp = new RegExp(\"(^|\" + whitespaceRegExp + \")\" + \"(k-filter-row)\" + \"(\" + whitespaceRegExp + \"|$)\");\n\n var classNames = {\n wrapper: \"k-treelist k-grid k-widget k-grid-display-block\",\n header: \"k-header\",\n button: \"k-button\",\n alt: \"k-alt\",\n editCell: \"k-edit-cell\",\n editRow: \"k-grid-edit-row\",\n dirtyCell: \"k-dirty-cell\",\n group: \"k-treelist-group\",\n toolbar: \"k-toolbar\",\n gridToolbar: \"k-grid-toolbar\",\n gridHeader: \"k-grid-header\",\n gridHeaderWrap: \"k-grid-header-wrap\",\n gridContent: \"k-grid-content\",\n gridContentWrap: \"k-grid-content\",\n gridFilter: \"k-grid-filter\",\n footerTemplate: \"k-footer-template\",\n focused: \"k-focus\",\n loading: \"k-i-loading\",\n refresh: \"k-i-reload\",\n retry: \"k-request-retry\",\n selected: \"k-selected\",\n status: \"k-status\",\n link: \"k-link\",\n filterable: \"k-filterable\",\n icon: \"k-icon\",\n iconFilter: \"k-i-filter\",\n iconCollapse: \"k-i-collapse\",\n iconExpand: \"k-i-expand\",\n iconHidden: \"k-i-none\",\n iconPlaceHolder: \"k-icon k-i-none\",\n input: \"k-input\",\n dropPositions: \"k-i-insert-up k-i-insert-down k-i-plus k-i-insert-middle\",\n dropTop: \"k-i-insert-up\",\n dropBottom: \"k-i-insert-down\",\n dropAdd: \"k-i-plus\",\n dropMiddle: \"k-i-insert-middle\",\n dropDenied: \"k-i-cancel\",\n dragStatus: \"k-drag-status\",\n dragClue: \"k-drag-clue\",\n dragClueText: \"k-clue-text\",\n headerCellInner: \"k-cell-inner\",\n columnTitle: \"k-column-title\"\n };\n\n var defaultCommands = {\n create: {\n imageClass: \"k-i-plus\",\n className: \"k-grid-add\",\n methodName: \"addRow\"\n },\n createchild: {\n imageClass: \"k-i-plus\",\n className: \"k-grid-add\",\n methodName: \"addRow\"\n },\n destroy: {\n imageClass: \"k-i-close\",\n className: \"k-grid-delete\",\n methodName: \"removeRow\"\n },\n edit: {\n imageClass: \"k-i-edit\",\n className: \"k-grid-edit\",\n methodName: \"editRow\"\n },\n update: {\n imageClass: \"k-i-check\",\n className: \"k-button-solid-primary k-grid-update\",\n methodName: \"saveRow\"\n },\n canceledit: {\n imageClass: \"k-i-cancel\",\n className: \"k-grid-cancel\",\n methodName: \"_cancelEdit\"\n },\n cancel: {\n imageClass: \"k-icon k-i-cancel\",\n text: \"Cancel changes\",\n className: \"k-grid-cancel-changes\",\n methodName: \"cancelChanges\"\n },\n save: {\n imageClass: \"k-icon k-i-check\",\n text: \"Save changes\",\n className: \"k-grid-save-changes\",\n methodName: \"saveChanges\"\n },\n excel: {\n imageClass: \"k-i-file-excel\",\n className: \"k-grid-excel\",\n methodName: \"saveAsExcel\"\n },\n pdf: {\n imageClass: \"k-i-file-pdf\",\n className: \"k-grid-pdf\",\n methodName: \"saveAsPDF\"\n },\n search: {\n template:\n \"\" +\n \"\" +\n \"\" +\n \"\" +\n \"\"\n }\n };\n\n var TreeView = kendo.Class.extend({\n init: function(data, options) {\n var that = this;\n\n that.data = data || [];\n that.options = extend(that.options, options);\n },\n\n options: {\n defaultParentId: null,\n idField: \"id\",\n parentIdField: PARENTIDFIELD\n },\n\n childrenMap: function() {\n var that = this;\n var childrenMap = {};\n var dataLength = that.data.length;\n var dataItem;\n var dataItemId;\n var dataItemParentId;\n var idField = that.options.idField;\n var parentIdField = that.options.parentIdField;\n\n if (that._childrenMap) {\n return that._childrenMap;\n }\n\n for (var i = 0; i < dataLength; i++) {\n dataItem = this.data[i];\n dataItemId = dataItem[idField];\n dataItemParentId = dataItem[parentIdField];\n\n childrenMap[dataItemId] = childrenMap[dataItemId] || [];\n childrenMap[dataItemParentId] = childrenMap[dataItemParentId] || [];\n\n childrenMap[dataItemParentId].push(dataItem);\n }\n\n that._childrenMap = childrenMap;\n\n return childrenMap;\n },\n\n idsMap: function() {\n var that = this;\n var idsMap = {};\n var data = that.data;\n var dataLength = data.length;\n var dataItem;\n var idField = that.options.idField;\n\n if (that._idMap) {\n return that._idMap;\n }\n\n for (var i = 0; i < dataLength; i++) {\n dataItem = data[i];\n idsMap[dataItem[idField]] = dataItem;\n }\n\n that.idsMap = idsMap;\n return idsMap;\n },\n\n dataMaps: function() {\n var that = this;\n var childrenMap = {};\n var data = that.data;\n var dataLength = data.length;\n var idsMap = {};\n var dataItem;\n var dataItemId;\n var dataItemParentId;\n var idField = that.options.idField;\n var parentIdField = that.options.parentIdField;\n\n if (that._dataMaps) {\n return that._dataMaps;\n }\n\n for (var i = 0; i < dataLength; i++) {\n dataItem = data[i];\n dataItemId = dataItem[idField];\n dataItemParentId = dataItem[parentIdField];\n\n idsMap[dataItemId] = dataItem;\n\n childrenMap[dataItemId] = childrenMap[dataItemId] || [];\n childrenMap[dataItemParentId] = childrenMap[dataItemParentId] || [];\n childrenMap[dataItemParentId].push(dataItem);\n }\n\n that._dataMaps = {\n children: childrenMap,\n ids: idsMap\n };\n\n return that._dataMaps;\n },\n\n rootNodes: function() {\n var that = this;\n var data = that.data;\n var defaultParentId = that.options.defaultParentId;\n var dataLength = data.length;\n var rootNodes = [];\n var dataItem;\n var parentIdField = that.options.parentIdField;\n\n for (var i = 0; i < dataLength; i++) {\n dataItem = data[i];\n\n if (dataItem[parentIdField] === defaultParentId) {\n rootNodes.push(dataItem);\n }\n }\n\n return rootNodes;\n },\n\n removeCollapsedSubtreesFromRootNodes: function(options) {\n options = options || {};\n var that = this;\n var rootNodes = that.rootNodes();\n var result = [];\n var prunedTree;\n\n that._childrenMap = options.childrenMap = options.childrenMap || that.childrenMap();\n options.maxDepth = options.maxDepth || Infinity;\n\n for (var i = 0; i < rootNodes.length; i++) {\n prunedTree = that.removeCollapsedSubtrees(rootNodes[i], options);\n result = result.concat(prunedTree);\n }\n\n return result;\n },\n\n removeCollapsedSubtrees: function(rootNode, options) {\n options = options || {};\n var that = this;\n var result = [];\n var childIdx;\n var prunedTree;\n var childrenMap = options.childrenMap || {};\n var maxDepth = options.maxDepth || Infinity;\n var idField = that.options.idField;\n var children = childrenMap[rootNode[idField]] || [];\n var expanded = isUndefined(rootNode.expanded) ? options.expanded : rootNode.expanded;\n\n result.push(rootNode);\n\n if (children && expanded) {\n for (childIdx = 0; childIdx < children.length; childIdx++) {\n if (result.length >= maxDepth) {\n break;\n }\n\n prunedTree = that.removeCollapsedSubtrees(children[childIdx], options);\n result = result.concat(prunedTree);\n }\n }\n\n return result;\n }\n });\n\n var TreeQuery = function(data) {\n this.data = data || [];\n };\n\n TreeQuery.prototype = new Query();\n TreeQuery.prototype.constructor = TreeQuery;\n\n TreeQuery.process = function(data, options, inPlace) {\n options = options || {};\n var query = new TreeQuery(data);\n var group = options.group;\n var sort = Query.normalizeGroup(group || []).concat(Query.normalizeSort(options.sort || []));\n var filterCallback = options.filterCallback;\n var filter = options.filter;\n var skip = options.skip;\n var take = options.take;\n var total;\n var childrenMap;\n var filteredChildrenMap;\n var view;\n var prunedData;\n\n if (sort && inPlace) {\n query = query.sort(sort, undefined, undefined, inPlace);\n }\n\n if (filter) {\n query = query.filter(filter);\n\n if (filterCallback) {\n query = filterCallback(query);\n }\n\n total = query.toArray().length;\n }\n\n if (sort && !inPlace) {\n query = query.sort(sort);\n\n if (group) {\n data = query.toArray();\n }\n }\n\n if (options.processFromRootNodes) {\n view = new TreeView(query.toArray(), options);\n\n if (filter) {\n filteredChildrenMap = view.childrenMap();\n }\n\n prunedData = view.removeCollapsedSubtreesFromRootNodes({\n // filtering or sorting requires changes to childrenMap\n childrenMap: filter || (sort && sort.length) ? undefined : options.childrenMap,\n expanded: options.expanded,\n maxDepth: (skip + take) || Infinity\n });\n\n childrenMap = view.childrenMap();\n\n query = new TreeQuery(prunedData);\n }\n\n if (skip !== undefined && take !== undefined) {\n query = query.range(skip, take);\n }\n\n if (group) {\n query = query.group(group, data);\n }\n\n return {\n total: total,\n data: query.toArray(),\n childrenMap: childrenMap,\n filteredChildrenMap: filteredChildrenMap\n };\n };\n\n var TreeListModel = Model.define({\n id: \"id\",\n\n parentId: PARENTIDFIELD,\n\n fields: {\n id: { type: \"number\" },\n parentId: { type: \"number\", nullable: true }\n },\n\n init: function(value) {\n Model.fn.init.call(this, value);\n\n this._loaded = false;\n\n if (!this.parentIdField) {\n this.parentIdField = PARENTIDFIELD;\n }\n\n this.parentId = this.get(this.parentIdField);\n },\n\n accept: function(data) {\n Model.fn.accept.call(this, data);\n\n this.parentId = this.get(this.parentIdField);\n },\n\n set: function(field, value, initiator) {\n if (field == PARENTIDFIELD && this.parentIdField != PARENTIDFIELD) {\n this[this.parentIdField] = value;\n }\n\n Model.fn.set.call(this, field, value, initiator);\n\n if (field == this.parentIdField) {\n this.parentId = this.get(this.parentIdField);\n }\n },\n\n loaded: function(value) {\n if (value !== undefined) {\n this._loaded = value;\n } else {\n return this._loaded;\n }\n },\n\n shouldSerialize: function(field) {\n return Model.fn.shouldSerialize.call(this, field) && field !== \"_loaded\" && field != \"_error\" && field != \"_edit\" && !(this.parentIdField !== \"parentId\" && field === \"parentId\");\n }\n });\n\n TreeListModel.parentIdField = PARENTIDFIELD;\n\n TreeListModel.define = function(base, options) {\n if (options === undefined) {\n options = base;\n base = TreeListModel;\n }\n\n var parentId = options.parentId || PARENTIDFIELD;\n\n options.parentIdField = parentId;\n\n var model = Model.define(base, options);\n\n if (parentId) {\n model.parentIdField = parentId;\n }\n\n return model;\n };\n\n function is(field) {\n return function(object) {\n return object[field];\n };\n }\n\n function not(func) {\n return function(object) {\n return !func(object);\n };\n }\n\n var TreeListDataSource = DataSource.extend({\n init: function(options) {\n options = options || {};\n var that = this;\n that._dataMaps = that._getDataMaps();\n\n options.schema = extend(true, {}, {\n modelBase: TreeListModel,\n model: TreeListModel\n }, options.schema);\n\n DataSource.fn.init.call(this, options);\n },\n\n _addRange: function() {\n // empty override for performance - the treelist does not support virtualization\n },\n\n _createNewModel: function(data) {\n var that = this;\n var model = {};\n var fromModel = data instanceof Model;\n var parentIdField = this._modelParentIdField();\n\n if (fromModel) {\n model = data;\n }\n\n model = DataSource.fn._createNewModel.call(this, model);\n\n if (!fromModel) {\n if (data.parentId) {\n data[model.parentIdField] = data.parentId;\n } else if (that._isPageable() && data[parentIdField]) {\n data[model.parentIdField] = data[parentIdField];\n }\n\n model.accept(data);\n }\n\n return model;\n },\n\n _shouldWrap: function() {\n return true;\n },\n\n _push: function(result, operation) {\n var data = DataSource.fn._readData.call(this, result);\n\n if (!data) {\n data = result;\n }\n\n this[operation](data);\n },\n\n _getData: function() {\n // do not use .data(), which wraps the data items\n return this._data || [];\n },\n\n _readData: function(newData) {\n var that = this;\n var data = that._isPageable() ? that._getData().toJSON() : that.data();\n\n newData = DataSource.fn._readData.call(this, newData);\n\n this._replaceData(((data.toJSON ? data.toJSON() : data)).concat(newData), data);\n\n if (newData instanceof ObservableArray) {\n return newData;\n }\n\n return data;\n },\n\n _replaceData: function(source, target) {\n var sourceLength = source.length;\n\n for (var i = 0; i < sourceLength; i++) {\n target[i] = source[i];\n }\n\n target.length = sourceLength;\n },\n\n _readAggregates: function(data) {\n var result = extend(this._aggregateResult, this.reader.aggregates(data));\n if (\"\" in result) {\n result[this._defaultParentId()] = result[\"\"];\n delete result[\"\"];\n }\n\n return result;\n },\n\n read: function(data) {\n var that = this;\n\n if (that._isPageable()) {\n that._dataMaps = {};\n if (!that._modelOptions().expanded) {\n that._skip = 0;\n that._page = 1;\n that._collapsedTotal = undefined;\n }\n }\n\n return DataSource.fn.read.call(that, data);\n },\n\n remove: function(root) {\n this._removeChildData(root);\n\n this._removeFromDataMaps(root);\n\n return DataSource.fn.remove.call(this, root);\n },\n\n _removeChildData: function(model, removePristine) {\n var that = this;\n var pageable = that._isPageable();\n var data = pageable ? this._getData() : this.data();\n var childrenMap = pageable ? that._getChildrenMap() || that.childrenMap(data) : that._childrenMap(data);\n var items = this._subtree(childrenMap, model.id);\n var shouldRemovePristine = isUndefined(removePristine) ? false : removePristine;\n\n var removedItems = this._removeItems(items, shouldRemovePristine);\n\n that._removeFromDataMaps(removedItems);\n },\n\n pushDestroy: function(items) {\n var that = this;\n\n if (!isArray(items)) {\n items = [items];\n }\n\n for (var i = 0; i < items.length; i++) {\n that._removeChildData(items[i], true);\n that._removeFromDataMaps(items[i]);\n }\n\n DataSource.fn.pushDestroy.call(that, items);\n },\n\n insert: function(index, model) {\n var that = this;\n var newModel = that._createNewModel(model);\n\n that._insertInDataMaps(newModel);\n\n return DataSource.fn.insert.call(that, index, newModel);\n },\n\n _filterCallback: function(query) {\n var that = this;\n var i, item;\n var map = {};\n var result = [];\n var data = query.toArray();\n var idField = that._modelIdField();\n var parentIdField = that._modelParentIdField();\n var pageable = that._isPageable();\n var parentSubtree = [];\n var parent;\n\n for (i = 0; i < data.length; i++) {\n item = data[i];\n\n if (pageable) {\n // return view from root nodes to child nodes\n parentSubtree = [];\n\n if (!map[item[idField]]) {\n map[item[idField]] = true;\n parentSubtree.push(item);\n }\n\n parent = that._parentNode(item);\n\n while (parent) {\n if (!map[parent[idField]]) {\n map[parent[idField]] = true;\n parentSubtree.unshift(parent);\n parent = that._parentNode(parent);\n } else {\n // the parent chain is already processed\n break;\n }\n }\n\n if (parentSubtree.length) {\n result = result.concat(parentSubtree);\n }\n } else {\n while (item) {\n if (!map[item[idField]]) {\n map[item[idField]] = true;\n result.push(item);\n }\n\n if (!map[item[parentIdField]]) {\n map[item[parentIdField]] = true;\n item = this.parentNode(item);\n\n if (item) {\n result.push(item);\n }\n } else {\n break;\n }\n }\n }\n }\n\n return new Query(result);\n },\n\n _subtree: function(map, id) {\n var that = this;\n var result = map[id] || [];\n var defaultParentId = that._defaultParentId();\n var idField = that._modelIdField();\n\n for (var i = 0, len = result.length; i < len; i++) {\n if (result[i][idField] !== defaultParentId) {\n result = result.concat(that._subtree(map, result[i][idField]));\n }\n }\n\n return result;\n },\n\n // builds hash id -> children\n _childrenMap: function(data) {\n var map = {};\n var i, item, id, parentId;\n\n data = this._observeView(data);\n\n for (i = 0; i < data.length; i++) {\n item = data[i];\n id = item.id;\n parentId = item.parentId;\n\n map[id] = map[id] || [];\n map[parentId] = map[parentId] || [];\n map[parentId].push(item);\n }\n\n return map;\n },\n\n childrenMap: function(data) {\n var view = this._createTreeView(data);\n var map = view.childrenMap();\n return map;\n },\n\n _getChildrenMap: function() {\n var that = this;\n var dataMaps = that._getDataMaps();\n return dataMaps.children;\n },\n\n _initIdsMap: function(data) {\n var that = this;\n var dataMaps = that._getDataMaps();\n\n if (isUndefined(dataMaps.ids)) {\n dataMaps.ids = that._idsMap(data);\n }\n\n return dataMaps.ids;\n },\n\n _idsMap: function(data) {\n var view = this._createTreeView(data);\n var map = view.idsMap();\n return map;\n },\n\n _getIdsMap: function() {\n var that = this;\n var dataMaps = that._getDataMaps();\n return dataMaps.ids || {};\n },\n\n _getFilteredChildrenMap: function() {\n var that = this;\n var dataMaps = that._getDataMaps();\n return dataMaps.filteredChildren;\n },\n\n _setFilteredChildrenMap: function(map) {\n var that = this;\n var dataMaps = that._getDataMaps();\n dataMaps.filteredChildren = map;\n },\n\n _initDataMaps: function(data) {\n var that = this;\n var view = that._createTreeView(data);\n\n that._dataMaps = view.dataMaps();\n\n return that._dataMaps;\n },\n\n _initChildrenMapForParent: function(parent) {\n var that = this;\n var data = that._getData();\n var childrenMap = that._getChildrenMap();\n var idField = that._modelIdField();\n var parentIdField = that._modelParentIdField();\n var parentId = (parent || {})[idField];\n\n if (childrenMap && parent) {\n childrenMap[parentId] = [];\n\n for (var i = 0; i < data.length; i++) {\n if (data[i][parentIdField] === parentId) {\n childrenMap[parentId].push(data[i]);\n }\n }\n }\n },\n\n _getDataMaps: function() {\n var that = this;\n that._dataMaps = that._dataMaps || {};\n return that._dataMaps;\n },\n\n _createTreeView: function(data, options) {\n var view = new TreeView(data, extend(options, this._defaultTreeModelOptions()));\n return view;\n },\n\n _defaultTreeModelOptions: function() {\n var that = this;\n var modelOptions = that._modelOptions();\n\n return {\n defaultParentId: that._defaultParentId(),\n idField: that._modelIdField(),\n parentIdField: that._modelParentIdField(),\n expanded: modelOptions.expanded\n };\n },\n\n _defaultDataItemType: function() {\n return this.reader.model || kendo.data.ObservableObject;\n },\n\n _calculateAggregates: function(data, options) {\n options = options || {};\n var that = this;\n var result = {};\n var item, subtree, i;\n var filter = options.filter;\n var skip = options.skip;\n var take = options.take;\n var maxDepth = !isUndefined(skip) && !isUndefined(take) ? (skip + take) : Infinity;\n var pageable = that._isPageable();\n var filteredChildrenMap = options.filteredChildrenMap;\n var childrenMap = options.childrenMap;\n var pageableChildrenMap;\n\n if (pageable) {\n if (isUndefined(options.aggregate)) {\n return result;\n }\n\n if (filteredChildrenMap) {\n pageableChildrenMap = filteredChildrenMap;\n } else if (childrenMap) {\n pageableChildrenMap = childrenMap;\n } else {\n pageableChildrenMap = that.childrenMap(that._getData());\n }\n }\n\n if (!pageable && filter) {\n data = Query.process(data, {\n filter: filter,\n filterCallback: this._filterCallback.bind(this)\n }).data;\n }\n\n var map = pageable ? pageableChildrenMap : that._childrenMap(data);\n\n // calculate aggregates for each subtree\n result[this._defaultParentId()] = new Query(this._subtree(map, this._defaultParentId())).aggregate(options.aggregate);\n\n for (i = 0; i < data.length; i++) {\n if (i >= maxDepth) {\n break;\n }\n\n item = data[i];\n subtree = this._subtree(map, item.id);\n\n result[item.id] = new Query(subtree).aggregate(options.aggregate);\n }\n\n return result;\n },\n\n _queryProcess: function(data, options) {\n var that = this;\n var result = {};\n options = options || {};\n options.filterCallback = this._filterCallback.bind(this);\n\n if (that._isPageable()) {\n return that._processPageableQuery(data, options);\n } else {\n var defaultParentId = this._defaultParentId();\n result = Query.process(data, options);\n var map = this._childrenMap(result.data);\n var hasLoadedChildren, i, item, children;\n\n data = map[defaultParentId] || [];\n\n for (i = 0; i < data.length; i++) {\n item = data[i];\n\n if (item.id === defaultParentId) {\n continue;\n }\n\n children = map[item.id];\n hasLoadedChildren = !!(children && children.length);\n\n if (!item.loaded()) {\n item.loaded(hasLoadedChildren || !item.hasChildren);\n }\n\n if (item.loaded() || item.hasChildren !== true) {\n item.hasChildren = hasLoadedChildren;\n }\n\n if (hasLoadedChildren) {\n //cannot use splice due to IE8 bug\n data = data.slice(0, i + 1).concat(children, data.slice(i + 1));\n }\n }\n\n result.data = data;\n }\n\n return result;\n },\n\n _processPageableQuery: function(data, options) {\n var that = this;\n var dataMaps = that._getDataMaps();\n var result;\n var filteredChildrenMap;\n\n if (that._getData() !== data || !dataMaps.children || !dataMaps.ids) {\n dataMaps = that._initDataMaps(that._getData());\n }\n\n options.childrenMap = dataMaps.children || {};\n options.idsMap = dataMaps.ids || {};\n\n result = that._processTreeQuery(data, options);\n\n that._replaceWithObservedData(result.data, data);\n\n that._processDataItemsState(result.data, result.childrenMap);\n\n that._replaceItemsInDataMaps(result.data);\n\n result.dataToAggregate = that._dataToAggregate(result.data, options);\n\n if (options.filter || that.filter()) {\n filteredChildrenMap = result.filteredChildrenMap;\n that._replaceInMapWithObservedData(filteredChildrenMap, data);\n that._setFilteredChildrenMap(filteredChildrenMap);\n options.filteredChildrenMap = filteredChildrenMap;\n that._calculateCollapsedTotal(result.data);\n } else {\n that._collapsedTotal = undefined;\n }\n\n return result;\n },\n\n _dataToAggregate: function(data) {\n var that = this;\n var firstDataItem = data[0] || {};\n var firstItemParents = that._parentNodes(firstDataItem);\n var dataToAggregate = firstItemParents.concat(data);\n\n return dataToAggregate;\n },\n\n _replaceItemsInDataMaps: function(observableArray) {\n var that = this;\n var view = isArray(observableArray) ? observableArray : [observableArray];\n var itemType = that._defaultDataItemType();\n var defaultParentId = that._defaultParentId();\n var idField = that._modelIdField();\n var parentIdField = that._modelParentIdField();\n var dataMaps = that._getDataMaps();\n var item;\n var parents;\n var directParent;\n\n for (var viewIndex = 0; viewIndex < view.length; viewIndex++) {\n item = view[viewIndex];\n\n if (!(item instanceof itemType)) {\n continue;\n }\n\n that._insertInIdsMap(item);\n\n parents = that._parentNodes(item);\n directParent = parents && parents.length ? parents[parents.length - 1] : undefined;\n\n if (item[parentIdField] === defaultParentId) {\n that._replaceInMap(dataMaps.children, defaultParentId, item, itemType);\n } else if (directParent) {\n that._replaceInMap(dataMaps.children, directParent[idField], item, itemType);\n }\n }\n },\n\n _replaceInMap: function(map, id, replacement, itemType) {\n var idField = this._modelIdField();\n map[id] = map[id] || [];\n itemType = itemType || this._defaultDataItemType();\n\n var itemInArray = map[id].filter(function(element) {\n return replacement[idField] === element[idField];\n })[0];\n\n var itemIndex = itemInArray ? map[id].indexOf(itemInArray) : -1;\n\n if (itemIndex !== -1 && !(itemInArray instanceof itemType)) {\n map[id][itemIndex] = replacement;\n }\n },\n\n _replaceWithObservedData: function(dataToReplace, replacementArray) {\n var that = this;\n var idsMap = that._getDataMaps().ids || {};\n var idField = that._modelIdField();\n var itemType = that._defaultDataItemType();\n var itemToReplace;\n var itemToReplaceId;\n var dataItem;\n var dataItemIndex;\n var observableItem;\n\n for (var i = 0; i < dataToReplace.length; i++) {\n itemToReplace = dataToReplace[i];\n itemToReplaceId = itemToReplace[idField];\n\n if (!(itemToReplace instanceof itemType)) {\n if (!(idsMap[itemToReplaceId] instanceof itemType)) {\n dataItem = that._getById(itemToReplaceId);\n dataItemIndex = replacementArray.indexOf(dataItem);\n\n if (dataItem && dataItemIndex !== -1) {\n observableItem = replacementArray.at(dataItemIndex);\n dataToReplace[i] = observableItem;\n }\n } else {\n dataToReplace[i] = idsMap[itemToReplaceId];\n }\n }\n }\n },\n\n _replaceInMapWithObservedData: function(map, replacementArray) {\n var that = this;\n\n for (var key in map) {\n that._replaceWithObservedData(map[key], replacementArray);\n }\n },\n\n _insertInDataMaps: function(item) {\n var that = this;\n\n if (that._isPageable()) {\n that._insertInIdsMap(item);\n that._insertInChildrenMap(item);\n }\n },\n\n _insertInIdsMap: function(item) {\n var that = this;\n var idsMap = that._getIdsMap();\n var idField = that._modelIdField();\n\n if (!isUndefined(item[idField])) {\n idsMap[item[idField]] = item;\n }\n },\n\n _insertInChildrenMap: function(item, index) {\n var that = this;\n var childrenMap = that._getChildrenMap() || {};\n var idField = that._modelIdField();\n var parentIdField = that._modelParentIdField();\n var itemId = item[idField];\n var parentId = item[parentIdField];\n index = index || 0;\n\n childrenMap[itemId] = childrenMap[itemId] || [];\n childrenMap[parentId] = childrenMap[parentId] || [];\n childrenMap[parentId].splice(index, 0, item);\n },\n\n _removeFromDataMaps: function(items) {\n var that = this;\n items = isArray(items) ? items : [items];\n\n if (that._isPageable()) {\n for (var i = 0; i < items.length; i++) {\n that._removeFromIdsMap(items[i]);\n that._removeFromChildrenMap(items[i]);\n }\n }\n },\n\n _removeFromIdsMap: function(item) {\n var that = this;\n var idsMap = that._getIdsMap();\n var idField = that._modelIdField();\n\n if (!isUndefined(item[idField])) {\n idsMap[item[idField]] = undefined;\n }\n },\n\n _removeFromChildrenMap: function(item) {\n var that = this;\n var childrenMap = that._getChildrenMap() || {};\n var parentIdField = that._modelParentIdField();\n var parentId = item[parentIdField];\n\n childrenMap[parentId] = childrenMap[parentId] || [];\n\n var itemIndex = that._indexInChildrenMap(item);\n\n if (itemIndex !== -1) {\n childrenMap[parentId].splice(itemIndex, 1);\n }\n },\n\n _indexInChildrenMap: function(item) {\n var that = this;\n return that._itemIndexInMap(item, that._getChildrenMap());\n },\n\n _itemIndexInMap: function(item, dataMap) {\n var that = this;\n var map = dataMap || {};\n var parentIdField = that._modelParentIdField();\n var parentId = item[parentIdField];\n\n map[parentId] = map[parentId] || [];\n\n var itemInArray = map[parentId].filter(function(element) {\n return item.uid === element.uid;\n })[0];\n\n var itemIndex = itemInArray ? map[parentId].indexOf(itemInArray) : -1;\n\n return itemIndex;\n },\n\n _getById: function(id) {\n var that = this;\n var idField = that._modelIdField();\n var data = that._getData();\n\n for (var i = 0; i < data.length; i++) {\n if (data[i][idField] === id) {\n return data[i];\n }\n }\n },\n\n _isLastItemInView: function(dataItem) {\n var view = this.view();\n return view.length && view[view.length - 1] === dataItem;\n },\n\n _defaultPageableQueryOptions: function() {\n var that = this;\n var dataMaps = that._getDataMaps();\n var options = {\n skip: that.skip(),\n take: that.take(),\n page: that.page(),\n pageSize: that.pageSize(),\n sort: that.sort(),\n filter: that.filter(),\n group: that.group(),\n aggregate: that.aggregate(),\n filterCallback: that._filterCallback.bind(that),\n childrenMap: dataMaps.children,\n idsMap: dataMaps.ids\n };\n\n return options;\n },\n\n _isPageable: function() {\n var pageSize = this.pageSize();\n return (!isUndefined(pageSize) && pageSize > 0 && !this.options.serverPaging);\n },\n\n _updateTotalForAction: function(action, items) {\n var that = this;\n\n DataSource.fn._updateTotalForAction.call(that, action, items);\n\n if (that._isPageable()) {\n that._updateCollapsedTotalForAction(action, items);\n }\n },\n\n _updateCollapsedTotalForAction: function(action, items) {\n var that = this;\n var total = parseInt(that._collapsedTotal, 10);\n\n if (!isNumber(that._collapsedTotal)) {\n that._calculateCollapsedTotal();\n return;\n }\n\n if (action === \"add\") {\n total += items.length;\n } else if (action === \"remove\") {\n total -= items.length;\n } else if (action !== \"itemchange\" && action !== \"sync\" && !that.options.serverPaging) {\n total = that._calculateCollapsedTotal();\n } else if (action === \"sync\") {\n total = that._calculateCollapsedTotal();\n }\n\n that._collapsedTotal = total;\n },\n\n _setFilterTotal: function(filterTotal, setDefaultValue) {\n var that = this;\n\n DataSource.fn._setFilterTotal.call(that, filterTotal, setDefaultValue);\n\n },\n\n collapsedTotal: function() {\n var that = this;\n\n if (!isUndefined(that._collapsedTotal)) {\n return that._collapsedTotal;\n }\n\n return that._calculateCollapsedTotal();\n },\n\n _calculateCollapsedTotal: function(filteredData) {\n var that = this;\n var data = that._dataWithoutCollapsedSubtrees(filteredData);//\n\n if (data.length) {\n that._collapsedTotal = data.length;\n }\n\n return that._collapsedTotal;\n },\n\n _dataWithoutCollapsedSubtrees: function(filteredData) {\n return this._removeCollapsedSubtrees(filteredData || this._getData());\n },\n\n _removeCollapsedSubtrees: function(data) {\n var that = this;\n var view = that._createTreeView(data);\n var result = view.removeCollapsedSubtreesFromRootNodes({\n expanded: that._modelOptions().expanded,\n childrenMap: that.filter() ? that._getFilteredChildrenMap() : that._getChildrenMap()\n });\n\n return result;\n },\n\n _processTreeQuery: function(data, options) {\n var result = TreeQuery.process(data, extend(options, this._defaultTreeModelOptions(), {\n processFromRootNodes: true\n }));\n\n return result;\n },\n\n _processDataItemsState: function(data, childrenMap) {\n var dataLength = data.length;\n var i;\n\n for (i = 0; i < dataLength; i++) {\n this._processDataItemState(data[i], childrenMap);\n }\n },\n\n _processDataItemState: function(dataItem, childrenMap) {\n var defaultParentId = this._defaultParentId();\n\n if (dataItem.id === defaultParentId) {\n return;\n }\n\n var children = childrenMap[dataItem.id] || [];\n var hasLoadedChildren = !!(children && children.length);\n\n if (!dataItem.loaded) {\n return;\n }\n\n if (!dataItem.loaded()) {\n dataItem.loaded(hasLoadedChildren || !dataItem.hasChildren);\n }\n\n if (dataItem.loaded() || dataItem.hasChildren !== true) {\n dataItem.hasChildren = hasLoadedChildren;\n }\n },\n\n _queueRequest: function(options, callback) {\n // allow simultaneous requests (loading multiple items at the same time)\n callback.call(this);\n },\n\n _modelLoaded: function(id) {\n var model = this.get(id);\n model.loaded(true);\n model.hasChildren = this.childNodes(model).length > 0;\n },\n\n _modelError: function(id, e) {\n this.get(id)._error = e;\n },\n\n success: function(data, requestParams) {\n if (!requestParams || typeof requestParams.id == \"undefined\") {\n this._data = this._observe([]);\n }\n\n DataSource.fn.success.call(this, data, requestParams);\n\t\t\tthis._total = this._data.length;\n },\n\n load: function(model) {\n var method = \"_query\";\n var remote = this.options.serverSorting || this.options.serverPaging || this.options.serverFiltering || this.options.serverGrouping || this.options.serverAggregates;\n var defaultPromise = $.Deferred().resolve().promise();\n\n if (model.loaded()) {\n if (remote) {\n return defaultPromise;\n }\n } else if (model.hasChildren) {\n method = \"read\";\n this._removeChildData(model);\n }\n\n return this[method]({ id: model.id })\n .done(this._modelLoaded.bind(this, model.id))\n .fail(this._modelError.bind(this, model.id));\n },\n\n contains: function(root, child) {\n var that = this;\n var idField = that._modelIdField();\n var parentIdField = that._modelParentIdField();\n var rootId = root[idField];\n var pageable = that._isPageable();\n\n while (child) {\n if (child[parentIdField] === rootId) {\n return true;\n }\n\n child = pageable ? that._parentNode(child) : that.parentNode(child);\n }\n\n return false;\n },\n\n _byParentId: function(id, defaultId) {\n var result = [];\n var view = this.view();\n var current;\n\n if (id === defaultId) {\n return [];\n }\n\n for (var i = 0; i < view.length; i++) {\n current = view.at(i);\n\n if (current.parentId == id) {\n result.push(current);\n }\n }\n\n return result;\n },\n\n _defaultParentId: function() {\n return this.reader.model.fn.defaults[this.reader.model.parentIdField];\n },\n\n _modelOptions: function() {\n var modelOptions = ((this.options.schema || {}).model || {});\n return modelOptions;\n },\n\n _modelIdField: function() {\n var modelOptions = this._modelOptions();\n return modelOptions.id || \"id\";\n },\n\n _modelParentIdField: function() {\n var modelOptions = this._modelOptions();\n return modelOptions.parentId || PARENTIDFIELD;\n },\n\n childNodes: function(model) {\n return this._byParentId(model.id, this._defaultParentId());\n },\n\n allChildNodes: function(model, result) {\n var directChildren = this.data().filter(function(item) {\n return item.parentId === model.id;\n });\n\n for (var i = 0; i < directChildren.length; i++) {\n result.push(directChildren[i]);\n this.allChildNodes(directChildren[i], result);\n }\n },\n\n rootNodes: function() {\n return this._byParentId(this._defaultParentId());\n },\n\n _rootNode: function(child) {\n return this._parentNodes(child)[0];\n },\n\n _pageableRootNodes: function(options) {\n options = options || {};\n var that = this;\n var defaultParentId = that._defaultParentId();\n var parentIdField = that._modelParentIdField();\n var result = [];\n var nodesWithoutParentInView = that._nodesWithoutParentInView(options);\n var node;\n var root;\n\n for (var i = 0; i < nodesWithoutParentInView.length; i++) {\n node = nodesWithoutParentInView[i];\n\n if (node[parentIdField] === defaultParentId) {\n result.push(node);\n } else {\n root = that._rootNode(node);\n\n if (root && result.indexOf(root) === -1) {\n result.push(root);\n }\n }\n }\n\n return result;\n },\n\n parentNode: function(model) {\n return this.get(model.parentId);\n },\n\n _parentNode: function(child) {\n var that = this;\n var parentIdField = that._modelParentIdField();\n var idsMap = that._initIdsMap(that._getData());\n var parentId = child[parentIdField];\n var parent = idsMap[parentId] || that._getById(parentId);\n\n return parent;\n },\n\n _parentNodes: function(child) {\n var that = this;\n var parent = that._parentNode(child);\n var parents = [];\n\n while (parent) {\n parents.unshift(parent);\n parent = that._parentNode(parent);\n }\n\n return parents;\n },\n\n _parentNodesNotInView: function() {\n var that = this;\n var view = that.view();\n var result = [];\n var defaultParentId = that._defaultParentId();\n var idField = that._modelIdField();\n var parentIdField = that._modelParentIdField();\n var parentInView;\n var parents = [];\n var directParent;\n var dataItem;\n var dataItemId;\n var dataItemParentId;\n\n for (var i = 0; i < view.length; i++) {\n dataItem = view[i];\n dataItemId = dataItem[idField];\n dataItemParentId = dataItem[parentIdField];\n parentInView = that._parentInView(dataItemParentId);\n\n if (!parentInView && dataItemParentId !== defaultParentId) {\n parents = that._parentNodes(dataItem);\n\n directParent = parents && parents.length ? parents[parents.length - 1] : that._getById(dataItemParentId);\n\n if (directParent && result.indexOf(directParent) === -1) {\n result.push(directParent);\n }\n }\n }\n\n return result;\n },\n\n _nodesWithoutParentInView: function(options) {\n options = options || {};\n var that = this;\n var view = that.view();\n var childrenMap = options.childrenMap || that.childrenMap(that._getData());\n var idField = that._modelIdField();\n var parentIdField = that._modelParentIdField();\n var dataItem;\n var parentInView;\n var children = [];\n var result = [];\n\n for (var i = 0; i < view.length; i++) {\n dataItem = view[i];\n children = childrenMap[dataItem[idField]];\n parentInView = that._parentInView(dataItem[parentIdField]);\n\n if (!parentInView) {\n result.push(dataItem);\n }\n }\n\n return result;\n },\n\n _parentInView: function(parentId) {\n var view = this.view();\n\n for (var i = 0; i < view.length; i++) {\n if (view[i].id === parentId) {\n return view[i];\n }\n }\n },\n\n level: function(model) {\n var result = -1;\n\n if (!(model instanceof TreeListModel)) {\n model = this.get(model);\n }\n\n do {\n model = this.parentNode(model);\n result++;\n } while (model);\n\n return result;\n },\n\n _pageableModelLevel: function(model) {\n var that = this;\n\n if (!model || !that._isPageable()) {\n return 0;\n }\n\n var parents = that._parentNodes(model);\n\n return parents.length;\n },\n\n filter: function(value) {\n var baseFilter = DataSource.fn.filter;\n\n if (value === undefined) {\n return baseFilter.call(this, value);\n }\n\n baseFilter.call(this, value);\n },\n\n _pageableQueryOptions: function(options) {\n var dataMaps = this._getDataMaps();\n\n options.childrenMap = dataMaps.children;\n options.idsMap = dataMaps.ids;\n\n return options;\n },\n\n _flatData: function(data, skip) {\n skip = this._isPageable() ? true : skip;\n return DataSource.fn._flatData.call(this, data, skip);\n },\n\n data: function(data) {\n var that = this;\n var result = DataSource.fn.data.call(that, data);\n\n if (that._isPageable()) {\n that._initDataMaps(that._getData());\n that._calculateCollapsedTotal();\n }\n\n return result;\n },\n\n cancelChanges: function(model) {\n var that = this;\n\n DataSource.fn.cancelChanges.call(that, model);\n\n that._restorePageSizeAfterAddChild();\n },\n\n _modelCanceled: function(model) {\n var that = this;\n\n if (that._isPageable()) {\n that._removeFromDataMaps(model);\n }\n },\n\n _changesCanceled: function() {\n var that = this;\n\n if (that._isPageable()) {\n that._initDataMaps(that._getData());\n }\n },\n\n _setAddChildPageSize: function() {\n var that = this;\n var queryOptions = {};\n\n if (that._isPageable()) {\n // increase the page size to make the new item visible in view\n that._addChildPageSize = that.pageSize() + 1;\n\n queryOptions = that._defaultPageableQueryOptions();\n queryOptions.take = that._addChildPageSize;\n queryOptions.pageSize = that._addChildPageSize;\n that._query(queryOptions);\n }\n },\n\n _restorePageSizeAfterAddChild: function() {\n var that = this;\n var queryOptions = {};\n\n if (that._isPageable()) {\n if (!isUndefined(that._addChildPageSize)) {\n queryOptions = that._defaultPageableQueryOptions();\n queryOptions.take = that._addChildPageSize - 1;\n queryOptions.pageSize = that._addChildPageSize - 1;\n that._query(queryOptions);\n }\n }\n\n that._addChildPageSize = undefined;\n },\n\n sync: function() {\n var that = this;\n\n return DataSource.fn.sync.call(that)\n .then(function() {\n that._restorePageSizeAfterAddChild();\n });\n },\n\n _syncEnd: function() {\n var that = this;\n\n if (that._isPageable()) {\n that._initDataMaps(that._getData());\n }\n }\n });\n\n TreeListDataSource.create = function(options) {\n if (Array.isArray(options)) {\n options = { data: options };\n } else if (options instanceof ObservableArray) {\n options = { data: options.toJSON() };\n }\n\n return options instanceof TreeListDataSource ? options : new TreeListDataSource(options);\n };\n\n function isCellVisible() {\n return this.style.display !== \"none\";\n }\n\n function sortCells(cells) {\n var indexAttr = kendo.attr(\"index\");\n return 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\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[data-index]\")\n .filter(filter);\n }\n\n cells = cells.add(rows.last().find(\"th\").filter(filter));\n\n return sortCells(cells);\n }\n\n function createPlaceholders(options) {\n var spans = [];\n var className = options.className;\n\n for (var i = 0, level = options.level; i < level; i++) {\n spans.push(kendoDomElement(\"span\", { className: className }));\n }\n\n return spans;\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 syncTableHeight(table1, table2) {\n table1 = table1[0];\n table2 = table2[0];\n\n if (table1.rows.length && table2.rows.length && table1.rows.length !== table2.rows.length) {\n var lockedHeigth = table1.offsetHeight;\n var tableHeigth = table2.offsetHeight;\n\n var row;\n var diff;\n if (lockedHeigth > tableHeigth) {\n row = table2.rows[table2.rows.length - 1];\n\n if (filterRowRegExp.test(row.className)) {\n row = table2.rows[table2.rows.length - 2];\n }\n\n diff = lockedHeigth - tableHeigth;\n } else {\n row = table1.rows[table1.rows.length - 1];\n\n if (filterRowRegExp.test(row.className)) {\n row = table1.rows[table1.rows.length - 2];\n }\n\n diff = tableHeigth - lockedHeigth;\n }\n row.style.height = row.offsetHeight + diff + \"px\";\n }\n }\n\n var TreeListPager = ui.Pager.extend({\n options: {\n name: \"TreeListPager\"\n },\n\n totalPages: function() {\n var that = this;\n var dataSource = that.dataSource;\n\n if (dataSource && dataSource._filter) {\n return ui.Pager.fn.totalPages.call(that);\n }\n\n return Math.ceil((that._collapsedTotal() || 0) / (that.pageSize() || 1));\n },\n\n _createDataSource: function(options) {\n this.dataSource = kendo.data.TreeListDataSource.create(options.dataSource);\n },\n\n _collapsedTotal: function() {\n var dataSource = this.dataSource;\n return dataSource ? (dataSource.collapsedTotal() || 0) : 0;\n }\n });\n\n var Editor = kendo.Observable.extend({\n init: function(element, options) {\n kendo.Observable.fn.init.call(this);\n\n options = this.options = extend(true, {}, this.options, options);\n\n this.element = element;\n\n this.bind(this.events, options);\n\n this.model = this.options.model;\n\n this.fields = this._fields(this.options.columns);\n\n this._initContainer();\n\n this.createEditable();\n },\n\n options: {\n renderForm: false\n },\n\n events: [],\n\n _initContainer: function() {\n this.wrapper = this.element;\n },\n\n createEditable: function() {\n var options = this.options;\n\n if (options.renderForm) {\n this.form = new ui.Form(this.wrapper.find(\".k-treelist-form\"), {\n items: this.fields,\n buttonsTemplate: \"\",\n formData: this.model,\n change: options.change\n });\n\n this.editable = this.form.editable;\n } else {\n this.editable = new ui.Editable(this.wrapper, {\n fields: this.fields,\n target: options.target,\n clearContainer: options.clearContainer,\n model: this.model,\n change: options.change\n });\n }\n },\n\n _isEditable: function(column) {\n return isColumnEditable(column, this.model);\n },\n\n _fields: function(columns) {\n var fields = [];\n var idx, length, column;\n\n for (idx = 0, length = columns.length; idx < length; idx++) {\n column = columns[idx];\n\n if (this._isEditable(column)) {\n fields.push({\n field: column.field,\n format: column.format,\n editor: column.editor,\n editorOptions: extend(true, { format: column.format }, column.editorOptions),\n label: column.title || column.field || \"\"\n });\n }\n }\n\n return fields;\n },\n\n end: function() {\n return this.editable.end();\n },\n\n close: function() {\n this.destroy();\n },\n\n destroy: function() {\n this.editable.destroy();\n this.editable.element\n .find(\"[\" + kendo.attr(\"container-for\") + \"]\")\n .empty()\n .end()\n .removeAttr(kendo.attr(\"role\"));\n\n this.model = this.wrapper = this.element = this.columns = this.editable = null;\n }\n });\n\n var PopupEditor = Editor.extend({\n init: function(element, options) {\n Editor.fn.init.call(this, element, options);\n\n this._attachHandlers();\n kendo.cycleForm(this.wrapper);\n\n this.open();\n },\n\n events: [\n CANCEL,\n SAVE\n ],\n\n options: {\n window: {\n modal: true,\n resizable: false,\n draggable: true,\n title: \"Edit\",\n visible: false\n }\n },\n\n _initContainer: function() {\n var options = this.options;\n var formContent = [];\n\n this.wrapper = $('