/** * Framework-internal class for managing touch scrolling of Components and providing * scroll indicators while scrolling. * * @private */Ext.define('Ext.scroll.Manager', { extend: 'Ext.util.Observable', requires: [ 'Ext.scroll.Scroller', 'Ext.scroll.Indicator', 'Ext.GlobalEvents' ], /** * @cfg {Ext.dom.Element} el The element that gets moved when touch-scrolling. This should * be an single child of an overflowing container element (an element that is styled * with overflow:auto), and should shrinkwrap around its contents (display: table, or * position: absolute) */ /** * @cfg {String} direction The {@link Ext.scroll.Scroller#direction direction} to use for * the Scroller. */ /** * @cfg {Number} minIndicatorLength The minimum length for the scroll indicators. */ minIndicatorLength: 24, /** * @cfg {Ext.Component} owner The owning component */ /** * @event scroll * Fires when scroller is scrolled. * @param {Ext.scroll.Manager} scrollManager * @param {Number} x * @param {Number} y */ refreshCounter: 0, translationMethods: { 1: 'scrollparent', 2: 'csstransform' }, constructor: function(config) { var me = this, containerListeners = { dragend: 'onDragEnd', dragcancel: 'onDragEnd', scope: me }, containerEl; if (Ext.supports.touchScroll === 2) { // If using full virtual scrolling attach a mousewheel listener for moving // the scroll position. Otherwise we use native scrolling and so do not // want to override the native behavior containerListeners.mousewheel = 'onMouseWheel'; containerListeners.scroll = { fn: 'onElementScroll', delegated: false, scope: me }; } me.callParent(arguments); me.scroller = new Ext.scroll.Scroller({ // scroller gets refreshed by Component#onResize, // so there is no need to initialize a SizeMonitor autoRefresh: false, element: me.el, direction: me.direction, momentumEasing: { bounce: { springTension: 1 } }, outOfBoundRestrictFactor: 0, translatable: { translationMethod: me.translationMethods[Ext.supports.touchScroll] }, listeners: { scrollstart: 'onScrollStart', scroll: 'onScroll', scrollend: 'onScrollEnd', scope: me } }); Ext.GlobalEvents.on('idle', me.doRefresh, me); containerEl = me.containerEl = me.el.parent(); me.owner.mon(containerEl, containerListeners); me.initIndicators(); }, onElementScroll: function(event, targetEl) { targetEl.scrollTop = targetEl.scrollLeft = 0; }, destroy: function() { var me = this; me.clearListeners(); Ext.GlobalEvents.un('idle', me.doRefresh, me); me.scroller.destroy(); }, initIndicators: function() { var me = this, containerEl = me.containerEl, scroller = me.scroller, minLength = me.minIndicatorLength; if (Ext.supports.touchScroll === 2) { me.xIndicator = new Ext.scroll.Indicator({ axis: 'x', scroller: scroller, containerEl: containerEl, minLength: minLength }); me.yIndicator = new Ext.scroll.Indicator({ axis: 'y', scroller: scroller, containerEl: containerEl, minLength: minLength }); me.refreshIndicators(); } }, invokeIndicators: function(name, args, yArgs) { var me = this, xIndicator = me.xIndicator, yIndicator = me.yIndicator; if (xIndicator && me.isAxisEnabled('x')) { xIndicator[name].apply(xIndicator, args); } if (yIndicator && me.isAxisEnabled('y')) { yIndicator[name].apply(yIndicator, yArgs || args); } }, getPosition: function() { return this.scroller.getPosition(); }, refresh: function(immediate) { ++this.refreshCounter; if (immediate) { this.doRefresh(); } }, refreshIndicators: function() { var me = this, scroller = me.scroller, maxPosition = scroller.getMaxPosition(), size = scroller.getSize(); me.invokeIndicators('setMaxScrollPosition', [maxPosition.x], [maxPosition.y]); me.invokeIndicators('setScrollSize', [size.x], [size.y]); me.invokeIndicators('setHasOpposite', [me.isAxisEnabled('y')], [me.isAxisEnabled('x')]); }, doRefresh: function() { var me = this, scroller = me.scroller; if (me.refreshCounter) { scroller.refresh(); me.refreshIndicators(); me.refreshCounter = 0; } }, onScrollStart: function() { this.isTouching = Ext.isScrolling = true; this.invokeIndicators('show'); this.toggleOthers(true); }, onScroll: function(scroller, x, y) { var me = this; me.invokeIndicators('setValue', [x], [y]); me.fireEvent('scroll', me, x, y); }, onScrollEnd: function() { var me = this; Ext.isScrolling = false; if (me.isTouching) { return; } me.invokeIndicators('hide'); }, onDragEnd: function() { this.isTouching = false; this.toggleOthers(false); }, onMouseWheel: function(e) { var me = this, scroller = me.scroller, delta = e.getWheelDeltas(), deltaX = -delta.x, deltaY = -delta.y, position = scroller.getPosition(), maxPosition = scroller.getMaxPosition(), minPosition = scroller.getMinPosition(), max = Math.max, min = Math.min, positionX = max(min(position.x + deltaX, maxPosition.x), minPosition.x), positionY = max(min(position.y + deltaY, maxPosition.y), minPosition.y); deltaX = positionX - position.x; deltaY = positionY - position.y; if (!deltaX && !deltaY) { return; } e.stopEvent(); me.onScrollStart(); me.scrollBy(deltaX, deltaY); me.onScroll(scroller, positionX, positionY); me.onScrollEnd(scroller); }, isAxisEnabled: function(axis) { return this.scroller.isAxisEnabled(axis); }, setScrollX: function(x) { var scroller = this.scroller; scroller.scrollTo(x, scroller.getPosition().y); }, setScrollY: function(y) { var scroller = this.scroller; scroller.scrollTo(scroller.getPosition().x, y); }, scrollTo: function(x, y, animate) { this.scroller.scrollTo(x, y, animate); }, scrollBy: function(x, y, animate) { if (x.length) { animate = y; y = x[1]; x = x[0]; } else if (!Ext.isNumber(x)) { animate = y; y = x.y; x = x.x; } this.scroller.scrollBy(x, y, animate); }, /** * Scrolls a descendant element of the scroller into view. * @param {String/HTMLElement/Ext.dom.Element} el the descendant to scroll into view * @param {Boolean} [hscroll=true] False to disable horizontal scroll. * @param {Boolean/Object} [animate] true for the default animation or a standard Element * animation config object * @private */ scrollIntoView: function(el, hscroll, animate) { var me = this, containerEl = me.containerEl, scroller = me.scroller, currentPosition = scroller.getPosition(), currentX = currentPosition.x, currentY = currentPosition.y, position = Ext.fly(el).getScrollIntoViewXY(containerEl, currentX, currentY), newX = position.x, newY = position.y; if (hscroll === false) { newX = currentX; } if (newX !== currentX || newY !== currentY) { scroller.scrollTo(newX, newY, animate); } }, toggleOthers: function(disabled) { var scrollers = Ext.scroll.Scroller.instances, scroller, id; // TODO: maybe only disable others in our hierachy instead of ALL others for (id in scrollers) { scroller = scrollers[id]; if (scroller !== this.scroller) { scroller.setDisabled(disabled); } } }, preventDefault: function(e) { if (e.touches.length === 1) { // prevent the body/viewport from scrolling e.preventDefault(); } }});