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

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

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

Powered by Google App Engine
This is Rietveld 408576698