|
26 | 26 | // Disabling "mouse" handlers for 2500ms is enough
|
27 | 27 | var MOUSE_TIMEOUT = 2500;
|
28 | 28 | var MOUSE_EVENTS = ['mousedown', 'mousemove', 'mouseup', 'click'];
|
| 29 | + // an array of bitmask values for mapping MouseEvent.which to MouseEvent.buttons |
| 30 | + var MOUSE_WHICH_TO_BUTTONS = [0, 1, 4, 2]; |
| 31 | + var MOUSE_HAS_BUTTONS = (function() { |
| 32 | + try { |
| 33 | + return new MouseEvent('test', {buttons: 1}).buttons === 1; |
| 34 | + } catch (e) { |
| 35 | + return false; |
| 36 | + } |
| 37 | + })(); |
29 | 38 |
|
30 | 39 | // Check for touch-only devices
|
31 | 40 | var IS_TOUCH_ONLY = navigator.userAgent.match(/iP(?:[oa]d|hone)|Android/);
|
|
78 | 87 | Polymer.Debounce(POINTERSTATE.mouse.mouseIgnoreJob, unset, MOUSE_TIMEOUT);
|
79 | 88 | }
|
80 | 89 |
|
| 90 | + function hasLeftMouseButton(ev) { |
| 91 | + var type = ev.type; |
| 92 | + // exit early if the event is not a mouse event |
| 93 | + if (MOUSE_EVENTS.indexOf(type) === -1) { |
| 94 | + return false; |
| 95 | + } |
| 96 | + // ev.button is not reliable for mousemove (0 is overloaded as both left button and no buttons) |
| 97 | + // instead we use ev.buttons (bitmask of buttons) or fall back to ev.which (deprecated, 0 for no buttons, 1 for left button) |
| 98 | + if (type === 'mousemove') { |
| 99 | + // allow undefined for testing events |
| 100 | + var buttons = ev.buttons === undefined ? 1 : ev.buttons; |
| 101 | + if ((ev instanceof window.MouseEvent) && !MOUSE_HAS_BUTTONS) { |
| 102 | + buttons = MOUSE_WHICH_TO_BUTTONS[ev.which] || 0; |
| 103 | + } |
| 104 | + // buttons is a bitmask, check that the left button bit is set (1) |
| 105 | + return Boolean(buttons & 1); |
| 106 | + } else { |
| 107 | + // allow undefined for testing events |
| 108 | + var button = ev.button === undefined ? 0 : ev.button; |
| 109 | + // ev.button is 0 in mousedown/mouseup/click for left button activation |
| 110 | + return button === 0; |
| 111 | + } |
| 112 | + } |
| 113 | + |
81 | 114 | var POINTERSTATE = {
|
82 | 115 | mouse: {
|
83 | 116 | target: null,
|
|
336 | 369 | Gestures.register({
|
337 | 370 | name: 'downup',
|
338 | 371 | deps: ['mousedown', 'touchstart', 'touchend'],
|
| 372 | + flow: { |
| 373 | + start: ['mousedown', 'touchstart'], |
| 374 | + end: ['mouseup', 'touchend'] |
| 375 | + }, |
339 | 376 | emits: ['down', 'up'],
|
340 | 377 |
|
| 378 | + info: { |
| 379 | + movefn: function(){}, |
| 380 | + upfn: function(){} |
| 381 | + }, |
| 382 | + |
| 383 | + reset: function() { |
| 384 | + this.untrackDocument(); |
| 385 | + }, |
| 386 | + |
| 387 | + trackDocument: function(movefn, upfn) { |
| 388 | + this.info.movefn = movefn; |
| 389 | + this.info.upfn = upfn; |
| 390 | + document.addEventListener('mousemove', movefn); |
| 391 | + document.addEventListener('mouseup', upfn); |
| 392 | + }, |
| 393 | + |
| 394 | + untrackDocument: function() { |
| 395 | + document.removeEventListener('mousemove', this.info.movefn); |
| 396 | + document.removeEventListener('mouseup', this.info.upfn); |
| 397 | + }, |
| 398 | + |
341 | 399 | mousedown: function(e) {
|
| 400 | + if (!hasLeftMouseButton(e)) { |
| 401 | + return; |
| 402 | + } |
342 | 403 | var t = Gestures.findOriginalTarget(e);
|
343 | 404 | var self = this;
|
| 405 | + var movefn = function movefn(e) { |
| 406 | + if (!hasLeftMouseButton(e)) { |
| 407 | + self.fire('up', t, e); |
| 408 | + self.untrackDocument(); |
| 409 | + } |
| 410 | + }; |
344 | 411 | var upfn = function upfn(e) {
|
345 |
| - self.fire('up', t, e); |
346 |
| - document.removeEventListener('mouseup', upfn); |
| 412 | + if (hasLeftMouseButton(e)) { |
| 413 | + self.fire('up', t, e); |
| 414 | + } |
| 415 | + self.untrackDocument(); |
347 | 416 | };
|
348 |
| - document.addEventListener('mouseup', upfn); |
| 417 | + this.trackDocument(movefn, upfn); |
349 | 418 | this.fire('down', t, e);
|
350 | 419 | },
|
351 | 420 | touchstart: function(e) {
|
|
387 | 456 | }
|
388 | 457 | this.moves.push(move);
|
389 | 458 | },
|
| 459 | + movefn: function(){}, |
| 460 | + upfn: function(){}, |
390 | 461 | prevent: false
|
391 | 462 | },
|
392 | 463 |
|
| 464 | + trackDocument: function(movefn, upfn) { |
| 465 | + this.info.movefn = movefn; |
| 466 | + this.info.upfn = upfn; |
| 467 | + document.addEventListener('mousemove', movefn); |
| 468 | + document.addEventListener('mouseup', upfn); |
| 469 | + }, |
| 470 | + |
| 471 | + untrackDocument: function() { |
| 472 | + document.removeEventListener('mousemove', this.info.movefn); |
| 473 | + document.removeEventListener('mouseup', this.info.upfn); |
| 474 | + }, |
| 475 | + |
393 | 476 | reset: function() {
|
394 | 477 | this.info.state = 'start';
|
395 | 478 | this.info.started = false;
|
396 | 479 | this.info.moves = [];
|
397 | 480 | this.info.x = 0;
|
398 | 481 | this.info.y = 0;
|
399 | 482 | this.info.prevent = false;
|
| 483 | + this.untrackDocument(); |
400 | 484 | },
|
401 | 485 |
|
402 | 486 | hasMovedEnough: function(x, y) {
|
|
412 | 496 | },
|
413 | 497 |
|
414 | 498 | mousedown: function(e) {
|
| 499 | + if (!hasLeftMouseButton(e)) { |
| 500 | + return; |
| 501 | + } |
415 | 502 | var t = Gestures.findOriginalTarget(e);
|
416 | 503 | var self = this;
|
417 | 504 | var movefn = function movefn(e) {
|
|
420 | 507 | // first move is 'start', subsequent moves are 'move', mouseup is 'end'
|
421 | 508 | self.info.state = self.info.started ? (e.type === 'mouseup' ? 'end' : 'track') : 'start';
|
422 | 509 | self.info.addMove({x: x, y: y});
|
| 510 | + if (!hasLeftMouseButton(e)) { |
| 511 | + // always fire "end" |
| 512 | + self.info.state = 'end'; |
| 513 | + self.untrackDocument(); |
| 514 | + } |
423 | 515 | self.fire(t, e);
|
424 | 516 | self.info.started = true;
|
425 | 517 | }
|
|
429 | 521 | Gestures.prevent('tap');
|
430 | 522 | movefn(e);
|
431 | 523 | }
|
| 524 | + |
432 | 525 | // remove the temporary listeners
|
433 |
| - document.removeEventListener('mousemove', movefn); |
434 |
| - document.removeEventListener('mouseup', upfn); |
| 526 | + self.untrackDocument(); |
435 | 527 | };
|
436 | 528 | // add temporary document listeners as mouse retargets
|
437 |
| - document.addEventListener('mousemove', movefn); |
438 |
| - document.addEventListener('mouseup', upfn); |
| 529 | + this.trackDocument(movefn, upfn); |
439 | 530 | this.info.x = e.clientX;
|
440 | 531 | this.info.y = e.clientY;
|
441 | 532 | },
|
|
523 | 614 | },
|
524 | 615 |
|
525 | 616 | mousedown: function(e) {
|
526 |
| - this.save(e); |
| 617 | + if (hasLeftMouseButton(e)) { |
| 618 | + this.save(e); |
| 619 | + } |
527 | 620 | },
|
528 | 621 | click: function(e) {
|
529 |
| - this.forward(e); |
| 622 | + if (hasLeftMouseButton(e)) { |
| 623 | + this.forward(e); |
| 624 | + } |
530 | 625 | },
|
531 | 626 |
|
532 | 627 | touchstart: function(e) {
|
|
0 commit comments