diff --git a/HISTORY.md b/HISTORY.md index 89e618c2..5fad761d 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -17,6 +17,7 @@ http://visjs.org ### DataSet - Support for queueing of changes, and flushing them at once. +- Implemented `DataSet.setOptions`. Only applicable for the `queue` options. ## 2014-10-24, version 3.6.2 diff --git a/docs/dataset.html b/docs/dataset.html index b5830523..d6c2bf81 100644 --- a/docs/dataset.html +++ b/docs/dataset.html @@ -185,7 +185,6 @@ var data = new vis.DataSet([data] [, options]) Default value is Infinity. - @@ -326,6 +325,41 @@ var data = new vis.DataSet([data] [, options]) + + + setOptions(options) + + none + + Set options for the DataSet. Available options: + + + + + update(data [, senderId]) diff --git a/index.js b/index.js index 211d73ca..a92b6d87 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ exports.DOMutil = require('./lib/DOMutil'); // data exports.DataSet = require('./lib/DataSet'); exports.DataView = require('./lib/DataView'); +exports.Queue = require('./lib/Queue'); // Graph3d exports.Graph3d = require('./lib/graph3d/Graph3d'); diff --git a/lib/DataSet.js b/lib/DataSet.js index d57b2445..f1f3b67a 100644 --- a/lib/DataSet.js +++ b/lib/DataSet.js @@ -84,16 +84,42 @@ function DataSet (data, options) { this.add(data); } - // change the dataset in a queued one - if (this._options.queue) { - var queue = { - replace: ['add', 'update', 'remove'] - }; - if (typeof this._options.queue === 'object') util.extend(queue, this._options.queue); - Queue.extend(this, queue); - } + this.setOptions(options); } +/** + * @param {Object} [options] Available options: + * {Object} queue Queue changes to the DataSet, + * flush them all at once. + * Queue options: + * - {number} delay Delay in ms, null by default + * - {number} max Maximum number of entries in the queue, Infinity by default + * @param options + */ +DataSet.prototype.setOptions = function(options) { + if (options && options.queue !== undefined) { + if (options.queue === false) { + // delete queue if loaded + if (this._queue) { + this._queue.destroy(); + delete this._queue; + } + } + else { + // create queue and update its options + if (!this._queue) { + this._queue = Queue.extend(this, { + replace: ['add', 'update', 'remove'] + }); + } + + if (typeof options.queue === 'object') { + this._queue.setOptions(options.queue); + } + } + } +}; + /** * Subscribe to an event, add an event listener * @param {String} event Event name. Available events: 'put', 'update', diff --git a/lib/Queue.js b/lib/Queue.js index 6c3cb6b9..4e9f56a4 100644 --- a/lib/Queue.js +++ b/lib/Queue.js @@ -2,7 +2,7 @@ * A queue * @param {Object} options * Available options: - * - delay: number When a number, the queue will be flushed + * - delay: number When provided, the queue will be flushed * automatically after an inactivity of this delay * in milliseconds. * Default value is null. @@ -13,14 +13,41 @@ */ function Queue(options) { // options - this.delay = options && typeof options.delay === 'number' ? options.delay : null; - this.max = options && typeof options.max === 'number' ? options.max : Infinity; + this.delay = null; + this.max = Infinity; // properties this._queue = []; this._timeout = null; + this._extended = null; + + this.setOptions(options); } +/** + * Update the configuration of the queue + * @param {Object} options + * Available options: + * - delay: number When provided, the queue will be flushed + * automatically after an inactivity of this delay + * in milliseconds. + * Default value is null. + * - max: number When the queue exceeds the given maximum number + * of entries, the queue is flushed automatically. + * Default value of max is Infinity. + * @param options + */ +Queue.prototype.setOptions = function (options) { + if (options && typeof options.delay !== 'undefined') { + this.delay = options.delay; + } + if (options && typeof options.max !== 'undefined') { + this.max = options.max; + } + + this._flushIfNeeded(); +}; + /** * Extend an object with queuing functionality. * The object will be extended with a function flush, and the methods provided @@ -31,13 +58,14 @@ function Queue(options) { * - replace: Array. * A list with method names of the methods * on the object to be replaced with queued ones. - * - delay: number When a number, the queue will be flushed + * - delay: number When provided, the queue will be flushed * automatically after an inactivity of this delay * in milliseconds. * Default value is null. * - max: number When the queue exceeds the given maximum number * of entries, the queue is flushed automatically. * Default value of max is Infinity. + * @return {Queue} Returns the created queue */ Queue.extend = function (object, options) { var queue = new Queue(options); @@ -49,11 +77,51 @@ Queue.extend = function (object, options) { queue.flush(); }; + var methods = [{ + name: 'flush', + original: undefined + }]; + if (options && options.replace) { for (var i = 0; i < options.replace.length; i++) { - queue.replace(object, options.replace[i]); + var name = options.replace[i]; + methods.push({ + name: name, + original: object[name] + }); + queue.replace(object, name); } } + + queue._extended = { + object: object, + methods: methods + }; + + return queue; +}; + +/** + * Destroy the queue. The queue will first flush all queued actions, and in + * case it has extended an object, will restore the original object. + */ +Queue.prototype.destroy = function () { + this.flush(); + + if (this._extended) { + var object = this._extended.object; + var methods = this._extended.methods; + for (var i = 0; i < methods.length; i++) { + var method = methods[i]; + if (method.original) { + object[method.name] = method.original; + } + else { + delete object[method.name]; + } + } + this._extended = null; + } }; /** @@ -96,15 +164,23 @@ Queue.prototype.queue = function(entry) { this._queue.push(entry); } + this._flushIfNeeded(); +}; + +/** + * Check whether the queue needs to be flushed + * @private + */ +Queue.prototype._flushIfNeeded = function () { // flush when the maximum is exceeded. if (this._queue.length > this.max) { this.flush(); } // flush after a period of inactivity when a delay is configured - if (typeof this.delay === 'number') { + clearTimeout(this._timeout); + if (this.queue.length > 0 && typeof this.delay === 'number') { var me = this; - clearTimeout(this._timeout); this._timeout = setTimeout(function () { me.flush(); }, this.delay); diff --git a/test/DataSet.test.js b/test/DataSet.test.js index fb3fd1de..85f8e455 100644 --- a/test/DataSet.test.js +++ b/test/DataSet.test.js @@ -236,4 +236,42 @@ describe('DataSet', function () { }, 200) }); + it('should remove a queue from the dataset', function () { + var options = {queue: true}; + var dataset = new DataSet([ + {id: 1, content: 'Item 1'}, + {id: 2, content: 'Item 2'} + ], options); + + assert.deepEqual(dataset.get(), [ + {id: 1, content: 'Item 1'}, + {id: 2, content: 'Item 2'} + ]); + + dataset.add({id: 3, content: 'Item 3'}); + dataset.update({id: 1, content: 'Item 1 (updated)'}); + dataset.remove(2); + + assert.deepEqual(dataset.get(), [ + {id: 1, content: 'Item 1'}, + {id: 2, content: 'Item 2'} + ]); + + dataset.setOptions({queue: false}); // remove queue, should flush changes + + assert.deepEqual(dataset.get(), [ + {id: 1, content: 'Item 1 (updated)'}, + {id: 3, content: 'Item 3'} + ]); + + dataset.add({id: 4, content: 'Item 4'}); + + assert.deepEqual(dataset.get(), [ + {id: 1, content: 'Item 1 (updated)'}, + {id: 3, content: 'Item 3'}, + {id: 4, content: 'Item 4'} + ]); + + }); + }); \ No newline at end of file diff --git a/test/Queue.test.js b/test/Queue.test.js index f99351e4..0dfa1779 100644 --- a/test/Queue.test.js +++ b/test/Queue.test.js @@ -153,4 +153,8 @@ describe('Queue', function () { assert.equal(obj.count, 2); }); + // TODO: test Queue.setOptions + + // TODO: test Queue.destroy + });