diff --git a/src/timeline/Timeline.js b/src/timeline/Timeline.js index 1b7d4540..067d71db 100644 --- a/src/timeline/Timeline.js +++ b/src/timeline/Timeline.js @@ -205,6 +205,53 @@ Timeline.prototype._create = function (container) { container.appendChild(this.dom.root); }; +/** + * Destroy the Timeline, clean up all DOM elements and event listeners. + */ +Timeline.prototype.destroy = function () { + // unbind datasets + this.clear(); + + // remove all event listeners + this.off(); + + // stop checking for changed size + this._stopAutoResize(); + + // remove from DOM + if (this.dom.root.parentNode) { + this.dom.root.parentNode.removeChild(this.dom.root); + } + this.dom = null; + + // cleanup hammer touch events + for (var event in this.listeners) { + if (this.listeners.hasOwnProperty(event)) { + this.hammer.off(event, this.listeners[event]); + } + } + this.listeners = null; + this.hammer = null; + + // give all components the opportunity to cleanup + this.components.forEach(function (component) { + component.destroy(); + }); + + // remove all components + this.components = []; + this.range = null; + this.timeAxis = null; + this.itemSet = null; + this.currentTime = null; + this.customTime = null; + + //this.body.util.snap = null; + //this.body.util.toTime = null; + //this.body.util.toScreen = null; + this.body = null; +}; + /** * Set options. Options will be passed to all components loaded in the Timeline. * @param {Object} [options] @@ -503,6 +550,8 @@ Timeline.prototype.redraw = function() { props = this.props, dom = this.dom; + if (!dom) return; // when destroyed + // update class names dom.root.className = 'vis timeline root ' + options.orientation; @@ -680,7 +729,7 @@ Timeline.prototype._startAutoResize = function () { this._stopAutoResize(); - function checkSize() { + this._onResize = function() { if (me.options.autoResize != true) { // stop watching when the option autoResize is changed to false me._stopAutoResize(); @@ -697,12 +746,12 @@ Timeline.prototype._startAutoResize = function () { me.emit('change'); } } - } + }; - // TODO: automatically cleanup the event listener when the frame is deleted - util.addEventListener(window, 'resize', checkSize); + // add event listener to window resize + util.addEventListener(window, 'resize', this._onResize); - this.watchTimer = setInterval(checkSize, 1000); + this.watchTimer = setInterval(this._onResize, 1000); }; /** @@ -715,7 +764,9 @@ Timeline.prototype._stopAutoResize = function () { this.watchTimer = undefined; } - // TODO: remove event listener on window.resize + // remove event listener on window.resize + util.removeEventListener(window, 'resize', this._onResize); + this._onResize = null; }; /** diff --git a/src/timeline/component/Component.js b/src/timeline/component/Component.js index d01a76bc..bddccf0d 100644 --- a/src/timeline/component/Component.js +++ b/src/timeline/component/Component.js @@ -28,6 +28,13 @@ Component.prototype.redraw = function() { return false; }; +/** + * Destroy the component. Cleanup DOM and event listeners + */ +Component.prototype.destroy = function() { + // should be implemented by the component +}; + /** * Test whether the component is resized since the last time _isResized() was * called. diff --git a/src/timeline/component/CurrentTime.js b/src/timeline/component/CurrentTime.js index ce08437f..3b067280 100644 --- a/src/timeline/component/CurrentTime.js +++ b/src/timeline/component/CurrentTime.js @@ -37,6 +37,16 @@ CurrentTime.prototype._create = function() { this.bar = bar; }; +/** + * Destroy the CurrentTime bar + */ +CurrentTime.prototype.destroy = function () { + this.options.showCurrentTime = false; + this.redraw(); // will remove the bar from the DOM and stop refreshing + + this.body = null; +}; + /** * Set options for the component. Options will be merged in current options. * @param {Object} options Available parameters: @@ -76,8 +86,8 @@ CurrentTime.prototype.redraw = function() { // remove the line from the DOM if (this.bar.parentNode) { this.bar.parentNode.removeChild(this.bar); - this.stop(); } + this.stop(); } return false; diff --git a/src/timeline/component/CustomTime.js b/src/timeline/component/CustomTime.js index 038d7be2..21c86e8b 100644 --- a/src/timeline/component/CustomTime.js +++ b/src/timeline/component/CustomTime.js @@ -68,6 +68,19 @@ CustomTime.prototype._create = function() { this.hammer.on('dragend', this._onDragEnd.bind(this)); }; +/** + * Destroy the CustomTime bar + */ +CustomTime.prototype.destroy = function () { + this.options.showCustomTime = false; + this.redraw(); // will remove the bar from the DOM + + this.hammer.enable(false); + this.hammer = null; + + this.body = null; +}; + /** * Repaint the component * @return {boolean} Returns true if the component is resized diff --git a/src/timeline/component/ItemSet.js b/src/timeline/component/ItemSet.js index 37f08aff..05ebdd2f 100644 --- a/src/timeline/component/ItemSet.js +++ b/src/timeline/component/ItemSet.js @@ -288,6 +288,21 @@ ItemSet.prototype.markDirty = function() { this.stackDirty = true; }; +/** + * Destroy the ItemSet + */ +ItemSet.prototype.destroy = function() { + this.hide(); + this.setItems(null); + this.setGroups(null); + + this.hammer.enable(false); + this.hammer = null; + + this.body = null; + this.conversion = null; +}; + /** * Hide the component from the DOM */ diff --git a/src/timeline/component/TimeAxis.js b/src/timeline/component/TimeAxis.js index 3dffeb5d..c244c0ce 100644 --- a/src/timeline/component/TimeAxis.js +++ b/src/timeline/component/TimeAxis.js @@ -73,6 +73,21 @@ TimeAxis.prototype._create = function() { this.dom.background.className = 'timeaxis background'; }; +/** + * Destroy the TimeAxis + */ +TimeAxis.prototype.destroy = function() { + // remove from DOM + if (this.dom.foreground.parentNode) { + this.dom.foreground.parentNode.removeChild(this.dom.foreground); + } + if (this.dom.background.parentNode) { + this.dom.background.parentNode.removeChild(this.dom.background); + } + + this.body = null; +}; + /** * Repaint the component * @return {boolean} Returns true if the component is resized