UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,476 lines (1,197 loc) • 59.8 kB
"use strict"; var $ = require("../../core/renderer"), domAdapter = require("../../core/dom_adapter"), eventsEngine = require("../../events/core/events_engine"), messageLocalization = require("../../localization/message"), clickEvent = require("../../events/click"), commonUtils = require("../../core/utils/common"), windowUtils = require("../../core/utils/window"), typeUtils = require("../../core/utils/type"), extend = require("../../core/utils/extend").extend, each = require("../../core/utils/iterator").each, getPublicElement = require("../../core/utils/dom").getPublicElement, CheckBox = require("../check_box"), HierarchicalCollectionWidget = require("../hierarchical_collection/ui.hierarchical_collection_widget"), eventUtils = require("../../events/utils"), pointerEvents = require("../../events/pointer"), dblclickEvent = require("../../events/double_click"), fx = require("../../animation/fx"), Scrollable = require("../scroll_view/ui.scrollable"), LoadIndicator = require("../load_indicator"), deferredUtils = require("../../core/utils/deferred"), Deferred = deferredUtils.Deferred, when = deferredUtils.when; var WIDGET_CLASS = "dx-treeview", NODE_CONTAINER_CLASS = "dx-treeview-node-container", OPENED_NODE_CONTAINER_CLASS = "dx-treeview-node-container-opened", NODE_CLASS = "dx-treeview-node", ITEM_CLASS = "dx-treeview-item", ITEM_WITH_CHECKBOX_CLASS = "dx-treeview-item-with-checkbox", ITEM_DATA_KEY = "dx-treeview-item-data", IS_LEAF = "dx-treeview-node-is-leaf", EXPAND_EVENT_NAMESPACE = "dxTreeView_expand", TOGGLE_ITEM_VISIBILITY_CLASS = "dx-treeview-toggle-item-visibility", LOAD_INDICATOR_CLASS = "dx-treeview-loadindicator", LOAD_INDICATOR_WRAPPER_CLASS = "dx-treeview-loadindicator-wrapper", NODE_LOAD_INDICATOR_CLASS = "dx-treeview-node-loadindicator", TOGGLE_ITEM_VISIBILITY_OPENED_CLASS = "dx-treeview-toggle-item-visibility-opened", SELECT_ALL_ITEM_CLASS = "dx-treeview-select-all-item", DISABLED_STATE_CLASS = "dx-state-disabled", SELECTED_ITEM_CLASS = "dx-state-selected", DATA_ITEM_ID = "data-item-id"; var TreeViewBase = HierarchicalCollectionWidget.inherit({ _supportedKeys: function _supportedKeys(e) { var click = function click(e) { var $itemElement = $(this.option("focusedElement")); if (!$itemElement.length) { return; } e.target = $itemElement; e.currentTarget = $itemElement; this._itemClickHandler(e, $itemElement.children("." + ITEM_CLASS)); }; var select = function select(e) { e.preventDefault(); this._changeCheckBoxState($(this.option("focusedElement"))); }; var toggleExpandedNestedItems = function toggleExpandedNestedItems(state, e) { if (!this.option("expandAllEnabled")) { return; } e.preventDefault(); var $rootElement = $(this.option("focusedElement")); if (!$rootElement.length) { return; } var rootItem = this._getItemData($rootElement.find("." + ITEM_CLASS)); this._toggleExpandedNestedItems([rootItem], state); }; return extend(this.callBase(), { enter: this._showCheckboxes() ? select : click, space: this._showCheckboxes() ? select : click, asterisk: toggleExpandedNestedItems.bind(this, true), minus: toggleExpandedNestedItems.bind(this, false) }); }, _changeCheckBoxState: function _changeCheckBoxState($element) { var checkboxInstance = this._getCheckBoxInstance($element), currentState = checkboxInstance.option("value"); if (!checkboxInstance.option("disabled")) { this._updateItemSelection(!currentState, $element.find("." + ITEM_CLASS).get(0), true, $element); } }, _toggleExpandedNestedItems: function _toggleExpandedNestedItems(items, state) { if (!items) { return; } for (var i = 0, len = items.length; i < len; i++) { var item = items[i], node = this._dataAdapter.getNodeByItem(item); this._toggleExpandedState(node, state); this._toggleExpandedNestedItems(item.items, state); } }, _getNodeElement: function _getNodeElement(node, cache) { var normalizedKey = commonUtils.normalizeKey(node.internalFields.key); if (cache) { if (!cache.$nodeByKey) { cache.$nodeByKey = {}; this.$element().find(".dx-treeview-node").each(function () { var $node = $(this), key = $node.attr(DATA_ITEM_ID); cache.$nodeByKey[key] = $node; }); } return cache.$nodeByKey[normalizedKey] || $(); } return this.$element().find("[" + DATA_ITEM_ID + "='" + normalizedKey + "']"); }, _activeStateUnit: "." + ITEM_CLASS, _widgetClass: function _widgetClass() { return WIDGET_CLASS; }, _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), { /** * @name dxTreeViewOptions.items * @publicName items * @type Array<dxTreeViewItemTemplate> * @inheritdoc */ /** * @name dxTreeViewOptions.animationEnabled * @publicName animationEnabled * @type boolean * @default true */ animationEnabled: true, /** * @name dxTreeViewOptions.dataStructure * @publicName dataStructure * @type Enums.TreeViewDataStructure * @default 'tree' */ dataStructure: "tree", /** * @name dxTreeViewOptions.expandAllEnabled * @publicName expandAllEnabled * @type boolean * @default false */ expandAllEnabled: false, /** * @name dxTreeViewOptions.hasItemsExpr * @publicName hasItemsExpr * @type string|function * @default 'hasItems' */ hasItemsExpr: "hasItems", /** * @name dxTreeViewOptions.selectNodesRecursive * @publicName selectNodesRecursive * @type boolean * @default true */ selectNodesRecursive: true, /** * @name dxTreeViewOptions.expandNodesRecursive * @publicName expandNodesRecursive * @type boolean * @default true */ expandNodesRecursive: true, /** * @name dxTreeViewOptions.showCheckBoxesMode * @publicName showCheckBoxesMode * @type Enums.TreeViewCheckBoxMode * @default 'none' */ showCheckBoxesMode: "none", /** * @name dxTreeViewOptions.selectAllText * @publicName selectAllText * @type string * @default "Select All" */ selectAllText: messageLocalization.format("dxList-selectAll"), /** * @name dxTreeViewOptions.onItemSelectionChanged * @publicName onItemSelectionChanged * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field7 node:dxTreeViewNode * @type_function_param1_field8 itemElement:dxElement * @action */ onItemSelectionChanged: null, /** * @name dxTreeViewOptions.onItemClick * @publicName onItemClick * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field7 node:dxTreeViewNode * @action * @inheritdoc */ /** * @name dxTreeViewOptions.onItemContextMenu * @publicName onItemContextMenu * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field8 node:dxTreeViewNode * @action * @inheritdoc */ /** * @name dxTreeViewOptions.onItemRendered * @publicName onItemRendered * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field8 node:dxTreeViewNode * @action * @inheritdoc */ /** * @name dxTreeViewOptions.onItemHold * @publicName onItemHold * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field8 node:dxTreeViewNode * @action * @inheritdoc */ /** * @name dxTreeViewOptions.onItemExpanded * @publicName onItemExpanded * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 itemData:object * @type_function_param1_field5 itemElement:dxElement * @type_function_param1_field6 itemIndex:Number * @type_function_param1_field7 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field8 event:event * @type_function_param1_field9 node:dxTreeViewNode * @action */ onItemExpanded: null, /** * @name dxTreeViewOptions.onItemCollapsed * @publicName onItemCollapsed * @extends Action * @type function(e) * @type_function_param1 e:object * @type_function_param1_field4 itemData:object * @type_function_param1_field5 itemElement:dxElement * @type_function_param1_field6 itemIndex:Number * @type_function_param1_field7 jQueryEvent:jQuery.Event:deprecated(event) * @type_function_param1_field8 event:event * @type_function_param1_field9 node:dxTreeViewNode * @action */ onItemCollapsed: null, /** * @name dxTreeViewOptions.scrollDirection * @publicName scrollDirection * @type Enums.ScrollDirection * @default "vertical" */ scrollDirection: "vertical", /** * @name dxTreeViewOptions.virtualModeEnabled * @publicName virtualModeEnabled * @type boolean * @default false */ virtualModeEnabled: false, /** * @name dxTreeViewOptions.rootValue * @type Object * @publicName rootValue * @default 0 */ rootValue: 0, focusStateEnabled: false, /** * @name dxTreeViewOptions.selectionMode * @publicName selectionMode * @type Enums.NavSelectionMode * @default "multiple" */ selectionMode: "multiple", expandEvent: 'dblclick', /** * @name dxTreeViewOptions.selectByClick * @publicName selectByClick * @type boolean * @default false */ selectByClick: false, /** * @name dxTreeViewOptions.createChildren * @publicName createChildren * @type function * @type_function_param1 parentNode:dxTreeViewNode * @type_function_return Promise<any>|Array<Object> */ createChildren: null /** * @name dxTreeViewOptions.onSelectionChanged * @publicName onSelectionChanged * @extends Action * @action */ /** * @name dxTreeViewOptions.parentIdExpr * @publicName parentIdExpr * @type string|function * @default 'parentId' * @hidden false */ /** * @name dxTreeViewOptions.expandedExpr * @publicName expandedExpr * @type string|function * @default 'expanded' * @hidden false */ /** * @name dxTreeViewOptions.selectedItem * @publicName selectedItem * @hidden * @inheritdoc */ /** * @name dxTreeViewOptions.selectedItems * @publicName selectedItems * @hidden * @inheritdoc */ /** * @name dxTreeViewOptions.selectedItemKeys * @publicName selectedItemKeys * @hidden * @inheritdoc */ /** * @name dxTreeViewOptions.selectedIndex * @publicName selectedIndex * @hidden * @inheritdoc */ /** * @name dxTreeViewItemTemplate * @publicName dxTreeViewItemTemplate * @inherits CollectionWidgetItemTemplate * @type object */ /** * @name dxTreeViewItemTemplate.selected * @publicName selected * @type boolean * @default false */ /** * @name dxTreeViewItemTemplate.expanded * @publicName expanded * @type boolean * @default false */ /** * @name dxTreeViewItemTemplate.icon * @publicName icon * @type String */ /** * @name dxTreeViewItemTemplate.items * @publicName items * @type Array<dxTreeViewItemTemplate> */ /** * @name dxTreeViewItemTemplate.parentId * @publicName parentId * @type number|string * @default undefined */ /** * @name dxTreeViewItemTemplate.hasItems * @publicName hasItems * @type boolean * @default undefined */ }); }, // TODO: implement these functions _initSelectedItems: commonUtils.noop, _syncSelectionOptions: commonUtils.noop, _fireSelectionChanged: function _fireSelectionChanged() { var selectionChangePromise = this._selectionChangePromise; when(selectionChangePromise).done(function () { this._createActionByOption("onSelectionChanged", { excludeValidators: ["disabled", "readOnly"] })(); }.bind(this)); }, _checkBoxModeChange: function _checkBoxModeChange(value, previousValue) { if (previousValue === "none" || value === "none") { this.repaint(); return; } var selectAllExists = this._$selectAllItem && this._$selectAllItem.length; switch (value) { case "selectAll": !selectAllExists && this._renderSelectAllItem(); break; case "normal": if (selectAllExists) { this._$selectAllItem.remove(); delete this._$selectAllItem; } break; } }, _removeSelection: function _removeSelection() { var that = this; each(this._dataAdapter.getFullData(), function (_, node) { if (!that._hasChildren(node)) { return; } that._dataAdapter.toggleSelection(node.internalFields.key, false, true); }); }, _optionChanged: function _optionChanged(args) { var name = args.name, value = args.value, previousValue = args.previousValue; switch (name) { case "selectAllText": if (this._$selectAllItem) { this._$selectAllItem.dxCheckBox("instance").option("text", value); } break; case "showCheckBoxesMode": this._checkBoxModeChange(value, previousValue); break; case "scrollDirection": this._scrollableContainer.option("direction", value); break; case "items": delete this._$selectAllItem; this.callBase(args); break; case "dataSource": this.callBase(args); this._initDataAdapter(); this._filter = {}; break; case "hasItemsExpr": this._initAccessors(); this.repaint(); break; case "expandEvent": this._initExpandEvent(); break; case "dataStructure": case "rootValue": case "createChildren": case "expandNodesRecursive": case "onItemSelectionChanged": case "onItemExpanded": case "onItemCollapsed": case "expandAllEnabled": case "animationEnabled": case "virtualModeEnabled": case "selectByClick": break; case "selectNodesRecursive": this._dataAdapter.setOption("recursiveSelection", args.value); this.repaint(); break; default: this.callBase(args); } }, _initDataSource: function _initDataSource() { if (this._useCustomChildrenLoader()) { this._loadChildrenByCustomLoader(null).done(function (newItems) { if (newItems && newItems.length) { this.option("items", newItems); } }.bind(this)); } else { this.callBase(); this._isVirtualMode() && this._initVirtualMode(); } }, _initVirtualMode: function _initVirtualMode() { var that = this, filter = that._filter; if (!filter.custom) { filter.custom = that._dataSource.filter(); } if (!filter.internal) { filter.internal = [that.option("parentIdExpr"), that.option("rootValue")]; } }, _useCustomChildrenLoader: function _useCustomChildrenLoader() { return typeUtils.isFunction(this.option("createChildren")) && this._isDataStructurePlain(); }, _loadChildrenByCustomLoader: function _loadChildrenByCustomLoader(parentNode) { var invocationResult = this.option("createChildren").call(this, parentNode); if (Array.isArray(invocationResult)) { return new Deferred().resolve(invocationResult).promise(); } if (invocationResult && typeUtils.isFunction(invocationResult.then)) { return deferredUtils.fromPromise(invocationResult); } return new Deferred().resolve([]).promise(); }, _combineFilter: function _combineFilter() { if (!this._filter.custom || !this._filter.custom.length) { return this._filter.internal; } return [this._filter.custom, this._filter.internal]; }, _dataSourceLoadErrorHandler: function _dataSourceLoadErrorHandler() { this._renderEmptyMessage(); }, _init: function _init() { this._filter = {}; this.callBase(); this._initStoreChangeHandlers(); }, _dataSourceChangedHandler: function _dataSourceChangedHandler(newItems) { if (this._initialized && this._isVirtualMode() && this.option("items").length) { return; } this.option("items", newItems); }, _removeTreeViewLoadIndicator: function _removeTreeViewLoadIndicator() { if (!this._treeViewLoadIndicator) return; this._treeViewLoadIndicator.remove(); this._treeViewLoadIndicator = null; }, _createTreeViewLoadIndicator: function _createTreeViewLoadIndicator() { this._treeViewLoadIndicator = $("<div>").addClass(LOAD_INDICATOR_CLASS); this._createComponent(this._treeViewLoadIndicator, LoadIndicator, {}); return this._treeViewLoadIndicator; }, _dataSourceLoadingChangedHandler: function _dataSourceLoadingChangedHandler(isLoading) { var resultFilter; if (this._isVirtualMode()) { resultFilter = this._combineFilter(); this._dataSource.filter(resultFilter); } if (isLoading && !this._dataSource.isLoaded()) { this.option("items", []); var $wrapper = $("<div>").addClass(LOAD_INDICATOR_WRAPPER_CLASS); this._createTreeViewLoadIndicator().appendTo($wrapper); this.itemsContainer().append($wrapper); if (this._isVirtualMode() && this._dataSource.filter() !== resultFilter) { this._dataSource.filter([]); } } else { this._removeTreeViewLoadIndicator(); } }, _initStoreChangeHandlers: function _initStoreChangeHandlers() { if (this.option("dataStructure") !== "plain") { return; } var that = this; this._dataSource && this._dataSource.store().on("inserted", function (newItem) { that.option().items = that.option("items").concat(newItem); that._dataAdapter.addItem(newItem); if (!that._dataAdapter.isFiltered(newItem)) { return; } that._updateLevel(that._parentIdGetter(newItem)); }).on("removed", function (removedKey) { var node = that._dataAdapter.getNodeByKey(removedKey); that.option("items")[that._dataAdapter.getIndexByKey(node.internalFields.key)] = 0; that._markChildrenItemsToRemove(node); that._removeItems(); that._dataAdapter.removeItem(removedKey); that._updateLevel(that._parentIdGetter(node)); }); }, _markChildrenItemsToRemove: function _markChildrenItemsToRemove(node) { var that = this, keys = node.internalFields.childrenKeys; each(keys, function (_, key) { that.option("items")[that._dataAdapter.getIndexByKey(key)] = 0; that._markChildrenItemsToRemove(that._dataAdapter.getNodeByKey(key)); }); }, _removeItems: function _removeItems() { var that = this, counter = 0, items = extend(true, [], this.option("items")); each(items, function (index, item) { if (!item) { that.option("items").splice(index - counter, 1); counter++; } }); }, _updateLevel: function _updateLevel(parentId) { var $container = this._getContainerByParentKey(parentId); this._renderItems($container, this._dataAdapter.getChildrenNodes(parentId)); }, _getOldContainer: function _getOldContainer($itemElement) { if ($itemElement.length) { return $itemElement.children("." + NODE_CONTAINER_CLASS); } if (this._scrollableContainer) { return this._scrollableContainer.$content().children(); } return $(); }, _getContainerByParentKey: function _getContainerByParentKey(parentId) { var $container, node = this._dataAdapter.getNodeByKey(parentId), $itemElement = node ? this._getNodeElement(node) : []; this._getOldContainer($itemElement).remove(); $container = this._renderNodeContainer($itemElement); if (this._isRootLevel(parentId)) { if (!this._scrollableContainer) this._renderScrollableContainer(); this._scrollableContainer.$content().append($container); } return $container; }, _isRootLevel: function _isRootLevel(parentId) { return parentId === this.option("rootValue"); }, _getAccessors: function _getAccessors() { return ["key", "display", "selected", "expanded", "items", "parentId", "disabled", "hasItems"]; }, _getDataAdapterOptions: function _getDataAdapterOptions() { return { rootValue: this.option("rootValue"), multipleSelection: !this._isSingleSelection(), recursiveSelection: this._isRecursiveSelection(), recursiveExpansion: this.option("expandNodesRecursive"), dataType: this.option("dataStructure"), sort: this._dataSource && this._dataSource.sort() }; }, _initMarkup: function _initMarkup() { this._renderScrollableContainer(); this._renderEmptyMessage(this._dataAdapter.getRootNodes()); this.callBase(); this.setAria("role", "tree"); }, _renderContentImpl: function _renderContentImpl() { var $nodeContainer = this._renderNodeContainer(); this._scrollableContainer.$content().append($nodeContainer); if (!this.option("items") || !this.option("items").length) { return; } this._renderItems($nodeContainer, this._dataAdapter.getRootNodes()); this._initExpandEvent(); if (this._selectAllEnabled()) { this._renderSelectAllItem($nodeContainer); } }, _isVirtualMode: function _isVirtualMode() { return this.option("virtualModeEnabled") && this._isDataStructurePlain() && !!this.option("dataSource"); }, _isDataStructurePlain: function _isDataStructurePlain() { return this.option("dataStructure") === "plain"; }, _fireContentReadyAction: function _fireContentReadyAction() { this.callBase(); if (this._scrollableContainer && windowUtils.hasWindow()) { this._scrollableContainer.update(); } }, _renderScrollableContainer: function _renderScrollableContainer() { this._scrollableContainer = this._createComponent($("<div>").appendTo(this.$element()), Scrollable, { direction: this.option("scrollDirection"), useKeyboard: false }); }, _renderNodeContainer: function _renderNodeContainer($parent) { var $container = $("<ul>").addClass(NODE_CONTAINER_CLASS); this.setAria("role", "group", $container); if ($parent && $parent.length) { var itemData = this._getItemData($parent.children("." + ITEM_CLASS)); if (this._expandedGetter(itemData)) { $container.addClass(OPENED_NODE_CONTAINER_CLASS); } $container.appendTo($parent); } return $container; }, _createDOMElement: function _createDOMElement($nodeContainer, node) { var $node = $("<li>").addClass(NODE_CLASS).attr(DATA_ITEM_ID, commonUtils.normalizeKey(node.internalFields.key)).prependTo($nodeContainer); this.setAria({ "role": "treeitem", "label": this._displayGetter(node.internalFields.item) || "", "expanded": node.internalFields.expanded || false, "level": this._getLevel($nodeContainer) }, $node); return $node; }, _getLevel: function _getLevel($nodeContainer) { var parent = $nodeContainer.parent(); return parent.hasClass("dx-scrollable-content") ? 1 : parseInt(parent.attr("aria-level")) + 1; }, _showCheckboxes: function _showCheckboxes() { return this.option("showCheckBoxesMode") !== "none"; }, _selectAllEnabled: function _selectAllEnabled() { return this.option("showCheckBoxesMode") === "selectAll"; }, _renderItems: function _renderItems($nodeContainer, nodes) { var length = nodes.length - 1; for (var i = length; i >= 0; i--) { this._renderItem(nodes[i], $nodeContainer); } this._renderFocusTarget(); }, _renderItem: function _renderItem(node, $nodeContainer) { var $node = this._createDOMElement($nodeContainer, node), nodeData = node.internalFields; this._showCheckboxes() && this._renderCheckBox($node, node); this.setAria("selected", nodeData.selected, $node); this._toggleSelectedClass($node, nodeData.selected); this.callBase(nodeData.key, nodeData.item, $node); if (nodeData.item.visible !== false) { this._renderChildren($node, node); } }, _renderChildren: function _renderChildren($node, node) { if (!this._hasChildren(node)) { this._addLeafClass($node); return; } this._renderToggleItemVisibilityIcon($node, node); if (!node.internalFields.expanded) { return; } var that = this; that._loadSublevel(node).done(function (childNodes) { that._renderSublevel($node, that._getActualNode(node), childNodes); }); }, _getActualNode: function _getActualNode(cachedNode) { return this._dataAdapter.getNodeByKey(cachedNode.internalFields.key); }, _hasChildren: function _hasChildren(node) { if (this._isVirtualMode() || this._useCustomChildrenLoader()) { return this._hasItemsGetter(node.internalFields.item) !== false; } return this.callBase(node); }, _loadSublevel: function _loadSublevel(node) { var deferred = new Deferred(), that = this, childrenNodes = that._getChildNodes(node); if (childrenNodes.length) { deferred.resolve(childrenNodes); } else { that._loadNestedItems(node).done(function (items) { deferred.resolve(that._dataAdapter.getNodesByItems(items)); }); } return deferred.promise(); }, _renderSublevel: function _renderSublevel($node, node, childNodes) { var $nestedNodeContainer = this._renderNodeContainer($node, node); this._renderItems($nestedNodeContainer, childNodes); if (childNodes.length && !node.internalFields.selected) { var firstChild = childNodes[0]; this._updateParentsState(firstChild, this._getNodeElement(firstChild)); } this._normalizeIconState($node, childNodes.length); $nestedNodeContainer.addClass(OPENED_NODE_CONTAINER_CLASS); }, _executeItemRenderAction: function _executeItemRenderAction(key, itemData, itemElement) { var node = this._dataAdapter.getNodeByKey(key); this._getItemRenderAction()({ itemElement: itemElement, itemIndex: key, itemData: itemData, node: node }); }, _addLeafClass: function _addLeafClass($node) { $node.addClass(IS_LEAF); }, _initExpandEvent: function _initExpandEvent() { var that = this, expandedEventName = this._getEventNameByOption(this.option("expandEvent")), $itemsContainer = this._itemContainer(), itemSelector = this._itemSelector(); eventsEngine.off($itemsContainer, "." + EXPAND_EVENT_NAMESPACE, itemSelector); eventsEngine.on($itemsContainer, expandedEventName, itemSelector, function (e) { var $nodeElement = $(e.currentTarget.parentNode); if (!$nodeElement.hasClass(IS_LEAF)) { that._toggleExpandedState(e.currentTarget, undefined, e); } }); }, _getEventNameByOption: function _getEventNameByOption(name) { var event = name === "click" ? clickEvent : dblclickEvent; return eventUtils.addNamespace(event.name, EXPAND_EVENT_NAMESPACE); }, _getNode: function _getNode(identifier) { if (!typeUtils.isDefined(identifier)) { return null; } if (identifier.internalFields) { return identifier; } if (typeUtils.isPrimitive(identifier)) { return this._dataAdapter.getNodeByKey(identifier); } var itemElement = $(identifier).get(0); if (!itemElement) { return null; } if (domAdapter.isElementNode(itemElement)) { return this._getNodeByElement(itemElement); } return this._dataAdapter.getNodeByItem(itemElement); }, _getNodeByElement: function _getNodeByElement(itemElement) { var $node = $(itemElement).closest("." + NODE_CLASS), key = commonUtils.denormalizeKey($node.attr(DATA_ITEM_ID)); return this._dataAdapter.getNodeByKey(key); }, _toggleExpandedState: function _toggleExpandedState(itemElement, state, e) { var node = this._getNode(itemElement), currentState = node.internalFields.expanded; if (node.internalFields.disabled || currentState === state) { return; } if (!typeUtils.isDefined(state)) { state = !currentState; } this._dataAdapter.toggleExpansion(node.internalFields.key, state); node.internalFields.expanded = state; if (this._hasChildren(node)) { var $node = this._getNodeElement(node); this._createLoadIndicator($node); } this._updateExpandedItemsUI(node, state, e); }, _createLoadIndicator: function _createLoadIndicator($node) { var $icon = $node.children("." + TOGGLE_ITEM_VISIBILITY_CLASS), $nodeContainer = $node.children("." + NODE_CONTAINER_CLASS); if ($icon.hasClass(TOGGLE_ITEM_VISIBILITY_OPENED_CLASS) || $nodeContainer.not(":empty").length) { return; } this._createComponent($("<div>").addClass(NODE_LOAD_INDICATOR_CLASS), LoadIndicator, {}).$element().appendTo($node); $icon.hide(); }, _renderToggleItemVisibilityIcon: function _renderToggleItemVisibilityIcon($node, node) { var $icon = $("<div>").addClass(TOGGLE_ITEM_VISIBILITY_CLASS).appendTo($node); if (node.internalFields.expanded) { $icon.addClass(TOGGLE_ITEM_VISIBILITY_OPENED_CLASS); $node.parent().addClass(OPENED_NODE_CONTAINER_CLASS); } if (node.internalFields.disabled) { $icon.addClass(DISABLED_STATE_CLASS); } this._renderToggleItemVisibilityIconClick($icon, node); }, _renderToggleItemVisibilityIconClick: function _renderToggleItemVisibilityIconClick($icon, node) { var that = this; var eventName = eventUtils.addNamespace(clickEvent.name, that.NAME); eventsEngine.off($icon, eventName); eventsEngine.on($icon, eventName, function (e) { that._toggleExpandedState(node, undefined, e); }); }, _updateExpandedItemsUI: function _updateExpandedItemsUI(node, state, e) { var $node = this._getNodeElement(node); if (!$node.length && this.option("expandNodesRecursive")) { this._updateExpandedItemsUI(this._getNode(node.internalFields.parentKey), state, e); } var $icon = $node.children("." + TOGGLE_ITEM_VISIBILITY_CLASS); var $nodeContainer = $node.children("." + NODE_CONTAINER_CLASS); $icon.toggleClass(TOGGLE_ITEM_VISIBILITY_OPENED_CLASS, state); var nodeContainerExists = $nodeContainer.length > 0; if (!state || nodeContainerExists && !$nodeContainer.is(":empty")) { this._updateExpandedItem(node, state, e); return; } if (this._isVirtualMode() || this._useCustomChildrenLoader()) { this._loadNestedItemsWithUpdate(node, state, e); return; } this._renderSublevel($node, node, this._getChildNodes(node)); this._fireContentReadyAction(); this._updateExpandedItem(node, state, e); }, _loadNestedItemsWithUpdate: function _loadNestedItemsWithUpdate(node, state, e) { var that = this, $node = this._getNodeElement(node); that._loadNestedItems(node).done(function (items) { var actualNodeData = that._getActualNode(node); that._renderSublevel($node, actualNodeData, that._dataAdapter.getNodesByItems(items)); if (!items || !items.length) { return; } that._fireContentReadyAction(); that._updateExpandedItem(actualNodeData, state, e); }); }, _loadNestedItems: function _loadNestedItems(node) { var that = this; if (that._useCustomChildrenLoader()) { var publicNode = this._dataAdapter.getPublicNode(node); return that._loadChildrenByCustomLoader(publicNode).done(function (newItems) { that._appendItems(newItems); }); } if (!that._isVirtualMode()) { return new Deferred().resolve([]).promise(); } that._filter.internal = [that.option("parentIdExpr"), node.internalFields.key]; that._dataSource.filter(that._combineFilter()); return that._dataSource.load().done(function (newItems) { if (!that._areNodesExists(newItems)) { that._appendItems(newItems); } }); }, _areNodesExists: function _areNodesExists(newItems, items) { var keyOfRootItem = this.keyOf(newItems[0]), fullData = this._dataAdapter.getFullData(); return !!this._dataAdapter.getNodeByKey(keyOfRootItem, fullData); }, _appendItems: function _appendItems(newItems) { this.option().items = this.option("items").concat(newItems); this._initDataAdapter(); }, _updateExpandedItem: function _updateExpandedItem(node, state, e) { this._animateNodeContainer(node, state, e); }, _animateNodeContainer: function _animateNodeContainer(node, state, e) { var $node = this._getNodeElement(node), $nodeContainer = $node.children("." + NODE_CONTAINER_CLASS), nodeHeight; // NOTE: The height of node container is should be used when the container is shown (T606878) $nodeContainer.addClass(OPENED_NODE_CONTAINER_CLASS); nodeHeight = $nodeContainer.height(); fx.stop($nodeContainer, true); fx.animate($nodeContainer, { type: "custom", duration: this.option("animationEnabled") ? 400 : 0, from: { "maxHeight": state ? 0 : nodeHeight }, to: { "maxHeight": state ? nodeHeight : 0 }, complete: function () { $nodeContainer.css("maxHeight", "none"); $nodeContainer.toggleClass(OPENED_NODE_CONTAINER_CLASS, state); this.setAria("expanded", state, $node); this._scrollableContainer.update(); this._fireExpandedStateUpdatedEvent(state, node, e); }.bind(this) }); }, _fireExpandedStateUpdatedEvent: function _fireExpandedStateUpdatedEvent(isExpanded, node, e) { var optionName = isExpanded ? "onItemExpanded" : "onItemCollapsed", target; if (!this._hasChildren(node)) { return; } if (typeUtils.isDefined(e)) { this._itemDXEventHandler(e, optionName, { node: this._dataAdapter.getPublicNode(node) }); } else { target = this._getNodeElement(node); this._itemEventHandler(target, optionName, { event: e, node: this._dataAdapter.getPublicNode(node) }); } }, _normalizeIconState: function _normalizeIconState($node, hasNewItems) { var $loadIndicator = $node.find(".dx-loadindicator"), $icon; $loadIndicator.length && LoadIndicator.getInstance($loadIndicator).option("visible", false); if (hasNewItems) { $icon = $node.find("." + TOGGLE_ITEM_VISIBILITY_CLASS); $icon.show(); return; } $node.find("." + TOGGLE_ITEM_VISIBILITY_CLASS).removeClass(TOGGLE_ITEM_VISIBILITY_CLASS); $node.addClass(IS_LEAF); }, _emptyMessageContainer: function _emptyMessageContainer() { return this._scrollableContainer ? this._scrollableContainer.content() : this.callBase(); }, _renderContent: function _renderContent() { var items = this.option("items"); if (items && items.length) { this._contentAlreadyRendered = true; } this.callBase(); }, _renderSelectAllItem: function _renderSelectAllItem($container) { $container = $container || this.$element().find("." + NODE_CONTAINER_CLASS).first(); this._$selectAllItem = $("<div>").addClass(SELECT_ALL_ITEM_CLASS); var value = this._dataAdapter.isAllSelected(); this._createComponent(this._$selectAllItem, CheckBox, { value: value, text: this.option("selectAllText"), onValueChanged: this._toggleSelectAll.bind(this) }); this._toggleSelectedClass(this._$selectAllItem, value); $container.before(this._$selectAllItem); }, _toggleSelectAll: function _toggleSelectAll(args) { this._dataAdapter.toggleSelectAll(args.value); this._updateItemsUI(); this._fireSelectionChanged(); }, _renderCheckBox: function _renderCheckBox($node, node) { $node.addClass(ITEM_WITH_CHECKBOX_CLASS); var $checkbox = $("<div>").appendTo($node); this._createComponent($checkbox, CheckBox, { value: node.internalFields.selected, onValueChanged: this._changeCheckboxValue.bind(this), focusStateEnabled: false, disabled: this._disabledGetter(node) }); }, _toggleSelectedClass: function _toggleSelectedClass($node, value) { $node.toggleClass(SELECTED_ITEM_CLASS, !!value); }, _toggleNodeDisabledState: function _toggleNodeDisabledState(node, state) { var $node = this._getNodeElement(node), $item = $node.find("." + ITEM_CLASS).eq(0); this._dataAdapter.toggleNodeDisabledState(node.internalFields.key, state); $item.toggleClass(DISABLED_STATE_CLASS, !!state); if (this._showCheckboxes()) { var checkbox = this._getCheckBoxInstance($node); checkbox.option("disabled", !!state); } }, _itemOptionChanged: function _itemOptionChanged(item, property, value) { var node = this._dataAdapter.getNodeByItem(item); switch (property) { case this.option("disabledExpr"): this._toggleNodeDisabledState(node, value); break; } }, _changeCheckboxValue: function _changeCheckboxValue(e) { var $node = $(e.element).parent("." + NODE_CLASS), $item = $node.children("." + ITEM_CLASS), item = this._getItemData($item), node = this._getNodeByElement($item), value = e.value; if (node && node.internalFields.selected === value) { return; } this._updateItemSelection(value, item, e.event); }, _isSingleSelection: function _isSingleSelection() { return this.option("selectionMode") === "single"; }, _isRecursiveSelection: function _isRecursiveSelection() { return this.option("selectNodesRecursive") && this.option("selectionMode") !== "single"; }, _updateItemSelection: function _updateItemSelection(value, itemElement, dxEvent) { var that = this, node = that._getNode(itemElement); if (!node || node.internalFields.selected === value) { return; } if (that._isSingleSelection() && value) { var selectedNodesKeys = that.getSelectedNodesKeys(); each(selectedNodesKeys, function (index, nodeKey) { that.unselectItem(nodeKey); }); } that._dataAdapter.toggleSelection(node.internalFields.key, value); that._updateItemsUI(); var initiator = dxEvent || that._findItemElementByItem(node.internalFields.item), handler = dxEvent ? that._itemDXEventHandler : that._itemEventHandler; handler.call(that, initiator, "onItemSelectionChanged", { node: that._dataAdapter.getPublicNode(node), itemData: node.internalFields.item }); that._fireSelectionChanged(); }, _getCheckBoxInstance: function _getCheckBoxInstance($node) { return $node.children(".dx-checkbox").dxCheckBox("instance"); }, _updateItemsUI: function _updateItemsUI() { var that = this, cache = {}; each(this._dataAdapter.getData(), function (_, node) { var $node = that._getNodeElement(node, cache), nodeSelection = node.internalFields.selected; if (!$node.length) { return; } that._toggleSelectedClass($node, nodeSelection); that.setAria("selected", nodeSelection, $node); if (that._showCheckboxes()) { var checkbox = that._getCheckBoxInstance($node); checkbox.option("value", nodeSelection); } }); if (this._selectAllEnabled()) { this._$selectAllItem.dxCheckBox("instance").option("value", this._dataAdapter.isAllSelected()); } }, _updateParentsState: function _updateParentsState(node, $node) { var parentNode = this._dataAdapter.getNodeByKey(node.internalFields.parentKey); if (!$node) { return; } var $parentNode = $($node.parents("." + NODE_CLASS)[0]); if (this._showCheckboxes()) { var parentValue = parentNode.internalFields.selected; this._getCheckBoxInstance($parentNode).option("value", parentValue); this._toggleSelectedClass($parentNode, parentValue); } if (parentNode.internalFields.parentKey !== this.option("rootValue")) { this._updateParentsState(parentNode, $parentNode); } }, _itemEventHandlerImpl: function _itemEventHandlerImpl(initiator, action, actionArgs) { var $itemElement = $(initiator).closest("." + NODE_CLASS).children("." + ITEM_CLASS); return action(extend(this._extendActionArgs($itemElement), actionArgs)); }, _itemContextMenuHandler: function _itemContextMenuHandler(e) { this._createEventHandler("onItemContextMenu", e); }, _itemHoldHandler: function _itemHoldHandler(e) { this._createEventHandler("onItemHold", e); }, _createEventHandler: function _createEventHandler(eventName, e) { var node = this._getNodeByElement(e.currentTarget); this._itemDXEventHandler(e, eventName, { node: this._dataAdapter.getPublicNode(node) }); }, _itemClass: function _itemClass() { return ITEM_CLASS; }, _itemDataKey: function _itemDataKey() { return ITEM_DATA_KEY; }, _attachClickEvent: function _attachClickEvent() { var that = this; var clickSelector = "." + that._itemClass(); var pointerDownSelector = "." + NODE_CLASS + ", ." + SELECT_ALL_ITEM_CLASS; var eventName = eventUtils.addNamespace(clickEvent.name, that.NAME); var pointerDownEvent = eventUtils.addNamespace(pointerEvents.down, that.NAME); var $itemContainer = that._itemContainer(); eventsEngine.off($itemContainer, eventName, clickSelector); eventsEngine.off($itemContainer, pointerDownEvent, pointerDownSelector); eventsEngine.on($itemContainer, eventName, clickSelector, function (e) { that._itemClickHandler(e, $(this)); }); eventsEngine.on($itemContainer, pointerDownEvent, pointerDownSelector, function (e) { that._itemPointerDownHandler(e); }); }, _itemClickHandler: function _itemClickHandler(e, $item) { var itemData = this._getItemData($item), node = this._getNodeByElement($item); this._itemDXEventHandler(e, "onItemClick", { node: this._dataAdapter.getPublicNode(node) }); if (this.option("selectByClick")) { this._updateItemSelection(!node.internalFields.selected, itemData, e); } }, _updateSelectionToFirstItem: function _updateSelectionToFirstItem($items, startIndex) { var itemIndex = startIndex; while (itemIndex >= 0) { var $item = $($items[itemIndex]); this._updateItemSelection(true, $item.find("." + ITEM_CLASS).get(0)); itemIndex--; } }, _updateSelectionToLastItem: function _updateSelectionToLastItem($items, startIndex) { var itemIndex = startIndex, length = $items.length; while (itemIndex < length) { var $item = $($items[itemIndex]); this._updateItemSelection(true, $item.find("." + ITEM_CLASS).get(0)); itemIndex++; } }, _focusInHandler: function _focusInHandler(e) { var that = this; that._updateFocusState(e, true); if (that.option("focusedElement")) { clearTimeout(that._setFocusedItemTimeout); that._setFocusedItemTimeout = setTimeout(function () { that._setFocusedItem($(that.option("focusedElement"))); }); return; } var $activeItem = that._getActiveItem(); that.option("focusedElement", getPublicElement($activeItem.closest("." + NODE_CLASS))); },