OLD | NEW |
(Empty) | |
| 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"); |
| 13 // |
| 14 goog.provide('i18n.input.chrome.inputview.handler.PointerActionBundle'); |
| 15 |
| 16 goog.require('goog.Timer'); |
| 17 goog.require('goog.dom'); |
| 18 goog.require('goog.events.EventTarget'); |
| 19 goog.require('goog.events.EventType'); |
| 20 goog.require('goog.math.Coordinate'); |
| 21 goog.require('i18n.input.chrome.inputview.SwipeDirection'); |
| 22 goog.require('i18n.input.chrome.inputview.events.DragEvent'); |
| 23 goog.require('i18n.input.chrome.inputview.events.EventType'); |
| 24 goog.require('i18n.input.chrome.inputview.events.PointerEvent'); |
| 25 goog.require('i18n.input.chrome.inputview.events.SwipeEvent'); |
| 26 goog.require('i18n.input.chrome.inputview.handler.SwipeState'); |
| 27 |
| 28 |
| 29 |
| 30 goog.scope(function() { |
| 31 |
| 32 |
| 33 |
| 34 /** |
| 35 * The handler for long press. |
| 36 * |
| 37 * @param {!Node} target The target for this pointer event. |
| 38 * @param {goog.events.EventTarget=} opt_parentEventTarget The parent event |
| 39 * target. |
| 40 * @constructor |
| 41 * @extends {goog.events.EventTarget} |
| 42 */ |
| 43 i18n.input.chrome.inputview.handler.PointerActionBundle = function(target, |
| 44 opt_parentEventTarget) { |
| 45 goog.base(this); |
| 46 this.setParentEventTarget(opt_parentEventTarget || null); |
| 47 |
| 48 /** |
| 49 * The target. |
| 50 * |
| 51 * @type {i18n.input.chrome.inputview.elements.Element} |
| 52 * @private |
| 53 */ |
| 54 this.view_ = this.getView_(target); |
| 55 |
| 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(); |
| 63 }; |
| 64 goog.inherits(i18n.input.chrome.inputview.handler.PointerActionBundle, |
| 65 goog.events.EventTarget); |
| 66 var PointerActionBundle = i18n.input.chrome.inputview.handler. |
| 67 PointerActionBundle; |
| 68 |
| 69 |
| 70 /** |
| 71 * The current target after the touch point is moved. |
| 72 * |
| 73 * @type {!Node | Element} |
| 74 * @private |
| 75 */ |
| 76 PointerActionBundle.prototype.currentTarget_; |
| 77 |
| 78 |
| 79 /** |
| 80 * How many milli-seconds to evaluate a double click event. |
| 81 * |
| 82 * @type {number} |
| 83 * @private |
| 84 */ |
| 85 PointerActionBundle.DOUBLE_CLICK_INTERVAL_ = 500; |
| 86 |
| 87 |
| 88 /** |
| 89 * The timer ID. |
| 90 * |
| 91 * @type {number} |
| 92 * @private |
| 93 */ |
| 94 PointerActionBundle.prototype.longPressTimer_; |
| 95 |
| 96 |
| 97 /** |
| 98 * The minimum swipe distance. |
| 99 * |
| 100 * @type {number} |
| 101 * @private |
| 102 */ |
| 103 PointerActionBundle.MINIMUM_SWIPE_DISTANCE_ = 20; |
| 104 |
| 105 |
| 106 /** |
| 107 * The timestamp of the pointer down. |
| 108 * |
| 109 * @type {number} |
| 110 * @private |
| 111 */ |
| 112 PointerActionBundle.prototype.pointerDownTimeStamp_ = 0; |
| 113 |
| 114 |
| 115 /** |
| 116 * The timestamp of the pointer up. |
| 117 * |
| 118 * @type {number} |
| 119 * @private |
| 120 */ |
| 121 PointerActionBundle.prototype.pointerUpTimeStamp_ = 0; |
| 122 |
| 123 |
| 124 /** |
| 125 * True if it is double clicking. |
| 126 * |
| 127 * @type {boolean} |
| 128 * @private |
| 129 */ |
| 130 PointerActionBundle.prototype.isDBLClicking_ = false; |
| 131 |
| 132 |
| 133 /** |
| 134 * True if it is long pressing. |
| 135 * |
| 136 * @type {boolean} |
| 137 * @private |
| 138 */ |
| 139 PointerActionBundle.prototype.isLongPressing_ = false; |
| 140 |
| 141 |
| 142 /** |
| 143 * True if it is flickering. |
| 144 * |
| 145 * @type {boolean} |
| 146 * @private |
| 147 */ |
| 148 PointerActionBundle.prototype.isFlickering_ = false; |
| 149 |
| 150 |
| 151 /** |
| 152 * Gets the view. |
| 153 * |
| 154 * @param {Node} target . |
| 155 * @return {i18n.input.chrome.inputview.elements.Element} . |
| 156 * @private |
| 157 */ |
| 158 PointerActionBundle.prototype.getView_ = function(target) { |
| 159 if (!target) { |
| 160 return null; |
| 161 } |
| 162 var element = /** @type {!Element} */ (target); |
| 163 var view = element['view']; |
| 164 while (!view && element) { |
| 165 view = element['view']; |
| 166 element = goog.dom.getParentElement(element); |
| 167 } |
| 168 return view; |
| 169 }; |
| 170 |
| 171 |
| 172 /** |
| 173 * Handles touchmove event for one target. |
| 174 * |
| 175 * @param {!Event} touchEvent . |
| 176 */ |
| 177 PointerActionBundle.prototype.handleTouchMove = function(touchEvent) { |
| 178 var direction = 0; |
| 179 var deltaX = this.swipeState_.previousX == 0 ? 0 : (touchEvent.pageX - |
| 180 this.swipeState_.previousX); |
| 181 var deltaY = this.swipeState_.previousY == 0 ? 0 : |
| 182 (touchEvent.pageY - this.swipeState_.previousY); |
| 183 this.swipeState_.offsetX += deltaX; |
| 184 this.swipeState_.offsetY += deltaY; |
| 185 this.dispatchEvent(new i18n.input.chrome.inputview.events.DragEvent( |
| 186 this.view_, direction, /** @type {!Node} */ (touchEvent.target), |
| 187 touchEvent.pageX, touchEvent.pageY, deltaX, deltaY)); |
| 188 |
| 189 var minimumSwipeDist = PointerActionBundle. |
| 190 MINIMUM_SWIPE_DISTANCE_; |
| 191 |
| 192 if (this.swipeState_.offsetX > minimumSwipeDist) { |
| 193 direction |= i18n.input.chrome.inputview.SwipeDirection.RIGHT; |
| 194 this.swipeState_.offsetX = 0; |
| 195 } else if (this.swipeState_.offsetX < -minimumSwipeDist) { |
| 196 direction |= i18n.input.chrome.inputview.SwipeDirection.LEFT; |
| 197 this.swipeState_.offsetX = 0; |
| 198 } |
| 199 |
| 200 if (Math.abs(deltaY) > Math.abs(deltaX)) { |
| 201 if (this.swipeState_.offsetY > minimumSwipeDist) { |
| 202 direction |= i18n.input.chrome.inputview.SwipeDirection.DOWN; |
| 203 this.swipeState_.offsetY = 0; |
| 204 } else if (this.swipeState_.offsetY < -minimumSwipeDist) { |
| 205 direction |= i18n.input.chrome.inputview.SwipeDirection.UP; |
| 206 this.swipeState_.offsetY = 0; |
| 207 } |
| 208 } |
| 209 |
| 210 this.swipeState_.previousX = touchEvent.pageX; |
| 211 this.swipeState_.previousY = touchEvent.pageY; |
| 212 |
| 213 if (direction > 0) { |
| 214 // If there is any movement, cancel the longpress timer. |
| 215 goog.Timer.clear(this.longPressTimer_); |
| 216 this.dispatchEvent(new i18n.input.chrome.inputview.events.SwipeEvent( |
| 217 this.view_, direction, /** @type {!Node} */ (touchEvent.target), |
| 218 touchEvent.pageX, touchEvent.pageY)); |
| 219 var currentTargetView = this.getView_(this.currentTarget_); |
| 220 if (this.view_) { |
| 221 this.isFlickering_ = !this.isLongPressing_ && !!(this.view_.pointerConfig. |
| 222 flickerDirection & direction) && currentTargetView == this.view_; |
| 223 } |
| 224 } |
| 225 |
| 226 this.maybeSwitchTarget_(touchEvent); |
| 227 }; |
| 228 |
| 229 |
| 230 /** |
| 231 * If the target is switched to a new one, sends out a pointer_over for the new |
| 232 * target and sends out a pointer_out for the old target. |
| 233 * |
| 234 * @param {!Event | !goog.events.BrowserEvent} e . |
| 235 * @private |
| 236 */ |
| 237 PointerActionBundle.prototype.maybeSwitchTarget_ = function(e) { |
| 238 if (!this.isFlickering_) { |
| 239 var pageOffset = this.getPageOffset_(e); |
| 240 var actualTarget = document.elementFromPoint(pageOffset.x, pageOffset.y); |
| 241 var currentTargetView = this.getView_(this.currentTarget_); |
| 242 var actualTargetView = this.getView_(actualTarget); |
| 243 if (currentTargetView != actualTargetView) { |
| 244 if (currentTargetView) { |
| 245 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 246 currentTargetView, |
| 247 i18n.input.chrome.inputview.events.EventType.POINTER_OUT, |
| 248 this.currentTarget_, pageOffset.x, pageOffset.y)); |
| 249 } |
| 250 if (actualTargetView) { |
| 251 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 252 actualTargetView, |
| 253 i18n.input.chrome.inputview.events.EventType.POINTER_OVER, |
| 254 actualTarget, pageOffset.x, pageOffset.y)); |
| 255 } |
| 256 this.currentTarget_ = actualTarget; |
| 257 } |
| 258 } |
| 259 }; |
| 260 |
| 261 |
| 262 /** |
| 263 * Handles pointer up, e.g., mouseup/touchend. |
| 264 * |
| 265 * @param {!goog.events.BrowserEvent} e The event. |
| 266 */ |
| 267 PointerActionBundle.prototype.handlePointerUp = function(e) { |
| 268 goog.Timer.clear(this.longPressTimer_); |
| 269 var pageOffset = this.getPageOffset_(e); |
| 270 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 271 this.view_, i18n.input.chrome.inputview.events.EventType.LONG_PRESS_END, |
| 272 e.target, pageOffset.x, pageOffset.y)); |
| 273 if (this.isDBLClicking_) { |
| 274 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 275 this.view_, i18n.input.chrome.inputview.events.EventType. |
| 276 DOUBLE_CLICK_END, e.target, pageOffset.x, pageOffset.y)); |
| 277 } else if (!(this.isLongPressing_ && this.view_.pointerConfig. |
| 278 longPressWithoutPointerUp)) { |
| 279 if (this.isLongPressing_) { |
| 280 // If the finger didn't move but it triggers a longpress, it could cause |
| 281 // a target switch, so checks it here. |
| 282 this.maybeSwitchTarget_(e); |
| 283 } |
| 284 var view = this.getView_(this.currentTarget_); |
| 285 var target = this.currentTarget_; |
| 286 if (this.isFlickering_) { |
| 287 view = this.view_; |
| 288 target = e.target; |
| 289 } |
| 290 if (view) { |
| 291 this.pointerUpTimeStamp_ = new Date().getTime(); |
| 292 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 293 view, i18n.input.chrome.inputview.events.EventType.POINTER_UP, |
| 294 target, pageOffset.x, pageOffset.y, this.pointerUpTimeStamp_)); |
| 295 } |
| 296 } |
| 297 if (this.getView_(this.currentTarget_) == this.view_) { |
| 298 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 299 this.view_, i18n.input.chrome.inputview.events.EventType.CLICK, |
| 300 e.target, pageOffset.x, pageOffset.y)); |
| 301 } |
| 302 this.isDBLClicking_ = false; |
| 303 this.isLongPressing_ = false; |
| 304 this.isFlickering_ = false; |
| 305 this.swipeState_.reset(); |
| 306 |
| 307 e.preventDefault(); |
| 308 if (this.view_ && this.view_.pointerConfig.stopEventPropagation) { |
| 309 e.stopPropagation(); |
| 310 } |
| 311 }; |
| 312 |
| 313 |
| 314 /** |
| 315 * Cancel double click recognition on this target. |
| 316 */ |
| 317 PointerActionBundle.prototype.cancelDoubleClick = function() { |
| 318 this.pointerDownTimeStamp_ = 0; |
| 319 }; |
| 320 |
| 321 |
| 322 /** |
| 323 * Handles pointer down, e.g., mousedown/touchstart. |
| 324 * |
| 325 * @param {!goog.events.BrowserEvent} e The event. |
| 326 */ |
| 327 PointerActionBundle.prototype.handlePointerDown = function(e) { |
| 328 this.currentTarget_ = e.target; |
| 329 goog.Timer.clear(this.longPressTimer_); |
| 330 if (e.type != goog.events.EventType.MOUSEDOWN) { |
| 331 // Don't trigger long press for mouse event. |
| 332 this.maybeTriggerKeyDownLongPress_(e); |
| 333 } |
| 334 this.maybeHandleDBLClick_(e); |
| 335 if (!this.isDBLClicking_) { |
| 336 var pageOffset = this.getPageOffset_(e); |
| 337 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 338 this.view_, i18n.input.chrome.inputview.events.EventType.POINTER_DOWN, |
| 339 e.target, pageOffset.x, pageOffset.y, this.pointerDownTimeStamp_)); |
| 340 } |
| 341 |
| 342 if (this.view_ && this.view_.pointerConfig.preventDefault) { |
| 343 e.preventDefault(); |
| 344 } |
| 345 if (this.view_ && this.view_.pointerConfig.stopEventPropagation) { |
| 346 e.stopPropagation(); |
| 347 } |
| 348 }; |
| 349 |
| 350 |
| 351 /** |
| 352 * Gets the page offset from the event which may be mouse event or touch event. |
| 353 * |
| 354 * @param {!goog.events.BrowserEvent | !TouchEvent | !Event} e . |
| 355 * @return {!goog.math.Coordinate} . |
| 356 * @private |
| 357 */ |
| 358 PointerActionBundle.prototype.getPageOffset_ = function(e) { |
| 359 if (e.pageX && e.pageY) { |
| 360 return new goog.math.Coordinate(e.pageX, e.pageY); |
| 361 } |
| 362 |
| 363 if (!e.getBrowserEvent) { |
| 364 return new goog.math.Coordinate(0, 0); |
| 365 } |
| 366 |
| 367 var nativeEvt = e.getBrowserEvent(); |
| 368 if (nativeEvt.pageX && nativeEvt.pageY) { |
| 369 return new goog.math.Coordinate(nativeEvt.pageX, nativeEvt.pageY); |
| 370 } |
| 371 |
| 372 |
| 373 var touchEventList = nativeEvt['changedTouches']; |
| 374 if (!touchEventList || touchEventList.length == 0) { |
| 375 touchEventList = nativeEvt['touches']; |
| 376 } |
| 377 if (touchEventList && touchEventList.length > 0) { |
| 378 var touchEvent = touchEventList[0]; |
| 379 return new goog.math.Coordinate(touchEvent.pageX, touchEvent.pageY); |
| 380 } |
| 381 |
| 382 return new goog.math.Coordinate(0, 0); |
| 383 }; |
| 384 |
| 385 |
| 386 /** |
| 387 * Maybe triggers the long press timer when pointer down. |
| 388 * |
| 389 * @param {!goog.events.BrowserEvent} e The event. |
| 390 * @private |
| 391 */ |
| 392 PointerActionBundle.prototype.maybeTriggerKeyDownLongPress_ = function(e) { |
| 393 if (this.view_ && (this.view_.pointerConfig.longPressWithPointerUp || |
| 394 this.view_.pointerConfig.longPressWithoutPointerUp)) { |
| 395 this.longPressTimer_ = goog.Timer.callOnce( |
| 396 goog.bind(this.triggerLongPress_, this, e), |
| 397 this.view_.pointerConfig.longPressDelay, this); |
| 398 } |
| 399 }; |
| 400 |
| 401 |
| 402 /** |
| 403 * Maybe handle the double click. |
| 404 * |
| 405 * @param {!goog.events.BrowserEvent} e . |
| 406 * @private |
| 407 */ |
| 408 PointerActionBundle.prototype.maybeHandleDBLClick_ = function(e) { |
| 409 if (this.view_ && this.view_.pointerConfig.dblClick) { |
| 410 var timeInMs = new Date().getTime(); |
| 411 var interval = this.view_.pointerConfig.dblClickDelay || |
| 412 PointerActionBundle.DOUBLE_CLICK_INTERVAL_; |
| 413 var nativeEvt = e.getBrowserEvent(); |
| 414 if ((timeInMs - this.pointerDownTimeStamp_) < interval) { |
| 415 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 416 this.view_, i18n.input.chrome.inputview.events.EventType.DOUBLE_CLICK, |
| 417 e.target, nativeEvt.pageX, nativeEvt.pageY)); |
| 418 this.isDBLClicking_ = true; |
| 419 } |
| 420 this.pointerDownTimeStamp_ = timeInMs; |
| 421 } |
| 422 }; |
| 423 |
| 424 |
| 425 /** |
| 426 * Triggers long press event. |
| 427 * |
| 428 * @param {!goog.events.BrowserEvent} e The event. |
| 429 * @private |
| 430 */ |
| 431 PointerActionBundle.prototype.triggerLongPress_ = function(e) { |
| 432 var nativeEvt = e.getBrowserEvent(); |
| 433 this.dispatchEvent(new i18n.input.chrome.inputview.events.PointerEvent( |
| 434 this.view_, i18n.input.chrome.inputview.events.EventType.LONG_PRESS, |
| 435 e.target, nativeEvt.pageX, nativeEvt.pageY)); |
| 436 this.isLongPressing_ = true; |
| 437 }; |
| 438 |
| 439 |
| 440 /** @override */ |
| 441 PointerActionBundle.prototype.disposeInternal = function() { |
| 442 goog.dispose(this.longPressTimer_); |
| 443 |
| 444 goog.base(this, 'disposeInternal'); |
| 445 }; |
| 446 |
| 447 }); // goog.scope |
OLD | NEW |