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

Side by Side Diff: chrome/browser/resources/touch_ntp/touchhandler.js

Issue 6661024: Use a specialized new tab page in TOUCH_UI builds (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Tweaks based on CR feedback from arv Created 9 years, 9 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview Touch Handler. Class that handles all touch events and
7 * uses them to interpret higher level gestures and behaviors. TouchEvent is a
8 * built in mobile safari type:
9 * http://developer.apple.com/safari/library/documentation/UserExperience/Refere nce/TouchEventClassReference/TouchEvent/TouchEvent.html.
10 * This class is intended to work with all webkit browsers, tested on Chrome and
11 * iOS.
12 *
13 * The following types of gestures are currently supported. See the definition
14 * of TouchHandler.EventType for details.
15 *
16 * Single Touch:
17 * This provides simple single-touch events. Any secondary touch is
18 * ignored.
19 *
20 * Drag:
21 * A single touch followed by some movement. This behavior will handle all
22 * of the required events and report the properties of the drag to you
23 * while the touch is happening and at the end of the drag sequence. This
24 * behavior will NOT perform the actual dragging (redrawing the element)
25 * for you, this responsibility is left to the client code.
26 *
27 * Long press:
28 * When your element is touched and held without any drag occuring, the
29 * LONG_PRESS event will fire.
30 */
31
32
33 /**
34 * A TouchHandler attaches to an Element, listents for low-level touch (or
35 * mouse) events and dispatching higher-level events on the element.
36 * @param {!Element} element The element to listen on and fire events
37 * for.
38 * @constructor
39 */
40 function TouchHandler(element) {
41 /**
42 * @type {!Element}
43 * @private
44 */
45 this.element_ = element;
46
47 /**
48 * The absolute sum of all touch y deltas.
49 * @type {number}
50 * @private
51 */
52 this.totalMoveY_ = 0;
53
54 /**
55 * The absolute sum of all touch x deltas.
56 * @type {number}
57 * @private
58 */
59 this.totalMoveX_ = 0;
60
61 /**
62 * An array of tuples where the first item is the horizontal component of a
63 * recent relevant touch and the second item is the touch's time stamp. Old
64 * touches are removed based on the max tracking time and when direction
65 * changes.
66 * @type {!Array.<number>}
67 * @private
68 */
69 this.recentTouchesX_ = [];
70
71 /**
72 * An array of tuples where the first item is the vertical component of a
73 * recent relevant touch and the second item is the touch's time stamp. Old
74 * touches are removed based on the max tracking time and when direction
75 * changes.
76 * @type {!Array.<number>}
77 * @private
78 */
79 this.recentTouchesY_ = [];
80
81 /**
82 * Used to keep track of all events we subscribe to so we can easily clean up
83 * @type {EventTracker}
84 * @private
85 */
86 this.events_ = new EventTracker();
87 }
88
89
90 /**
91 * DOM Events that may be fired by the TouchHandler at the element
92 * All events are fired as DOM CustomEvents using TouchHandler.EventDetail
93 * to supply the details.
94 */
95 TouchHandler.EventType = {
96 // Fired whenever the element is touched as the only touch to the device.
97 // detail.enableDrag defaults to false, set to true to permit dragging.
98 TOUCH_START: 'touchhandler:touch_start',
99
100 // Fired when an element is held for a period of time. Prevents dragging
101 // from occuring (even if enableDrag was set to true).
102 LONG_PRESS: 'touchhandler:long_press',
103
104 // If enableDrag was set to true at TOUCH_START, DRAG_START will fire when the
105 // touch first moves sufficient distance. detail.enableDrag is set to true
106 // but can be reset to false to cancel the drag.
107 DRAG_START: 'touchhandler:drag_start',
108
109 // If detail.enableDrag was true after DRAG_START, DRAG_MOVE will fire
110 // whenever the touch is moved.
111 DRAG_MOVE: 'touchhandler:drag_move',
112
113 // Fired just before TOUCH_END when a drag is released. Correlates 1:1 with a
114 // DRAG_START.
115 DRAG_END: 'touchhandler:drag_end',
116
117 // Fired whenever a touch that is being tracked has been released. Correlates
118 // 1:1 with a TOUCH_START.
119 TOUCH_END: 'touchhandler:touch_end'
120 };
121
122
123 /**
124 * The type of object use for our CustomEvent detail
125 * @constructor
126 * @param {number} clientX The X location of the touch.
127 * @param {number} clientY The Y location of the touch.
128 */
129 TouchHandler.EventDetail = function(clientX, clientY) {
130 /**
131 * The X location of the touch affected
132 * @type {number}
133 */
134 this.clientX = clientX;
135
136 /**
137 * The Y location of the touch affected
138 * @type {number}
139 */
140 this.clientY = clientY;
141 };
142
143 /**
144 * For TOUCH_START and DRAG START events, set to true to enable dragging or
145 * false to disable dragging.
146 * @type {boolean|undefined}
147 */
148 TouchHandler.EventDetail.enableDrag;
149
150 /**
151 * For DRAG events, provides the horizontal component of the
152 * drag delta. Drag delta is defined as the delta of the start touch position
153 * and the current drag position.
154 * @type {number|undefined}
155 */
156 TouchHandler.EventDetail.dragDeltaX;
157
158 /**
159 * For DRAG events, provides the vertical component of the
160 * drag delta.
161 * @type {number|undefined}
162 */
163 TouchHandler.EventDetail.dragDeltaY;
164
165 /**
166 * Minimum movement of touch required to be considered a drag.
167 * @type {number}
168 * @private
169 */
170 TouchHandler.MIN_TRACKING_FOR_DRAG_ = 8;
171
172
173 /**
174 * The maximum number of ms to track a touch event. After an event is older than
175 * this value, it will be ignored in velocity calculations.
176 * @type {number}
177 * @private
178 */
179 TouchHandler.MAX_TRACKING_TIME_ = 250;
180
181
182 /**
183 * The maximum number of touches to track.
184 * @type {number}
185 * @private
186 */
187 TouchHandler.MAX_TRACKING_TOUCHES_ = 5;
188
189
190 /**
191 * The maximum velocity to return, in pixels per millisecond, that is used to
192 * guard against errors in calculating end velocity of a drag. This is a very
193 * fast drag velocity.
194 * @type {number}
195 * @private
196 */
197 TouchHandler.MAXIMUM_VELOCITY_ = 5;
198
199
200 /**
201 * The velocity to return, in pixel per millisecond, when the time stamps on the
202 * events are erroneous. The browser can return bad time stamps if the thread
203 * is blocked for the duration of the drag. This is a low velocity to prevent
204 * the content from moving quickly after a slow drag. It is less jarring if the
205 * content moves slowly after a fast drag.
206 * @type {number}
207 * @private
208 */
209 TouchHandler.VELOCITY_FOR_INCORRECT_EVENTS_ = 1;
210
211 /**
212 * The time, in milliseconds, that a touch must be held to be considered
213 * 'long'.
214 * @type {number}
215 * @private
216 */
217 TouchHandler.TIME_FOR_LONG_PRESS_ = 500;
218
219 /**
220 * If defined, the identifer of the single touch that is active. Note that 0 is
221 * a valid touch identifier - it should not be treated equivalently to
222 * undefined.
223 * @type {number|undefined}
224 * @private
225 */
226 TouchHandler.prototype.activeTouch_;
227
228
229 /**
230 * @type {boolean|undefined}
231 * @private
232 */
233 TouchHandler.prototype.tracking_;
234
235
236 /**
237 * @type {number|undefined}
238 * @private
239 */
240 TouchHandler.prototype.startTouchX_;
241
242
243 /**
244 * @type {number|undefined}
245 * @private
246 */
247 TouchHandler.prototype.startTouchY_;
248
249
250 /**
251 * @type {number|undefined}
252 * @private
253 */
254 TouchHandler.prototype.endTouchX_;
255
256
257 /**
258 * @type {number|undefined}
259 * @private
260 */
261 TouchHandler.prototype.endTouchY_;
262
263
264 /**
265 * Time of the touchstart event.
266 * @type {number|undefined}
267 * @private
268 */
269 TouchHandler.prototype.startTime_;
270
271
272 /**
273 * The time of the touchend event.
274 * @type {number|undefined}
275 * @private
276 */
277 TouchHandler.prototype.endTime_;
278
279
280 /**
281 * @type {number|undefined}
282 * @private
283 */
284 TouchHandler.prototype.lastTouchX_;
285
286
287 /**
288 * @type {number|undefined}
289 * @private
290 */
291 TouchHandler.prototype.lastTouchY_;
292
293
294 /**
295 * @type {number|undefined}
296 * @private
297 */
298 TouchHandler.prototype.lastMoveX_;
299
300
301 /**
302 * @type {number|undefined}
303 * @private
304 */
305 TouchHandler.prototype.lastMoveY_;
306
307 /**
308 * @type {number|undefined}
309 * @private
310 */
311 TouchHandler.prototype.longPressTimeout_;
312
313 /**
314 * If defined and true, the next click event should be swallowed
315 * @type {boolean|undefined}
316 * @private
317 */
318 TouchHandler.prototype.swallowNextClick_;
319
320 /**
321 * Start listenting for events.
322 * @param {boolean=} opt_capture True if the TouchHandler should listen to
323 * during the capture phase.
324 */
325 TouchHandler.prototype.enable = function(opt_capture) {
326 var capture = !!opt_capture;
327
328 // Just listen to start events for now. When a touch is occuring we'll want
329 // to be subscribed to move and end events on the document, but we don't want
330 // to incur the cost of lots of no-op handlers on the document.
331 this.events_.add(this.element_, 'touchstart', this.onStart_.bind(this),
332 capture);
333 this.events_.add(this.element_, 'mousedown',
334 this.mouseToTouchCallback_(this.onStart_.bind(this)),
335 capture);
336
337 // If the element is long-pressed, we may need to swallow a click
338 this.events_.add(this.element_, 'click', this.onClick_.bind(this), true);
339 };
340
341 /**
342 * Stop listening to all events.
343 */
344 TouchHandler.prototype.disable = function() {
345 this.stopTouching_();
346 this.events_.removeAll();
347 };
348
349 /**
350 * Wraps a callback with translations of mouse events to touch events.
351 * NOTE: These types really should be function(Event) but then we couldn't use
352 * this with bind (which operates on any type of function). Doesn't JSDoc
353 * support some sort of polymorphic types?
354 * @param {Function} callback The event callback.
355 * @return {Function} The wrapping callback.
356 * @private
357 */
358 TouchHandler.prototype.mouseToTouchCallback_ = function(callback) {
359 return function(e) {
360 // Note that there may be synthesizes mouse events caused by touch events (a
361 // mouseDown after a touch-click). We leave it up to the client to worry
362 // about this if it matters to them (typically a short mouseDown/mouseUp
363 // without a click is no big problem and it's not obvious how we identify
364 // such synthesized events in a general way).
365 var touch = {
366 // any fixed value will do for the identifier - there will only
367 // ever be a single active 'touch' when using the mouse.
368 identifier: 0,
369 clientX: e.clientX,
370 clientY: e.clientY,
371 target: e.target
372 };
373 e.touches = [];
374 e.targetTouches = [];
375 e.changedTouches = [touch];
376 if (e.type != 'mouseup') {
377 e.touches[0] = touch;
378 e.targetTouches[0] = touch;
379 }
380 callback(e);
381 };
382 };
383
384 /**
385 * Is the touch manager currently tracking touch moves to detect a drag?
386 * @return {boolean|undefined} True if currently tracking.
387 */
388 TouchHandler.prototype.isTracking = function() {
389 return this.tracking_;
390 };
391
392
393 /**
394 * Begin tracking the touchable element, it is eligible for dragging.
395 * @private
396 */
397 TouchHandler.prototype.beginTracking_ = function() {
398 this.tracking_ = true;
399 };
400
401
402 /**
403 * Stop tracking the touchable element, it is no longer dragging.
404 * @private
405 */
406 TouchHandler.prototype.endTracking_ = function() {
407 this.tracking_ = false;
408 this.dragging_ = false;
409 this.totalMoveY_ = 0;
410 this.totalMoveX_ = 0;
411 };
412
413
414 /**
415 * Reset the touchable element as if we never saw the touchStart
416 * Doesn't dispatch any end events - be careful of existing listeners.
417 */
418 TouchHandler.prototype.cancelTouch = function() {
419 this.stopTouching_();
420 this.endTracking_();
421 // If clients needed to be aware of this, we could fire a cancel event here.
422 };
423
424 /**
425 * Record that touching has stopped
426 * @private
427 */
428 TouchHandler.prototype.stopTouching_ = function() {
429 // Mark as no longer being touched
430 this.activeTouch_ = undefined;
431
432 // If we're waiting for a long press, stop
433 window.clearTimeout(this.longPressTimeout_);
434
435 // Stop listening for move/end events until there's another touch.
436 // We don't want to leave handlers piled up on the document.
437 // Note that there's no harm in removing handlers that weren't added, so
438 // rather than track whether we're using mouse or touch we do both.
439 this.events_.remove(document, 'touchmove');
440 this.events_.remove(document, 'touchend');
441 this.events_.remove(document, 'touchcancel');
442 this.events_.remove(document, 'mousemove');
443 this.events_.remove(document, 'mouseup');
444 };
445
446 /**
447 * Touch start handler.
448 * @param {!TouchEvent} e The touchstart event.
449 * @private
450 */
451 TouchHandler.prototype.onStart_ = function(e) {
452 // Only process single touches. If there is already a touch happening, or
453 // two simultaneous touches then just ignore them.
454 if (e.touches.length > 1)
455 // Note that we could cancel an active touch here. That would make
456 // simultaneous touch behave similar to near-simultaneous. However, if the
457 // user is dragging something, an accidental second touch could be quite
458 // disruptive if it cancelled their drag. Better to just ignore it.
459 return;
460
461 // It's still possible there could be an active "touch" if the user is
462 // simultaneously using a mouse and a touch input.
463 if (this.activeTouch_ !== undefined)
464 return;
465
466 var touch = e.targetTouches[0];
467 this.activeTouch_ = touch.identifier;
468
469 // We've just started touching so shouldn't swallow any upcoming click
470 if (this.swallowNextClick_)
471 this.swallowNextClick_ = false;
472
473 // Sign up for end/cancel notifications for this touch.
474 // Note that we do this on the document so that even if the user drags their
475 // finger off the element, we'll still know what they're doing.
476 if (e.type == 'mousedown') {
477 this.events_.add(document, 'mouseup',
478 this.mouseToTouchCallback_(this.onEnd_.bind(this)), false);
479 } else {
480 this.events_.add(document, 'touchend', this.onEnd_.bind(this), false);
481 this.events_.add(document, 'touchcancel', this.onEnd_.bind(this), false);
482 }
483
484 // This timeout is cleared on touchEnd and onDrag
485 // If we invoke the function then we have a real long press
486 window.clearTimeout(this.longPressTimeout_);
487 this.longPressTimeout_ = window.setTimeout(
488 this.onLongPress_.bind(this),
489 TouchHandler.TIME_FOR_LONG_PRESS_);
490
491 // Dispatch the TOUCH_START event
492 if (!this.dispatchEvent_(TouchHandler.EventType.TOUCH_START, touch))
493 // Dragging was not enabled, nothing more to do
494 return;
495
496 // We want dragging notifications
497 if (e.type == 'mousedown') {
498 this.events_.add(document, 'mousemove',
499 this.mouseToTouchCallback_(this.onMove_.bind(this)), false);
500 } else {
501 this.events_.add(document, 'touchmove', this.onMove_.bind(this), false);
502 }
503
504 this.startTouchX_ = this.lastTouchX_ = touch.clientX;
505 this.startTouchY_ = this.lastTouchY_ = touch.clientY;
506 this.startTime_ = e.timeStamp;
507
508 this.recentTouchesX_ = [];
509 this.recentTouchesY_ = [];
510 this.recentTouchesX_.push(touch.clientX, e.timeStamp);
511 this.recentTouchesY_.push(touch.clientY, e.timeStamp);
512
513 this.beginTracking_();
514 };
515
516 /**
517 * Given a list of Touches, find the one matching our activeTouch identifier.
518 * Note that Chrome currently always uses 0 as the identifier. In that case
519 * we'll end up always choosing the first element in the list.
520 * @param {TouchList} touches The list of Touch objects to search.
521 * @return {!Touch|undefined} The touch matching our active ID if any.
522 * @private
523 */
524 TouchHandler.prototype.findActiveTouch_ = function(touches) {
525 assert(this.activeTouch_ !== undefined, 'Expecting an active touch');
526 // A TouchList isn't actually an array, so we shouldn't use
527 // Array.prototype.filter/some, etc.
528 for (var i = 0; i < touches.length; i++) {
529 if (touches[i].identifier == this.activeTouch_)
530 return touches[i];
531 }
532 return undefined;
533 };
534
535 /**
536 * Touch move handler.
537 * @param {!TouchEvent} e The touchmove event.
538 * @private
539 */
540 TouchHandler.prototype.onMove_ = function(e) {
541 if (!this.tracking_)
542 return;
543
544 // Our active touch should always be in the list of touches still active
545 assert(this.findActiveTouch_(e.touches), 'Missing touchEnd');
546
547 var that = this;
548 var touch = this.findActiveTouch_(e.changedTouches);
549 if (!touch)
550 return;
551
552 var clientX = touch.clientX;
553 var clientY = touch.clientY;
554
555 var moveX = this.lastTouchX_ - clientX;
556 var moveY = this.lastTouchY_ - clientY;
557 this.totalMoveX_ += Math.abs(moveX);
558 this.totalMoveY_ += Math.abs(moveY);
559 this.lastTouchX_ = clientX;
560 this.lastTouchY_ = clientY;
561
562 if (!this.dragging_ && (this.totalMoveY_ >
563 TouchHandler.MIN_TRACKING_FOR_DRAG_ ||
564 this.totalMoveX_ >
565 TouchHandler.MIN_TRACKING_FOR_DRAG_)) {
566 // If we're waiting for a long press, stop
567 window.clearTimeout(this.longPressTimeout_);
568
569 // Dispatch the DRAG_START event and record whether dragging should be
570 // allowed or not. Note that this relies on the current value of
571 // startTouchX/Y - handlers may use the initial drag delta to determine
572 // if dragging should be permitted.
573 this.dragging_ = this.dispatchEvent_(
574 TouchHandler.EventType.DRAG_START, touch);
575
576 if (this.dragging_) {
577 // Update the start position here so that drag deltas have better values
578 // but don't touch the recent positions so that velocity calculations can
579 // still use touchstart position in the time and distance delta.
580 this.startTouchX_ = clientX;
581 this.startTouchY_ = clientY;
582 this.startTime_ = e.timeStamp;
583 } else {
584 this.endTracking_();
585 }
586 }
587
588 if (this.dragging_) {
589 this.dispatchEvent_(TouchHandler.EventType.DRAG_MOVE, touch);
590
591 this.removeTouchesInWrongDirection_(this.recentTouchesX_, this.lastMoveX_,
592 moveX);
593 this.removeTouchesInWrongDirection_(this.recentTouchesY_, this.lastMoveY_,
594 moveY);
595 this.removeOldTouches_(this.recentTouchesX_, e.timeStamp);
596 this.removeOldTouches_(this.recentTouchesY_, e.timeStamp);
597 this.recentTouchesX_.push(clientX, e.timeStamp);
598 this.recentTouchesY_.push(clientY, e.timeStamp);
599 }
600
601 this.lastMoveX_ = moveX;
602 this.lastMoveY_ = moveY;
603 };
604
605
606 /**
607 * Filters the provided recent touches array to remove all touches except the
608 * last if the move direction has changed.
609 * @param {!Array.<number>} recentTouches An array of tuples where the first
610 * item is the x or y component of the recent touch and the second item
611 * is the touch time stamp.
612 * @param {number|undefined} lastMove The x or y component of the previous
613 * move.
614 * @param {number} recentMove The x or y component of the most recent move.
615 * @private
616 */
617 TouchHandler.prototype.removeTouchesInWrongDirection_ =
618 function(recentTouches, lastMove, recentMove) {
619 if (lastMove && recentMove && recentTouches.length > 2 &&
620 (lastMove > 0 ^ recentMove > 0)) {
621 recentTouches.splice(0, recentTouches.length - 2);
622 }
623 };
624
625
626 /**
627 * Filters the provided recent touches array to remove all touches older than
628 * the max tracking time or the 5th most recent touch.
629 * @param {!Array.<number>} recentTouches An array of tuples where the first
630 * item is the x or y component of the recent touch and the second item
631 * is the touch time stamp.
632 * @param {number} recentTime The time of the most recent event.
633 * @private
634 */
635 TouchHandler.prototype.removeOldTouches_ =
636 function(recentTouches, recentTime) {
637 while (recentTouches.length && recentTime - recentTouches[1] >
638 TouchHandler.MAX_TRACKING_TIME_ ||
639 recentTouches.length >
640 TouchHandler.MAX_TRACKING_TOUCHES_ * 2) {
641 recentTouches.splice(0, 2);
642 }
643 };
644
645 /**
646 * Touch end handler.
647 * @param {!TouchEvent} e The touchend event.
648 * @private
649 */
650 TouchHandler.prototype.onEnd_ = function(e) {
651 var that = this;
652 assert(this.activeTouch_ !== undefined, 'Expect to already be touching');
653
654 // If the touch we're tracking isn't changing here, ignore this touch end.
655 var touch = this.findActiveTouch_(e.changedTouches);
656 if (!touch) {
657 // In most cases, our active touch will be in the 'touches' collection,
658 // but we can't assert that because occasionally two touchend events can
659 // occur at almost the same time with both having empty 'touches' lists.
660 // I.e., 'touches' seems like it can be a bit more up-to-date than the
661 // current event.
662 return;
663 }
664
665 // This is touchEnd for the touch we're monitoring
666 assert(!this.findActiveTouch_(e.touches),
667 'Touch ended also still active');
668
669 // Indicate that touching has finished
670 this.stopTouching_();
671
672 if (this.tracking_) {
673 var clientX = touch.clientX;
674 var clientY = touch.clientY;
675
676 if (this.dragging_) {
677 this.endTime_ = e.timeStamp;
678 this.endTouchX_ = clientX;
679 this.endTouchY_ = clientY;
680
681 this.removeOldTouches_(this.recentTouchesX_, e.timeStamp);
682 this.removeOldTouches_(this.recentTouchesY_, e.timeStamp);
683
684 this.dispatchEvent_(TouchHandler.EventType.DRAG_END, touch);
685
686 // Note that in some situations we can get a click event here as well.
687 // For now this isn't a problem, but we may want to consider having some
688 // logic that hides clicks that appear to be caused by a touchEnd used for
689 // dragging.
690 }
691
692 this.endTracking_();
693 }
694
695 // Note that we dispatch the touchEnd event last so that events at different
696 // levels of semantics nest nicely (similar to how DOM drag-and-drop events
697 // are nested inside of the mouse events that trigger them).
698 this.dispatchEvent_(TouchHandler.EventType.TOUCH_END, touch);
699 };
700
701
702 /**
703 * Get end velocity of the drag. This method is specific to drag behavior, so if
704 * touch behavior and drag behavior is split then this should go with drag
705 * behavior. End velocity is defined as deltaXY / deltaTime where deltaXY is
706 * the difference between endPosition and the oldest recent position, and
707 * deltaTime is the difference between endTime and the oldest recent time stamp.
708 * @return {Object} The x and y velocity.
709 */
710 TouchHandler.prototype.getEndVelocity = function() {
711 // Note that we could move velocity to just be an end-event parameter.
712 var velocityX = this.recentTouchesX_.length ?
713 (this.endTouchX_ - this.recentTouchesX_[0]) /
714 (this.endTime_ - this.recentTouchesX_[1]) : 0;
715 var velocityY = this.recentTouchesY_.length ?
716 (this.endTouchY_ - this.recentTouchesY_[0]) /
717 (this.endTime_ - this.recentTouchesY_[1]) : 0;
718
719 velocityX = this.correctVelocity_(velocityX);
720 velocityY = this.correctVelocity_(velocityY);
721
722 return {
723 x: velocityX,
724 y: velocityY
725 };
726 };
727
728
729 /**
730 * Correct erroneous velocities by capping the velocity if we think it's too
731 * high, or setting it to a default velocity if know that the event data is bad.
732 * @param {number} velocity The x or y velocity component.
733 * @return {number} The corrected velocity.
734 * @private
735 */
736 TouchHandler.prototype.correctVelocity_ = function(velocity) {
737 var absVelocity = Math.abs(velocity);
738
739 // We add to recent touches for each touchstart and touchmove. If we have
740 // fewer than 3 touches (6 entries), we assume that the thread was blocked for
741 // the duration of the drag and we received events in quick succession with
742 // the wrong time stamps.
743 if (absVelocity > TouchHandler.MAXIMUM_VELOCITY_) {
744 absVelocity = this.recentTouchesY_.length < 3 ?
745 TouchHandler.VELOCITY_FOR_INCORRECT_EVENTS_ :
746 TouchHandler.MAXIMUM_VELOCITY_;
747 }
748 return absVelocity * (velocity < 0 ? -1 : 1);
749 };
750
751
752 /**
753 * Handler when an element has been pressed for a long time
754 * @private
755 */
756 TouchHandler.prototype.onLongPress_ = function() {
757 // Swallow any click that occurs on this element without an intervening touch
758 // start event. This simple click-busting technique should be sufficient here
759 // since a real click should have a touchstart first.
760 this.swallowNextClick_ = true;
761
762 // Dispatch to the LONG_PRESS
763 this.dispatchEventXY_(TouchHandler.EventType.LONG_PRESS,
764 /** @type {number} */ (this.startTouchX_),
765 /** @type {number} */ (this.startTouchY_));
766 };
767
768 /**
769 * Click handler - used to swallow clicks after a long-press
770 * @param {!Event} e The click event.
771 * @private
772 */
773 TouchHandler.prototype.onClick_ = function(e) {
774 if (this.swallowNextClick_) {
775 e.preventDefault();
776 e.stopPropagation();
777 this.swallowNextClick_ = false;
778 }
779 };
780
781 /**
782 * Dispatch a TouchHandler event to the element
783 * @param {string} eventType The event to dispatch.
784 * @param {Touch} touch The touch triggering this event.
785 * @return {boolean|undefined} The value of detail.enableDrag after dispatching
786 * the event.
787 * @private
788 */
789 TouchHandler.prototype.dispatchEvent_ = function(eventType, touch) {
790 return this.dispatchEventXY_(eventType, touch.clientX, touch.clientY);
791 };
792
793 /**
794 * Dispatch a TouchHandler event to the element
795 * @param {string} eventType The event to dispatch.
796 @param {number} clientX The X location for the event.
797 @param {number} clientY The Y location for the event.
798 * @return {boolean|undefined} The value of detail.enableDrag after dispatching
799 * the event.
800 * @private
801 */
802 TouchHandler.prototype.dispatchEventXY_ =
803 function(eventType, clientX, clientY) {
804 var event = document.createEvent('CustomEvent');
805 var detail = new TouchHandler.EventDetail(clientX, clientY);
806
807 // Set enableDrag as when it can be overridden
808 if (eventType == TouchHandler.EventType.TOUCH_START) {
809 detail.enableDrag = false;
810 } else if (eventType == TouchHandler.EventType.DRAG_START) {
811 detail.enableDrag = true;
812 }
813
814 var isDrag = (eventType == TouchHandler.EventType.DRAG_START ||
815 eventType == TouchHandler.EventType.DRAG_MOVE ||
816 eventType == TouchHandler.EventType.DRAG_END);
817 if (isDrag) {
818 detail.dragDeltaX = clientX - this.startTouchX_;
819 detail.dragDeltaY = clientY - this.startTouchY_;
820 }
821
822 // Drag events don't bubble - we're really just dragging the element,
823 // not affecting its parent at all.
824 var bubbles = !isDrag;
825
826 event.initCustomEvent(eventType, bubbles, /*cancelable*/ true, detail);
827 event.sender = this;
828 this.element_.dispatchEvent(event);
829 return detail.enableDrag;
830 };
831
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698