/** * Create a timeline visualization * @param {HTMLElement} container * @param {DataSet | Array | DataTable} [data] * @param {Object} [options] See Timeline.setOptions for the available options. * @constructor */ function Timeline (container, data, options) { var me = this; this.options = { orientation: 'bottom', zoomMin: 10, // milliseconds zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000, // milliseconds moveable: true, zoomable: true }; // controller this.controller = new Controller(); // main panel if (!container) { throw new Error('No container element provided'); } this.main = new RootPanel(container, { autoResize: false }); this.controller.add(this.main); // range var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0); this.range = new Range({ start: now.clone().add('days', -3).valueOf(), end: now.clone().add('days', 4).valueOf() }); // TODO: reckon with options moveable and zoomable this.range.subscribe(this.main, 'move', 'horizontal'); this.range.subscribe(this.main, 'zoom', 'horizontal'); this.range.on('rangechange', function () { var force = true; me.controller.requestReflow(force); }); this.range.on('rangechanged', function () { var force = true; me.controller.requestReflow(force); }); // TODO: put the listeners in setOptions, be able to dynamically change with options moveable and zoomable // time axis this.timeaxis = new TimeAxis(this.main, [], { orientation: this.options.orientation, range: this.range }); this.timeaxis.setRange(this.range); this.controller.add(this.timeaxis); // items panel this.itemset = new ItemSet(this.main, [this.timeaxis], { orientation: this.options.orientation }); this.itemset.setRange(this.range); this.controller.add(this.itemset); // set options (must take place before setting the data) this.setOptions(options); // set data if (data) { this.setData(data); } } /** * Set options * @param {Object} options TODO: describe the available options */ Timeline.prototype.setOptions = function (options) { util.extend(this.options, options); // update options the timeaxis this.timeaxis.setOptions(this.options); // update options for the range this.range.setOptions(this.options); // update options the itemset var itemsTop, itemsHeight, mainHeight, maxHeight, me = this; if (this.options.orientation == 'top') { itemsTop = function () { return me.timeaxis.height; } } else { itemsTop = function () { return me.main.height - me.timeaxis.height - me.itemset.height; } } if (options.height) { // fixed height mainHeight = options.height; itemsHeight = function () { return me.main.height - me.timeaxis.height; }; } else { // auto height mainHeight = function () { return me.timeaxis.height + me.itemset.height; }; itemsHeight = null; } // TODO: maxHeight should be a string in px or % (currently only accepts a number) if (this.options.maxHeight) { if (!util.isNumber(this.options.maxHeight)) { throw new TypeError('Number expected for property maxHeight'); } maxHeight = function () { return me.options.maxHeight - me.timeaxis.height; } } this.main.setOptions({ height: mainHeight }); this.itemset.setOptions({ orientation: this.options.orientation, top: itemsTop, height: itemsHeight, maxHeight: maxHeight }); this.controller.repaint(); }; /** * Set data * @param {DataSet | Array | DataTable} data */ Timeline.prototype.setData = function(data) { var dataset = this.itemset.data; if (!dataset) { // first load of data this.itemset.setData(data); if (this.options.start == undefined || this.options.end == undefined) { // apply the data range as range var dataRange = this.itemset.getDataRange(); // add 5% on both sides var min = dataRange.min; var max = dataRange.max; if (min != null && max != null) { var interval = (max.valueOf() - min.valueOf()); min = new Date(min.valueOf() - interval * 0.05); max = new Date(max.valueOf() + interval * 0.05); } // override specified start and/or end date if (this.options.start != undefined) { min = new Date(this.options.start.valueOf()); } if (this.options.end != undefined) { max = new Date(this.options.end.valueOf()); } // apply range if there is a min or max available if (min != null || max != null) { this.range.setRange(min, max); } } } else { // updated data this.itemset.setData(data); } };