@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
JavaScript
//#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 ||