Update mojo sdk to rev 1dc8a9a5db73d3718d99917fadf31f5fb2ebad4f
[chromium-blink-merge.git] / third_party / google_input_tools / src / chrome / os / inputview / handler / pointeractionbundle.js
blobe2e3adde1d3dbc2b6bf7904437d209920309d660
1 // Copyright 2014 The ChromeOS IME Authors. All Rights Reserved.
2 // limitations under the License.
3 // See the License for the specific language governing permissions and
4 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5 // distributed under the License is distributed on an "AS-IS" BASIS,
6 // Unless required by applicable law or agreed to in writing, software
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // You may obtain a copy of the License at
11 // you may not use this file except in compliance with the License.
12 // Licensed under the Apache License, Version 2.0 (the "License");
14 goog.provide('i18n.input.chrome.inputview.handler.PointerActionBundle');
16 goog.require('goog.Timer');
17 goog.require('goog.events.EventTarget');
18 goog.require('goog.events.EventType');
19 goog.require('goog.math.Coordinate');
20 goog.require('i18n.input.chrome.inputview.SwipeDirection');
21 goog.require('i18n.input.chrome.inputview.events.DragEvent');
22 goog.require('i18n.input.chrome.inputview.events.EventType');
23 goog.require('i18n.input.chrome.inputview.events.PointerEvent');
24 goog.require('i18n.input.chrome.inputview.events.SwipeEvent');
25 goog.require('i18n.input.chrome.inputview.handler.SwipeState');
26 goog.require('i18n.input.chrome.inputview.handler.Util');
30 goog.scope(function() {
34 /**
35  * The handler for long press.
36  *
37  * @param {!i18n.input.chrome.inputview.elements.Element} view The view for this
38  *     pointer event.
39  * @param {goog.events.EventTarget=} opt_parentEventTarget The parent event
40  *     target.
41  * @constructor
42  * @extends {goog.events.EventTarget}
43  */
44 i18n.input.chrome.inputview.handler.PointerActionBundle = function(view,
45     opt_parentEventTarget) {
46   goog.base(this);
47   this.setParentEventTarget(opt_parentEventTarget || null);
49   /**
50    * The target.
51    *
52    * @type {!i18n.input.chrome.inputview.elements.Element}
53    */
54   this.view = view;
56   /**
57    * The swipe offset.
58    *
59    * @type {!i18n.input.chrome.inputview.handler.SwipeState}
60    * @private
61    */
62   this.swipeState_ = new i18n.input.chrome.inputview.handler.SwipeState();
64 goog.inherits(i18n.input.chrome.inputview.handler.PointerActionBundle,
65     goog.events.EventTarget);
66 var PointerActionBundle = i18n.input.chrome.inputview.handler.
67     PointerActionBundle;
68 var Util = i18n.input.chrome.inputview.handler.Util;
71 /**
72  * The current target after the touch point is moved.
73  *
74  * @type {!Node | Element}
75  * @private
76  */
77 PointerActionBundle.prototype.currentTarget_;
80 /**
81  * How many milli-seconds to evaluate a double click event.
82  *
83  * @type {number}
84  * @private
85  */
86 PointerActionBundle.DOUBLE_CLICK_INTERVAL_ = 500;
89 /**
90  * The timer ID.
91  *
92  * @type {number}
93  * @private
94  */
95 PointerActionBundle.prototype.longPressTimer_;
98 /**
99  * The minimum swipe distance.
101  * @type {number}
102  * @private
103  */
104 PointerActionBundle.MINIMUM_SWIPE_DISTANCE_ = 20;
108  * The timestamp of the pointer down.
110  * @type {number}
111  * @private
112  */
113 PointerActionBundle.prototype.pointerDownTimeStamp_ = 0;
117  * The timestamp of the pointer up.
119  * @type {number}
120  * @private
121  */
122 PointerActionBundle.prototype.pointerUpTimeStamp_ = 0;
126  * True if it is double clicking.
128  * @type {boolean}
129  * @private
130  */
131 PointerActionBundle.prototype.isDBLClicking_ = false;
135  * True if it is long pressing.
137  * @type {boolean}
138  * @private
139  */
140 PointerActionBundle.prototype.isLongPressing_ = false;
144  * True if it is flickering.
146  * @type {boolean}
147  * @private
148  */
149 PointerActionBundle.prototype.isFlickering_ = false;
153  * Handles touchmove event for one target.
155  * @param {!Touch | !Event} e .
156  */
157 PointerActionBundle.prototype.handlePointerMove = function(e) {
158   var identifier = Util.getEventIdentifier(e);
159   var direction = 0;
160   var deltaX = this.swipeState_.previousX == 0 ? 0 : (e.pageX -
161       this.swipeState_.previousX);
162   var deltaY = this.swipeState_.previousY == 0 ? 0 :
163       (e.pageY - this.swipeState_.previousY);
164   this.swipeState_.offsetX += deltaX;
165   this.swipeState_.offsetY += deltaY;
166   this.dispatchEvent(new i18n.input.chrome.inputview.events.DragEvent(
167       this.view, direction, /** @type {!Node} */ (e.target),
168       e.pageX, e.pageY, deltaX, deltaY, identifier));
170   var minimumSwipeDist = PointerActionBundle.
171       MINIMUM_SWIPE_DISTANCE_;
173   if (this.swipeState_.offsetX > minimumSwipeDist) {
174     direction |= i18n.input.chrome.inputview.SwipeDirection.RIGHT;
175     this.swipeState_.offsetX = 0;
176   } else if (this.swipeState_.offsetX < -minimumSwipeDist) {
177     direction |= i18n.input.chrome.inputview.SwipeDirection.LEFT;
178     this.swipeState_.offsetX = 0;
179   }
181   if (Math.abs(deltaY) > Math.abs(deltaX)) {
182     if (this.swipeState_.offsetY > minimumSwipeDist) {
183       direction |= i18n.input.chrome.inputview.SwipeDirection.DOWN;
184       this.swipeState_.offsetY = 0;
185     } else if (this.swipeState_.offsetY < -minimumSwipeDist) {
186       direction |= i18n.input.chrome.inputview.SwipeDirection.UP;
187       this.swipeState_.offsetY = 0;
188     }
189   }
191   this.swipeState_.previousX = e.pageX;
192   this.swipeState_.previousY = e.pageY;
194   if (direction > 0) {
195     // If there is any movement, cancel the longpress timer.
196     goog.Timer.clear(this.longPressTimer_);
197     this.dispatchEvent(new i18n.input.chrome.inputview.events.SwipeEvent(
198         this.view, direction, /** @type {!Node} */ (e.target),
199         e.pageX, e.pageY, identifier));
200     var currentTargetView = Util.getView(this.currentTarget_);
201     this.isFlickering_ = !this.isLongPressing_ && !!(this.view.pointerConfig.
202         flickerDirection & direction) && currentTargetView == this.view;
203   }
205   this.maybeSwitchTarget_(
206       new goog.math.Coordinate(e.pageX, e.pageY), identifier);
211  * If the target is switched to a new one, sends out a pointer_over for the new
212  * target and sends out a pointer_out for the old target.
214  * @param {!goog.math.Coordinate} pageOffset .
215  * @param {number} identifier .
216  * @private
217  */
218 PointerActionBundle.prototype.maybeSwitchTarget_ = function(pageOffset,
219     identifier) {
220   if (!this.isFlickering_) {
221     var actualTarget = document.elementFromPoint(pageOffset.x, pageOffset.y);
222     var currentTargetView = Util.getView(this.currentTarget_);
223     var actualTargetView = Util.getView(actualTarget);
224     if (currentTargetView != actualTargetView) {
225       if (currentTargetView) {
226         this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
227             currentTargetView,
228             i18n.input.chrome.inputview.events.EventType.POINTER_OUT,
229             this.currentTarget_, pageOffset.x, pageOffset.y, identifier));
230       }
231       if (actualTargetView) {
232         this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
233             actualTargetView,
234             i18n.input.chrome.inputview.events.EventType.POINTER_OVER,
235             actualTarget, pageOffset.x, pageOffset.y, identifier));
236       }
237       this.currentTarget_ = actualTarget;
238     }
239   }
244  * Handles pointer up, e.g., mouseup/touchend.
246  * @param {!goog.events.BrowserEvent} e The event.
247  */
248 PointerActionBundle.prototype.handlePointerUp = function(e) {
249   goog.Timer.clear(this.longPressTimer_);
250   var pageOffset = this.getPageOffset_(e);
251   var identifier = Util.getEventIdentifier(e);
252   this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
253       this.view, i18n.input.chrome.inputview.events.EventType.LONG_PRESS_END,
254       e.target, pageOffset.x, pageOffset.y, identifier));
255   if (this.isDBLClicking_) {
256     this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
257         this.view, i18n.input.chrome.inputview.events.EventType.
258         DOUBLE_CLICK_END, e.target, pageOffset.x, pageOffset.y, identifier));
259   } else if (!(this.isLongPressing_ && this.view.pointerConfig.
260       longPressWithoutPointerUp)) {
261     this.maybeSwitchTarget_(pageOffset, identifier);
262     var view = Util.getView(this.currentTarget_);
263     var target = this.currentTarget_;
264     if (this.isFlickering_) {
265       view = this.view;
266       target = e.target;
267     }
268     this.pointerUpTimeStamp_ = new Date().getTime();
269     // Note |view| can be null if the finger moves outside of keyboard window
270     // area. This is possible when user try to select an accent character
271     // which is displayed outside of keyboard window. We need to dispatch a
272     // POINTER_UP event to keyboard to commit the selected accent character.
273     this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
274         view, i18n.input.chrome.inputview.events.EventType.POINTER_UP,
275         target, pageOffset.x, pageOffset.y, identifier,
276         this.pointerUpTimeStamp_));
277   }
278   if (Util.getView(this.currentTarget_) == this.view) {
279     this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
280         this.view, i18n.input.chrome.inputview.events.EventType.CLICK,
281         e.target, pageOffset.x, pageOffset.y, identifier));
282   }
283   this.isDBLClicking_ = false;
284   this.isLongPressing_ = false;
285   this.isFlickering_ = false;
286   this.swipeState_.reset();
291  * Cancel double click recognition on this target.
292  */
293 PointerActionBundle.prototype.cancelDoubleClick = function() {
294   this.pointerDownTimeStamp_ = 0;
299  * Handles pointer down, e.g., mousedown/touchstart.
301  * @param {!goog.events.BrowserEvent} e The event.
302  */
303 PointerActionBundle.prototype.handlePointerDown = function(e) {
304   this.currentTarget_ = e.target;
305   goog.Timer.clear(this.longPressTimer_);
306   var identifier = Util.getEventIdentifier(e);
307   if (e.type == goog.events.EventType.TOUCHSTART) {
308     this.maybeTriggerKeyDownLongPress_(e, identifier);
309   }
310   this.maybeHandleDBLClick_(e, identifier);
311   if (!this.isDBLClicking_) {
312     var pageOffset = this.getPageOffset_(e);
313     this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
314         this.view, i18n.input.chrome.inputview.events.EventType.POINTER_DOWN,
315         e.target, pageOffset.x, pageOffset.y, identifier,
316         this.pointerDownTimeStamp_));
317   }
322  * Gets the page offset from the event which may be mouse event or touch event.
324  * @param {!goog.events.BrowserEvent} e .
325  * @return {!goog.math.Coordinate} .
326  * @private
327  */
328 PointerActionBundle.prototype.getPageOffset_ = function(e) {
329   var nativeEvt = e.getBrowserEvent();
330   if (nativeEvt.pageX && nativeEvt.pageY) {
331     return new goog.math.Coordinate(nativeEvt.pageX, nativeEvt.pageY);
332   }
335   var touchEventList = nativeEvt['changedTouches'];
336   if (!touchEventList || touchEventList.length == 0) {
337     touchEventList = nativeEvt['touches'];
338   }
339   if (touchEventList && touchEventList.length > 0) {
340     var touchEvent = touchEventList[0];
341     return new goog.math.Coordinate(touchEvent.pageX, touchEvent.pageY);
342   }
344   return new goog.math.Coordinate(0, 0);
349  * Maybe triggers the long press timer when pointer down.
351  * @param {!goog.events.BrowserEvent} e The event.
352  * @param {number} identifier .
353  * @private
354  */
355 PointerActionBundle.prototype.maybeTriggerKeyDownLongPress_ = function(e,
356     identifier) {
357   if (this.view && (this.view.pointerConfig.longPressWithPointerUp ||
358       this.view.pointerConfig.longPressWithoutPointerUp)) {
359     this.longPressTimer_ = goog.Timer.callOnce(
360         goog.bind(this.triggerLongPress_, this, e, identifier),
361         this.view.pointerConfig.longPressDelay, this);
362   }
367  * Maybe handle the double click.
369  * @param {!goog.events.BrowserEvent} e .
370  * @param {number} identifier .
371  * @private
372  */
373 PointerActionBundle.prototype.maybeHandleDBLClick_ = function(e, identifier) {
374   if (this.view && this.view.pointerConfig.dblClick) {
375     var timeInMs = new Date().getTime();
376     var interval = this.view.pointerConfig.dblClickDelay ||
377         PointerActionBundle.DOUBLE_CLICK_INTERVAL_;
378     var nativeEvt = e.getBrowserEvent();
379     if ((timeInMs - this.pointerDownTimeStamp_) < interval) {
380       this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
381           this.view, i18n.input.chrome.inputview.events.EventType.DOUBLE_CLICK,
382           e.target, nativeEvt.pageX, nativeEvt.pageY, identifier));
383       this.isDBLClicking_ = true;
384     }
385     this.pointerDownTimeStamp_ = timeInMs;
386   }
391  * Triggers long press event.
393  * @param {!goog.events.BrowserEvent} e The event.
394  * @param {number} identifier .
395  * @private
396  */
397 PointerActionBundle.prototype.triggerLongPress_ = function(e, identifier) {
398   var nativeEvt = e.getBrowserEvent();
399   if (nativeEvt.touches.length > 1) {
400     return;
401   }
402   this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent(
403       this.view, i18n.input.chrome.inputview.events.EventType.LONG_PRESS,
404       e.target, nativeEvt.pageX, nativeEvt.pageY, identifier));
405   this.isLongPressing_ = true;
409 /** @override */
410 PointerActionBundle.prototype.disposeInternal = function() {
411   goog.dispose(this.longPressTimer_);
413   goog.base(this, 'disposeInternal');
416 });  // goog.scope