devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
338 lines (248 loc) • 9.5 kB
JavaScript
"use strict";
var eventsEngine = require("../../events/core/events_engine"),
Class = require("../../core/class"),
abstract = Class.abstract,
eventUtils = require("../../events/utils"),
GestureEmitter = require("../../events/gesture/emitter.gesture"),
registerEmitter = require("../../events/core/emitter_registrator"),
animationFrame = require("../../animation/frame"),
realDevice = require("../../core/devices").real(),
compareVersions = require("../../core/utils/version").compare;
var SCROLL_INIT_EVENT = "dxscrollinit",
SCROLL_START_EVENT = "dxscrollstart",
SCROLL_MOVE_EVENT = "dxscroll",
SCROLL_END_EVENT = "dxscrollend",
SCROLL_STOP_EVENT = "dxscrollstop",
SCROLL_CANCEL_EVENT = "dxscrollcancel";
var isWheelEvent = function isWheelEvent(e) {
return e.type === "dxmousewheel";
};
var Locker = Class.inherit(function () {
var NAMESPACED_SCROLL_EVENT = eventUtils.addNamespace("scroll", "dxScrollEmitter");
return {
ctor: function ctor(element) {
this._element = element;
this._locked = false;
var that = this;
this._proxiedScroll = function (e) {
that._scroll(e);
};
eventsEngine.on(this._element, NAMESPACED_SCROLL_EVENT, this._proxiedScroll);
},
_scroll: abstract,
check: function check(e, callback) {
if (this._locked) {
callback();
}
},
dispose: function dispose() {
eventsEngine.off(this._element, NAMESPACED_SCROLL_EVENT, this._proxiedScroll);
}
};
}());
var TimeoutLocker = Locker.inherit(function () {
return {
ctor: function ctor(element, timeout) {
this.callBase(element);
this._timeout = timeout;
},
_scroll: function _scroll() {
this._prepare();
this._forget();
},
_prepare: function _prepare() {
if (this._timer) {
this._clearTimer();
}
this._locked = true;
},
_clearTimer: function _clearTimer() {
clearTimeout(this._timer);
this._locked = false;
this._timer = null;
},
_forget: function _forget() {
var that = this;
this._timer = setTimeout(function () {
that._clearTimer();
}, this._timeout);
},
dispose: function dispose() {
this.callBase();
this._clearTimer();
}
};
}());
var WheelLocker = TimeoutLocker.inherit(function () {
var WHEEL_UNLOCK_TIMEOUT = 400;
return {
ctor: function ctor(element) {
this.callBase(element, WHEEL_UNLOCK_TIMEOUT);
this._lastWheelDirection = null;
},
check: function check(e, callback) {
this._checkDirectionChanged(e);
this.callBase(e, callback);
},
_checkDirectionChanged: function _checkDirectionChanged(e) {
if (!isWheelEvent(e)) {
this._lastWheelDirection = null;
return;
}
var direction = e.shiftKey || false,
directionChange = this._lastWheelDirection !== null && direction !== this._lastWheelDirection;
this._lastWheelDirection = direction;
this._locked = this._locked && !directionChange;
}
};
}());
var PointerLocker = TimeoutLocker.inherit(function () {
var POINTER_UNLOCK_TIMEOUT = 400;
return {
ctor: function ctor(element) {
this.callBase(element, POINTER_UNLOCK_TIMEOUT);
}
};
}());
(function () {
var ios8_greater = realDevice.ios && compareVersions(realDevice.version, [8]) >= 0,
android5_greater = realDevice.android && compareVersions(realDevice.version, [5]) >= 0;
if (!(ios8_greater || android5_greater)) {
return;
}
PointerLocker = Locker.inherit(function () {
return {
_scroll: function _scroll() {
this._locked = true;
var that = this;
animationFrame.cancelAnimationFrame(this._scrollFrame);
this._scrollFrame = animationFrame.requestAnimationFrame(function () {
that._locked = false;
});
},
check: function check(e, callback) {
animationFrame.cancelAnimationFrame(this._scrollFrame);
animationFrame.cancelAnimationFrame(this._checkFrame);
var that = this,
callBase = this.callBase;
this._checkFrame = animationFrame.requestAnimationFrame(function () {
callBase.call(that, e, callback);
that._locked = false;
});
},
dispose: function dispose() {
this.callBase();
animationFrame.cancelAnimationFrame(this._scrollFrame);
animationFrame.cancelAnimationFrame(this._checkFrame);
}
};
}());
})();
var ScrollEmitter = GestureEmitter.inherit(function () {
var INERTIA_TIMEOUT = 100,
VELOCITY_CALC_TIMEOUT = 200,
FRAME_DURATION = Math.round(1000 / 60);
return {
ctor: function ctor(element) {
this.callBase.apply(this, arguments);
this.direction = "both";
this._pointerLocker = new PointerLocker(element);
this._wheelLocker = new WheelLocker(element);
},
validate: function validate() {
return true;
},
configure: function configure(data) {
if (data.scrollTarget) {
this._pointerLocker.dispose();
this._wheelLocker.dispose();
this._pointerLocker = new PointerLocker(data.scrollTarget);
this._wheelLocker = new WheelLocker(data.scrollTarget);
}
this.callBase(data);
},
_init: function _init(e) {
this._wheelLocker.check(e, function () {
if (isWheelEvent(e)) {
this._accept(e);
}
}.bind(this));
this._pointerLocker.check(e, function () {
var skipCheck = this.isNative && eventUtils.isMouseEvent(e);
if (!isWheelEvent(e) && !skipCheck) {
this._accept(e);
}
}.bind(this));
this._fireEvent(SCROLL_INIT_EVENT, e);
this._prevEventData = eventUtils.eventData(e);
},
move: function move(e) {
this.callBase.apply(this, arguments);
e.isScrollingEvent = this.isNative || e.isScrollingEvent;
},
_start: function _start(e) {
this._savedEventData = eventUtils.eventData(e);
this._fireEvent(SCROLL_START_EVENT, e);
this._prevEventData = eventUtils.eventData(e);
},
_move: function _move(e) {
var currentEventData = eventUtils.eventData(e);
this._fireEvent(SCROLL_MOVE_EVENT, e, {
delta: eventUtils.eventDelta(this._prevEventData, currentEventData)
});
var eventDelta = eventUtils.eventDelta(this._savedEventData, currentEventData);
if (eventDelta.time > VELOCITY_CALC_TIMEOUT) {
this._savedEventData = this._prevEventData;
}
this._prevEventData = eventUtils.eventData(e);
},
_end: function _end(e) {
var endEventDelta = eventUtils.eventDelta(this._prevEventData, eventUtils.eventData(e));
var velocity = { x: 0, y: 0 };
if (!isWheelEvent(e) && endEventDelta.time < INERTIA_TIMEOUT) {
var eventDelta = eventUtils.eventDelta(this._savedEventData, this._prevEventData),
velocityMultiplier = FRAME_DURATION / eventDelta.time;
velocity = { x: eventDelta.x * velocityMultiplier, y: eventDelta.y * velocityMultiplier };
}
this._fireEvent(SCROLL_END_EVENT, e, {
velocity: velocity
});
},
_stop: function _stop(e) {
this._fireEvent(SCROLL_STOP_EVENT, e);
},
cancel: function cancel(e) {
this.callBase.apply(this, arguments);
this._fireEvent(SCROLL_CANCEL_EVENT, e);
},
dispose: function dispose() {
this.callBase.apply(this, arguments);
this._pointerLocker.dispose();
this._wheelLocker.dispose();
},
_clearSelection: function _clearSelection() {
if (this.isNative) {
return;
}
return this.callBase.apply(this, arguments);
},
_toggleGestureCover: function _toggleGestureCover() {
if (this.isNative) {
return;
}
return this.callBase.apply(this, arguments);
}
};
}());
registerEmitter({
emitter: ScrollEmitter,
events: [SCROLL_INIT_EVENT, SCROLL_START_EVENT, SCROLL_MOVE_EVENT, SCROLL_END_EVENT, SCROLL_STOP_EVENT, SCROLL_CANCEL_EVENT]
});
module.exports = {
init: SCROLL_INIT_EVENT,
start: SCROLL_START_EVENT,
move: SCROLL_MOVE_EVENT,
end: SCROLL_END_EVENT,
stop: SCROLL_STOP_EVENT,
cancel: SCROLL_CANCEL_EVENT
};