/*!
* UI development toolkit for HTML5 (OpenUI5)
* (c) Copyright 2009-2018 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides control sap.ui.commons.RoadMap.
sap.ui.define([
'jquery.sap.global',
'./library',
'sap/ui/core/Control',
"./RoadMapRenderer"
],
function(jQuery, library, Control, RoadMapRenderer) {
"use strict";
/**
* Constructor for a new RoadMap.
*
* @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
* RoadMap is used to display step-by-step work flows of a clearly defined work process.
* @extends sap.ui.core.Control
*
* @author SAP SE
* @version 1.60.23
*
* @constructor
* @public
* @deprecated Since version 1.38. Instead, use the sap.m.Wizard
control.
* @alias sap.ui.commons.RoadMap
* @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
*/
var RoadMap = Control.extend("sap.ui.commons.RoadMap", /** @lends sap.ui.commons.RoadMap.prototype */ { metadata : {
library : "sap.ui.commons",
properties : {
/**
* Total number of steps to be displayed at once
*/
numberOfVisibleSteps : {type : "int", group : "Misc", defaultValue : null},
/**
* ID of the first step to be displayed
*/
firstVisibleStep : {type : "string", group : "Misc", defaultValue : null},
/**
* ID of the step which is currently selected
*/
selectedStep : {type : "string", group : "Misc", defaultValue : null},
/**
* Determines the control width in CSS size
*/
width : {type : "sap.ui.core.CSSSize", group : "Dimension", defaultValue : '100%'}
},
defaultAggregation : "steps",
aggregations : {
/**
* Steps that are composing the RoadMap
*/
steps : {type : "sap.ui.commons.RoadMapStep", multiple : true, singularName : "step"}
},
events : {
/**
* Event is fired when the user selects a step.
*/
stepSelected : {
parameters : {
/**
* ID of the selected step
*/
stepId : {type : "string"}
}
},
/**
* Event is fired when a given step is expanded or collapsed by user.
*/
stepExpanded : {
parameters : {
/**
* ID of the expanded/collapsed step
*/
stepId : {type : "string"}
}
}
}
}});
(function() {
/**
* Does the setup when the RoadMap is created.
* @private
*/
RoadMap.prototype.init = function(){
this.iStepWidth = -1;
this.sCurrentFocusedStepRefId = null;
};
/**
* Does all the cleanup when the RoadMap is to be destroyed.
* Called from Element's destroy() method.
* @private
*/
RoadMap.prototype.exit = function (){
// Cleanup resize event registration
if (this.sResizeListenerId) {
sap.ui.core.ResizeHandler.deregister(this.sResizeListenerId);
this.sResizeListenerId = null;
}
};
//Setter for property numberOfVisibleSteps which suppresses rerendering if possible -> Comment generated automatically
RoadMap.prototype.setNumberOfVisibleSteps = function(iNumberOfVisibleSteps) {
var bIsRendered = this.getDomRef() ? true : false;
this.setProperty("numberOfVisibleSteps", iNumberOfVisibleSteps, bIsRendered);
if (bIsRendered) {
sap.ui.commons.RoadMapRenderer.updateScrollArea(this, true);
}
return this;
};
//Setter for property firstVisibleStep which suppresses rerendering if possible -> Comment generated automatically
RoadMap.prototype.setFirstVisibleStep = function(sFirstVisibleStep) {
var bIsRendered = this.getDomRef() ? true : false;
if (bIsRendered) {
if (sFirstVisibleStep) {
var oStep = sap.ui.getCore().byId(sFirstVisibleStep);
if (oStep && oStep.getParent() && (oStep.getParent() === this || oStep.getParent().getParent() === this) && oStep.getVisible()) {
this.setProperty("firstVisibleStep", sFirstVisibleStep, true);
sap.ui.commons.RoadMapRenderer.updateScrollArea(this);
}
} else {
this.setProperty("firstVisibleStep", "", true);
sap.ui.commons.RoadMapRenderer.updateScrollArea(this);
}
} else {
this.setProperty("firstVisibleStep", sFirstVisibleStep);
}
return this;
};
//Setter for property width which suppresses rerendering if possible -> Comment generated automatically
RoadMap.prototype.setWidth = function(sWidth) {
var bIsRendered = this.getDomRef() ? true : false;
this.setProperty("width", sWidth, bIsRendered);
if (bIsRendered) {
sap.ui.commons.RoadMapRenderer.setRoadMapWidth(this, sWidth);
sap.ui.commons.RoadMapRenderer.updateScrollArea(this, true);
}
return this;
};
//Setter for property selectedStep which suppresses rerendering if possible -> Comment generated automatically
RoadMap.prototype.setSelectedStep = function(sSelectedStep) {
var bIsRendered = this.getDomRef() ? true : false;
if (bIsRendered) {
if (sSelectedStep) {
var oStep = sap.ui.getCore().byId(sSelectedStep);
if (oStep && oStep.getParent() && (oStep.getParent() === this || oStep.getParent().getParent() === this)
&& oStep.getEnabled() && oStep.getVisible()) {
sap.ui.commons.RoadMapRenderer.selectStepWithId(this, sSelectedStep);
this.setProperty("selectedStep", sSelectedStep, true);
}
} else {
sap.ui.commons.RoadMapRenderer.selectStepWithId(this, "");
this.setProperty("selectedStep", "", true);
}
} else {
this.setProperty("selectedStep", sSelectedStep);
}
return this;
};
/**
* Called when the theme is changed.
* @private
*/
RoadMap.prototype.onThemeChanged = function(oEvent){
this.iStepWidth = -1;
if (this.getDomRef()) {
this.invalidate();
}
};
/**
* Called before rendering starts by the renderer
* (This is not the onBeforeRendering method which would be not called for the first rendering)
* @private
*/
RoadMap.prototype.doBeforeRendering = function(){
//Bring the properties into a solid state
var bIsValidSelectedStep = false;
var bIsValidFirstStep = false;
var aSteps = this.getSteps();
for (var i = 0; i < aSteps.length; i++) {
var oStep = aSteps[i];
//expanded=true only possible if substeps available and enabled
if (oStep.getSubSteps().length == 0 || !oStep.getEnabled()) {
oStep.setProperty("expanded", false, true);
}
//A selected step must exist, be enabled and visible
if (!oStep.getEnabled() && !oStep.getVisible() && this.getSelectedStep() == oStep.getId()) {
this.setProperty("selectedStep", "", true);
} else if (oStep.getEnabled() && oStep.getVisible() && this.getSelectedStep() == oStep.getId()) {
bIsValidSelectedStep = true;
}
//A first step must exist and be visible
if (oStep.getVisible() && this.getFirstVisibleStep() == oStep.getId()) {
bIsValidFirstStep = true;
}
var aSubSteps = oStep.getSubSteps();
for (var j = 0; j < aSubSteps.length; j++) {
var oSubStep = aSubSteps[j];
//expanded always false
oSubStep.setProperty("expanded", false, true);
//A selected step must exist, be enabled and visible
if (!oSubStep.getEnabled() && !oSubStep.getVisible() && this.getSelectedStep() == oSubStep.getId()) {
this.setProperty("selectedStep", "", true);
} else if (oSubStep.getEnabled() && oSubStep.getVisible() && this.getSelectedStep() == oSubStep.getId()) {
bIsValidSelectedStep = true;
}
//A first step must exist and be visible
if (oSubStep.getVisible() && this.getFirstVisibleStep() == oSubStep.getId()) {
bIsValidFirstStep = true;
}
}
}
if (!bIsValidSelectedStep) {
this.setProperty("selectedStep", "", true);
}
if (!bIsValidFirstStep) {
this.setProperty("firstVisibleStep", "", true);
}
// Cleanup resize event registration before re-rendering
if (this.sResizeListenerId) {
sap.ui.core.ResizeHandler.deregister(this.sResizeListenerId);
this.sResizeListenerId = null;
}
};
/**
* Called when the rendering is complete
* @private
*/
RoadMap.prototype.onAfterRendering = function(){
var aSteps = this.getSteps();
//Compute the step width
if (this.iStepWidth == -1 && aSteps.length > 0) {
var jRef = aSteps[0].$();
this.iStepWidth = jRef.outerWidth();
}
//Adapt the step labels if needed
for (var i = 0; i < aSteps.length; i++) {
var oStep = aSteps[i];
sap.ui.commons.RoadMapRenderer.addEllipses(oStep);
var aSubSteps = oStep.getSubSteps();
for (var j = 0; j < aSubSteps.length; j++) {
sap.ui.commons.RoadMapRenderer.addEllipses(aSubSteps[j]);
}
}
//Adapt the size of the scroll area
sap.ui.commons.RoadMapRenderer.updateScrollArea(this);
// Listen to resizing
this.sResizeListenerId = sap.ui.core.ResizeHandler.register(this.getDomRef(), jQuery.proxy(this.onresize, this));
};
/**
* Called when the Roadmap is resized
* @private
*/
RoadMap.prototype.onresize = function(oEvent) {
var fDoOnResize = function() {
if (this.getDomRef()) {
//Adapt the size of the scroll area
sap.ui.commons.RoadMapRenderer.updateScrollArea(this, true);
refreshFocus(this, "prev");
this.sResizeInProgress = null;
}
};
if (!!sap.ui.Device.browser.firefox) {
fDoOnResize.apply(this, []);
} else {
if (!this.sResizeInProgress) {
this.sResizeInProgress = jQuery.sap.delayedCall(300, this, fDoOnResize);
}
}
};
/**
* Behavior implementation which is executed when the user clicks the step.
*
* @param {jQuery.Event} oEvent
* @private
*/
RoadMap.prototype.onclick = function(oEvent){
handleSelect(this, oEvent);
};
/**
* Behavior implementation which is executed when the user presses the space or enter key.
*
* @param {jQuery.Event} oEvent
* @private
*/
RoadMap.prototype.onsapselect = function(oEvent){
handleSelect(this, oEvent);
};
/**
* Behavior implementation which is executed when the focus comes into the control or on one of its children.
*
* @param {jQuery.Event} oEvent
* @private
*/
RoadMap.prototype.onfocusin = function(oEvent){
var jTarget = jQuery(oEvent.target);
var jTargetId = jTarget.attr("id");
/*eslint-disable no-empty */
//TODO Rethink if empty block is needed
if (jTargetId && jQuery.sap.endsWith(jTargetId, "-box")) {
this.sCurrentFocusedStepRefId = jTargetId.substring(0, jTargetId.length - 4);
} else if (jTargetId && (jQuery.sap.endsWith(jTargetId, "-Start") || jQuery.sap.endsWith(jTargetId, "-End"))) {
//Keep the current focus
} else {
this.sCurrentFocusedStepRefId = sap.ui.commons.RoadMapRenderer.getFirstVisibleRef(this).attr("id");
refreshFocus(this);
}
/*eslint-enable no-empty */
//Remove the control from tab chain to make tab out working (see onfocusout)
this.$().attr("tabindex", "-1");
};
/**
* Behavior implementation which is executed when the focus leaves the control or one of its children.
*
* @param {jQuery.Event} oEvent
* @private
*/
RoadMap.prototype.onfocusout = function(oEvent){
//Add the control to tab chain again to make tab in working (see onfocusin)
this.$().attr("tabindex", "0");
};
/**
* Behavior implementation which is executed when the user presses the arrow up or arrow left (RTL: arrow right) key.
*
* @param {jQuery.Event} oEvent
* @private
*/
RoadMap.prototype.onsapprevious = function(oEvent){
focusStep(oEvent, this, "prev");
};
/**
* Behavior implementation which is executed when the user presses the arrow down or arrow right (RTL: arrow left) key.
*
* @param {jQuery.Event} oEvent
* @private
*/
RoadMap.prototype.onsapnext = function(oEvent){
focusStep(oEvent, this, "next");
};
/**
* Behavior implementation which is executed when the user presses the home/pos1 key.
*
* @param {jQuery.Event} oEvent
* @private
*/
RoadMap.prototype.onsaphome = function(oEvent){
focusStep(oEvent, this, "first");
};
/**
* Behavior implementation which is executed when the user presses the end key.
*
* @param {jQuery.Event} oEvent
* @private
*/
RoadMap.prototype.onsapend = function(oEvent){
focusStep(oEvent, this, "last");
};
//********* Private *********
//Called when either the Roadmap is clicked or the space or enter key is pressed
var handleSelect = function(oThis, oEvent){
oEvent.stopPropagation();
oEvent.preventDefault();
var jTarget = jQuery(oEvent.target);
var sTargetId = jTarget.attr("id");
if (!sTargetId) {
return;
}
//Handle event for the end of an expandable step
var iIdx = sTargetId.lastIndexOf("-expandend");
if (iIdx != -1) {
var oStep = sap.ui.getCore().byId(sTargetId.substring(0, iIdx));
if (oStep && oThis.indexOfStep(oStep) >= 0) {
oStep.handleSelect(oEvent, true);
return;
}
}
//Handle select on delimiter
if (sTargetId == oThis.getId() + "-Start") {
if (jTarget.hasClass("sapUiRoadMapStartScroll")) {
scrollToNextStep(oThis, "prev", true);
} else {
refreshFocus(oThis);
}
} else if (sTargetId == oThis.getId() + "-End") {
if (jTarget.hasClass("sapUiRoadMapEndScroll")) {
scrollToNextStep(oThis, "next", true);
} else {
refreshFocus(oThis);
}
}
};
//Helper function to scroll to following step (optionally with updating the focus (see focusStep)).
//Allowed directions are: next, prev, first, last.
var scrollToNextStep = function(oThis, sDir, bUpdateFocus){
sap.ui.commons.RoadMapRenderer.scrollToNextStep(oThis, sDir, function(sFirstVisibleNodeId){
var iIdx = sFirstVisibleNodeId.lastIndexOf("-expandend");
if (iIdx != -1) {
sFirstVisibleNodeId = sFirstVisibleNodeId.substring(0, iIdx);
}
oThis.setProperty("firstVisibleStep", sFirstVisibleNodeId, true);
if (bUpdateFocus) {
refreshFocus(oThis, sDir);
}
});
};
//Helper function to focus the following step of the current focused step in the given direction.
//Allowed directions are: next, prev, first, last. If this step is not visible an automatic scrolling is done.
var focusStep = function(oEvent, oThis, sDir){
if (oEvent) {
oEvent.stopPropagation();
oEvent.preventDefault();
}
if (!oThis.sCurrentFocusedStepRefId) {
return;
}
var sFoo = sDir + "All";
var bIsJumpToDelimiter = false;
if (sDir == "first") {
sFoo = "prevAll";
bIsJumpToDelimiter = true;
} else if (sDir == "last") {
sFoo = "nextAll";
bIsJumpToDelimiter = true;
}
var jCurrentFocusStep = jQuery.sap.byId(oThis.sCurrentFocusedStepRefId);
var jFollowingSteps = jCurrentFocusStep[sFoo](":visible");
var sFollowingFocusStepId = jQuery(jFollowingSteps.get(bIsJumpToDelimiter ? jFollowingSteps.length - 1 : 0)).attr("id");
if (sFollowingFocusStepId) {
if (!sap.ui.commons.RoadMapRenderer.isVisibleRef(oThis, sFollowingFocusStepId)) {
scrollToNextStep(oThis, sDir);
}
jQuery.sap.byId(sFollowingFocusStepId + "-box").get(0).focus();
}
};
//Sets the focus on the current focused step again. If the current focused step is not visible anymore
//the following step in the given direction is focused. Allowed directions are: next, prev, first, last
var refreshFocus = function(oThis, sDir){
if (!oThis.sCurrentFocusedStepRefId) {
return;
}
if (sDir && !sap.ui.commons.RoadMapRenderer.isVisibleRef(oThis, oThis.sCurrentFocusedStepRefId)) {
focusStep(null, oThis, sDir);
} else {
jQuery.sap.byId(oThis.sCurrentFocusedStepRefId + "-box").get(0).focus();
}
};
}());
return RoadMap;
}, /* bExport= */ true);