Browse Source

its working! woohoo!

css_transitions
Alex de Mulder 10 years ago
parent
commit
7767a45ab3
7 changed files with 306 additions and 358 deletions
  1. +3
    -3
      dist/vis.css
  2. +148
    -174
      dist/vis.js
  3. +6
    -6
      examples/Graph2d/01_basic.html
  4. +1
    -5
      src/timeline/DataStep.js
  5. +101
    -76
      src/timeline/component/DataAxis.js
  6. +44
    -91
      src/timeline/component/Linegraph.js
  7. +3
    -3
      src/timeline/component/css/dataaxis.css

+ 3
- 3
dist/vis.css View File

@ -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;
}

+ 148
- 174
dist/vis.js View File

@ -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];

+ 6
- 6
examples/Graph2d/01_basic.html View File

@ -18,12 +18,12 @@
<script type="text/javascript">
var container = document.getElementById('visualization');
var items = [
{id: 1, content: 'item 1', x: '2014-06-01', y: 10},
{id: 2, content: 'item 2', x: '2014-06-02', y: 20},
{id: 3, content: 'item 3', x: '2014-06-03', y: 30},
{id: 4, content: 'item 4', x: '2014-06-04', y: 10},
{id: 5, content: 'item 5', x: '2014-06-05', y: 20},
{id: 6, content: 'item 6', x: '2014-06-06', y: 30}
{x: '2014-06-11', y: 10},
{x: '2014-06-12', y: 25},
{x: '2014-06-13', y: 30},
{x: '2014-06-14', y: 10},
{x: '2014-06-15', y: 15},
{x: '2014-06-16', y: 30}
];
var options = {};
var graph2d = new vis.Graph2d(container, items, options);

+ 1
- 5
src/timeline/DataStep.js View File

@ -27,7 +27,6 @@
function DataStep(start, end, minimumStep, containerHeight) {
// variables
this.current = 0;
this.containerHeight = containerHeight;
this.autoScale = true;
this.stepIndex = 0;
@ -40,7 +39,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);
}
@ -159,9 +158,6 @@ DataStep.prototype.setMinimumStep = function(minimumStep, containerHeight) {
break;
}
}
this.stepIndex = minorStepIdx;
this.scale = magnitudefactor;
this.step = magnitudefactor * this.minorSteps[minorStepIdx];

+ 101
- 76
src/timeline/component/DataAxis.js View File

@ -5,9 +5,19 @@
* @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: [],
@ -20,23 +30,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
@ -44,71 +48,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';
@ -124,26 +152,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;
@ -155,7 +183,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;
@ -167,7 +195,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']) {
@ -175,12 +203,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();
@ -189,9 +217,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']) {
@ -200,7 +225,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);
}
}
@ -225,7 +250,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
}
/**
@ -235,7 +260,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();
@ -245,7 +270,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);
@ -271,7 +296,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();
@ -281,7 +306,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);
@ -306,7 +331,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();
@ -314,7 +339,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);
@ -336,7 +361,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();
@ -344,7 +369,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);
@ -372,12 +397,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)) {
@ -385,12 +410,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);
}
};
@ -400,6 +425,6 @@ 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);
};

+ 44
- 91
src/timeline/component/Linegraph.js View File

@ -69,25 +69,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();
@ -124,6 +131,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");
@ -148,16 +156,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();
};
@ -165,45 +170,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
}
};
@ -216,11 +182,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);
}
};
/**
@ -232,11 +193,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);
}
};
@ -284,7 +240,7 @@ Linegraph.prototype.setItems = function(items) {
// ids = this.itemsData.getIds();
// this._onAdd(ids);
}
this.updateGraph();
this.redraw();
};
@ -312,7 +268,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);
@ -323,10 +283,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();
@ -336,19 +296,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) {
@ -373,16 +326,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);
}
}

+ 3
- 3
src/timeline/component/css/dataaxis.css View File

@ -1,5 +1,5 @@
.vis.timeline .dataaxis .grid.horizontal {
.vis.timeline .vispanel.background.horizontal .grid.horizontal {
position: absolute;
left: 0;
width: 100%;
@ -7,11 +7,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;
}

Loading…
Cancel
Save