|
|
- var mousetrap = require('mousetrap');
- var Emitter = require('emitter-component');
- var Hammer = require('../module/hammer');
- var util = require('../util');
-
- /**
- * Turn an element into an clickToUse element.
- * When not active, the element has a transparent overlay. When the overlay is
- * clicked, the mode is changed to active.
- * When active, the element is displayed with a blue border around it, and
- * the interactive contents of the element can be used. When clicked outside
- * the element, the elements mode is changed to inactive.
- * @param {Element} container
- * @constructor
- */
- function Activator(container) {
- this.active = false;
-
- this.dom = {
- container: container
- };
-
- this.dom.overlay = document.createElement('div');
- this.dom.overlay.className = 'overlay';
-
- this.dom.container.appendChild(this.dom.overlay);
-
- this.hammer = Hammer(this.dom.overlay, {prevent_default: false});
- this.hammer.on('tap', this._onTapOverlay.bind(this));
-
- // block all touch events (except tap)
- var me = this;
- var events = [
- 'touch', 'pinch',
- 'doubletap', 'hold',
- 'dragstart', 'drag', 'dragend',
- 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
- ];
- events.forEach(function (event) {
- me.hammer.on(event, function (event) {
- event.stopPropagation();
- });
- });
-
- // attach a tap event to the window, in order to deactivate when clicking outside the timeline
- this.windowHammer = Hammer(window, {prevent_default: false});
- this.windowHammer.on('tap', function (event) {
- // deactivate when clicked outside the container
- if (!_hasParent(event.target, container)) {
- me.deactivate();
- }
- });
-
- // mousetrap listener only bounded when active)
- this.escListener = this.deactivate.bind(this);
- }
-
- // turn into an event emitter
- Emitter(Activator.prototype);
-
- // The currently active activator
- Activator.current = null;
-
- /**
- * Destroy the activator. Cleans up all created DOM and event listeners
- */
- Activator.prototype.destroy = function () {
- this.deactivate();
-
- // remove dom
- this.dom.overlay.parentNode.removeChild(this.dom.overlay);
-
- // cleanup hammer instances
- this.hammer = null;
- this.windowHammer = null;
- // FIXME: cleaning up hammer instances doesn't work (Timeline not removed from memory)
- };
-
- /**
- * Activate the element
- * Overlay is hidden, element is decorated with a blue shadow border
- */
- Activator.prototype.activate = function () {
- // we allow only one active activator at a time
- if (Activator.current) {
- Activator.current.deactivate();
- }
- Activator.current = this;
-
- this.active = true;
- this.dom.overlay.style.display = 'none';
- util.addClassName(this.dom.container, 'vis-active');
-
- this.emit('change');
- this.emit('activate');
-
- // ugly hack: bind ESC after emitting the events, as the Network rebinds all
- // keyboard events on a 'change' event
- mousetrap.bind('esc', this.escListener);
- };
-
- /**
- * Deactivate the element
- * Overlay is displayed on top of the element
- */
- Activator.prototype.deactivate = function () {
- this.active = false;
- this.dom.overlay.style.display = '';
- util.removeClassName(this.dom.container, 'vis-active');
- mousetrap.unbind('esc', this.escListener);
-
- this.emit('change');
- this.emit('deactivate');
- };
-
- /**
- * Handle a tap event: activate the container
- * @param event
- * @private
- */
- Activator.prototype._onTapOverlay = function (event) {
- // activate the container
- this.activate();
- event.stopPropagation();
- };
-
- /**
- * Test whether the element has the requested parent element somewhere in
- * its chain of parent nodes.
- * @param {HTMLElement} element
- * @param {HTMLElement} parent
- * @returns {boolean} Returns true when the parent is found somewhere in the
- * chain of parent nodes.
- * @private
- */
- function _hasParent(element, parent) {
- while (element) {
- if (element === parent) {
- return true
- }
- element = element.parentNode;
- }
- return false;
- }
-
- module.exports = Activator;
|