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.
 
 
 

156 lines
4.0 KiB

var keycharm = require('keycharm');
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 = 'vis-overlay';
this.dom.container.appendChild(this.dom.overlay);
this.hammer = Hammer(this.dom.overlay);
this.hammer.on('tap', this._onTapOverlay.bind(this));
// block all touch events (except tap)
var me = this;
var events = [
'tap', 'doubletap', 'press',
'pinch',
'pan', 'panstart', 'panmove', 'panend'
];
events.forEach(function (event) {
me.hammer.on(event, function (event) {
event.stopPropagation();
});
});
// attach a click event to the window, in order to deactivate when clicking outside the timeline
if (document && document.body) {
this.onClick = function (event) {
if (!_hasParent(event.target, container)) {
me.deactivate();
}
};
document.body.addEventListener('click', this.onClick);
}
if (this.keycharm !== undefined) {
this.keycharm.destroy();
}
this.keycharm = keycharm();
// keycharm 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);
// remove global event listener
if (this.onClick) {
document.body.removeEventListener('click', this.onClick);
}
// cleanup hammer instances
this.hammer.destroy();
this.hammer = 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
this.keycharm.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');
this.keycharm.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;