UNPKG

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