@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,377 lines (1,157 loc) • 38.9 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.
*/
// Provides control sap.m.ListItemBase.
sap.ui.define([
"sap/ui/base/DataType",
"sap/ui/events/KeyCodes",
"sap/ui/model/BindingMode",
"sap/ui/Device",
"sap/ui/core/library",
"sap/ui/core/Control",
"sap/ui/core/IconPool",
"sap/ui/core/Icon",
"sap/ui/core/InvisibleText",
"sap/ui/core/theming/Parameters",
"./library",
"./Button",
"./CheckBox",
"./RadioButton",
"./ListItemBaseRenderer",
"sap/base/strings/capitalize",
"sap/ui/thirdparty/jquery",
"sap/ui/core/Lib",
// jQuery custom selectors ":sapTabbable", ":sapFocusable"
"sap/ui/dom/jquery/Selectors"
],
function(
DataType,
KeyCodes,
BindingMode,
Device,
coreLibrary,
Control,
IconPool,
Icon,
InvisibleText,
ThemeParameters,
library,
Button,
CheckBox,
RadioButton,
ListItemBaseRenderer,
capitalize,
jQuery,
Library
) {
"use strict";
// shortcut for sap.m.ListKeyboardMode
var ListKeyboardMode = library.ListKeyboardMode;
// shortcut for sap.m.ListMode
var ListMode = library.ListMode;
// shortcut for sap.m.ListType
var ListItemType = library.ListType;
// shortcut for sap.m.ButtonType
var ButtonType = library.ButtonType;
// shortcut for sap.ui.core.MessageType
var MessageType = coreLibrary.MessageType;
/**
* Constructor for a new ListItemBase.
*
* @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
* ListItemBase contains the base features of all specific list items.
* <b>Note:</b> If not mentioned otherwise in the individual subclasses, list items must only be used in the <code>items</code> aggregation of <code>sap.m.ListBase</code> controls.
* @extends sap.ui.core.Control
*
* @author SAP SE
* @version 1.117.4
*
* @constructor
* @public
* @alias sap.m.ListItemBase
*/
var ListItemBase = Control.extend("sap.m.ListItemBase", /** @lends sap.m.ListItemBase.prototype */ {
metadata : {
library : "sap.m",
properties : {
/**
* Defines the visual indication and behavior of the list items, e.g. <code>Active</code>, <code>Navigation</code>, <code>Detail</code>.
*/
type : {type : "sap.m.ListType", group : "Misc", defaultValue : ListItemType.Inactive},
/**
* Whether the control should be visible on the screen. If set to false, a placeholder is rendered instead of the real control.
*/
visible : {type : "boolean", group : "Appearance", defaultValue : true},
/**
* Activates the unread indicator for the list item, if set to <code>true</code>.
* <b>Note:</b> This flag is ignored when the <code>showUnread</code> property of the parent is set to <code>false</code>.
*/
unread : {type : "boolean", group : "Misc", defaultValue : false},
/**
* Defines the selected state of the list items.
* <b>Note:</b> Binding the <code>selected</code> property in single selection modes may cause unwanted results if you have more than one selected items in your binding.
*/
selected : {type : "boolean", defaultValue : false},
/**
* Defines the counter value of the list items.
*/
counter : {type : "int", group : "Misc", defaultValue : null},
/**
* Defines the highlight state of the list items.
*
* Valid values for the <code>highlight</code> property are values of the enumerations {@link sap.ui.core.MessageType} or
* {@link sap.ui.core.IndicationColor}.
*
* Accessibility support is provided through the associated {@link sap.m.ListItemBase#setHighlightText highlightText} property.
* If the <code>highlight</code> property is set to a value of {@link sap.ui.core.MessageType}, the <code>highlightText</code>
* property does not need to be set because a default text is used. However, the default text can be overridden by setting the
* <code>highlightText</code> property.
* In all other cases the <code>highlightText</code> property must be set.
*
* @since 1.44.0
*/
highlight : {type : "string", group : "Appearance", defaultValue : "None"},
/**
* Defines the semantics of the {@link sap.m.ListItemBase#setHighlight highlight} property for accessibility purposes.
*
* @since 1.62
*/
highlightText : {type : "string", group : "Misc", defaultValue : ""},
/**
* The navigated state of the list item.
*
* If set to <code>true</code>, a navigation indicator is displayed at the end of the list item.
* <b>Note:</b> This property must be set for <b>one</b> list item only.
*
* @since 1.72
*/
navigated : {type : "boolean", group : "Appearance", defaultValue : false}
},
associations: {
/**
* Association to controls / ids which label this control (see WAI-ARIA attribute aria-labelledby).
* @since 1.28.0
*/
ariaLabelledBy: { type: "sap.ui.core.Control", multiple: true, singularName: "ariaLabelledBy" }
},
events : {
/**
* Fires when the user taps on the control.
* @deprecated Since version 1.20.0. Instead, use <code>press</code> event.
*/
tap : {deprecated: true},
/**
* Fires when the user taps on the detail button of the control.
* @deprecated Since version 1.20.0. Instead, use <code>detailPress</code> event.
*/
detailTap : {deprecated: true},
/**
* Fires when the user clicks on the control.
* <b>Note:</b> This event is not fired when the parent <code>mode</code> is <code>SingleSelectMaster</code> or when the <code>includeItemInSelection</code> property is set to <code>true</code>.
* If there is an interactive element that handles its own <code>press</code> event then the list item's <code>press</code> event is not fired.
* Also see {@link sap.m.ListBase#attachItemPress}.
*/
press : {},
/**
* Fires when the user clicks on the detail button of the control.
*/
detailPress : {}
},
designtime: "sap/m/designtime/ListItemBase.designtime",
dnd: true
},
renderer: ListItemBaseRenderer
});
ListItemBase.getAccessibilityText = function(oControl, bDetectEmpty, bHeaderAnnouncement) {
var oBundle = Library.getResourceBundleFor("sap.m");
if (!oControl || !oControl.getVisible || !oControl.getVisible()) {
return bDetectEmpty ? oBundle.getText("CONTROL_EMPTY") : "";
}
var oAccInfo;
if (oControl.getAccessibilityInfo) {
oAccInfo = oControl.getAccessibilityInfo();
}
if (!oAccInfo || !oControl.getAccessibilityInfo) {
oAccInfo = this.getDefaultAccessibilityInfo(oControl.getDomRef());
}
oAccInfo = jQuery.extend({
type: "",
description: "",
children: []
}, oAccInfo);
var sText = oAccInfo.type + " " + oAccInfo.description + " ",
sTooltip = oControl.getTooltip_AsString();
if (oAccInfo.required === true) {
sText += oBundle.getText(bHeaderAnnouncement ? "CONTROL_IN_COLUMN_REQUIRED" : "ELEMENT_REQUIRED") + " ";
}
if (oAccInfo.enabled === false) {
sText += oBundle.getText("CONTROL_DISABLED") + " ";
}
if (oAccInfo.editable === false) {
sText += oBundle.getText("CONTROL_READONLY") + " ";
}
if (!oAccInfo.type && sTooltip && sText.indexOf(sTooltip) == -1) {
sText = sTooltip + " " + sText;
}
oAccInfo.children.forEach(function(oChild) {
sText += ListItemBase.getAccessibilityText(oChild) + " ";
});
sText = sText.trim();
if (bDetectEmpty && !sText) {
sText = oBundle.getText("CONTROL_EMPTY");
}
return sText;
};
ListItemBase.getDefaultAccessibilityInfo = function(oDomRef) {
if (!oDomRef) {
return null;
}
var aText = [],
Node = window.Node,
NodeFilter = window.NodeFilter,
oTreeWalker = document.createTreeWalker(oDomRef, NodeFilter.SHOW_TEXT + NodeFilter.SHOW_ELEMENT);
while (oTreeWalker.nextNode()) {
var oNode = oTreeWalker.currentNode;
if (oNode.nodeType === Node.TEXT_NODE) {
var sText = (oNode.nodeValue || "").trim();
if (sText) {
aText.push(sText);
}
}
}
return {
description: aText.join(" ")
};
};
// icon URI configuration
ListItemBase.prototype.DetailIconURI = IconPool.getIconURI("edit");
ListItemBase.prototype.NavigationIconURI = IconPool.getIconURI("slim-arrow-right");
// defines the root tag name for rendering purposes
ListItemBase.prototype.TagName = "li";
// internal active state of the listitem
ListItemBase.prototype.init = function() {
this._active = false;
this._bGroupHeader = false;
this._bNeedsHighlight = false;
this._bNeedsNavigated = false;
};
ListItemBase.prototype.onBeforeRendering = function() {
this._oDomRef = this.getDomRef();
};
ListItemBase.prototype.onAfterRendering = function() {
if (!this._oDomRef || this._oDomRef !== this.getDomRef()) {
this.informList("DOMUpdate", true);
}
this._oDomRef = undefined;
this._checkHighlight();
this._checkNavigated();
};
ListItemBase.prototype.invalidate = function() {
if (!this.bOutput) {
return;
}
Control.prototype.invalidate.apply(this, arguments);
};
/*
* Returns the binding context path via checking the named model of parent
*
* @protected
* @since 1.16.3
*/
ListItemBase.prototype.getBindingContextPath = function(sModelName) {
var oList = this.getList();
if (oList && !sModelName) {
sModelName = (oList.getBindingInfo("items") || {}).model;
}
var oContext = this.getBindingContext(sModelName);
if (oContext) {
return oContext.getPath();
}
};
/*
* Returns whether selected property is two-way bound or not
* @protected
*/
ListItemBase.prototype.isSelectedBoundTwoWay = function() {
var oBinding = this.getBinding("selected");
if (oBinding && oBinding.getBindingMode() == BindingMode.TwoWay) {
return true;
}
};
/*
* Returns the responsible list control
*
* @returns {sap.m.ListBase|undefined}
* @protected
*/
ListItemBase.prototype.getList = function() {
var oParent = this.getParent();
if (oParent && oParent.isA("sap.m.ListBase")) {
return oParent;
}
};
/*
* Returns the property of the responsible list container according to given parameter.
*
* @param {string} sProperty property name
* @param {*} [vFallbackValue] fallback value when list is not found
* @return {*}
* @protected
*/
ListItemBase.prototype.getListProperty = function(sProperty, vFallbackValue) {
var oList = this.getList();
if (oList) {
sProperty = capitalize(sProperty);
return oList["get" + sProperty]();
}
return vFallbackValue;
};
/*
* Informs the responsible list for item events
*
* @param {string} sEvent the name of the event
* @param {*} [vParam1] first additional parameter
* @param {*} [vParam2] second additional parameter
* @protected
*/
ListItemBase.prototype.informList = function(sEvent, vParam1, vParam2) {
var oList = this.getList();
if (oList) {
var sMethod = "onItem" + sEvent;
if (oList[sMethod]) {
oList[sMethod](this, vParam1, vParam2);
}
}
};
ListItemBase.prototype.informSelectedChange = function(bSelected) {
var oList = this.getList();
if (oList) {
oList.onItemSelectedChange(this, bSelected);
this.bSelectedDelayed = undefined;
} else {
this.bSelectedDelayed = bSelected;
}
};
ListItemBase.prototype.getAccessibilityType = function(oBundle) {
return this.getListProperty("ariaRole") == "list" ? oBundle.getText("ACC_CTR_TYPE_LISTITEM") : "";
};
ListItemBase.prototype.getGroupAnnouncement = function() {
return this.$().prevAll(".sapMGHLI:first").text();
};
ListItemBase.prototype.getAccessibilityDescription = function(oBundle) {
var aOutput = [],
sType = this.getType(),
sHighlight = this.getHighlight(),
bIsTree = this.getListProperty("ariaRole") === "tree";
if (this.getSelected() && !bIsTree) {
aOutput.push(oBundle.getText("LIST_ITEM_SELECTED"));
}
if (sHighlight !== MessageType.None) {
var sHighlightText = this.getHighlightText();
if (sHighlight in MessageType && !sHighlightText) {
sHighlightText = oBundle.getText("LIST_ITEM_STATE_" + sHighlight.toUpperCase());
}
aOutput.push(sHighlightText);
}
if (this.getUnread() && this.getListProperty("showUnread")) {
aOutput.push(oBundle.getText("LIST_ITEM_UNREAD"));
}
if (this.getCounter()) {
aOutput.push(oBundle.getText("LIST_ITEM_COUNTER", this.getCounter()));
}
if (sType == ListItemType.Navigation) {
aOutput.push(oBundle.getText("LIST_ITEM_NAVIGATION"));
} else {
if (sType == ListItemType.Detail || sType == ListItemType.DetailAndActive) {
aOutput.push(oBundle.getText("LIST_ITEM_DETAIL"));
}
if (sType == ListItemType.Active || sType == ListItemType.DetailAndActive) {
aOutput.push(oBundle.getText("LIST_ITEM_ACTIVE"));
}
}
var sGroupAnnouncement = this.getGroupAnnouncement() || "";
if (sGroupAnnouncement) {
aOutput.push(sGroupAnnouncement);
}
if (this.getContentAnnouncement) {
var sContentAnnouncement = (this.getContentAnnouncement(oBundle) || "").trim();
sContentAnnouncement && aOutput.push(sContentAnnouncement);
}
if (this.getListProperty("ariaRole") !== "listbox" && !bIsTree && this.isSelectable() && !this.getSelected()) {
aOutput.push(oBundle.getText("LIST_ITEM_NOT_SELECTED"));
}
//The dot is added so the screenreader can pause
return aOutput.join(" . ");
};
ListItemBase.prototype.getAccessibilityInfo = function() {
var oBundle = Library.getResourceBundleFor("sap.m");
return {
type: this.getAccessibilityType(oBundle),
description: this.getAccessibilityDescription(oBundle),
focusable: true
};
};
/**
* Returns the accessibility announcement for the content.
*
* Hook for the subclasses.
*
* @returns {string}
* @protected
* @name sap.m.ListItemBase.prototype.getContentAnnouncement
* @function
*/
/*
* Returns the mode of the current item according to list mode
* Subclasses can overwrite this if item should not have any mode
* Default empty mode is used when list mode is not yet known
*
* @returns {sap.m.ListMode|""}
* @protected
*/
ListItemBase.prototype.getMode = function() {
return this.getListProperty("mode", "");
};
/*
* Updates the accessibility state of the control.
*
* @param {Object} [mAccessibility] a map of accessibility properties
* @protected
*/
ListItemBase.prototype.updateAccessibilityState = function(mAccessibility) {
var $This = this.$();
if (!$This.length) {
return;
}
var $Items = $This.parent().children(".sapMLIB");
$This.attr(jQuery.extend({
"aria-setsize": $Items.length,
"aria-posinset": $Items.index($This) + 1
}, mAccessibility));
};
/**
* Returns the delete icon when mode is Delete
*
* @return {sap.ui.core.Icon}
* @private
*/
ListItemBase.prototype.getDeleteControl = function(bCreateIfNotExist) {
if (!bCreateIfNotExist || this._oDeleteControl) {
return this._oDeleteControl;
}
if (!this.DeleteIconURI) {
ListItemBase.prototype.DeleteIconURI = IconPool.getIconURI(
ThemeParameters.get({name: "_sap_m_ListItemBase_DeleteIcon"}) || "decline"
);
}
this._oDeleteControl = new Button({
id: this.getId() + "-imgDel",
icon: this.DeleteIconURI,
type: ButtonType.Transparent,
tooltip: Library.getResourceBundleFor("sap.m").getText("LIST_ITEM_DELETE")
}).addStyleClass("sapMLIBIconDel sapMLIBSelectD").setParent(this, null, true).attachPress(function(oEvent) {
this.informList("Delete");
}, this);
this._oDeleteControl._bExcludeFromTabChain = true;
// prevent disabling of internal controls by the sap.ui.core.EnabledPropagator
this._oDeleteControl.useEnabledPropagator(false);
return this._oDeleteControl;
};
ListItemBase.prototype.onThemeChanged = function() {
ListItemBase.prototype.DeleteIconURI = IconPool.getIconURI(
ThemeParameters.get({name: "_sap_m_ListItemBase_DeleteIcon"})
);
if (this._oDeleteControl) {
this._oDeleteControl.setIcon(this.DeleteIconURI);
}
};
/**
* Returns the detail icon when item type is Detail|DetailAndActive
*
* @return {sap.ui.core.Icon}
* @private
*/
ListItemBase.prototype.getDetailControl = function(bCreateIfNotExist) {
if (!bCreateIfNotExist || this._oDetailControl) {
return this._oDetailControl;
}
this._oDetailControl = new Button({
id: this.getId() + "-imgDet",
icon: this.DetailIconURI,
type: ButtonType.Transparent,
tooltip: Library.getResourceBundleFor("sap.m").getText("LIST_ITEM_EDIT")
}).addStyleClass("sapMLIBType sapMLIBIconDet").setParent(this, null, true).attachPress(function() {
this.fireDetailTap();
this.fireDetailPress();
}, this);
this._oDetailControl._bExcludeFromTabChain = true;
// prevent disabling of internal controls by the sap.ui.core.EnabledPropagator
this._oDetailControl.useEnabledPropagator(false);
return this._oDetailControl;
};
/**
* Returns the navigation icon when item type is Navigation
*
* @return {sap.ui.core.Icon}
* @private
*/
ListItemBase.prototype.getNavigationControl = function(bCreateIfNotExist) {
if (!bCreateIfNotExist || this._oNavigationControl) {
return this._oNavigationControl;
}
this._oNavigationControl = new Icon({
id: this.getId() + "-imgNav",
src: this.NavigationIconURI,
useIconTooltip: false,
noTabStop: true
}).setParent(this, null, true).addStyleClass("sapMLIBType sapMLIBImgNav");
return this._oNavigationControl;
};
/**
* Returns RadioButton control when mode is one of Single Selection type
*
* @return {sap.m.RadioButton}
* @private
*/
ListItemBase.prototype.getSingleSelectControl = function(bCreateIfNotExist) {
if (!bCreateIfNotExist || this._oSingleSelectControl) {
bCreateIfNotExist && this._oSingleSelectControl.setSelected(this.getSelected());
return this._oSingleSelectControl;
}
this._oSingleSelectControl = new RadioButton({
id: this.getId() + "-selectSingle",
groupName: this.getListProperty("id") + "_selectGroup",
activeHandling: false,
selected: this.getSelected(),
ariaLabelledBy: InvisibleText.getStaticId("sap.m", "LIST_ITEM_SELECTION")
}).addStyleClass("sapMLIBSelectS").setParent(this, null, true).setTabIndex(-1).attachSelect(function(oEvent) {
var bSelected = oEvent.getParameter("selected");
this.setSelected(bSelected);
this.informList("Select", bSelected);
}, this);
// prevent disabling of internal controls by the sap.ui.core.EnabledPropagator
this._oSingleSelectControl.useEnabledPropagator(false);
return this._oSingleSelectControl;
};
/**
* Returns CheckBox control when mode is MultiSelection
*
* @return {sap.m.CheckBox}
* @private
*/
ListItemBase.prototype.getMultiSelectControl = function(bCreateIfNotExist) {
if (!bCreateIfNotExist || this._oMultiSelectControl) {
bCreateIfNotExist && this._oMultiSelectControl.setSelected(this.getSelected());
return this._oMultiSelectControl;
}
this._oMultiSelectControl = new CheckBox({
id: this.getId() + "-selectMulti",
activeHandling: false,
selected: this.getSelected(),
ariaLabelledBy: InvisibleText.getStaticId("sap.m", "LIST_ITEM_SELECTION")
}).addStyleClass("sapMLIBSelectM").setParent(this, null, true).setTabIndex(-1).addEventDelegate({
onkeydown: function (oEvent) {
this.informList("KeyDown", oEvent);
},
onkeyup: function (oEvent) {
this.informList("KeyUp", oEvent);
}
}, this).attachSelect(function(oEvent) {
var bSelected = oEvent.getParameter("selected");
this.setSelected(bSelected);
this.informList("Select", bSelected);
}, this);
// prevent disabling of internal controls by the sap.ui.core.EnabledPropagator
this._oMultiSelectControl.useEnabledPropagator(false);
return this._oMultiSelectControl;
};
/**
* Returns responsible control depends on the mode
*
* @returns {sap.ui.core.Control}
* @private
*/
ListItemBase.prototype.getModeControl = function(bCreateIfNotExist) {
var sMode = this.getMode();
if (!sMode || sMode == ListMode.None) {
return;
}
if (sMode == ListMode.Delete) {
return this.getDeleteControl(bCreateIfNotExist);
}
if (sMode == ListMode.MultiSelect) {
return this.getMultiSelectControl(bCreateIfNotExist);
}
return this.getSingleSelectControl(bCreateIfNotExist);
};
/**
* Returns item type icon
*
* @returns {sap.ui.core.Icon}
* @private
*/
ListItemBase.prototype.getTypeControl = function(bCreateIfNotExist) {
var sType = this.getType();
if (sType == ListItemType.Detail || sType == ListItemType.DetailAndActive) {
return this.getDetailControl(bCreateIfNotExist);
}
if (sType == ListItemType.Navigation) {
return this.getNavigationControl(bCreateIfNotExist);
}
};
/**
* Destroys generated mode/type controls
*
* @param {string[]} aControls array of control names
* @private
*/
ListItemBase.prototype.destroyControls = function(aControls) {
aControls.forEach(function(sControl) {
sControl = "_o" + sControl + "Control";
if (this[sControl]) {
this[sControl].destroy("KeepDom");
this[sControl] = null;
}
}, this);
};
/**
* Determines whether item has any action or not.
* @private
*/
ListItemBase.prototype.isActionable = function(bCheckDevice) {
if (bCheckDevice && !Device.system.desktop) {
return false;
}
return this.isIncludedIntoSelection() || (
this.getType() != ListItemType.Inactive &&
this.getType() != ListItemType.Detail
);
};
ListItemBase.prototype.exit = function() {
this._oDomRef = null;
this._oLastFocused = null;
this._checkHighlight(false);
this._checkNavigated(false);
this.setActive(false);
this.destroyControls([
"Delete",
"SingleSelect",
"MultiSelect",
"Detail",
"Navigation"
]);
};
ListItemBase.prototype.setHighlight = function(sValue) {
if (sValue == null) {
sValue = MessageType.None;
} else if (!DataType.getType("sap.ui.core.MessageType").isValid(sValue) && !DataType.getType("sap.ui.core.IndicationColor").isValid(sValue)) {
throw new Error('"' + sValue + '" is not a value of the enums sap.ui.core.MessageType or sap.ui.core.IndicationColor for property "highlight" of ' + this);
}
return this.setProperty("highlight", sValue);
};
/**
* Determines whether item is selectable or not.
* By default, when item should be in selectable mode
*
* Subclasses can overwrite in case of unselectable item.
* @returns {boolean}
* @private
*/
ListItemBase.prototype.isSelectable = function() {
var sMode = this.getMode();
return !(sMode == ListMode.None || sMode == ListMode.Delete);
};
ListItemBase.prototype.getSelected = function() {
if (this.isSelectable()) {
return this.getProperty("selected");
}
return false;
};
/**
* Returns the state of the item selection as a boolean
*
* @public
* @returns {boolean}
* @deprecated Since version 1.10.2.
* API Change makes this method unnecessary. Use the {@link #getSelected} method instead.
* @function
*/
ListItemBase.prototype.isSelected = ListItemBase.prototype.getSelected;
ListItemBase.prototype.setSelected = function(bSelected, bDontNotifyParent) {
// do not handle when item is not selectable or in same status
bSelected = this.validateProperty("selected", bSelected);
if (!this.isSelectable() || bSelected == this.getSelected()) {
return this;
}
// notify parent about the selection first
if (!bDontNotifyParent) {
this.informSelectedChange(bSelected);
}
// update the selection control status
var oSelectionControl = this.getModeControl();
if (oSelectionControl) {
oSelectionControl.setSelected(bSelected);
}
// run the hook to update dom state
this.updateSelectedDOM(bSelected, this.$());
// set the property and do not invalidate
this.setProperty("selected", bSelected, true);
return this;
};
// Updates the selected state of the DOM
ListItemBase.prototype.updateSelectedDOM = function(bSelected, $This) {
$This.toggleClass("sapMLIBSelected", bSelected);
if ($This.attr("role") !== "listitem") {
$This.attr("aria-selected", bSelected);
}
};
ListItemBase.prototype.setParent = function(oParent) {
if (!oParent) {
this.informList("Removed");
}
Control.prototype.setParent.apply(this, arguments);
this.informList("Inserted", this.bSelectedDelayed);
return this;
};
ListItemBase.prototype.setBindingContext = function() {
Control.prototype.setBindingContext.apply(this, arguments);
this.informList("BindingContextSet");
return this;
};
/**
* Determines whether group header item or not.
*
* @return {boolean}
*/
ListItemBase.prototype.isGroupHeader = function() {
return this._bGroupHeader;
};
/**
* This gets called from the ListBase for the GroupHeader items to inform the connected sub items
*
* @param {sap.m.ListItemBase} oLI The list item
*/
ListItemBase.prototype.setGroupedItem = function(oLI) {
this._aGroupedItems = this._aGroupedItems || [];
this._aGroupedItems.push(oLI.getId());
};
ListItemBase.prototype.getGroupedItems = function() {
return this._aGroupedItems;
};
/**
* Determines whether item is in SingleSelectMaster mode or
* other selection modes when includeItemInSelection is true
*
* @return {boolean}
*/
ListItemBase.prototype.isIncludedIntoSelection = function() {
if (!this.isSelectable()) {
return false;
}
var sMode = this.getMode();
return sMode == ListMode.SingleSelectMaster || (
this.getListProperty("includeItemInSelection") && (
sMode == ListMode.SingleSelectLeft ||
sMode == ListMode.SingleSelect ||
sMode == ListMode.MultiSelect
)
);
};
// informs the list when item's highlight is changed
ListItemBase.prototype._checkHighlight = function(bNeedsHighlight) {
if (bNeedsHighlight == undefined) {
bNeedsHighlight = (this.getVisible() && this.getHighlight() != MessageType.None);
}
if (this._bNeedsHighlight != bNeedsHighlight) {
this._bNeedsHighlight = bNeedsHighlight;
this.informList("HighlightChange", bNeedsHighlight);
}
};
ListItemBase.prototype._checkNavigated = function(bNeedsNavigated) {
if (bNeedsNavigated == undefined) {
bNeedsNavigated = (this.getVisible() && this.getNavigated());
}
if (this._bNeedsNavigated != bNeedsNavigated) {
this._bNeedsNavigated = bNeedsNavigated;
this.informList("NavigatedChange", bNeedsNavigated);
}
};
/**
* Determines whether item needs icon to render type or not
*
* @return {boolean}
*/
ListItemBase.prototype.hasActiveType = function() {
var sType = this.getType();
return (sType == ListItemType.Active ||
sType == ListItemType.Navigation ||
sType == ListItemType.DetailAndActive);
};
ListItemBase.prototype.setActive = function(bActive) {
if (bActive == this._active) {
return this;
}
if (bActive && this.getListProperty("activeItem")) {
return this;
}
var $This = this.$();
this._active = bActive;
this._activeHandling($This);
if (this.getType() == ListItemType.Navigation) {
this._activeHandlingNav($This);
}
if (bActive) {
this._activeHandlingInheritor($This);
} else {
this._inactiveHandlingInheritor($This);
}
this.informList("ActiveChange", bActive);
};
/**
* Detect text selection.
*
* @param {HTMLElement} oDomRef DOM element of the control
* @returns {boolean} true if text selection is done within the control else false
* @private
*/
ListItemBase.detectTextSelection = function(oDomRef) {
var oSelection = window.getSelection(),
sTextSelection = oSelection.toString().replace("\n", "");
return sTextSelection && (oDomRef !== oSelection.focusNode && oDomRef.contains(oSelection.focusNode));
};
ListItemBase.prototype.ontap = function(oEvent) {
// do not handle already handled events
if (this._eventHandledByControl) {
return oEvent.setMarked();
}
// do not handle in case of text selection within the list item
if (ListItemBase.detectTextSelection(this.getDomRef())) {
return;
}
// if includeItemInSelection all tap events will be used for the mode select and delete
// SingleSelectMaster always behaves like includeItemInSelection is set
if (this.isIncludedIntoSelection()) {
// update selected property
if (this.getMode() == ListMode.MultiSelect) {
this.setSelected(!this.getSelected());
this.informList("Select", this.getSelected());
} else if (!this.getSelected()) {
this.setSelected(true);
this.informList("Select", true);
}
} else if (this.hasActiveType()) {
// if a fast tap happens deactivate the touchstart/touchend timers and their logic
window.clearTimeout(this._timeoutIdStart);
window.clearTimeout(this._timeoutIdEnd);
// active feedback
this.setActive(true);
// even though the tabindex=-1, list items are not focusable on iPhone
if (Device.os.ios) {
this.focus();
}
setTimeout(function() {
this.setActive(false);
}.bind(this), 180);
setTimeout(function() {
this.fireTap();
this.firePress();
}.bind(this), 0);
}
// tell the parent, item is pressed
this.informList("Press", oEvent.srcControl);
};
ListItemBase.prototype.ontouchstart = function(oEvent) {
this._eventHandledByControl = oEvent.isMarked();
var oTargetTouch = oEvent.targetTouches[0];
this._touchedY = oTargetTouch.clientY;
this._touchedX = oTargetTouch.clientX;
// active handling if not handled already by control
// several fingers could be used
if (this._eventHandledByControl ||
oEvent.touches.length != 1 ||
!this.hasActiveType()) {
if (this.getListProperty("includeItemInSelection") && this.getList()._mRangeSelection) {
// prevet text selection when rangeSelection is detected
oEvent.preventDefault();
}
return;
}
// timeout regarding active state when scrolling
this._timeoutIdStart = setTimeout(function() {
this.setActive(true);
}.bind(this), 100);
};
// handle touchmove to prevent active state when scrolling
ListItemBase.prototype.ontouchmove = function(oEvent) {
if ((this._active || this._timeoutIdStart) &&
(Math.abs(this._touchedY - oEvent.targetTouches[0].clientY) > 10 || Math.abs(this._touchedX - oEvent.targetTouches[0].clientX) > 10)) {
// there is movement and therefore no tap...remove active styles
clearTimeout(this._timeoutIdStart);
this._timeoutIdStart = null;
this._timeoutIdEnd = null;
this.setActive(false);
}
};
ListItemBase.prototype.ontouchend = function(oEvent) {
if (this.hasActiveType()) {
this._timeoutIdEnd = setTimeout(function() {
this.setActive(false);
}.bind(this), 100);
}
};
// During native scrolling: Chrome sends touchcancel and no touchend thereafter
ListItemBase.prototype.ontouchcancel = ListItemBase.prototype.ontouchend;
// active handling should be removed when dragging an item is over
ListItemBase.prototype.ondragend = ListItemBase.prototype.ontouchend;
// toggle active styles for navigation items
ListItemBase.prototype._activeHandlingNav = function() {};
// hook method for active handling...inheritors should overwrite this method
ListItemBase.prototype._activeHandlingInheritor = function() {};
// hook method for inactive handling...inheritors should overwrite this method
ListItemBase.prototype._inactiveHandlingInheritor = function() {};
// switch background style... toggle active feedback
ListItemBase.prototype._activeHandling = function($This) {
$This.toggleClass("sapMLIBActive", this._active);
if (this.isActionable(true)) {
$This.toggleClass("sapMLIBHoverable", !this._active);
}
};
/* Keyboard Handling */
ListItemBase.prototype.onsapspace = function(oEvent) {
// handle only the events that are coming from ListItemBase
if (oEvent.srcControl !== this) {
return;
}
// prevent default not to scroll down
oEvent.preventDefault();
// allow only for selectable items
if (oEvent.isMarked() || !this.isSelectable()) {
return;
}
// update selected property
if (this.getMode() == ListMode.MultiSelect) {
this.setSelected(!this.getSelected());
this.informList("Select", this.getSelected());
} else if (!this.getSelected()) {
this.setSelected(true);
this.informList("Select", true);
}
// event is handled
oEvent.setMarked();
};
ListItemBase.prototype.onsapenter = function(oEvent) {
var oList = this.getList();
if (oEvent.isMarked() || !oList) {
return;
}
// exit from edit mode
if (oEvent.srcControl !== this && oList.getKeyboardMode() == ListKeyboardMode.Edit) {
oList.setKeyboardMode(ListKeyboardMode.Navigation);
this._switchFocus(oEvent);
return;
}
// handle only item events
if (oEvent.srcControl !== this) {
return;
}
if (this.isIncludedIntoSelection()) {
// support old bug and mimic space key handling and
// do not fire item's press event when item is included into selection
this.onsapspace(oEvent);
} else if (this.hasActiveType()) {
// active feedback
oEvent.setMarked();
this.setActive(true);
setTimeout(function() {
this.setActive(false);
}.bind(this), 180);
// fire own press event
setTimeout(function() {
this.fireTap();
this.firePress();
}.bind(this), 0);
}
// let the parent know item is pressed
oList.onItemPress(this, oEvent.srcControl);
};
ListItemBase.prototype.onsapdelete = function(oEvent) {
if (oEvent.isMarked() ||
oEvent.srcControl !== this ||
this.getMode() != ListMode.Delete) {
return;
}
this.informList("Delete");
oEvent.preventDefault();
oEvent.setMarked();
};
ListItemBase.prototype._switchFocus = function(oEvent) {
var oList = this.getList();
if (!oList) {
return;
}
var $Tabbables = this.getTabbables();
if (oEvent.srcControl !== this) {
oList._iLastFocusPosOfItem = $Tabbables.index(oEvent.target);
this.focus();
} else if ($Tabbables.length) {
var iFocusPos = oList._iLastFocusPosOfItem || 0;
iFocusPos = $Tabbables[iFocusPos] ? iFocusPos : -1;
$Tabbables.eq(iFocusPos).trigger("focus");
}
oEvent.preventDefault();
oEvent.setMarked();
};
ListItemBase.prototype.onkeydown = function(oEvent) {
// check whether event is marked or not
if (oEvent.isMarked()) {
return;
}
// switch focus to row and focused item with F7
if (oEvent.which == KeyCodes.F7) {
this._switchFocus(oEvent);
return;
}
// F2 fire detail event or switch keyboard mode
if (oEvent.which == KeyCodes.F2) {
if (oEvent.srcControl === this &&
this.getType().indexOf("Detail") == 0 &&
this.hasListeners("detailPress") ||
this.hasListeners("detailTap")) {
this.fireDetailTap();
this.fireDetailPress();
oEvent.preventDefault();
oEvent.setMarked();
} else {
var oList = this.getList();
if (oList) {
this.$().prop("tabIndex", -1);
oList.setKeyboardMode(oList.getKeyboardMode() == ListKeyboardMode.Edit ? ListKeyboardMode.Navigation : ListKeyboardMode.Edit);
this._switchFocus(oEvent);
}
}
}
if (oEvent.srcControl !== this) {
return;
}
this.informList("KeyDown", oEvent);
};
ListItemBase.prototype.onkeyup = function(oEvent) {
if (oEvent.isMarked() || oEvent.srcControl !== this) {
return;
}
this.informList("KeyUp", oEvent);
};
ListItemBase.prototype.onsapupmodifiers = function(oEvent) {
if (oEvent.isMarked()) {
return;
}
this.informList("UpDownModifiers", oEvent, -1);
};
ListItemBase.prototype.onsapdownmodifiers = function(oEvent) {
if (oEvent.isMarked()) {
return;
}
this.informList("UpDownModifiers", oEvent, 1);
};
/**
* Returns the tabbable DOM elements as a jQuery collection
*
* @returns {jQuery} jQuery object
* @protected
* @since 1.26
*/
ListItemBase.prototype.getTabbables = function() {
return this.$().find(":sapTabbable");
};
// handle the TAB key
ListItemBase.prototype.onsaptabnext = function(oEvent) {
// check whether event is marked or not
var oList = this.getList();
if (!oList || oList.getKeyboardMode() == ListKeyboardMode.Edit) {
return;
}
var oTarget = oEvent.target;
if (document.activeElement.matches(".sapMListDummyArea")) {
oTarget = document.activeElement;
} else if (oEvent.isMarked()) {
return;
}
// if tab key is pressed while the last tabbable element of the list item
// has been focused, we forward tab to the last pseudo element of the table
var oLastTabbableDomRef = this.getTabbables().get(-1) || this.getDomRef();
if (oTarget === oLastTabbableDomRef) {
oList.forwardTab(true);
oEvent.setMarked();
}
};
// handle the SHIFT-TAB key
ListItemBase.prototype.onsaptabprevious = function(oEvent) {
var oList = this.getList();
if (!oList || oEvent.isMarked() || oList.getKeyboardMode() == ListKeyboardMode.Edit) {
return;
}
// if shift-tab is pressed while the list item has been focused,
// we forward tab to the root element of the list
if (oEvent.target === this.getDomRef()) {
oList.forwardTab(false);
oEvent.setMarked();
}
};
// handle propagated focus to make the item row focusable
ListItemBase.prototype.onfocusin = function(oEvent) {
var oList = this.getList();
if (!oList || oEvent.isMarked()) {
return;
}
this.informList("FocusIn", oEvent.srcControl);
oEvent.setMarked();
if (oEvent.srcControl === this) {
return;
}
if (oList.getKeyboardMode() == ListKeyboardMode.Edit ||
!jQuery(oEvent.target).is(":sapFocusable")) {
return;
}
// inform the list async that this item should be focusable
setTimeout(oList["setItemFocusable"].bind(oList, this), 0);
};
ListItemBase.prototype.onfocusout = function(oEvent) {
if (oEvent.isMarked() || oEvent.srcControl !== this) {
return;
}
this.informList("FocusOut", oEvent.srcControl);
oEvent.setMarked();
};
// inform the list for the vertical navigation
ListItemBase.prototype.onsapup = function(oEvent) {
if (oEvent.isMarked() ||
oEvent.srcControl === this ||
oEvent.target instanceof HTMLInputElement ||
oEvent.target instanceof HTMLTextAreaElement) {
return;
}
this.informList("ArrowUpDown", oEvent);
};
ListItemBase.prototype.oncontextmenu = function(oEvent) {
// context menu is not required on the group header.
if (this._bGroupHeader) {
return;
}
// allow the context menu to open on the SingleSelect or MultiSelect control
// is(":focusable") check is required as IE sets activeElement also to text controls
if (jQuery(document.activeElement).is(":focusable") &&
document.activeElement !== this.getDomRef() &&
oEvent.srcControl !== this.getModeControl()) {
return;
}
this.informList("ContextMenu", oEvent);
};
// inform the list for the vertical navigation
ListItemBase.prototype.onsapdown = ListItemBase.prototype.onsapup;
return ListItemBase;
});