HoverFix.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import {
  2. closest as domClosest
  3. } from 'min-dom';
  4. import {
  5. toPoint
  6. } from '../../util/Event';
  7. var HIGH_PRIORITY = 1500;
  8. /**
  9. * Browsers may swallow certain events (hover, out ...) if users are to
  10. * fast with the mouse.
  11. *
  12. * @see http://stackoverflow.com/questions/7448468/why-cant-i-reliably-capture-a-mouseout-event
  13. *
  14. * The fix implemented in this component ensure that we
  15. *
  16. * 1) have a hover state after a successful drag.move event
  17. * 2) have an out event when dragging leaves an element
  18. *
  19. * @param {ElementRegistry} elementRegistry
  20. * @param {EventBus} eventBus
  21. * @param {Injector} injector
  22. */
  23. export default function HoverFix(elementRegistry, eventBus, injector) {
  24. var self = this;
  25. var dragging = injector.get('dragging', false);
  26. /**
  27. * Make sure we are god damn hovering!
  28. *
  29. * @param {Event} dragging event
  30. */
  31. function ensureHover(event) {
  32. if (event.hover) {
  33. return;
  34. }
  35. var originalEvent = event.originalEvent;
  36. var gfx = self._findTargetGfx(originalEvent);
  37. var element = gfx && elementRegistry.get(gfx);
  38. if (gfx && element) {
  39. // 1) cancel current mousemove
  40. event.stopPropagation();
  41. // 2) emit fake hover for new target
  42. dragging.hover({ element: element, gfx: gfx });
  43. // 3) re-trigger move event
  44. dragging.move(originalEvent);
  45. }
  46. }
  47. if (dragging) {
  48. /**
  49. * We wait for a specific sequence of events before
  50. * emitting a fake drag.hover event.
  51. *
  52. * Event Sequence:
  53. *
  54. * drag.start
  55. * drag.move >> ensure we are hovering
  56. */
  57. eventBus.on('drag.start', function(event) {
  58. eventBus.once('drag.move', HIGH_PRIORITY, function(event) {
  59. ensureHover(event);
  60. });
  61. });
  62. }
  63. /**
  64. * We make sure that element.out is always fired, even if the
  65. * browser swallows an element.out event.
  66. *
  67. * Event sequence:
  68. *
  69. * element.hover
  70. * (element.out >> sometimes swallowed)
  71. * element.hover >> ensure we fired element.out
  72. */
  73. (function() {
  74. var hoverGfx;
  75. var hover;
  76. eventBus.on('element.hover', function(event) {
  77. // (1) remember current hover element
  78. hoverGfx = event.gfx;
  79. hover = event.element;
  80. });
  81. eventBus.on('element.hover', HIGH_PRIORITY, function(event) {
  82. // (3) am I on an element still?
  83. if (hover) {
  84. // (4) that is a problem, gotta "simulate the out"
  85. eventBus.fire('element.out', {
  86. element: hover,
  87. gfx: hoverGfx
  88. });
  89. }
  90. });
  91. eventBus.on('element.out', function() {
  92. // (2) unset hover state if we correctly outed us *GG*
  93. hoverGfx = null;
  94. hover = null;
  95. });
  96. })();
  97. this._findTargetGfx = function(event) {
  98. var position,
  99. target;
  100. if (!(event instanceof MouseEvent)) {
  101. return;
  102. }
  103. position = toPoint(event);
  104. // damn expensive operation, ouch!
  105. target = document.elementFromPoint(position.x, position.y);
  106. return getGfx(target);
  107. };
  108. }
  109. HoverFix.$inject = [
  110. 'elementRegistry',
  111. 'eventBus',
  112. 'injector'
  113. ];
  114. // helpers /////////////////////
  115. function getGfx(target) {
  116. return domClosest(target, 'svg, .djs-element', true);
  117. }