UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

1,007 lines (824 loc) • 32.8 kB
"use strict"; var $ = require("../../core/renderer"), domAdapter = require("../../core/dom_adapter"), eventsEngine = require("../../events/core/events_engine"), math = Math, titleize = require("../../core/utils/inflector").titleize, extend = require("../../core/utils/extend").extend, iteratorUtils = require("../../core/utils/iterator"), translator = require("../../animation/translator"), Class = require("../../core/class"), Animator = require("./animator"), devices = require("../../core/devices"), eventUtils = require("../../events/utils"), commonUtils = require("../../core/utils/common"), Scrollbar = require("./ui.scrollbar"), deferredUtils = require("../../core/utils/deferred"), when = deferredUtils.when, Deferred = deferredUtils.Deferred; var realDevice = devices.real; var isSluggishPlatform = realDevice.platform === "win" || realDevice.platform === "android"; var SCROLLABLE_SIMULATED = "dxSimulatedScrollable", SCROLLABLE_STRATEGY = "dxScrollableStrategy", SCROLLABLE_SIMULATED_CURSOR = SCROLLABLE_SIMULATED + "Cursor", SCROLLABLE_SIMULATED_KEYBOARD = SCROLLABLE_SIMULATED + "Keyboard", SCROLLABLE_SIMULATED_CLASS = "dx-scrollable-simulated", SCROLLABLE_SCROLLBARS_HIDDEN = "dx-scrollable-scrollbars-hidden", SCROLLABLE_SCROLLBARS_ALWAYSVISIBLE = "dx-scrollable-scrollbars-alwaysvisible", SCROLLABLE_SCROLLBAR_CLASS = "dx-scrollable-scrollbar", VERTICAL = "vertical", HORIZONTAL = "horizontal", ACCELERATION = isSluggishPlatform ? 0.95 : 0.92, OUT_BOUNDS_ACCELERATION = 0.5, MIN_VELOCITY_LIMIT = 1, FRAME_DURATION = math.round(1000 / 60), SCROLL_LINE_HEIGHT = 20, BOUNCE_MIN_VELOCITY_LIMIT = MIN_VELOCITY_LIMIT / 5, BOUNCE_DURATION = isSluggishPlatform ? 300 : 400, BOUNCE_FRAMES = BOUNCE_DURATION / FRAME_DURATION, BOUNCE_ACCELERATION_SUM = (1 - math.pow(ACCELERATION, BOUNCE_FRAMES)) / (1 - ACCELERATION); var KEY_CODES = { PAGE_UP: 33, PAGE_DOWN: 34, END: 35, HOME: 36, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 }; var InertiaAnimator = Animator.inherit({ ctor: function ctor(scroller) { this.callBase(); this.scroller = scroller; }, VELOCITY_LIMIT: MIN_VELOCITY_LIMIT, _isFinished: function _isFinished() { return math.abs(this.scroller._velocity) <= this.VELOCITY_LIMIT; }, _step: function _step() { this.scroller._scrollStep(this.scroller._velocity); this.scroller._velocity *= this._acceleration(); }, _acceleration: function _acceleration() { return this.scroller._inBounds() ? ACCELERATION : OUT_BOUNDS_ACCELERATION; }, _complete: function _complete() { this.scroller._scrollComplete(); }, _stop: function _stop() { this.scroller._stopComplete(); } }); var BounceAnimator = InertiaAnimator.inherit({ VELOCITY_LIMIT: BOUNCE_MIN_VELOCITY_LIMIT, _isFinished: function _isFinished() { return this.scroller._crossBoundOnNextStep() || this.callBase(); }, _acceleration: function _acceleration() { return ACCELERATION; }, _complete: function _complete() { this.scroller._move(this.scroller._bounceLocation); this.callBase(); } }); var isWheelEvent = function isWheelEvent(e) { return e.type === "dxmousewheel"; }; var Scroller = Class.inherit({ ctor: function ctor(options) { this._initOptions(options); this._initAnimators(); this._initScrollbar(); }, _initOptions: function _initOptions(options) { this._location = 0; this._topReached = false; this._bottomReached = false; this._axis = options.direction === HORIZONTAL ? "x" : "y"; this._prop = options.direction === HORIZONTAL ? "left" : "top"; this._dimension = options.direction === HORIZONTAL ? "width" : "height"; this._scrollProp = options.direction === HORIZONTAL ? "scrollLeft" : "scrollTop"; iteratorUtils.each(options, function (optionName, optionValue) { this["_" + optionName] = optionValue; }.bind(this)); }, _initAnimators: function _initAnimators() { this._inertiaAnimator = new InertiaAnimator(this); this._bounceAnimator = new BounceAnimator(this); }, _initScrollbar: function _initScrollbar() { this._scrollbar = new Scrollbar($("<div>").appendTo(this._$container), { direction: this._direction, visible: this._scrollByThumb, visibilityMode: this._visibilityModeNormalize(this._scrollbarVisible), expandable: this._scrollByThumb }); this._$scrollbar = this._scrollbar.$element(); }, _visibilityModeNormalize: function _visibilityModeNormalize(mode) { return mode === true ? "onScroll" : mode === false ? "never" : mode; }, _scrollStep: function _scrollStep(delta) { var prevLocation = this._location; this._location += delta; this._suppressBounce(); this._move(); if (Math.abs(prevLocation - this._location) < 1) { return; } eventsEngine.triggerHandler(this._$container, { type: "scroll" }); }, _suppressBounce: function _suppressBounce() { if (this._bounceEnabled || this._inBounds(this._location)) { return; } this._velocity = 0; this._location = this._boundLocation(); }, _boundLocation: function _boundLocation(location) { location = location !== undefined ? location : this._location; return math.max(math.min(location, this._maxOffset), this._minOffset); }, _move: function _move(location) { this._location = location !== undefined ? location : this._location; this._moveContent(); this._moveScrollbar(); }, _moveContent: function _moveContent() { var location = this._location; this._$container[this._scrollProp](-location); this._moveContentByTranslator(location); }, _moveContentByTranslator: function _moveContentByTranslator(location) { var translateOffset, minOffset = -this._maxScrollPropValue; if (location > 0) { translateOffset = location; } else if (location <= minOffset) { translateOffset = location - minOffset; } else { translateOffset = location % 1; } if (this._translateOffset === translateOffset) { return; } var targetLocation = {}; targetLocation[this._prop] = translateOffset; this._translateOffset = translateOffset; if (translateOffset === 0) { translator.resetPosition(this._$content); return; } translator.move(this._$content, targetLocation); }, _moveScrollbar: function _moveScrollbar() { this._scrollbar.moveTo(this._location); }, _scrollComplete: function _scrollComplete() { if (this._inBounds()) { this._hideScrollbar(); if (this._completeDeferred) { this._completeDeferred.resolve(); } } this._scrollToBounds(); }, _scrollToBounds: function _scrollToBounds() { if (this._inBounds()) { return; } this._bounceAction(); this._setupBounce(); this._bounceAnimator.start(); }, _setupBounce: function _setupBounce() { var boundLocation = this._bounceLocation = this._boundLocation(), bounceDistance = boundLocation - this._location; this._velocity = bounceDistance / BOUNCE_ACCELERATION_SUM; }, _inBounds: function _inBounds(location) { location = location !== undefined ? location : this._location; return this._boundLocation(location) === location; }, _crossBoundOnNextStep: function _crossBoundOnNextStep() { var location = this._location, nextLocation = location + this._velocity; return location < this._minOffset && nextLocation >= this._minOffset || location > this._maxOffset && nextLocation <= this._maxOffset; }, _initHandler: function _initHandler(e) { this._stopDeferred = new Deferred(); this._stopScrolling(); this._prepareThumbScrolling(e); return this._stopDeferred.promise(); }, _stopScrolling: commonUtils.deferRenderer(function () { this._hideScrollbar(); this._inertiaAnimator.stop(); this._bounceAnimator.stop(); }), _prepareThumbScrolling: function _prepareThumbScrolling(e) { if (isWheelEvent(e.originalEvent)) { return; } var $target = $(e.originalEvent.target); var scrollbarClicked = this._isScrollbar($target); if (scrollbarClicked) { this._moveToMouseLocation(e); } this._thumbScrolling = scrollbarClicked || this._isThumb($target); this._crossThumbScrolling = !this._thumbScrolling && this._isAnyThumbScrolling($target); if (this._thumbScrolling) { this._scrollbar.feedbackOn(); } }, _isThumbScrollingHandler: function _isThumbScrollingHandler($target) { return this._isThumb($target); }, _moveToMouseLocation: function _moveToMouseLocation(e) { var mouseLocation = e["page" + this._axis.toUpperCase()] - this._$element.offset()[this._prop]; var location = this._location + mouseLocation / this._containerToContentRatio() - this._$container.height() / 2; this._scrollStep(-Math.round(location)); }, _stopComplete: function _stopComplete() { if (this._stopDeferred) { this._stopDeferred.resolve(); } }, _startHandler: function _startHandler() { this._showScrollbar(); }, _moveHandler: function _moveHandler(delta) { if (this._crossThumbScrolling) { return; } if (this._thumbScrolling) { delta[this._axis] = -Math.round(delta[this._axis] / this._containerToContentRatio()); } this._scrollBy(delta); }, _scrollBy: function _scrollBy(delta) { delta = delta[this._axis]; if (!this._inBounds()) { delta *= OUT_BOUNDS_ACCELERATION; } this._scrollStep(delta); }, _scrollByHandler: function _scrollByHandler(delta) { this._scrollBy(delta); this._scrollComplete(); }, _containerToContentRatio: function _containerToContentRatio() { return this._scrollbar.containerToContentRatio(); }, _endHandler: function _endHandler(velocity) { this._completeDeferred = new Deferred(); this._velocity = velocity[this._axis]; this._inertiaHandler(); this._resetThumbScrolling(); return this._completeDeferred.promise(); }, _inertiaHandler: function _inertiaHandler() { this._suppressInertia(); this._inertiaAnimator.start(); }, _suppressInertia: function _suppressInertia() { if (!this._inertiaEnabled || this._thumbScrolling) { this._velocity = 0; } }, _resetThumbScrolling: function _resetThumbScrolling() { this._thumbScrolling = false; this._crossThumbScrolling = false; }, _stopHandler: function _stopHandler() { if (this._thumbScrolling) { this._scrollComplete(); } this._resetThumbScrolling(); this._scrollToBounds(); }, _disposeHandler: function _disposeHandler() { this._stopScrolling(); this._$scrollbar.remove(); }, _updateHandler: function _updateHandler() { this._update(); this._moveToBounds(); }, _update: function _update() { var that = this; that._stopScrolling(); return commonUtils.deferUpdate(function () { that._updateLocation(); that._updateBounds(); that._updateScrollbar(); commonUtils.deferRender(function () { that._moveScrollbar(); that._scrollbar.update(); }); }); }, _updateLocation: function _updateLocation() { this._location = translator.locate(this._$content)[this._prop] - this._$container[this._scrollProp](); }, _updateBounds: function _updateBounds() { this._maxOffset = Math.round(this._getMaxOffset()); this._minOffset = Math.round(this._getMinOffset()); }, _getMaxOffset: function _getMaxOffset() { return 0; }, _getMinOffset: function _getMinOffset() { this._maxScrollPropValue = math.max(this._contentSize() - this._containerSize(), 0); return -this._maxScrollPropValue; }, _updateScrollbar: commonUtils.deferUpdater(function () { var that = this, containerSize = that._containerSize(), contentSize = that._contentSize(); commonUtils.deferRender(function () { that._scrollbar.option({ containerSize: containerSize, contentSize: contentSize }); }); }), _moveToBounds: commonUtils.deferRenderer(commonUtils.deferUpdater(commonUtils.deferRenderer(function () { var location = this._boundLocation(); var locationChanged = location !== this._location; this._location = location; this._move(); if (locationChanged) { this._scrollAction(); } }))), _createActionsHandler: function _createActionsHandler(actions) { this._scrollAction = actions.scroll; this._bounceAction = actions.bounce; }, _showScrollbar: function _showScrollbar() { this._scrollbar.option("visible", true); }, _hideScrollbar: function _hideScrollbar() { this._scrollbar.option("visible", false); }, _containerSize: function _containerSize() { return this._$container[this._dimension](); }, _contentSize: function _contentSize() { var isOverflowHidden = this._$content.css("overflow" + this._axis.toUpperCase()) === "hidden", contentSize = this._$content[this._dimension](); if (!isOverflowHidden) { var containerScrollSize = this._$content[0]["scroll" + titleize(this._dimension)]; contentSize = math.max(containerScrollSize, contentSize); } return contentSize; }, _validateEvent: function _validateEvent(e) { var $target = $(e.originalEvent.target); return this._isThumb($target) || this._isScrollbar($target) || this._isContent($target); }, _isThumb: function _isThumb($element) { return this._scrollByThumb && this._scrollbar.isThumb($element); }, _isScrollbar: function _isScrollbar($element) { return this._scrollByThumb && $element && $element.is(this._$scrollbar); }, _isContent: function _isContent($element) { return this._scrollByContent && !!$element.closest(this._$element).length; }, _reachedMin: function _reachedMin() { return this._location <= this._minOffset; }, _reachedMax: function _reachedMax() { return this._location >= this._maxOffset; }, _cursorEnterHandler: function _cursorEnterHandler() { this._scrollbar.cursorEnter(); }, _cursorLeaveHandler: function _cursorLeaveHandler() { this._scrollbar.cursorLeave(); }, dispose: commonUtils.noop }); var hoveredScrollable, activeScrollable; var SimulatedStrategy = Class.inherit({ ctor: function ctor(scrollable) { this._init(scrollable); }, _init: function _init(scrollable) { this._component = scrollable; this._$element = scrollable.$element(); this._$container = scrollable._$container; this._$wrapper = scrollable._$wrapper; this._$content = scrollable._$content; this.option = scrollable.option.bind(scrollable); this._createActionByOption = scrollable._createActionByOption.bind(scrollable); this._isLocked = scrollable._isLocked.bind(scrollable); this._isDirection = scrollable._isDirection.bind(scrollable); this._allowedDirection = scrollable._allowedDirection.bind(scrollable); }, render: function render() { this._$element.addClass(SCROLLABLE_SIMULATED_CLASS); this._createScrollers(); if (this.option("useKeyboard")) { this._$container.prop("tabIndex", 0); } this._attachKeyboardHandler(); this._attachCursorHandlers(); }, _createScrollers: function _createScrollers() { this._scrollers = {}; if (this._isDirection(HORIZONTAL)) { this._createScroller(HORIZONTAL); } if (this._isDirection(VERTICAL)) { this._createScroller(VERTICAL); } this._$element.toggleClass(SCROLLABLE_SCROLLBARS_ALWAYSVISIBLE, this.option("showScrollbar") === "always"); this._$element.toggleClass(SCROLLABLE_SCROLLBARS_HIDDEN, !this.option("showScrollbar")); }, _createScroller: function _createScroller(direction) { this._scrollers[direction] = new Scroller(this._scrollerOptions(direction)); }, _scrollerOptions: function _scrollerOptions(direction) { return { direction: direction, $content: this._$content, $container: this._$container, $wrapper: this._$wrapper, $element: this._$element, scrollByContent: this.option("scrollByContent"), scrollByThumb: this.option("scrollByThumb"), scrollbarVisible: this.option("showScrollbar"), bounceEnabled: this.option("bounceEnabled"), inertiaEnabled: this.option("inertiaEnabled"), isAnyThumbScrolling: this._isAnyThumbScrolling.bind(this) }; }, _isAnyThumbScrolling: function _isAnyThumbScrolling($target) { var result = false; this._eventHandler("isThumbScrolling", $target).done(function (isThumbScrollingVertical, isThumbScrollingHorizontal) { result = isThumbScrollingVertical || isThumbScrollingHorizontal; }); return result; }, handleInit: function handleInit(e) { this._suppressDirections(e); this._eventForUserAction = e; this._eventHandler("init", e).done(this._stopAction); }, _suppressDirections: function _suppressDirections(e) { if (isWheelEvent(e.originalEvent)) { this._prepareDirections(true); return; } this._prepareDirections(); this._eachScroller(function (scroller, direction) { var isValid = scroller._validateEvent(e); this._validDirections[direction] = isValid; }); }, _prepareDirections: function _prepareDirections(value) { value = value || false; this._validDirections = {}; this._validDirections[HORIZONTAL] = value; this._validDirections[VERTICAL] = value; }, _eachScroller: function _eachScroller(callback) { callback = callback.bind(this); iteratorUtils.each(this._scrollers, function (direction, scroller) { callback(scroller, direction); }); }, handleStart: function handleStart(e) { this._eventForUserAction = e; this._eventHandler("start").done(this._startAction); }, _saveActive: function _saveActive() { activeScrollable = this; }, _resetActive: function _resetActive() { if (activeScrollable === this) { activeScrollable = null; } }, handleMove: function handleMove(e) { if (this._isLocked()) { e.cancel = true; this._resetActive(); return; } this._saveActive(); e.preventDefault && e.preventDefault(); this._adjustDistance(e.delta); this._eventForUserAction = e; this._eventHandler("move", e.delta); }, _adjustDistance: function _adjustDistance(distance) { distance.x *= this._validDirections[HORIZONTAL]; distance.y *= this._validDirections[VERTICAL]; }, handleEnd: function handleEnd(e) { this._resetActive(); this._refreshCursorState(e.originalEvent && e.originalEvent.target); this._adjustDistance(e.velocity); this._eventForUserAction = e; return this._eventHandler("end", e.velocity).done(this._endAction); }, handleCancel: function handleCancel(e) { this._resetActive(); this._eventForUserAction = e; return this._eventHandler("end", { x: 0, y: 0 }); }, handleStop: function handleStop() { this._resetActive(); this._eventHandler("stop"); }, handleScroll: function handleScroll() { this._scrollAction(); }, _attachKeyboardHandler: function _attachKeyboardHandler() { eventsEngine.off(this._$element, "." + SCROLLABLE_SIMULATED_KEYBOARD); if (!this.option("disabled") && this.option("useKeyboard")) { eventsEngine.on(this._$element, eventUtils.addNamespace("keydown", SCROLLABLE_SIMULATED_KEYBOARD), this._keyDownHandler.bind(this)); } }, _keyDownHandler: function _keyDownHandler(e) { if (!this._$container.is(domAdapter.getActiveElement())) { return; } var handled = true; switch (e.keyCode) { case KEY_CODES.DOWN: this._scrollByLine({ y: 1 }); break; case KEY_CODES.UP: this._scrollByLine({ y: -1 }); break; case KEY_CODES.RIGHT: this._scrollByLine({ x: 1 }); break; case KEY_CODES.LEFT: this._scrollByLine({ x: -1 }); break; case KEY_CODES.PAGE_DOWN: this._scrollByPage(1); break; case KEY_CODES.PAGE_UP: this._scrollByPage(-1); break; case KEY_CODES.HOME: this._scrollToHome(); break; case KEY_CODES.END: this._scrollToEnd(); break; default: handled = false; break; } if (handled) { e.stopPropagation(); e.preventDefault(); } }, _scrollByLine: function _scrollByLine(lines) { this.scrollBy({ top: (lines.y || 0) * -SCROLL_LINE_HEIGHT, left: (lines.x || 0) * -SCROLL_LINE_HEIGHT }); }, _scrollByPage: function _scrollByPage(page) { var prop = this._wheelProp(), dimension = this._dimensionByProp(prop); var distance = {}; distance[prop] = page * -this._$container[dimension](); this.scrollBy(distance); }, _dimensionByProp: function _dimensionByProp(prop) { return prop === "left" ? "width" : "height"; }, _scrollToHome: function _scrollToHome() { var prop = this._wheelProp(); var distance = {}; distance[prop] = 0; this._component.scrollTo(distance); }, _scrollToEnd: function _scrollToEnd() { var prop = this._wheelProp(), dimension = this._dimensionByProp(prop); var distance = {}; distance[prop] = this._$content[dimension]() - this._$container[dimension](); this._component.scrollTo(distance); }, createActions: function createActions() { this._startAction = this._createActionHandler("onStart"); this._stopAction = this._createActionHandler("onStop"); this._endAction = this._createActionHandler("onEnd"); this._updateAction = this._createActionHandler("onUpdated"); this._createScrollerActions(); }, _createScrollerActions: function _createScrollerActions() { this._scrollAction = this._createActionHandler("onScroll"); this._bounceAction = this._createActionHandler("onBounce"); this._eventHandler("createActions", { scroll: this._scrollAction, bounce: this._bounceAction }); }, _createActionHandler: function _createActionHandler(optionName) { var that = this, actionHandler = that._createActionByOption(optionName); return function () { actionHandler(extend(that._createActionArgs(), arguments)); }; }, _createActionArgs: function _createActionArgs() { var scrollerX = this._scrollers[HORIZONTAL], scrollerY = this._scrollers[VERTICAL]; var location = this.location(); this._scrollOffset = { top: scrollerY && -location.top, left: scrollerX && -location.left }; return { event: this._eventForUserAction, scrollOffset: this._scrollOffset, reachedLeft: scrollerX && scrollerX._reachedMax(), reachedRight: scrollerX && scrollerX._reachedMin(), reachedTop: scrollerY && scrollerY._reachedMax(), reachedBottom: scrollerY && scrollerY._reachedMin() }; }, _eventHandler: function _eventHandler(eventName) { var args = [].slice.call(arguments).slice(1), deferreds = iteratorUtils.map(this._scrollers, function (scroller) { return scroller["_" + eventName + "Handler"].apply(scroller, args); }); return when.apply($, deferreds).promise(); }, location: function location() { var location = translator.locate(this._$content); location.top -= this._$container.scrollTop(); location.left -= this._$container.scrollLeft(); return location; }, disabledChanged: function disabledChanged() { this._attachCursorHandlers(); }, _attachCursorHandlers: function _attachCursorHandlers() { eventsEngine.off(this._$element, "." + SCROLLABLE_SIMULATED_CURSOR); if (!this.option("disabled") && this._isHoverMode()) { eventsEngine.on(this._$element, eventUtils.addNamespace("mouseenter", SCROLLABLE_SIMULATED_CURSOR), this._cursorEnterHandler.bind(this)); eventsEngine.on(this._$element, eventUtils.addNamespace("mouseleave", SCROLLABLE_SIMULATED_CURSOR), this._cursorLeaveHandler.bind(this)); } }, _isHoverMode: function _isHoverMode() { return this.option("showScrollbar") === "onHover"; }, _cursorEnterHandler: function _cursorEnterHandler(e) { e = e || {}; e.originalEvent = e.originalEvent || {}; if (activeScrollable || e.originalEvent._hoverHandled) { return; } if (hoveredScrollable) { hoveredScrollable._cursorLeaveHandler(); } hoveredScrollable = this; this._eventHandler("cursorEnter"); e.originalEvent._hoverHandled = true; }, _cursorLeaveHandler: function _cursorLeaveHandler(e) { if (hoveredScrollable !== this || activeScrollable === hoveredScrollable) { return; } this._eventHandler("cursorLeave"); hoveredScrollable = null; this._refreshCursorState(e && e.relatedTarget); }, _refreshCursorState: function _refreshCursorState(target) { if (!this._isHoverMode() && (!target || activeScrollable)) { return; } var $target = $(target); var $scrollable = $target.closest("." + SCROLLABLE_SIMULATED_CLASS + ":not(.dx-state-disabled)"); var targetScrollable = $scrollable.length && $scrollable.data(SCROLLABLE_STRATEGY); if (hoveredScrollable && hoveredScrollable !== targetScrollable) { hoveredScrollable._cursorLeaveHandler(); } if (targetScrollable) { targetScrollable._cursorEnterHandler(); } }, update: function update() { var that = this; var result = this._eventHandler("update").done(this._updateAction); return when(result, commonUtils.deferUpdate(function () { var allowedDirections = that._allowedDirections(); commonUtils.deferRender(function () { var touchDirection = allowedDirections.vertical ? "pan-x" : ""; touchDirection = allowedDirections.horizontal ? "pan-y" : touchDirection; touchDirection = allowedDirections.vertical && allowedDirections.horizontal ? "none" : touchDirection; that._$container.css("touchAction", touchDirection); }); return when().promise(); })); }, _allowedDirections: function _allowedDirections() { var bounceEnabled = this.option("bounceEnabled"), verticalScroller = this._scrollers[VERTICAL], horizontalScroller = this._scrollers[HORIZONTAL]; return { vertical: verticalScroller && (verticalScroller._minOffset < 0 || bounceEnabled), horizontal: horizontalScroller && (horizontalScroller._minOffset < 0 || bounceEnabled) }; }, updateBounds: function updateBounds() { this._scrollers[HORIZONTAL] && this._scrollers[HORIZONTAL]._updateBounds(); }, scrollBy: function scrollBy(distance) { var verticalScroller = this._scrollers[VERTICAL], horizontalScroller = this._scrollers[HORIZONTAL]; if (verticalScroller) { distance.top = verticalScroller._boundLocation(distance.top + verticalScroller._location) - verticalScroller._location; } if (horizontalScroller) { distance.left = horizontalScroller._boundLocation(distance.left + horizontalScroller._location) - horizontalScroller._location; } this._prepareDirections(true); this._startAction(); this._eventHandler("scrollBy", { x: distance.left, y: distance.top }); this._endAction(); }, validate: function validate(e) { if (this.option("disabled")) { return false; } if (this.option("bounceEnabled")) { return true; } return isWheelEvent(e) ? this._validateWheel(e) : this._validateMove(e); }, _validateWheel: function _validateWheel(e) { var scroller = this._scrollers[this._wheelDirection(e)]; var reachedMin = scroller._reachedMin(); var reachedMax = scroller._reachedMax(); var contentGreaterThanContainer = !reachedMin || !reachedMax; var locatedNotAtBound = !reachedMin && !reachedMax; var scrollFromMin = reachedMin && e.delta > 0; var scrollFromMax = reachedMax && e.delta < 0; return contentGreaterThanContainer && (locatedNotAtBound || scrollFromMin || scrollFromMax); }, _validateMove: function _validateMove(e) { if (!this.option("scrollByContent") && !$(e.target).closest("." + SCROLLABLE_SCROLLBAR_CLASS).length) { return false; } return this._allowedDirection(); }, getDirection: function getDirection(e) { return isWheelEvent(e) ? this._wheelDirection(e) : this._allowedDirection(); }, _wheelProp: function _wheelProp() { return this._wheelDirection() === HORIZONTAL ? "left" : "top"; }, _wheelDirection: function _wheelDirection(e) { switch (this.option("direction")) { case HORIZONTAL: return HORIZONTAL; case VERTICAL: return VERTICAL; default: return e && e.shiftKey ? HORIZONTAL : VERTICAL; } }, verticalOffset: function verticalOffset() { return 0; }, dispose: function dispose() { this._resetActive(); if (hoveredScrollable === this) { hoveredScrollable = null; } this._eventHandler("dispose"); this._detachEventHandlers(); this._$element.removeClass(SCROLLABLE_SIMULATED_CLASS); this._eventForUserAction = null; clearTimeout(this._gestureEndTimer); }, _detachEventHandlers: function _detachEventHandlers() { eventsEngine.off(this._$element, "." + SCROLLABLE_SIMULATED_CURSOR); eventsEngine.off(this._$container, "." + SCROLLABLE_SIMULATED_KEYBOARD); } }); exports.SimulatedStrategy = SimulatedStrategy; exports.Scroller = Scroller; ///#DEBUG exports.ACCELERATION = ACCELERATION; exports.MIN_VELOCITY_LIMIT = MIN_VELOCITY_LIMIT; exports.FRAME_DURATION = FRAME_DURATION; exports.SCROLL_LINE_HEIGHT = SCROLL_LINE_HEIGHT; ///#ENDDEBUG