Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(480)

Side by Side Diff: appengine/config_service/ui/bower_components/polymer/lib/utils/gestures.html

Issue 2923973003: Added base template for config ui. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!--
2 @license
3 Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 -->
10 <link rel="import" href="boot.html">
11 <link rel="import" href="async.html">
12 <link rel="import" href="debounce.html">
13
14 <script>
15 (function() {
16
17 'use strict';
18
19 // detect native touch action support
20 let HAS_NATIVE_TA = typeof document.head.style.touchAction === 'string';
21 let GESTURE_KEY = '__polymerGestures';
22 let HANDLED_OBJ = '__polymerGesturesHandled';
23 let TOUCH_ACTION = '__polymerGesturesTouchAction';
24 // radius for tap and track
25 let TAP_DISTANCE = 25;
26 let TRACK_DISTANCE = 5;
27 // number of last N track positions to keep
28 let TRACK_LENGTH = 2;
29
30 // Disabling "mouse" handlers for 2500ms is enough
31 let MOUSE_TIMEOUT = 2500;
32 let MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'click'];
33 // an array of bitmask values for mapping MouseEvent.which to MouseEvent.butto ns
34 let MOUSE_WHICH_TO_BUTTONS = [0, 1, 4, 2];
35 let MOUSE_HAS_BUTTONS = (function() {
36 try {
37 return new MouseEvent('test', {buttons: 1}).buttons === 1;
38 } catch (e) {
39 return false;
40 }
41 })();
42
43 /* eslint no-empty: ["error", { "allowEmptyCatch": true }] */
44 // check for passive event listeners
45 let SUPPORTS_PASSIVE = false;
46 (function() {
47 try {
48 let opts = Object.defineProperty({}, 'passive', {get: function() {SUPPORTS _PASSIVE = true;}})
49 window.addEventListener('test', null, opts);
50 window.removeEventListener('test', null, opts);
51 } catch(e) {}
52 })();
53
54 // Check for touch-only devices
55 let IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);
56
57 // touch will make synthetic mouse events
58 // `preventDefault` on touchend will cancel them,
59 // but this breaks `<input>` focus and link clicks
60 // disable mouse handlers for MOUSE_TIMEOUT ms after
61 // a touchend to ignore synthetic mouse events
62 let mouseCanceller = function(mouseEvent) {
63 // Check for sourceCapabilities, used to distinguish synthetic events
64 // if mouseEvent did not come from a device that fires touch events,
65 // it was made by a real mouse and should be counted
66 // http://wicg.github.io/InputDeviceCapabilities/#dom-inputdevicecapabilitie s-firestouchevents
67 let sc = mouseEvent.sourceCapabilities;
68 if (sc && !sc.firesTouchEvents) {
69 return;
70 }
71 // skip synthetic mouse events
72 mouseEvent[HANDLED_OBJ] = {skip: true};
73 // disable "ghost clicks"
74 if (mouseEvent.type === 'click') {
75 let path = mouseEvent.composedPath && mouseEvent.composedPath();
76 if (path) {
77 for (let i = 0; i < path.length; i++) {
78 if (path[i] === POINTERSTATE.mouse.target) {
79 return;
80 }
81 }
82 }
83 mouseEvent.preventDefault();
84 mouseEvent.stopPropagation();
85 }
86 };
87
88 /**
89 * @param {boolean=} setup True to add, false to remove.
90 */
91 function setupTeardownMouseCanceller(setup) {
92 let events = IS_TOUCH_ONLY ? ['click'] : MOUSE_EVENTS;
93 for (let i = 0, en; i < events.length; i++) {
94 en = events[i];
95 if (setup) {
96 document.addEventListener(en, mouseCanceller, true);
97 } else {
98 document.removeEventListener(en, mouseCanceller, true);
99 }
100 }
101 }
102
103 function ignoreMouse(e) {
104 if (!POINTERSTATE.mouse.mouseIgnoreJob) {
105 setupTeardownMouseCanceller(true);
106 }
107 let unset = function() {
108 setupTeardownMouseCanceller();
109 POINTERSTATE.mouse.target = null;
110 POINTERSTATE.mouse.mouseIgnoreJob = null;
111 };
112 POINTERSTATE.mouse.target = e.composedPath()[0];
113 POINTERSTATE.mouse.mouseIgnoreJob = Polymer.Debouncer.debounce(
114 POINTERSTATE.mouse.mouseIgnoreJob
115 , Polymer.Async.timeOut.after(MOUSE_TIMEOUT)
116 , unset);
117 }
118
119 function hasLeftMouseButton(ev) {
120 let type = ev.type;
121 // exit early if the event is not a mouse event
122 if (MOUSE_EVENTS.indexOf(type) === -1) {
123 return false;
124 }
125 // ev.button is not reliable for mousemove (0 is overloaded as both left but ton and no buttons)
126 // instead we use ev.buttons (bitmask of buttons) or fall back to ev.which ( deprecated, 0 for no buttons, 1 for left button)
127 if (type === 'mousemove') {
128 // allow undefined for testing events
129 let buttons = ev.buttons === undefined ? 1 : ev.buttons;
130 if ((ev instanceof window.MouseEvent) && !MOUSE_HAS_BUTTONS) {
131 buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0;
132 }
133 // buttons is a bitmask, check that the left button bit is set (1)
134 return Boolean(buttons & 1);
135 } else {
136 // allow undefined for testing events
137 let button = ev.button === undefined ? 0 : ev.button;
138 // ev.button is 0 in mousedown/mouseup/click for left button activation
139 return button === 0;
140 }
141 }
142
143 function isSyntheticClick(ev) {
144 if (ev.type === 'click') {
145 // ev.detail is 0 for HTMLElement.click in most browsers
146 if (ev.detail === 0) {
147 return true;
148 }
149 // in the worst case, check that the x/y position of the click is within
150 // the bounding box of the target of the event
151 // Thanks IE 10 >:(
152 let t = Gestures._findOriginalTarget(ev);
153 // make sure the target of the event is an element so we can use getBoundi ngClientRect,
154 // if not, just assume it is a synthetic click
155 if (t.nodeType !== Node.ELEMENT_NODE) {
156 return true;
157 }
158 let bcr = t.getBoundingClientRect();
159 // use page x/y to account for scrolling
160 let x = ev.pageX, y = ev.pageY;
161 // ev is a synthetic click if the position is outside the bounding box of the target
162 return !((x >= bcr.left && x <= bcr.right) && (y >= bcr.top && y <= bcr.bo ttom));
163 }
164 return false;
165 }
166
167 let POINTERSTATE = {
168 mouse: {
169 target: null,
170 mouseIgnoreJob: null
171 },
172 touch: {
173 x: 0,
174 y: 0,
175 id: -1,
176 scrollDecided: false
177 }
178 };
179
180 function firstTouchAction(ev) {
181 let ta = 'auto';
182 let path = ev.composedPath && ev.composedPath();
183 if (path) {
184 for (let i = 0, n; i < path.length; i++) {
185 n = path[i];
186 if (n[TOUCH_ACTION]) {
187 ta = n[TOUCH_ACTION];
188 break;
189 }
190 }
191 }
192 return ta;
193 }
194
195 function trackDocument(stateObj, movefn, upfn) {
196 stateObj.movefn = movefn;
197 stateObj.upfn = upfn;
198 document.addEventListener('mousemove', movefn);
199 document.addEventListener('mouseup', upfn);
200 }
201
202 function untrackDocument(stateObj) {
203 document.removeEventListener('mousemove', stateObj.movefn);
204 document.removeEventListener('mouseup', stateObj.upfn);
205 stateObj.movefn = null;
206 stateObj.upfn = null;
207 }
208
209 // use a document-wide touchend listener to start the ghost-click prevention m echanism
210 // Use passive event listeners, if supported, to not affect scrolling performa nce
211 document.addEventListener('touchend', ignoreMouse, SUPPORTS_PASSIVE ? {passive : true} : false);
212
213 /**
214 * Module for adding listeners to a node for the following normalized
215 * cross-platform "gesture" events:
216 * - `down` - mouse or touch went down
217 * - `up` - mouse or touch went up
218 * - `tap` - mouse click or finger tap
219 * - `track` - mouse drag or touch move
220 *
221 * @namespace
222 * @memberof Polymer
223 * @summary Module for adding cross-platform gesture event listeners.
224 */
225 const Gestures = {
226 gestures: {},
227 recognizers: [],
228
229 /**
230 * Finds the element rendered on the screen at the provided coordinates.
231 *
232 * Similar to `document.elementFromPoint`, but pierces through
233 * shadow roots.
234 *
235 * @memberof Polymer.Gestures
236 * @param {number} x Horizontal pixel coordinate
237 * @param {number} y Vertical pixel coordinate
238 * @return {HTMLElement} Returns the deepest shadowRoot inclusive element
239 * found at the screen position given.
240 */
241 deepTargetFind: function(x, y) {
242 let node = document.elementFromPoint(x, y);
243 let next = node;
244 // this code path is only taken when native ShadowDOM is used
245 // if there is a shadowroot, it may have a node at x/y
246 // if there is not a shadowroot, exit the loop
247 while (next && next.shadowRoot && !window.ShadyDOM) {
248 // if there is a node at x/y in the shadowroot, look deeper
249 let oldNext = next;
250 next = next.shadowRoot.elementFromPoint(x, y);
251 // on Safari, elementFromPoint may return the shadowRoot host
252 if (oldNext === next) {
253 break;
254 }
255 if (next) {
256 node = next;
257 }
258 }
259 return node;
260 },
261 /**
262 * a cheaper check than ev.composedPath()[0];
263 *
264 * @private
265 * @param {Event} ev Event.
266 * @return {HTMLElement} Returns the event target.
267 */
268 _findOriginalTarget: function(ev) {
269 // shadowdom
270 if (ev.composedPath) {
271 return ev.composedPath()[0];
272 }
273 // shadydom
274 return ev.target;
275 },
276
277 /**
278 * @private
279 * @param {Event} ev Event.
280 */
281 _handleNative: function(ev) {
282 let handled;
283 let type = ev.type;
284 let node = ev.currentTarget;
285 let gobj = node[GESTURE_KEY];
286 if (!gobj) {
287 return;
288 }
289 let gs = gobj[type];
290 if (!gs) {
291 return;
292 }
293 if (!ev[HANDLED_OBJ]) {
294 ev[HANDLED_OBJ] = {};
295 if (type.slice(0, 5) === 'touch') {
296 let t = ev.changedTouches[0];
297 if (type === 'touchstart') {
298 // only handle the first finger
299 if (ev.touches.length === 1) {
300 POINTERSTATE.touch.id = t.identifier;
301 }
302 }
303 if (POINTERSTATE.touch.id !== t.identifier) {
304 return;
305 }
306 if (!HAS_NATIVE_TA) {
307 if (type === 'touchstart' || type === 'touchmove') {
308 Gestures._handleTouchAction(ev);
309 }
310 }
311 }
312 }
313 handled = ev[HANDLED_OBJ];
314 // used to ignore synthetic mouse events
315 if (handled.skip) {
316 return;
317 }
318 let recognizers = Gestures.recognizers;
319 // reset recognizer state
320 for (let i = 0, r; i < recognizers.length; i++) {
321 r = recognizers[i];
322 if (gs[r.name] && !handled[r.name]) {
323 if (r.flow && r.flow.start.indexOf(ev.type) > -1 && r.reset) {
324 r.reset();
325 }
326 }
327 }
328 // enforce gesture recognizer order
329 for (let i = 0, r; i < recognizers.length; i++) {
330 r = recognizers[i];
331 if (gs[r.name] && !handled[r.name]) {
332 handled[r.name] = true;
333 r[type](ev);
334 }
335 }
336 },
337
338 /**
339 * @private
340 * @param {Event} ev Event.
341 */
342 _handleTouchAction: function(ev) {
343 let t = ev.changedTouches[0];
344 let type = ev.type;
345 if (type === 'touchstart') {
346 POINTERSTATE.touch.x = t.clientX;
347 POINTERSTATE.touch.y = t.clientY;
348 POINTERSTATE.touch.scrollDecided = false;
349 } else if (type === 'touchmove') {
350 if (POINTERSTATE.touch.scrollDecided) {
351 return;
352 }
353 POINTERSTATE.touch.scrollDecided = true;
354 let ta = firstTouchAction(ev);
355 let prevent = false;
356 let dx = Math.abs(POINTERSTATE.touch.x - t.clientX);
357 let dy = Math.abs(POINTERSTATE.touch.y - t.clientY);
358 if (!ev.cancelable) {
359 // scrolling is happening
360 } else if (ta === 'none') {
361 prevent = true;
362 } else if (ta === 'pan-x') {
363 prevent = dy > dx;
364 } else if (ta === 'pan-y') {
365 prevent = dx > dy;
366 }
367 if (prevent) {
368 ev.preventDefault();
369 } else {
370 Gestures.prevent('track');
371 }
372 }
373 },
374
375 /**
376 * Adds an event listener to a node for the given gesture type.
377 *
378 * @memberof Polymer.Gestures
379 * @param {Node} node Node to add listener on
380 * @param {string} evType Gesture type: `down`, `up`, `track`, or `tap`
381 * @param {Function} handler Event listener function to call
382 * @return {boolean} Returns true if a gesture event listener was added.
383 */
384 addListener: function(node, evType, handler) {
385 if (this.gestures[evType]) {
386 this._add(node, evType, handler);
387 return true;
388 }
389 },
390
391 /**
392 * Removes an event listener from a node for the given gesture type.
393 *
394 * @memberof Polymer.Gestures
395 * @param {Node} node Node to remove listener from
396 * @param {string} evType Gesture type: `down`, `up`, `track`, or `tap`
397 * @param {Function} handler Event listener function previously passed to
398 * `addListener`.
399 * @return {boolean} Returns true if a gesture event listener was removed.
400 */
401 removeListener: function(node, evType, handler) {
402 if (this.gestures[evType]) {
403 this._remove(node, evType, handler);
404 return true;
405 }
406 },
407
408 /**
409 * automate the event listeners for the native events
410 *
411 * @private
412 * @param {HTMLElement} node Node on which to add the event.
413 * @param {string} evType Event type to add.
414 * @param {function} handler Event handler function.
415 */
416 _add: function(node, evType, handler) {
417 let recognizer = this.gestures[evType];
418 let deps = recognizer.deps;
419 let name = recognizer.name;
420 let gobj = node[GESTURE_KEY];
421 if (!gobj) {
422 node[GESTURE_KEY] = gobj = {};
423 }
424 for (let i = 0, dep, gd; i < deps.length; i++) {
425 dep = deps[i];
426 // don't add mouse handlers on iOS because they cause gray selection ove rlays
427 if (IS_TOUCH_ONLY && MOUSE_EVENTS.indexOf(dep) > -1 && dep !== 'click') {
428 continue;
429 }
430 gd = gobj[dep];
431 if (!gd) {
432 gobj[dep] = gd = {_count: 0};
433 }
434 if (gd._count === 0) {
435 node.addEventListener(dep, this._handleNative);
436 }
437 gd[name] = (gd[name] || 0) + 1;
438 gd._count = (gd._count || 0) + 1;
439 }
440 node.addEventListener(evType, handler);
441 if (recognizer.touchAction) {
442 this.setTouchAction(node, recognizer.touchAction);
443 }
444 },
445
446 /**
447 * automate event listener removal for native events
448 *
449 * @private
450 * @param {HTMLElement} node Node on which to remove the event.
451 * @param {string} evType Event type to remove.
452 * @param {function} handler Event handler function.
453 */
454 _remove: function(node, evType, handler) {
455 let recognizer = this.gestures[evType];
456 let deps = recognizer.deps;
457 let name = recognizer.name;
458 let gobj = node[GESTURE_KEY];
459 if (gobj) {
460 for (let i = 0, dep, gd; i < deps.length; i++) {
461 dep = deps[i];
462 gd = gobj[dep];
463 if (gd && gd[name]) {
464 gd[name] = (gd[name] || 1) - 1;
465 gd._count = (gd._count || 1) - 1;
466 if (gd._count === 0) {
467 node.removeEventListener(dep, this._handleNative);
468 }
469 }
470 }
471 }
472 node.removeEventListener(evType, handler);
473 },
474
475 /**
476 * Registers a new gesture event recognizer for adding new custom
477 * gesture event types.
478 *
479 * @memberof Polymer.Gestures
480 * @param {Object} recog Gesture recognizer descriptor
481 */
482 register: function(recog) {
483 this.recognizers.push(recog);
484 for (let i = 0; i < recog.emits.length; i++) {
485 this.gestures[recog.emits[i]] = recog;
486 }
487 },
488
489 /**
490 * @private
491 * @param {string} evName Event name.
492 * @return {Object} Returns the gesture for the given event name.
493 */
494 _findRecognizerByEvent: function(evName) {
495 for (let i = 0, r; i < this.recognizers.length; i++) {
496 r = this.recognizers[i];
497 for (let j = 0, n; j < r.emits.length; j++) {
498 n = r.emits[j];
499 if (n === evName) {
500 return r;
501 }
502 }
503 }
504 return null;
505 },
506
507 /**
508 * Sets scrolling direction on node.
509 *
510 * This value is checked on first move, thus it should be called prior to
511 * adding event listeners.
512 *
513 * @memberof Polymer.Gestures
514 * @param {Node} node Node to set touch action setting on
515 * @param {string} value Touch action value
516 */
517 setTouchAction: function(node, value) {
518 if (HAS_NATIVE_TA) {
519 node.style.touchAction = value;
520 }
521 node[TOUCH_ACTION] = value;
522 },
523
524 /**
525 * Dispatches an event on the `target` element of `type` with the given
526 * `detail`.
527 * @private
528 * @param {HTMLElement} target The element on which to fire an event.
529 * @param {string} type The type of event to fire.
530 * @param {Object=} detail The detail object to populate on the event.
531 */
532 _fire: function(target, type, detail) {
533 let ev = new Event(type, { bubbles: true, cancelable: true, composed: true });
534 ev.detail = detail;
535 target.dispatchEvent(ev);
536 // forward `preventDefault` in a clean way
537 if (ev.defaultPrevented) {
538 let preventer = detail.preventer || detail.sourceEvent;
539 if (preventer && preventer.preventDefault) {
540 preventer.preventDefault();
541 }
542 }
543 },
544
545 /**
546 * Prevents the dispatch and default action of the given event name.
547 *
548 * @memberof Polymer.Gestures
549 * @param {string} evName Event name.
550 */
551 prevent: function(evName) {
552 let recognizer = this._findRecognizerByEvent(evName);
553 if (recognizer.info) {
554 recognizer.info.prevent = true;
555 }
556 },
557
558 /**
559 * Reset the 2500ms timeout on processing mouse input after detecting touch input.
560 *
561 * Touch inputs create synthesized mouse inputs anywhere from 0 to 2000ms af ter the touch.
562 * This method should only be called during testing with simulated touch inp uts.
563 * Calling this method in production may cause duplicate taps or other Gestu res.
564 *
565 * @memberof Polymer.Gestures
566 */
567 resetMouseCanceller: function() {
568 if (POINTERSTATE.mouse.mouseIgnoreJob) {
569 POINTERSTATE.mouse.mouseIgnoreJob.flush();
570 }
571 }
572 };
573
574 Gestures.register({
575 name: 'downup',
576 deps: ['mousedown', 'touchstart', 'touchend'],
577 flow: {
578 start: ['mousedown', 'touchstart'],
579 end: ['mouseup', 'touchend']
580 },
581 emits: ['down', 'up'],
582
583 info: {
584 movefn: null,
585 upfn: null
586 },
587
588 reset: function() {
589 untrackDocument(this.info);
590 },
591
592 mousedown: function(e) {
593 if (!hasLeftMouseButton(e)) {
594 return;
595 }
596 let t = Gestures._findOriginalTarget(e);
597 let self = this;
598 let movefn = function movefn(e) {
599 if (!hasLeftMouseButton(e)) {
600 self._fire('up', t, e);
601 untrackDocument(self.info);
602 }
603 };
604 let upfn = function upfn(e) {
605 if (hasLeftMouseButton(e)) {
606 self._fire('up', t, e);
607 }
608 untrackDocument(self.info);
609 };
610 trackDocument(this.info, movefn, upfn);
611 this._fire('down', t, e);
612 },
613 touchstart: function(e) {
614 this._fire('down', Gestures._findOriginalTarget(e), e.changedTouches[0], e );
615 },
616 touchend: function(e) {
617 this._fire('up', Gestures._findOriginalTarget(e), e.changedTouches[0], e);
618 },
619 _fire: function(type, target, event, preventer) {
620 Gestures._fire(target, type, {
621 x: event.clientX,
622 y: event.clientY,
623 sourceEvent: event,
624 preventer: preventer,
625 prevent: function(e) {
626 return Gestures.prevent(e);
627 }
628 });
629 }
630 });
631
632 Gestures.register({
633 name: 'track',
634 touchAction: 'none',
635 deps: ['mousedown', 'touchstart', 'touchmove', 'touchend'],
636 flow: {
637 start: ['mousedown', 'touchstart'],
638 end: ['mouseup', 'touchend']
639 },
640 emits: ['track'],
641
642 info: {
643 x: 0,
644 y: 0,
645 state: 'start',
646 started: false,
647 moves: [],
648 addMove: function(move) {
649 if (this.moves.length > TRACK_LENGTH) {
650 this.moves.shift();
651 }
652 this.moves.push(move);
653 },
654 movefn: null,
655 upfn: null,
656 prevent: false
657 },
658
659 reset: function() {
660 this.info.state = 'start';
661 this.info.started = false;
662 this.info.moves = [];
663 this.info.x = 0;
664 this.info.y = 0;
665 this.info.prevent = false;
666 untrackDocument(this.info);
667 },
668
669 hasMovedEnough: function(x, y) {
670 if (this.info.prevent) {
671 return false;
672 }
673 if (this.info.started) {
674 return true;
675 }
676 let dx = Math.abs(this.info.x - x);
677 let dy = Math.abs(this.info.y - y);
678 return (dx >= TRACK_DISTANCE || dy >= TRACK_DISTANCE);
679 },
680
681 mousedown: function(e) {
682 if (!hasLeftMouseButton(e)) {
683 return;
684 }
685 let t = Gestures._findOriginalTarget(e);
686 let self = this;
687 let movefn = function movefn(e) {
688 let x = e.clientX, y = e.clientY;
689 if (self.hasMovedEnough(x, y)) {
690 // first move is 'start', subsequent moves are 'move', mouseup is 'end '
691 self.info.state = self.info.started ? (e.type === 'mouseup' ? 'end' : 'track') : 'start';
692 if (self.info.state === 'start') {
693 // if and only if tracking, always prevent tap
694 Gestures.prevent('tap');
695 }
696 self.info.addMove({x: x, y: y});
697 if (!hasLeftMouseButton(e)) {
698 // always _fire "end"
699 self.info.state = 'end';
700 untrackDocument(self.info);
701 }
702 self._fire(t, e);
703 self.info.started = true;
704 }
705 };
706 let upfn = function upfn(e) {
707 if (self.info.started) {
708 movefn(e);
709 }
710
711 // remove the temporary listeners
712 untrackDocument(self.info);
713 };
714 // add temporary document listeners as mouse retargets
715 trackDocument(this.info, movefn, upfn);
716 this.info.x = e.clientX;
717 this.info.y = e.clientY;
718 },
719
720 touchstart: function(e) {
721 let ct = e.changedTouches[0];
722 this.info.x = ct.clientX;
723 this.info.y = ct.clientY;
724 },
725
726 touchmove: function(e) {
727 let t = Gestures._findOriginalTarget(e);
728 let ct = e.changedTouches[0];
729 let x = ct.clientX, y = ct.clientY;
730 if (this.hasMovedEnough(x, y)) {
731 if (this.info.state === 'start') {
732 // if and only if tracking, always prevent tap
733 Gestures.prevent('tap');
734 }
735 this.info.addMove({x: x, y: y});
736 this._fire(t, ct);
737 this.info.state = 'track';
738 this.info.started = true;
739 }
740 },
741
742 touchend: function(e) {
743 let t = Gestures._findOriginalTarget(e);
744 let ct = e.changedTouches[0];
745 // only trackend if track was started and not aborted
746 if (this.info.started) {
747 // reset started state on up
748 this.info.state = 'end';
749 this.info.addMove({x: ct.clientX, y: ct.clientY});
750 this._fire(t, ct, e);
751 }
752 },
753
754 _fire: function(target, touch) {
755 let secondlast = this.info.moves[this.info.moves.length - 2];
756 let lastmove = this.info.moves[this.info.moves.length - 1];
757 let dx = lastmove.x - this.info.x;
758 let dy = lastmove.y - this.info.y;
759 let ddx, ddy = 0;
760 if (secondlast) {
761 ddx = lastmove.x - secondlast.x;
762 ddy = lastmove.y - secondlast.y;
763 }
764 return Gestures._fire(target, 'track', {
765 state: this.info.state,
766 x: touch.clientX,
767 y: touch.clientY,
768 dx: dx,
769 dy: dy,
770 ddx: ddx,
771 ddy: ddy,
772 sourceEvent: touch,
773 hover: function() {
774 return Gestures.deepTargetFind(touch.clientX, touch.clientY);
775 }
776 });
777 }
778
779 });
780
781 Gestures.register({
782 name: 'tap',
783 deps: ['mousedown', 'click', 'touchstart', 'touchend'],
784 flow: {
785 start: ['mousedown', 'touchstart'],
786 end: ['click', 'touchend']
787 },
788 emits: ['tap'],
789 info: {
790 x: NaN,
791 y: NaN,
792 prevent: false
793 },
794 reset: function() {
795 this.info.x = NaN;
796 this.info.y = NaN;
797 this.info.prevent = false;
798 },
799 save: function(e) {
800 this.info.x = e.clientX;
801 this.info.y = e.clientY;
802 },
803
804 mousedown: function(e) {
805 if (hasLeftMouseButton(e)) {
806 this.save(e);
807 }
808 },
809 click: function(e) {
810 if (hasLeftMouseButton(e)) {
811 this.forward(e);
812 }
813 },
814
815 touchstart: function(e) {
816 this.save(e.changedTouches[0], e);
817 },
818 touchend: function(e) {
819 this.forward(e.changedTouches[0], e);
820 },
821
822 forward: function(e, preventer) {
823 let dx = Math.abs(e.clientX - this.info.x);
824 let dy = Math.abs(e.clientY - this.info.y);
825 let t = Gestures._findOriginalTarget(e);
826 // dx,dy can be NaN if `click` has been simulated and there was no `down` for `start`
827 if (isNaN(dx) || isNaN(dy) || (dx <= TAP_DISTANCE && dy <= TAP_DISTANCE) | | isSyntheticClick(e)) {
828 // prevent taps from being generated if an event has canceled them
829 if (!this.info.prevent) {
830 Gestures._fire(t, 'tap', {
831 x: e.clientX,
832 y: e.clientY,
833 sourceEvent: e,
834 preventer: preventer
835 });
836 }
837 }
838 }
839 });
840
841 /** @deprecated */
842 Gestures.findOriginalTarget = Gestures._findOriginalTarget;
843
844 /** @deprecated */
845 Gestures.add = Gestures.addListener;
846
847 /** @deprecated */
848 Gestures.remove = Gestures.removeListener;
849
850 Polymer.Gestures = Gestures;
851
852 })();
853 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698