diff --git a/dist/vis.css b/dist/vis.css index 71e09683..4c476d13 100644 --- a/dist/vis.css +++ b/dist/vis.css @@ -323,7 +323,7 @@ z-index: 1; } -.vis.timeline .dataaxis .grid.horizontal { +.vis.timeline .vispanel.background.horizontal .grid.horizontal { position: absolute; left: 0; width: 100%; @@ -331,11 +331,11 @@ border-bottom: 1px solid; } -.vis.timeline .dataaxis .grid.minor { +.vis.timeline .vispanel.background.horizontal .grid.minor { border-color: #e5e5e5; } -.vis.timeline .dataaxis .grid.major { +.vis.timeline .vispanel.background.horizontal .grid.major { border-color: #bfbfbf; } diff --git a/dist/vis.js b/dist/vis.js index a89c0527..bae9fc7b 100644 --- a/dist/vis.js +++ b/dist/vis.js @@ -4,8 +4,8 @@ * * A dynamic, browser-based visualization library. * - * @version 1.1.0 - * @date 2014-06-13 + * @version @@version + * @date @@date * * @license * Copyright (C) 2011-2014 Almende B.V, http://almende.com @@ -2544,9 +2544,19 @@ DataView.prototype.unsubscribe = DataView.prototype.off; * @constructor DataAxis * @extends Component */ -function DataAxis (options) { +function DataAxis (body, options) { this.id = util.randomUUID(); + this.body = body; + this.defaultOptions = { + orientation: 'left', // supported: 'left' + showMinorLabels: true, + showMajorLabels: true, + width: '90px', + height: '300px' + }; + + this.props = {}; this.dom = { majorLines: [], majorTexts: [], @@ -2559,23 +2569,17 @@ function DataAxis (options) { minorTexts: [] } }; - this.props = { - range: { - start: 0, - end: 0, - minimumStep: 0 - }, - lineTop: 0 - }; - this.options = options || {}; - this.defaultOptions = { - orientation: 'left', // supported: 'left' - showMinorLabels: true, - showMajorLabels: true - }; +// this.yRange = new Range(body,{ +// direction: 'vertical', +// min: null, +// max: null, +// zoomMin: 1e-5, +// zoomMax: 1e9 +// }); + this.yRange = {start:0, end:0}; - this.range = null; + this.options = util.extend({}, this.defaultOptions); this.conversionFactor = 1; // create the HTML DOM @@ -2583,71 +2587,95 @@ function DataAxis (options) { } DataAxis.prototype = new Component(); - -// TODO: comment options DataAxis.prototype.setOptions = Component.prototype.setOptions; + /** * Create the HTML DOM for the DataAxis */ -DataAxis.prototype._create = function _create() { - this.frame = document.createElement('div'); +DataAxis.prototype._create = function() { + this.dom.frame = document.createElement('div'); + this.dom.frame.style.width = this.options.width; + this.dom.frame.style.height = this.options.height; + + this.dom.lineContainer = document.createElement('div'); + this.dom.lineContainer.style.width = '100%'; + this.dom.lineContainer.style.height = this.options.height; + + this.show(); }; + /** - * Set a range (start and end) - * @param {Range | Object} range A Range or an object containing start and end. + * Create the HTML DOM for the DataAxis */ -DataAxis.prototype.setRange = function (range) { - if (!(range instanceof Range) && (!range || range.start === undefined || range.end === undefined)) { - throw new TypeError('Range must be an instance of Range, ' + - 'or an object containing start and end.'); +DataAxis.prototype.show = function() { + if (!this.dom.frame.parentNode) { + if (this.options.orientation == 'left') { + this.body.dom.left.appendChild(this.dom.frame); + } + if (this.options.orientation == 'right') { + this.body.dom.right.appendChild(this.dom.frame); + } + } + + if (!this.dom.lineContainer.parentNode) { + this.body.dom.backgroundHorizontal.appendChild(this.dom.lineContainer); } - this.range = range; }; /** - * Get the outer frame of the time axis - * @return {HTMLElement} frame + * Create the HTML DOM for the DataAxis */ -DataAxis.prototype.getFrame = function getFrame() { - return this.frame; +DataAxis.prototype.hide = function() { + if (this.dom.frame.parentNode) { + this.dom.frame.parentNode.removeChild(this.dom.frame); + } + + if (this.dom.lineContainer.parentNode) { + this.body.dom.backgroundHorizontal.removeChild(this.dom.lineContainer); + } +}; +/** + * Set a range (start and end) + * @param {Range | Object} range A Range or an object containing start and end. + */ +DataAxis.prototype.setRange = function (range) { + if (!(range instanceof Range) && (!range || range.start === undefined || range.end === undefined)) { + throw new TypeError('Range must be an instance of Range, ' + 'or an object containing start and end.'); + } + this.yRange.start = range.start; + this.yRange.end = range.end; }; /** * Repaint the component * @return {boolean} Returns true if the component is resized */ -DataAxis.prototype.repaint = function () { - var asSize = util.option.asSize; - var options = this.options; +DataAxis.prototype.redraw = function () { var props = this.props; - var frame = this.frame; + var frame = this.dom.frame; // update classname - frame.className = 'dataaxis'; // TODO: add className from options if defined + frame.className = 'dataaxis'; // calculate character width and height this._calculateCharSize(); - // TODO: recalculate sizes only needed when parent is resized or options is changed - var orientation = this.options['orientation']; - var showMinorLabels = this.options['showMinorLabels']; - var showMajorLabels = this.options['showMajorLabels']; + var orientation = this.options.orientation; + var showMinorLabels = this.options.showMinorLabels; + var showMajorLabels = this.options.showMajorLabels; // determine the width and height of the elemens for the axis props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; - this.height = this.options.height; - this.width = frame.offsetWidth; // TODO: only update the width when the frame is resized? - props.minorLineWidth = this.options.svg.offsetWidth; - props.minorLineHeight = 1; // TODO: really calculate width - props.majorLineWidth = this.options.svg.offsetWidth; - props.majorLineHeight = 1; // TODO: really calculate width + props.minorLineWidth = 3000; + props.minorLineHeight = 1; + props.majorLineWidth = 3000; + props.majorLineHeight = 1; // take frame offline while updating (is almost twice as fast) - // TODO: top/bottom positioning should be determined by options set in the Timeline, not here if (orientation == 'left') { frame.style.top = '0'; frame.style.left = '0'; @@ -2663,26 +2691,26 @@ DataAxis.prototype.repaint = function () { frame.style.height = this.height + "px"; } - this._repaintLabels(); + this._redrawLabels(); }; /** * Repaint major and minor text labels and vertical grid lines * @private */ -DataAxis.prototype._repaintLabels = function () { +DataAxis.prototype._redrawLabels = function () { var orientation = this.options['orientation']; // calculate range and step (step such that we have space for 7 characters per label) - var start = this.range.start; - var end = this.range.end; + var start = this.yRange.start; + var end = this.yRange.end; var minimumStep = (this.props.minorCharHeight || 10); //in pixels - var step = new DataStep(start, end, minimumStep, this.options.svg.offsetHeight); + var step = new DataStep(start, end, minimumStep, this.dom.frame.offsetHeight); this.step = step; // Move all DOM elements to a "redundant" list, where they // can be picked for re-use, and clear the lists with lines and texts. - // At the end of the function _repaintLabels, left over elements will be cleaned up + // At the end of the function _redrawLabels, left over elements will be cleaned up var dom = this.dom; dom.redundant.majorLines = dom.majorLines; dom.redundant.majorTexts = dom.majorTexts; @@ -2694,7 +2722,7 @@ DataAxis.prototype._repaintLabels = function () { dom.minorTexts = []; step.first(); - var stepPixels = this.options.svg.offsetHeight / ((step.marginRange / step.step) + 1); + var stepPixels = this.dom.frame.offsetHeight / ((step.marginRange / step.step) + 1); var xFirstMajorLabel = undefined; this.valueAtZero = step.marginEnd; @@ -2706,7 +2734,7 @@ DataAxis.prototype._repaintLabels = function () { var isMajor = step.isMajor(); if (this.options['showMinorLabels'] && isMajor == false) { - this._repaintMinorText(y, step.getLabelMinor(), orientation); + this._redrawMinorText(y, step.getLabelMinor(), orientation); } if (isMajor && this.options['showMajorLabels']) { @@ -2714,12 +2742,12 @@ DataAxis.prototype._repaintLabels = function () { if (xFirstMajorLabel == undefined) { xFirstMajorLabel = y; } - this._repaintMajorText(y, step.getLabelMajor(), orientation); + this._redrawMajorText(y, step.getLabelMajor(), orientation); } - this._repaintMajorLine(y, orientation); + this._redrawMajorLine(y, orientation); } else { - this._repaintMinorLine(y, orientation); + this._redrawMinorLine(y, orientation); } step.next(); @@ -2728,9 +2756,6 @@ DataAxis.prototype._repaintLabels = function () { } this.conversionFactor = marginStartPos/step.marginRange; - console.log(marginStartPos, step.marginRange, this.conversionFactor); - - // create a major label on the left when needed if (this.options['showMajorLabels']) { @@ -2739,7 +2764,7 @@ DataAxis.prototype._repaintLabels = function () { var widthText = leftText.length * (this.props.majorCharWidth || 10) + 10; // upper bound estimation if (xFirstMajorLabel == undefined || widthText < xFirstMajorLabel) { - this._repaintMajorText(0, leftText, orientation); + this._redrawMajorText(0, leftText, orientation); } } @@ -2764,7 +2789,7 @@ DataAxis.prototype.convertValues = function(data) { DataAxis.prototype._getPos = function(value) { var invertedValue = this.valueAtZero - value; var convertedValue = invertedValue * this.conversionFactor; - return convertedValue + return convertedValue - 2; // the -2 is to compensate for the borders } /** @@ -2774,7 +2799,7 @@ DataAxis.prototype._getPos = function(value) { * @param {String} orientation "top" or "bottom" (default) * @private */ -DataAxis.prototype._repaintMinorText = function (x, text, orientation) { +DataAxis.prototype._redrawMinorText = function (x, text, orientation) { // reuse redundant label var label = this.dom.redundant.minorTexts.shift(); @@ -2784,7 +2809,7 @@ DataAxis.prototype._repaintMinorText = function (x, text, orientation) { label = document.createElement('div'); label.appendChild(content); label.className = 'yAxis minor'; - this.frame.appendChild(label); + this.dom.frame.appendChild(label); } this.dom.minorTexts.push(label); @@ -2810,7 +2835,7 @@ DataAxis.prototype._repaintMinorText = function (x, text, orientation) { * @param {String} orientation "top" or "bottom" (default) * @private */ -DataAxis.prototype._repaintMajorText = function (x, text, orientation) { +DataAxis.prototype._redrawMajorText = function (x, text, orientation) { // reuse redundant label var label = this.dom.redundant.majorTexts.shift(); @@ -2820,7 +2845,7 @@ DataAxis.prototype._repaintMajorText = function (x, text, orientation) { label = document.createElement('div'); label.className = 'yAxis major'; label.appendChild(content); - this.frame.appendChild(label); + this.dom.frame.appendChild(label); } this.dom.majorTexts.push(label); @@ -2845,7 +2870,7 @@ DataAxis.prototype._repaintMajorText = function (x, text, orientation) { * @param {String} orientation "top" or "bottom" (default) * @private */ -DataAxis.prototype._repaintMinorLine = function (y, orientation) { +DataAxis.prototype._redrawMinorLine = function (y, orientation) { // reuse redundant line var line = this.dom.redundant.minorLines.shift(); @@ -2853,7 +2878,7 @@ DataAxis.prototype._repaintMinorLine = function (y, orientation) { // create vertical line line = document.createElement('div'); line.className = 'grid horizontal minor'; - this.frame.appendChild(line); + this.dom.lineContainer.appendChild(line); } this.dom.minorLines.push(line); @@ -2875,7 +2900,7 @@ DataAxis.prototype._repaintMinorLine = function (y, orientation) { * @param {String} orientation "top" or "bottom" (default) * @private */ -DataAxis.prototype._repaintMajorLine = function (y, orientation) { +DataAxis.prototype._redrawMajorLine = function (y, orientation) { // reuse redundant line var line = this.dom.redundant.majorLines.shift(); @@ -2883,7 +2908,7 @@ DataAxis.prototype._repaintMajorLine = function (y, orientation) { // create vertical line line = document.createElement('div'); line.className = 'grid horizontal major'; - this.frame.appendChild(line); + this.dom.lineContainer.appendChild(line); } this.dom.majorLines.push(line); @@ -2911,12 +2936,12 @@ DataAxis.prototype._calculateCharSize = function () { var measureCharMinor = document.createElement('DIV'); measureCharMinor.className = 'text minor measure'; measureCharMinor.appendChild(textMinor); - this.frame.appendChild(measureCharMinor); + this.dom.frame.appendChild(measureCharMinor); this.props.minorCharHeight = measureCharMinor.clientHeight; this.props.minorCharWidth = measureCharMinor.clientWidth; - this.frame.removeChild(measureCharMinor); + this.dom.frame.removeChild(measureCharMinor); } if (!('majorCharHeight' in this.props)) { @@ -2924,12 +2949,12 @@ DataAxis.prototype._calculateCharSize = function () { var measureCharMajor = document.createElement('DIV'); measureCharMajor.className = 'text major measure'; measureCharMajor.appendChild(textMajor); - this.frame.appendChild(measureCharMajor); + this.dom.frame.appendChild(measureCharMajor); this.props.majorCharHeight = measureCharMajor.clientHeight; this.props.majorCharWidth = measureCharMajor.clientWidth; - this.frame.removeChild(measureCharMajor); + this.dom.frame.removeChild(measureCharMajor); } }; @@ -2939,7 +2964,7 @@ DataAxis.prototype._calculateCharSize = function () { * @param {Date} date the date to be snapped. * @return {Date} snappedDate */ -DataAxis.prototype.snap = function snap (date) { +DataAxis.prototype.snap = function(date) { return this.step.snap(date); }; @@ -3014,25 +3039,32 @@ function Linegraph(body, options) { this.items = {}; // object with an Item for every data item this.selection = []; // list with the ids of all selected nodes - + this.lastStart = this.body.range.start; this.touchParams = {}; // stores properties while dragging // create the HTML DOM this._create(); - this.body.emitter.on("drag", this._onDrag.bind(this)) - this.body.emitter.on("dragEnd", this._onDragEnd.bind(this)) - - this.setOptions(options); -} + var me = this; -Linegraph.prototype._onDrag = function (event) { - if (this.svg) { - - } -} + this.body.emitter.on("rangechange",function() { + if (me.lastStart != 0) { + var offset = me.body.range.start - me.lastStart; + var range = me.body.range.end - me.body.range.start; + if (me.width != 0) { + var rangePerPixelInv = me.width/range; + var xOffset = offset * rangePerPixelInv; + me.svg.style.left = (-me.width - xOffset) + "px"; + } + } + }); + this.body.emitter.on("rangechanged", function() { + me.lastStart = me.body.range.start; + me.svg.style.left = util.option.asSize(-me.width); + me.updateGraph.apply(me); + }); -Linegraph.prototype._onDragEnd = function (event) { - this.updateGraph(); + this.setOptions(options); + this.body.emitter.emit("change"); } Linegraph.prototype = new Component(); @@ -3069,6 +3101,7 @@ Linegraph.prototype._create = function(){ this.svg = document.createElementNS('http://www.w3.org/2000/svg',"svg"); this.svg.style.position = "relative" this.svg.style.height = "300px"; +// this.svg.style.width = this.svg.style.display = "block"; this.path = document.createElementNS('http://www.w3.org/2000/svg',"path"); @@ -3093,16 +3126,13 @@ Linegraph.prototype._create = function(){ this.svg.appendChild(this.path); // panel with time axis - var dataAxisOptions = { - range: this.body.range, - left: null, - top: null, - width: null, - height: 300, - svg: this.svg - }; - this.yAxis = new DataAxis(dataAxisOptions); - this.dom.axis = this.yAxis.getFrame(); + this.yAxis = new DataAxis(this.body, { + orientation: 'left', + showMinorLabels: true, + showMajorLabels: true, + width: '90px', + height: this.svg.style.height + }); this.show(); }; @@ -3110,45 +3140,6 @@ Linegraph.prototype._create = function(){ Linegraph.prototype.setOptions = function(options) { if (options) { - // copy all options that we know - var fields = ['type', 'align', 'orientation', 'padding', 'stack', 'selectable', 'groupOrder']; - util.selectiveExtend(fields, this.options, options); - - if ('margin' in options) { - if (typeof options.margin === 'number') { - this.options.margin.axis = options.margin; - this.options.margin.item = options.margin; - } - else if (typeof options.margin === 'object'){ - util.selectiveExtend(['axis', 'item'], this.options.margin, options.margin); - } - } - - if ('editable' in options) { - if (typeof options.editable === 'boolean') { - this.options.editable.updateTime = options.editable; - this.options.editable.updateGroup = options.editable; - this.options.editable.add = options.editable; - this.options.editable.remove = options.editable; - } - else if (typeof options.editable === 'object') { - util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable); - } - } - - // callback functions - var addCallback = (function (name) { - if (name in options) { - var fn = options[name]; - if (!(fn instanceof Function) || fn.length != 2) { - throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)'); - } - this.options[name] = fn; - } - }).bind(this); - ['onAdd', 'onUpdate', 'onRemove', 'onMove'].forEach(addCallback); - - // force the itemSet to refresh: options like orientation and margins may be changed } }; @@ -3161,11 +3152,6 @@ Linegraph.prototype.hide = function() { if (this.dom.frame.parentNode) { this.dom.frame.parentNode.removeChild(this.dom.frame); } - - // remove the labelset containing all group labels - if (this.dom.axis.parentNode) { - this.dom.axis.parentNode.removeChild(this.dom.axis); - } }; /** @@ -3177,11 +3163,6 @@ Linegraph.prototype.show = function() { if (!this.dom.frame.parentNode) { this.body.dom.center.appendChild(this.dom.frame); } - - // show labelset containing labels - if (!this.dom.axis.parentNode) { - this.body.dom.left.appendChild(this.dom.axis); - } }; @@ -3229,7 +3210,7 @@ Linegraph.prototype.setItems = function(items) { // ids = this.itemsData.getIds(); // this._onAdd(ids); } - this.updateGraph(); + this.redraw(); }; @@ -3257,7 +3238,11 @@ Linegraph.prototype._onUpdate = function(ids) { * @return {boolean} Returns true if the component is resized */ Linegraph.prototype.redraw = function() { - console.log('redraw') + var resized = false; + + if (this.lastWidth === undefined && this.width) { + resized = true; + } // check whether zoomed (in that case we need to re-stack everything) var visibleInterval = this.body.range.end - this.body.range.start; var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.width != this.lastWidth); @@ -3268,10 +3253,10 @@ Linegraph.prototype.redraw = function() { this.width = this.dom.frame.offsetWidth; // check if this component is resized - var resized = this._isResized(); + resized = this._isResized() || resized; if (resized) { - this.svg.style.width = asSize(3*this.width); - this.svg.style.left = asSize(-this.width); + this.svg.style.width = util.option.asSize(3*this.width); + this.svg.style.left = util.option.asSize(-this.width); } if (zoomed) { this.updateGraph(); @@ -3281,19 +3266,12 @@ Linegraph.prototype.redraw = function() { Linegraph.prototype._extractData = function(dataset) { var extractedData = []; + var low = dataset[0].y; var high = dataset[0].y; - var range = this.body.range.end - this.body.range.start; - var rangePerPixel = range/this.width; - var rangePerPixelInv = this.width/range; - var xOffset = -this.body.range.start + this.width*rangePerPixel; - for (var i = 0; i < dataset.length; i++) { - var val = new Date(dataset[i].x).getTime(); - val += xOffset; - val *= rangePerPixelInv; - + var val = this.body.util.toScreen(new Date(dataset[i].x)) + this.width; extractedData.push({x:val, y:dataset[i].y}); if (low > dataset[i].y) { @@ -3318,16 +3296,16 @@ Linegraph.prototype.updateGraph = function() { var data = dataset.data; this.yAxis.setRange({start:dataset.range.low,end:dataset.range.high}); - this.yAxis.repaint(); + this.yAxis.redraw(); data = this.yAxis.convertValues(data); var d, d2, d3; d = this._catmullRom(data,0.5); - d3 = this._catmullRom(data,0); - d2 = this._catmullRom(data,1); + d2 = this._catmullRom(data,0); + d3 = this._linear(data); - this.path.setAttributeNS(null, "d",d); - this.path2.setAttributeNS(null, "d",d2); +// this.path.setAttributeNS(null, "d",d); +// this.path2.setAttributeNS(null, "d",d2); this.path3.setAttributeNS(null, "d",d3); } } @@ -3487,7 +3465,6 @@ Linegraph.prototype._linear = function(data) { function DataStep(start, end, minimumStep, containerHeight) { // variables this.current = 0; - this.containerHeight = containerHeight; this.autoScale = true; this.stepIndex = 0; @@ -3500,7 +3477,7 @@ function DataStep(start, end, minimumStep, containerHeight) { this.majorSteps = [1, 2, 5, 10]; this.minorSteps = [0.25, 0.5, 1, 2]; - this.setRange(start,end,minimumStep, containerHeight); + this.setRange(start, end, minimumStep, containerHeight); } @@ -3619,9 +3596,6 @@ DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) { break; } } - - - this.stepIndex = minorStepIdx; this.scale = magnitudefactor; this.step = magnitudefactor * this.minorSteps[minorStepIdx]; diff --git a/examples/Graph2d/01_basic.html b/examples/Graph2d/01_basic.html index b0e4355d..5e67b94b 100644 --- a/examples/Graph2d/01_basic.html +++ b/examples/Graph2d/01_basic.html @@ -18,12 +18,12 @@