@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,616 lines (1,371 loc) • 116 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define([
'./InputBase',
'./ComboBoxBase',
'./Tokenizer',
'./Token',
'./Popover',
'./CheckBox',
'./Toolbar',
'./library',
'sap/ui/core/Element',
'sap/ui/core/EnabledPropagator',
'sap/ui/core/IconPool',
'sap/ui/core/library',
'sap/ui/Device',
'sap/ui/core/Item',
'sap/ui/core/ResizeHandler',
'./MultiComboBoxRenderer',
"sap/ui/dom/containsOrEquals",
"sap/m/inputUtils/completeTextSelected",
"sap/m/inputUtils/inputsDefaultFilter",
"sap/m/inputUtils/typeAhead",
"sap/m/inputUtils/ListHelpers",
"sap/m/inputUtils/filterItems",
"sap/m/inputUtils/itemsVisibilityHandler",
"sap/m/inputUtils/forwardItemPropertiesToToken",
"sap/m/inputUtils/getTokenByItem",
"sap/ui/events/KeyCodes",
"sap/base/util/deepEqual",
"sap/base/assert",
"sap/base/Log",
"sap/ui/core/Core",
'sap/ui/core/InvisibleText',
"sap/ui/thirdparty/jquery",
// jQuery Plugin "cursorPos"
"sap/ui/dom/jquery/cursorPos"
],
function(
InputBase,
ComboBoxBase,
Tokenizer,
Token,
Popover,
CheckBox,
Toolbar,
library,
Element,
EnabledPropagator,
IconPool,
coreLibrary,
Device,
Item,
ResizeHandler,
MultiComboBoxRenderer,
containsOrEquals,
completeTextSelected,
inputsDefaultFilter,
typeAhead,
ListHelpers,
filterItems,
itemsVisibilityHandler,
forwardItemPropertiesToToken,
getTokenByItem,
KeyCodes,
deepEqual,
assert,
Log,
core,
InvisibleText,
jQuery
) {
"use strict";
// shortcut for sap.m.ListMode
var ListMode = library.ListMode;
// shortcut for sap.ui.core.ValueState
var ValueState = coreLibrary.ValueState;
// shortcut for sap.ui.core.OpenState
var OpenState = coreLibrary.OpenState;
// shortcut for sap.m.TokenizerRenderMode
var TokenizerRenderMode = library.TokenizerRenderMode;
/**
* Constructor for a new MultiComboBox.
*
* @param {string} [sId] id for the new control, generated automatically if no id is given
* @param {object} [mSettings] initial settings for the new control
*
* @class
* The MultiComboBox control provides a list box with items and a text field allowing the user to either type a value directly into the control or choose from the list of existing items.
*
* A drop-down list for selecting and filtering values.
* <h3>Overview</h3>
* The MultiComboBox control is commonly used to enable users to select one or more options from a predefined list. The control provides an editable input field to filter the list, and a dropdown arrow of available options.
* The select options in the list have checkboxes that permit multi-selection. Entered values are displayed as {@link sap.m.Token tokens}.
*
* When an invalid character is typed into the text field of the MultiComboBox control, the value state is changed to <code>sap.ui.core.ValueState.Error</code> only for a second, as the invalid value is immediately deleted from the input field.
* <h3>Structure</h3>
* The MultiComboBox consists of the following elements:
* <ul>
* <li> Input field - displays the selected option/s as token/s. Users can type to filter the list.
* <li> Drop-down arrow - expands\collapses the option list.</li>
* <li> Option list - the list of available options. <b>Note:</b> Disabled items are not visualized in the list with the available options, however they can still be accessed through the <code>items</code> aggregation.</li>
* </ul>
* <h3>Usage</h3>
* <h4>When to use:</h4>
* <ul>
* <li>The user needs to select one or more options from a long list of options (maximum of approximately 200).</li>
* </ul>
* <h4>When not to use:</h4>
* <ul>
* <li>The user needs to choose between two options such as ON or OFF and YES or NO. In this case, consider using a {@link sap.m.Switch switch} control instead</li>
* <li>You need to display more that one attribute. In this case, consider using the {@link sap.m.SelectDialog select dialog} or value help dialog instead.</li>
* <li>The user needs to search on multiple attributes. In this case, consider using the {@link sap.m.SelectDialog select dialog} or value help dialog instead.</li>
* <li>Your use case requires all available options to be displayed right away, without any user interaction. In this case, consider using the {@link sap.m.CheckBox checkboxes} instead.</li>
* </ul>
* <h3>Responsive Behavior</h3>
* If there are many tokens, the control shows only the last selected tokens that fit and for the others a label N-more is provided.
* In case the length of the last selected token is exceeding the width of the control, only a label N-Items is shown. In both cases, pressing on the label will show the tokens in a popup.
* <u>On Phones:</u>
* <ul>
* <li>A new full-screen dialog opens where all items from the option list are shown.</li>
* <li>You can select and deselect items from the option list.</li>
* <li>With the help of a toggle button you can switch between showing all tokens and only selected ones.</li>
* <li>You can filter the option list by entering a value in the input.</li>
* </ul>
* <u>On Tablets:</u>
* <ul>
* <li>The auto-complete suggestions appear below or above the input field.</li>
* <li>You can review the tokens by swiping them to left or right.</li>
* </ul>
* <u>On Desktop:</u>
* <ul>
* <li>The auto-complete suggestions appear below or above the input field.</li>
* <li>You can review the tokens by pressing the right or left arrows on the keyboard.</li>
* <li>You can select single tokens or a range of tokens and you can copy/cut/delete them.</li>
* </ul>
*
* @author SAP SE
* @version 1.117.4
*
* @constructor
* @extends sap.m.ComboBoxBase
* @public
* @since 1.22.0
* @alias sap.m.MultiComboBox
* @see {@link fiori:https://experience.sap.com/fiori-design-web/multi-combobox/ Multi-Combo Box}
*/
var MultiComboBox = ComboBoxBase.extend("sap.m.MultiComboBox", /** @lends sap.m.MultiComboBox.prototype */ {
metadata: {
library: "sap.m",
designtime: "sap/m/designtime/MultiComboBox.designtime",
properties: {
/**
* Keys of the selected items. If the key has no corresponding item, no changes will apply. If duplicate keys exists the first item matching the key is used.
*/
selectedKeys: { type: "string[]", group: "Data", defaultValue: [] },
/**
* Defines if there are selected items or not.
*/
hasSelection: { type: "boolean", visibility: "hidden", defaultValue: false },
/**
* Determines if the select all checkbox is visible on top of suggestions.
*/
showSelectAll: { type: "boolean", defaultValue: false }
},
associations: {
/**
* Provides getter and setter for the selected items from
* the aggregation named items.
*/
selectedItems: { type: "sap.ui.core.Item", multiple: true, singularName: "selectedItem" }
},
aggregations: {
/**
* The tokenizer which displays the tokens
*/
tokenizer: {type: "sap.m.Tokenizer", multiple: false, visibility: "hidden"}
},
events: {
/**
* Event is fired when selection of an item is changed.
* Note: please do not use the "change" event inherited from sap.m.InputBase
*/
selectionChange: {
parameters: {
/**
* Item which selection is changed
*/
changedItem: { type: "sap.ui.core.Item" },
/**
* Array of items whose selection has changed.
*/
changedItems : {type : "sap.ui.core.Item[]"},
/**
* Selection state: true if item is selected, false if
* item is not selected
*/
selected: { type: "boolean" },
/**
* Indicates whether the select all action is triggered or not.
*/
selectAll : {type : "boolean"}
}
},
/**
* Event is fired when user has finished a selection of items in a list box and list box has been closed.
*/
selectionFinish: {
parameters: {
/**
* The selected items which are selected after list box has been closed.
*/
selectedItems: { type: "sap.ui.core.Item[]" }
}
}
},
dnd: { draggable: false, droppable: true }
},
renderer: MultiComboBoxRenderer
});
IconPool.insertFontFaceStyle();
EnabledPropagator.apply(MultiComboBox.prototype, [true]);
/**
* Clones the <code>sap.m.MultiComboBox</code> control.
*
* @param {string} sIdSuffix Suffix to be added to the ids of the new control and its internal objects.
* @returns {this} The cloned <code>sap.m.MultiComboBox</code> control.
* @public
*/
MultiComboBox.prototype.clone = function (sIdSuffix) {
var oComboBoxClone = ComboBoxBase.prototype.clone.apply(this, arguments),
oList = this._getList();
if (oList) {
oComboBoxClone.syncPickerContent();
}
return oComboBoxClone;
};
/**
* Opens the control's picker popup.
*
* @returns {this} <code>this</code> to allow method chaining.
* @protected
*/
MultiComboBox.prototype.open = function () {
if (!this.isOpen()) {
this._bPickerIsOpening = true;
}
this.syncPickerContent();
return ComboBoxBase.prototype.open.apply(this, arguments);
};
/* ----------------------------------------------------------- */
/* Keyboard handling */
/* ----------------------------------------------------------- */
/**
* Handle End key pressed. Scroll the last token into viewport.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onsapend = function(oEvent) {
if (oEvent.isMarked("forwardFocusToParent")) {
this.focus();
}
};
/**
* Handle Home key pressed. Scroll the first token into viewport.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onsaphome = function(oEvent) {
// if the caret is already moved to the start of the input text
// execute tokenizer's onsaphome handler
if (!this.getFocusDomRef().selectionStart && this._hasTokens()) {
Tokenizer.prototype.onsaphome.apply(this.getAggregation("tokenizer"), arguments);
}
oEvent.setMarked();
};
/**
* Handle DOWN arrow key pressed. Set focus to the first list item if the list is open. Otherwise show in input field
* the description of the next traversal item.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onsapdown = function(oEvent) {
if (!this.getEnabled() || !this.getEditable()) {
return;
}
// mark the event for components that needs to know if the event was handled by this control
oEvent.setMarked();
// note: prevent document scrolling when arrow keys are pressed
oEvent.preventDefault();
this.syncPickerContent();
if (!this.isOpen()) {
this._oTraversalItem = this._getNextTraversalItem();
if (this._oTraversalItem && !this.isFocusInTokenizer() && !this.isComposingCharacter()) {
this.updateDomValue(this._oTraversalItem.getText());
this.selectText(0, this.getValue().length);
}
return;
}
// wait for the composition and input events to fire properly since the focus of the list item
// triggers unwanted extra events when called in while composing
setTimeout(this.handleDownEvent.bind(this, oEvent), 0);
};
/**
* Handles Up arrow key pressed. Set the focus to the input field if there are no links in
* the value state message and the first list item is selected. Otherwise show in input field
* description of the previous traversal item.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onsapup = function(oEvent) {
if (!this.getEnabled() || !this.getEditable()) {
return;
}
// mark the event for components that needs to know if the event was handled
// by this control
oEvent.setMarked();
// note: prevent document scrolling when arrow keys are pressed
oEvent.preventDefault();
this.syncPickerContent();
if (this.isFocusInTokenizer() || this.isOpen()) {
return;
}
this._oTraversalItem = this._getPreviousTraversalItem();
if (this._oTraversalItem) {
this.updateDomValue(this._oTraversalItem.getText());
this.selectText(0, this.getValue().length);
}
};
/**
* Handles the Down Arrow press event.
*
* @param {jquery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.handleDownEvent = function (oEvent) {
if (!this.isOpen()) {
return;
}
var oSrcControl = oEvent.srcControl,
oSrcDomRef = oSrcControl && oSrcControl.getDomRef(),
bFocusInInput = containsOrEquals(this.getDomRef(), oSrcDomRef),
oValueStateHeader = this.getPicker().getCustomHeader(),
oValueStateHeaderDom = oValueStateHeader && oValueStateHeader.getDomRef();
oEvent.setMarked();
// note: Prevent document scrolling when Down key is pressed
oEvent.preventDefault();
if (bFocusInInput && this.getValueState() != ValueState.None) {
this._handleFormattedTextNav();
return;
}
if ((bFocusInInput || containsOrEquals(oValueStateHeaderDom, oSrcDomRef)) && this.getShowSelectAll()) {
this.focusSelectAll();
return;
}
this.focusFirstItemInList();
};
/**
* Handles the End press event.
*
* @param {jquery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.handleEndEvent = function (oEvent) {
oEvent.setMarked();
// Note: Prevent document scrolling when End key is pressed
oEvent.preventDefault();
var aVisibleItems = ListHelpers.getVisibleItems(this.getItems()),
oListItem = aVisibleItems.length && ListHelpers.getListItem(aVisibleItems[aVisibleItems.length - 1]);
oListItem && oListItem.focus();
};
/**
* Handles the Home press event.
*
* @param {jquery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.handleHomeEvent = function (oEvent) {
oEvent.setMarked();
// note: Prevent document scrolling when Home key is pressed
oEvent.preventDefault();
if (this.getValueState() !== ValueState.None) {
this._handleFormattedTextNav();
oEvent.stopPropagation(true);
return;
}
if (this.getShowSelectAll()) {
this.focusSelectAll();
oEvent.stopPropagation(true);
return;
}
this.focusFirstItemInList();
};
/**
* Focuses on the first item in the list of options.
* @private
*/
MultiComboBox.prototype.focusFirstItemInList = function () {
var aVisibleItems = ListHelpers.getVisibleItems(this.getItems()),
oListItem = aVisibleItems.length && ListHelpers.getListItem(aVisibleItems[0]);
oListItem && oListItem.focus();
};
/**
* Checks if the focused element is part of the Tokenizer.
* @returns {boolean} True if the focus is inside the Tokenizer
* @private
*/
MultiComboBox.prototype.isFocusInTokenizer = function () {
return jQuery.contains(this.getAggregation("tokenizer").getFocusDomRef(), document.activeElement);
};
/**
* Handles the <code>onsapshow</code> event when either F4 is pressed or Alt + Down arrow are pressed.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onsapshow = function(oEvent) {
oEvent.preventDefault();
this._handleItemToFocus();
ComboBoxBase.prototype.onsapshow.apply(this, arguments);
};
MultiComboBox.prototype._handlePopupOpenAndItemsLoad = function () {
// should sync the content before setting the initial focus and opening the picker
// the picker opening is handled in the base function
this._handleItemToFocus();
ComboBoxBase.prototype._handlePopupOpenAndItemsLoad.apply(this, arguments);
};
/**
* Generates an event delegate for keyboard navigation for <code>sap.m.FormattedText</code> value state header.
* If the focus is on the formatted text value state message:
* - pressing the Up arrow key will move the focus to the input,
* - pressing the Down arrow key - will select the first selectable item.
*
* @param {object} oValueStateHeader The value state header.
* @param {array} aValueStateLinks The links in <code>sap.m.FormattedText</code> value state message.
* @returns {object} Delegate for navigation and focus handling for <code>sap.m.ValueStateHeader</code> containing <code>sap.m.FormattedText</code> message with links.
*
* @private
*/
MultiComboBox.prototype._valueStateNavDelegate = function(oValueStateHeader, aValueStateLinks) {
return {
onsapdown: this.handleDownEvent,
onsapup: this.focus,
onsapend: this.handleEndEvent,
onfocusout: function(oEvent) {
// Links should not be tabbable after the focus is moved outside of the value state header
oValueStateHeader.removeStyleClass("sapMFocusable");
// Check if the element getting the focus is outside the value state header
if (!oValueStateHeader.getDomRef().contains(oEvent.relatedTarget)) {
aValueStateLinks.forEach(function(oLink) {
oLink.getDomRef().setAttribute("tabindex", "-1");
});
}
},
onsapshow: this.close,
onsaphide: this.close
};
};
/**
* Event delegate for the last link in the <code>sap.m.FormattedText</code> value state message.
* Closes the picker and the value state popup if tab key is pressed on the last value state message link in the header.
*
* @private
*/
MultiComboBox.prototype._closePickerDelegate = {
onsaptabnext: function() {
this.close();
// Closing with timeout as it is open that way
setTimeout(function() {
this.closeValueStateMessage();
}.bind(this), 0);
}
};
/**
* Event delegate that Handles the arrow navigation of the links in the value state header
*
* @private
*/
MultiComboBox.prototype._formattedTextLinksNav = {
onsapup: this.focus,
onsapdown: this.handleDownEvent
};
/**
* Handles the focus and the navigation of the value state header
* when <code>sap.m.Link</code> is present in the value state message.
*
* @private
*/
MultiComboBox.prototype._handleFormattedTextNav = function() {
var oCustomHeader = this.getPicker().getCustomHeader(),
aValueStateLinks = this.getValueStateLinks(),
oLastValueStateLink = aValueStateLinks ? aValueStateLinks[aValueStateLinks.length - 1] : null,
oFirstValueStateLink = aValueStateLinks ? aValueStateLinks[0] : null;
if (!oCustomHeader.getDomRef() || oCustomHeader.getDomRef() === document.activeElement) {
return;
}
if (!this.oValueStateNavDelegate) {
this.oValueStateNavDelegate = this._valueStateNavDelegate(oCustomHeader, aValueStateLinks);
oCustomHeader.addEventDelegate(this.oValueStateNavDelegate, this);
}
// Make the value state header focusable and focus it
oCustomHeader.getDomRef().setAttribute("tabindex", "-1");
oCustomHeader.addStyleClass("sapMFocusable");
oCustomHeader.focus();
// Linka should not be part of the tab chain when the focus is out of the value state header
// (on the items list or on the input) and the opposite when the header is focused.
aValueStateLinks.forEach(function(oLink) {
oLink.getDomRef().setAttribute("tabindex", "0");
oLink.addEventDelegate(this._formattedTextLinksNav, this);
}, this);
this.oMoveFocusBackToVSHeader = !this.oMoveFocusBackToVSHeader ? {
onsaptabprevious: function(oEvent) {
oEvent.preventDefault();
oCustomHeader.focus();
oCustomHeader.addStyleClass("sapMFocusable");
}
} : this.oMoveFocusBackToVSHeader;
oLastValueStateLink && oLastValueStateLink.addEventDelegate(this._closePickerDelegate, this);
oFirstValueStateLink && oFirstValueStateLink.addEventDelegate(this.oMoveFocusBackToVSHeader, this);
};
/**
* Handles when Alt + Up arrow are pressed.
*
* @param {jQuery.Event} oEvent The event object.
* @private
*/
MultiComboBox.prototype.onsaphide = function (oEvent) {
this.onsapshow(oEvent);
};
/**
* Handles the item selection when user triggers an item selection via key press (TAB, ENTER etc.).
*
* @param {jQuery.Event} oEvent The key event object
* @private
*/
MultiComboBox.prototype._selectItemByKey = function(oEvent) {
var aVisibleItems, oParam,
oItem, i, bItemMatched, bKeyIsValid;
if (!this.getEnabled() || !this.getEditable()) {
return;
}
// mark the event for components that needs to know if the event was handled
// by this control
if (oEvent) {
oEvent.setMarked();
}
aVisibleItems = this._getUnselectedItems();
for (i = 0; i < aVisibleItems.length; i++) {
// Empty string should be valid key for sap.ui.core.Item only
// as sap.ui.core.SeparatorItem with empty key is used for Grouping
// while sap.ui.core.SeparatorItem without key and text is used for horizontal visible separator
bKeyIsValid = !(aVisibleItems[i].getKey() === undefined || aVisibleItems[i].getKey() === null) && !aVisibleItems[i].isA("sap.ui.core.SeparatorItem");
if (aVisibleItems[i].getText().toUpperCase() === this.getValue().toUpperCase() && bKeyIsValid) {
oItem = aVisibleItems[i];
bItemMatched = true;
break;
}
}
if (bItemMatched) {
oParam = {
item: oItem,
id: oItem.getId(),
key: oItem.getKey(),
fireChangeEvent: true,
fireFinishEvent: true,
suppressInvalidate: true,
listItemUpdated: false
};
this._bPreventValueRemove = false;
if (this.getValue() === "" || (typeof this.getValue() === "string" && oItem.getText().toLowerCase().startsWith(this.getValue().toLowerCase()))) {
if (ListHelpers.getListItem(oItem).isSelected()) {
this.setValue('');
} else {
this.setSelection(oParam);
}
}
} else {
this._bPreventValueRemove = true;
}
if (oEvent) {
this.close();
}
};
/**
* Handle when enter is pressed.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onsapenter = function(oEvent) {
var oTokenizer = this.getAggregation("tokenizer");
// intentionally skip implementation of ComboTextField.prototype.onsapenter
InputBase.prototype.onsapenter.apply(this, arguments);
// validate if an item is already selected
this._showAlreadySelectedVisualEffect();
if (this.getValue()) {
this._selectItemByKey(oEvent);
}
//Open popover with items if in readonly mode and has Nmore indicator
if (!this.getEditable() && oTokenizer.getHiddenTokensCount() && oEvent.target === this.getFocusDomRef()) {
oTokenizer._togglePopup(oTokenizer.getTokensPopup());
}
};
/**
* Handles tab key event. Selects an item according to given input if there is exactly one fitting item available.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onsaptabnext = function(oEvent) {
var sInputValue = this.getValue();
if (sInputValue) {
var aSelectableItems = this._getUnselectedItemsStartingText(sInputValue);
if (aSelectableItems.length) {
this._selectItemByKey(oEvent);
} else {
this._showWrongValueVisualEffect();
}
}
};
MultiComboBox.prototype.onsaptabprevious = MultiComboBox.prototype.onsaptabnext;
/* =========================================================== */
/* Event handlers */
/* =========================================================== */
/**
* Handle the focus leave event.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onsapfocusleave = function(oEvent) {
var bTablet = this.isPlatformTablet(),
oControl = core.byId(oEvent.relatedControlId),
oFocusDomRef = oControl && oControl.getFocusDomRef(),
sOldValue = this.getValue(),
oPicker = this.getPicker(),
oTokenizer = this.getAggregation("tokenizer");
// If focus target is outside of picker and the picker is fully opened
if (!this._bPickerIsOpening && (!oPicker || !oPicker.getFocusDomRef() || !oFocusDomRef || !jQuery.contains(oPicker.getFocusDomRef(), oFocusDomRef))) {
this.setValue(null);
// fire change event only if the value of the MCB is not empty
if (sOldValue) {
this.fireChangeEvent("", { value: sOldValue });
}
// if the focus is outside the MultiComboBox, the tokenizer should be collapsed
if (!jQuery.contains(this.getDomRef(), document.activeElement)) {
oTokenizer.setRenderMode(TokenizerRenderMode.Narrow);
}
}
if (oPicker && oFocusDomRef) {
if (deepEqual(oPicker.getFocusDomRef(), oFocusDomRef) && !bTablet && !this.isPickerDialog()) {
// force the focus to stay in the MultiComboBox field when scrollbar
// is moving
this.focus();
}
}
};
/**
* Handle the focus in event.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onfocusin = function(oEvent) {
var oPicker = this.getPicker();
var bPreviousFocusInDropdown = false;
var oPickerDomRef = oPicker && oPicker.getFocusDomRef();
var sCurrentState = (oPicker && oPicker.oPopup.getOpenState()) || OpenState.CLOSED;
var bPickerClosedOrClosing = sCurrentState === OpenState.CLOSING || sCurrentState === OpenState.CLOSED;
var bDropdownPickerType = this.getPickerType() === "Dropdown";
var oTokenizer = this.getAggregation("tokenizer");
if (bDropdownPickerType) {
bPreviousFocusInDropdown = oPickerDomRef && jQuery.contains(oPickerDomRef, oEvent.relatedTarget);
}
if (this.getEditable() && oEvent.target === this.getDomRef("inner")) {
oTokenizer.setRenderMode(TokenizerRenderMode.Loose);
}
if (oEvent.target === this.getFocusDomRef()) {
oTokenizer.hasOneTruncatedToken() && oTokenizer.setFirstTokenTruncated(false);
this.getEnabled() && this.addStyleClass("sapMFocus");
// enable type ahead when switching focus from the dropdown to the input field
// we need to check whether the focus has been triggered by the popover's closing or just a manual focusin
// isOpen is still true as the closing has not finished yet.
!bPickerClosedOrClosing && bPreviousFocusInDropdown && this.handleInputValidation(oEvent, false);
}
if (oEvent.target === this.getOpenArea() && bDropdownPickerType && !this.isPlatformTablet()) {
// avoid the text-editing mode popup to be open on mobile,
// text-editing mode disturbs the usability experience (it blocks the UI in some devices)
// force the focus to stay in the input field
this.focus();
}
// message popup won't open when the item list is shown
if (!this.isOpen() && this.shouldValueStateMessageBeOpened()) {
this.openValueStateMessage();
}
};
/**
* Handles the <code>tap</code> event on the list's items.
*
* @param {sap.ui.base.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype._handleItemTap = function(oEvent) {
var oTappedControl = Element.closestTo(oEvent.target);
if (!oTappedControl.isA("sap.m.CheckBox") && !oTappedControl.isA("sap.m.GroupHeaderListItem")) {
this._bCheckBoxClicked = false;
}
};
/**
* Handles the <code>press</code> event on the list's items.
*
* @param {sap.ui.base.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype._handleItemPress = function(oEvent) {
// If an item is selected clicking on checkbox inside of suggest list the list with all entries should be opened
if (this.isOpen() && this._isListInSuggestMode() && this.getPicker().oPopup.getOpenState() !== OpenState.CLOSING) {
this.clearFilter();
var oItem = this._getLastSelectedItem();
// Scrolls an item into the visual viewport
if (oItem) {
ListHelpers.getListItem(oItem).focus();
}
}
};
/**
* Handles the <code>selectionChange</code> event on the List.
*
* @param {sap.ui.base.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype._handleSelectionLiveChange = function(oEvent) {
if (oEvent.getParameter("selectAll")) {
return;
}
var oListItem = oEvent.getParameter("listItem");
var aListItems = oEvent.getParameter("listItems");
var oListItemToFocus = aListItems && aListItems[aListItems.length - 1] || oListItem;
var oInputControl = this.isPickerDialog() ? this.getPickerTextField() : this;
var bShouldFocusItem = this._getIsClick() && !!oListItemToFocus;
var bIsSelected = oEvent.getParameter("selected");
var oNewSelectedItem = ListHelpers.getItemByListItem(this.getItems(), oListItem);
var aNewSelectedItems;
if (aListItems && aListItems.length) {
aNewSelectedItems = [];
aListItems.forEach(function (oNewItem) {
if (oNewItem.getType() === "Active") {
aNewSelectedItems.push(ListHelpers.getItemByListItem(this.getItems(), oNewItem));
}
}, this);
}
if (oListItem.getType() === "Inactive") {
// workaround: this is needed because the List fires the "selectionChange" event on inactive items
return;
}
// pre-assertion
assert(oNewSelectedItem, "The corresponding mapped item was not found on " + this);
if (!oNewSelectedItem) {
return;
}
var oParam = {
item: oNewSelectedItem,
items: aNewSelectedItems,
id: oNewSelectedItem.getId(),
key: oNewSelectedItem.getKey(),
selectAll: false,
fireChangeEvent: true,
suppressInvalidate: true,
listItemUpdated: true
};
if (bIsSelected) {
// update the selected item
this.fireChangeEvent(oNewSelectedItem.getText());
this.setSelection(oParam);
} else {
this.fireChangeEvent(oNewSelectedItem.getText());
this.removeSelection(oParam);
}
if (this._bCheckBoxClicked) {
oInputControl.setValue(this._sOldInput);
if (bShouldFocusItem && this.isOpen() && this.getPicker().oPopup.getOpenState() !== OpenState.CLOSING) {
oListItemToFocus.focus();
this._setIsClick(false);
}
} else {
this._bCheckBoxClicked = true;
this.setValue("");
this.close();
}
};
/**
* Handles the <code>keydown</code> event when any key is pressed.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onkeydown = function(oEvent) {
var bEditable = this.getEditable(),
oTokenizer = this.getAggregation("tokenizer"),
iTokensCount = oTokenizer.getTokens().length;
ComboBoxBase.prototype.onkeydown.apply(this, arguments);
if (!this.getEnabled()) {
return;
}
if ((oEvent.ctrlKey || oEvent.metaKey) && oEvent.which === KeyCodes.I && iTokensCount) {
oEvent.preventDefault();
if (bEditable) {
this._togglePopover();
} else {
this._handleIndicatorPress();
}
return;
}
this._bIsPasteEvent = (oEvent.ctrlKey || oEvent.metaKey) && (oEvent.which === KeyCodes.V);
// only if there is no text and tokenizer has some tokens
if (this.getValue().length === 0 && (oEvent.ctrlKey || oEvent.metaKey) && (oEvent.which === KeyCodes.A)
&& this._hasTokens()) {
oTokenizer.focus();
oTokenizer.selectAllTokens(true);
oEvent.preventDefault();
}
// workaround - keyup is not fired on mobile device
if (this.isPickerDialog()) {
this._sOldValue = this.getPickerTextField().getValue();
this._iOldCursorPos = jQuery(this.getFocusDomRef()).cursorPos();
}
this._bDoTypeAhead = !Device.os.android && (oEvent.which !== KeyCodes.BACKSPACE) && (oEvent.which !== KeyCodes.DELETE);
};
/**
* Handles the <code>input</code> event on the control's input field.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.oninput = function(oEvent) {
ComboBoxBase.prototype.oninput.apply(this, arguments);
var oInput = oEvent.srcControl,
bIsPickerDialog = this.isPickerDialog(),
oInputField = bIsPickerDialog ? this.getPickerTextField() : this,
sValueState = oInputField.getValueState();
// reset the value state
if (sValueState === ValueState.Error && this._bAlreadySelected) {
oInputField.setValueState(this._sInitialValueState);
oInputField.setValueStateText(this._sInitialValueStateText);
this._bAlreadySelected = false;
}
if (!this.getEnabled() || !this.getEditable()) {
return;
}
this.syncPickerContent();
// suppress invalid value
this.handleInputValidation(oEvent, this.isComposingCharacter());
if (this._bIsPasteEvent) {
oInput.updateDomValue(this._sOldValue || oEvent.target.value || "");
return;
}
if (this.isOpen()) {
// wait a tick so the setVisible call has replaced the DOM
setTimeout(this.highlightList.bind(this, this._sOldInput));
}
// if recommendations were shown - add the icon pressed style
if (this._getItemsShownWithFilter()) {
this.toggleIconPressedStyle(true);
}
};
/**
* Filters array of items for given value.
*
* @param {object} mOptions Options object
* @returns {sap.ui.core.Item[]} Array of filtered items
* @private
*/
MultiComboBox.prototype.filterItems = function (mOptions) {
return filterItems(this, mOptions.items, mOptions.value, true, false, this.fnFilter || inputsDefaultFilter);
};
/**
* Function is called on key up keyboard input
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype.onkeyup = function(oEvent) {
ComboBoxBase.prototype.onkeyup.apply(this, arguments);
if (!this.getEnabled() || !this.getEditable()) {
return;
}
this._sOldValue = this.getValue();
this._iOldCursorPos = jQuery(this.getFocusDomRef()).cursorPos();
};
/* ----------------------------------------------------------- */
/* */
/* ----------------------------------------------------------- */
/**
* Triggers the value state "Error" for 1s, and resets the state to the previous one.
*
* @private
*/
MultiComboBox.prototype._showWrongValueVisualEffect = function() {
var oSuggestionsPopover = this._getSuggestionsPopover();
var sInitialValueStateText = this._sInitialValueStateText;
var sInitialValueState = this._sInitialValueState;
var sInvalidEntry = sInitialValueStateText || this._oRbC.getText("VALUE_STATE_ERROR");
var that = this;
if (sInitialValueState === ValueState.Error) {
return;
}
if (oSuggestionsPopover) {
oSuggestionsPopover.updateValueState(ValueState.Error, sInvalidEntry, true);
setTimeout(oSuggestionsPopover.updateValueState.bind(oSuggestionsPopover, that.getValueState(), sInvalidEntry, true), 1000);
}
if (!this.isPickerDialog()) {
this.setValueState(ValueState.Error);
this.setValueStateText(this.getValueStateText() || sInvalidEntry);
setTimeout(this["setValueState"].bind(this, sInitialValueState || ValueState.Error), 1000);
}
this._syncInputWidth(this.getAggregation("tokenizer"));
};
/**
* Triggers the value state "Error" when the item is already selected and enter is pressed.
*
* @private
*/
MultiComboBox.prototype._showAlreadySelectedVisualEffect = function() {
var sAlreadySelectedText = this._oRb.getText("VALUE_STATE_ERROR_ALREADY_SELECTED");
if (!this.getValue()) {
return;
}
var bAlreadySelected = !!this.getSelectedItems().filter(function(oItem) {
return oItem.getText().toLowerCase() === this.getValue().toLowerCase();
}, this).length;
var bNewSelection = this.getItems().filter(function(oItem) {
return oItem.getText().toLowerCase() === this.getValue().toLowerCase();
}, this).length;
if (bAlreadySelected) {
if (!this._bAlreadySelected) {
this._sInitialValueState = this.getValueState();
this._sInitialValueStateText = this.getValueStateText();
}
this._bAlreadySelected = true;
this.setValueStateText(sAlreadySelectedText);
this.setValueState("Error");
return;
} else if (bNewSelection) {
return;
} else {
this._showWrongValueVisualEffect();
}
};
MultiComboBox.prototype._hasShowSelectedButton = function () {
return true;
};
MultiComboBox.prototype.forwardEventHandlersToSuggPopover = function (oSuggPopover) {
ComboBoxBase.prototype.forwardEventHandlersToSuggPopover.apply(this, arguments);
oSuggPopover.setShowSelectedPressHandler(this._filterSelectedItems.bind(this));
};
/**
* <code>MultiComboBox</code> picker configuration
*
* @param {sap.m.Popover | sap.m.Dialog} oPicker Picker instance
* @protected
*/
MultiComboBox.prototype.configPicker = function (oPicker) {
var oRenderer = this.getRenderer(),
CSS_CLASS_MULTICOMBOBOX = oRenderer.CSS_CLASS_MULTICOMBOBOX;
oPicker.setHorizontalScrolling(false)
.addStyleClass(oRenderer.CSS_CLASS_COMBOBOXBASE + "Picker")
.addStyleClass(CSS_CLASS_MULTICOMBOBOX + "Picker")
.addStyleClass(CSS_CLASS_MULTICOMBOBOX + "Picker-CTX")
.attachBeforeOpen(this.onBeforeOpen, this)
.attachAfterOpen(this.onAfterOpen, this)
.attachBeforeClose(this.onBeforeClose, this)
.attachAfterClose(this.onAfterClose, this)
.addEventDelegate({
onBeforeRendering : this.onBeforeRenderingPicker,
onAfterRendering : this.onAfterRenderingPicker
}, this);
};
/**
* Configures the SuggestionsPopover internal list and attaches it's event handlers/delegates.
*
* @param {sap.m.List} oList The list instance to be configured
* @private
* @function
*/
MultiComboBox.prototype._configureList = function (oList) {
// overwrite the default page size of the list
// in order to be consistent with the other inputs
// page size is used for pageup and pagedown
var iPageSize = 10;
if (!oList) {
return;
}
// apply aria role="listbox" to List control
oList.applyAriaRole("listbox");
// configure the list
oList.setMode(ListMode.MultiSelect);
oList.setIncludeItemInSelection(true);
oList.setGrowingThreshold(iPageSize);
// attach event handlers
oList
.attachBrowserEvent("tap", this._handleItemTap, this)
.attachSelectionChange(this._handleSelectionLiveChange, this)
.attachItemPress(this._handleItemPress, this);
// attach event delegates
oList.addEventDelegate({
onAfterRendering: this.onAfterRenderingList,
onfocusin: this.onFocusinList
}, this);
this.getShowSelectAll() && this.createSelectAllHeaderToolbar(oList);
};
/**
* Modifies the suggestions dialog input
* @param {sap.m.Input} oInput The input
*
* @returns {sap.m.Input} The modified input control
* @private
* @ui5-restricted
*/
MultiComboBox.prototype._decoratePopupInput = function(oInput) {
ComboBoxBase.prototype._decoratePopupInput.apply(this, arguments);
if (!oInput || !oInput.isA(["sap.m.InputBase"])) {
return;
}
oInput.attachSubmit(function (oEvent) {
var sValue = oInput.getValue();
if (sValue) {
this.setValue(sValue);
this._selectItemByKey();
this.setValue(this._sOldInput);
this.close();
}
}.bind(this));
oInput.addEventDelegate({
// remove the type ahead when focus is not in the input
onfocusout: this._handleInputFocusOut
}, this);
oInput.attachChange(this._handleInnerInputChange.bind(this));
return oInput;
};
/**
* Handles dialog's OK button press.
*
* @private
*/
MultiComboBox.prototype._handleOkPress = function () {
ComboBoxBase.prototype._handleOkPress.apply(this, arguments);
if (this.getValue()) {
this._selectItemByKey();
}
};
/**
* Handles the picker input change.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
MultiComboBox.prototype._handleInnerInputChange = function (oEvent) {
if (oEvent.getParameter("value") === "") {
this._sOldInput = "";
this.clearFilter();
}
};
/**
* This hook method is called before the MultiComboBox is rendered.
*
* @protected
*/
MultiComboBox.prototype.onBeforeRendering = function() {
var bEditable = this.getEditable();
var oTokenizer = this.getAggregation("tokenizer");
var aItems = this.getItems();
ComboBoxBase.prototype.onBeforeRendering.apply(this, arguments);
this._bInitialSelectedKeysSettersCompleted = true;
oTokenizer.setEnabled(this.getEnabled());
oTokenizer.setEditable(bEditable);
this._updatePopoverBasedOnEditMode(bEditable);
if (!aItems.length) {
this._clearTokenizer();
}
if (this._getList()) {
this.syncPickerContent(true);
}
this.toggleSelectAllVisibility(this.getShowSelectAll());
// In case there is an old input, the picker is opened and there are items
// we need to return the previous state of the filtering as syncPickerContent
// will have removed it.
if (this._sOldInput && aItems.length && this.isOpen()) {
itemsVisibilityHandler(this.getItems(), this.filterItems({ value: this._sOldInput, items: aItems }));
// wait a tick so the setVisible call has replaced the DOM
setTimeout(this.highlightList.bind(this, this._sOldInput));
}
this._deregisterResizeHandler();
this._synchronizeSelectedItemAndKey();
this.setProperty("hasSelection", !!this.getSelectedItems().length);
if (!this._bAlreadySelected) {
this._sInitialValueStateText = this.getValueStateText();
}
if (this.getValueState() !== ValueState.Error) {
this._sInitialValueState = this.getValueState();
}
if (this.getShowClearIcon()) {
this._getClearIcon().setVisible(this.shouldShowClearIcon());
} else if (this._oClearIcon) {
this._getClearIcon().setVisible(false);
}
};
/**
* Creates picker if doesn't exist yet and sync with Control items
*
* @param {boolean} [bForceListSync] Force MultiComboBox to SuggestionPopover sync
* @protected
* @returns {sap.m.Dialog|sap.m.Popover} The picker instance
*/
MultiComboBox.prototype.syncPickerContent = function (bForceListSync) {
var aItems, oList;
var oPicker = this.getPicker();
var oTokenizer = this.getAggregation("tokenizer");
if (!oPicker) {
oPicker = this.createPicker(this.getPickerType());
this._updateSuggestionsPopoverValueState();
bForceListSync = true;
}
if (bForceListSync) {
oList = this._getList();
aItems = this.getEditable() ? this.getItems() : this.getSelectedItems();
this._synchronizeSelectedItemAndKey();
var aTokens = oTokenizer.getTokens();
var iFocusedIndex = aTokens.findIndex(function (oToken) {
return document.activeElement === oToken.getDomRef();
});
// prevent closing of popup on re-rendering
oList.destroyItems();
this._clearTokenizer();
this._fillList(aItems);
this.bShouldRestoreTokenizerFocus = iFocusedIndex > -1;
this.iFocusedIndex = iFocusedIndex;
// save focused index, and re-apply after rendering of the list
if (oList.getItemNavigation()) {
this._iFocusedIndex = oList.getItemNavigation().getFocusedIndex();
}
}
return oPicker;
};
/**
* Registers resize handler
*
* @private
*/
MultiComboBox.prototype._registerResizeHandler = function () {
assert(!this._iResizeHandlerId, "Resize handler already registered");
this._iResizeHandlerId = ResizeHandler.register(this, this._onResize.bind(this));
};
/**
* Deregisters resize handler
*
* @private
*/
MultiComboBox.prototype._deregisterResizeHandler = function () {
if (this._iResizeHandlerId) {
ResizeHandler.deregister(this._iResizeHandlerId);
this._iResizeHandlerId = null;
}
};
/**
* Handler for resizing
*
* @private
*/
MultiComboBox.prototype._onResize = function () {
var oTokenizer = this.getAggregation("tokenizer");
oTokenizer.setMaxWidth(this._calculateSpaceForTokenizer());
this._syncInputWidth(oTokenizer);
this._handleNMoreAccessibility();
};
/**
* This hook method is called after the MultiComboBox's Pop-up is rendered.
*
* @protected
*/
MultiComboBox.prototype.onAfterRenderingPicker = function() {
var fnOnAfterRenderingPopupType = this["_onAfterRendering" + this.getPickerType()];
var iInputWidth = this.getDomRef().getBoundingClientRect().width;
var sPopoverMaxWidth = getComputedStyle(this.getDomRef()).getPropertyValue("--sPopoverMaxWidth");
if (fnOnAfterRenderingPopupType) {
fnOnAfterRenderingPopupType.call(this);
}
if (iInputWidth <= parseInt(sPopoverMaxWidth) && !Device.system.phone) {
this.getPicker().getDomRef().style.setProperty("max-width", "40rem");
} else {
this.getPicker().getDomRef().style.setProperty("max-width", iInputWidth + "px");
}
};
/**
* This event handler will be called before the MultiComboBox Popup is opened.
*
* @private
*/
MultiComboBox.prototype.onBeforeOpen = function() {
ComboBoxBase.prototype.onBeforeOpen.apply(this, arguments);
var oSuggestionsPopover = this._getSuggestionsPopover();
var fnPickerTypeBeforeOpen = this["_onBeforeOpen" + this.getPickerType()],
oDomRef = this.getFocusDomRef();
if (oDomRef) {
// expose a parent/child contextual relationship to assistive technologies,
// notice that the "aria-controls" attribute is set when the popover opened.
oDomRef.setAttribute("aria-controls", this.getPicker().getId());
}
// add the active state to the MultiComboBox's field
this.addContent();
this._aInitiallySelectedItems = this.getSelectedItems();
this._synchronizeSelectedItemAndKey();
if (fnPickerTypeBeforeOpen) {
fnPickerTypeBeforeOpen.call(this);
}
oSuggestionsPopover.resizePopup(this);
};
/**
* This event handler will be called after the MultiComboBox's Pop-up is opened.
*
* @private
*/
MultiComboBox.prototype.onAfterOpen = function() {
var oDomRef = this.getFocusDomRef(),
aValueStateLinks = this.getValueStateLinks();
oDomRef && this.getFocusDomRef().setAttribute("aria-expanded", "true");
this._bPickerIsOpening = false;
// reset the initial focus back to the input
if (!this.isPlatformTablet()) {
this.getPicker().setInitialFocus(this);
}
// If there are links in the value state take the links out of
// the tab chain by default. They will be tabbable only if the focus in the value state message
aValueStateLinks.forEach(function(oLink) {
oLink.addDelegate({
onAfterRendering: function() {
if (this.getFocusDomRef()) {
this.getFocusDomRef().setAttribute("tabindex", "-1");
}
}
}, oLink);
});
// close error message when the list is open, otherwise the list can be covered by the message
this.closeValueStateMessage();
};
/**
* This event handler will be called before the MultiComboBox's Pop-up is closed.
*
*/
MultiComboBox.prototype.onBeforeClose = function () {
ComboBoxBase.prototype.onBeforeClose.apply(this, arguments);
};
/**
* This event handler will be called after the MultiComboBox's Pop-up is closed.
*
* @private
*/
MultiComboBox.prototype.onAfterClose = function() {
var bUseNarrow = !jQuery.contains(this.getDomRef(), document.activeElement) || this.isPickerDialog(),
oDomRef = this.getFocusDomRef();
oDomRef && this.getFocusDomRef().setAttribute("aria-expanded", "false");
// remove the active state of the MultiComboBox's field
this.toggleIconPressedStyle(false);
// Show all items when the list will be opened next time
this.clearFilter();
// resets or not the value of the input depending on the event (enter does not clear the value)
!this.isComposingCharacter() && !this._bPreventValueRemove && this.setValue("");
// clear old values
this._sOldValue = "";
this._sOldInput = "";
// clear the typed in value, since SP does not clean it itself,
// if no autocomplete property is present
this._getSuggestionsPopover()._sTypedInValue = "";
if (this.isPickerDialog()) {
// reset the value state after the dialog is closed
this.getPickerTextField().setValue("");
this.getFilterSelectedButton() && this.getFilterSelectedButton().setPressed(false);
}
this.fireSelectionFinish({
selectedItems: this.getSelectedItems()
});
this.getAggregation("tokenizer").setRenderMode(bUseNarrow ? TokenizerRenderMode.Narrow : TokenizerRenderMode.Loose);
// show value state message when focus is in the input field
if (this.getValueState() == ValueState.Error && document.activeElement === this.getFocusDomRef()) {
this.selectText(0, this.getValue().length);
}
};
/**
* Called before the Dialog is opened.
*
* @private
*/
MultiComboBox.prototype._onBeforeOpenDialog = function() {};
/**
* This event handler will be called before the control's picker popover is opened.
*
* @private
*/
MultiComboBox.prototype._onBeforeOpenDropdown = function() {
var oPopover = this.getPicker(),
oDomRef = this.getDomRef(),
sWidth;
if (oDomRef && oPopover) {
sWidth = (oDomRef.offsetWidth / parseFloat(library.BaseFontSize)) + "rem";
oPopover.setContentMinWidth(sWidth);
}
};
/**
* Gets the filter selected toggle button for the control's picker.
*
* @returns {sap.m.ToggleButton} The button's instance
* @private
*/
MultiComboBox.prototype.getFilterSelectedButton = function () {
return this._getSuggestionsPopover().getFilterSelectedButton();
};
/**
* Filters visible selected items
* @param {jQuery.Event} oEvent The event object
* @param {boolean} bForceShowSelected Should the selected items be shown
* @returns {void}
* @private
*/
MultiComboBox.prototype._filterSelectedItems = function (oEvent, bForceShowSelected) {
var oSource = oEvent.oSource, oListItem, bMatch,
sValue = this.getPickerTextField() ? this.getPickerTextField().getValue() : "",
bShowSelectedOnly = (oSource && oSource.getPressed && oSource.getPressed()) || bForceShowSelected,
aVisibleItems = ListHelpers.getVisibleItems(this.getItems()),
aItems = this.getItems(),
aSelectedItems = this.getSelectedItems(),
oLastGroupListItem = null;
if (bShowSelectedOnly) {
aVisibleItems.forEach(function(oItem) {
bMatch = aSelectedItems.indexOf(oItem) > -1 ? true : false;
oListItem = ListHelpers.getListItem(oItem);
if (!oListItem) {
return;
}
if (oListItem.isA("sap.m.GroupHeaderListItem")) {
oListItem.setVisible(false);
oLastGroupListItem = oListItem;
} else {
oListItem.setVisible(bMatch);
if (bMatch && oLastGroupListItem) {
oLastGroupListItem.setVisible(true);
}
}
}, this);
} else {
itemsVisibilityHandler(this.getItems(), this.filterItems({value: sValue, items: aItems}));
}
this.manageSelectAllCheckBoxState();
};
/**
* Reverts the selection as before opening the picker.
*
* @private
*/
MultiComboBox.prototype.revertSelection = function () {
this.setSelectedItems(this._aInitiallySelectedItems);
};
/**
* Update and synchronize "selectedItems" association and the "selectedItems" in the List.
*
* @param {o