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

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: 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;
arv (Not doing code reviews) 2011/03/10 19:25:59 move value types to prototype
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
462 // It's still possible there could be an active "touch" if the user is
463 // simultaneously using a mouse and a touch input.
464 if (this.activeTouch_ !== undefined) {
465 return;
466 }
467
468 var touch = e.targetTouches[0];
469 this.activeTouch_ = touch.identifier;
470
471 // We've just started touching so shouldn't swallow any upcoming click
472 if (this.swallowNextClick_) {
473 this.swallowNextClick_ = false;
474 }
475
476 // Sign up for end/cancel notifications for this touch.
477 // Note that we do this on the document so that even if the user drags their
478 // finger off the element, we'll still know what they're doing.
479 if (e.type == 'mousedown') {
480 this.events_.add(document, 'mouseup',
481 this.mouseToTouchCallback_(this.onEnd_.bind(this)), false);
482 } else {
483 this.events_.add(document, 'touchend', this.onEnd_.bind(this), false);
484 this.events_.add(document, 'touchcancel', this.onEnd_.bind(this), false);
485 }
486
487 // This timeout is cleared on touchEnd and onDrag
488 // If we invoke the function then we have a real long press
489 window.clearTimeout(this.longPressTimeout_);
490 this.longPressTimeout_ = window.setTimeout(
491 this.onLongPress_.bind(this),
492 TouchHandler.TIME_FOR_LONG_PRESS_);
493
494 // Dispatch the TOUCH_START event
495 if (!this.dispatchEvent_(TouchHandler.EventType.TOUCH_START, touch)) {
496 // Dragging was not enabled, nothing more to do
497 return;
498 }
499
500 // We want dragging notifications
501 if (e.type == 'mousedown') {
502 this.events_.add(document, 'mousemove',
503 this.mouseToTouchCallback_(this.onMove_.bind(this)), false);
504 } else {
505 this.events_.add(document, 'touchmove', this.onMove_.bind(this), false);
506 }
507
508 this.startTouchX_ = this.lastTouchX_ = touch.clientX;
509 this.startTouchY_ = this.lastTouchY_ = touch.clientY;
510 this.startTime_ = e.timeStamp;
511
512 this.recentTouchesX_ = [];
513 this.recentTouchesY_ = [];
514 this.recentTouchesX_.push(touch.clientX, e.timeStamp);
515 this.recentTouchesY_.push(touch.clientY, e.timeStamp);
516
517 this.beginTracking_();
518 };
519
520 /**
521 * Given a list of Touches, find the one matching our activeTouch identifier.
522 * Note that Chrome currently always uses 0 as the identifier. In that case
523 * we'll end up always choosing the first element in the list.
524 * @param {TouchList} touches The list of Touch objects to search.
525 * @return {!Touch|undefined} The touch matching our active ID if any.
526 * @private
527 */
528 TouchHandler.prototype.findActiveTouch_ = function(touches) {
529 assert(this.activeTouch_ !== undefined, 'Expecting an active touch');
530 // A TouchList isn't actually an array, so we shouldn't use
531 // Array.prototype.filter/some, etc.
532 for (var i = 0; i < touches.length; i++) {
533 if (touches[i].identifier == this.activeTouch_) {
534 return touches[i];
535 }
536 }
537 return undefined;
538 };
539
540 /**
541 * Touch move handler.
542 * @param {!TouchEvent} e The touchmove event.
543 * @private
544 */
545 TouchHandler.prototype.onMove_ = function(e) {
546 if (!this.tracking_) {
547 return;
548 }
549
550 // Our active touch should always be in the list of touches still active
551 assert(this.findActiveTouch_(e.touches), 'Missing touchEnd');
552
553 var that = this;
554 var touch = this.findActiveTouch_(e.changedTouches);
555 if (!touch) {
556 return;
557 }
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 values
585 // but don't touch the recent positions so that velocity calculations can
586 // still use touchstart position in the time and distance delta.
587 this.startTouchX_ = clientX;
588 this.startTouchY_ = clientY;
589 this.startTime_ = e.timeStamp;
590 } else {
591 this.endTracking_();
592 }
593 }
594
595 if (this.dragging_) {
596 this.dispatchEvent_(TouchHandler.EventType.DRAG_MOVE, touch);
597
598 this.removeTouchesInWrongDirection_(this.recentTouchesX_, this.lastMoveX_,
599 moveX);
600 this.removeTouchesInWrongDirection_(this.recentTouchesY_, this.lastMoveY_,
601 moveY);
602 this.removeOldTouches_(this.recentTouchesX_, e.timeStamp);
603 this.removeOldTouches_(this.recentTouchesY_, e.timeStamp);
604 this.recentTouchesX_.push(clientX, e.timeStamp);
605 this.recentTouchesY_.push(clientY, e.timeStamp);
606 }
607
608 this.lastMoveX_ = moveX;
609 this.lastMoveY_ = moveY;
610 };
611
612
613 /**
614 * Filters the provided recent touches array to remove all touches except the
615 * 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 TouchHandler.prototype.removeTouchesInWrongDirection_ =
625 function(recentTouches, lastMove, 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 /**
634 * Filters the provided recent touches array to remove all touches older than
635 * the max tracking time or the 5th most recent touch.
636 * @param {!Array.<number>} recentTouches An array of tuples where the first
637 * item is the x or y component of the recent touch and the second item
638 * is the touch time stamp.
639 * @param {number} recentTime The time of the most recent event.
640 * @private
641 */
642 TouchHandler.prototype.removeOldTouches_ =
643 function(recentTouches, recentTime) {
644 while (recentTouches.length && recentTime - recentTouches[1] >
645 TouchHandler.MAX_TRACKING_TIME_ ||
646 recentTouches.length >
647 TouchHandler.MAX_TRACKING_TOUCHES_ * 2) {
648 recentTouches.splice(0, 2);
649 }
650 };
651
652 /**
653 * Touch end handler.
654 * @param {!TouchEvent} e The touchend event.
655 * @private
656 */
657 TouchHandler.prototype.onEnd_ = function(e) {
658 var that = this;
659 assert(this.activeTouch_ !== undefined, 'Expect to already be touching');
660
661 // If the touch we're tracking isn't changing here, ignore this touch end.
662 var touch = this.findActiveTouch_(e.changedTouches);
663 if (!touch) {
664 // In most cases, our active touch will be in the 'touches' collection,
665 // but we can't assert that because occasionally two touchend events can
666 // occur at almost the same time with both having empty 'touches' lists.
667 // I.e., 'touches' seems like it can be a bit more up-to-date than the
668 // current event.
669 return;
670 }
671
672 // This is touchEnd for the touch we're monitoring
673 assert(!this.findActiveTouch_(e.touches),
674 'Touch ended also still active');
675
676 // Indicate that touching has finished
677 this.stopTouching_();
678
679 if (this.tracking_) {
680 var clientX = touch.clientX;
681 var clientY = touch.clientY;
682
683 if (this.dragging_) {
684 this.endTime_ = e.timeStamp;
685 this.endTouchX_ = clientX;
686 this.endTouchY_ = clientY;
687
688 this.removeOldTouches_(this.recentTouchesX_, e.timeStamp);
689 this.removeOldTouches_(this.recentTouchesY_, e.timeStamp);
690
691 this.dispatchEvent_(TouchHandler.EventType.DRAG_END, touch);
692
693 // Note that in some situations we can get a click event here as well.
694 // For now this isn't a problem, but we may want to consider having some
695 // logic that hides clicks that appear to be caused by a touchEnd used for
696 // dragging.
697 }
698
699 this.endTracking_();
700 }
701
702 // Note that we dispatch the touchEnd event last so that events at different
703 // levels of semantics nest nicely (similar to how DOM drag-and-drop events
704 // are nested inside of the mouse events that trigger them).
705 this.dispatchEvent_(TouchHandler.EventType.TOUCH_END, touch);
706 };
707
708
709 /**
710 * Get end velocity of the drag. This method is specific to drag behavior, so if
711 * touch behavior and drag behavior is split then this should go with drag
712 * behavior. End velocity is defined as deltaXY / deltaTime where deltaXY is
713 * the difference between endPosition and the oldest recent position, and
714 * deltaTime is the difference between endTime and the oldest recent time stamp.
715 * @return {Object} The x and y velocity.
716 */
717 TouchHandler.prototype.getEndVelocity = function() {
718 // Note that we could move velocity to just be an end-event parameter.
719 var velocityX = this.recentTouchesX_.length ?
720 (this.endTouchX_ - this.recentTouchesX_[0]) /
721 (this.endTime_ - this.recentTouchesX_[1]) : 0;
722 var velocityY = this.recentTouchesY_.length ?
723 (this.endTouchY_ - this.recentTouchesY_[0]) /
724 (this.endTime_ - this.recentTouchesY_[1]) : 0;
725
726 velocityX = this.correctVelocity_(velocityX);
727 velocityY = this.correctVelocity_(velocityY);
728
729 return {
730 x: velocityX,
731 y: velocityY
732 };
733 };
734
735
736 /**
737 * Correct erroneous velocities by capping the velocity if we think it's too
738 * high, or setting it to a default velocity if know that the event data is bad.
739 * @param {number} velocity The x or y velocity component.
740 * @return {number} The corrected velocity.
741 * @private
742 */
743 TouchHandler.prototype.correctVelocity_ = function(velocity) {
744 var absVelocity = Math.abs(velocity);
745
746 // We add to recent touches for each touchstart and touchmove. If we have
747 // fewer than 3 touches (6 entries), we assume that the thread was blocked for
748 // the duration of the drag and we received events in quick succession with
749 // the wrong time stamps.
750 if (absVelocity > TouchHandler.MAXIMUM_VELOCITY_) {
751 absVelocity = this.recentTouchesY_.length < 3 ?
752 TouchHandler.VELOCITY_FOR_INCORRECT_EVENTS_ :
753 TouchHandler.MAXIMUM_VELOCITY_;
754 }
755 return absVelocity * (velocity < 0 ? -1 : 1);
756 };
757
758
759 /**
760 * Handler when an element has been pressed for a long time
761 * @private
762 */
763 TouchHandler.prototype.onLongPress_ = function() {
764 // Swallow any click that occurs on this element without an intervening touch
765 // start event. This simple click-busting technique should be sufficient here
766 // since a real click should have a touchstart first.
767 this.swallowNextClick_ = true;
768
769 // Dispatch to the LONG_PRESS
770 this.dispatchEventXY_(TouchHandler.EventType.LONG_PRESS,
771 /** @type {number} */ (this.startTouchX_),
772 /** @type {number} */ (this.startTouchY_));
773 };
774
775 /**
776 * Click handler - used to swallow clicks after a long-press
777 * @param {!Event} e The click event.
778 * @private
779 */
780 TouchHandler.prototype.onClick_ = function(e) {
781 if (this.swallowNextClick_) {
782 e.preventDefault();
783 e.stopPropagation();
784 this.swallowNextClick_ = false;
785 }
786 };
787
788 /**
789 * Dispatch a TouchHandler event to the element
790 * @param {string} eventType The event to dispatch.
791 * @param {Touch} touch The touch triggering this event.
792 * @return {boolean|undefined} The value of detail.enableDrag after dispatching
793 * the event.
794 * @private
795 */
796 TouchHandler.prototype.dispatchEvent_ = function(eventType, touch) {
797 return this.dispatchEventXY_(eventType, touch.clientX, touch.clientY);
798 };
799
800 /**
801 * Dispatch a TouchHandler event to the element
802 * @param {string} eventType The event to dispatch.
803 @param {number} clientX The X location for the event.
804 @param {number} clientY The Y location for the event.
805 * @return {boolean|undefined} The value of detail.enableDrag after dispatching
806 * the event.
807 * @private
808 */
809 TouchHandler.prototype.dispatchEventXY_ =
810 function(eventType, clientX, clientY) {
811 var event = document.createEvent('CustomEvent');
812 var detail = new TouchHandler.EventDetail(clientX, clientY);
813
814 // Set enableDrag as when it can be overridden
815 if (eventType == TouchHandler.EventType.TOUCH_START) {
816 detail.enableDrag = false;
817 } else if (eventType == TouchHandler.EventType.DRAG_START) {
818 detail.enableDrag = true;
819 }
820
821 var isDrag = (eventType == TouchHandler.EventType.DRAG_START ||
822 eventType == TouchHandler.EventType.DRAG_MOVE ||
823 eventType == TouchHandler.EventType.DRAG_END);
824 if (isDrag) {
825 detail.dragDeltaX = clientX - this.startTouchX_;
826 detail.dragDeltaY = clientY - this.startTouchY_;
827 }
828
829 // Drag events don't bubble - we're really just dragging the element,
830 // not affecting its parent at all.
831 var bubbles = !isDrag;
832
833 event.initCustomEvent(eventType, bubbles, /*cancelable*/ true, detail);
arv (Not doing code reviews) 2011/03/10 19:25:59 var event = new cr.Event(eventType, bubbles, true)
834 event.sender = this;
835 this.element_.dispatchEvent(event);
836 return detail.enableDrag;
837 };
838
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698