devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,367 lines (1,090 loc) • 43.7 kB
JavaScript
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var $ = require("../core/renderer"),
eventsEngine = require("../events/core/events_engine"),
dataUtils = require("../core/element_data"),
getPublicElement = require("../core/utils/dom").getPublicElement,
devices = require("../core/devices"),
commonUtils = require("../core/utils/common"),
noop = commonUtils.noop,
isDefined = require("../core/utils/type").isDefined,
arrayUtils = require("../core/utils/array"),
typeUtils = require("../core/utils/type"),
windowUtils = require("../core/utils/window"),
iteratorUtils = require("../core/utils/iterator"),
extend = require("../core/utils/extend").extend,
messageLocalization = require("../localization/message"),
registerComponent = require("../core/component_registrator"),
eventUtils = require("../events/utils"),
SelectBox = require("./select_box"),
clickEvent = require("../events/click"),
caret = require("./text_box/utils.caret"),
browser = require("../core/utils/browser"),
FilterCreator = require("../core/utils/selection_filter").SelectionFilterCreator,
deferredUtils = require("../core/utils/deferred"),
compileSetter = require("../core/utils/data").compileSetter,
when = deferredUtils.when,
Deferred = deferredUtils.Deferred,
BindableTemplate = require("./widget/bindable_template"),
inArray = require("../core/utils/array").inArray,
each = require("../core/utils/iterator").each;
var TAGBOX_TAG_DATA_KEY = "dxTagData";
var TAGBOX_CLASS = "dx-tagbox",
TAGBOX_TAG_CONTAINER_CLASS = "dx-tag-container",
TAGBOX_TAG_CLASS = "dx-tag",
TAGBOX_MULTI_TAG_CLASS = "dx-tagbox-multi-tag",
TAGBOX_CUSTOM_TAG_CLASS = "dx-tag-custom",
TAGBOX_TAG_REMOVE_BUTTON_CLASS = "dx-tag-remove-button",
TAGBOX_ONLY_SELECT_CLASS = "dx-tagbox-only-select",
TAGBOX_SINGLE_LINE_CLASS = "dx-tagbox-single-line",
TAGBOX_POPUP_WRAPPER_CLASS = "dx-tagbox-popup-wrapper",
LIST_SELECT_ALL_CHECKBOX_CLASS = "dx-list-select-all-checkbox",
TAGBOX_TAG_CONTENT_CLASS = "dx-tag-content",
TAGBOX_DEFAULT_FIELD_TEMPLATE_CLASS = "dx-tagbox-default-template",
TAGBOX_CUSTOM_FIELD_TEMPLATE_CLASS = "dx-tagbox-custom-template",
NATIVE_CLICK_CLASS = "dx-native-click",
TEXTEDITOR_CONTAINER_CLASS = "dx-texteditor-container";
var TAGBOX_MOUSE_WHEEL_DELTA_MULTIPLIER = -0.3;
/**
* @name dxTagBox
* @isEditor
* @publicName dxTagBox
* @inherits dxSelectBox
* @module ui/tag_box
* @export default
*/
var TagBox = SelectBox.inherit({
_supportedKeys: function _supportedKeys() {
var parent = this.callBase();
return extend(parent, {
backspace: function backspace(e) {
if (!this._isCaretAtTheStart()) {
return;
}
e.preventDefault();
e.stopPropagation();
this._isTagRemoved = true;
var $tagToDelete = this._$focusedTag || this._tagElements().last();
if (this._$focusedTag) {
this._moveTagFocus("prev", true);
}
if ($tagToDelete.length === 0) {
return;
}
this._preserveFocusedTag = true;
this._removeTagElement($tagToDelete);
delete this._preserveFocusedTag;
},
del: function del(e) {
if (!this._$focusedTag || !this._isCaretAtTheStart()) {
return;
}
e.preventDefault();
e.stopPropagation();
this._isTagRemoved = true;
var $tagToDelete = this._$focusedTag;
this._moveTagFocus("next", true);
this._preserveFocusedTag = true;
this._removeTagElement($tagToDelete);
delete this._preserveFocusedTag;
},
enter: function enter(e) {
var isListItemFocused = this._list && this._list.option("focusedElement") !== null,
isCustomItem = this.option("acceptCustomValue") && !isListItemFocused;
if (isCustomItem) {
e.preventDefault();
this._searchValue() !== "" && this._customItemAddedHandler();
return;
}
if (!this.option("opened")) {
return;
}
e.preventDefault();
this._keyboardProcessor._childProcessors[0].process(e);
},
leftArrow: function leftArrow(e) {
if (!this._isCaretAtTheStart()) {
return;
}
var rtlEnabled = this.option("rtlEnabled");
if (this._isEditable() && rtlEnabled && !this._$focusedTag) {
return;
}
e.preventDefault();
var direction = rtlEnabled ? "next" : "prev";
this._moveTagFocus(direction);
!this.option("multiline") && this._scrollContainer(direction);
},
rightArrow: function rightArrow(e) {
if (!this._isCaretAtTheStart()) {
return;
}
var rtlEnabled = this.option("rtlEnabled");
if (this._isEditable() && !rtlEnabled && !this._$focusedTag) {
return;
}
e.preventDefault();
var direction = rtlEnabled ? "prev" : "next";
this._moveTagFocus(direction);
!this.option("multiline") && this._scrollContainer(direction);
}
});
},
_isCaretAtTheStart: function _isCaretAtTheStart() {
var position = caret(this._input());
return position.start === 0 && position.end === 0;
},
_moveTagFocus: function _moveTagFocus(direction, clearOnBoundary) {
if (!this._$focusedTag) {
var tagElements = this._tagElements();
this._$focusedTag = direction === "next" ? tagElements.first() : tagElements.last();
this._toggleFocusClass(true, this._$focusedTag);
return;
}
var $nextFocusedTag = this._$focusedTag[direction]("." + TAGBOX_TAG_CLASS);
if ($nextFocusedTag.length > 0) {
this._replaceFocusedTag($nextFocusedTag);
} else if (clearOnBoundary || direction === "next" && this._isEditable()) {
this._clearTagFocus();
}
},
_replaceFocusedTag: function _replaceFocusedTag($nextFocusedTag) {
this._toggleFocusClass(false, this._$focusedTag);
this._$focusedTag = $nextFocusedTag;
this._toggleFocusClass(true, this._$focusedTag);
},
_clearTagFocus: function _clearTagFocus() {
if (!this._$focusedTag) {
return;
}
this._toggleFocusClass(false, this._$focusedTag);
delete this._$focusedTag;
},
_focusClassTarget: function _focusClassTarget($element) {
if ($element && $element.length && $element[0] !== this._focusTarget()[0]) {
return $element;
}
return this.callBase();
},
_scrollContainer: function _scrollContainer(direction) {
if (this.option("multiline") || !windowUtils.hasWindow()) {
return;
}
if (!this._$tagsContainer) {
return;
}
var scrollPosition = this._getScrollPosition(direction);
this._$tagsContainer.scrollLeft(scrollPosition);
},
_getScrollPosition: function _getScrollPosition(direction) {
if (direction === "start" || direction === "end") {
return this._getBorderPosition(direction);
}
return this._$focusedTag ? this._getFocusedTagPosition(direction) : this._getBorderPosition("end");
},
_getBorderPosition: function _getBorderPosition(direction) {
var rtlEnabled = this.option("rtlEnabled"),
isScrollLeft = direction === "end" ^ rtlEnabled,
isScrollReverted = rtlEnabled && !browser.webkit,
scrollSign = !rtlEnabled || browser.webkit || browser.msie ? 1 : -1;
return isScrollLeft ^ !isScrollReverted ? 0 : scrollSign * (this._$tagsContainer.get(0).scrollWidth - this._$tagsContainer.outerWidth());
},
_getFocusedTagPosition: function _getFocusedTagPosition(direction) {
var rtlEnabled = this.option("rtlEnabled"),
isScrollLeft = direction === "next" ^ rtlEnabled,
scrollOffset = this._$focusedTag.position().left,
scrollLeft = this._$tagsContainer.scrollLeft();
if (isScrollLeft) {
scrollOffset += this._$focusedTag.outerWidth(true) - this._$tagsContainer.outerWidth();
}
if (isScrollLeft ^ scrollOffset < 0) {
var scrollCorrection = rtlEnabled && browser.msie ? -1 : 1;
scrollLeft += scrollOffset * scrollCorrection;
}
return scrollLeft;
},
_setNextValue: noop,
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxTagBoxOptions.value
* @publicName value
* @type Array<string,number,Object>
*/
value: [],
showDropDownButton: false,
maxFilterLength: 1500,
/**
* @name dxTagBoxOptions.tagTemplate
* @publicName tagTemplate
* @type template|function
* @default "tag"
* @type_function_param1 itemData:object
* @type_function_param2 itemElement:dxElement
* @type_function_return string|Node|jQuery
*/
tagTemplate: "tag",
selectAllText: messageLocalization.format("dxList-selectAll"),
/**
* @name dxTagBoxOptions.hideSelectedItems
* @publicName hideSelectedItems
* @type boolean
* @default false
*/
hideSelectedItems: false,
/**
* @name dxTagBoxOptions.selectedItems
* @publicName selectedItems
* @type Array<string,number,Object>
* @readonly
*/
selectedItems: [],
/**
* @name dxTagBoxOptions.selectAllMode
* @publicName selectAllMode
* @type Enums.SelectAllMode
* @default 'page'
*/
selectAllMode: 'page',
/**
* @name dxTagBoxOptions.onSelectAllValueChanged
* @publicName onSelectAllValueChanged
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 value:boolean
* @action
*/
onSelectAllValueChanged: null,
/**
* @name dxTagBoxOptions.maxDisplayedTags
* @publicName maxDisplayedTags
* @type number
* @default undefined
*/
maxDisplayedTags: undefined,
/**
* @name dxTagBoxOptions.showMultiTagOnly
* @publicName showMultiTagOnly
* @type boolean
* @default true
*/
showMultiTagOnly: true,
/**
* @name dxTagBoxOptions.onMultiTagPreparing
* @publicName onMultiTagPreparing
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 multiTagElement:dxElement
* @type_function_param1_field5 selectedItems:Array<string,number,Object>
* @type_function_param1_field6 text:string
* @type_function_param1_field7 cancel:boolean
* @action
*/
onMultiTagPreparing: null,
/**
* @name dxTagBoxOptions.multiline
* @publicName multiline
* @type boolean
* @default true
*/
multiline: true,
/**
* @name dxTagBoxOptions.useSubmitBehavior
* @publicName useSubmitBehavior
* @type boolean
* @default true
* @hidden
*/
useSubmitBehavior: true
/**
* @name dxTagBoxOptions.applyValueMode
* @publicName applyValueMode
* @type Enums.EditorApplyValueMode
* @default "instantly"
*/
/**
* @name dxTagBoxOptions.onSelectionChanged
* @publicName onSelectionChanged
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 addedItems:Array<string,number,Object>
* @type_function_param1_field5 removedItems:Array<string,number,Object>
* @action
*/
/**
* @name dxTagBoxOptions.closeAction
* @publicName closeAction
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.hiddenAction
* @publicName hiddenAction
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.itemRender
* @publicName itemRender
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.openAction
* @publicName openAction
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.shownAction
* @publicName shownAction
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.valueChangeEvent
* @publicName valueChangeEvent
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.maxLength
* @publicName maxLength
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.onCopy
* @publicName onCopy
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.onCut
* @publicName onCut
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.onPaste
* @publicName onPaste
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.spellcheck
* @publicName spellcheck
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.displayValue
* @publicName displayValue
* @hidden
* @inheritdoc
*/
/**
* @name dxTagBoxOptions.selectedItem
* @publicName selectedItem
* @hidden
* @inheritdoc
*/
});
},
_init: function _init() {
this.callBase();
this._selectedItems = [];
this._initSelectAllValueChangedAction();
},
_initActions: function _initActions() {
this.callBase();
this._initMultiTagPreparingAction();
},
_initMultiTagPreparingAction: function _initMultiTagPreparingAction() {
this._multiTagPreparingAction = this._createActionByOption("onMultiTagPreparing", {
beforeExecute: function (e) {
this._multiTagPreparingHandler(e.args[0]);
}.bind(this)
});
},
_compileDisplaySetter: function _compileDisplaySetter() {
var displayGetterExpr = this._displayGetterExpr();
this._displaySetter = displayGetterExpr && compileSetter(displayGetterExpr);
},
_initDataExpressions: function _initDataExpressions() {
this.callBase();
this._compileDisplaySetter();
},
_multiTagPreparingHandler: function _multiTagPreparingHandler(args) {
var selectedCount = this._getValue().length;
if (!this.option("showMultiTagOnly")) {
args.text = messageLocalization.getFormatter("dxTagBox-moreSelected")(selectedCount - this.option("maxDisplayedTags") + 1);
} else {
args.text = messageLocalization.getFormatter("dxTagBox-selected")(selectedCount);
}
},
_initTemplates: function _initTemplates() {
this.callBase();
this._defaultTemplates["tag"] = new BindableTemplate(function ($container, data) {
if (this._displayGetterExpr()) {
data = this._displayGetter(data);
}
var $tagContent = $("<div>").addClass(TAGBOX_TAG_CONTENT_CLASS);
$("<span>").text(data).appendTo($tagContent);
$("<div>").addClass(TAGBOX_TAG_REMOVE_BUTTON_CLASS).appendTo($tagContent);
$container.append($tagContent);
}.bind(this), [this._displayGetterExpr()], this.option("integrationOptions.watchMethod"));
},
_toggleSubmitElement: function _toggleSubmitElement(enabled) {
if (enabled) {
this._renderSubmitElement();
this._setSubmitValue();
} else {
this._$submitElement && this._$submitElement.remove();
delete this._$submitElement;
}
},
_renderSubmitElement: function _renderSubmitElement() {
if (!this.option("useSubmitBehavior")) {
return;
}
this._$submitElement = $("<select>").attr("multiple", "multiple").css("display", "none").appendTo(this.$element());
},
_setSubmitValue: function _setSubmitValue() {
if (!this.option("useSubmitBehavior")) {
return;
}
var value = this._getValue(),
useDisplayText = this.option("valueExpr") === "this",
$options = [];
for (var i = 0, n = value.length; i < n; i++) {
$options.push($("<option>").val(useDisplayText ? this._displayGetter(value[i]) : value[i]).attr("selected", "selected"));
}
this._$submitElement.empty();
this._$submitElement.append($options);
},
_initMarkup: function _initMarkup() {
this._tagElementsCache = $();
var isSingleLineMode = !this.option("multiline");
this.$element().addClass(TAGBOX_CLASS).toggleClass(TAGBOX_ONLY_SELECT_CLASS, !(this.option("searchEnabled") || this.option("acceptCustomValue"))).toggleClass(TAGBOX_SINGLE_LINE_CLASS, isSingleLineMode);
// TODO: texteditor render methods order research
this._initTagTemplate();
this.callBase();
},
_render: function _render() {
this.callBase();
this._renderTagRemoveAction();
this._renderSingleLineScroll();
this._scrollContainer("start");
},
_initTagTemplate: function _initTagTemplate() {
this._tagTemplate = this._getTemplateByOption("tagTemplate");
},
_renderField: function _renderField() {
var isDefaultFieldTemplate = !isDefined(this.option("fieldTemplate"));
this.$element().toggleClass(TAGBOX_DEFAULT_FIELD_TEMPLATE_CLASS, isDefaultFieldTemplate).toggleClass(TAGBOX_CUSTOM_FIELD_TEMPLATE_CLASS, !isDefaultFieldTemplate);
this.callBase();
},
_renderTagRemoveAction: function _renderTagRemoveAction() {
var tagRemoveAction = this._createAction(this._removeTagHandler.bind(this));
var eventName = eventUtils.addNamespace(clickEvent.name, "dxTagBoxTagRemove");
eventsEngine.off(this._$tagsContainer, eventName);
eventsEngine.on(this._$tagsContainer, eventName, "." + TAGBOX_TAG_REMOVE_BUTTON_CLASS, function (e) {
tagRemoveAction({ event: e });
});
this._renderTypingEvent();
},
_renderSingleLineScroll: function _renderSingleLineScroll() {
var mouseWheelEvent = eventUtils.addNamespace("dxmousewheel", this.NAME),
$element = this.$element(),
isMultiline = this.option("multiline");
eventsEngine.off($element, mouseWheelEvent);
if (devices.real().deviceType !== "desktop") {
this._$tagsContainer && this._$tagsContainer.css("overflowX", isMultiline ? "" : "auto");
return;
}
if (isMultiline) {
return;
}
eventsEngine.on($element, mouseWheelEvent, this._tagContainerMouseWheelHandler.bind(this));
},
_tagContainerMouseWheelHandler: function _tagContainerMouseWheelHandler(e) {
var scrollLeft = this._$tagsContainer.scrollLeft();
this._$tagsContainer.scrollLeft(scrollLeft + e.delta * TAGBOX_MOUSE_WHEEL_DELTA_MULTIPLIER);
return false;
},
_renderTypingEvent: function _renderTypingEvent() {
eventsEngine.on(this._input(), eventUtils.addNamespace("keydown", this.NAME), function (e) {
if (!this._isControlKey(e.key) && this._isEditable()) {
this._clearTagFocus();
}
}.bind(this));
},
_popupWrapperClass: function _popupWrapperClass() {
return this.callBase() + " " + TAGBOX_POPUP_WRAPPER_CLASS;
},
_renderInput: function _renderInput() {
this.callBase();
this._renderPreventBlur(this._inputWrapper());
},
_renderInputValueImpl: function _renderInputValueImpl() {
this._renderMultiSelect();
},
_loadInputValue: function _loadInputValue() {
return when();
},
_clearTextValue: function _clearTextValue() {
this._input().val("");
},
_focusInHandler: function _focusInHandler(e) {
this.callBase(e);
this._scrollContainer("end");
},
_restoreInputText: function _restoreInputText() {
this._clearTextValue();
},
_focusOutHandler: function _focusOutHandler(e) {
this.callBase(e);
this._clearTagFocus();
this._scrollContainer("start");
},
_getFirstPopupElement: function _getFirstPopupElement() {
return this.option("showSelectionControls") ? this._popup._wrapper().find("." + LIST_SELECT_ALL_CHECKBOX_CLASS) : this.callBase();
},
_initSelectAllValueChangedAction: function _initSelectAllValueChangedAction() {
this._selectAllValueChangeAction = this._createActionByOption("onSelectAllValueChanged");
},
_renderList: function _renderList() {
this.callBase();
this._setListDataSourceFilter();
if (!this.option("showSelectionControls")) {
return;
}
var $selectAllCheckBox = this._list.$element().find("." + LIST_SELECT_ALL_CHECKBOX_CLASS),
selectAllCheckbox = $selectAllCheckBox.dxCheckBox("instance");
selectAllCheckbox.registerKeyHandler("tab", this._popupElementTabHandler.bind(this));
selectAllCheckbox.registerKeyHandler("escape", this._popupElementEscHandler.bind(this));
},
_listConfig: function _listConfig() {
var that = this,
selectionMode = this.option("showSelectionControls") ? "all" : "multiple";
return extend(this.callBase(), {
selectionMode: selectionMode,
selectAllText: this.option("selectAllText"),
onSelectAllValueChanged: function onSelectAllValueChanged(e) {
that._selectAllValueChangeAction({
value: e.value
});
},
selectAllMode: this.option("selectAllMode"),
selectedItems: this._selectedItems,
onFocusedItemChanged: null
});
},
_renderMultiSelect: function _renderMultiSelect() {
this._$tagsContainer = this.$element().find("." + TEXTEDITOR_CONTAINER_CLASS).addClass(TAGBOX_TAG_CONTAINER_CLASS).addClass(NATIVE_CLICK_CLASS);
this._renderInputSize();
this._renderTags();
this._popup && this._popup.refreshPosition();
},
_listItemClickHandler: function _listItemClickHandler(e) {
!this.option("showSelectionControls") && this._clearTextValue();
if (this.option("applyValueMode") === "useButtons") {
return;
}
this.callBase(e);
},
_renderInputSize: function _renderInputSize() {
var $input = this._input();
$input.prop("size", $input.val() ? $input.val().length + 2 : 1);
},
_renderInputSubstitution: function _renderInputSubstitution() {
this.callBase();
this._renderInputSize();
},
_getValue: function _getValue() {
return this.option("value") || [];
},
_multiTagRequired: function _multiTagRequired() {
var values = this._getValue(),
maxDisplayedTags = this.option("maxDisplayedTags");
return isDefined(maxDisplayedTags) && values.length > maxDisplayedTags;
},
_renderMultiTag: function _renderMultiTag($input) {
var item,
$tag = $("<div>").addClass(TAGBOX_TAG_CLASS).addClass(TAGBOX_MULTI_TAG_CLASS);
var args = {
multiTagElement: getPublicElement($tag),
selectedItems: this.option("selectedItems")
};
this._multiTagPreparingAction(args);
if (args.cancel) {
return false;
}
$tag.data(TAGBOX_TAG_DATA_KEY, args.text);
$tag.insertBefore($input);
if (this._displaySetter) {
item = {};
this._displaySetter(item, args.text);
}
this._tagTemplate.render({
model: item || args.text,
container: getPublicElement($tag)
});
return $tag;
},
_getFilteredItems: function _getFilteredItems(values) {
var creator = new FilterCreator(values);
var selectedItems = this._list && this._list.option("selectedItems") || this.option("selectedItems"),
clientFilterFunction = creator.getLocalFilter(this._valueGetter),
filteredItems = selectedItems.filter(clientFilterFunction),
selectedItemsAlreadyLoaded = filteredItems.length === values.length,
d = new Deferred();
if (selectedItemsAlreadyLoaded) {
return d.resolve(filteredItems).promise();
} else {
var dataSourceFilter = this._dataSource.filter(),
filterExpr = creator.getCombinedFilter(this.option("valueExpr"), dataSourceFilter),
filterLength = encodeURI(JSON.stringify(filterExpr)).length,
resultFilter = filterLength > this.option("maxFilterLength") ? undefined : filterExpr;
this._dataSource.store().load({ filter: resultFilter }).done(function (items) {
d.resolve(items.filter(clientFilterFunction));
});
return d.promise();
}
},
_createTagData: function _createTagData(values, filteredItems) {
var items = [];
iteratorUtils.each(values, function (valueIndex, value) {
var item = filteredItems[valueIndex];
if (isDefined(item)) {
this._selectedItems.push(item);
items.splice(valueIndex, 0, item);
} else {
var selectedItem = this.option("selectedItem"),
customItem = this._valueGetter(selectedItem) === value ? selectedItem : value;
items.splice(valueIndex, 0, customItem);
}
}.bind(this));
return items;
},
_loadTagData: function _loadTagData() {
var values = this._getValue(),
tagData = new Deferred();
this._selectedItems = [];
this._getFilteredItems(values).done(function (filteredItems) {
var items = this._createTagData(values, filteredItems);
tagData.resolve(items);
}.bind(this)).fail(tagData.reject.bind(this));
return tagData.promise();
},
_renderTags: function _renderTags() {
this._loadTagData().always(function (items) {
this._renderTagsCore(items);
}.bind(this));
this._renderEmptyState();
if (!this._preserveFocusedTag) {
this._clearTagFocus();
}
},
_renderTagsCore: function _renderTagsCore(items) {
this._renderInputAddons();
this.option("selectedItems", this._selectedItems.slice());
this._cleanTags();
var $multiTag = this._multiTagRequired() && this._renderMultiTag(this._input()),
showMultiTagOnly = this.option("showMultiTagOnly"),
maxDisplayedTags = this.option("maxDisplayedTags");
items.forEach(function (item, index) {
if ($multiTag && showMultiTagOnly || $multiTag && !showMultiTagOnly && index - maxDisplayedTags >= -1) {
return false;
}
this._renderTag(item, $multiTag || this._input());
}.bind(this));
this._scrollContainer("end");
this._refreshTagElements();
},
_cleanTags: function _cleanTags() {
if (this._multiTagRequired()) {
this._tagElements().remove();
} else {
var $tags = this._tagElements(),
values = this._getValue();
each($tags, function (_, tag) {
var $tag = $(tag),
index = inArray($tag.data(TAGBOX_TAG_DATA_KEY), values);
if (index < 0) {
$tag.remove();
}
});
}
},
_renderEmptyState: function _renderEmptyState() {
var isEmpty = !(this._getValue().length || this._selectedItems.length || this._searchValue());
this._toggleEmptiness(isEmpty);
this._renderDisplayText();
},
_renderDisplayText: function _renderDisplayText() {
this._renderInputSize();
},
_refreshTagElements: function _refreshTagElements() {
this._tagElementsCache = this.$element().find("." + TAGBOX_TAG_CLASS);
},
_tagElements: function _tagElements() {
return this._tagElementsCache;
},
_applyTagTemplate: function _applyTagTemplate(item, $tag) {
this._tagTemplate.render({
model: item,
container: getPublicElement($tag)
});
},
_renderTag: function _renderTag(item, $input) {
var value = this._valueGetter(item);
if (!isDefined(value)) {
return;
}
var $tag = this._getTag(value);
if ($tag) {
var displayValue = this._displayGetter(item);
if (isDefined(displayValue)) {
$tag.empty();
this._applyTagTemplate(item, $tag);
}
$tag.removeClass(TAGBOX_CUSTOM_TAG_CLASS);
} else {
$tag = this._createTag(value, $input);
if (isDefined(item)) {
this._applyTagTemplate(item, $tag);
} else {
$tag.addClass(TAGBOX_CUSTOM_TAG_CLASS);
this._applyTagTemplate(value, $tag);
}
}
},
_getTag: function _getTag(value) {
var $tags = this._tagElements(),
tagsLength = $tags.length;
var result = false;
for (var i = 0; i < tagsLength; i++) {
var $tag = $tags[i],
tagData = dataUtils.data($tag, TAGBOX_TAG_DATA_KEY);
if (value === tagData || commonUtils.equalByValue(value, tagData)) {
result = $($tag);
break;
}
}
return result;
},
_createTag: function _createTag(value, $input) {
return $("<div>").addClass(TAGBOX_TAG_CLASS).data(TAGBOX_TAG_DATA_KEY, value).insertBefore($input);
},
_toggleEmptinessEventHandler: function _toggleEmptinessEventHandler() {
this._toggleEmptiness(!this._getValue().length && !this._searchValue().length);
},
_customItemAddedHandler: function _customItemAddedHandler(e) {
this.callBase(e);
this._input().val("");
},
_removeTagHandler: function _removeTagHandler(args) {
var e = args.event;
e.stopPropagation();
var $tag = $(e.target).closest("." + TAGBOX_TAG_CLASS);
this._removeTagElement($tag);
},
_removeTagElement: function _removeTagElement($tag) {
if ($tag.hasClass(TAGBOX_MULTI_TAG_CLASS)) {
if (!this.option("showMultiTagOnly")) {
this.option("value", this._getValue().slice(0, this.option("maxDisplayedTags")));
} else {
this.reset();
}
return;
}
var itemValue = $tag.data(TAGBOX_TAG_DATA_KEY);
this._removeTagWithUpdate(itemValue);
this._refreshTagElements();
},
_updateField: noop,
_removeTagWithUpdate: function _removeTagWithUpdate(itemValue) {
var value = this._getValue().slice();
this._removeTag(value, itemValue);
this.option("value", value);
if (value.length === 0) {
this._clearTagFocus();
}
},
_getCurrentValue: function _getCurrentValue() {
return this._lastValue();
},
_selectionChangeHandler: function _selectionChangeHandler(e) {
if (this.option("applyValueMode") === "useButtons") {
return;
}
var value = this._getValue().slice();
iteratorUtils.each(e.removedItems || [], function (_, removedItem) {
this._removeTag(value, this._valueGetter(removedItem));
}.bind(this));
iteratorUtils.each(e.addedItems || [], function (_, addedItem) {
this._addTag(value, this._valueGetter(addedItem));
}.bind(this));
this._updateWidgetHeight();
this.option("value", value);
},
_removeTag: function _removeTag(value, item) {
var index = this._valueIndex(item, value);
if (index >= 0) {
value.splice(index, 1);
}
},
_addTag: function _addTag(value, item) {
var index = this._valueIndex(item);
if (index < 0) {
value.push(item);
}
},
_fieldRenderData: function _fieldRenderData() {
return this._selectedItems.slice();
},
_completeSelection: function _completeSelection(value) {
if (!this.option("showSelectionControls")) {
this._setValue(value);
}
},
_setValue: function _setValue(value) {
if (value === null) {
return;
}
var useButtons = this.option("applyValueMode") === "useButtons",
valueIndex = this._valueIndex(value),
values = (useButtons ? this._list.option("selectedItemKeys") : this._getValue()).slice();
if (valueIndex >= 0) {
values.splice(valueIndex, 1);
} else {
values.push(value);
}
if (this.option("applyValueMode") === "useButtons") {
this._list.option("selectedItemKeys", values);
} else {
this.option("value", values);
}
},
_isSelectedValue: function _isSelectedValue(value, cache) {
return this._valueIndex(value, null, cache) > -1;
},
_valueIndex: function _valueIndex(value, values, cache) {
var result = -1;
if (cache && (typeof value === "undefined" ? "undefined" : _typeof(value)) !== "object") {
if (!cache.indexByValues) {
cache.indexByValues = {};
values = values || this._getValue();
values.forEach(function (value, index) {
cache.indexByValues[value] = index;
});
}
if (value in cache.indexByValues) {
return cache.indexByValues[value];
}
}
values = values || this._getValue();
iteratorUtils.each(values, function (index, selectedValue) {
if (this._isValueEquals(value, selectedValue)) {
result = index;
return false;
}
}.bind(this));
return result;
},
_lastValue: function _lastValue() {
var values = this._getValue(),
lastValue = values[values.length - 1];
return isDefined(lastValue) ? lastValue : null;
},
_valueChangeEventHandler: noop,
_shouldRenderSearchEvent: function _shouldRenderSearchEvent() {
return this.option("searchEnabled") || this.option("acceptCustomValue");
},
_searchHandler: function _searchHandler(e) {
if (this.option("searchEnabled") && !!e && !this._isTagRemoved) {
this.callBase(e);
}
this._updateWidgetHeight();
delete this._isTagRemoved;
},
_updateWidgetHeight: function _updateWidgetHeight() {
var element = this.$element(),
originalHeight = element.height();
this._renderInputSize();
var currentHeight = element.height();
if (this._popup && this.option("opened") && this._isEditable() && currentHeight !== originalHeight) {
this._popup.repaint();
}
},
_refreshSelected: function _refreshSelected() {
this._list && this._list.option("selectedItems", this._selectedItems);
},
_resetListDataSourceFilter: function _resetListDataSourceFilter() {
var dataSource = this._getDataSource();
if (!dataSource) {
return;
}
delete this._userFilter;
dataSource.filter(null);
dataSource.reload();
},
_setListDataSourceFilter: function _setListDataSourceFilter() {
if (!this.option("hideSelectedItems") || !this._list) {
return;
}
var dataSource = this._getDataSource();
if (!dataSource) {
return;
}
var valueGetterExpr = this._valueGetterExpr();
if (typeUtils.isString(valueGetterExpr) && valueGetterExpr !== "this") {
var filter = this._dataSourceFilterExpr();
if (this._userFilter === undefined) {
this._userFilter = dataSource.filter() || null;
}
this._userFilter && filter.push(this._userFilter);
filter.length ? dataSource.filter(filter) : dataSource.filter(null);
} else {
dataSource.filter(this._dataSourceFilterFunction.bind(this));
}
dataSource.load();
},
_dataSourceFilterExpr: function _dataSourceFilterExpr() {
var filter = [];
iteratorUtils.each(this._getValue(), function (index, value) {
filter.push(["!", [this._valueGetterExpr(), value]]);
}.bind(this));
return filter;
},
_dataSourceFilterFunction: function _dataSourceFilterFunction(itemData) {
var itemValue = this._valueGetter(itemData),
result = true;
iteratorUtils.each(this._getValue(), function (index, value) {
if (this._isValueEquals(value, itemValue)) {
result = false;
return false;
}
}.bind(this));
return result;
},
_applyButtonHandler: function _applyButtonHandler() {
this.option("value", this._getSortedListValues());
this._clearTextValue();
this._clearFilter();
this.callBase();
},
_getSortedListValues: function _getSortedListValues() {
var listValues = this._getListValues(),
currentValue = this.option("value"),
sortFunction = function sortFunction(item1, item2) {
return currentValue.indexOf(item2) - currentValue.indexOf(item1);
};
return currentValue ? listValues.sort(sortFunction) : listValues;
},
_getListValues: function _getListValues() {
if (!this._list) {
return [];
}
var that = this,
selectedItems = this._getPlainItems(this._list.option("selectedItems")),
result = [];
iteratorUtils.each(selectedItems, function (index, item) {
result[index] = that._valueGetter(item);
});
return result;
},
_renderOpenedState: function _renderOpenedState() {
this.callBase();
if (this.option("applyValueMode") === "useButtons" && !this.option("opened")) {
this._refreshSelected();
}
},
_clean: function _clean() {
this.callBase();
delete this._defaultTagTemplate;
delete this._tagTemplate;
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "onSelectAllValueChanged":
this._initSelectAllValueChangedAction();
break;
case "onMultiTagPreparing":
this._initMultiTagPreparingAction();
this._renderTags();
break;
case "hideSelectedItems":
if (args.value) {
this._setListDataSourceFilter();
} else {
this._resetListDataSourceFilter();
}
break;
case "useSubmitBehavior":
this._toggleSubmitElement(args.value);
break;
case "displayExpr":
this.callBase(args);
this._initTemplates();
this._compileDisplaySetter();
this._invalidate();
break;
case "tagTemplate":
this._initTagTemplate();
this._invalidate();
break;
case "selectAllText":
this._setListOption("selectAllText", this.option("selectAllText"));
break;
case "value":
this.callBase(args);
this._setListDataSourceFilter();
break;
case "maxDisplayedTags":
case "showMultiTagOnly":
this._renderTags();
break;
case "selectAllMode":
this._setListOption(args.name, args.value);
break;
case "selectedItem":
break;
case "selectedItems":
var addedItems = arrayUtils.removeDuplicates(args.value, args.previousValue),
removedItems = arrayUtils.removeDuplicates(args.previousValue, args.value);
this._selectionChangedAction({
addedItems: addedItems,
removedItems: removedItems
});
break;
case "multiline":
this.$element().toggleClass(TAGBOX_SINGLE_LINE_CLASS, !args.value);
this._renderSingleLineScroll();
break;
case "maxFilterLength":
break;
default:
this.callBase(args);
}
},
_getActualSearchValue: function _getActualSearchValue() {
return this.callBase() || this._searchValue();
},
_popupHidingHandler: function _popupHidingHandler() {
this.callBase();
this._clearFilter();
},
reset: function reset() {
this.option("value", []);
this._clearFilter();
this._clearSelectedItem();
}
});
registerComponent("dxTagBox", TagBox);
module.exports = TagBox;