@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
873 lines (765 loc) • 28.7 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.
*/
/**
* @namespace
* @name sap.ui.core.delegate
* @public
*/
// Provides class sap.ui.core.delegate.ScrollEnablement
sap.ui.define([
'sap/ui/Device',
'sap/ui/base/Object',
'sap/ui/core/Core',
'sap/ui/core/IntervalTrigger',
'sap/ui/core/ResizeHandler',
"sap/ui/thirdparty/jquery",
"sap/ui/events/KeyCodes"
],
function(
Device,
BaseObject,
Core,
IntervalTrigger,
ResizeHandler,
jQuery,
KeyCodes
) {
"use strict";
/**
* Creates a ScrollEnablement delegate that can be attached to Controls requiring
* capabilities for scrolling of a certain part of their DOM.
*
* @class Delegate for touch scrolling on mobile devices.
*
* This delegate uses native scrolling of mobile and desktop browsers. Third party scrolling libraries are not supported.
*
* Controls that implement ScrollEnablement should additionally provide the getScrollDelegate method that returns
* the current instance of this delegate object
*
* @extends sap.ui.base.Object
*
* @param {sap.ui.core.Control} oControl the Control of which this Scroller is the delegate
* @param {string} sScrollContentDom the Id of the element within the DOM of the Control which should be scrollable
* @param {object} oConfig the configuration of the scroll delegate
* @param {boolean} [oConfig.horizontal=false] Whether the element should be scrollable horizontally
* @param {boolean} [oConfig.vertical=false] Whether the element should be scrollable vertically
* @param {boolean} [oConfig.zynga=false] @deprecated since 1.42, the parameter has no effect
* @param {boolean} [oConfig.iscroll=false] @deprecated since 1.42, the parameter has no effect
* @param {boolean} [oConfig.preventDefault=false] @deprecated since 1.42, the parameter has no effect
* @param {boolean} [oConfig.nonTouchScrolling=false] If true, the delegate will also be active to allow touch like scrolling with the mouse on non-touch platforms.
* @param {string} [oConfig.scrollContainerId=""] Native scrolling does not need content wrapper. In this case, ID of the container element should be provided.
*
* @protected
* @alias sap.ui.core.delegate.ScrollEnablement
* @version 1.111.5
* @author SAP SE
*/
var ScrollEnablement = BaseObject.extend("sap.ui.core.delegate.ScrollEnablement", /** @lends sap.ui.core.delegate.ScrollEnablement.prototype */ {
constructor : function(oControl, sScrollContentDom, oConfig) {
BaseObject.apply(this);
this._oControl = oControl;
this._oControl.addDelegate(this);
this._sContentId = sScrollContentDom;
this._sContainerId = oConfig.scrollContainerId;
this._bHorizontal = !!oConfig.horizontal;
this._bVertical = !!oConfig.vertical;
this._scrollX = 0;
this._scrollY = 0;
this._scrollCoef = 0.9; // Approximation coefficient used to mimic page down and page up behaviour when [CTRL] + [RIGHT] and [CTRL] + [LEFT] is used
this._iLastMaxScrollTop = 0;
this._iScrollPaddingTop = 0;
initDelegateMembers(this);
if (this._init) {
this._init.apply(this, arguments);
}
},
/**
* Enable or disable horizontal scrolling.
*
* @param {boolean} bHorizontal set true to enable horizontal scrolling, false - to disable
* @protected
*/
setHorizontal : function(bHorizontal) {
this._bHorizontal = !!bHorizontal;
this._setOverflow && this._setOverflow();
},
/**
* Enable or disable vertical scrolling.
*
* @param {boolean} bVertical set true to enable vertical scrolling, false - to disable
* @protected
*/
setVertical : function(bVertical) {
this._bVertical = !!bVertical;
this._setOverflow && this._setOverflow();
},
/**
* Get current setting for horizontal scrolling.
*
* @return {boolean} true if horizontal scrolling is enabled
* @protected
* @since 1.9.1
*/
getHorizontal : function() {
return this._bHorizontal;
},
/**
* Get current setting for vertical scrolling.
*
* @return {boolean} true if vertical scrolling is enabled
* @protected
* @since 1.9.1
*/
getVertical : function() {
return this._bVertical;
},
/**
* Setter for property <code>bounce</code>.
*
* @param {boolean} bBounce new value for property <code>bounce</code>.
* @protected
* @since 1.17
* @deprecated since 1.42
*/
setBounce: function(bBounce) {
},
/**
* Set overflow control on top of scroll container.
*
* @param {sap.ui.core.Control} oControl Top control that should be normally hidden over
* the top border of the scroll container (pull-down content).
* @protected
* @since 1.9.2
*/
setPullDown : function(oControl) {
this._oPullDown = oControl;
return this;
},
/**
* Sets GrowingList control to scroll container
*
* @param {function} fnScrollLoadCallback Scrolling callback
* @param {sap.m.ListGrowingDirection} sScrollLoadDirection Scrolling direction
* @param {function} fnOverflowChange listener for the <code>overflowChange</code> event
* @protected
* @since 1.11.0
*/
setGrowingList : function(fnScrollLoadCallback, sScrollLoadDirection, fnOverflowChange) {
this._fnScrollLoadCallback = fnScrollLoadCallback;
this._sScrollLoadDirection = sScrollLoadDirection;
this.onOverflowChange(fnOverflowChange);
return this;
},
/**
* Sets the listener for the custom <code>overflowChange</code> event
*
* Only a single listener can be registered
*
* @param {function} fnCallback
* @private
* @ui5-restricted sap.m.GrowingEnablement
* @since 1.74
*/
onOverflowChange : function(fnCallback){
this._fnOverflowChangeCallback = fnCallback;
if (!this._fnOverflowChangeCallback) {
this._deregisterOverflowMonitor();
}
},
/**
* Sets the listener for the end of any <code>scrollToElement</code>
*
* Only a single listener can be registered
*
* @param {function} fnCallback
* @private
* @ui5-restricted sap.uxap.ObjectPageLayout, sap.f.DynamicPage
* @since 1.87
*/
setOnAfterScrollToElement : function(fnCallback){
this._fnAfterScrollToElement = fnCallback;
},
/**
* Sets IconTabBar control to scroll container
*
* @param {sap.m.IconTabBar} oIconTabBar instance
* @param {function} fnScrollEndCallback callback function for the scroll end event
* @param {function} fnScrollStartCallback callback function for the scroll start event
* @protected
* @since 1.16.1
*/
setIconTabBar : function(oIconTabBar, fnScrollEndCallback, fnScrollStartCallback) {
this._oIconTabBar = oIconTabBar;
if (fnScrollEndCallback) {
this._fnScrollEndCallback = fnScrollEndCallback.bind(oIconTabBar);
}
if (fnScrollStartCallback) {
this._fnScrollStartCallback = fnScrollStartCallback.bind(oIconTabBar);
}
return this;
},
/**
* Sets the top offset of the optimal viewing region of the scrollport:
* the region used as the target region for placing things in view of the user.
* This offset is required when the topmost part of the scroll container is overlapped
* by other content (e.g. overlapped by the sticky header content of the scrollable page)
*
* @param {number} iValue
* @private
* @ui5-restricted sap.uxap.ObjectPageLayout, sap.f.DynamicPage
*/
setScrollPaddingTop : function(iValue) {
if (typeof iValue === 'number') {
this._iScrollPaddingTop = iValue;
}
},
/**
* Scrolls to a specific position in scroll container.
* @param {int} x Horizontal position of the scrollbar
* @param {int} y Vertical position of the scrollbar
* @param {int} [time=0]
* The duration of animated scrolling in milliseconds. To scroll immediately without animation,
* give 0 as value.
* @param {function} fnScrollEndCallback Called when the scroll completes or stops without completing
* @returns {this}
* @public
*/
scrollTo : function(x, y, time, fnScrollEndCallback) {
this._scrollX = x; // remember for later rendering
this._scrollY = y;
this._scrollTo(x, y, time, fnScrollEndCallback);
return this;
},
/**
* Calculates scroll position of a child of a container.
* @param {HTMLElement | jQuery} vElement An element(DOM or jQuery) for which the scroll position will be calculated.
* @returns {object} Position object.
* @protected
*/
getChildPosition: function(vElement) {
// check if vElement is a DOM element and if yes convert it to jQuery object
var $Element = vElement instanceof jQuery ? vElement : jQuery(vElement),
oElementPosition = $Element.position(),
$OffsetParent = $Element.offsetParent(),
oAddUpPosition;
// Not positioned elements (position = "unset" or "fixed") are skipped and not returned as offsetParent.
// If the this._$Container is not positioned this function ends in infinite loop.
// Last value returned from offsetParent is the root html element or null.
while (!$OffsetParent.is(this._$Container) && !$OffsetParent.is("html") && $OffsetParent.length) {
oAddUpPosition = $OffsetParent.position();
oElementPosition.top += oAddUpPosition.top;
oElementPosition.left += oAddUpPosition.left;
$OffsetParent = $OffsetParent.offsetParent();
}
return oElementPosition;
},
/**
* Scrolls to an element within a container.
* @param {HTMLElement} oElement A DOM element.
* @param {int} [iTime=0]
* The duration of animated scrolling in milliseconds. To scroll immediately without animation,
* give 0 as value.
* @param {int[]} [aOffset=[0,0]]
* Specifies an additional left and top offset of the target scroll position, relative to
* the upper left corner of the DOM element
* @param {boolean} [bSkipElementsInScrollport] The configuration of the parameter for scrolling only if the element is not in the view port -
* i.e. if bSkipElementsInScrollport is set to true, there will be no scrolling if the element is already in the view port
* @returns {this}
* @protected
*/
scrollToElement: function(oElement, iTime, aOffset, bSkipElementsInScrollport) {
aOffset = aOffset || [0, 0];
// do nothing if _$Container is not a (grand)parent of oElement
if (!this._$Container[0].contains(oElement) ||
oElement.style.display === "none" ||
oElement.offsetParent.nodeName.toUpperCase() === "HTML") {
return this;
}
// the visible part of the scrollport is positioned below the
// <code>this._iScrollPaddingTop</code>, so we offset that padding as well
aOffset[1] -= this._iScrollPaddingTop;
if (bSkipElementsInScrollport && this._isInScrollport(oElement, aOffset)) {
return this;
}
var $Element = jQuery(oElement),
oScrollPosition = this.getChildPosition($Element),
iLeftScroll = this.getScrollLeft() + oScrollPosition.left + aOffset[0],
iTopScroll = this.getScrollTop() + oScrollPosition.top + aOffset[1];
if (this._bFlipX) {
// in IE RTL scrollLeft goes opposite direction
iLeftScroll = this.getScrollLeft() - (oScrollPosition.left - this._$Container.width()) - $Element.width();
}
// scroll to destination
this._scrollTo(iLeftScroll, iTopScroll , iTime, this._fnAfterScrollToElement);
return this;
},
/**
* Destroys this Scrolling delegate.
*
* This function must be called by the control which uses this delegate in the <code>exit</code> function.
* @protected
*/
destroy : function() {
if (this._exit) {
this._exit();
}
if (this._oControl) {
this._oControl.removeDelegate(this);
this._oControl = undefined;
}
},
/**
* Refreshes this Scrolling delegate.
*
* @protected
*/
refresh : function() {
if (this._refresh) {
this._refresh();
}
},
_useDefaultScroll : function(target) {
return target.isContentEditable;
},
onkeydown : function(oEvent) {
if (this._useDefaultScroll(oEvent.target)) {
return;
}
var container = this._$Container[0];
if (oEvent.altKey && this.getHorizontal()) {
switch (oEvent.keyCode) {
case KeyCodes.PAGE_UP:
// Navigate 1 page left
this._customScrollTo(this._scrollX - container.clientWidth, this._scrollY, oEvent);
break;
case KeyCodes.PAGE_DOWN:
// Navigate 1 page right
this._customScrollTo(this._scrollX + container.clientWidth, this._scrollY, oEvent);
break;
}
}
if (oEvent.ctrlKey && !oEvent.altKey) {
switch (oEvent.keyCode) {
case KeyCodes.ARROW_UP:
// [CTRL]+[UP] - 1 page up
if (this.getVertical()) {
this._customScrollTo(this._scrollX, this._scrollY - container.clientHeight * this._scrollCoef, oEvent);
}
break;
case KeyCodes.ARROW_DOWN:
// [CTRL]+[DOWN] - 1 page down
if (this.getVertical()) {
this._customScrollTo(this._scrollX, this._scrollY + container.clientHeight * this._scrollCoef, oEvent);
}
break;
case KeyCodes.ARROW_LEFT:
// [CTRL]+[LEFT] - 1 page left
if (this.getHorizontal()) {
this._customScrollTo(this._scrollX - container.clientWidth, this._scrollY, oEvent);
}
break;
case KeyCodes.ARROW_RIGHT:
// [CTRL]+[RIGHT] - 1 page right
if (this.getHorizontal()) {
this._customScrollTo(this._scrollX + container.clientWidth, this._scrollY, oEvent);
}
break;
case KeyCodes.HOME:
if (this.getHorizontal()) {
this._customScrollTo(0, this._scrollY, oEvent);
}
if (this.getVertical()) {
this._customScrollTo(this._scrollX, 0, oEvent);
}
break;
case KeyCodes.END:
var left = container.scrollWidth - container.clientWidth;
var top = container.scrollHeight - container.clientHeight;
if (!this.getHorizontal()) {
top = this._scrollY;
}
if (!this.getVertical()) {
left = Core.getConfiguration().getRTL() ?
container.clientWidth - container.scrollWidth : this._scrollX;
}
this._customScrollTo(left, top, oEvent);
break;
}
}
},
_customScrollTo : function(left, top, oEvent) {
var sNodeName = oEvent.target.nodeName;
// do not prevent events coming from input controls
if (sNodeName != "INPUT" && sNodeName != "TEXTAREA") {
oEvent.preventDefault();
oEvent.setMarked();
this._scrollTo(left, top);
}
}
});
/* =========================================================== */
/* Native scroll delegate */
/* =========================================================== */
var oNativeScrollDelegate = {
getScrollTop : function() {
return this._scrollY || 0;
},
getScrollLeft : function() {
return this._scrollX || 0;
},
getScrollHeight : function() {
var $Container = this._$Container;
return ($Container && $Container[0]) ? $Container[0].scrollHeight : 0;
},
getMaxScrollTop : function() {
var $Container = this._$Container;
return ($Container && $Container[0]) ? $Container[0].scrollHeight - $Container[0].clientHeight : -1;
},
getContainerDomRef : function() {
return this._$Container && this._$Container[0];
},
_cleanup : function() {
if (this._sResizeListenerId) {
ResizeHandler.deregister(this._sResizeListenerId);
this._sResizeListenerId = null;
}
this._deregisterOverflowMonitor();
},
_setOverflow : function(){
var $Container = this._$Container;
if (!$Container || !$Container[0]) {
return;
}
// Let container scroll into the configured directions
if (Device.os.ios) {
$Container
.css("overflow-x", this._bHorizontal && !this._bDragScroll ? "scroll" : "hidden")
.css("overflow-y", this._bVertical && !this._bDragScroll ? "scroll" : "hidden")
.css("-webkit-overflow-scrolling", "touch");
} else { //other browsers do not support -webkit-overflow-scrolling
$Container
.css("overflow-x", this._bHorizontal && !this._bDragScroll ? "auto" : "hidden")
.css("overflow-y", this._bVertical && !this._bDragScroll ? "auto" : "hidden");
}
},
_refresh : function(){
var $Container = this._$Container;
if (!($Container && $Container.length)) {
return;
}
if (this._oPullDown && this._oPullDown._bTouchMode) {
// hide pull to refresh (except for state 2 - loading)
var domRef = this._oPullDown.getDomRef();
if (domRef) {
domRef.style.marginTop = this._oPullDown._iState == 2 ? "" : "-" + domRef.offsetHeight + "px";
}
}
if ($Container.scrollTop() != this._scrollY) {
$Container.scrollTop(this._scrollY);
}
if (!(this._oPullDown && this._oPullDown._bTouchMode)
&& !this._fnScrollLoadCallback) {
// for IE the resize listener must remain in place for the case when navigating away and coming back.
// For the other browsers it seems to work fine without.
ResizeHandler.deregister(this._sResizeListenerId);
this._sResizeListenerId = null;
}
},
_onScroll: function() {
var $Container = this._$Container,
fScrollTop = $Container.scrollTop(),
fVerticalMove = fScrollTop - this._scrollY;
this._scrollX = $Container.scrollLeft(); // remember position
this._scrollY = fScrollTop;
// Growing List/Table
if (this._fnScrollLoadCallback) {
if (this._sScrollLoadDirection == "Upwards") {
if (fVerticalMove < 0 && fScrollTop < 10) {
this._fnScrollLoadCallback();
}
} else if (fVerticalMove >= 0 && $Container[0].scrollHeight - fScrollTop - $Container[0].clientHeight < 100) {
this._fnScrollLoadCallback();
}
}
// IconTabHeader
if (this._oIconTabBar && this._fnScrollEndCallback) {
this._fnScrollEndCallback();
}
},
_onStart : function(oEvent){
var container = this._$Container[0];
if (!container) {
return;
}
// Drag instead of native scroll
this._bDoDrag = this._bDragScroll;
// Store initial coordinates for drag scrolling
var point = oEvent.touches ? oEvent.touches[0] : oEvent;
this._iX = point.pageX;
this._iY = point.pageY;
this._bPullDown = false;
this._iDirection = ""; // h - horizontal, v - vertical
},
_onTouchMove : function(oEvent){
var container = this._$Container[0];
var point = oEvent.touches ? oEvent.touches[0] : oEvent;
var dx = point.pageX - this._iX;
var dy = point.pageY - this._iY;
if (this._iDirection == "") { // do once at start
if (dx != 0 || dy != 0) {
this._iDirection = Math.abs(dy) > Math.abs(dx) ? "v" : "h";
}
// PullToRefresh: replace native scrolling with drag, but only in this case
if (this._oPullDown && this._oPullDown._bTouchMode && this._iDirection == "v" && container.scrollTop <= 1) {
// pull only of near to top
if (dy > Math.abs(dx)) {
// user drags vertically down, disable native scrolling
this._bPullDown = true;
}
}
}
if (this._bPullDown === true) {
var pd = this._oPullDown.getDomRef();
var top = oEvent.touches[0].pageY - this._iY - pd.offsetHeight;
if ( top > 20) {
top = 20;
}
pd.style.marginTop = top + "px";
// rotate pointer
this._oPullDown.doPull(top);
// prevent scrolling
oEvent.preventDefault();
this._bDoDrag = false; // avoid interference with drag scrolling
}
// Special case for dragging instead of scrolling:
if (this._bDoDrag) {
var scrollLeft = container.scrollLeft,
scrollTop = container.scrollTop;
if (this._bHorizontal) {
if (this._bFlipX) {
container.scrollLeft = scrollLeft - this._iX + point.pageX;
} else {
container.scrollLeft = scrollLeft + this._iX - point.pageX;
}
}
if (this._bVertical) {
container.scrollTop = scrollTop + this._iY - point.pageY;
}
if ((container.scrollLeft != scrollLeft) || (container.scrollTop != scrollTop)) { // if moved
oEvent.setMarked && oEvent.setMarked();
oEvent.preventDefault();
}
this._iX = point.pageX;
this._iY = point.pageY;
return;
}
},
_onEnd : function(oEvent){
if (this._oPullDown && this._oPullDown._bTouchMode) {
this._oPullDown.doScrollEnd();
this._refresh();
}
if (this._bDragScroll && this._iDirection) {
oEvent.setMarked && oEvent.setMarked();
}
},
// Mouse drag scrolling, optional.
// Set options.nonTouchScrolling = true to enable
_onMouseDown : function(oEvent){
// start scrolling only when the left button is pressed
if (this._bDragScroll && oEvent.button == 0) {
this._bScrolling = true;
this._onStart(oEvent);
}
},
_onMouseMove : function(oEvent){
// check if scrolling and the (left) button is pressed
if (this._bScrolling) {
var e = oEvent.originalEvent || oEvent;
var button = e.buttons || e.which;
if (button == 1 || oEvent.pressure) { // either the left mouse button or pen is pressed
var container = this._$Container[0];
if (this._bHorizontal) {
if ( this._bFlipX ) {
container.scrollLeft = container.scrollLeft - this._iX + oEvent.pageX;
} else {
container.scrollLeft = container.scrollLeft + this._iX - oEvent.pageX;
}
}
if (this._bVertical) {
container.scrollTop = container.scrollTop + this._iY - oEvent.pageY;
}
this._iX = oEvent.pageX;
this._iY = oEvent.pageY;
}
}
},
_onMouseUp : function(){
if (this._bScrolling) {
this._bScrolling = false;
this._onEnd();
}
},
onBeforeRendering: function() {
if (this._sResizeListenerId) {
ResizeHandler.deregister(this._sResizeListenerId);
this._sResizeListenerId = null;
}
this._deregisterOverflowMonitor();
var $Container = this._$Container;
if ($Container) {
if ($Container.height() > 0) {
this._scrollX = $Container.scrollLeft(); // remember position
this._scrollY = $Container.scrollTop();
}
$Container.off(); // delete all event handlers
}
},
_checkOverflowChange: function(oEvent) {
var iMaxScrollTop = this.getMaxScrollTop(),
bOverflow = iMaxScrollTop > 0 && this._iLastMaxScrollTop === 0,
bUnderflow = iMaxScrollTop === 0 && this._iLastMaxScrollTop > 0,
bOverflowChange = bOverflow || bUnderflow;
if (bOverflowChange) {
this._fnOverflowChangeCallback(bOverflow, iMaxScrollTop);
}
this._iLastMaxScrollTop = iMaxScrollTop;
},
_registerOverflowMonitor: function() {
this._fnOverflowChangeCallback && IntervalTrigger.addListener(this._checkOverflowChange, this);
},
_deregisterOverflowMonitor: function() {
IntervalTrigger.removeListener(this._checkOverflowChange, this);
},
onAfterRendering: function() {
var $Container = this._$Container = this._sContainerId ? jQuery(document.getElementById(this._sContainerId)) : jQuery(document.getElementById(this._sContentId)).parent();
var _fnRefresh = this._refresh;
var bElementVisible = $Container.is(":visible");
$Container.addClass("sapUiScrollDelegate");
this._setOverflow();
// apply the previous scroll state
if (this._scrollX !== 0 || this._scrollY !== 0) {
this._scrollTo(this._scrollX, this._scrollY);
}
this._refresh();
if (!bElementVisible
|| this._oPullDown
|| this._fnScrollLoadCallback) {
// element may be hidden and have height 0
this._sResizeListenerId = ResizeHandler.register($Container[0], _fnRefresh.bind(this));
}
if (this._fnOverflowChangeCallback) {
this._iOverflowTimer && window.cancelAnimationFrame(this._iOverflowTimer);
this._iOverflowTimer = window.requestAnimationFrame(this._registerOverflowMonitor.bind(this));
}
//
// Set event listeners
//
$Container.on("scroll", this._onScroll.bind(this));
var oContainerRef = $Container[0];
function addEventListeners (sEvents, fListener) {
sEvents.split(" ").forEach(function(sEvent){
oContainerRef && oContainerRef.addEventListener(sEvent, fListener);
});
}
// React on mouse/pen and touch actions accordingly.
// Pen behavior is the same as of the mouse.
function onPointerDown(oEvent) {
return oEvent.pointerType == "touch" ? this._onStart(oEvent) : this._onMouseDown(oEvent);
}
function onPointerMove(oEvent) {
return oEvent.pointerType == "touch" ? this._onTouchMove(oEvent) : this._onMouseMove(oEvent);
}
function onPointerUp(oEvent) {
return oEvent.pointerType == "touch" ? this._onEnd(oEvent) : this._onMouseUp(oEvent);
}
// Set pointer, touch or mouse event listeners, if needed:
if (Device.support.pointer && Device.system.desktop) {
// Desktop + pointer events: drag scroll (IconTabBar, Tokenizer)
if (this._bDragScroll) {
addEventListeners("pointerdown", onPointerDown.bind(this));
addEventListeners("pointermove", onPointerMove.bind(this));
addEventListeners("pointerup pointercancel pointerleave", onPointerUp.bind(this));
}
} else if (Device.support.touch) {
// Drag scroll, PullToRefresh
if (this._bDragScroll || this._oPullDown && this._oPullDown._bTouchMode) {
$Container
.on("touchcancel touchend", this._onEnd.bind(this))
.on("touchstart", this._onStart.bind(this))
.on("touchmove", this._onTouchMove.bind(this));
}
} else if (this._bDragScroll) {
// Everything else: drag scroll
$Container
.on("mouseup mouseleave", this._onMouseUp.bind(this))
.on("mousedown", this._onMouseDown.bind(this))
.on("mousemove", this._onMouseMove.bind(this));
}
},
_readActualScrollPosition: function() {
// if container has a size, this method reads the current scroll position and stores it as desired position
if (this._$Container.width() > 0) {
this._scrollX = this._$Container.scrollLeft();
}
if (this._$Container.height() > 0) {
this._scrollY = this._$Container.scrollTop();
}
},
_scrollTo: function(x, y, time, fnScrollEndCallback) {
if (this._$Container.length > 0) {
if (time > 0) {
this._$Container.finish().animate({ scrollTop: y, scrollLeft: x }, {
duration: time,
complete: this._readActualScrollPosition.bind(this),
always: fnScrollEndCallback
});
} else {
this._$Container.scrollTop(y);
this._$Container.scrollLeft(x);
this._readActualScrollPosition(); // if container is too large no scrolling is possible
fnScrollEndCallback && fnScrollEndCallback();
}
}
},
//checks if the element is already visible in the view port
_isInScrollport: function(oElement, aOffset) {
var oElementRect = oElement.getBoundingClientRect(),
oContainerRect = this._$Container[0].getBoundingClientRect(),
iContainerRectTop = oContainerRect.top - aOffset[1];
return Math.ceil(oElementRect.top) >= Math.floor(iContainerRectTop)
&& Math.floor(oElementRect.bottom) <= Math.ceil(oContainerRect.bottom);
}
};
/*
* Init delegator prototype according to various conditions.
*/
function initDelegateMembers(oScrollerInstance) {
var oDelegateMembers = {
_init : function(oControl, sScrollContentDom, oConfig) {
// default scroll supression threshold of jQuery mobile is too small and prevent native scrolling
if (jQuery.event && jQuery.event.special && jQuery.event.special.swipe && jQuery.event.special.swipe.scrollSupressionThreshold < 120) {
jQuery.event.special.swipe.scrollSupressionThreshold = 120;
}
Object.assign(this, oNativeScrollDelegate);
if (oConfig.nonTouchScrolling === true) {
this._bDragScroll = true; // optional drag instead of native scrolling
}
if (Core.getConfiguration().getRTL()) {
this._scrollX = 9999; // in RTL case initially scroll to the very right
}
},
_exit : function() {
if (this._cleanup) {
this._cleanup();
}
}
};
// Copy over members to prototype
Object.assign(oScrollerInstance, oDelegateMembers);
}
return ScrollEnablement;
});