@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,314 lines (1,126 loc) • 39.2 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([
"sap/ui/thirdparty/jquery",
"sap/ui/core/Control",
"sap/ui/core/CustomData",
"sap/ui/core/IconPool",
"sap/ui/core/HTML",
"sap/ui/core/Icon",
"./Button",
"./Toolbar",
"./ToolbarSpacer",
"./List",
"./MessageListItem",
"./library",
"./Text",
"./SegmentedButton",
"./Page",
"./NavContainer",
"./Link",
"./MessageItem",
"./GroupHeaderListItem",
'sap/ui/core/InvisibleText',
"sap/ui/core/library",
"sap/ui/base/ManagedObject",
"./MessageViewRenderer",
"sap/ui/events/KeyCodes",
"sap/base/Log",
"sap/base/security/URLListValidator",
"sap/ui/thirdparty/caja-html-sanitizer"
], function(
jQuery,
Control,
CustomData,
IconPool,
HTML,
Icon,
Button,
Toolbar,
ToolbarSpacer,
List,
MessageListItem,
library,
Text,
SegmentedButton,
Page,
NavContainer,
Link,
MessageItem,
GroupHeaderListItem,
InvisibleText,
coreLibrary,
ManagedObject,
MessageViewRenderer,
KeyCodes,
Log,
URLListValidator
) {
"use strict";
// shortcut for sap.ui.core.ValueState
var ValueState = coreLibrary.ValueState;
// shortcut for sap.ui.core.MessageType
var MessageType = coreLibrary.MessageType;
// shortcut for sap.m.ListType
var ListType = library.ListType;
/**
* Constructor for a new MessageView
*
* @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
* It is used to display a summarized list of different types of messages (error, warning, success, and information messages).
*
* <h3>Overview</h3>
* It is meant to be embedded into container controls (such as {@link sap.m.Popover}, {@link sap.m.ResponsivePopover}, {@link sap.m.Dialog}).
* It provides a handy and systematized way to navigate and explore details for every message.
* If the <code>MessageView</code> contains only one item, which has either description, markupDescription or longTextUrl, its details page will be displayed initially.
* It also exposes the {@link sap.m.MessageView#event:activeTitlePress} event, which can be used for navigation from a message to its source.
* <h3>Notes:</h3>
* <ul>
* <li>If your application changes its model between two interactions with the <code>MessageView</code>, this could lead to outdated messages being shown.
* To avoid this, you need to call <code>navigateBack</code> on the <code>MessageView</code> BEFORE opening its container.</li>
* <li> Messages can have descriptions preformatted with HTML markup. In this case, the <code>markupDescription</code> has to be set to <code>true</code>. </li>
* <li> If the message cannot be fully displayed, or includes a long description, the <code>MessageView</code> provides navigation to the detailed description. </li>
* </ul>
* <h3>Structure</h3>
* The <code>MessageView</code> stores all messages in an association of type {@link sap.m.MessageItem}, named <code>items</code>.
* <br>
* A set of properties determines how the items are rendered:
* <ul>
* <li> counter - An integer that is used to indicate the number of errors for each type. </li>
* <li> type - The type of message. </li>
* <li> title/subtitle - The title and subtitle of the message.</li>
* <li> description - The long text description of the message.</li>
* <li> activeTitle - Determines whether the title of the item is interactive.</li>
* </ul>
* <h3>Usage</h3>
* <h4>When to use:</h4>
* <ul>
* <li>When you want a way to centrally manage messages and show them to the user without additional work for the developer.
* If needed the navigation between the message item and the source of the error can be created by the application.
* This can be done by setting the <code>activeTitle</code> property to true and providing a handler for the <code>activeTitlePress</code> event.</li>
* </ul>
* <h3>Responsive Behavior</h3>
* The responsiveness of the <code>MessageView</code> is determined by the container in which it is embedded. For that reason the control could not be visualized if the
* container’s sizes are not defined.
* @author SAP SE
* @version 1.117.4
*
* @extends sap.ui.core.Control
* @constructor
* @public
* @since 1.46
* @alias sap.m.MessageView
* @see {@link fiori:https://experience.sap.com/fiori-design-web/message-view/ Message View}
*/
var MessageView = Control.extend("sap.m.MessageView", /** @lends sap.m.MessageView.prototype */ {
metadata: {
library: "sap.m",
properties: {
/**
* Callback function for resolving a promise after description has been asynchronously loaded inside this function.
* @callback sap.m.MessageView~asyncDescriptionHandler
* @param {object} config A single parameter object
* @param {sap.m.MessageItem} config.item Reference to respective MessageItem instance
* @param {object} config.promise Object grouping a promise's reject and resolve methods
* @param {function} config.promise.resolve Method to resolve promise
* @param {function} config.promise.reject Method to reject promise
*/
asyncDescriptionHandler: {type: "function", group: "Behavior", defaultValue: null},
/**
* Callback function for resolving a promise after a link has been asynchronously validated inside this function.
* @callback sap.m.MessageView~asyncURLHandler
* @param {object} config A single parameter object
* @param {string} config.url URL to validate
* @param {string|int} config.id ID of the validation job
* @param {object} config.promise Object grouping a promise's reject and resolve methods
* @param {function} config.promise.resolve Method to resolve promise
* @param {function} config.promise.reject Method to reject promise
*/
asyncURLHandler: {type: "function", group: "Behavior", defaultValue: null},
/**
* Defines whether the MessageItems are grouped or not.
*/
groupItems: { type: "boolean", group: "Behavior", defaultValue: false },
/**
* Defines whether the header of details page will be shown.
*/
showDetailsPageHeader: { type: "boolean", group: "Behavior", defaultValue: true }
},
defaultAggregation: "items",
aggregations: {
/**
* A list with message items.
* If only one item is provided, the initial page will be the details page for the item.
*/
items: { type: "sap.m.MessageItem", multiple: true, singularName: "item" },
/**
* Sets a custom header button.
*/
headerButton: { type: "sap.m.Button", multiple: false },
/**
* A navContainer that contains both details and list pages.
*/
_navContainer: { type: "sap.m.NavContainer", multiple: false, visibility : "hidden" }
},
events: {
/**
* Event fired after the popover is opened.
* @deprecated As of version 1.72. Use the appropriate event from the wrapper control, instead.
*/
afterOpen: {
parameters: {
/**
* This refers to the control which opens the popover.
*/
openBy: {type: "sap.ui.core.Control"}
},
deprecated: true
},
/**
* Event fired when description is shown.
*/
itemSelect: {
parameters: {
/**
* Refers to the message item that is being presented.
*/
item: {type: "sap.m.MessageItem"},
/**
* Refers to the type of messages being shown.
* See sap.ui.core.MessageType values for types.
*/
messageTypeFilter: {type: "sap.ui.core.MessageType"}
}
},
/**
* Event fired when one of the lists is shown when (not) filtered by type.
*/
listSelect: {
parameters: {
/**
* This parameter refers to the type of messages being shown.
*/
messageTypeFilter: {type: "sap.ui.core.MessageType"}
}
},
/**
* Event fired when the long text description data from a remote URL is loaded.
*/
longtextLoaded: {},
/**
* Event fired when a validation of a URL from long text description is ready.
*/
urlValidated: {},
/**
* Event fired when an activeTitle of a MessageItem is pressed.
* @since 1.58
*/
activeTitlePress: {
parameters: {
/**
* Refers to the message item that contains the activeTitle.
*/
item: { type: "sap.m.MessageItem" }
}
}
}
},
renderer: MessageViewRenderer
});
var CSS_CLASS = "sapMMsgView";
var ICONS = {
back: IconPool.getIconURI("nav-back"),
close: IconPool.getIconURI("decline"),
information: IconPool.getIconURI("information"),
warning: IconPool.getIconURI("alert"),
error: IconPool.getIconURI("error"),
success: IconPool.getIconURI("sys-enter-2")
};
var LIST_TYPES = ["all", "error", "warning", "success", "information"];
// Property names array
var ASYNC_HANDLER_NAMES = ["asyncDescriptionHandler", "asyncURLHandler"];
// Private class variable used for static method below that sets default async handlers
var DEFAULT_ASYNC_HANDLERS = {
asyncDescriptionHandler: function (config) {
var sLongTextUrl = config.item.getLongtextUrl();
if (sLongTextUrl) {
jQuery.ajax({
type: "GET",
url: sLongTextUrl,
success: function (data) {
config.item.setDescription(data);
config.promise.resolve();
},
error: function () {
var sError = "A request has failed for long text data. URL: " + sLongTextUrl;
Log.error(sError);
config.promise.reject(sError);
}
});
}
}
};
/**
* Setter for default description and URL validation callbacks across all instances of MessageView
* @static
* @protected
* @param {object} mDefaultHandlers An object setting default callbacks
* @param {function} mDefaultHandlers.asyncDescriptionHandler The description handler
* @param {function} mDefaultHandlers.asyncURLHandler The URL handler
*/
MessageView.setDefaultHandlers = function (mDefaultHandlers) {
ASYNC_HANDLER_NAMES.forEach(function (sFuncName) {
if (mDefaultHandlers.hasOwnProperty(sFuncName)) {
DEFAULT_ASYNC_HANDLERS[sFuncName] = mDefaultHandlers[sFuncName];
}
});
};
/**
* Initializes the control
*
* @override
* @private
*/
MessageView.prototype.init = function () {
var that = this;
this._bHasHeaderButton = false;
this._oResourceBundle = sap.ui.getCore().getLibraryResourceBundle("sap.m");
this._createNavigationPages();
this._createLists();
// Check for default async handlers and set them appropriately
ASYNC_HANDLER_NAMES.forEach(function (sFuncName) {
if (DEFAULT_ASYNC_HANDLERS.hasOwnProperty(sFuncName)) {
that.setProperty(sFuncName, DEFAULT_ASYNC_HANDLERS[sFuncName]);
}
});
};
/**
* Handles navigate event of the NavContainer
*
* @private
*/
MessageView.prototype._afterNavigate = function () {
setTimeout(this["_restoreFocus"].bind(this), 0);
setTimeout(this["_restoreItemsType"].bind(this), 0);
};
/**
* Restores the focus after navigation
*
* @private
*/
MessageView.prototype._restoreFocus = function () {
if (this._isListPage() && this.getItems().length) {
this._oLists[this._sCurrentList || 'all'].focus();
} else if (this._oBackButton){
this._oBackButton.focus();
}
};
/**
* Restores the items type after navigation
*
* @private
*/
MessageView.prototype._restoreItemsType = function () {
if (this._isListPage() && this.getItems().length > 1) {
var that = this;
this._oLists[this._sCurrentList || 'all'].getItems().forEach(function (oListItem) {
if (oListItem.isA("sap.m.MessageListItem")) {
that._setItemType(oListItem);
}
});
}
};
/**
* Sets the item type to navigation if the text is too long
*
* @param {sap.m.MessageListItem} oListItem The list item
* @private
*/
MessageView.prototype._setItemType = function (oListItem) {
var oItemTitleRef = oListItem.getTitleRef();
if (oItemTitleRef && (oItemTitleRef.offsetWidth < oItemTitleRef.scrollWidth)) {
// if title's text overflows, make the item type Navigation
oListItem.setType(ListType.Navigation);
if (this.getItems().length === 1) {
this._fnHandleForwardNavigation(oListItem, "show");
}
}
};
MessageView.prototype.onBeforeRendering = function () {
var oGroupedItems,
aListItems,
aItems = this.getItems();
this._clearLists();
this._detailsPage.setShowHeader(this.getShowDetailsPageHeader());
if (this.getGroupItems()) {
oGroupedItems = this._groupItems(aItems);
this._fillGroupedLists(oGroupedItems);
} else {
this._fillLists(aItems);
}
var headerButton = this.getHeaderButton();
if (headerButton) {
this._bHasHeaderButton = true;
this._oListHeader.insertContent(headerButton, 2);
}
this._clearSegmentedButton();
this._fillSegmentedButton();
this._fnFilterList(this._getCurrentMessageTypeFilter() || "all");
aListItems = this._oLists.all.getItems().filter(function (oItem) {
return oItem.isA("sap.m.MessageListItem");
});
if (aListItems.length === 1 && aListItems[0].getType() === ListType.Navigation) {
this._fnHandleForwardNavigation(aListItems[0], "show");
// TODO: adopt this to NavContainer's public API once a parameter for back navigation transition name is available
this._navContainer._pageStack[this._navContainer._pageStack.length - 1].transition = "slide";
} else if (aListItems.length === 0) {
this._navContainer.backToTop();
}
// Bind automatically to the MessageModel if no items are bound
this._makeAutomaticBinding();
};
/**
* Fills grouped items in the lists
* @param {sap.m.MessageItem[]} aGroupedItems An array of items
* @private
*/
MessageView.prototype._fillGroupedLists = function(aGroupedItems) {
var aGroups = Object.keys(aGroupedItems),
iUngroupedIndex = aGroups.indexOf(""),
aUngrouped;
if (iUngroupedIndex !== -1) {
aUngrouped = aGroupedItems[""];
this._fillLists(aUngrouped);
delete aGroupedItems[""];
aGroups.splice(iUngroupedIndex, 1);
}
aGroups.forEach(function(sGroupName) {
this._fillListsWithGroups(sGroupName, aGroupedItems[sGroupName]);
}, this);
};
MessageView.prototype._fillListsWithGroups = function(sGroupName, aItems) {
var oHeader = new GroupHeaderListItem({
title: sGroupName
});
this._oLists["all"].addAggregation("items", oHeader, true);
["error", "warning", "success", "information"].forEach(function (sListType) {
if (this._hasGroupItemsOfType(aItems, sListType)) {
this._oLists[sListType].addAggregation("items", oHeader.clone(), true);
}
}, this);
this._fillLists(aItems);
};
MessageView.prototype._hasGroupItemsOfType = function (aItems, sListType) {
return aItems.some(function (oItem) {
return oItem.getType().toLowerCase() === sListType;
});
};
/**
* Called when the control is destroyed
*
* @private
*/
MessageView.prototype.exit = function () {
if (this._oLists) {
this._destroyLists();
}
if (this._oMessageItemTemplate) {
this._oMessageItemTemplate.destroy();
}
this._oResourceBundle = null;
this._oListHeader = null;
this._oDetailsHeader = null;
this._oSegmentedButton = null;
this._oBackButton = null;
this._navContainer = null;
this._listPage = null;
this._detailsPage = null;
this._sCurrentList = null;
};
/**
* If there's no items binding, attach the MessageView to the sap.ui.getCore().getMessageManager().getMessageModel()
*
* @private
* @ui5-restricted sap.m.MessagePopover
*/
MessageView.prototype._makeAutomaticBinding = function () {
var aItems = this.getItems();
if (!this.getBindingInfo("items") && !aItems.length) {
this._bindToMessageModel();
}
};
/**
* Makes automatic binding to the Message Model with default template
*
* @private
*/
MessageView.prototype._bindToMessageModel = function () {
var that = this;
this.setModel(sap.ui.getCore().getMessageManager().getMessageModel(), "message");
this._oMessageItemTemplate = new MessageItem({
type: "{message>type}",
title: "{message>message}",
description: "{message>description}",
longtextUrl: "{message>longtextUrl}"
});
this.bindAggregation("items",
{
path: "message>/",
template: that._oMessageItemTemplate
}
);
};
/**
* Groups items in an object of keys and correspoding array of items
* @param {sap.m.MessageItem[]} aItems An array of items
* @returns {object} Item object
* @private
*/
MessageView.prototype._groupItems = function (aItems) {
var oGroups = {}, sItemGroup;
aItems.forEach(function(oItem) {
sItemGroup = oItem.getGroupName();
oGroups[sItemGroup] = oGroups[sItemGroup] || [];
oGroups[sItemGroup].push(oItem);
});
return oGroups;
};
/**
* Handles keyup event
*
* @param {jQuery.Event} oEvent - keyup event object
* @private
*/
MessageView.prototype._onkeypress = function (oEvent) {
if (oEvent.shiftKey && oEvent.keyCode == KeyCodes.ENTER) {
this.navigateBack();
}
};
/**
* Returns header of the MessageView's ListPage
*
* @returns {sap.m.Toolbar} ListPage header
* @private
*/
MessageView.prototype._getListHeader = function () {
return this._oListHeader || this._createListHeader();
};
/**
* Returns header of the MessageView's ListPage
*
* @returns {sap.m.Toolbar} DetailsPage header
* @private
*/
MessageView.prototype._getDetailsHeader = function () {
return this._oDetailsHeader || this._createDetailsHeader();
};
/**
* Creates header of MessageView's ListPage
*
* @returns {sap.m.Toolbar} ListPage header
* @private
*/
MessageView.prototype._createListHeader = function () {
var sCloseBtnDescr = this._oResourceBundle.getText("MESSAGEPOPOVER_CLOSE");
var sCloseBtnDescrId = this.getId() + "-CloseBtnDescr";
var oCloseBtnARIAHiddenDescr = new HTML(sCloseBtnDescrId, {
content: "<span id=\"" + sCloseBtnDescrId + "\" class=\"sapMMsgViewHiddenContainer\">" + sCloseBtnDescr + "</span>"
});
var sHeadingDescr = this._oResourceBundle.getText("MESSAGEPOPOVER_ARIA_HEADING"),
sHeadingDescrId = this.getId() + "-HeadingDescr",
sSegmentedBtnDescrId = InvisibleText.getStaticId("sap.m", "MESSAGEVIEW_SEGMENTED_BTN_DESCRIPTION"),
oHeadingARIAHiddenDescr = new HTML(sHeadingDescrId, {
content: "<span id=\"" + sHeadingDescrId + "\" class=\"sapMMsgViewHiddenContainer\" role=\"heading\">" + sHeadingDescr + "</span>"
});
this._oSegmentedButton = new SegmentedButton(this.getId() + "-segmented", {
ariaLabelledBy: sSegmentedBtnDescrId
}).addStyleClass("sapMSegmentedButtonNoAutoWidth");
this._oListHeader = new Toolbar({
content: [this._oSegmentedButton, new ToolbarSpacer(), oCloseBtnARIAHiddenDescr, oHeadingARIAHiddenDescr]
});
return this._oListHeader;
};
/**
* Creates header of MessageView's ListPage
*
* @returns {sap.m.Toolbar} DetailsPage header
* @private
*/
MessageView.prototype._createDetailsHeader = function () {
var sCloseBtnDescr = this._oResourceBundle.getText("MESSAGEPOPOVER_CLOSE");
var sCloseBtnDescrId = this.getId() + "-CloseBtnDetDescr";
var oCloseBtnARIAHiddenDescr = new HTML(sCloseBtnDescrId, {
content: "<span id=\"" + sCloseBtnDescrId + "\" class=\"sapMMsgViewHiddenContainer\">" + sCloseBtnDescr + "</span>"
});
var sBackBtnTooltipDescr = this._oResourceBundle.getText("MESSAGEPOPOVER_ARIA_BACK_BUTTON_TOOLTIP");
var sBackBtnDescr = this._oResourceBundle.getText("MESSAGEPOPOVER_ARIA_BACK_BUTTON");
var sBackBtnDescrId = this.getId() + "-BackBtnDetDescr";
var oBackBtnARIAHiddenDescr = new HTML(sBackBtnDescrId, {
content: "<span id=\"" + sBackBtnDescrId + "\" class=\"sapMMsgViewHiddenContainer\">" + sBackBtnDescr + "</span>"
});
this._oBackButton = new Button({
icon: ICONS["back"],
press: this.navigateBack.bind(this),
ariaLabelledBy: oBackBtnARIAHiddenDescr,
tooltip: sBackBtnTooltipDescr
}).addStyleClass(CSS_CLASS + "BackBtn");
this._oDetailsHeader = new Toolbar({
content: [this._oBackButton, new ToolbarSpacer(), oCloseBtnARIAHiddenDescr, oBackBtnARIAHiddenDescr]
});
return this._oDetailsHeader;
};
/**
* Creates navigation pages
*
* @returns {this} Reference to the 'this' for chaining purposes
* @private
*/
MessageView.prototype._createNavigationPages = function () {
// Create two main pages
this._listPage = new Page(this.getId() + "listPage", {
customHeader: this._getListHeader()
});
this._detailsPage = new Page(this.getId() + "-detailsPage", {
customHeader: this._getDetailsHeader()
}).addStyleClass("sapMMsgViewDetailsPage");
// TODO: check if this is the best location for this
// Disable clicks on disabled and/or pending links
this._detailsPage.addEventDelegate({
onclick: function (oEvent) {
var target = oEvent.target;
if (target.nodeName.toUpperCase() === "A" &&
(target.className.indexOf("sapMMsgViewItemDisabledLink") !== -1 ||
target.className.indexOf("sapMMsgViewItemPendingLink") !== -1)) {
oEvent.preventDefault();
}
}
});
// Initialize nav container with two main pages
this._navContainer = new NavContainer(this.getId() + "-navContainer", {
initialPage: this.getId() + "listPage",
pages: [this._listPage, this._detailsPage],
afterNavigate: this._afterNavigate.bind(this)
});
this.setAggregation("_navContainer", this._navContainer);
return this;
};
/**
* Creates Lists of the MessageView
*
* @returns {this} Reference to the 'this' for chaining purposes
* @private
*/
MessageView.prototype._createLists = function () {
this._oLists = {};
LIST_TYPES.forEach(function (sListName) {
this._oLists[sListName] = new List({
itemPress: this._fnHandleItemPress.bind(this),
visible: false
});
// no re-rendering
this._listPage.addAggregation("content", this._oLists[sListName], true);
}, this);
return this;
};
/**
* Destroy items in the MessageView's Lists
*
* @returns {this} Reference to the 'this' for chaining purposes
* @private
*/
MessageView.prototype._clearLists = function () {
LIST_TYPES.forEach(function (sListName) {
if (this._oLists[sListName]) {
this._oLists[sListName].destroyAggregation("items", true);
}
}, this);
return this;
};
/**
* Destroys internal Lists of the MessageView
*
* @private
*/
MessageView.prototype._destroyLists = function () {
LIST_TYPES.forEach(function (sListName) {
this._oLists[sListName] = null;
}, this);
this._oLists = null;
};
/**
* Fill the list with items
*
* @param {array} aItems An array with items type of sap.ui.core.Item.
* @private
*/
MessageView.prototype._fillLists = function (aItems) {
aItems.forEach(function (oMessageItem) {
var oListItem = this._mapItemToListItem(oMessageItem),
oCloneListItem = this._mapItemToListItem(oMessageItem);
// add the mapped item to the List
this._oLists["all"].addAggregation("items", oListItem, true);
this._oLists[oMessageItem.getType().toLowerCase()].addAggregation("items", oCloneListItem, true);
}, this);
};
/**
* Map a MessageItem to MessageListItem
*
* @param {sap.m.MessageItem} oMessageItem Base information to generate the list items
* @returns {sap.m.MessageListItem | null} oListItem List item which will be displayed
* @private
*/
MessageView.prototype._mapItemToListItem = function (oMessageItem) {
if (!oMessageItem) {
return null;
}
var sType = oMessageItem.getType(),
that = this,
listItemType = this._getItemType(oMessageItem),
oListItem = new MessageListItem({
title: ManagedObject.escapeSettingsValue(oMessageItem.getTitle()),
description: ManagedObject.escapeSettingsValue(oMessageItem.getSubtitle()),
counter: oMessageItem.getCounter(),
icon: this._mapIcon(sType),
infoState: this._mapInfoState(sType),
info: "",
type: listItemType,
messageType: oMessageItem.getType(),
activeTitle: oMessageItem.getActiveTitle(),
activeTitlePress: function () {
that.fireActiveTitlePress({ item: oMessageItem });
}
}).addStyleClass(CSS_CLASS + "Item")
.addStyleClass(CSS_CLASS + "Item" + sType)
.toggleStyleClass(CSS_CLASS + "ItemActive", oMessageItem.getActiveTitle());
if (listItemType !== ListType.Navigation) {
oListItem.addEventDelegate({
onAfterRendering: function () {
that._setItemType(oListItem);
}
}, this);
}
oListItem._oMessageItem = oMessageItem;
return oListItem;
};
/**
* Map ValueState according the MessageType of the message.
*
* @param {sap.ui.core.MessageType} sType Type of Message
* @returns {sap.ui.core.ValueState | null} The ValueState
* @private
*/
MessageView.prototype._mapInfoState = function (sType) {
if (!sType) {
return null;
}
switch (sType) {
case MessageType.Warning:
return ValueState.Warning;
case MessageType.Error:
return ValueState.Error;
case MessageType.Success:
return ValueState.Success;
case MessageType.Information:
case MessageType.None:
return ValueState.None;
default:
Log.warning("The provided MessageType is not mapped to a specific ValueState", sType);
return null;
}
};
/**
* Map a MessageType to the Icon URL.
*
* @param {sap.ui.core.ValueState} sIcon Type of Error
* @returns {string | null} Icon string
* @private
*/
MessageView.prototype._mapIcon = function (sIcon) {
if (!sIcon) {
return null;
}
return ICONS[sIcon.toLowerCase()];
};
MessageView.prototype._getItemType = function (oMessageItem) {
return (oMessageItem.getDescription() || oMessageItem.getMarkupDescription() || oMessageItem.getLongtextUrl()) ?
ListType.Navigation : ListType.Inactive;
};
/**
* Destroy the buttons in the SegmentedButton
*
* @returns {this} Reference to the 'this' for chaining purposes
* @private
*/
MessageView.prototype._clearSegmentedButton = function () {
if (this._oSegmentedButton) {
this._oSegmentedButton.destroyAggregation("buttons", true);
}
return this;
};
/**
* Fill SegmentedButton with needed Buttons for filtering
*
* @returns {this} Reference to the 'this' for chaining purposes
* @private
*/
MessageView.prototype._fillSegmentedButton = function () {
var that = this;
var pressClosure = function (sListName) {
return function () {
that._fnFilterList(sListName);
};
};
LIST_TYPES.forEach(function (sListName) {
var oList = this._oLists[sListName],
sBundleText = sListName == "all" ? "MESSAGEPOPOVER_ALL" : "MESSAGEVIEW_BUTTON_TOOLTIP_" + sListName.toUpperCase(),
iCount = oList.getItems().filter(function(oItem) {
return (oItem instanceof MessageListItem);
}).length, oButton;
if (iCount > 0) {
oButton = new Button(this.getId() + "-" + sListName, {
text: sListName == "all" ? this._oResourceBundle.getText(sBundleText) : iCount,
tooltip: sListName === "all" ? "" : this._oResourceBundle.getText(sBundleText),
icon: ICONS[sListName],
press: pressClosure(sListName)
}).addStyleClass(CSS_CLASS + "Btn" + sListName.charAt(0).toUpperCase() + sListName.slice(1));
this._oSegmentedButton.addButton(oButton, true);
}
}, this);
// If there is only the always-present 'all' button and a single group button
// no need for a segmented button
var bSegmentedButtonVisible = this._oSegmentedButton.getButtons().length > 2;
this._oSegmentedButton.setVisible(bSegmentedButtonVisible);
// If there's only one group reset filter and highlight the "All" button from the SegmentedButton list.
// Otherwise if the user has filtered and the model changes, he could be stuck to a "no data" page without a way
// to navigate back and see the remaining messages
if (!bSegmentedButtonVisible) {
this._oSegmentedButton.setSelectedButton(this._oSegmentedButton.getButtons()[0]);
this._fnFilterList('all');
}
// If SegmentedButton should not be visible,
// and there is no custom button - hide the initial page's header
var bListPageHeaderVisible = bSegmentedButtonVisible || this._bHasHeaderButton;
this._listPage.setShowHeader(bListPageHeaderVisible);
return this;
};
/**
* Sets icon in details page
* @param {sap.m.MessageItem} oMessageItem The message item
* @param {sap.m.MessageListItem} oListItem The list item
* @private
*/
MessageView.prototype._setIcon = function (oMessageItem, oListItem) {
this._previousIconTypeClass = CSS_CLASS + "DescIcon" + oMessageItem.getType();
this._oMessageIcon = new Icon({
src: oListItem.getIcon()
})
.addStyleClass(CSS_CLASS + "DescIcon")
.addStyleClass(this._previousIconTypeClass);
this._detailsPage.addContent(this._oMessageIcon);
};
/**
* Sets title part of details page
* @param {sap.m.MessageItem} oMessageItem The message item
* @private
*/
MessageView.prototype._setTitle = function (oMessageItem, oListItem) {
var bActive = oMessageItem.getActiveTitle(),
oDetailsContent, that = this,
sText = ManagedObject.escapeSettingsValue(oMessageItem.getTitle()),
sId = this.getId() + "MessageTitleText";
if (bActive) {
oDetailsContent = new Link(sId, {
text: sText,
ariaDescribedBy: oListItem.getId() + "-link",
press: function () {
that.fireActiveTitlePress({ item: oMessageItem });
}
});
} else {
oDetailsContent = new Text(sId, {
text: sText
});
}
oDetailsContent.addStyleClass("sapMMsgViewTitleText");
this._detailsPage.addAggregation("content", oDetailsContent);
};
/**
* Sets description text part of details page
* When markup description is used it is sanitized within it's container's setter method (MessageItem)
* @param {sap.m.MessageItem} oMessageItem The message item
* @private
*/
MessageView.prototype._setDescription = function (oMessageItem) {
var oLink = oMessageItem.getLink();
var sDescription = oMessageItem.getDescription();
if (oMessageItem.getMarkupDescription()) {
var tagPolicy = this._getTagPolicy();
/*global html*/
sDescription = html.sanitizeWithPolicy(sDescription, tagPolicy);
}
this._oLastSelectedItem = oMessageItem;
if (oMessageItem.getMarkupDescription()) {
this._oMessageDescriptionText = new HTML(this.getId() + "MarkupDescription", {
content: "<div class='sapMMsgViewDescriptionText'>" + ManagedObject.escapeSettingsValue(sDescription) + "</div>"
});
} else {
this._oMessageDescriptionText = new Text(this.getId() + "MessageDescriptionText", {
text: ManagedObject.escapeSettingsValue(sDescription)
}).addStyleClass("sapMMsgViewDescriptionText");
}
this._detailsPage.addContent(this._oMessageDescriptionText);
if (oLink) {
var oLinkClone = this._createLinkCopy(oLink);
this._detailsPage.addContent(oLinkClone);
oLinkClone.addStyleClass("sapMMsgViewDescriptionLink");
}
};
MessageView.prototype._createLinkCopy = function (oLink) {
var aLinkProperties,
oLinkClone = oLink.clone("", "", {
cloneChildren: false,
cloneBindings: false
}),
aCustomData = oLink.getCustomData() || [];
aLinkProperties = Object.keys(oLink.getMetadata().getProperties());
aLinkProperties.forEach(function(sProp){
oLinkClone.setProperty(sProp, oLink.getProperty(sProp));
});
oLinkClone.destroyCustomData();
aCustomData.forEach(function(oCustomData){
var oCustomDataCopy = new CustomData({
key: oCustomData.getKey(),
value: oCustomData.getValue()
});
oLinkClone.addCustomData(oCustomDataCopy);
});
return oLinkClone;
};
MessageView.prototype._iNextValidationTaskId = 0;
MessageView.prototype._validateURL = function (sUrl) {
if (URLListValidator.validate(sUrl)) {
return sUrl;
}
Log.warning("You have entered invalid URL");
return "";
};
MessageView.prototype._queueValidation = function (href) {
var fnAsyncURLHandler = this.getAsyncURLHandler();
var iValidationTaskId = ++this._iNextValidationTaskId;
var oPromiseArgument = {};
var oPromise = new Promise(function (resolve, reject) {
oPromiseArgument.resolve = resolve;
oPromiseArgument.reject = reject;
var config = {
url: href,
id: iValidationTaskId,
promise: oPromiseArgument
};
fnAsyncURLHandler(config);
});
oPromise.id = iValidationTaskId;
return oPromise;
};
MessageView.prototype._getTagPolicy = function () {
var that = this,
i;
/*global html*/
var defaultTagPolicy = html.makeTagPolicy(this._validateURL());
return function customTagPolicy(tagName, attrs) {
var href,
validateLink = false;
if (tagName.toUpperCase() === "A") {
for (i = 0; i < attrs.length;) {
// if there is href the link should be validated, href's value is on position(i+1)
if (attrs[i] === "href") {
validateLink = true;
href = attrs[i + 1];
attrs.splice(0, 2);
continue;
}
i += 2;
}
}
// let the default sanitizer do its work
// it won't see the href attribute
attrs = defaultTagPolicy(tagName, attrs);
// if we detected a link before, we modify the <A> tag
// and keep the link in a dataset attribute
if (validateLink && typeof that.getAsyncURLHandler() === "function") {
attrs = attrs || [];
// first check if there is a class attribute and enrich it with 'sapMMsgViewItemDisabledLink'
// else, add proper class
var sClasses = "sapMMsgViewItemDisabledLink sapMMsgViewItemPendingLink sapMLnk";
var indexOfClass = attrs.indexOf("class");
if (indexOfClass > -1) {
attrs[indexOfClass + 1] += sClasses;
} else {
attrs.unshift(sClasses);
attrs.unshift("class");
}
// check for existing id
var indexOfId = attrs.indexOf("id");
if (indexOfId > -1) {
// we start backwards
attrs.splice(indexOfId + 1, 1);
attrs.splice(indexOfId, 1);
}
var oValidation = that._queueValidation(href);
// add other attributes
attrs.push("href");
// the link is deactivated via class names later read by event delegate on the description page
attrs.push(href);
// let the page open in another window, so state is preserved
attrs.push("target");
attrs.push("_blank");
// use id here as data attributes are not passing through caja
attrs.push("id");
attrs.push("sap-ui-" + that.getId() + "-link-under-validation-" + oValidation.id);
oValidation
.then(function (result) {
// Update link in output
var $link = jQuery(document.getElementById("sap-ui-" + that.getId() + "-link-under-validation-" + result.id));
if (result.allowed) {
Log.info("Allow link " + href);
} else {
Log.info("Disallow link " + href);
}
// Adapt the link style
$link.removeClass("sapMMsgViewItemPendingLink");
$link.toggleClass("sapMMsgViewItemDisabledLink", !result.allowed);
that.fireUrlValidated();
})
.catch(function () {
Log.warning("Async URL validation could not be performed.");
});
}
return attrs;
};
};
/**
* Handles click on a list item
*
* @param {sap.m.MessageListItem} oListItem ListItem that is pressed
* @param {string} sTransiotionName name of transition could be slide, show, flip or fade
* @private
*/
MessageView.prototype._fnHandleForwardNavigation = function (oListItem, sTransiotionName) {
var oMessageItem = oListItem._oMessageItem,
asyncDescHandler = this.getAsyncDescriptionHandler();
this._previousIconTypeClass = this._previousIconTypeClass || "";
this.fireItemSelect({
item: oMessageItem,
messageTypeFilter: this._getCurrentMessageTypeFilter()
});
this._clearDetailsPage.call(this);
if (typeof asyncDescHandler === "function" && oMessageItem.getLongtextUrl()) {
// Set markupDescription to true as markup description should be processed as markup
oMessageItem.setMarkupDescription(true);
var oPromiseArgument = {};
var oPromise = new Promise(function (resolve, reject) {
oPromiseArgument.resolve = resolve;
oPromiseArgument.reject = reject;
});
var proceed = function () {
this._clearDetailsPage.call(this);
this._detailsPage.setBusy(false);
this._navigateToDetails.call(this, oMessageItem, oListItem, sTransiotionName, true);
}.bind(this);
oPromise
.then(proceed)
.catch(function () {
Log.warning("Async description loading could not be performed.");
proceed();
});
this._navContainer.to(this._detailsPage);
this._detailsPage.setBusy(true);
asyncDescHandler({
promise: oPromiseArgument,
item: oMessageItem
});
} else {
this._navigateToDetails.call(this, oMessageItem, oListItem, sTransiotionName, false);
}
this._listPage.$().attr("aria-hidden", "true");
};
/**
* Handles click of the ListItems
*
* @param {jQuery.Event} oEvent ListItem click event object
* @private
*/
MessageView.prototype._fnHandleItemPress = function (oEvent) {
this._fnHandleForwardNavigation(oEvent.getParameter("listItem"), "slide");
};
MessageView.prototype._navigateToDetails = function(oMessageItem, oListItem, sTransiotionName, bSuppressNavigate) {
this._setTitle(oMessageItem, oListItem);
this._setDescription(oMessageItem);
this._setIcon(oMessageItem, oListItem);
this._detailsPage.invalidate();
this.fireLongtextLoaded();
if (!bSuppressNavigate) {
this._navContainer.to(this._detailsPage, sTransiotionName);
}
};
/**
* Destroys the content of details page
* @param {sap.ui.core.Control} aDetailsPageContent The details page content
* @private
*/
MessageView.prototype._clearDetailsPage = function () {
this._detailsPage.getContent().forEach(function (oControl) {
oControl.destroy();
}, this);
};
/**
* Navigates back to the list page
*
* @public
*/
MessageView.prototype.navigateBack = function () {
this._listPage.$().removeAttr("aria-hidden");
this._navContainer.back();
};
/**
* Handles click of the SegmentedButton
*
* @param {string} sCurrentListName ListName to be shown
* @private
*/
MessageView.prototype._fnFilterList = function (sCurrentListName) {
LIST_TYPES.forEach(function (sListIterName) {
if (sListIterName != sCurrentListName && this._oLists[sListIterName].getVisible()) {
// Hide Lists if they are visible and their name is not the same as current list name
this._oLists[sListIterName].setVisible(false);
}
}, this);
this._sCurrentList = sCurrentListName;
this._oLists[sCurrentListName].setVisible(true);
this.fireListSelect({messageTypeFilter: this._getCurrentMessageTypeFilter()});
};
/**
* Returns current selected List name
*
* @returns {string} Current list name
* @private
*/
MessageView.prototype._getCurrentMessageTypeFilter = function () {
return this._sCurrentList == "all" ? "" : this._sCurrentList;
};
/**
* Checks whether the current page is ListPage
*
* @returns {boolean} Whether the current page is ListPage
* @private
*/
MessageView.prototype._isListPage = function () {
return this._navContainer.getCurrentPage() == this._listPage;
};
return MessageView;
});