@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,624 lines (1,399 loc) • 68.1 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides control sap.m.TileContainer.
sap.ui.define([
'./library',
'sap/ui/core/Control',
'sap/ui/core/Core',
'sap/ui/core/Element',
'sap/ui/core/IconPool',
'sap/ui/Device',
'sap/ui/core/ResizeHandler',
'./TileContainerRenderer',
"sap/base/Log",
"sap/ui/thirdparty/jquery",
// jQuery custom selectors ':sapTabbable'
"sap/ui/dom/jquery/Selectors"
],
function(
library,
Control,
oCore,
Element,
IconPool,
Device,
ResizeHandler,
TileContainerRenderer,
Log,
jQuery
) {
"use strict";
/**
* Constructor for a new TileContainer.
*
* @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
* A container that arranges same-size tiles nicely on carousel pages.
* @extends sap.ui.core.Control
*
* @author SAP SE
* @version 1.117.4
*
* @constructor
* @public
* @since 1.12
* @deprecated as of version 1.50, replaced by a container of your choice with {@link sap.m.GenericTile} instances
* @alias sap.m.TileContainer
*/
var TileContainer = Control.extend("sap.m.TileContainer", /** @lends sap.m.TileContainer.prototype */ {
metadata : {
library : "sap.m",
deprecated: true,
properties : {
/**
* Defines the width of the TileContainer in px.
*/
width : {type : "sap.ui.core.CSSSize", group : "Dimension", defaultValue : '100%'},
/**
* Defines the height of the TileContainer in px.
*/
height : {type : "sap.ui.core.CSSSize", group : "Dimension", defaultValue : '100%'},
/**
* Determines whether the TileContainer is editable so you can move, delete or add tiles.
*/
editable : {type : "boolean", group : "Misc", defaultValue : null},
/**
* Determines whether the user is allowed to add Tiles in Edit mode (editable = true).
*/
allowAdd : {type : "boolean", group : "Misc", defaultValue : null}
},
defaultAggregation : "tiles",
aggregations : {
/**
* The Tiles to be displayed by the TileContainer.
*/
tiles : {type : "sap.m.Tile", multiple : true, singularName : "tile"}
},
events : {
/**
* Fires if a Tile is moved.
*/
tileMove : {
parameters : {
/**
* The Tile that has been moved.
*/
tile : {type : "sap.m.Tile"},
/**
* The new index of the Tile in the tiles aggregation.
*/
newIndex : {type : "int"}
}
},
/**
* Fires if a Tile is deleted in Edit mode.
*/
tileDelete : {
parameters : {
/**
* The deleted Tile.
*/
tile : {type : "sap.m.Tile"}
}
},
/**
* Fires when a Tile is added.
*/
tileAdd : {}
}
},
renderer: TileContainerRenderer
});
IconPool.insertFontFaceStyle();
TileContainer.prototype._bRtl = oCore.getConfiguration().getRTL();
/**
* Initializes the control.
*
* @private
*/
TileContainer.prototype.init = function() {
this._iCurrentTileStartIndex = 0;
//keeps info about last known container dimension in order to reduce the access to the DOM. Guarantee up to date
//value hooking into the resize handler and onAfterRendering.
this._oDim = null;
this._iScrollLeft = 0;
this._iScrollGap = 0; // gap to the left and right that is allowed to be moved while touchmove event if max scrollwidth or min scrollwidth is already reached
if (!Device.system.desktop) {
this._iScrollGap = 0;
}
this.bAllowTextSelection = false;
this._oDragSession = null;
this._oTouchSession = null;
this._bAvoidChildTapEvent = false;
// the amount on the left and right during drag drop of a tile needed to start showing the edge of the page
this._iEdgeShowStart = Device.system.phone ? 10 : 20;
// the amount of pixels a tile needs to be moved over the left or right edge to trigger a scroll
if (Device.system.phone) {
this._iTriggerScrollOffset = 10;
} else if (Device.system.desktop) {
this._iTriggerScrollOffset = -40;
} else {
this._iTriggerScrollOffset = 20;
}
// keyboard support
this._iCurrentFocusIndex = -1;
if (Device.system.desktop || Device.system.combi) {
var fnOnHome = jQuery.proxy(function(oEvent) {
if (this._iCurrentFocusIndex >= 0) {
var iRowFirstTileIndex = this._iCurrentFocusIndex - this._iCurrentFocusIndex % this._iMaxTilesX;
var iFirstOnPageOrVeryFirstIndex = this._iCurrentTileStartIndex === this._iCurrentFocusIndex ? 0 : this._iCurrentTileStartIndex;
var iTargetTileIndex = oEvent.ctrlKey
// if we are on the first tile of the current page already, go to the very first tile
? iFirstOnPageOrVeryFirstIndex
: iRowFirstTileIndex;
var oFirstTile = this._getVisibleTiles()[iTargetTileIndex];
if (oFirstTile) {
this._findTile(oFirstTile.$()).trigger("focus");
// event should not trigger any further actions
oEvent.stopPropagation();
}
this._handleAriaActiveDescendant();
}
}, this),
fnOnEnd = jQuery.proxy(function(oEvent) {
if (this._iCurrentFocusIndex >= 0) {
var oTiles = this._getVisibleTiles();
var iRowFirstTileIndex = this._iCurrentFocusIndex - this._iCurrentFocusIndex % this._iMaxTilesX;
var iRowLastTileIndex = iRowFirstTileIndex + this._iMaxTilesX < oTiles.length ? iRowFirstTileIndex + this._iMaxTilesX - 1 : oTiles.length - 1;
var iLastTileIndex = this._iCurrentTileStartIndex + this._iMaxTiles < oTiles.length ? this._iCurrentTileStartIndex + this._iMaxTiles - 1 : oTiles.length - 1;
var iLastOnPageOrVeryLastIndex = iLastTileIndex === this._iCurrentFocusIndex ? oTiles.length - 1 : iLastTileIndex;
var iTargetTileIndex = oEvent.ctrlKey
? iLastOnPageOrVeryLastIndex
: iRowLastTileIndex;
if (oTiles.length > 0) {
this._findTile(oTiles[iTargetTileIndex].$()).trigger("focus");
// event should not trigger any further actions
oEvent.stopPropagation();
}
this._handleAriaActiveDescendant();
}
}, this),
fnOnPageUp = jQuery.proxy(function(oEvent) {
var aTiles = this._getVisibleTiles();
if (aTiles.length > 0) {
var iNextIndex = this._iCurrentFocusIndex - this._iMaxTiles >= 0 ? this._iCurrentFocusIndex - this._iMaxTiles : 0;
var oNextTile = aTiles[iNextIndex];
if (oNextTile) {
this._renderTilesInTheSamePage(iNextIndex, aTiles);
this._findTile(oNextTile.$()).trigger("focus");
// event should not trigger any further actions
oEvent.stopPropagation();
}
this._handleAriaActiveDescendant();
}
}, this),
fnOnPageDown = jQuery.proxy(function(oEvent) {
var aTiles = this._getVisibleTiles(),
iTilesCount = aTiles.length;
if (iTilesCount > 0) {
var iNextIndex = this._iCurrentFocusIndex + this._iMaxTiles < iTilesCount ? this._iCurrentFocusIndex + this._iMaxTiles : iTilesCount - 1;
var oNextTile = aTiles[iNextIndex];
if (oNextTile) {
this._renderTilesInTheSamePage(iNextIndex, aTiles);
this._findTile(oNextTile.$()).trigger("focus");
// event should not trigger any further actions
oEvent.stopPropagation();
}
this._handleAriaActiveDescendant();
}
}, this),
fnOnRight = jQuery.proxy(function(oEvent) {
if (this._iCurrentFocusIndex >= 0) {
var aTiles = this._getVisibleTiles();
var iNextIndex = this._iCurrentFocusIndex + 1 < aTiles.length ? this._iCurrentFocusIndex + 1 : this._iCurrentFocusIndex;
if (!oEvent.ctrlKey) {
var oNextTile = aTiles[iNextIndex];
if (oNextTile) {
if (iNextIndex < this._iCurrentTileStartIndex + this._iMaxTiles) { // tile on same page?
this._findTile(oNextTile.$()).trigger("focus");
} else {
this._renderTilesInTheSamePage(iNextIndex, aTiles);
this.scrollIntoView(oNextTile, true, aTiles);
var that = this;
setTimeout(function() {
that._findTile(oNextTile.$()).trigger("focus");
}, 400);
}
}
} else if (this.getEditable()) {
var oTile = aTiles[this._iCurrentFocusIndex];
this.moveTile(oTile, iNextIndex);
oTile.$().trigger("focus");
}
this._handleAriaActiveDescendant();
// event should not trigger any further actions
oEvent.stopPropagation();
}
}, this),
fnOnLeft = jQuery.proxy(function(oEvent) {
if (this._iCurrentFocusIndex >= 0) {
var aTiles = this._getVisibleTiles();
var iNextIndex = this._iCurrentFocusIndex - 1 >= 0 ? this._iCurrentFocusIndex - 1 : this._iCurrentFocusIndex;
if (!oEvent.ctrlKey) {
var oNextTile = aTiles[iNextIndex];
if (oNextTile) {
if (iNextIndex >= this._iCurrentTileStartIndex) { // tile on same page?
this._findTile(oNextTile.$()).trigger("focus");
} else {
this._renderTilesInTheSamePage(iNextIndex, aTiles);
this.scrollIntoView(oNextTile, true, aTiles);
var that = this;
setTimeout(function () {
that._findTile(oNextTile.$()).trigger("focus");
}, 400);
}
}
} else if (this.getEditable()) {
var oTile = aTiles[this._iCurrentFocusIndex];
this.moveTile(oTile, iNextIndex);
oTile.$().trigger("focus");
}
this._handleAriaActiveDescendant();
// event should not trigger any further actions
oEvent.stopPropagation();
}
}, this),
fnOnDown = jQuery.proxy(function(oEvent) {
var oTiles = this._getVisibleTiles();
if (this._iCurrentFocusIndex >= 0) {
var iModCurr = this._iCurrentFocusIndex % this._iMaxTiles,
iNextIndex = this._iCurrentFocusIndex + this._iMaxTilesX,
iModNext = iNextIndex % this._iMaxTiles;
if (!oEvent.ctrlKey) {
var oNextTile = oTiles[iNextIndex];
if ((iModNext > iModCurr) && oNextTile) {
// '(iModNext > iModCurr)' means: still on same page
this._findTile(oNextTile.$()).trigger("focus");
}
} else if (this.getEditable()) {
var oTile = oTiles[this._iCurrentFocusIndex];
this.moveTile(oTile, iNextIndex);
oTile.$().trigger("focus");
}
this._handleAriaActiveDescendant();
// event should not trigger any further actions
oEvent.stopPropagation();
}
}, this),
fnOnUp = jQuery.proxy(function(oEvent) {
var oTiles = this._getVisibleTiles();
if (this._iCurrentFocusIndex >= 0) {
var iModCurr = this._iCurrentFocusIndex % this._iMaxTiles,
iNextIndex = this._iCurrentFocusIndex - this._iMaxTilesX,
iModNext = iNextIndex % this._iMaxTiles;
if (!oEvent.ctrlKey) {
var oNextTile = oTiles[iNextIndex];
if ((iModNext < iModCurr) && oNextTile) {
// '(iModNext < iModCurr)' means: still on same page
this._findTile(oNextTile.$()).trigger("focus");
}
} else if (this.getEditable()) {
var oTile = oTiles[this._iCurrentFocusIndex];
this.moveTile(oTile, iNextIndex);
oTile.$().trigger("focus");
}
this._handleAriaActiveDescendant();
// event should not trigger any further actions
oEvent.stopPropagation();
}
}, this),
fnOnDelete = jQuery.proxy(function(oEvent) {
var oTiles = this._getVisibleTiles();
if (this._iCurrentFocusIndex >= 0 && this.getEditable()) {
var oTile = oTiles[this._iCurrentFocusIndex];
if (oTile.getRemovable()) {
this.deleteTile(oTile);
oTiles = this._getVisibleTiles();
if (this._iCurrentFocusIndex === oTiles.length) {
if (oTiles.length !== 0) {
oTiles[this._iCurrentFocusIndex - 1].$().trigger("focus");
} else {
this._findNextTabbable().trigger("focus");
}
} else {
oTiles[this._iCurrentFocusIndex].$().trigger("focus");
}
this._handleAriaActiveDescendant();
}
oEvent.stopPropagation();
}
}, this);
this.onsaphome = fnOnHome;
this.onsaphomemodifiers = fnOnHome;
this.onsapend = fnOnEnd;
this.onsapendmodifiers = fnOnEnd;
this.onsapright = this._bRtl ? fnOnLeft : fnOnRight;
this.onsaprightmodifiers = this._bRtl ? fnOnLeft : fnOnRight;
this.onsapleft = this._bRtl ? fnOnRight : fnOnLeft;
this.onsapleftmodifiers = this._bRtl ? fnOnRight : fnOnLeft;
this.onsapup = fnOnUp;
this.onsapupmodifiers = fnOnUp;
this.onsapdown = fnOnDown;
this.onsapdownmodifiers = fnOnDown;
this.onsappageup = fnOnPageUp;
this.onsappagedown = fnOnPageDown;
this.onsapdelete = fnOnDelete;
this.data("sap-ui-fastnavgroup", "true", true); // Define group for F6 handling
}
if (Device.system.tablet || Device.system.phone) {
this._fnOrientationChange = function(oEvent) {
if (this.getDomRef()) {
this._oTileDimensionCalculator.calc();
//there is not need to call this._update, because resize event will be triggered also, where it is called
}
}.bind(this);
}
this._oTileDimensionCalculator = new TileDimensionCalculator(this);
this._bRtl = oCore.getConfiguration().getRTL();
//Keeps info about the current page and total page count. In addition the old(previous) values of the same are kept.
this._oPagesInfo = (function (bRightToLeftMode) {
var iCurrentPage, iCount,
iOldCurrentPage, iOldCount,
bPagerCreated = false,
bRtl = bRightToLeftMode;
return {
/* Zero based index of the current page */
setCurrentPage: function (currentPage) {
iOldCurrentPage = iCurrentPage;
iCurrentPage = currentPage;
},
setCount: function (count) {
iOldCount = iCount;
iCount = count;
},
/*Sets that the pager with dots is created*/
setPagerCreated: function(created) {
bPagerCreated = created;
},
/*Sets the old values the same as the current*/
syncOldToCurrentValues: function() {
iOldCount = iCount;
iOldCurrentPage = iCurrentPage;
},
reset: function() {
iOldCount = undefined;
iOldCurrentPage = undefined;
iCount = undefined;
iCurrentPage = undefined;
bPagerCreated = false;
},
getCurrentPage: function () {
return iCurrentPage;
},
getCount: function () {
return iCount;
},
getOldCurrentPage: function () {
return iOldCurrentPage;
},
getOldCount: function () {
return iOldCount;
},
/*If the pager with dots is created*/
isPagerCreated: function() {
return bPagerCreated;
},
/*Checks if the current page is the last page (considers RTL)*/
currentPageIsLast: function() {
return bRtl ? (iCurrentPage === 0) : (iCurrentPage === iCount - 1);
},
/*Checks if the current page is the first page (considers RTL)*/
currentPageIsFirst: function() {
return bRtl ? (iCurrentPage === iCount - 1) : (iCurrentPage === 0);
},
oldCurrentPageIsLast: function() {
if (isNaN(iOldCurrentPage)) {
return false;
}
return bRtl ? (iOldCurrentPage === 0) : (iOldCurrentPage === iOldCount - 1);
},
oldCurrentPageIsFirst: function() {
if (isNaN(iOldCurrentPage)) {
return false;
}
return bRtl ? (iOldCurrentPage === iOldCount - 1) : (iOldCurrentPage === 0);
},
/*Is the 'currentPage is last' has changed. Example - it wasn't last before, but now it is and vice versa*/
currentPageIsLastChanged: function() {
return this.currentPageIsLast() !== this.oldCurrentPageIsLast();
},
/*Is the 'currentPage is first' has changed. Example - it wasn't first before, but now it is and vice versa*/
currentPageIsFirstChanged: function() {
return this.currentPageIsFirst() !== this.oldCurrentPageIsFirst();
},
/* true if current page's relative position is changed - the page becomes first, last or was first or last and now it is not*/
currentPageRelativePositionChanged: function() {
return this.currentPageIsFirstChanged() || this.currentPageIsLastChanged();
},
pageCountChanged: function() {
return iCount !== iOldCount;
},
currentPageChanged: function() {
return iCurrentPage !== iOldCurrentPage;
}
};
}(this._bRtl));
//make sure we start from starting meaningful, otherwise we may not have right value unless height is given.
this._iMaxTiles = 1;
};
/**
* Finds the next tabbable element after the TileContainer.
* @returns {Element} The next tabbable element after the tile container
* @private
*/
TileContainer.prototype._findNextTabbable = function() {
var $Ref = this.$();
var $Tabbables = jQuery.merge(
jQuery.merge($Ref.nextAll(), $Ref.parents().nextAll()).find(':sapTabbable').addBack(':sapTabbable'),
jQuery.merge($Ref.parents().prevAll(), $Ref.prevAll()).find(':sapTabbable').addBack(':sapTabbable')
);
return $Tabbables.first();
};
/**
* Handles the internal event onBeforeRendering.
*
* @private
*/
TileContainer.prototype.onBeforeRendering = function () {
var aTiles = this.getTiles(),
iTilesCount = aTiles.length;
// unregister the resize listener
if (this._sResizeListenerId) {
ResizeHandler.deregister(this._sResizeListenerId);
this._sResizeListenerId = null;
}
this._oPagesInfo.reset();
for (var i = 0; i < iTilesCount; i++) {
aTiles[i]._rendered = false;
}
};
/**
* Handles the internal event onAfterRendering.
*
* @private
*/
TileContainer.prototype.onAfterRendering = function() {
var aVisibleTiles = [];
// init resizing
this._sResizeListenerId = ResizeHandler.register(this.getDomRef().parentElement, jQuery.proxy(this._resize, this));
// init the dimensions to the container scoll area
this._oDim = this._calculateDimension();
this._applyDimension();
this.$().toggleClass("sapMTCEditable",this.getEditable() === true);
if (this._bRenderFirstPage) { //Set by the TileContainerRenderer if it cannot determine the size of the tiles per page
this._bRenderFirstPage = false;
aVisibleTiles = this._getVisibleTiles();
this._updateTileDimensionInfoAndPageSize(aVisibleTiles);
if (this.getTiles().length === 1) {
// in case of only one tile, it was rendered
// but still needs it's position and visibility to be updated
this._update(false, aVisibleTiles);
} else if (this._iMaxTiles !== Infinity && this._iMaxTiles ) {
this._renderTiles(aVisibleTiles, 0, this._iMaxTiles - 1);
}
} else {
this._update(true);
}
if (Device.system.desktop || Device.system.combi) {
var aTiles = aVisibleTiles || this._getVisibleTiles();
if (aTiles.length > 0 && this._mFocusables && this._mFocusables[aTiles[0].getId()]) {
this._mFocusables[aTiles[0].getId()].eq(0).attr('tabindex', '0');
}
}
if (Device.system.tablet || Device.system.phone) {
Device.orientation.attachHandler(this._fnOrientationChange, this);
}
};
/**
* Sets the editable property to the TileContainer, allowing to move icons.
* This is currently also set with a long tap.
*
* @param {boolean} bValue Whether the container is in edit mode or not
* @returns {this} this pointer for chaining
* @public
*/
TileContainer.prototype.setEditable = function(bValue) {
var aTiles = this._getVisibleTiles();
// set the property
this.setProperty("editable", bValue, true);
var bEditable = this.getEditable();
this.$().toggleClass("sapMTCEditable", bEditable);
for (var i = 0;i < aTiles.length; i++) {
var oTile = aTiles[i];
if (oTile.isA("sap.m.Tile")) {
oTile.isEditable(bEditable);
}
}
return this; // allow chaining;
};
/**
* Called whenever the model is updated
*
* @private
*/
TileContainer.prototype.updateTiles = function () {
this.destroyTiles();
this.updateAggregation('tiles');
};
/**
* Applies the container's dimensions.
*
* @private
*/
TileContainer.prototype._applyDimension = function() {
var oDim = this._getDimension(),
$this = this.$(),
oThisPos,
iOffset = 10,
$scroll = this.$("scrl"),
scrollPos,
scrollOuterHeight,
pagerHeight = this.$("pager").outerHeight();
$scroll.css({
width : oDim.outerwidth + "px",
height : (oDim.outerheight - pagerHeight) + "px"
});
oThisPos = $this.position();
scrollPos = $scroll.position();
scrollOuterHeight = $scroll.outerHeight();
if (Device.system.phone) {
iOffset = 2;
} else if (Device.system.desktop) {
iOffset = 0;
}
this.$("blind").css({
top : (scrollPos.top + iOffset) + "px",
left : (scrollPos.left + iOffset) + "px",
right: "auto",
width : ($scroll.outerWidth() - iOffset) + "px",
height : (scrollOuterHeight - iOffset) + "px"
});
this.$("rightedge").css({
top : (oThisPos.top + iOffset) + "px",
right : iOffset + "px",
left : "auto",
height : (scrollOuterHeight - iOffset) + "px"
});
this.$("leftedge").css({
top : (oThisPos.top + iOffset) + "px",
left : (oThisPos.left + iOffset) + "px",
right: "auto",
height : (scrollOuterHeight - iOffset) + "px"
});
};
/**
* Handles the resize event for the TileContainer.
* Called whenever the orientation of browser size changes.
*
* @private
*/
TileContainer.prototype._resize = function() {
if (this._oDragSession) {
return;
}
setTimeout(jQuery.proxy(function() {
var aVisibleTiles = this._getVisibleTiles(),
iTilesCount = aVisibleTiles.length,
iCurrentPageStartTileIndex = this._iCurrentTileStartIndex,
oOldDim = this._oDim,
iNewPage, iNewPageTileStartIndex, iNewPageTileEndIndex;
this._oPagesInfo.reset();
this._oDim = this._calculateDimension();
this._updateTileDimensionInfoAndPageSize(aVisibleTiles);
if (oOldDim.width !== this._oDim.width || oOldDim.height !== this._oDim.height) {
//remove all previously rendered tiles(should be a few pages)
// in order to make sure the don't interfere with the new
for (var i = 0; i < iTilesCount; i++) {
if (aVisibleTiles[i]._rendered) {
aVisibleTiles[i]._rendered = false;
aVisibleTiles[i].$().remove();
}
}
iNewPage = this._getPageNumberForTile(iCurrentPageStartTileIndex);
iNewPageTileStartIndex = iNewPage * this._iMaxTiles;
iNewPageTileEndIndex = iNewPageTileStartIndex + this._iMaxTiles - 1;
this._renderTiles(aVisibleTiles, iNewPageTileStartIndex, iNewPageTileEndIndex);
}
},this), 0);
};
/**
* Called from parent if the control is destroyed.
*
* @private
*/
TileContainer.prototype.exit = function() {
if (this._sResizeListenerId) {
ResizeHandler.deregister(this._sResizeListenerId);
this._sResizeListenerId = null;
}
if (Device.system.tablet || Device.system.phone) {
Device.orientation.detachHandler(this._fnOrientationChange, this);
}
delete this._oPagesInfo;
};
/**
* Updates all Tiles.
* @param {boolean} bAnimated to apply animation during update
* @param {sap.m.Tile[]} [aVisibleTiles] optional list of visible tiles in order to avoid filtering them again.
* @return {void}
* @private
*/
TileContainer.prototype._update = function(bAnimated, aVisibleTiles) {
if (!this.getDomRef()) {
return;
}
if (!this.getVisible()) {
return;
}
aVisibleTiles = aVisibleTiles || this._getVisibleTiles();
this._oTileDimensionCalculator.calc(aVisibleTiles);
this._updateTilePositions(aVisibleTiles);
if (!this._oDragSession) {
this.scrollIntoView(this._iCurrentTileStartIndex || 0, bAnimated, aVisibleTiles);
}
};
/**
* Returns the index of the first Tile visible in the current page.
*
* @returns {int} The index of the first Tile that is visible in the current page
* @public
*/
TileContainer.prototype.getPageFirstTileIndex = function() {
return this._iCurrentTileStartIndex || 0;
};
/**
* Moves a given Tile to the given index.
*
* @param {sap.m.Tile} vTile The tile to move
* @param {int} iNewIndex The new Tile position in the tiles aggregation
* @returns {this} this pointer for chaining
* @public
*/
TileContainer.prototype.moveTile = function(vTile, iNewIndex) {
if (!isNaN(vTile)) {
vTile = this._getVisibleTiles()[vTile];
}
if (!vTile) {
Log.info("No Tile to move");
return this;
}
this.deleteTile(vTile);
this.insertTile(vTile, iNewIndex);
return this;
};
/**
* Adds a Tile to the end of the tiles collection.
*
* @param {sap.m.Tile} oTile The tile to add
* @returns {this} this pointer for chaining
* @override
* @public
*/
TileContainer.prototype.addTile = function(oTile) {
this.insertTile(oTile,this.getTiles().length);
return this;
};
/**
* Inserts a Tile to the given index.
*
* @param {sap.m.Tile} oTile The Tile to insert
* @param {int} iIndex The new Tile position in the tiles aggregation
* @returns {this} this pointer for chaining
* @override
* @public
*/
TileContainer.prototype.insertTile = function(oTile, iIndex) {
var that = this,
aVisibleTiles;
oTile.isEditable(this.getEditable());
// keyboard support for desktop environments
if (Device.system.desktop || Device.system.combi) {
oTile.addEventDelegate({
"onAfterRendering": function() {
if (!that._mFocusables) {
that._mFocusables = {};
}
that._mFocusables[this.getId()] = this.$().find("[tabindex!='-1']").addBack().filter(that._isFocusable);
that._mFocusables[this.getId()].attr('tabindex', '-1');
}
}, oTile);
var fnOnFocusIn = function(oEvent) {
var iIndex = that._getVisibleTiles().indexOf(this),
iExpectedPage = Math.floor(iIndex / that._iMaxTiles),
iPageDelta = iExpectedPage - that._oPagesInfo.getCurrentPage();
var iPreviousTileIndex = that._iCurrentFocusIndex >= 0 ? that._iCurrentFocusIndex : 0;
var aVTiles = that._getVisibleTiles();
var oPrevTile = aVTiles[iPreviousTileIndex];
if (oPrevTile) {
that._mFocusables[oPrevTile.getId()].attr("tabindex", "-1");
that._mFocusables[this.getId()].attr("tabindex", "0");
}
if (iPageDelta != 0) {
that.scrollIntoView(iIndex, null, aVTiles);
}
that._handleAriaActiveDescendant();
that._iCurrentFocusIndex = iIndex;
};
oTile.addEventDelegate({
"onfocusin": fnOnFocusIn
}, oTile);
}
if (this.getDomRef()) {
this.insertAggregation("tiles", oTile, iIndex, true);
aVisibleTiles = this._getVisibleTiles();
if (!this._oDragSession) {
//Render the tiles and reposition the rest if the tile is visible and inserted at position that needs other tiles repositioning.
// Ex. 12 tiles, 3 pages x 4 tiles, current page is 2, tile inserted at index 0 (page 1) - should render.
if (oTile.getVisible() &&
(aVisibleTiles.length === 1 || this._getPageNumberForTile(iIndex) <= this._oPagesInfo.getCurrentPage())) {
this._renderTile(oTile, iIndex);
this._update(false, aVisibleTiles);//updates also the page's count
} else {//we just need to update the pager
this._oPagesInfo.setCount(Math.ceil(aVisibleTiles.length / this._iMaxTiles));
this._updatePager();
}
} else {
this._update(false, aVisibleTiles);
}
// When the control is initialized/updated with data binding and optimization for rendering
// tile by tile is used we need to be sure we have a focusable tile.
if (Device.system.desktop || Device.system.combi) {
this._updateTilesTabIndex(aVisibleTiles);
}
} else {
this.insertAggregation("tiles",oTile,iIndex);
aVisibleTiles = this._getVisibleTiles();
}
if (oTile.getVisible()) {
handleAriaPositionInSet.call(this, iIndex, aVisibleTiles.length, aVisibleTiles);
handleAriaSize.call(this, aVisibleTiles);
}
return this;
};
/**
* Updates the tab index of the Tiles.
* If there is no focusable Tile (for example, tabindex = 0), updates the first tile.
* @private
*/
TileContainer.prototype._updateTilesTabIndex = function (aVisibleTiles) {
aVisibleTiles = aVisibleTiles || this._getVisibleTiles();
if (aVisibleTiles.length && aVisibleTiles.length > 0) {
for (var i = 0; i < aVisibleTiles.length; i++) {
if (aVisibleTiles[i].$().attr("tabindex") === "0") {
return;
}
}
}
aVisibleTiles[0].$().attr("tabindex", "0");
};
/**
* Checks if a DOM element is focusable.
* To be used within jQuery.filter function.
* @param {int} index Index of the element within an array
* @param {Element} element DOM element to check
* @returns {boolean} If a DOM element is focusable
* @private
*/
TileContainer.prototype._isFocusable = function(index, element) {
var isTabIndexNotNaN = !isNaN(jQuery(element).attr("tabindex"));
var nodeName = element.nodeName.toLowerCase();
if ( nodeName === "area" ) {
var map = element.parentNode,
mapName = map.name,
img;
if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
return false;
}
img = jQuery( "img[usemap='#" + mapName + "']" )[0];
return !!img;
}
/*eslint-disable no-nested-ternary */
return ( /input|select|textarea|button|object/.test( nodeName )
? !element.disabled
: nodeName == "a"
? element.href || isTabIndexNotNaN
: isTabIndexNotNaN);
/*eslint-enable no-nested-ternary */
};
/**
* Deletes a Tile.
*
* @param {sap.m.Tile} oTile The tile to move
* @returns {this} this pointer for chaining
* @override
* @public
*/
TileContainer.prototype.deleteTile = function(oTile) {
var aVisibleTiles = this._getVisibleTiles(),
iDeletedTileIndex = this._indexOfVisibleTile(oTile, aVisibleTiles);
if (this.getDomRef()) {
aVisibleTiles.splice(iDeletedTileIndex, 1);
this.removeAggregation("tiles",oTile,true);
if (!this._oDragSession) {
if (oTile.getDomRef()) {
oTile.getDomRef().parentNode.removeChild(oTile.getDomRef());
}
if (Device.system.desktop || Device.system.combi) {
if (this._mFocusables && this._mFocusables[oTile.getId()]) {
delete this._mFocusables[oTile.getId()];
}
}
}
if (aVisibleTiles.length === 0) {
this._oPagesInfo.reset();
} else if (oTile.getVisible() && iDeletedTileIndex >= 0
&& this._getPageNumberForTile(iDeletedTileIndex) <= this._oPagesInfo.getCurrentPage()) {
this._renderTilesInTheSamePage(this._oPagesInfo.getCurrentPage() * this._iMaxTiles, aVisibleTiles);
}
this._update(false);
} else {
this.removeAggregation("tiles",oTile,false);
aVisibleTiles = this._getVisibleTiles();
}
handleAriaPositionInSet.call(this, iDeletedTileIndex, aVisibleTiles.length);
handleAriaSize.call(this, aVisibleTiles);
return this;
};
TileContainer.prototype.removeTile = TileContainer.prototype.deleteTile;
TileContainer.prototype.removeAllTiles = function() {
var iTileCount = this.getTiles().length - 1; //Zero based index
for (var iIndex = iTileCount; iIndex >= 0; iIndex--) {
var oTile = this.getTiles()[iIndex];
this.deleteTile(oTile);
}
return this;
};
TileContainer.prototype.destroyTiles = function(){
if (this.getDomRef()) {
var aTiles = this.getTiles();
this.removeAllAggregation("tiles", true);
this._oPagesInfo.reset();
this._update();
for (var i = 0;i < aTiles.length; i++) {
var tile = aTiles[i];
tile.destroy();
}
} else {
this.destroyAggregation("tiles", false);
}
return this;
};
TileContainer.prototype.rerender = function() {
if (!this._oDragSession || this._oDragSession.bDropped) {
Control.prototype.rerender.apply(this);
}
};
/**
* Scrolls one page to the left.
*
* @public
*/
TileContainer.prototype.scrollLeft = function() {
var iScrollToIndex = 0,
aVisibleTiles = this._getVisibleTiles();
if (this._bRtl) {
iScrollToIndex = this._iCurrentTileStartIndex + this._iMaxTiles;
} else {
iScrollToIndex = this._iCurrentTileStartIndex - this._iMaxTiles;
}
this._renderTiles(aVisibleTiles, iScrollToIndex, iScrollToIndex + this._iMaxTiles - 1);
this.scrollIntoView(iScrollToIndex, null, aVisibleTiles);
};
/**
* Scrolls one page to the right.
*
* @public
*/
TileContainer.prototype.scrollRight = function() {
var iScrollToIndex = 0,
aVisibleTiles = this._getVisibleTiles();
if (this._bRtl) {
iScrollToIndex = this._iCurrentTileStartIndex - this._iMaxTiles;
} else {
iScrollToIndex = this._iCurrentTileStartIndex + this._iMaxTiles;
}
this._renderTiles(aVisibleTiles, iScrollToIndex, iScrollToIndex + this._iMaxTiles - 1);
this.scrollIntoView(iScrollToIndex, null, aVisibleTiles);
};
/**
* Renders all tiles (if not rendered yet) that share the same page as the given tile
* @param {int} tileIndex the given tile whose page of tiles should be rendered
* @param {sap.m.Tile[]} tiles tiles to check against
* @private
* @returns {void}
*/
TileContainer.prototype._renderTilesInTheSamePage = function(tileIndex, tiles) {
var iTilePage = this._getPageNumberForTile(tileIndex),
iFirstTileInPage = iTilePage * this._iMaxTiles,
iLastTileInPage = iFirstTileInPage + this._iMaxTiles - 1;
this._renderTiles(tiles, iFirstTileInPage, iLastTileInPage);
};
/**
* Renders any tile in given range if it is not rendered yet.
* @param {sap.m.Tile[]} tiles tiles list
* @param {int} startIndex start position of a tile in the given tiles list
* @param {int} endIndex end position (inclusive) of a tile in the given tiles list
* @private
* @returns {void}
*/
TileContainer.prototype._renderTiles = function(tiles, startIndex, endIndex) {
var bNewTilesRendered = false,
i;
for (i = startIndex; i <= endIndex; i++) {
if (tiles[i] && !tiles[i]._rendered) {
this._renderTile(tiles[i], i);
bNewTilesRendered = true;
}
}
if (bNewTilesRendered) {
this._update(false, tiles);
// When the control is initialized/updated with data binding and optimization for rendering
// tile by tile is used we need to be sure we have a focusable tile.
if (Device.system.desktop || Device.system.combi) {
this._updateTilesTabIndex();
}
}
};
/**
* Scrolls to the page where the given Tile or tile index is included.
* Optionally this can be done animated or not. With IE9 the scroll is never animated.
*
* @param {sap.m.Tile|int} vTile The Tile or tile index to be scrolled into view
* @param {boolean} bAnimated Whether the scroll should be animated
* @param {sap.m.Tile[]} [aVisibleTiles] optional list of visible tiles in order to avoid filtering them again.
* @public
*/
TileContainer.prototype.scrollIntoView = function(vTile, bAnimated, aVisibleTiles) {
var iContentWidth = this._getContentDimension().outerwidth,
iIndex = vTile,
aAllTiles = this.getTiles();
if (isNaN(vTile)) {
iIndex = this.indexOfAggregation("tiles",vTile);
}
if (!aAllTiles[iIndex] || !aAllTiles[iIndex].getVisible()) {
return;
}
aVisibleTiles = aVisibleTiles || this._getVisibleTiles();
iIndex = this._indexOfVisibleTile(aAllTiles[iIndex]);//find tile's index amongst visible tiles
if (iIndex > -1) {
this._renderTilesInTheSamePage(iIndex, aVisibleTiles);
}
this._applyPageStartIndex(iIndex, aVisibleTiles);
this._oPagesInfo.setCurrentPage(Math.floor(this._iCurrentTileStartIndex / this._iMaxTiles));
if (this._bRtl) {
this._scrollTo((this._oPagesInfo.getCount() - this._oPagesInfo.getCurrentPage()) * iContentWidth, bAnimated);
} else {
this._scrollTo(this._oPagesInfo.getCurrentPage() * iContentWidth, bAnimated);
}
this._updatePager();
};
/**
* Updates the tile positions only of the rendered tiles.
* Tile property _rendered is set inside Tile.js onAfterRendering.
*
* @private
*/
TileContainer.prototype._updateTilePositions = function(aVisibleTiles){
var oDim = this._getDimension();
if (oDim.height === 0) {// nothing to do because the height of the content is not (yet) available
return;
}
aVisibleTiles = aVisibleTiles || this._getVisibleTiles();
if (aVisibleTiles.length === 0) { // no tiles
this._oPagesInfo.setCount(0); // no tiles no pages
this._updatePager();
return;
}
this._applyPageStartIndex(this._iCurrentTileStartIndex, aVisibleTiles);
this._applyDimension();
var oContentDimension = this._getContentDimension();
this._oPagesInfo.setCount(Math.ceil(aVisibleTiles.length / this._iMaxTiles));
var oTileDimension = this._oTileDimensionCalculator.getLastCalculatedDimension();
for (var i = 0; i < aVisibleTiles.length; i++) {
if (!aVisibleTiles[i]._rendered || aVisibleTiles[i].isDragged()) {
continue;
}
var iPage = Math.floor(i / this._iMaxTiles),
oTile = aVisibleTiles[i],
iLeft = (iPage * oContentDimension.outerwidth) + this._iOffsetX + i % this._iMaxTilesX * oTileDimension.width,
iTop = this._iOffsetY + Math.floor(i / this._iMaxTilesX) * oTileDimension.height - (iPage * this._iMaxTilesY * oTileDimension.height);
if (this._bRtl) {
iLeft = (this._oPagesInfo.getCount() - iPage) * oContentDimension.outerwidth - this._iOffsetX - (i % this._iMaxTilesX + 1) * oTileDimension.width;
}
oTile.setPos(iLeft,iTop);
oTile.setSize(oTileDimension.width, oTileDimension.height);
}
};
/**
* Finds a Tile.
* Convenience method, which returns $node if it has Css class sapMTile
* or the first child with that class.
* @param {jQuery.object} $node The node to be examined
* @returns {jQuery.object} The first node which has the class
* @private
*/
TileContainer.prototype._findTile = function($node) {
if ($node.hasClass('sapMTile') || $node.hasClass('sapMCustomTile')) {
return $node;
} else {
// return $node.find('.sapMTile');
return $node.find('.sapMTile') || $node.find('.sapMCustomTile');
}
};
/**
* Updates the pager part of the TileContainer.
* This is done dynamically.
*
* @private
*/
TileContainer.prototype._updatePager = function() {
var oPager,
oScrollLeft,
oScrollRight,
aHTML,
/* true if the pager is created as part of this function*/
bPagerJustCreated = false;
if (!this._oPagesInfo.pageCountChanged() && !this._oPagesInfo.currentPageChanged()) {
return;
}
oPager = this.$("pager")[0];
oScrollLeft = this.$("leftscroller")[0];
oScrollRight = this.$("rightscroller")[0];
if (this._oPagesInfo.getCount() == undefined || this._oPagesInfo.getCount() <= 1) { //reset pager if there is no need of it
oPager.innerHTML = "";
oScrollRight.style.right = "-100px";
oScrollLeft.style.left = "-100px";
oScrollLeft.style.display = "none";
oScrollRight.style.display = "none";
this._oPagesInfo.setPagerCreated(false);
return;
}
if (!this._oPagesInfo.isPagerCreated()) {
aHTML = [""];
for (var i = 0; i < this._oPagesInfo.getCount(); i++) {
aHTML.push("");
}
oPager.innerHTML = aHTML.join("<span></span>");
oPager.style.display = "block";
oPager.childNodes[0].className = "sapMTCActive"; //initially active page is the 1st(span)
this._oPagesInfo.setPagerCreated(true);
bPagerJustCreated = true;
} else if (this._oPagesInfo.pageCountChanged()) {
if (this._oPagesInfo.getCount() - this._oPagesInfo.getOldCount() < 0) {//one page less
oPager.removeChild(oPager.lastChild);
} else {
oPager.appendChild(document.createElement("span")); //one page more
}
}
if (this._oPagesInfo.currentPageChanged()) {
oPager.childNodes[this._oPagesInfo.getCurrentPage()].className = "sapMTCActive";
if (oPager.childNodes[this._oPagesInfo.getOldCurrentPage()]) {
oPager.childNodes[this._oPagesInfo.getOldCurrentPage()].className = "";
}
if (this._oPagesInfo.getCurrentPage() >= 1) { //deactivate the initially active page (span)
oPager.childNodes[0].className = "";
}
}
if (Device.system.desktop && (bPagerJustCreated || this._oPagesInfo.currentPageRelativePositionChanged())) {
if (this._bRtl) {
// Less builder swaps left and right in RTL styles,
// and that is not required here, otherwise left scroller will go right and vice versa.
oScrollRight.style.left = "auto";
oScrollLeft.style.right = "auto";
}
oScrollRight.style.right = this._oPagesInfo.currentPageIsLast() ? "-100px" : "1rem";
oScrollLeft.style.left = this._oPagesInfo.currentPageIsFirst() ? "-100px" : "1rem";
oScrollRight.style.display = this._oPagesInfo.currentPageIsLast() ? "none" : "block";
oScrollLeft.style.display = this._oPagesInfo.currentPageIsFirst() ? "none" : "block";
}
this._oPagesInfo.syncOldToCurrentValues();
};
/**
* Returns the dimension (width and height) of the pages content.
*
* @returns {object} Width and height of the pages content
* @private
*/
TileContainer.prototype._getContentDimension = function() {
if (!this.getDomRef()) {
return;
}
var oScroll = this.$("scrl");
return {
width : oScroll.width(),
height : oScroll.height() - 20,
outerheight : oScroll.outerHeight() - 20,
outerwidth : oScroll.outerWidth()
};
};
/**
* Returns the dimension (width and height) of the TileContainer content.
*
* @returns {{width, height, outerheight, outerwidth}|{width, height: *, outerheight: *, outerwidth: *}|*|null}
* Width and height of the pages content
* @private
*/
TileContainer.prototype._getDimension = function() {
if (!this._oDim) {
this._oDim = this._calculateDimension();
}
return this._oDim;
};
/**
* Calculates the Tile page sizes, i.e. how many tiles per X, Y and page can be rendered
*
* @private
*/
TileContainer.prototype._calculatePageSize = function(aVisibleTiles) {
var oDim,
iTiles;
aVisibleTiles = aVisibleTiles || this._getVisibleTiles();
iTiles = aVisibleTiles.length;
if (iTiles === 0) {// no tiles
return;
}
oDim = jQuery.extend({}, this._getDimension());
if (oDim.height === 0) { // nothing to do because the height of the content is not (yet) available
return;
}
if (Device.system.desktop) {
oDim.width -= 45 * 2;
}
var oTileDimension = this._oTileDimensionCalculator.getLastCalculatedDimension(),
iPagerHeight = this.$("pager")[0].offsetHeight,
iMaxTilesX = Math.max( Math.floor( oDim.width / oTileDimension.width ),1), //at least one tile needs to be visible
iMaxTilesY = Math.max( Math.floor((oDim.height - iPagerHeight) / oTileDimension.height),1), //at least one tile needs to be visible
iNumTileX = (iTiles < iMaxTilesX) ? iTiles : iMaxTilesX,
iNumTileY = (iTiles / iNumTileX < iMaxTilesY) ? Math.ceil(iTiles / iNumTileX) : iMaxTilesY;
// set the member vars for further usage
this._iMaxTiles = iMaxTilesX * iMaxTilesY;
this._iMaxTilesX = iMaxTilesX;
this._iMaxTilesY = iMaxTilesY;
this._iOffsetX = Math.floor(( oDim.width - (oTileDimension.width * iNumTileX)) / 2);
if (Device.system.desktop) {
this._iOffsetX += 45;
}
this._iOffsetY = Math.floor(( oDim.height - iPagerHeight - (oTileDimension.height * iNumTileY )) / 2);
};
/**
* Gets Tiles from a given position.
* Returns an array for a given pixel position in the TileContainer.
* Normally, there is only one Tile for a position.
*
* @param {int} iX Position in px
* @param {int} iY Position in px
* @returns {array} Array of Tiles for the given position
* @private
*/
TileContainer.prototype._getTilesFromPosition = function(iX, iY) {
if (!this._getVisibleTiles().length) {
return [];
}
iX = iX + this._iScrollLeft;
var aTiles = this._getVisibleTiles(),
aResult = [];
for (var i = 0;i < aTiles.length;i++) {
var oTile = aTiles[i],
oRect = {
top: oTile._posY,
left: oTile._posX,
width: oTile._width,
height: oTile._height
};
if (!aTiles[i].isDragged() && iY > oRect.top && iY < oRect.top + oRect.height && iX > oRect.left && iX < oRect.left + oRect.width) {
aResult.push(aTiles[i]);
}
}
return aResult;
};
/**
* Applies the start index of the pages' first Tile according to the given index.
*
* @param {int} iIndex The index of the tile that should be visible
* @param {sap.m.Tile[]} [aVisibleTiles] optional list of visible tiles in order to avoid filtering them again.
* @private
*/
TileContainer.prototype._applyPageStartIndex = function (iIndex, aVisibleTiles) {
var oContentDimension = this._getDimension();
if (oContentDimension.height === 0) { // nothing to do because the height of the content is not (yet) available
return;
}
aVisibleTiles = aVisibleTiles || this._getVisibleTiles();
this._calculatePageSize(aVisibleTiles);
var iLength = aVisibleTiles.length;
if (iIndex < 0) {
iIndex = 0;
} else if (iIndex > iLength - 1) {
iIndex = iLength - 1;
}
// where does the page start
var iCurrentPage = Math.floor(iIndex / this._iMaxTiles || 0);
this._iCurrentTileStartIndex = iCurrentPage * (this._iMaxTiles || 0);
Log.info("current index " + this._iCurrentTileStartIndex);
};
/**
* Scrolls to the given position.
*
* @param {int} iScrollLeft The new scroll position
* @param {boolean} bAnimated Whether the scroll is animated
* @private
*/
TileContainer.prototype._scrollTo = function(iScrollLeft, bAnimated) {
if (bAnimated !== false) {
bAnimated = true; // animated needs to be set explicitly to false
}
this._applyTranslate(this.$("cnt"), -iScrollLeft, 0, bAnimated);
if (this._bRtl) {
this._iScrollLeft = iScrollLeft - this._getContentDimension().outerwidth;
} else {
this._iScrollLeft = iScrollLeft;
}
};
/**
* Applies the translate x and y to the given jQuery object.
*
* @param {object} o$ The jQuery object
* @param {int} iX The px x value for the translate
* @param {int} iY The px y value for the translate
* @param {boolean} bAnimated Whether the translate should be animated or not
* @private
*/
TileContainer.prototype._applyTranslate = function(o$, iX, iY, bAnimated) {
var o = o$[0];
this.$("cnt").toggleClass("sapMTCAnim",bAnimated);
if ("webkitTransform" in o.style) {
o$.css('-webkit-transform','translate3d(' + iX + 'px,' + iY + 'px,0)');
} else if ("MozTransform" in o.style) {
o$.css('-moz-transform','translate(' + iX + 'px,' + iY + 'px)');
} else if ("transform" in o.style) {
o$.css('transform','translate3d(' + iX + 'px,' + iY + 'px,0)');
} else if ("msTransform" in o.style) {
o$.css('-ms-transform','translate(' + iX + 'px,' + iY + 'px)');
}
};
/**
* Initializes the touch session for the TileContainer.
*
* @param {jQuery.Event} oEvent The event object that started the touch
* @private
*/
TileContainer.prototype._initTouchSession = function(oEvent) {
if (oEvent.type == "touchstart") {
var targetTouches = oEvent.targetTouches[0];
this._oTouchSession = {
dStartTime : new Date(),
fStartX : targetTouches.pageX,
fStartY : targetTouches.pageY,
fDiffX : 0,
fDiffY : 0,
oControl : oEvent.srcControl,
iOffsetX : targetTouches.pageX - oEvent.target.offsetLeft
};
} else { // mousedown
this._oTouchSession = {
dStartTime : new Date(),
fStartX : oEvent.pageX,
fStartY : oEvent.pageY,
fDiffX : 0,
fDiffY : 0,
oControl : oEvent.srcControl,
iOffsetX : oEvent.pageX - oEvent.target.offsetLeft
};
}
};
/**
* Initializes the drag session for the TileContainer.
*
* @param {jQuery.Event} oEvent The event object that started the drag
* @private
*/
TileContainer.prototype._initDragSession = function(oEvent) {
while (oEvent.srcControl && oEvent.srcControl.getParent() != this) {
oEvent.srcControl = oEvent.srcControl.getParent();
}
var iIndex = this.indexOfAggregation("tiles",oEvent.srcControl);
if (oEvent.type == "touchstart") {
this._oDragSession = {
oTile : oEvent.srcControl,
oTileElement : oEvent.srcControl.$()[0],
iOffsetLeft : oEvent.targetTouches[0].pageX - oEvent.srcControl._posX + this._iScrollLeft,
iOffsetTop : oEvent.targetTouches[0].pageY - oEvent.srcControl._posY,
iIndex : iIndex,
iOldIndex : iIndex,
iDiffX : oEvent.targetTouches[0].pageX,
iDiffY : oEvent.targetTouches[0].pageY
};
} else { // mousedown
this._oDragSession = {
oTile : oEvent.srcControl,
oTileElement : oEvent.srcControl.$()[0],
iOffsetLeft : oEvent.pageX - oEvent.srcControl._posX + this._iScrollLeft,
iOffsetTop : oEvent.pageY - oEvent.srcControl._posY,
iIndex : iIndex,
iOldIndex : iIndex,
iDiffX : oEvent.pageX,
iDiffY : oEvent.pageY
};
}
};
/**
* Handles click events for scrollers on desktop.
*
* @param {jQuery.Event} oEvent The event object that started the drag
* @private
*/
TileContainer.prototype.onclick = function(oEvent) {
var oPager = this.$("pager")[0];
if (oEvent.target.id == this.getId() + "-leftscroller" || oEvent.target.parentNode.id == this.getId() + "-leftscroller") {
this.scrollLeft();
} else if (oEvent.target.id == this.getId() + "-rightscroller" || oEvent.target.parentNode.id == this.getId() + "-rightscroller") {
this.scrollRight();
} else if (oEvent.target == oPager && Device.system.desktop) {
if (oEvent.offsetX < oPager.offsetWidth / 2) {
this.scrollLeft();
} else {
this.scrollRight();
}
}
};
/**
* Handles the touchstart event on the TileContainer.
*
* @param {jQuery.Event} oEvent The event object
* @private
*/
TileContainer.prototype.ontouchstart = function(oEvent) {
// mark the event for components that needs to know if the event was handled by this control.
oEvent.setMarked();
if (oEvent.targetTouches.length > 1 || this._oTouchSession) { // allow only one touch session
return;
}
while (oEvent.srcControl && oEvent.srcControl.getParent() != this) {
oEvent.srcControl = oEvent.srcControl.getParent();
}
if (oEvent.srcControl && oEvent.srcControl.isA("sap.m.Tile") && this.getEditable()) {
if (oEvent.target.className != "sapMTCRemove") {
this._initDragSession(oEvent);
this._initTouchSession(oEvent);
this._oDragSession.oTile.isDragged(true);
} else {
this._initTouchSession(oEvent);
}
this._bAvoidChildTapEvent = true;
} else {
this._initTou