UNPKG

@progress/kendo-ui

Version:

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

1,676 lines (1,673 loc) 243 kB
//#region ../src/treelist/contextmenu.js (function($, undefined) { var kendo = window.kendo, ui = kendo.ui, ContextMenu = ui.ContextMenu, extend = $.extend, encode = kendo.htmlEncode; var ACTION = "action"; var TreeListContextMenu = ContextMenu.extend({ init: function(element, options) { var that = this; ContextMenu.fn.init.call(that, element, options); that._overrideTemplates(); that._extendItems(); that.bind("select", that._onSelect.bind(that)); that.bind("open", that._onOpen.bind(that)); }, _overrideTemplates: function() { this.templates.sprite = ({ icon, spriteCssClass }) => `${icon || spriteCssClass ? kendo.ui.icon({ icon: encode(icon || ""), iconClass: encode(spriteCssClass || "") }) : ""}`; }, defaultItems: { "separator": { name: "separator", separator: true }, "create": { name: "create", text: "Add", icon: "plus", command: "AddCommand", rules: "isEditable" }, "createChild": { name: "createChild", text: "Add Child", icon: "plus", command: "CreateChildCommand", rules: "isEditable" }, "edit": { name: "edit", text: "Edit", icon: "pencil", command: "EditCommand", rules: "isEditable" }, "destroy": { name: "destroy", text: "Delete", icon: "trash", command: "DeleteCommand", rules: "isEditable" }, "select": { name: "select", text: "Select", icon: "table-body", rules: "isSelectable", items: [ { name: "selectRow", text: "Row", icon: "table-row-groups", command: "SelectRowCommand" }, { name: "selectAllRows", text: "All rows", icon: "grid", command: "SelectAllRowsCommand", softRules: "isMultiRowSelectionEnabled" }, { name: "clearSelection", text: "Clear selection", icon: "table-unmerge", softRules: "hasSelection", command: "ClearSelectionCommand" } ] }, "exportPDF": { name: "exportPDF", text: "Export to PDF", icon: "file-pdf", command: "ExportPDFCommand" }, "exportExcel": { name: "exportExcel", text: "Export to Excel", icon: "file-excel", command: "ExportExcelCommand" }, "sortAsc": { name: "sortAsc", text: "Sort Ascending", icon: "sort-asc-small", rules: "isSortable", command: "SortCommand", options: "dir:asc" }, "sortDesc": { name: "sortDesc", text: "Sort Descending", icon: "sort-desc-small", rules: "isSortable", command: "SortCommand", options: "dir:desc" }, "expandItem": { name: "expandItem", text: "Expand Item", icon: "folder-open", softRules: "isExpandable", command: "ToggleItemCommand", options: "expand:true" }, "collapseItem": { name: "collapseItem", text: "Collapse Item", icon: "folder", softRules: "isCollapsible", command: "ToggleItemCommand", options: "expand:false" } }, events: ContextMenu.fn.events.concat([ACTION]), _onSelect: function(ev) { var command = $(ev.item).data("command"); var options = $(ev.item).data("options"); options = options ? options.split(",").map((val) => { if (val.indexOf(":") > -1) { var [key, val] = val.split(":"); return { [key || "_"]: val }; } return { [val]: true }; }).reduce((acc, v) => Object.assign(acc, v), {}) : {}; var target = $(ev.target); if (!command) { return; } this.action({ command, options: Object.assign(options, { target }) }); }, _onOpen: function(ev) { var menu = ev.sender, items = menu.options.items, elTarget = $(ev.event ? ev.event.target : null); if (!items && $.isEmptyObject(this.defaultItems) || elTarget.closest(".k-grid-column-menu").length) { ev.preventDefault(); return; } this._toggleSeparatorVisibility(); menu.element.find(`[${kendo.attr("soft-rules")}]`).each((i, item) => { var rules = $(item).attr(kendo.attr("soft-rules")).split(";"); menu.enable(item, this._validateSoftRules(rules, elTarget)); }); }, _toggleSeparatorVisibility: function() { var that = this, items = that.element.find(".k-item.k-separator").filter((i, item) => { var prev = $(item).prev(".k-item:not(.k-separator)"); var next = $(item).next(".k-item:not(.k-separator)"); return !(prev.length && next.length); }); items.hide(); }, _extendItems: function() { var that = this, items = that.options.items, item, isBuiltInTool; if (items && items.length) { for (var i = 0; i < items.length; i++) { item = items[i]; isBuiltInTool = $.isPlainObject(item) && Object.keys(item).length === 1 && item.name; if (isBuiltInTool) { item = item.name; } if ($.isPlainObject(item)) { that._append(item); } else if (that.defaultItems[item]) { item = that.defaultItems[item]; that._append(item); } else if (typeof item === "string") { item = { name: item, text: item, spriteCssClass: item, command: item + "Command" }; that._append(item); } } } else { for (var key in that.defaultItems) { item = that.defaultItems[key]; that._append(item); } } }, _extendItem: function(item) { var that = this, messages = that.options.messages, attr = item.attr || {}; if (item.command) { attr[kendo.attr("command")] = item.command; } if (item.options) { attr[kendo.attr("options")] = item.options; } if (item.softRules) { attr[kendo.attr("soft-rules")] = item.softRules; } if (item.items) { for (var j = 0; j < item.items.length; j++) { item.items.forEach((subItem) => { that._extendItem(subItem); }); } } extend(item, { text: messages.commands[item.name], icon: item.icon || "", spriteCssClass: item.spriteCssClass || "", attr, uid: kendo.guid() }); }, _validateSoftRules: function(rules, target) { var that = this; if (!rules || !(rules && rules.length)) { return true; } for (var i = 0; i < rules.length; i++) { if (!this._readState(rules[i], target)) { return false; } } return true; }, _validateRules: function(tool) { var that = this, rules = tool.rules ? tool.rules.split(";") : []; if (!rules.length) { return true; } for (var i = 0; i < rules.length; i++) { if (!this._readState(rules[i])) { return false; } } return true; }, _readState: function(state, target) { var that = this, states = that.options.states; if (kendo.isFunction(states[state])) { return states[state](target); } else { return states[state]; } }, _append: function(item) { var that = this; that._extendItem(item); if (that._validateRules(item)) { that.append(item); } }, action: function(args) { this.trigger(ACTION, args); } }); kendo.ui.treelist = kendo.ui.treelist || {}; extend(kendo.ui.treelist, { ContextMenu: TreeListContextMenu }); })(window.kendo.jQuery); //#endregion //#region ../src/treelist/commands.js (function($, undefined) { var kendo = window.kendo, extend = $.extend, Class = kendo.Class; var Command = Class.extend({ init: function(options) { this.options = options; this.treelist = options.treelist; } }); var SortCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist, dataSource = treelist.dataSource, sort = dataSource.sort() || [], options = that.options, dir = options.dir, field = options.target.attr(kendo.attr("field")), multipleMode = treelist.options.sortable.mode && treelist.options.sortable.mode === "multiple", compare = treelist.options.compare, length, idx; if (multipleMode) { for (idx = 0, length = sort.length; idx < length; idx++) { if (sort[idx].field === field) { sort.splice(idx, 1); break; } } sort.push({ field, dir, compare }); } else { sort = [{ field, dir, compare }]; } dataSource.sort(sort); } }); var AddCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist; treelist.addRow(); } }); var CreateChildCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist, target = that.options.target.closest("tr"); treelist.addRow(target); } }); var EditCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist, inCellMode = treelist._editMode() === "incell", target = inCellMode ? that.options.target : that.options.target.closest("tr"); if (inCellMode) { treelist.editCell(target); } else { treelist.editRow(target); } } }); var DeleteCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist, target = that.options.target.closest("tr"); treelist.removeRow(target); } }); var SelectRowCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist, selectMode = kendo.ui.Selectable.parseOptions(treelist.options.selectable), target = that.options.target.closest("tr"); treelist.select(selectMode.cell ? target.find("td") : target); } }); var SelectAllRowsCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist, selectMode = kendo.ui.Selectable.parseOptions(treelist.options.selectable), rows = treelist.items(); treelist.select(selectMode.cell ? rows.find("td") : rows); } }); var ClearSelectionCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist; treelist.clearSelection(); } }); var ExportPDFCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist; treelist.saveAsPDF(); } }); var ExportExcelCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist; treelist.saveAsExcel(); } }); var ToggleItemCommand = Command.extend({ exec: function() { var that = this, treelist = that.treelist, target = that.options.target, options = that.options, expand = options.expand === "true"; if (expand) { treelist.expand(target); } else { treelist.collapse(target); } } }); kendo.ui.treelist = kendo.ui.treelist || {}; extend(kendo.ui.treelist, { TreeListCommand: Command, commands: { SortCommand, AddCommand, CreateChildCommand, EditCommand, DeleteCommand, SelectRowCommand, SelectAllRowsCommand, ClearSelectionCommand, ExportPDFCommand, ExportExcelCommand, ToggleItemCommand } }); })(window.kendo.jQuery); //#endregion //#region ../src/kendo.treelist.js const __meta__ = { id: "treelist", name: "TreeList", category: "web", description: "The TreeList widget displays self-referencing data and offers rich support for interacting with data, sorting, filtering, and selection.", depends: [ "dom", "data", "pager", "toolbar", "icons", "reorderable", "menu" ], features: [ { id: "treelist-sorting", name: "Sorting", description: "Support for column sorting", depends: ["columnsorter"] }, { id: "treelist-filtering", name: "Filtering", description: "Support for record filtering", depends: ["filtermenu"] }, { id: "treelist-columnmenu", name: "Column menu", description: "Support for header column menu", depends: ["columnmenu"] }, { id: "treelist-editing", name: "Editing", description: "Support for record editing", depends: [ "editable", "window", "textbox", "form" ] }, { id: "treelist-selection", name: "Selection", description: "Support for row selection", depends: ["selectable"] }, { id: "treelist-column-resize", name: "Column resizing", description: "Support for column resizing", depends: ["resizable"] }, { id: "treelist-dragging", name: "Drag & Drop", description: "Support for drag & drop of rows", depends: ["treeview.draganddrop"] }, { id: "treelist-excel-export", name: "Excel export", description: "Export data as Excel spreadsheet", depends: ["excel"] }, { id: "treelist-pdf-export", name: "PDF export", description: "Export data as PDF", depends: [ "pdf", "drawing", "progressbar" ] }, { id: "treelist-paging", name: "Paging", description: "Support for treelist paging", depends: ["pager"] } ] }; (function($, undefined) { var data = kendo.data; var encode = kendo.htmlEncode; var kendoDom = kendo.dom; var kendoDomElement = kendoDom.element; var kendoTextElement = kendoDom.text; var kendoHtmlElement = kendoDom.html; var outerWidth = kendo._outerWidth; var keys = $.extend({ F10: 121 }, kendo.keys); var outerHeight = kendo._outerHeight; var ui = kendo.ui; var DataBoundWidget = ui.DataBoundWidget; var DataSource = data.DataSource; var ObservableArray = data.ObservableArray; var Query = data.Query; var Model = data.Model; var browser = kendo.support.browser; var kendoTemplate = kendo.template; var toCamelCase = kendo.toCamelCase; var activeElement = kendo._activeElement; var touchDevice = kendo.support.touch; var isArray = Array.isArray; var extend = $.extend; var map = $.map; var grep = $.grep; var inArray = $.inArray; var isPlainObject = $.isPlainObject; var push = Array.prototype.push; var STRING = "string"; var CHANGE = "change"; var ITEM_CHANGE = "itemChange"; var ERROR = "error"; var PROGRESS = "progress"; var DOT = "."; var NS = ".kendoTreeList"; var CLICK = "click"; var INPUT = "input"; var BEFORE_EDIT = "beforeEdit"; var EDIT = "edit"; var PAGE = "page"; var PAGE_CHANGE = "pageChange"; var SAVE = "save"; var SAVE_CHANGES = "saveChanges"; var EXPAND = "expand"; var COLLAPSE = "collapse"; var CELL_CLOSE = "cellClose"; var REMOVE = "remove"; var DATA_CELL = "td:not(.k-group-cell):not(.k-hierarchy-cell):visible,th:not(.k-group-cell):not(.k-hierarchy-cell):visible"; var FILTER_CELL = ".k-filter-row td:not(.k-group-cell):not(.k-hierarchy-cell):visible,.k-filter-row th:not(.k-group-cell):not(.k-hierarchy-cell):visible"; var DATABINDING = "dataBinding"; var DATABOUND = "dataBound"; var CANCEL = "cancel"; var TABINDEX = "tabIndex"; var FILTERMENUINIT = "filterMenuInit"; var FILTERMENUOPEN = "filterMenuOpen"; var COLUMNHIDE = "columnHide"; var COLUMNSHOW = "columnShow"; var HEADERCELLS = "th.k-header"; var COLUMNREORDER = "columnReorder"; var COLUMNRESIZE = "columnResize"; var COLUMNMENUINIT = "columnMenuInit"; var COLUMNMENUOPEN = "columnMenuOpen"; var COLUMNLOCK = "columnLock"; var COLUMNUNLOCK = "columnUnlock"; var FILTER = "filter"; var NAVIGATE = "navigate"; var SORT = "sort"; var PARENTIDFIELD = "parentId"; var DRAGSTART = "dragstart"; var DRAG = "drag"; var DROP = "drop"; var DRAGEND = "dragend"; var NAVROW = "tr:visible"; var NAVCELL = "td:visible"; var NAVHEADER = "th:visible"; var NORECORDSCLASS = "k-grid-norecords"; var ITEMROW = "tr:not(.k-footer-template):visible"; var isRtl = false; var HEIGHT = "height"; var INCELL = "incell"; var INLINE = "inline"; var POPUP = "popup"; var TABLE = "table"; var CHECKBOX = "k-checkbox"; var CHECKBOXINPUT = "input[data-role='checkbox']." + CHECKBOX; var SELECTCOLUMNTMPL = "<input class=\"" + CHECKBOX + "\" data-role=\"checkbox\" aria-label=\"Select row\" aria-checked=\"false\" type=\"checkbox\">"; var SELECTCOLUMNHEADERTMPL = "<input class=\"" + CHECKBOX + "\" data-role=\"checkbox\" aria-label=\"Select all rows\" aria-checked=\"false\" type=\"checkbox\">"; var DRAGHANDLECOLUMNTMPL = () => kendo.ui.icon("reorder"); var SELECTED = "k-selected"; var whitespaceRegExp = "[\\x20\\t\\r\\n\\f]"; var filterRowRegExp = new RegExp("(^|" + whitespaceRegExp + ")" + "(k-filter-row)" + "(" + whitespaceRegExp + "|$)"); var ICON_REFRESH_SELECTOR = "[class*='-i-arrow-rotate-cw']"; var ICON_EXPAND_COLLAPSE_SELECTOR = "[ref-treelist-expand-collapse-icon]"; var CARET_ALT_RIGHT = "caret-alt-right"; var CARET_ALT_LEFT = "caret-alt-left"; var ARIA_LABEL = "aria-label"; var ID = "id", PX = "px", TR = "tr", DIV = "div", ARIA_LABEL = "aria-label", ARIA_OWNS = "aria-owns", ARIA_ROWCOUNT = "aria-rowcount", ARIA_COLCOUNT = "aria-colcount", ARIA_CONTROLS = "aria-controls", ARIA_COLINDEX = "aria-colindex", ARIA_ROWINDEX = "aria-rowindex", ARIA_EXPANDED = "aria-expanded", ARIA_CHECKED = "aria-checked", ARIA_ACTIVEDESCENDANT = "aria-activedescendant", ROLE = "role", NONE = "none", ROW = "row", ROWGROUP = "rowgroup", COLUMNHEADER = "columnheader", GRIDCELL = "gridcell", BLANK_ICON_SELECTOR = "[ref-blank-icon]"; var classNames = { wrapper: "k-treelist k-grid", header: "k-header k-table-th", button: "k-button", alt: "k-table-alt-row", editCell: "k-edit-cell", editRow: "k-grid-edit-row", dirtyCell: "k-dirty-cell", toolbar: "k-toolbar", gridToolbar: "k-grid-toolbar", gridHeader: "k-grid-header", gridHeaderWrap: "k-grid-header-wrap", gridContent: "k-grid-content", gridContentWrap: "k-grid-content", gridFilter: "k-grid-filter-menu", footerTemplate: "k-footer-template", focused: "k-focus", loading: "k-i-loading", refresh: "arrow-rotate-cw", retry: "k-request-retry", selected: "k-selected", status: "k-status", link: "k-link", filterable: "k-filterable", icon: "k-icon", iconFilter: "filter", iconCollapse: "caret-alt-down", iconExpand: "caret-alt-right", iconPlaceHolder: "k-treelist-toggle k-icon k-svg-icon", input: "k-input", dropPositions: "k-i-insert-top k-i-insert-bottom k-i-plus k-i-insert-middle", dropTop: "insert-top", dropBottom: "insert-bottom", dropAdd: "plus", dropMiddle: "insert-middle", dropDenied: "cancel", dragStatus: "k-drag-status", dragClue: "k-drag-clue", dragClueText: "k-clue-text", headerCellInner: "k-cell-inner", columnTitle: "k-column-title" }; var defaultCommands = { create: { icon: "plus", className: "k-grid-add", methodName: "addRow" }, createchild: { icon: "plus", className: "k-grid-add", methodName: "addRow" }, destroy: { icon: "x", className: "k-grid-remove-command", methodName: "removeRow" }, edit: { icon: "pencil", className: "k-button-primary k-grid-edit-command", methodName: "editRow" }, update: { icon: "save", className: "k-button-primary k-grid-save-command", methodName: "saveRow" }, canceledit: { icon: "cancel", className: "k-grid-cancel-command", methodName: "_cancelEdit" }, cancel: { icon: "cancel-outline", text: "Cancel changes", className: "k-grid-cancel-changes", methodName: "cancelChanges" }, save: { icon: "check", text: "Save changes", className: "k-grid-save-changes", methodName: "saveChanges" }, excel: { icon: "file-excel", className: "k-grid-excel", methodName: "saveAsExcel" }, pdf: { icon: "file-pdf", className: "k-grid-pdf", methodName: "saveAsPDF" }, search: { template: ({ message }) => "<span class='k-spacer'></span>" + "<span class='k-searchbox k-input k-grid-search'>" + kendo.ui.icon({ icon: "search", iconClass: "k-input-icon" }) + `<input autocomplete='off' placeholder='${message}' title='${message}' aria-label='${message}' class='k-input-inner' />` + "</span>" } }; var defaultBodyContextMenu = [ "create", "createChild", "edit", "destroy", "separator", "select", "separator", "exportPDF", "exportExcel", "separator", "expandItem", "collapseItem", "separator" ]; var defaultHeadContextMenu = [ "sortAsc", "sortDesc", "separator" ]; var TreeView = kendo.Class.extend({ init: function(data, options) { var that = this; that.data = data || []; that.options = extend(that.options, options); }, options: { defaultParentId: null, idField: "id", parentIdField: PARENTIDFIELD }, childrenMap: function() { var that = this; var childrenMap = {}; var dataLength = that.data.length; var dataItem; var dataItemId; var dataItemParentId; var idField = that.options.idField; var parentIdField = that.options.parentIdField; if (that._childrenMap) { return that._childrenMap; } for (var i = 0; i < dataLength; i++) { dataItem = this.data[i]; dataItemId = dataItem[idField]; dataItemParentId = dataItem[parentIdField]; childrenMap[dataItemId] = childrenMap[dataItemId] || []; childrenMap[dataItemParentId] = childrenMap[dataItemParentId] || []; childrenMap[dataItemParentId].push(dataItem); } that._childrenMap = childrenMap; return childrenMap; }, idsMap: function() { var that = this; var idsMap = {}; var data = that.data; var dataLength = data.length; var dataItem; var idField = that.options.idField; if (that._idMap) { return that._idMap; } for (var i = 0; i < dataLength; i++) { dataItem = data[i]; idsMap[dataItem[idField]] = dataItem; } that.idsMap = idsMap; return idsMap; }, dataMaps: function() { var that = this; var childrenMap = {}; var data = that.data; var dataLength = data.length; var idsMap = {}; var dataItem; var dataItemId; var dataItemParentId; var idField = that.options.idField; var parentIdField = that.options.parentIdField; if (that._dataMaps) { return that._dataMaps; } for (var i = 0; i < dataLength; i++) { dataItem = data[i]; dataItemId = dataItem[idField]; dataItemParentId = dataItem[parentIdField]; idsMap[dataItemId] = dataItem; childrenMap[dataItemId] = childrenMap[dataItemId] || []; childrenMap[dataItemParentId] = childrenMap[dataItemParentId] || []; childrenMap[dataItemParentId].push(dataItem); } that._dataMaps = { children: childrenMap, ids: idsMap }; return that._dataMaps; }, rootNodes: function() { var that = this; var data = that.data; var defaultParentId = that.options.defaultParentId; var dataLength = data.length; var rootNodes = []; var dataItem; var parentIdField = that.options.parentIdField; for (var i = 0; i < dataLength; i++) { dataItem = data[i]; if (dataItem[parentIdField] === defaultParentId) { rootNodes.push(dataItem); } } return rootNodes; }, removeCollapsedSubtreesFromRootNodes: function(options) { options = options || {}; var that = this; var rootNodes = that.rootNodes(); var result = []; var prunedTree; that._childrenMap = options.childrenMap = options.childrenMap || that.childrenMap(); options.maxDepth = options.maxDepth || Infinity; for (var i = 0; i < rootNodes.length; i++) { prunedTree = that.removeCollapsedSubtrees(rootNodes[i], options); result = result.concat(prunedTree); } return result; }, removeCollapsedSubtrees: function(rootNode, options) { options = options || {}; var that = this; var result = []; var childIdx; var prunedTree; var childrenMap = options.childrenMap || {}; var maxDepth = options.maxDepth || Infinity; var idField = that.options.idField; var children = childrenMap[rootNode[idField]] || []; var expanded = isUndefined(rootNode.expanded) ? options.expanded : rootNode.expanded; result.push(rootNode); if (children && expanded) { for (childIdx = 0; childIdx < children.length; childIdx++) { if (result.length >= maxDepth) { break; } prunedTree = that.removeCollapsedSubtrees(children[childIdx], options); result = result.concat(prunedTree); } } return result; } }); var TreeQuery = function(data) { this.data = data || []; }; TreeQuery.prototype = new Query(); TreeQuery.prototype.constructor = TreeQuery; TreeQuery.process = function(data, options, inPlace) { options = options || {}; var query = new TreeQuery(data); var group = options.group; var sort = Query.normalizeGroup(group || []).concat(Query.normalizeSort(options.sort || [])); var filterCallback = options.filterCallback; var filter = options.filter; var skip = options.skip; var take = options.take; var total; var childrenMap; var filteredChildrenMap; var view; var prunedData; if (sort && inPlace) { query = query.sort(sort, undefined, undefined, inPlace); } if (filter) { query = query.filter(filter); if (filterCallback) { query = filterCallback(query); } total = query.toArray().length; } if (sort && !inPlace) { query = query.sort(sort); if (group) { data = query.toArray(); } } if (options.processFromRootNodes) { view = new TreeView(query.toArray(), options); if (filter) { filteredChildrenMap = view.childrenMap(); } prunedData = view.removeCollapsedSubtreesFromRootNodes({ childrenMap: filter || sort && sort.length ? undefined : options.childrenMap, expanded: options.expanded, maxDepth: skip + take || Infinity }); childrenMap = view.childrenMap(); query = new TreeQuery(prunedData); } if (skip !== undefined && take !== undefined) { query = query.range(skip, take); } if (group) { query = query.group(group, data); } return { total, data: query.toArray(), childrenMap, filteredChildrenMap }; }; var TreeListModel = Model.define({ id: "id", parentId: PARENTIDFIELD, fields: { id: { type: "number" }, parentId: { type: "number", nullable: true } }, init: function(value) { Model.fn.init.call(this, value); this._loaded = false; if (!this.parentIdField) { this.parentIdField = PARENTIDFIELD; } this.parentId = this.get(this.parentIdField); }, accept: function(data) { Model.fn.accept.call(this, data); this.parentId = this.get(this.parentIdField); }, set: function(field, value, initiator) { if (field == PARENTIDFIELD && this.parentIdField != PARENTIDFIELD) { this[this.parentIdField] = value; } Model.fn.set.call(this, field, value, initiator); if (field == this.parentIdField) { this.parentId = this.get(this.parentIdField); } }, loaded: function(value) { if (value !== undefined) { this._loaded = value; } else { return this._loaded; } }, shouldSerialize: function(field) { return Model.fn.shouldSerialize.call(this, field) && field !== "_loaded" && field != "_error" && field != "_edit" && !(this.parentIdField !== "parentId" && field === "parentId"); } }); TreeListModel.parentIdField = PARENTIDFIELD; TreeListModel.define = function(base, options) { if (options === undefined) { options = base; base = TreeListModel; } var parentId = options.parentId || PARENTIDFIELD; options.parentIdField = parentId; var model = Model.define(base, options); if (parentId) { model.parentIdField = parentId; } return model; }; function is(field) { return function(object) { return object[field]; }; } function not(func) { return function(object) { return !func(object); }; } var TreeListDataSource = DataSource.extend({ init: function(options) { options = options || {}; var that = this; that._dataMaps = that._getDataMaps(); options.schema = extend(true, {}, { modelBase: TreeListModel, model: TreeListModel }, options.schema); DataSource.fn.init.call(this, options); }, _addRange: function() {}, _createNewModel: function(data) { var that = this; var model = {}; var fromModel = data instanceof Model; var parentIdField = this._modelParentIdField(); if (fromModel) { model = data; } model = DataSource.fn._createNewModel.call(this, model); if (!fromModel) { if (data.parentId) { data[model.parentIdField] = data.parentId; } else if (that._isPageable() && data[parentIdField]) { data[model.parentIdField] = data[parentIdField]; } model.accept(data); } return model; }, _shouldWrap: function() { return true; }, _push: function(result, operation) { var data = DataSource.fn._readData.call(this, result); if (!data) { data = result; } this[operation](data); }, _getData: function() { return this._data || []; }, _readData: function(newData) { var that = this; var data = that._isPageable() ? that._getData().toJSON() : that.data(); newData = DataSource.fn._readData.call(this, newData); this._replaceData((data.toJSON ? data.toJSON() : data).concat(newData), data); if (newData instanceof ObservableArray) { return newData; } return data; }, _replaceData: function(source, target) { var sourceLength = source.length; for (var i = 0; i < sourceLength; i++) { target[i] = source[i]; } target.length = sourceLength; }, _readAggregates: function(data) { var result = extend(this._aggregateResult, this.reader.aggregates(data)); if ("" in result) { result[this._defaultParentId()] = result[""]; delete result[""]; } return result; }, read: function(data) { var that = this; if (that._isPageable()) { that._dataMaps = {}; if (!that._modelOptions().expanded) { that._skip = 0; that._page = 1; that._collapsedTotal = undefined; } } return DataSource.fn.read.call(that, data); }, remove: function(root) { this._removeChildData(root); this._removeFromDataMaps(root); return DataSource.fn.remove.call(this, root); }, _removeChildData: function(model, removePristine) { var that = this; var pageable = that._isPageable(); var data = pageable ? this._getData() : this.data(); var childrenMap = pageable ? that._getChildrenMap() || that.childrenMap(data) : that._childrenMap(data); var items = this._subtree(childrenMap, model.id); var shouldRemovePristine = isUndefined(removePristine) ? false : removePristine; var removedItems = this._removeItems(items, shouldRemovePristine); that._removeFromDataMaps(removedItems); }, pushDestroy: function(items) { var that = this; if (!isArray(items)) { items = [items]; } for (var i = 0; i < items.length; i++) { that._removeChildData(items[i], true); that._removeFromDataMaps(items[i]); } DataSource.fn.pushDestroy.call(that, items); }, insert: function(index, model) { var that = this; var newModel = that._createNewModel(model); that._insertInDataMaps(newModel); return DataSource.fn.insert.call(that, index, newModel); }, _filterCallback: function(query) { var that = this; var i, item; var map = {}; var result = []; var data = query.toArray(); var idField = that._modelIdField(); var parentIdField = that._modelParentIdField(); var pageable = that._isPageable(); var parentSubtree = []; var parent; for (i = 0; i < data.length; i++) { item = data[i]; if (pageable) { parentSubtree = []; if (!map[item[idField]]) { map[item[idField]] = true; parentSubtree.push(item); } parent = that._parentNode(item); while (parent) { if (!map[parent[idField]]) { map[parent[idField]] = true; parentSubtree.unshift(parent); parent = that._parentNode(parent); } else { break; } } if (parentSubtree.length) { result = result.concat(parentSubtree); } } else { while (item) { if (!map[item[idField]]) { map[item[idField]] = true; result.push(item); } if (!map[item[parentIdField]]) { map[item[parentIdField]] = true; item = this.parentNode(item); if (item) { result.push(item); } } else { break; } } } } return new Query(result); }, _subtree: function(map, id) { var that = this; var result = map[id] || []; var defaultParentId = that._defaultParentId(); var idField = that._modelIdField(); for (var i = 0, len = result.length; i < len; i++) { if (result[i][idField] !== defaultParentId) { result = result.concat(that._subtree(map, result[i][idField])); } } return result; }, _childrenMap: function(data) { var map = {}; var i, item, id, parentId; data = this._observeView(data); for (i = 0; i < data.length; i++) { item = data[i]; id = item.id; parentId = item.parentId; map[id] = map[id] || []; map[parentId] = map[parentId] || []; map[parentId].push(item); } return map; }, childrenMap: function(data) { var view = this._createTreeView(data); var map = view.childrenMap(); return map; }, _getChildrenMap: function() { var that = this; var dataMaps = that._getDataMaps(); return dataMaps.children; }, _initIdsMap: function(data) { var that = this; var dataMaps = that._getDataMaps(); if (isUndefined(dataMaps.ids)) { dataMaps.ids = that._idsMap(data); } return dataMaps.ids; }, _idsMap: function(data) { var view = this._createTreeView(data); var map = view.idsMap(); return map; }, _getIdsMap: function() { var that = this; var dataMaps = that._getDataMaps(); return dataMaps.ids || {}; }, _getFilteredChildrenMap: function() { var that = this; var dataMaps = that._getDataMaps(); return dataMaps.filteredChildren; }, _setFilteredChildrenMap: function(map) { var that = this; var dataMaps = that._getDataMaps(); dataMaps.filteredChildren = map; }, _initDataMaps: function(data) { var that = this; var view = that._createTreeView(data); that._dataMaps = view.dataMaps(); return that._dataMaps; }, _initChildrenMapForParent: function(parent) { var that = this; var data = that._getData(); var childrenMap = that._getChildrenMap(); var idField = that._modelIdField(); var parentIdField = that._modelParentIdField(); var parentId = (parent || {})[idField]; if (childrenMap && parent) { childrenMap[parentId] = []; for (var i = 0; i < data.length; i++) { if (data[i][parentIdField] === parentId) { childrenMap[parentId].push(data[i]); } } } }, _getDataMaps: function() { var that = this; that._dataMaps = that._dataMaps || {}; return that._dataMaps; }, _createTreeView: function(data, options) { var view = new TreeView(data, extend(options, this._defaultTreeModelOptions())); return view; }, _defaultTreeModelOptions: function() { var that = this; var modelOptions = that._modelOptions(); return { defaultParentId: that._defaultParentId(), idField: that._modelIdField(), parentIdField: that._modelParentIdField(), expanded: modelOptions.expanded }; }, _defaultDataItemType: function() { return this.reader.model || kendo.data.ObservableObject; }, _calculateAggregates: function(data, options) { options = options || {}; var that = this; var result = {}; var item, subtree, i; var filter = options.filter; var skip = options.skip; var take = options.take; var maxDepth = !isUndefined(skip) && !isUndefined(take) ? skip + take : Infinity; var pageable = that._isPageable(); var filteredChildrenMap = options.filteredChildrenMap; var childrenMap = options.childrenMap; var pageableChildrenMap; if (pageable) { if (isUndefined(options.aggregate)) { return result; } if (filteredChildrenMap) { pageableChildrenMap = filteredChildrenMap; } else if (childrenMap) { pageableChildrenMap = childrenMap; } else { pageableChildrenMap = that.childrenMap(that._getData()); } } if (!pageable && filter) { data = Query.process(data, { filter, filterCallback: this._filterCallback.bind(this) }).data; } var map = pageable ? pageableChildrenMap : that._childrenMap(data); result[this._defaultParentId()] = new Query(this._subtree(map, this._defaultParentId())).aggregate(options.aggregate); for (i = 0; i < data.length; i++) { if (i >= maxDepth) { break; } item = data[i]; subtree = this._subtree(map, item.id); result[item.id] = new Query(subtree).aggregate(options.aggregate); } return result; }, _queryProcess: function(data, options) { var that = this; var result = {}; options = options || {}; options.filterCallback = this._filterCallback.bind(this); if (that._isPageable()) { return that._processPageableQuery(data, options); } else { var defaultParentId = this._defaultParentId(); result = Query.process(data, options); var map = this._childrenMap(result.data); var hasLoadedChildren, i, item, children; data = map[defaultParentId] || []; for (i = 0; i < data.length; i++) { item = data[i]; if (item.id === defaultParentId) { continue; } children = map[item.id]; hasLoadedChildren = !!(children && children.length); if (!item.loaded()) { item.loaded(hasLoadedChildren || !item.hasChildren); } if (item.loaded() || item.hasChildren !== true) { item.hasChildren = hasLoadedChildren; } if (hasLoadedChildren) { data = data.slice(0, i + 1).concat(children, data.slice(i + 1)); } } result.data = data; } return result; }, _processPageableQuery: function(data, options) { var that = this; var dataMaps = that._getDataMaps(); var result; var filteredChildrenMap; if (that._getData() !== data || !dataMaps.children || !dataMaps.ids) { dataMaps = that._initDataMaps(that._getData()); } options.childrenMap = dataMaps.children || {}; options.idsMap = dataMaps.ids || {}; result = that._processTreeQuery(data, options); that._replaceWithObservedData(result.data, data); that._processDataItemsState(result.data, result.childrenMap); that._replaceItemsInDataMaps(result.data); result.dataToAggregate = that._dataToAggregate(result.data, options); if (options.filter || that.filter()) { filteredChildrenMap = result.filteredChildrenMap; that._replaceInMapWithObservedData(filteredChildrenMap, data); that._setFilteredChildrenMap(filteredChildrenMap); options.filteredChildrenMap = filteredChildrenMap; that._calculateCollapsedTotal(result.data); } else { that._collapsedTotal = undefined; } return result; }, _dataToAggregate: function(data) { var that = this; var firstDataItem = data[0] || {}; var firstItemParents = that._parentNodes(firstDataItem); var dataToAggregate = firstItemParents.concat(data); return dataToAggregate; }, _replaceItemsInDataMaps: function(observableArray) { var that = this; var view = isArray(observableArray) ? observableArray : [observableArray]; var itemType = that._defaultDataItemType(); var defaultParentId = that._defaultParentId(); var idField = that._modelIdField(); var parentIdField = that._modelParentIdField(); var dataMaps = that._getDataMaps(); var item; var parents; var directParent; for (var viewIndex = 0; viewIndex < view.length; viewIndex++) { item = view[viewIndex]; if (!(item instanceof itemType)) { continue; } that._insertInIdsMap(item); parents = that._parentNodes(item); directParent = parents && parents.length ? parents[parents.length - 1] : undefined; if (item[parentIdField] === defaultParentId) { that._replaceInMap(dataMaps.children, defaultParentId, item, itemType); } else if (directParent) { that._replaceInMap(dataMaps.children, directParent[idField], item, itemType); } } }, _replaceInMap: function(map, id, replacement, itemType) { var idField = this._modelIdField(); map[id] = map[id] || []; itemType = itemType || this._defaultDataItemType(); var itemInArray = map[id].filter(function(element) { return replacement[idField] === element[idField]; })[0]; var itemIndex = itemInArray ? map[id].indexOf(itemInArray) : -1; if (itemIndex !== -1 && !(itemInArray instanceof itemType)) { map[id][itemIndex] = replacement; } }, _replaceWithObservedData: function(dataToReplace, replacementArray) { var that = this; var idsMap = that._getDataMaps().ids || {}; var idField = that._modelIdField(); var itemType = that._defaultDataItemType(); var itemToReplace; var itemToReplaceId; var dataItem; var dataItemIndex; var observableItem; for (var i = 0; i < dataToReplace.length; i++) { itemToReplace = dataToReplace[i]; itemToReplaceId = itemToReplace[idField]; if (!(itemToReplace instanceof itemType)) { if (!(idsMap[itemToReplaceId] instanceof itemType)) { dataItem = that._getById(itemToReplaceId); dataItemIndex = replacementArray.indexOf(dataItem); if (dataItem && dataItemIndex !== -1) { observableItem = replacementArray.at(dataItemIndex); dataToReplace[i] = observableItem; } } else { dataToReplace[i] = idsMap[itemToReplaceId]; } } } }, _replaceInMapWithObservedData: function(map, replacementArray) { var that = this; for (var key in map) { that._replaceWithObservedData(map[key], replacementArray); } }, _insertInDataMaps: function(item) { var that = this; if (that._isPageable()) { that._insertInIdsMap(item); that._insertInChildrenMap(item); } }, _insertInIdsMap: function(item) { var that = this; var idsMap = that._getIdsMap(); var idField = that._modelIdField(); if (!isUndefined(item[idField])) { idsMap[item[idField]] = item; } }, _insertInChildrenMap: function(item, index) { var that = this; var childrenMap = that._getChildrenMap() || {}; var idField = that._modelIdField(); var parentIdField = that._modelParentIdField(); var itemId = item[idField]; var parentId = item[parentIdField]; index = index || 0; childrenMap[itemId] = childrenMap[itemId] || []; childrenMap[parentId] = childrenMap[parentId] || []; childrenMap[parentId].splice(index, 0, item); }, _removeFromDataMaps: function(items) { var that = this; items = isArray(items) ? items : [items]; if (that._isPageable()) { for (var i = 0; i < items.length; i++) { that._removeFromIdsMap(items[i]); that._removeFromChildrenMap(items[i]); } } }, _removeFromIdsMap: function(item) { var that = this; var idsMap = that._getIdsMap(); var idField = that._modelIdField(); if (!isUndefined(item[idField])) { idsMap[item[idField]] = undefined; } }, _removeFromChildrenMap: function(item) { var that = this; var childrenMap = that._getChildrenMap() || {}; var parentIdField = that._modelParentIdField(); var parentId = item[parentIdField]; childrenMap[parentId] = childrenMap[parentId] || []; var itemIndex = that._indexInChildrenMap(item); if (itemIndex !== -1) { childrenMap[parentId].splice(itemIndex, 1); } }, _indexInChildrenMap: function(item) { var that = this; return that._itemIndexInMap(item, that._getChildrenMap()); }, _itemIndexInMap: function(item, dataMap) { var that = this; var map = dataMap || {}; var parentIdField = that._modelParentIdField(); var parentId = item[parentIdField]; map[parentId] = map[parentId] || []; var itemInArray = map[parentId].filter(function(element) { return item.uid === element.uid; })[0]; var itemIndex = itemInArray ? map[parentId].indexOf(itemInArray) : -1; return itemIndex; }, _getById: function(id) { var that = this; var idField = that._modelIdField(); var data = that._getData(); for (var i = 0; i < data.length; i++) { if (data[i][idField] === id) { return data[i]; } } }, _isLastItemInView: function(dataItem) { var view = this.view(); return view.length && view[view.length - 1] === dataItem; }, _defaultPageableQueryOptions: function() { var that = this; var dataMaps = that._getDataMaps(); var options = { skip: that.skip(), take: that.take(), page: that.page(), pageSize: that.pageSize(), sort: that.sort(), filter: that.filter(), group: that.group(), aggregate: that.aggregate(), filterCallback: that._filterCallback.bind(that), childrenMap: dataMaps.children, idsMap: dataMaps.ids }; return options; }, _isPageable: function() { var pageSize = this.pageSize(); return !isUndefined(pageSize) && pageSize > 0 && !this.options.serverPaging; }, _updateTotalForAction: function(action, items) { var that = this; DataSource.fn._updateTotalForAction.call(that, action, items); if (that._isPageable()) { that._updateCollapsedTotalForAction(action, items); } }, _updateCollapsedTotalForAction: function(action, items) { var that = this; var total = parseInt(that._collapsedTotal, 10); if (!isNumber(that._collapsedTotal)) { that._calculateCollapsedTotal(); return; } if (action === "add") { total += items.length; } else if (action === "remove") { total -= items.length; } else if (action !== "itemchange" && action !== "sync" && !that.options.serverPaging) { total = that._calculateCollapsedTotal(); } else if (action === "sync") { total = that._calculateCollapsedTotal(); } that._collapsedTotal = total; }, _setFilterTotal: function(filterTotal, setDefaultValue) { var that = this; DataSource.fn._setFilterTotal.call(that, filterTotal, setDefaultValue); }, collapsedTotal: function() { var that = this; if (!isUndefined(that._collapsedTotal)) { return that._collapsedTotal; } return that._calculateCollapsedTotal(); }, _calculateCollapsedTotal: function(filteredData) { var that = this; var data = that._dataWithoutCollapsedSubtrees(filteredData); if (data.length) { that._collapsedTotal = data.length; } return that._collapsedTotal; }, _dataWithoutCollapsedSubtrees: function(filteredData) { return this._removeCollapsedSubtrees(filteredData || this._getData()); }, _removeCollapsedSubtrees: function(data) { var that = this; var view = that._createTreeView(data); var result = view.removeCollapsedSubtreesFromRootNodes({ expanded: that._modelOptions().expanded, childrenMap: that.filter() ? that._getFilteredChildrenMap() : that._getChildrenMap() }); return result; }, _processTreeQuery: function(data, options) { var result = TreeQuery.process(data, extend(options, this._defaultTreeModelOptions(), { processFromRootNodes: true })); return result; }, _processDataItemsState: function(data, childrenMap) { var dataLength = data.length; var i; for (i = 0; i < dataLength; i++) { this._processDataItemState(data[i], childrenMap); } }, _processDataItemState: function(dataItem, childrenMap) { var defaultParentId = this._defaultParentId(); if (dataItem.id === defaultParentId) { return; } var children = childrenMap[dataItem.id] || []; var hasLoadedChildren = !!(children && children.length); if (!dataItem.loaded) { return; } if (!dataItem.loaded()) { dataItem.loaded(hasLoadedChildren || !dataItem.hasChildren); } if (dataItem.loaded() || dataItem.hasChildren !== true) { dataItem.hasChildren = hasLoadedChildren; } }, _queueRequest: function(options, callback) { callback.call(this); }, _modelLoaded: function(id) { var model = this.get(id); model.loaded(true); model.hasChildren = this.childNodes(model).length > 0; }, _modelError: function(id, e) { this.get(id)._error = e; }, success: function(data, requestParams) { if (!requestParams || typeof requestParams.id == "undefined") { this._data = this._observe([]); } DataSource.fn.success.call(this, data, requestParams); this._total = this._data.length; }, load: function(model) { var method = "_query"; var remote = this.options.serverSorting || this.options.serverPaging || this.options.serverFiltering || this.options.serverGrouping || this.options.serverAggregates; var defaultPromise = $.Deferred().resolve().promise(); if (model.loaded()) { if (remote) { return defaultPromise; } } else if (model.hasChildren) { method = "read"; this._removeChildData(model); } return this[method]({ id: model.id }).done(this._modelLoaded.bind(this, model.id)).fail(this._modelError.bind(this, model.id)); }, contains: function(root, child) { var that = this; var idField = that._modelIdField(); var parentIdField = that._modelParentIdField(); var rootId = root[idField]; var pageable = that._isPageable(); while (child) { if (child[parentIdField] === rootId) { return true; } child = pageable ? that._parentNode(child) : that.parentNode(child); } return false; }, _byParentId: function(id, defaultId) { var result = []; var view = this.view(); var current; if (id === defaultId) { return []; } for (var i = 0; i < view.length; i++) { current = view.at(i); if (current.parentId == id) { result.push(current); } } return result; }, _defaultParentId: function() { return this.reader.model.fn.defaults[this.reader.model.parentIdField]; }, _modelOptions: function() { var modelOptions = (this.options.schema || {}).model || {}; return modelOptions; }, _modelIdField: function() { var modelOptions = this._modelOptions(); return modelOptions.id || "id"; }, _modelParentIdField: function() { var modelOptions = this._modelOptions(); return modelOptions.parentId || PARENTIDFIELD; }, childNodes: function(model) { return this._byParentId(model.id, this._defaultParentId()); }, allChildNodes: function(model, result) { var directChildren = this.data().filter(function(item) { return item.parentId === model.id; }); for (var i = 0; i < directChildren.length; i++) { result.push(directChildren[i]); this.allChildNodes(directChildren[i], result); } }, rootNodes: function() { return this._byParentId(this._defaultParentId()); }, _rootNode: function(child) { return this._parentNodes(child)[0]; }, _pageableRootNodes: function(options) { options = options ||