UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

622 lines (507 loc) 17.5 kB
import { Class } from '../drawing-utils'; import { applyEventMap, eventMap } from './event-map'; import { on, off } from './event-utils'; import getSupportedFeatures from './get-supported-features'; import noop from './noop'; import now from './now'; import grep from './grep'; import Observable from './observable'; var extend = Object.assign; var preventDefault = function (e) { e.preventDefault(); }; var DEFAULT_MIN_HOLD = 800, CLICK_DELAY = 300, DEFAULT_THRESHOLD = 0, PRESS = 'press', HOLD = 'hold', SELECT = 'select', START = 'start', MOVE = 'move', END = 'end', CANCEL = 'cancel', TAP = 'tap', DOUBLETAP = 'doubleTap', RELEASE = 'release', GESTURESTART = 'gesturestart', GESTURECHANGE = 'gesturechange', GESTUREEND = 'gestureend', GESTURETAP = 'gesturetap'; var THRESHOLD = { 'api': 0, 'touch': 0, 'mouse': 9, 'pointer': 9 }; function touchDelta(touch1, touch2) { var x1 = touch1.x.location, y1 = touch1.y.location, x2 = touch2.x.location, y2 = touch2.y.location, dx = x1 - x2, dy = y1 - y2; return { center: { x: (x1 + x2) / 2, y: (y1 + y2) / 2 }, distance: Math.sqrt(dx * dx + dy * dy) }; } function getTouches(e) { var touches = [], originalEvent = e.originalEvent || e, currentTarget = e.currentTarget; if (e.api) { touches.push({ id: 2, // hardcoded ID for API call event: e, target: e.target, currentTarget: e.target, location: e, type: 'api' }); } else { touches.push({ location: originalEvent, event: e, target: e.target, currentTarget: currentTarget, id: originalEvent.pointerId, type: 'pointer' }); } return touches; } var TouchAxis = (function (Class) { function TouchAxis(axis, location) { Class.call(this); var that = this; that.support = getSupportedFeatures(); that.invalidZeroEvents = this.support.mobileOS && this.support.mobileOS.android; that.axis = axis; that._updateLocationData(location); that.startLocation = that.location; that.velocity = that.delta = 0; that.timeStamp = now(); } if ( Class ) TouchAxis.__proto__ = Class; TouchAxis.prototype = Object.create( Class && Class.prototype ); TouchAxis.prototype.constructor = TouchAxis; TouchAxis.prototype.move = function move (location) { var that = this, offset = location['page' + that.axis], timeStamp = now(), timeDelta = timeStamp - that.timeStamp || 1; if (!offset && this.invalidZeroEvents) { return; } that.delta = offset - that.location; that._updateLocationData(location); that.initialDelta = offset - that.startLocation; that.velocity = that.delta / timeDelta; that.timeStamp = timeStamp; }; TouchAxis.prototype._updateLocationData = function _updateLocationData (location) { var that = this, axis = that.axis; that.location = location['page' + axis]; that.client = location['client' + axis]; that.screen = location['screen' + axis]; }; return TouchAxis; }(Class)); var Touch = (function (Class) { function Touch(userEvents, target, touchInfo) { Class.call(this); extend(this, { x: new TouchAxis('X', touchInfo.location), y: new TouchAxis('Y', touchInfo.location), type: touchInfo.type, threshold: userEvents.threshold || THRESHOLD[touchInfo.type], userEvents: userEvents, target: target, currentTarget: touchInfo.currentTarget, initialTouch: touchInfo.target, id: touchInfo.id, pressEvent: touchInfo, _clicks: userEvents._clicks, supportDoubleTap: userEvents.supportDoubleTap, _moved: false, _finished: false }); } if ( Class ) Touch.__proto__ = Class; Touch.prototype = Object.create( Class && Class.prototype ); Touch.prototype.constructor = Touch; Touch.prototype.press = function press () { var this$1 = this; this._holdTimeout = setTimeout(function () { return this$1._hold(); }, this.userEvents.minHold); this._trigger(PRESS, this.pressEvent); }; Touch.prototype._tap = function _tap (touchInfo) { var that = this; that.userEvents._clicks++; if (that.userEvents._clicks === 1) { that._clickTimeout = setTimeout(function() { if (that.userEvents._clicks === 1) { that._trigger(TAP, touchInfo); } else { that._trigger(DOUBLETAP, touchInfo); } that.userEvents._clicks = 0; }, CLICK_DELAY); } }; Touch.prototype._hold = function _hold () { this._trigger(HOLD, this.pressEvent); }; /* eslint-disable consistent-return */ Touch.prototype.move = function move (touchInfo) { var that = this; var preventMove = touchInfo.type !== 'api' && that.userEvents._shouldNotMove; if (that._finished || preventMove) { return; } that.x.move(touchInfo.location); that.y.move(touchInfo.location); if (!that._moved) { if (that._withinIgnoreThreshold()) { return; } if (!UserEvents.current || UserEvents.current === that.userEvents) { that._start(touchInfo); } else { return that.dispose(); } } if (!that._finished) { that._trigger(MOVE, touchInfo); } }; /* eslint-enable consistent-return */ Touch.prototype.end = function end (touchInfo) { this.endTime = now(); if (this._finished) { return; } this._finished = true; this._trigger(RELEASE, touchInfo); if (this._moved) { this._trigger(END, touchInfo); } else { if (this.supportDoubleTap) { this._tap(touchInfo); } else { this._trigger(TAP, touchInfo); } } clearTimeout(this._holdTimeout); this.dispose(); }; Touch.prototype.dispose = function dispose () { var userEvents = this.userEvents, activeTouches = userEvents.touches || []; this._finished = true; this.pressEvent = null; clearTimeout(this._holdTimeout); // activeTouches.splice($.inArray(this, activeTouches), 1); var activeTouchIndex = activeTouches.indexOf(this); activeTouches.splice(activeTouchIndex, 1); }; Touch.prototype.skip = function skip () { this.dispose(); }; Touch.prototype.cancel = function cancel () { this.dispose(); }; Touch.prototype.isMoved = function isMoved () { return this._moved; }; Touch.prototype._start = function _start (touchInfo) { clearTimeout(this._holdTimeout); this.startTime = now(); this._moved = true; this._trigger(START, touchInfo); }; Touch.prototype._trigger = function _trigger (name, touchInfo) { var e = touchInfo.event; var data = { touch: this, x: this.x, y: this.y, target: this.target, event: e }; if (this.userEvents.notify(name, data)) { e.preventDefault(); } }; Touch.prototype._withinIgnoreThreshold = function _withinIgnoreThreshold () { var xDelta = this.x.initialDelta, yDelta = this.y.initialDelta; return Math.sqrt(xDelta * xDelta + yDelta * yDelta) <= this.threshold; }; return Touch; }(Class)); function withEachUpEvent(callback) { var downEvents = eventMap.up.split(' '), idx = 0, length = downEvents.length; for (; idx < length; idx++) { callback(downEvents[idx]); } } var UserEvents = (function (Observable) { function UserEvents(element, options) { Observable.call(this); var that = this; var filter; var support = getSupportedFeatures(); this.support = support; /* eslint-disable no-param-reassign */ options = options || {}; /* eslint-enable no-param-reassign */ this.options = options; filter = that.filter = options.filter; that.threshold = options.threshold || DEFAULT_THRESHOLD; that.minHold = options.minHold || DEFAULT_MIN_HOLD; that.touches = []; that._maxTouches = options.multiTouch ? 2 : 1; that.allowSelection = options.allowSelection; that.captureUpIfMoved = options.captureUpIfMoved; that._clicks = 0; that.supportDoubleTap = options.supportDoubleTap; extend(that, { element: element, surface: options.surface || element, stopPropagation: options.stopPropagation, pressed: false }); this._surfaceMoveHandler = this._move.bind(this); on(that.surface, applyEventMap('move'), this._surfaceMoveHandler); this._surfaceEndHandler = this._end.bind(this); on(that.surface, applyEventMap('up cancel'), this._surfaceEndHandler); this._elementStartHandler = this._start.bind(this); on(element, applyEventMap('down'), filter, this._elementStartHandler); element.style['touch-action'] = options.touchAction || 'none'; if (options.preventDragEvent) { this._elementDragStartHandler = preventDefault; on(element, applyEventMap('dragstart'), this._elementDragStartHandler); } // element.on(kendo.applyEventMap('mousedown'), filter, { // root: element // } '_select'); // todo: use root this._elementSelectHandler = this._select.bind(this); on(element, applyEventMap('mousedown'), filter, this._elementSelectHandler); if (that.captureUpIfMoved) { var surfaceElement = that.surface, preventIfMovingProxy = that.preventIfMoving.bind(that); withEachUpEvent(function(eventName) { surfaceElement.addEventListener(eventName, preventIfMovingProxy, true); }); } that.bind([ PRESS, HOLD, TAP, DOUBLETAP, START, MOVE, END, RELEASE, CANCEL, GESTURESTART, GESTURECHANGE, GESTUREEND, GESTURETAP, SELECT ], options); } if ( Observable ) UserEvents.__proto__ = Observable; UserEvents.prototype = Object.create( Observable && Observable.prototype ); UserEvents.prototype.constructor = UserEvents; UserEvents.prototype.preventIfMoving = function preventIfMoving (e) { if (this._isMoved()) { e.preventDefault(); } }; UserEvents.prototype.destroy = function destroy () { var that = this; var options = this.options; var element = this.element; if (that._destroyed) { return; } that._destroyed = true; if (that.captureUpIfMoved) { var surfaceElement = that.surface; withEachUpEvent(function(eventName) { surfaceElement.removeEventListener(eventName, that.preventIfMoving); }); } off(that.surface, applyEventMap('move'), this._surfaceMoveHandler); off(that.surface, applyEventMap('up cancel'), this._surfaceEndHandler); off(element, applyEventMap('down'), this._elementStartHandler); if (options.preventDragEvent) { off(element, applyEventMap('dragstart'), this._elementDragStartHandler); } off(element, applyEventMap('mousedown'), this._elementSelectHandler); that._disposeAll(); that.unbind(); delete that.surface; delete that.element; delete that.currentTarget; }; UserEvents.prototype.capture = function capture () { UserEvents.current = this; }; UserEvents.prototype.cancel = function cancel () { this._disposeAll(); this.trigger(CANCEL); }; UserEvents.prototype.notify = function notify (event, data) { var that = this, touches = that.touches; var eventName = event; if (this._isMultiTouch()) { switch (eventName) { case MOVE: eventName = GESTURECHANGE; break; case END: eventName = GESTUREEND; break; case TAP: eventName = GESTURETAP; break; default: break; } extend(data, { touches: touches }, touchDelta(touches[0], touches[1])); } return this.trigger(eventName, extend(data, { type: eventName })); }; UserEvents.prototype.press = function press (x, y, target) { this._apiCall('_start', x, y, target); }; UserEvents.prototype.move = function move (x, y) { this._apiCall('_move', x, y); }; UserEvents.prototype.end = function end (x, y) { this._apiCall('_end', x, y); }; UserEvents.prototype._isMultiTouch = function _isMultiTouch () { return this.touches.length > 1; }; UserEvents.prototype._maxTouchesReached = function _maxTouchesReached () { return this.touches.length >= this._maxTouches; }; UserEvents.prototype._disposeAll = function _disposeAll () { var touches = this.touches; while (touches.length > 0) { touches.pop().dispose(); } }; UserEvents.prototype._isMoved = function _isMoved () { return grep(this.touches, function(touch) { return touch.isMoved(); }).length; }; UserEvents.prototype._select = function _select (e) { if (!this.allowSelection || this.trigger(SELECT, { event: e })) { e.preventDefault(); } }; UserEvents.prototype._start = function _start (e) { var this$1 = this; if (e.which && e.which > 1 || this._maxTouchesReached()) { return; } UserEvents.current = null; this.currentTarget = e.currentTarget; if (this.stopPropagation) { e.stopPropagation(); } var target; var eventTouches = getTouches(e); for (var idx = 0; idx < eventTouches.length; idx++) { if (this$1._maxTouchesReached()) { break; } var eventTouch = eventTouches[idx]; if (this$1.filter) { target = eventTouch.currentTarget; } else { target = this$1.element; } if (target && target.length === 0) { continue; } var touch = new Touch(this$1, target, eventTouch); this$1.touches.push(touch); touch.press(); if (this$1._isMultiTouch()) { this$1.notify('gesturestart', {}); } } }; UserEvents.prototype._move = function _move (e) { this._eachTouch('move', e); }; UserEvents.prototype._end = function _end (e) { this._eachTouch('end', e); }; UserEvents.prototype._eachTouch = function _eachTouch (methodName, e) { var this$1 = this; var that = this, dict = {}, touches = getTouches(e), activeTouches = that.touches, idx, touch, touchInfo, matchingTouch; for (idx = 0; idx < activeTouches.length; idx++) { touch = activeTouches[idx]; dict[touch.id] = touch; } for (idx = 0; idx < touches.length; idx++) { touchInfo = touches[idx]; matchingTouch = dict[touchInfo.id]; if (matchingTouch) { var shouldCapture = methodName === 'move' && touchInfo.type === 'pointer' && !this$1.surface.hasPointerCapture(touchInfo.id); if (shouldCapture) { this$1.surface.setPointerCapture(touchInfo.id); } matchingTouch[methodName](touchInfo); } } }; UserEvents.prototype._apiCall = function _apiCall (type, x, y, target) { this[type]({ api: true, pageX: x, pageY: y, clientX: x, clientY: y, target: target || this.element, stopPropagation: noop, preventDefault: noop }); }; UserEvents.defaultThreshold = function defaultThreshold (value) { DEFAULT_THRESHOLD = value; }; UserEvents.minHold = function minHold (value) { DEFAULT_MIN_HOLD = value; }; return UserEvents; }(Observable)); export default UserEvents;