@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,652 lines (1,395 loc) • 49.5 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides control sap.m.Carousel.
sap.ui.define([
"./library",
"sap/base/i18n/Localization",
"sap/base/util/clamp",
"sap/ui/core/Control",
"sap/ui/core/Element",
"sap/ui/core/Theming",
"sap/ui/Device",
"sap/ui/core/Lib",
"sap/ui/core/ResizeHandler",
"sap/ui/core/library",
"sap/m/IllustratedMessage",
"sap/m/IllustratedMessageType",
"./CarouselRenderer",
"sap/ui/events/KeyCodes",
"sap/base/Log",
"sap/base/util/isPlainObject",
"sap/m/ImageHelper",
"sap/ui/thirdparty/jquery",
"./CarouselLayout",
"sap/ui/core/IconPool",
// provides jQuery custom selector ":sapTabbable"
"sap/ui/dom/jquery/Selectors"
], function(
library,
Localization,
clamp,
Control,
Element,
Theming,
Device,
Library,
ResizeHandler,
coreLibrary,
IllustratedMessage,
IllustratedMessageType,
CarouselRenderer,
KeyCodes,
Log,
isPlainObject,
ImageHelper,
jQuery,
CarouselLayout
/*, IconPool (indirect dependency, kept for compatibility with tests, to be fixed in ImageHelper) */
) {
"use strict";
//shortcut for sap.ui.core.BusyIndicatorSize
var BusyIndicatorSize = coreLibrary.BusyIndicatorSize;
// shortcut for sap.m.CarouselArrowsPlacement
var CarouselArrowsPlacement = library.CarouselArrowsPlacement;
//shortcut for sap.m.CarouselPageIndicatorPlacementType
var CarouselPageIndicatorPlacementType = library.CarouselPageIndicatorPlacementType;
//shortcut for sap.m.BackgroundDesign
var BackgroundDesign = library.BackgroundDesign;
//shortcut for sap.m.BorderDesign
var BorderDesign = library.BorderDesign;
//shortcut for sap.m.CarouselScrollMode
var CarouselScrollMode = library.CarouselScrollMode;
var iDragRadius = 10;
var iMoveRadius = 20;
var bRtl = Localization.getRTL();
const MIN_PAGE_WIDTH = 16;
function getCursorPosition(e) {
e = e.originalEvent || e;
var oTouches = e.touches && e.touches[0];
return {
x: oTouches ? oTouches.clientX : e.clientX,
y: oTouches ? oTouches.clientY : e.clientY
};
}
function translateX(element, delta) {
element.style["transform"] = 'translate3d(' + delta + 'px, 0, 0)';
}
/**
* Constructor for a new Carousel.
*
* @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 carousel allows the user to browse through a set of items by swiping right or left.
* <h3>Overview</h3>
* The control is mostly used for showing a gallery of images, but can hold any sap.m control.
* <h3>Structure</h3>
* The carousel consists of a the following elements:
* <ul>
* <li>Content area - displays the different items.</li>
* <li>Navigation - arrows to the left and right for switching between items.</li>
* <li>(optional) Paging - indicator at the bottom to show the current position in the set.</li>
* </ul>
* The paging indicator can be configured as follows:
* <ul>
* <li><code>showPageIndicator</code> - determines if the indicator is displayed.</li>
* <li>If the pages are less than 9, the page indicator is represented with bullets.</li>
* <li>If the pages are 9 or more, the page indicator is numeric.</li>
* <li><code>pageIndicatorPlacement</code> - determines where the indicator is located. Default (<code>sap.m.CarouselPageIndicatorPlacementType.Bottom</code>) - below the content.</li>
*</ul>
* Additionally, you can also change the location of the navigation arrows.
* By setting <code>arrowsPlacement</code> to <code>sap.m.CarouselArrowsPlacement.PageIndicator</code>, the arrows will be located at the bottom by the paging indicator.
* Note: When the content is of type <code>sap.m.Image</code> add "Image" text at the end of the <code>"alt"</code> description in order to provide accessibility info for the UI element.
* <h3>Usage</h3>
* <h4> When to use</h4>
* <ul>
* <li>The items you want to display are very different from each other.</li>
* <li>You want to display the items one after the other.</li>
* </ul>
* <h4> When not to use</h4>
* <ul>
* <li>The items you want to display need to be visible at the same time.</li>
* <li>The items you want to display are uniform and very similar</li>
* </ul>
* <h3>Responsive Behavior</h3>
* <ul>
* <li>On touch devices, navigation is performed with swipe gestures (swipe right or swipe left) or with the navigation arrows.</li>
* <li>On desktop, navigation is done with the navigation arrows.</li>
* <li>The paging indicator (when activated) is visible on each form factor.</li>
* <li>When using {@link sap.m.CarouselLayout CarouselLayout} with the <code>responsive</code> property set to <code>true</code>,
* the number of visible pages adjusts automatically based on the available width and the specified <code>minPageWidth</code>.</li>
* </ul>
* @extends sap.ui.core.Control
*
* @author SAP SE
* @version 1.146.0
*
* @constructor
* @public
* @alias sap.m.Carousel
* @see {@link fiori:https://experience.sap.com/fiori-design-web/carousel/ Carousel}
*/
var Carousel = Control.extend("sap.m.Carousel", /** @lends sap.m.Carousel.prototype */ {
metadata : {
library : "sap.m",
designtime: "sap/m/designtime/Carousel.designtime",
properties : {
/**
* The height of the carousel. Note that when a percentage value is used, the height of the surrounding container must be defined.
*/
height : {type : "sap.ui.core.CSSSize", group : "Dimension", defaultValue : '100%'},
/**
* The width of the carousel. Note that when a percentage value is used, the height of the surrounding container must be defined.
*/
width : {type : "sap.ui.core.CSSSize", group : "Dimension", defaultValue : '100%'},
/**
* Defines whether the carousel should loop, i.e show the first page after the last page is reached and vice versa.
*/
loop : {type : "boolean", group : "Misc", defaultValue : false},
/**
* Show or hide carousel's page indicator.
*/
showPageIndicator : {type : "boolean", group : "Appearance", defaultValue : true},
/**
* Defines where the carousel's page indicator is displayed.
* Possible values are sap.m.CarouselPageIndicatorPlacementType.Top, sap.m.CarouselPageIndicatorPlacementType.Bottom,
* CarouselPageIndicatorPlacementType.OverContentTop and CarouselPageIndicatorPlacementType.OverContentBottom.
*
* <b>Note:</b> When the page indicator is placed over the carousel's content (values "OverContentBottom" and "OverContentTop"),
* the properties <code>pageIndicatorBackgroundDesign</code> and <code>pageIndicatorBorderDesign</code> will not take effect.
*
* <b>Note:</b> We recommend using a page indicator placed over the carousel's content (values "OverContentBottom" and "OverContentTop")
* only if the content consists of images.
*/
pageIndicatorPlacement : {type : "sap.m.CarouselPageIndicatorPlacementType", group : "Appearance", defaultValue : CarouselPageIndicatorPlacementType.Bottom},
/**
* Show or hide busy indicator in the carousel when loading pages after swipe.
* @deprecated Since version 1.18.7.
* Since 1.18.7 pages are no longer loaded or unloaded. Therefore busy indicator is not necessary any longer.
*/
showBusyIndicator : {type : "boolean", group : "Appearance", defaultValue : true, deprecated: true},
/**
* Defines where the carousel's arrows are placed. Default is <code>sap.m.CarouselArrowsPlacement.Content</code> used to
* place the arrows on the sides of the carousel. Alternatively <code>sap.m.CarouselArrowsPlacement.PageIndicator</code> can
* be used to place the arrows on the sides of the page indicator.
*/
arrowsPlacement : {type : "sap.m.CarouselArrowsPlacement", group : "Appearance", defaultValue : CarouselArrowsPlacement.Content},
/**
* Defines the carousel's background design. Default is <code>sap.m.BackgroundDesign.Translucent</code>.
* @public
* @since 1.110
*/
backgroundDesign : {type : "sap.m.BackgroundDesign", group : "Appearance", defaultValue : BackgroundDesign.Translucent},
/**
* Defines the carousel page indicator background design. Default is <code>sap.m.BackgroundDesign.Solid</code>.
* @public
* @since 1.115
*/
pageIndicatorBackgroundDesign : {type : "sap.m.BackgroundDesign", group : "Appearance", defaultValue : BackgroundDesign.Solid},
/**
* Defines the carousel page indicator border design. Default is <code>sap.m.BorderDesign.Solid</code>.
* @public
* @since 1.115
*/
pageIndicatorBorderDesign : {type : "sap.m.BorderDesign", group : "Appearance", defaultValue : BorderDesign.Solid}
},
defaultAggregation : "pages",
aggregations : {
/**
* The content which the carousel displays.
*/
pages : {type : "sap.ui.core.Control", multiple : true, singularName : "page"},
/**
* Defines how many pages are displayed in the visible area of the <code>Carousel</code> control.
*
* <b>Note:</b> When this property is used, the <code>loop</code> property is ignored.
* @since 1.62
*/
customLayout: { type: "sap.m.CarouselLayout", multiple: false },
/**
* Message page, that is shown when no pages are loaded or provided
*/
_emptyPage: { type: "sap.m.IllustratedMessage", multiple: false, visibility: "hidden" }
},
associations : {
/**
* Provides getter and setter for the currently displayed page. For the setter, argument may be the control itself, which must be member of the carousel's page list, or the control's id.
* The getter will return the control id
*/
activePage : {type : "sap.ui.core.Control", multiple : false},
/**
* Association to controls / IDs which label this control (see WAI-ARIA attribute <code>aria-labelledby</code>).
* @since 1.125
*/
ariaLabelledBy: {
type: "sap.ui.core.Control", multiple: true, singularName: "ariaLabelledBy"
}
},
events : {
/**
* Carousel requires a new page to be loaded. This event may be used to fill the content of that page
* @deprecated Since version 1.18.7.
* Since 1.18.7 pages are no longer loaded or unloaded
*/
loadPage : {deprecated: true,
parameters : {
/**
* Id of the page which will be loaded
*/
pageId : {type : "string"}
}
},
/**
* Carousel does not display a page any longer and unloads it. This event may be used to clean up the content of that page.
* @deprecated Since version 1.18.7.
* Since 1.18.7 pages are no longer loaded or unloaded
*/
unloadPage : {deprecated: true,
parameters : {
/**
* Id of the page which will be unloaded
*/
pageId : {type : "string"}
}
},
/**
* This event is fired after a carousel swipe has been completed.
* It is triggered both by physical swipe events and through API carousel manipulations such as calling
* 'next', 'previous' or 'setActivePage' functions.
*/
pageChanged : {
parameters : {
/**
* ID of the page which was active before the page change.
*/
oldActivePageId : {type : "string"},
/**
* ID of the page which will be active after the page change.
*/
newActivePageId : {type : "string"},
/**
* Indexes of all active pages after the page change.
* @since 1.62
*/
activePages : {type : "array"}
}
},
/**
* This event is fired before a carousel swipe has been completed.
* It is triggered both by physical swipe events and through API carousel manipulations such as calling
* 'next', 'previous' or 'setActivePage' functions.
*/
beforePageChanged : {
parameters : {
/**
* Indexes of all active pages after the page change.
* @since 1.63
*/
activePages : {type : "array"}
}
}
}
},
renderer: CarouselRenderer
});
//Constants convenient class selections
Carousel._INNER_SELECTOR = ".sapMCrslInner";
Carousel._PAGE_INDICATOR_SELECTOR = ".sapMCrslBulleted";
Carousel._PAGE_INDICATOR_ARROWS_SELECTOR = ".sapMCrslIndicatorArrow";
Carousel._CONTROLS = ".sapMCrslControls";
Carousel._ITEM_SELECTOR = ".sapMCrslItem";
Carousel._LEFTMOST_CLASS = "sapMCrslLeftmost";
Carousel._RIGHTMOST_CLASS = "sapMCrslRightmost";
Carousel._MODIFIERNUMBERFORKEYBOARDHANDLING = 10; // The number 10 is by keyboard specification
Carousel._BULLETS_TO_NUMBERS_THRESHOLD = 9; //The number 9 is by visual specification. Less than 9 pages - bullets for page indicator. 9 or more pages - numeric page indicator.
/**
* Initialize member variables which are needed later on.
*
* @private
*/
Carousel.prototype.init = function() {
this._aAllActivePages = [];
this._aAllActivePagesIndexes = [];
this._iFocusedPageIndex = -1;
this._bShouldFireEvent = true;
this._handleThemeAppliedBound = this._handleThemeApplied.bind(this);
this.data("sap-ui-fastnavgroup", "true", true); // Define group for F6 handling
this._oRb = Library.getResourceBundleFor("sap.m");
};
/**
* Called when the control is destroyed.
*
* @private
*/
Carousel.prototype.exit = function() {
if (this._oArrowLeft) {
this._oArrowLeft.destroy();
delete this._oArrowLeft;
}
if (this._oArrowRight) {
this._oArrowRight.destroy();
delete this._oArrowRight;
}
if (this._sResizeListenerId) {
ResizeHandler.deregister(this._sResizeListenerId);
this._sResizeListenerId = null;
}
this.$().off('afterSlide');
this._aAllActivePages = null;
this._aAllActivePagesIndexes = null;
if (this._bThemeAppliedAttached) {
Theming.detachApplied(this._handleThemeAppliedBound);
this._bThemeAppliedAttached = false;
}
};
Carousel.prototype.onBeforeRendering = function() {
if (!this.getActivePage() && this.getPages().length > 0) {
//if no active page is specified, set first page.
this.setAssociation("activePage", this.getPages()[0].getId(), true);
}
var sActivePage = this.getActivePage();
if (sActivePage) {
this._updateActivePages(sActivePage);
if (this._iFocusedPageIndex === -1) {
this._iFocusedPageIndex = this._aAllActivePagesIndexes[0];
}
}
if (this._sResizeListenerId) {
ResizeHandler.deregister(this._sResizeListenerId);
this._sResizeListenerId = null;
}
return this;
};
Carousel.prototype._resize = function() {
var $inner = this.$().find('> .sapMCrslList > .sapMCrslInner');
if (this._iResizeTimeoutId) {
clearTimeout(this._iResizeTimeoutId);
delete this._iResizeTimeoutId;
}
$inner.addClass("sapMCrslNoTransition");
$inner.addClass("sapMCrslHideNonActive");
this._iResizeTimeoutId = setTimeout(function () {
$inner.removeClass("sapMCrslNoTransition");
$inner.removeClass("sapMCrslHideNonActive");
});
this.invalidate();
};
/**
* Returns the number of items displayed in <code>Carousel</code>, depending on the <code>CarouselLayout</code> aggregation settings and pages count.
*
* @private
*/
Carousel.prototype._getNumberOfItemsToShow = function () {
const iPagesCount = this.getPages().length;
const oCarouselLayout = this.getCustomLayout();
if (!oCarouselLayout || !iPagesCount) {
return 1;
}
let iNumberOfItemsToShow;
const bResponsive = oCarouselLayout.getResponsive();
if (bResponsive) {
if (!this.getDomRef()) {
return 1;
}
const oFirstItem = this.getDomRef().querySelector(".sapMCrslList .sapMCrslItem");
const iMargin = parseFloat(window.getComputedStyle(oFirstItem).marginInlineEnd);
const iMinWidth = Math.max(MIN_PAGE_WIDTH, oCarouselLayout.getMinPageWidth()) + iMargin;
iNumberOfItemsToShow = Math.floor(this.$().width() / iMinWidth);
} else {
iNumberOfItemsToShow = oCarouselLayout.getVisiblePagesCount();
}
return clamp(iNumberOfItemsToShow, 1, iPagesCount);
};
/**
* When this method is called for the first time, a swipe-view instance is created which is renders
* itself into its dedicated spot within the DOM tree. This instance is used throughout the
* Carousel instance's lifecycle.
*
* @private
*/
Carousel.prototype.onAfterRendering = function() {
var iActivePageIndex = this._getActivePageIndex();
var $innerDiv = this.$().find(Carousel._INNER_SELECTOR)[0];
var iPagesLength = this.getPages().length;
if (!iPagesLength) {
return;
}
this._iCurrSlideIndex = Math.min(iActivePageIndex, iPagesLength - this._getNumberOfItemsToShow());
if (this.getPages().length &&
this.getPages()[this._getPageIndex(this.getActivePage())].getId() !== this.getActivePage()) {
this.setAssociation("activePage", this.getPages()[iActivePageIndex].getId(), true);
}
if (!this._bThemeAppliedAttached) {
this._bThemeAppliedAttached = true;
Theming.attachApplied(this._handleThemeAppliedBound);
}
this._sResizeListenerId = ResizeHandler.register($innerDiv, this._resize.bind(this));
};
Carousel.prototype.getFocusDomRef = function () {
if (!this.getPages().length) {
return this.getDomRef("noData");
}
if (this._iFocusedPageIndex === -1) {
return null;
}
const sPageId = this.getPages()[this._iFocusedPageIndex].getId();
return this.getDomRef(sPageId + "-slide");
};
/**
* Fired when the theme is changed.
*
* @private
*/
Carousel.prototype._handleThemeApplied = function () {
this._initialize();
Theming.detachApplied(this._handleThemeAppliedBound);
this._bThemeAppliedAttached = false;
};
/**
* Calls logic for updating active pages and fires 'beforePageChanged' event with the new active pages.
*
* @param {int} iPreviousSlide carousel index of the previous active slide
* @param {int} iNextSlide carousel index of the next active slide
* @private
*/
Carousel.prototype._onBeforePageChanged = function (iPreviousSlide, iNextSlide) {
var sNewActivePageId = this.getPages()[iNextSlide].getId();
this._updateActivePages(sNewActivePageId);
this.fireBeforePageChanged({
activePages: this._aAllActivePagesIndexes
});
};
/**
* Sets the width of the visible pages, rendered in the <code>Carousel</code> control.
*
* @param {int} iNumberOfItemsToShow number of items to be shown from 'pages' aggregation.
* @private
*/
Carousel.prototype._setWidthOfPages = function (iNumberOfItemsToShow) {
var $items = this.$().find(".sapMCrslItem"),
iItemWidth;
if (!$items.length) {
// pages are not yet rendered, calculation will be done in the next onAfterRendering
return;
}
iItemWidth = this._calculatePagesWidth(iNumberOfItemsToShow);
$items.each(function (iIndex, oPage) {
oPage.style.width = iItemWidth + "%";
});
};
/**
* Calculates the correct width of the visible pages, rendered in the <code>Carousel</code> control.
*
* @param {int} iNumberOfItemsToShow number of items to be shown from 'pages' aggregation.
* @returns {float} width of each page in percentage
* @private
*/
Carousel.prototype._calculatePagesWidth = function (iNumberOfItemsToShow) {
var iWidth = this.$().width(),
oSlide = this.getDomRef().querySelector(".sapMCrslFluid .sapMCrslItem"),
iMargin = parseFloat(window.getComputedStyle(oSlide).marginInlineEnd),
iItemWidth = (iWidth - (iMargin * (iNumberOfItemsToShow - 1))) / iNumberOfItemsToShow,
iItemWidthPercent = (iItemWidth / iWidth) * 100;
return iItemWidthPercent;
};
/**
* Moves carousel to specific slide and changes the active page after the move has been completed.
* Each carousel slide can hold multiple carousel pages.
*
* @param {int} iNewIndex index of the new active slide
* @private
*/
Carousel.prototype._moveToPage = function(iNewIndex, iFocusPageIndex) {
if (!this._bIsInitialized || this.getPages().length === 0) {
return;
}
var $element = this.$(),
$inner = $element.find('> .sapMCrslList > .sapMCrslInner'),
$items = $inner.children(),
iIndex = this._iCurrSlideIndex,
iLength = $items.length,
iNumberOfItemsToShow = this._getNumberOfItemsToShow(),
bLoop = this.getLoop(),
bIsCarouselActive = this.getDomRef().contains(document.activeElement);
// prevent loop when carousel shows more pages than 1
if (bLoop && iNumberOfItemsToShow !== 1 &&
(iNewIndex < 0 || iNewIndex > iLength - 1)) { // new index out of range - will cause loop
return;
}
// Bound Values between [1, length];
if (iNewIndex < 0) {
//if looping move to last index
if (bLoop) {
iNewIndex = iLength - 1;
} else {
iNewIndex = 0;
}
} else if (iNewIndex > iLength - 1) {
// if looping move to first index
if (bLoop) {
iNewIndex = 0;
} else {
iNewIndex = iLength - 1;
}
}
if (iNewIndex + iNumberOfItemsToShow > iLength - 1) {
iNewIndex = iLength - iNumberOfItemsToShow;
}
// Bail out early if no move is necessary.
var bTriggerEvents = true;
if (iNewIndex === iIndex) {
//only trigger events if index changes
bTriggerEvents = false;
}
// Trigger beforeSlide event
if (bTriggerEvents) {
this._onBeforePageChanged(iIndex, iNewIndex);
}
this._iOffsetDrag = 0;
this._iCurrSlideIndex = iNewIndex;
this._updateTransformValue();
this._initActivePages();
this._updateItemsAttributes(iFocusPageIndex);
if (bTriggerEvents) {
this._changeActivePage(this._aAllActivePagesIndexes[0]);
}
// focus the new page after transition if the focus was in the carousel
if (bIsCarouselActive || this._bPageIndicatorArrowPress) {
this._focusPage(iFocusPageIndex);
this._bPageIndicatorArrowPress = false;
}
};
/**
* Private method which adjusts the Hud visibility and fires a page change
* event when the active page changes
*
* @param {int} iNewPageIndex index of new page in 'pages' aggregation.
* @private
*/
Carousel.prototype._changeActivePage = function(iNewPageIndex) {
var sOldActivePageId = this.getActivePage();
if (this._sOldActivePageId) {
sOldActivePageId = this._sOldActivePageId;
delete this._sOldActivePageId;
}
var sNewActivePageId = this.getPages()[iNewPageIndex].getId();
this.setAssociation("activePage", sNewActivePageId, true);
// close the soft keyboard
if (!Device.system.desktop) {
jQuery(document.activeElement).trigger("blur");
}
if (this._bShouldFireEvent) {
Log.debug("sap.m.Carousel: firing pageChanged event: old page: " + sOldActivePageId + ", new page: " + sNewActivePageId);
this.firePageChanged({
oldActivePageId: sOldActivePageId,
newActivePageId: sNewActivePageId,
activePages: this._aAllActivePagesIndexes
});
}
this._adjustArrowsVisibility();
this._updatePageIndicator();
};
Carousel.prototype._focusPage = function(sPageIndex) {
this._iFocusedPageIndex = sPageIndex;
const oPageDomRef = this.getDomRef(this.getPages()[sPageIndex].getId() + "-slide");
// focus the new page if the is not on some of the page children
if (!oPageDomRef.contains(document.activeElement)) {
oPageDomRef.focus({ preventScroll: true });
}
};
Carousel.prototype._updateItemsAttributes = function (iSelectedPageIndex) {
this.$().find(Carousel._ITEM_SELECTOR).each(function (iIndex, oPage) {
var bSelected = iIndex === iSelectedPageIndex;
oPage.setAttribute("aria-hidden", !this._isPageDisplayed(iIndex));
oPage.setAttribute("tabindex", bSelected ? 0 : -1);
}.bind(this));
};
Carousel.prototype._updatePageIndicator = function () {
// change the number in the page indicator
this.$("slide-number").text(this._getPageIndicatorText(this._iCurrSlideIndex + 1));
};
/**
* Returns page indicator text.
*
* @param {int} iNewPageIndex carousel slide index
* @returns {string} page indicator text
* @private
*/
Carousel.prototype._getPageIndicatorText = function (iNewPageIndex) {
return this._oRb.getText("CAROUSEL_PAGE_INDICATOR_TEXT", [iNewPageIndex, this.getPages().length - this._getNumberOfItemsToShow() + 1]);
};
/**
* Adjusts arrows' visibility
*
* @private
*/
Carousel.prototype._adjustArrowsVisibility = function() {
if (this._loops() || this.getPages().length <= 1) {
return;
}
var $HUDContainer = this.$("hud");
var $ArrowPrev = this.$("arrow-previous");
var $ArrowNext = this.$("arrow-next");
var iFirstDisplayedPageIndex = this._aAllActivePagesIndexes[0];
var iLastDisplayedPageIndex = this._aAllActivePagesIndexes[this._aAllActivePagesIndexes.length - 1];
// clear marker classes first
if (this.getArrowsPlacement() === CarouselArrowsPlacement.Content) {
$HUDContainer.removeClass(Carousel._LEFTMOST_CLASS).removeClass(Carousel._RIGHTMOST_CLASS);
} else {
$ArrowPrev.removeClass(Carousel._LEFTMOST_CLASS);
$ArrowNext.removeClass(Carousel._RIGHTMOST_CLASS);
}
if (iFirstDisplayedPageIndex === 0) {
if (this.getArrowsPlacement() === CarouselArrowsPlacement.Content) {
$HUDContainer.addClass(Carousel._LEFTMOST_CLASS);
} else {
$ArrowPrev.addClass(Carousel._LEFTMOST_CLASS);
}
}
if (iLastDisplayedPageIndex === this.getPages().length - 1) {
if (this.getArrowsPlacement() === CarouselArrowsPlacement.Content) {
$HUDContainer.addClass(Carousel._RIGHTMOST_CLASS);
} else {
$ArrowNext.addClass(Carousel._RIGHTMOST_CLASS);
}
}
};
Carousel.prototype.setActivePage = function (vPage) {
var sPageId = null;
if (typeof (vPage) == 'string') {
sPageId = vPage;
} else if (vPage instanceof Control) {
sPageId = vPage.getId();
}
if (sPageId) {
if (sPageId === this.getActivePage()) {
//page has not changed, nothing to do, return
return this;
}
var iPageNr = this._getPageIndex(sPageId);
this._sOldActivePageId = this.getActivePage();
this._moveToPage(iPageNr, iPageNr);
}
this.setAssociation("activePage", sPageId, true);
return this;
};
/**
* Returns the icon of the requested direction (left/right).
* @private
* @param {string} sDirection Left or Right
* @returns {sap.ui.core.Control} icon of the requested arrow
*/
Carousel.prototype._getNavigationArrow = function (sDirection) {
if (!this["_oArrow" + sDirection]) {
this["_oArrow" + sDirection] = ImageHelper.getImageControl(
this.getId() + "-arrowScroll" + sDirection,
this["_oArrow" + sDirection],
this,
{ src: "sap-icon://slim-arrow-" + sDirection.toLowerCase(), useIconTooltip: false }
);
}
return this["_oArrow" + sDirection];
};
/**
* Private method that creates message page when no pages are loaded or provided
*
* @private
*/
Carousel.prototype._getEmptyPage = function () {
if (!this.getAggregation("_emptyPage")) {
var emptyPage = new IllustratedMessage({
illustrationType: IllustratedMessageType.NoData,
enableVerticalResponsiveness: true
});
this.setAggregation("_emptyPage", emptyPage);
}
return this.getAggregation("_emptyPage");
};
/**
* Returns the index of the slide that should be shown
* @private
* @param {int} iCurrentSlideIndex Current slide index
* @param {int} iDefaultIndexStep Index that shows if previous or next arrow is pressed
* @returns {int} Index of the slide
*/
Carousel.prototype._calculateSlideIndex = function (iCurrentSlideIndex, iDefaultIndexStep) {
const oCarouselLayout = this.getCustomLayout();
let iSlideIndex;
if (oCarouselLayout && oCarouselLayout.getScrollMode() === CarouselScrollMode.VisiblePages) {
const iNumberOfItemsOnPage = this._getNumberOfItemsToShow();
iSlideIndex = iDefaultIndexStep > 0 ? iCurrentSlideIndex + iNumberOfItemsOnPage : Math.max(0, iCurrentSlideIndex - iNumberOfItemsOnPage);
} else {
iSlideIndex = iDefaultIndexStep > 0 ? iCurrentSlideIndex + 1 : iCurrentSlideIndex - 1;
}
return iSlideIndex;
};
/**
* Call this method to display the previous page (corresponds to a swipe left).
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
Carousel.prototype.previous = function () {
const iSlideIndex = this._calculateSlideIndex(this._iCurrSlideIndex, -1);
let iFocusPageIndex = this._iFocusedPageIndex;
if (this._aAllActivePagesIndexes.at(-1) === this._iFocusedPageIndex) {
iFocusPageIndex = this._iFocusedPageIndex - 1;
}
this._moveToPage(iSlideIndex, this._makeInRange(iFocusPageIndex, false));
return this;
};
/**
* Call this method to display the next page (corresponds to a swipe right).
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
Carousel.prototype.next = function () {
const iSlideIndex = this._calculateSlideIndex(this._iCurrSlideIndex, 1);
let iFocusPageIndex = this._iFocusedPageIndex;
if (this._aAllActivePagesIndexes[0] === this._iFocusedPageIndex) {
iFocusPageIndex = this._iFocusedPageIndex + 1;
}
this._moveToPage(iSlideIndex, this._makeInRange(iFocusPageIndex, false));
return this;
};
/**
* Determines the position of a given page in the carousel's page list
*
* @return the position of a given page in the carousel's page list or 'undefined' if it does not exist in the list.
* @private
*/
Carousel.prototype._getPageIndex = function(sPageId) {
var i, result = 0;
for (i = 0; i < this.getPages().length; i++) {
if (this.getPages()[i].getId() === sPageId) {
result = i;
break;
}
}
return result;
};
Carousel.prototype._getActivePageIndex = function () {
var iActivePageIndex = 0,
sActivePage = this.getActivePage();
if (sActivePage) {
iActivePageIndex = this._getPageIndex(sActivePage);
}
return iActivePageIndex;
};
/**
* Handles 'touchstart' event
*
* @param oEvent
*/
Carousel.prototype.ontouchstart = function(oEvent) {
if (!this.getPages().length || !this._bIsInitialized) {
return;
}
const sTargetTag = oEvent.target.tagName.toLowerCase();
if (["input", "textarea", "select"].indexOf(sTargetTag) > -1 || oEvent.target.isContentEditable) {
return;
}
if (this._isPageIndicatorArrow(oEvent.target)) {
// prevent upcoming focusin event on the arrow and focusout on the active page
this._bPageIndicatorArrowPress = true;
oEvent.preventDefault();
return;
}
if (oEvent.target.draggable) {
// Some elements like images are draggable by default.
// When swiped they begin dragging as ghost images (eg. dragstart event).
// This dragging behavior is not desired when inside a Carousel, so we disable it.
// Note that preventDefault() prevents next events to happen (in particular focusin), so disable the dragging via property
oEvent.target.draggable = false;
}
if (oEvent.isMarked("delayedMouseEvent")) {
return;
}
//add event handler flags
var oElement = Element.closestTo(oEvent.target);
if (oElement &&
(oElement.isA("sap.m.Slider") ||
oElement.isA("sap.m.Switch") ||
oElement.isA("sap.m.IconTabBar"))) {
//Make sure that swipe is executed for all controls except those that
//themselves require horizontal swiping
this._bDragCanceled = true;
return;
}
this._bDragging = true;
this._bDragCanceled = false;
this._mCurrentXY = getCursorPosition(oEvent);
this._iDx = 0;
this._iDy = 0;
this._bDragThresholdMet = false;
// Disable smooth transitions
this.$().addClass("sapMCrslDragging");
this._bLockLeft = this._iCurrSlideIndex === 1;
this._bLockRight = this._iCurrSlideIndex === this.getPages().length - 1;
};
/**
* Handles 'touchmove' event
*
* @param oEvent
*/
Carousel.prototype.ontouchmove = function(oEvent) {
if (this._isPageIndicatorArrow(oEvent.target)) {
return;
}
if (!this._bDragging || this._bDragCanceled || oEvent.isMarked("delayedMouseEvent")) {
return;
}
// mark the event for components that need to know if the event was handled by the carousel
oEvent.setMarked();
var iDragLimit = this.$().width();
var mNewXY = getCursorPosition(oEvent);
this._iDx = this._mCurrentXY.x - mNewXY.x;
this._iDy = this._mCurrentXY.y - mNewXY.y;
if (this._bDragThresholdMet || Math.abs(this._iDx) > Math.abs(this._iDy) && (Math.abs(this._iDx) > iDragRadius)) {
this._bDragThresholdMet = true;
// prevent default action when mouse drag is used
if (isPlainObject(oEvent.touches[0])) {
oEvent.preventDefault();
}
if (this._bLockLeft && (this._iDx < 0)) {
this._iDx = this._iDx * (-iDragLimit) / (this._iDx - iDragLimit);
} else if (this._bLockRight && (this._iDx > 0)) {
this._iDx = this._iDx * (iDragLimit) / (this._iDx + iDragLimit);
}
this._iOffsetDrag = -this._iDx;
this._updateTransformValue();
} else if ((Math.abs(this._iDy) > Math.abs(this._iDx)) && (Math.abs(this._iDy) > iDragRadius)) {
this._bDragCanceled = true;
}
};
/**
* Handles 'touchend' event
*
* @param oEvent
*/
Carousel.prototype.ontouchend = function(oEvent) {
if (this._isPageIndicatorArrow(oEvent.target)) {
return;
}
if (!this._bDragging || oEvent.isMarked("delayedMouseEvent")) {
return;
}
this._bDragging = false;
this.$().removeClass("sapMCrslDragging");
if (!this._bDragCanceled && Math.abs(this._iDx) > iMoveRadius) {
// Move to the next slide if necessary
if (this._iDx > 0) {
bRtl ? this.previous() : this.next();
} else {
bRtl ? this.next() : this.previous();
}
} else {
// Reset back to regular position
this._iOffsetDrag = 0;
this._updateTransformValue();
}
};
//================================================================================
// Keyboard handling
//================================================================================
/**
* Handler for 'tab previous' key event.
*
* @param {Object} oEvent - key event
* @private
*
*/
Carousel.prototype.onsaptabprevious = function(oEvent) {
this._bDirection = false;
if (this._isSlide(oEvent.target) || oEvent.target === this.getDomRef("noData")) {
this._forwardTab(false);
}
};
/**
* Handler for 'tab next' key event.
*
* @param {Object} oEvent - key event
* @private
*
*/
Carousel.prototype.onsaptabnext = function(oEvent) {
this._bDirection = true;
var $activePageTabbables = this._getActivePageTabbables();
if (!$activePageTabbables.length || oEvent.target === $activePageTabbables.get(-1)) {
this._forwardTab(true);
}
};
Carousel.prototype._forwardTab = function (bForward) {
this.getDomRef(bForward ? "after" : "before").focus();
};
Carousel.prototype._getActivePageTabbables = function () {
return this.$(this.getPages()[this._iFocusedPageIndex].getId() + "-slide").find(":sapTabbable");
};
/**
* Focus the last interactive element inside the active page, or the page itself
* @param {jQuery.Event} oEvent the event
*/
Carousel.prototype._focusPrevious = function(oEvent) {
var oActivePageDomRef = this.getFocusDomRef();
if (!oActivePageDomRef) {
return;
}
var $activePage = jQuery(oActivePageDomRef);
var $activePageTabbables = this._getActivePageTabbables();
$activePage.add($activePageTabbables).eq(-1).trigger("focus");
};
/**
* Handler for focus event
*
* @param {Object} oEvent - The event object
*/
Carousel.prototype.onfocusin = function(oEvent) {
if (oEvent.target === this.getDomRef("before") && !this.getDomRef().contains(oEvent.relatedTarget)) {
this.getFocusDomRef().focus();
return;
}
if (oEvent.target === this.getDomRef("after") && !this.getDomRef().contains(oEvent.relatedTarget)) {
this._focusPrevious(oEvent);
return;
}
if (this._isSlide(oEvent.target)) {
this.addStyleClass("sapMCrslShowArrows");
}
this._handlePageElemFocus(oEvent.target);
this._updateItemsAttributes(this._iFocusedPageIndex);
// Save focus reference
this.saveLastFocusReference(oEvent);
// Reset the reference for future use
this._bDirection = undefined;
};
Carousel.prototype.onfocusout = function(oEvent) {
if (this._isSlide(oEvent.target)) {
this.removeStyleClass("sapMCrslShowArrows");
}
};
/**
* When any element is focused with mouse set its containing page focused page
* @param {HTMLElement} oFocusedElement The focused element
*/
Carousel.prototype._handlePageElemFocus = function(oFocusedElement) {
var oPage;
if (this._isSlide(oFocusedElement)) {
oPage = Element.closestTo(jQuery(oFocusedElement).find(".sapMCrsPage")[0]);
} else {
oPage = this._getClosestPage(oFocusedElement);
}
if (oPage) {
var sPageId = oPage.getId();
this._iFocusedPageIndex = this._getPageIndex(sPageId);
}
};
/**
* Handler for key down
*
* @param {Object} oEvent - key object
*/
Carousel.prototype.onkeydown = function(oEvent) {
if (oEvent.keyCode == KeyCodes.F7) {
this._handleF7Key(oEvent);
return;
}
if (!this._isSlide(oEvent.target)) {
return;
}
switch (oEvent.keyCode) {
// Minus keys
// TODO KeyCodes.MINUS is not returning 189
case 189:
case KeyCodes.NUMPAD_MINUS:
this._fnSkipToIndex(oEvent, -1, false);
break;
// Plus keys
case KeyCodes.PLUS:
case KeyCodes.NUMPAD_PLUS:
this._fnSkipToIndex(oEvent, 1, false);
break;
}
};
/**
* Move focus to the next item. If focus is on the last item, do nothing.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsapright = function(oEvent) {
this._fnSkipToIndex(oEvent, 1, false);
};
/**
* Move focus to the next item. If focus is on the last item, do nothing.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsapup = function(oEvent) {
this._fnSkipToIndex(oEvent, -1, false);
};
/**
* Move focus to the previous item. If focus is on the first item, do nothing.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsapleft = function(oEvent) {
this._fnSkipToIndex(oEvent, -1, false);
};
/**
*
* Move focus to the next item. If focus is on the last item, do nothing.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsapdown = function(oEvent) {
this._fnSkipToIndex(oEvent, 1, false);
};
/**
* Move focus to the first item.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsaphome = function(oEvent) {
this._fnSkipToIndex(oEvent, -this._iFocusedPageIndex, true);
};
/**
* Move focus to the last item.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsapend = function(oEvent) {
this._fnSkipToIndex(oEvent, this.getPages().length - this._iFocusedPageIndex - 1, true);
};
/**
* Move focus 10 items to the right. If there are less than 10 items right, move
* focus to last item.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsaprightmodifiers = function(oEvent) {
if (oEvent.ctrlKey) {
this._fnSkipToIndex(oEvent, Carousel._MODIFIERNUMBERFORKEYBOARDHANDLING, true);
}
};
/**
* Move focus 10 items to the right. If there are less than 10 items right, move
* focus to last item.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsapupmodifiers = function(oEvent) {
if (oEvent.ctrlKey) {
this._fnSkipToIndex(oEvent, Carousel._MODIFIERNUMBERFORKEYBOARDHANDLING, true);
}
};
/**
* Move focus 10 items to the right. If there are less than 10 items right, move
* focus to last item.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsappageup = function(oEvent) {
this._fnSkipToIndex(oEvent, Carousel._MODIFIERNUMBERFORKEYBOARDHANDLING, true);
};
/**
* Move focus 10 items to the left. If there are less than 10 items left, move
* focus to first item.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsapleftmodifiers = function(oEvent) {
if (oEvent.ctrlKey) {
this._fnSkipToIndex(oEvent, -Carousel._MODIFIERNUMBERFORKEYBOARDHANDLING, true);
}
};
/**
* Move focus 10 items to the left. If there are less than 10 items left, move
* focus to first item.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsapdownmodifiers = function(oEvent) {
if (oEvent.ctrlKey) {
this._fnSkipToIndex(oEvent, -Carousel._MODIFIERNUMBERFORKEYBOARDHANDLING, true);
}
};
/**
* Move focus 10 items to the left. If there are less than 10 items left, move
* focus to first item.
*
* @param {Object} oEvent - key event
* @private
*/
Carousel.prototype.onsappagedown = function(oEvent) {
this._fnSkipToIndex(oEvent, -Carousel._MODIFIERNUMBERFORKEYBOARDHANDLING, true);
};
/**
* Save reference of the last focused element for each page
*
* @param {Object} oEvent - The event object
* @private
*/
Carousel.prototype.saveLastFocusReference = function(oEvent) {
var oClosestPage = this._getClosestPage(oEvent.target),
sFocusedPageId;
// Don't save focus references triggered from the mouse
if (this._bDirection === undefined) {
return;
}
if (this._lastFocusablePageElement === undefined) {
this._lastFocusablePageElement = {};
}
if (oClosestPage) {
sFocusedPageId = oClosestPage.getId();
this._lastFocusablePageElement[sFocusedPageId] = oEvent.target;
}
};
/**
* Returns the last element that has been focused in the last focused active page.
* @returns {Element | undefined} HTML DOM or undefined
* @private
*/
Carousel.prototype._getActivePageLastFocusedElement = function() {
if (this._lastFocusablePageElement) {
return this._lastFocusablePageElement[this.getActivePage()];
}
};
/**
* Updates the currently active (visible) pages.
* @param {number} sNewActivePageId - The new active page ID
* @private
*/
Carousel.prototype._updateActivePages = function(sNewActivePageId) {
var iNewPageIndex = this._getPageIndex(sNewActivePageId),
iNumberOfItemsToShown = this._getNumberOfItemsToShow(),
aAllPages = this.getPages(),
iLastPageIndex;
if (!aAllPages.length) {
return;
}
// When CarouselLayout is used, the index of the activePage should not exceed allPages count minus the number of visible pages
iNewPageIndex = Math.min(iNewPageIndex, aAllPages.length - iNumberOfItemsToShown);
iLastPageIndex = iNewPageIndex + iNumberOfItemsToShown;
this._aAllActivePages = [];
this._aAllActivePagesIndexes = [];
for (var i = iNewPageIndex; i < iLastPageIndex; i++) {
this._aAllActivePages.push(aAllPages[i].getId());
this._aAllActivePagesIndexes.push(i);
}
};
/**
* Change active page via keyboard
*
* @param {Object} oEvent - The event object
* @param {int} iOffset - The index offset from the currently active page.
* @param {int} bPreventLoop Whether to prevent potential loop
* @private
*/
Carousel.prototype._fnSkipToIndex = function(oEvent, iOffset, bPreventLoop) {
if (!this._isSlide(oEvent.target)) {
return;
}
oEvent.preventDefault();
var iSkipToIndex = this._makeInRange(this._iFocusedPageIndex + iOffset, bPreventLoop);
var sOldActivePageId = this.getActivePage();
var iNewSlideIndex = this._iCurrSlideIndex + iOffset;
if (bPreventLoop) {
iNewSlideIndex = Math.max(0, Math.min(iNewSlideIndex, this.getPages().length - this._getNumberOfItemsToShow()));
}
if (!this._isPageDisplayed(iSkipToIndex)) {
this._bShouldFireEvent = false;
this._moveToPage(iNewSlideIndex, iSkipToIndex);
this._bShouldFireEvent = true;
this._sOldActivePageId = sOldActivePageId;
}
this._changeActivePage(this._aAllActivePagesIndexes[0]);
this._updateItemsAttributes(iSkipToIndex);
this._focusPage(iSkipToIndex);
};
Carousel.prototype._isPageDisplayed = function (iIndex) {
return this._aAllActivePagesIndexes.includes(iIndex);
};
/**
* Handler for F7 key
* @param {Object} oEvent - key object
* @private
*/
Carousel.prototype._handleF7Key = function (oEvent) {
var oActivePageLastFocusedElement = this._getActivePageLastFocusedElement();
if (this._isSlide(oEvent.target) && oActivePageLastFocusedElement) {
oActivePageLastFocusedElement.focus();
} else {
this.getFocusDomRef().focus();
}
};
Carousel.prototype._isSlide = function (oElement) {
return oElement.id.endsWith("slide") && oElement.parentElement === this.getDomRef().querySelector(Carousel._INNER_SELECTOR);
};
Carousel.prototype._isPageIndicatorArrow = function (oElement) {
return oElement.classList.contains("sapMCrslArrow");
};
Carousel.prototype._loops = function () {
return this.getLoop() && this._getNumberOfItemsToShow() === 1;
};
/**
* @param {int} iIndex Page index
* @param {boolean} bPreventLoop Whether to prevent loop if index is out of range
* @returns {int} index in range of pages aggregation
*/
Carousel.prototype._makeInRange = function (iIndex, bPreventLoop) {
var iPagesLength = this.getPages().length;
var iIndexInRange = iIndex;
var bLoops = this._loops();
if (iIndex >= iPagesLength) {
if (bLoops && !bPreventLoop) {
iIndexInRange = 0;
} else {
iIndexInRange = iPagesLength - 1;
}
} else if (iIndex < 0) {
if (bLoops && !bPreventLoop) {
iIndexInRange = iPagesLength - 1;
} else {
iIndexInRange = 0;
}
}
return iIndexInRange;
};
/**
* Searches for the parent page of the given child element
* @param {HTMLElement} oElement The child element
* @returns {sap.ui.core.Control} The page
*/
Carousel.prototype._getClosestPage = function (oElement) {
return Element.closestTo(oElement.closest(".sapMCrsPage"));
};
//================================================================================
// DEPRECATED METHODS
//================================================================================
/*
* API method to set whether the carousel should display the busy indicators.
* This property has been deprecated since 1.18.7. Does nothing and returns the carousel reference.
*
* @deprecated
* @public
*/
Carousel.prototype.setShowBusyIndicator = function() {
Log.warning("sap.m.Carousel: Deprecated function 'setShowBusyIndicator' called. Does nothing.");
return this;
};
/*
* API method to check whether the carousel should display the busy indicators.
* This property has been deprecated since 1.18.7. Always returns false,
*
* @deprecated
* @public
*/
Carousel.prototype.getShowBusyIndicator = function() {
Log.warning("sap.m.Carousel: Deprecated function 'getShowBusyIndicator' called. Does nothing.");
return false;
};
/*
* @see sap.ui.core.Control#setBusyIndicatorSize
* Original property was depracated so we removed it, but made it failsafe
* by mapping a 'wrong' input value to the new enum.
*
* @public
*/
Carousel.prototype.setBusyIndicatorSize = function(sSize) {
if (!(sSize in BusyIndicatorSize)) {
sSize = BusyIndicatorSize.Medium;
}
return Control.prototype.setBusyIndicatorSize.call(this, sSize);
};
Carousel.prototype.onclick = function (oEvent) {
var oTarget = oEvent.target;
switch (oTarget.id) {
case this.getId() + "-arrow-next":
this.next();
break;
case this.getId() + "-arrow-previous":
this.previous();
break;
}
};
Carousel.prototype._initialize = function () {
var $inner = this.$().find('> .sapMCrslList > .sapMCrslInner'),
iNumberOfItemsToShow = this._getNumberOfItemsToShow();
this._bIsInitialized = false;
if (this._iTimeoutId) {
clearTimeout(this._iTimeoutId);
delete this._iTimeoutId;
}
$inner.addClass("sapMCrslNoTransition");
this._iOffsetDrag = 0;
this._initActivePages();
this._bIsInitialized = true;
if (iNumberOfItemsToShow > 1) {
this._setWidthOfPages(iNumberOfItemsToShow);
}
this._adjustArrowsVisibility();
this._updateItemsAttributes(this._getActivePageIndex());
this._updatePageIndicator();
this._updateTransformValue();
this._iTimeoutId = setTimeout(function () {
$inner.removeClass("sapMCrslNoTransition");
}, 50);
};
Carousel.prototype._updateTransformValue = function () {
if (this.getPages().length === 0) {
return;
}
var $element = this.$(),
$inner = $element.find('> .sapMCrslList > .sapMCrslInner'),
$items = $inner.children(),
$start = $items.eq(0),
$current = $items.eq(this._iCurrSlideIndex),
currentOffset,
startOffset,
iOffset,
x;
if (!$inner.length) {
return;
}
currentOffset = $current.prop('offsetLeft') + $current.prop('clientWidth');
startOffset = $start.prop('offsetLeft') + $start.prop('clientWidth');
iOffset = startOffset - currentOffset;
x = Math.round(iOffset + this._iOffsetDrag);
translateX($inner[0], x);
};
Carousel.prototype._initActivePages = function () {
var sActiveClass = "sapMCrslActive",
$element = this.$(),
$inner = $element.find('> .sapMCrslList > .sapMCrslInner'),
$items = $inner.children(),
sId = this.getDomRef().id,
sPageIndicatorId = sId.replace(/(:|\.)/g,'\\$1') + '-pageIndicator',
iIndex = this._iCurrSlideIndex,
i;
for (i = 0; i < $items.length; i++) {
if (i < iIndex || i > iIndex + this._getNumberOfItemsToShow() - 1) {
$items.eq(i).removeClass(sActiveClass);
} else {
$items.eq(i).addClass(sActiveClass);
}
}
$element.find('span[data-slide]').removeClass(sActiveClass);
$element.find('#' + sPageIndicatorId + ' > [data-slide=\'' + (iIndex + 1) + '\']').addClass(sActiveClass);
};
return Carousel;
});