vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
3.9 KiB

  1. var mousetrap = require('mousetrap');
  2. var Emitter = require('emitter-component');
  3. var Hammer = require('../module/hammer');
  4. var util = require('../util');
  5. /**
  6. * Turn an element into an clickToUse element.
  7. * When not active, the element has a transparent overlay. When the overlay is
  8. * clicked, the mode is changed to active.
  9. * When active, the element is displayed with a blue border around it, and
  10. * the interactive contents of the element can be used. When clicked outside
  11. * the element, the elements mode is changed to inactive.
  12. * @param {Element} container
  13. * @constructor
  14. */
  15. function Activator(container) {
  16. this.active = false;
  17. this.dom = {
  18. container: container
  19. };
  20. this.dom.overlay = document.createElement('div');
  21. this.dom.overlay.className = 'overlay';
  22. this.dom.container.appendChild(this.dom.overlay);
  23. this.hammer = Hammer(this.dom.overlay, {prevent_default: false});
  24. this.hammer.on('tap', this._onTapOverlay.bind(this));
  25. // block all touch events (except tap)
  26. var me = this;
  27. var events = [
  28. 'touch', 'pinch',
  29. 'doubletap', 'hold',
  30. 'dragstart', 'drag', 'dragend',
  31. 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
  32. ];
  33. events.forEach(function (event) {
  34. me.hammer.on(event, function (event) {
  35. event.stopPropagation();
  36. });
  37. });
  38. // attach a tap event to the window, in order to deactivate when clicking outside the timeline
  39. this.windowHammer = Hammer(window, {prevent_default: false});
  40. this.windowHammer.on('tap', function (event) {
  41. // deactivate when clicked outside the container
  42. if (!_hasParent(event.target, container)) {
  43. me.deactivate();
  44. }
  45. });
  46. // mousetrap listener only bounded when active)
  47. this.escListener = this.deactivate.bind(this);
  48. }
  49. // turn into an event emitter
  50. Emitter(Activator.prototype);
  51. // The currently active activator
  52. Activator.current = null;
  53. /**
  54. * Destroy the activator. Cleans up all created DOM and event listeners
  55. */
  56. Activator.prototype.destroy = function () {
  57. this.deactivate();
  58. // remove dom
  59. this.dom.overlay.parentNode.removeChild(this.dom.overlay);
  60. // cleanup hammer instances
  61. this.hammer = null;
  62. this.windowHammer = null;
  63. // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory)
  64. };
  65. /**
  66. * Activate the element
  67. * Overlay is hidden, element is decorated with a blue shadow border
  68. */
  69. Activator.prototype.activate = function () {
  70. // we allow only one active activator at a time
  71. if (Activator.current) {
  72. Activator.current.deactivate();
  73. }
  74. Activator.current = this;
  75. this.active = true;
  76. this.dom.overlay.style.display = 'none';
  77. util.addClassName(this.dom.container, 'vis-active');
  78. this.emit('change');
  79. this.emit('activate');
  80. // ugly hack: bind ESC after emitting the events, as the Network rebinds all
  81. // keyboard events on a 'change' event
  82. mousetrap.bind('esc', this.escListener);
  83. };
  84. /**
  85. * Deactivate the element
  86. * Overlay is displayed on top of the element
  87. */
  88. Activator.prototype.deactivate = function () {
  89. this.active = false;
  90. this.dom.overlay.style.display = '';
  91. util.removeClassName(this.dom.container, 'vis-active');
  92. mousetrap.unbind('esc', this.escListener);
  93. this.emit('change');
  94. this.emit('deactivate');
  95. };
  96. /**
  97. * Handle a tap event: activate the container
  98. * @param event
  99. * @private
  100. */
  101. Activator.prototype._onTapOverlay = function (event) {
  102. // activate the container
  103. this.activate();
  104. event.stopPropagation();
  105. };
  106. /**
  107. * Test whether the element has the requested parent element somewhere in
  108. * its chain of parent nodes.
  109. * @param {HTMLElement} element
  110. * @param {HTMLElement} parent
  111. * @returns {boolean} Returns true when the parent is found somewhere in the
  112. * chain of parent nodes.
  113. * @private
  114. */
  115. function _hasParent(element, parent) {
  116. while (element) {
  117. if (element === parent) {
  118. return true
  119. }
  120. element = element.parentNode;
  121. }
  122. return false;
  123. }
  124. module.exports = Activator;