1 | 'use strict';
|
2 |
|
3 | const gestureController = require('./gesture-controller-29adda71.js');
|
4 |
|
5 | const addEventListener = (el, eventName, callback, opts) => {
|
6 |
|
7 |
|
8 | const listenerOpts = supportsPassive(el) ? {
|
9 | 'capture': !!opts.capture,
|
10 | 'passive': !!opts.passive,
|
11 | } : !!opts.capture;
|
12 | let add;
|
13 | let remove;
|
14 | if (el['__zone_symbol__addEventListener']) {
|
15 | add = '__zone_symbol__addEventListener';
|
16 | remove = '__zone_symbol__removeEventListener';
|
17 | }
|
18 | else {
|
19 | add = 'addEventListener';
|
20 | remove = 'removeEventListener';
|
21 | }
|
22 | el[add](eventName, callback, listenerOpts);
|
23 | return () => {
|
24 | el[remove](eventName, callback, listenerOpts);
|
25 | };
|
26 | };
|
27 | const supportsPassive = (node) => {
|
28 | if (_sPassive === undefined) {
|
29 | try {
|
30 | const opts = Object.defineProperty({}, 'passive', {
|
31 | get: () => {
|
32 | _sPassive = true;
|
33 | }
|
34 | });
|
35 | node.addEventListener('optsTest', () => { return; }, opts);
|
36 | }
|
37 | catch (e) {
|
38 | _sPassive = false;
|
39 | }
|
40 | }
|
41 | return !!_sPassive;
|
42 | };
|
43 | let _sPassive;
|
44 |
|
45 | const MOUSE_WAIT = 2000;
|
46 | const createPointerEvents = (el, pointerDown, pointerMove, pointerUp, options) => {
|
47 | let rmTouchStart;
|
48 | let rmTouchMove;
|
49 | let rmTouchEnd;
|
50 | let rmTouchCancel;
|
51 | let rmMouseStart;
|
52 | let rmMouseMove;
|
53 | let rmMouseUp;
|
54 | let lastTouchEvent = 0;
|
55 | const handleTouchStart = (ev) => {
|
56 | lastTouchEvent = Date.now() + MOUSE_WAIT;
|
57 | if (!pointerDown(ev)) {
|
58 | return;
|
59 | }
|
60 | if (!rmTouchMove && pointerMove) {
|
61 | rmTouchMove = addEventListener(el, 'touchmove', pointerMove, options);
|
62 | }
|
63 | |
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 | if (!rmTouchEnd) {
|
73 | rmTouchEnd = addEventListener(ev.target, 'touchend', handleTouchEnd, options);
|
74 | }
|
75 | if (!rmTouchCancel) {
|
76 | rmTouchCancel = addEventListener(ev.target, 'touchcancel', handleTouchEnd, options);
|
77 | }
|
78 | };
|
79 | const handleMouseDown = (ev) => {
|
80 | if (lastTouchEvent > Date.now()) {
|
81 | return;
|
82 | }
|
83 | if (!pointerDown(ev)) {
|
84 | return;
|
85 | }
|
86 | if (!rmMouseMove && pointerMove) {
|
87 | rmMouseMove = addEventListener(getDocument(el), 'mousemove', pointerMove, options);
|
88 | }
|
89 | if (!rmMouseUp) {
|
90 | rmMouseUp = addEventListener(getDocument(el), 'mouseup', handleMouseUp, options);
|
91 | }
|
92 | };
|
93 | const handleTouchEnd = (ev) => {
|
94 | stopTouch();
|
95 | if (pointerUp) {
|
96 | pointerUp(ev);
|
97 | }
|
98 | };
|
99 | const handleMouseUp = (ev) => {
|
100 | stopMouse();
|
101 | if (pointerUp) {
|
102 | pointerUp(ev);
|
103 | }
|
104 | };
|
105 | const stopTouch = () => {
|
106 | if (rmTouchMove) {
|
107 | rmTouchMove();
|
108 | }
|
109 | if (rmTouchEnd) {
|
110 | rmTouchEnd();
|
111 | }
|
112 | if (rmTouchCancel) {
|
113 | rmTouchCancel();
|
114 | }
|
115 | rmTouchMove = rmTouchEnd = rmTouchCancel = undefined;
|
116 | };
|
117 | const stopMouse = () => {
|
118 | if (rmMouseMove) {
|
119 | rmMouseMove();
|
120 | }
|
121 | if (rmMouseUp) {
|
122 | rmMouseUp();
|
123 | }
|
124 | rmMouseMove = rmMouseUp = undefined;
|
125 | };
|
126 | const stop = () => {
|
127 | stopTouch();
|
128 | stopMouse();
|
129 | };
|
130 | const enable = (isEnabled = true) => {
|
131 | if (!isEnabled) {
|
132 | if (rmTouchStart) {
|
133 | rmTouchStart();
|
134 | }
|
135 | if (rmMouseStart) {
|
136 | rmMouseStart();
|
137 | }
|
138 | rmTouchStart = rmMouseStart = undefined;
|
139 | stop();
|
140 | }
|
141 | else {
|
142 | if (!rmTouchStart) {
|
143 | rmTouchStart = addEventListener(el, 'touchstart', handleTouchStart, options);
|
144 | }
|
145 | if (!rmMouseStart) {
|
146 | rmMouseStart = addEventListener(el, 'mousedown', handleMouseDown, options);
|
147 | }
|
148 | }
|
149 | };
|
150 | const destroy = () => {
|
151 | enable(false);
|
152 | pointerUp = pointerMove = pointerDown = undefined;
|
153 | };
|
154 | return {
|
155 | enable,
|
156 | stop,
|
157 | destroy
|
158 | };
|
159 | };
|
160 | const getDocument = (node) => {
|
161 | return node instanceof Document ? node : node.ownerDocument;
|
162 | };
|
163 |
|
164 | const createPanRecognizer = (direction, thresh, maxAngle) => {
|
165 | const radians = maxAngle * (Math.PI / 180);
|
166 | const isDirX = direction === 'x';
|
167 | const maxCosine = Math.cos(radians);
|
168 | const threshold = thresh * thresh;
|
169 | let startX = 0;
|
170 | let startY = 0;
|
171 | let dirty = false;
|
172 | let isPan = 0;
|
173 | return {
|
174 | start(x, y) {
|
175 | startX = x;
|
176 | startY = y;
|
177 | isPan = 0;
|
178 | dirty = true;
|
179 | },
|
180 | detect(x, y) {
|
181 | if (!dirty) {
|
182 | return false;
|
183 | }
|
184 | const deltaX = (x - startX);
|
185 | const deltaY = (y - startY);
|
186 | const distance = deltaX * deltaX + deltaY * deltaY;
|
187 | if (distance < threshold) {
|
188 | return false;
|
189 | }
|
190 | const hypotenuse = Math.sqrt(distance);
|
191 | const cosine = (isDirX ? deltaX : deltaY) / hypotenuse;
|
192 | if (cosine > maxCosine) {
|
193 | isPan = 1;
|
194 | }
|
195 | else if (cosine < -maxCosine) {
|
196 | isPan = -1;
|
197 | }
|
198 | else {
|
199 | isPan = 0;
|
200 | }
|
201 | dirty = false;
|
202 | return true;
|
203 | },
|
204 | isGesture() {
|
205 | return isPan !== 0;
|
206 | },
|
207 | getDirection() {
|
208 | return isPan;
|
209 | }
|
210 | };
|
211 | };
|
212 |
|
213 | const createGesture = (config) => {
|
214 | let hasCapturedPan = false;
|
215 | let hasStartedPan = false;
|
216 | let hasFiredStart = true;
|
217 | let isMoveQueued = false;
|
218 | const finalConfig = Object.assign({ disableScroll: false, direction: 'x', gesturePriority: 0, passive: true, maxAngle: 40, threshold: 10 }, config);
|
219 | const canStart = finalConfig.canStart;
|
220 | const onWillStart = finalConfig.onWillStart;
|
221 | const onStart = finalConfig.onStart;
|
222 | const onEnd = finalConfig.onEnd;
|
223 | const notCaptured = finalConfig.notCaptured;
|
224 | const onMove = finalConfig.onMove;
|
225 | const threshold = finalConfig.threshold;
|
226 | const passive = finalConfig.passive;
|
227 | const blurOnStart = finalConfig.blurOnStart;
|
228 | const detail = {
|
229 | type: 'pan',
|
230 | startX: 0,
|
231 | startY: 0,
|
232 | startTime: 0,
|
233 | currentX: 0,
|
234 | currentY: 0,
|
235 | velocityX: 0,
|
236 | velocityY: 0,
|
237 | deltaX: 0,
|
238 | deltaY: 0,
|
239 | currentTime: 0,
|
240 | event: undefined,
|
241 | data: undefined
|
242 | };
|
243 | const pan = createPanRecognizer(finalConfig.direction, finalConfig.threshold, finalConfig.maxAngle);
|
244 | const gesture = gestureController.GESTURE_CONTROLLER.createGesture({
|
245 | name: config.gestureName,
|
246 | priority: config.gesturePriority,
|
247 | disableScroll: config.disableScroll
|
248 | });
|
249 | const pointerDown = (ev) => {
|
250 | const timeStamp = now(ev);
|
251 | if (hasStartedPan || !hasFiredStart) {
|
252 | return false;
|
253 | }
|
254 | updateDetail(ev, detail);
|
255 | detail.startX = detail.currentX;
|
256 | detail.startY = detail.currentY;
|
257 | detail.startTime = detail.currentTime = timeStamp;
|
258 | detail.velocityX = detail.velocityY = detail.deltaX = detail.deltaY = 0;
|
259 | detail.event = ev;
|
260 |
|
261 | if (canStart && canStart(detail) === false) {
|
262 | return false;
|
263 | }
|
264 |
|
265 | gesture.release();
|
266 |
|
267 | if (!gesture.start()) {
|
268 | return false;
|
269 | }
|
270 | hasStartedPan = true;
|
271 | if (threshold === 0) {
|
272 | return tryToCapturePan();
|
273 | }
|
274 | pan.start(detail.startX, detail.startY);
|
275 | return true;
|
276 | };
|
277 | const pointerMove = (ev) => {
|
278 |
|
279 |
|
280 | if (hasCapturedPan) {
|
281 | if (!isMoveQueued && hasFiredStart) {
|
282 | isMoveQueued = true;
|
283 | calcGestureData(detail, ev);
|
284 | requestAnimationFrame(fireOnMove);
|
285 | }
|
286 | return;
|
287 | }
|
288 |
|
289 | calcGestureData(detail, ev);
|
290 | if (pan.detect(detail.currentX, detail.currentY)) {
|
291 | if (!pan.isGesture() || !tryToCapturePan()) {
|
292 | abortGesture();
|
293 | }
|
294 | }
|
295 | };
|
296 | const fireOnMove = () => {
|
297 |
|
298 |
|
299 | if (!hasCapturedPan) {
|
300 | return;
|
301 | }
|
302 | isMoveQueued = false;
|
303 | if (onMove) {
|
304 | onMove(detail);
|
305 | }
|
306 | };
|
307 | const tryToCapturePan = () => {
|
308 | if (gesture && !gesture.capture()) {
|
309 | return false;
|
310 | }
|
311 | hasCapturedPan = true;
|
312 | hasFiredStart = false;
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 | detail.startX = detail.currentX;
|
320 | detail.startY = detail.currentY;
|
321 | detail.startTime = detail.currentTime;
|
322 | if (onWillStart) {
|
323 | onWillStart(detail).then(fireOnStart);
|
324 | }
|
325 | else {
|
326 | fireOnStart();
|
327 | }
|
328 | return true;
|
329 | };
|
330 | const blurActiveElement = () => {
|
331 |
|
332 | if (typeof document !== 'undefined') {
|
333 | const activeElement = document.activeElement;
|
334 | if (activeElement !== null && activeElement.blur) {
|
335 | activeElement.blur();
|
336 | }
|
337 | }
|
338 | };
|
339 | const fireOnStart = () => {
|
340 | if (blurOnStart) {
|
341 | blurActiveElement();
|
342 | }
|
343 | if (onStart) {
|
344 | onStart(detail);
|
345 | }
|
346 | hasFiredStart = true;
|
347 | };
|
348 | const reset = () => {
|
349 | hasCapturedPan = false;
|
350 | hasStartedPan = false;
|
351 | isMoveQueued = false;
|
352 | hasFiredStart = true;
|
353 | gesture.release();
|
354 | };
|
355 |
|
356 | const pointerUp = (ev) => {
|
357 | const tmpHasCaptured = hasCapturedPan;
|
358 | const tmpHasFiredStart = hasFiredStart;
|
359 | reset();
|
360 | if (!tmpHasFiredStart) {
|
361 | return;
|
362 | }
|
363 | calcGestureData(detail, ev);
|
364 |
|
365 | if (tmpHasCaptured) {
|
366 | if (onEnd) {
|
367 | onEnd(detail);
|
368 | }
|
369 | return;
|
370 | }
|
371 |
|
372 | if (notCaptured) {
|
373 | notCaptured(detail);
|
374 | }
|
375 | };
|
376 | const pointerEvents = createPointerEvents(finalConfig.el, pointerDown, pointerMove, pointerUp, {
|
377 | capture: false,
|
378 | passive
|
379 | });
|
380 | const abortGesture = () => {
|
381 | reset();
|
382 | pointerEvents.stop();
|
383 | if (notCaptured) {
|
384 | notCaptured(detail);
|
385 | }
|
386 | };
|
387 | return {
|
388 | enable(enable = true) {
|
389 | if (!enable) {
|
390 | if (hasCapturedPan) {
|
391 | pointerUp(undefined);
|
392 | }
|
393 | reset();
|
394 | }
|
395 | pointerEvents.enable(enable);
|
396 | },
|
397 | destroy() {
|
398 | gesture.destroy();
|
399 | pointerEvents.destroy();
|
400 | }
|
401 | };
|
402 | };
|
403 | const calcGestureData = (detail, ev) => {
|
404 | if (!ev) {
|
405 | return;
|
406 | }
|
407 | const prevX = detail.currentX;
|
408 | const prevY = detail.currentY;
|
409 | const prevT = detail.currentTime;
|
410 | updateDetail(ev, detail);
|
411 | const currentX = detail.currentX;
|
412 | const currentY = detail.currentY;
|
413 | const timestamp = detail.currentTime = now(ev);
|
414 | const timeDelta = timestamp - prevT;
|
415 | if (timeDelta > 0 && timeDelta < 100) {
|
416 | const velocityX = (currentX - prevX) / timeDelta;
|
417 | const velocityY = (currentY - prevY) / timeDelta;
|
418 | detail.velocityX = velocityX * 0.7 + detail.velocityX * 0.3;
|
419 | detail.velocityY = velocityY * 0.7 + detail.velocityY * 0.3;
|
420 | }
|
421 | detail.deltaX = currentX - detail.startX;
|
422 | detail.deltaY = currentY - detail.startY;
|
423 | detail.event = ev;
|
424 | };
|
425 | const updateDetail = (ev, detail) => {
|
426 |
|
427 |
|
428 | let x = 0;
|
429 | let y = 0;
|
430 | if (ev) {
|
431 | const changedTouches = ev.changedTouches;
|
432 | if (changedTouches && changedTouches.length > 0) {
|
433 | const touch = changedTouches[0];
|
434 | x = touch.clientX;
|
435 | y = touch.clientY;
|
436 | }
|
437 | else if (ev.pageX !== undefined) {
|
438 | x = ev.pageX;
|
439 | y = ev.pageY;
|
440 | }
|
441 | }
|
442 | detail.currentX = x;
|
443 | detail.currentY = y;
|
444 | };
|
445 | const now = (ev) => {
|
446 | return ev.timeStamp || Date.now();
|
447 | };
|
448 |
|
449 | exports.GESTURE_CONTROLLER = gestureController.GESTURE_CONTROLLER;
|
450 | exports.createGesture = createGesture;
|