From f30a3a455656b4f0ebd23511e0e473001e4adce0 Mon Sep 17 00:00:00 2001 From: jos Date: Thu, 7 May 2015 12:26:58 +0200 Subject: [PATCH] Integrated an option configurator --- HISTORY.md | 2 +- docs/timeline/index.html | 7 + lib/timeline/Core.js | 16 +- lib/timeline/TimeStep.js | 6 +- lib/timeline/Timeline.js | 22 ++ lib/timeline/component/ItemSet.js | 16 +- lib/timeline/component/TimeAxis.js | 20 +- lib/timeline/component/item/BackgroundItem.js | 2 +- lib/timeline/component/item/BoxItem.js | 2 +- lib/timeline/component/item/PointItem.js | 4 +- lib/timeline/component/item/RangeItem.js | 4 +- lib/timeline/options.js | 198 ++++++++++++++++++ test/timeline.html | 2 + test/timeline_groups.html | 1 + 14 files changed, 273 insertions(+), 29 deletions(-) create mode 100644 lib/timeline/options.js diff --git a/HISTORY.md b/HISTORY.md index cea287db..bdf12af9 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -11,7 +11,6 @@ http://visjs.org When creating a custom bundle using browserify, one now needs to add a transform step using `6to5ify`, this is described in README.md. - ### Timeline - Implemented option `multiselect`, which is false by default. @@ -93,6 +92,7 @@ http://visjs.org ### Timeline +- Integrated an option configurator. - Implemented orientation option `'both'`, displaying a time axis both on top and bottom (#665). - Implemented creating new range items by dragging in an empty space with the diff --git a/docs/timeline/index.html b/docs/timeline/index.html index f0b78860..58a4b3df 100644 --- a/docs/timeline/index.html +++ b/docs/timeline/index.html @@ -477,6 +477,13 @@ var options = { When active, a blue shadow border is displayed around the Timeline. The Timeline is set active by clicking on it, and is changed to inactive again by clicking outside the Timeline or by pressing the ESC key. + + configure + boolean + false + When true, a configurator is loaded where all configuration options of the Timeline can be changed live. + + dataAttributes string[] or 'all' diff --git a/lib/timeline/Core.js b/lib/timeline/Core.js index 906b829d..303f8964 100644 --- a/lib/timeline/Core.js +++ b/lib/timeline/Core.js @@ -13,9 +13,6 @@ var CustomTime = require('./component/CustomTime'); /** * Create a timeline visualization - * @param {HTMLElement} container - * @param {vis.DataSet | Array} [items] - * @param {Object} [options] See Core.setOptions for the available options. * @constructor */ function Core () {} @@ -284,6 +281,19 @@ Core.prototype.setOptions = function (options) { // propagate options to all components this.components.forEach(component => component.setOptions(options)); + // enable/disable configure + if (this.configurationSystem) { + this.configurationSystem.setOptions(options.configure); + + // collect the settings of all components, and pass them to the configuration system + var appliedOptions = util.deepExtend({}, this.options); + this.components.forEach(function (component) { + util.deepExtend(appliedOptions, component.options); + }); + console.log('options', appliedOptions) + this.configurationSystem.setModuleOptions({global: appliedOptions}); + } + // redraw everything this._redraw(); }; diff --git a/lib/timeline/TimeStep.js b/lib/timeline/TimeStep.js index 5313ace1..88d25d7a 100644 --- a/lib/timeline/TimeStep.js +++ b/lib/timeline/TimeStep.js @@ -80,7 +80,7 @@ TimeStep.FORMAT = { /** * Set custom formatting for the minor an major labels of the TimeStep. * Both `minorLabels` and `majorLabels` are an Object with properties: - * 'millisecond, 'second, 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * 'millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'. * @param {{minorLabels: Object, majorLabels: Object}} format */ TimeStep.prototype.setFormat = function (format) { @@ -246,7 +246,7 @@ TimeStep.prototype.getCurrent = function() { * @param {{scale: string, step: number}} params * An object containing two properties: * - A string 'scale'. Choose from 'millisecond', 'second', - * 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * 'minute', 'hour', 'weekday', 'day', 'month', 'year'. * - A number 'step'. A step size, by default 1. * Choose for example 1, 2, 5, or 10. */ @@ -324,7 +324,7 @@ TimeStep.prototype.setMinimumStep = function(minimumStep) { * Static function * @param {Date} date the date to be snapped. * @param {string} scale Current scale, can be 'millisecond', 'second', - * 'minute', 'hour', 'weekday, 'day, 'month, 'year'. + * 'minute', 'hour', 'weekday, 'day', 'month', 'year'. * @param {number} step Current step (1, 2, 4, 5, ... * @return {Date} snappedDate */ diff --git a/lib/timeline/Timeline.js b/lib/timeline/Timeline.js index e4b25613..7962bced 100644 --- a/lib/timeline/Timeline.js +++ b/lib/timeline/Timeline.js @@ -9,6 +9,10 @@ var TimeAxis = require('./component/TimeAxis'); var CurrentTime = require('./component/CurrentTime'); var CustomTime = require('./component/CustomTime'); var ItemSet = require('./component/ItemSet'); +var ConfigurationSystem = require('../network/modules/ConfigurationSystem'); + +var allOptions = require('./options').allOptions; +var configureOptions = require('./options').configureOptions; /** * Create a timeline visualization @@ -111,6 +115,9 @@ function Timeline (container, items, groups, options) { me.emit('contextmenu', me.getEventProperties(event)) }; + // setup configuration system + this.configurationSystem = new ConfigurationSystem(this, container, configureOptions); + // apply options if (options) { this.setOptions(options); @@ -143,6 +150,21 @@ Timeline.prototype.redraw = function() { this._redraw(); }; +Timeline.prototype.setOptions = function (options) { + Core.prototype.setOptions.call(this, options); + + if ('type' in options) { + if (options.type !== this.options.type) { + this.options.type = options.type; + + // force recreation of all items + var itemsData = this.itemsData; + this.setItems(null); // remove all + this.setItems(itemsData); // add all + } + } +}; + /** * Set items * @param {vis.DataSet | Array | null} items diff --git a/lib/timeline/component/ItemSet.js b/lib/timeline/component/ItemSet.js index 487bdcf1..0480786d 100644 --- a/lib/timeline/component/ItemSet.js +++ b/lib/timeline/component/ItemSet.js @@ -29,7 +29,9 @@ function ItemSet(body, options) { this.defaultOptions = { type: null, // 'box', 'point', 'range', 'background' - orientation: 'bottom', // item orientation: 'top' or 'bottom' + orientation: { + item: 'bottom' // item orientation: 'top' or 'bottom' + }, align: 'auto', // alignment of box items stack: true, groupOrder: null, @@ -222,7 +224,7 @@ ItemSet.prototype._create = function(){ * Alignment for the items, only applicable for * BoxItem. Choose 'center' (default), 'left', or * 'right'. - * {String} orientation + * {String} orientation.item * Orientation of the item set. Choose 'top' or * 'bottom' (default). * {Function} groupOrder @@ -282,10 +284,10 @@ ItemSet.prototype.setOptions = function(options) { if ('orientation' in options) { if (typeof options.orientation === 'string') { - this.options.orientation = options.orientation; + this.options.orientation.item = options.orientation === 'top' ? 'top' : 'bottom'; } else if (typeof options.orientation === 'object' && 'item' in options.orientation) { - this.options.orientation = options.orientation.item; + this.options.orientation.item = options.orientation.item; } } @@ -504,7 +506,7 @@ ItemSet.prototype.redraw = function() { range = this.body.range, asSize = util.option.asSize, options = this.options, - orientation = options.orientation, + orientation = options.orientation.item, resized = false, frame = this.dom.frame, editable = options.editable.updateTime || options.editable.updateGroup; @@ -578,7 +580,7 @@ ItemSet.prototype.redraw = function() { * @private */ ItemSet.prototype._firstGroup = function() { - var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1); + var firstGroupIndex = (this.options.orientation.item == 'top') ? 0 : (this.groupIds.length - 1); var firstGroupId = this.groupIds[firstGroupIndex]; var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED]; @@ -1593,7 +1595,7 @@ ItemSet.prototype.groupFromTarget = function(event) { return group; } - if (this.options.orientation === 'top') { + if (this.options.orientation.item === 'top') { if (i === this.groupIds.length - 1 && pageY > top) { return group; } diff --git a/lib/timeline/component/TimeAxis.js b/lib/timeline/component/TimeAxis.js index ad60c8ef..5571f54d 100644 --- a/lib/timeline/component/TimeAxis.js +++ b/lib/timeline/component/TimeAxis.js @@ -34,10 +34,12 @@ function TimeAxis (body, options) { }; this.defaultOptions = { - orientation: 'bottom', // axis orientation: 'top' or 'bottom' + orientation: { + axis: 'bottom' + }, // axis orientation: 'top' or 'bottom' showMinorLabels: true, showMajorLabels: true, - format: null, + format: TimeStep.FORMAT, timeAxis: null }; this.options = util.extend({}, this.defaultOptions); @@ -56,14 +58,14 @@ TimeAxis.prototype = new Component(); * Set options for the TimeAxis. * Parameters will be merged in current options. * @param {Object} options Available options: - * {string} [orientation] + * {string} [orientation.axis] * {boolean} [showMinorLabels] * {boolean} [showMajorLabels] */ TimeAxis.prototype.setOptions = function(options) { if (options) { // copy all options that we know - util.selectiveExtend([ + util.selectiveDeepExtend([ 'showMinorLabels', 'showMajorLabels', 'hiddenDates', @@ -73,10 +75,10 @@ TimeAxis.prototype.setOptions = function(options) { if ('orientation' in options) { if (typeof options.orientation === 'string') { - this.options.orientation = options.orientation; + this.options.orientation.axis = options.orientation; } else if (typeof options.orientation === 'object' && 'axis' in options.orientation) { - this.options.orientation = options.orientation.axis; + this.options.orientation.axis = options.orientation.axis; } } @@ -131,7 +133,7 @@ TimeAxis.prototype.redraw = function () { var background = this.dom.background; // determine the correct parent DOM element (depending on option orientation) - var parent = (options.orientation == 'top') ? this.body.dom.top : this.body.dom.bottom; + var parent = (options.orientation.axis == 'top') ? this.body.dom.top : this.body.dom.bottom; var parentChanged = (foreground.parentNode !== parent); // calculate character width and height @@ -148,7 +150,7 @@ TimeAxis.prototype.redraw = function () { props.width = foreground.offsetWidth; props.minorLineHeight = this.body.domProps.root.height - props.majorLabelHeight - - (options.orientation == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); + (options.orientation.axis == 'top' ? this.body.domProps.bottom.height : this.body.domProps.top.height); props.minorLineWidth = 1; // TODO: really calculate width props.majorLineHeight = props.minorLineHeight + props.majorLabelHeight; props.majorLineWidth = 1; // TODO: really calculate width @@ -185,7 +187,7 @@ TimeAxis.prototype.redraw = function () { * @private */ TimeAxis.prototype._repaintLabels = function () { - var orientation = this.options.orientation; + var orientation = this.options.orientation.axis; // calculate range and step (step such that we have space for 7 characters per label) var start = util.convert(this.body.range.start, 'Number'); diff --git a/lib/timeline/component/item/BackgroundItem.js b/lib/timeline/component/item/BackgroundItem.js index dba94be0..cdebc4ed 100644 --- a/lib/timeline/component/item/BackgroundItem.js +++ b/lib/timeline/component/item/BackgroundItem.js @@ -143,7 +143,7 @@ BackgroundItem.prototype.repositionX = RangeItem.prototype.repositionX; * @Override */ BackgroundItem.prototype.repositionY = function(margin) { - var onTop = this.options.orientation === 'top'; + var onTop = this.options.orientation.item === 'top'; this.dom.content.style.top = onTop ? '' : '0'; this.dom.content.style.bottom = onTop ? '0' : ''; var height; diff --git a/lib/timeline/component/item/BoxItem.js b/lib/timeline/component/item/BoxItem.js index 171a573a..65b720a3 100644 --- a/lib/timeline/component/item/BoxItem.js +++ b/lib/timeline/component/item/BoxItem.js @@ -191,7 +191,7 @@ BoxItem.prototype.repositionX = function() { * @Override */ BoxItem.prototype.repositionY = function() { - var orientation = this.options.orientation; + var orientation = this.options.orientation.item; var box = this.dom.box; var line = this.dom.line; var dot = this.dom.dot; diff --git a/lib/timeline/component/item/PointItem.js b/lib/timeline/component/item/PointItem.js index d70f6a3b..ace98250 100644 --- a/lib/timeline/component/item/PointItem.js +++ b/lib/timeline/component/item/PointItem.js @@ -166,8 +166,8 @@ PointItem.prototype.repositionX = function() { * @Override */ PointItem.prototype.repositionY = function() { - var orientation = this.options.orientation, - point = this.dom.point; + var orientation = this.options.orientation.item; + var point = this.dom.point; if (orientation == 'top') { point.style.top = this.top + 'px'; diff --git a/lib/timeline/component/item/RangeItem.js b/lib/timeline/component/item/RangeItem.js index 10e36a78..64b80a97 100644 --- a/lib/timeline/component/item/RangeItem.js +++ b/lib/timeline/component/item/RangeItem.js @@ -234,8 +234,8 @@ RangeItem.prototype.repositionX = function(limitSize) { * @Override */ RangeItem.prototype.repositionY = function() { - var orientation = this.options.orientation, - box = this.dom.box; + var orientation = this.options.orientation.item; + var box = this.dom.box; if (orientation == 'top') { box.style.top = this.top + 'px'; diff --git a/lib/timeline/options.js b/lib/timeline/options.js new file mode 100644 index 00000000..5b703fce --- /dev/null +++ b/lib/timeline/options.js @@ -0,0 +1,198 @@ +/** + * This object contains all possible options. It will check if the types are correct, if required if the option is one + * of the allowed values. + * + * __any__ means that the name of the property does not matter. + * __type__ is a required field for all objects and contains the allowed types of all objects + */ +let string = 'string'; +let boolean = 'boolean'; +let number = 'number'; +let array = 'array'; +let date = 'date'; +let object = 'object'; // should only be in a __type__ property +let dom = 'dom'; +let moment = 'moment'; +let fn = 'function'; +let nada = 'null'; +let undef = 'undefined'; +let any = 'any'; + + +let allOptions = { + configure: { + enabled: {boolean}, + filter: {boolean,string,array}, + container: {dom}, + __type__: {object,boolean,string,array} + }, + + //globals : + align: {string}, + autoResize: {boolean}, + clickToUse: {boolean}, + dataAttributes: {string, Array}, + editable: {boolean, object}, + end: {number, Date, string, moment}, + format: { + minorLabels: { + millisecond: {string}, + second: {string}, + minute: {string}, + hour: {string}, + weekday: {string}, + day: {string}, + month: {string}, + year: {string} + }, + majorLabels: { + millisecond: {string}, + second: {string}, + minute: {string}, + hour: {string}, + weekday: {string}, + day: {string}, + month: {string}, + year: {string} + } + }, + groupOrder: {string, fn}, + height: {string, number}, + hiddenDates: {object, array}, + locale:{string}, + locales:{ + __any__: {object}, + __type__: {object} + }, + margin: { + axis: {number}, + item: { + horizontal: {number}, + vertical: {number}, + __type__: {object,number} + }, + __type__: {object,number} + }, + max: {date, number, string, moment}, + maxHeight: {number, string}, + min: {date, number, string, moment}, + minHeight: {number, string}, + moveable: {boolean}, + multiselect: {boolean}, + onAdd: {fn}, + onUpdate: {fn}, + onMove: {fn}, + onMoving: {fn}, + onRename: {fn}, + order: {fn}, + orientation: { + axis: {string}, + item: {string}, + __type__: {string, object} + }, + selectable: {boolean}, + showCurrentTime: {boolean}, + showMajorLabels: {boolean}, + showMinorLabels: {boolean}, + stack: {boolean}, + snap: {fn, nada}, + start: {date, number, string, moment}, + template: {fn}, + timeAxis: { + scale: {string}, + step: {number}, + __type__: {object} + }, + type: {string}, + width: {string, number}, + zoomable: {boolean}, + zoomMax: {number}, + zoomMin: {number}, + + __type__: {object} +}; + +let configureOptions = { + global: { + align: ['center', 'left', 'right'], + autoResize: true, + clickToUse: false, + // dataAttributes: ['all'], // FIXME: can be 'all' or string[] + editable: { + add: false, + remove: false, + updateGroup: false, + updateTime: false + }, + end: '', + format: { + minorLabels: { + millisecond:'SSS', + second: 's', + minute: 'HH:mm', + hour: 'HH:mm', + weekday: 'ddd D', + day: 'D', + month: 'MMM', + year: 'YYYY' + }, + majorLabels: { + millisecond:'HH:mm:ss', + second: 'D MMMM HH:mm', + minute: 'ddd D MMMM', + hour: 'ddd D MMMM', + weekday: 'MMMM YYYY', + day: 'MMMM YYYY', + month: 'YYYY', + year: '' + } + }, + + //groupOrder: {string, fn}, + height: '', + //hiddenDates: {object, array}, + locale: '', + margin: { + axis: [20, 0, 100, 1], + item: { + horizontal: [10, 0, 100, 1], + vertical: [10, 0, 100, 1] + } + }, + max: '', + maxHeight: '', + min: '', + minHeight: '', + moveable: false, + multiselect: false, + //onAdd: {fn}, + //onUpdate: {fn}, + //onMove: {fn}, + //onMoving: {fn}, + //onRename: {fn}, + //order: {fn}, + orientation: { + axis: ['both', 'bottom', 'top'], + item: ['bottom', 'top'] + }, + selectable: true, + showCurrentTime: false, + showMajorLabels: true, + showMinorLabels: true, + stack: true, + //snap: {fn, nada}, + start: '', + //template: {fn}, + //timeAxis: { + // scale: ['millisecond', 'second', 'minute', 'hour', 'weekday', 'day', 'month', 'year'], + // step: [1, 1, 10, 1] + //}, + type: ['box', 'point', 'range', 'background'], + width: '100%', + zoomable: true, + zoomMax: [315360000000000, 10, 315360000000000, 1], + zoomMin: [10, 10, 315360000000000, 1] + } +}; + +export {allOptions, configureOptions}; \ No newline at end of file diff --git a/test/timeline.html b/test/timeline.html index bc6769cb..7923cb33 100644 --- a/test/timeline.html +++ b/test/timeline.html @@ -142,6 +142,8 @@ var container = document.getElementById('visualization'); var options = { + configure: true, + multiselect: true, editable: true, //orientation: 'top', orientation: 'both', diff --git a/test/timeline_groups.html b/test/timeline_groups.html index bef4ac9a..c6592c54 100644 --- a/test/timeline_groups.html +++ b/test/timeline_groups.html @@ -90,6 +90,7 @@ var container = document.getElementById('visualization'); var options = { //orientation: 'top', + multiselect: true, editable: { add: true, remove: true,