Browse Source

structure, bugs, barchart, icons

css_transitions
Alex de Mulder 10 years ago
parent
commit
6ad2f0e8ef
10 changed files with 435 additions and 539 deletions
  1. +1
    -1
      Jakefile.js
  2. +1
    -0
      dist/vis.css
  3. +216
    -269
      dist/vis.js
  4. +61
    -8
      src/DOMutil.js
  5. +0
    -22
      src/timeline/DataStep.js
  6. +53
    -127
      src/timeline/component/DataAxis.js
  7. +20
    -13
      src/timeline/component/GraphGroup.js
  8. +6
    -6
      src/timeline/component/Legend.js
  9. +76
    -93
      src/timeline/component/Linegraph.js
  10. +1
    -0
      src/timeline/component/css/pathStyles.css

+ 1
- 1
Jakefile.js View File

@ -64,7 +64,7 @@ task('build', {async: true}, function () {
'./src/shim.js',
'./src/util.js',
'./src/SVGutil.js',
'./src/DOMutil.js',
'./src/DataSet.js',
'./src/DataView.js',

+ 1
- 0
dist/vis.css View File

@ -450,6 +450,7 @@
.vis.timeline .bar {
fill-opacity:0.7;
stroke-width:1px;
}
.vis.timeline .point {

+ 216
- 269
dist/vis.js View File

@ -1370,13 +1370,13 @@ util._mergeOptions = function (mergeTarget, options, option) {
* Created by Alex on 6/20/14.
*/
var SVGutil = {}
var DOMutil = {}
/**
* this prepares the JSON container for allocating SVG elements
* @param JSONcontainer
* @private
*/
SVGutil._prepareSVGElements = function(JSONcontainer) {
DOMutil.prepareElements = function(JSONcontainer) {
// cleanup the redundant svgElements;
for (var elementType in JSONcontainer) {
if (JSONcontainer.hasOwnProperty(elementType)) {
@ -1393,7 +1393,7 @@ SVGutil._prepareSVGElements = function(JSONcontainer) {
* @param JSONcontainer
* @private
*/
SVGutil._cleanupSVGElements = function(JSONcontainer) {
DOMutil.cleanupElements = function(JSONcontainer) {
// cleanup the redundant svgElements;
for (var elementType in JSONcontainer) {
if (JSONcontainer.hasOwnProperty(elementType)) {
@ -1415,7 +1415,7 @@ SVGutil._cleanupSVGElements = function(JSONcontainer) {
* @returns {*}
* @private
*/
SVGutil._getSVGElement = function (elementType, JSONcontainer, svgContainer) {
DOMutil.getSVGElement = function (elementType, JSONcontainer, svgContainer) {
var element;
// allocate SVG element, if it doesnt yet exist, create one.
if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before
@ -1441,6 +1441,43 @@ SVGutil._getSVGElement = function (elementType, JSONcontainer, svgContainer) {
};
/**
* Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
* the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
*
* @param elementType
* @param JSONcontainer
* @param svgContainer
* @returns {*}
* @private
*/
DOMutil.getDOMElement = function (elementType, JSONcontainer, DOMContainer) {
var element;
// allocate SVG element, if it doesnt yet exist, create one.
if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before
// check if there is an redundant element
if (JSONcontainer[elementType].redundant.length > 0) {
element = JSONcontainer[elementType].redundant[0];
JSONcontainer[elementType].redundant.shift()
}
else {
// create a new element and add it to the SVG
element = document.createElement(elementType);
DOMContainer.appendChild(element);
}
}
else {
// create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
element = document.createElement(elementType);
JSONcontainer[elementType] = {used: [], redundant: []};
DOMContainer.appendChild(element);
}
JSONcontainer[elementType].used.push(element);
return element;
};
/**
* draw a point object. this is a seperate function because it can also be called by the legend.
@ -1454,17 +1491,17 @@ SVGutil._getSVGElement = function (elementType, JSONcontainer, svgContainer) {
* @param svgContainer
* @returns {*}
*/
SVGutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
DOMutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
var point;
if (group.options.drawPoints.style == 'circle') {
point = SVGutil._getSVGElement('circle',JSONcontainer,svgContainer);
point = DOMutil.getSVGElement('circle',JSONcontainer,svgContainer);
point.setAttributeNS(null, "cx", x);
point.setAttributeNS(null, "cy", y);
point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size);
point.setAttributeNS(null, "class", group.className + " point");
}
else {
point = SVGutil._getSVGElement('rect',JSONcontainer,svgContainer);
point = DOMutil.getSVGElement('rect',JSONcontainer,svgContainer);
point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size);
point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size);
point.setAttributeNS(null, "width", group.options.drawPoints.size);
@ -1472,7 +1509,23 @@ SVGutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
point.setAttributeNS(null, "class", group.className + " point");
}
return point;
}
};
/**
* draw a bar SVG element
*
* @param x
* @param y
* @param className
*/
DOMutil.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) {
rect = DOMutil.getSVGElement('rect',JSONcontainer, svgContainer);
rect.setAttributeNS(null, "x", Math.round(x - 0.5 * width));
rect.setAttributeNS(null, "y", Math.round(y));
rect.setAttributeNS(null, "width", width);
rect.setAttributeNS(null, "height", height);
rect.setAttributeNS(null, "class", className);
};
/**
* DataSet
*
@ -2735,20 +2788,21 @@ GraphGroup.prototype.update = function(group) {
GraphGroup.prototype.drawIcon = function(x,y,JSONcontainer, SVGcontainer, iconWidth, iconHeight) {
var fillHeight = iconHeight * 0.5;
var path, fillPath, outline;
var path, fillPath;
var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer);
outline.setAttributeNS(null, "x", x);
outline.setAttributeNS(null, "y", y - fillHeight);
outline.setAttributeNS(null, "width", iconWidth);
outline.setAttributeNS(null, "height", 2*fillHeight);
outline.setAttributeNS(null, "class", "outline");
if (this.options.style == 'line') {
outline = SVGutil._getSVGElement("rect", JSONcontainer, SVGcontainer);
outline.setAttributeNS(null, "x", x);
outline.setAttributeNS(null, "y", y - fillHeight);
outline.setAttributeNS(null, "width", iconWidth);
outline.setAttributeNS(null, "height", 2*fillHeight);
outline.setAttributeNS(null, "class", "outline");
path = SVGutil._getSVGElement("path", JSONcontainer, SVGcontainer);
path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer);
path.setAttributeNS(null, "class", this.className);
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+"");
if (this.options.shaded.enabled == true) {
fillPath = SVGutil._getSVGElement("path", JSONcontainer, SVGcontainer);
fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer);
if (this.options.shaded.orientation == 'top') {
fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) +
"L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight));
@ -2763,12 +2817,18 @@ GraphGroup.prototype.drawIcon = function(x,y,JSONcontainer, SVGcontainer, iconWi
}
if (this.options.drawPoints.enabled == true) {
SVGutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer);
DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer);
}
}
else {
console.log("bar")
//TODO: bars
var barWidth = Math.round(0.3 * iconWidth);
var bar1Height = Math.round(0.4 * iconHeight);
var bar2Height = Math.round(0.75 * iconHeight);
var offset = Math.round((iconWidth - (2 * barWidth))/3);
DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer);
DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer);
}
}
@ -2868,7 +2928,7 @@ Legend.prototype.redraw = function() {
}
Legend.prototype.drawLegend = function() {
this.linegraph._prepareSVGElements.call(this,this.svgElements);
this.linegraph.prepareElements.call(this,this.svgElements);
var x = 0;
var y = 0;
var lineLength = 30;
@ -2878,7 +2938,7 @@ Legend.prototype.drawLegend = function() {
var legendWidth = 298;
var padding = 5;
var border = this._getSVGElement("rect", this.svgLegendElements, this.svgLegend);
var border = this.getSVGElement("rect", this.svgLegendElements, this.svgLegend);
border.setAttributeNS(null, "x", x);
border.setAttributeNS(null, "y", y);
border.setAttributeNS(null, "width", legendWidth);
@ -2889,18 +2949,18 @@ Legend.prototype.drawLegend = function() {
if (classes.length > 0) {
for (var i = 0; i < classes.length; i++) {
outline = this._getSVGElement("rect", this.svgLegendElements, this.svgLegend);
outline = this.getSVGElement("rect", this.svgLegendElements, this.svgLegend);
outline.setAttributeNS(null, "x", x);
outline.setAttributeNS(null, "y", y - fillHeight);
outline.setAttributeNS(null, "width", lineLength);
outline.setAttributeNS(null, "height", 2*fillHeight);
outline.setAttributeNS(null, "class", "outline");
path = this._getSVGElement("path", this.svgLegendElements, this.svgLegend);
path = this.getSVGElement("path", this.svgLegendElements, this.svgLegend);
path.setAttributeNS(null, "class", classes[i]);
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + lineLength) + ","+y+"");
if (this.options.shaded.enabled == true) {
fillPath = this._getSVGElement("path", this.svgLegendElements, this.svgLegend);
fillPath = this.getSVGElement("path", this.svgLegendElements, this.svgLegend);
if (this.options.shaded.orientation == 'top') {
fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) +
"L"+x+","+y+" L"+ (x + lineLength) + ","+y+" L"+ (x + lineLength) + "," + (y - fillHeight));
@ -2926,7 +2986,7 @@ Legend.prototype.drawLegend = function() {
this._cleanupSVGElements(this.svgLegendElements);
this.cleanupElements(this.svgLegendElements);
}
/**
* A horizontal time axis
@ -2934,6 +2994,7 @@ Legend.prototype.drawLegend = function() {
* options.
* @constructor DataAxis
* @extends Component
* @param body
*/
function DataAxis (body, options) {
this.id = util.randomUUID();
@ -2954,15 +3015,13 @@ function DataAxis (body, options) {
};
this.props = {};
this.dom = {
lines: [],
labels: [],
redundant: {
lines: [],
labels: []
}
this.DOMelements = { // dynamic elements
lines: {},
labels: {}
};
this.dom = {};
this.yRange = {start:0, end:0};
this.options = util.extend({}, this.defaultOptions);
@ -3006,7 +3065,7 @@ DataAxis.prototype.deleteGroup = function(label) {
};
DataAxis.prototype.setOptions = function(options) {
DataAxis.prototype.setOptions = function (options) {
if (options) {
var redraw = false;
if (this.options.orientation != options.orientation && options.orientation !== undefined) {
@ -3030,7 +3089,7 @@ DataAxis.prototype.setOptions = function(options) {
this.show();
}
}
}
};
/**
@ -3055,14 +3114,15 @@ DataAxis.prototype._create = function() {
this.dom.frame.appendChild(this.svg);
};
DataAxis.prototype._redrawGroupIcons = function() {
SVGutil._prepareSVGElements(this.svgContainer);
DataAxis.prototype._redrawGroupIcons = function () {
DOMutil.prepareElements(this.svgElements);
var x;
var iconWidth = this.options.iconWidth;
var iconHeight = 15;
var iconOffset = 4;
var y = iconOffset + 0.5 * iconHeight;
if (this.options.orientation == 'left') {
x = iconOffset;
}
@ -3077,8 +3137,8 @@ DataAxis.prototype._redrawGroupIcons = function() {
}
}
SVGutil._cleanupSVGElements(this.svgContainer);
}
DOMutil.cleanupElements(this.svgElements);
};
/**
* Create the HTML DOM for the DataAxis
@ -3113,14 +3173,13 @@ DataAxis.prototype.hide = function() {
/**
* Set a range (start and end)
* @param {Range | Object} range A Range or an object containing start and end.
* @param end
* @param start
* @param 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;
DataAxis.prototype.setRange = function (start, end) {
this.yRange.start = start;
this.yRange.end = end;
};
/**
@ -3147,7 +3206,7 @@ DataAxis.prototype.redraw = function () {
props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2*this.options.minorLinesOffset;
props.minorLineHeight = 1;
props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2*this.options.majorLinesOffset;;
props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset;
props.majorLineHeight = 1;
// take frame offline while updating (is almost twice as fast)
@ -3177,26 +3236,19 @@ DataAxis.prototype.redraw = function () {
* @private
*/
DataAxis.prototype._redrawLabels = function () {
DOMutil.prepareElements(this.DOMelements);
var orientation = this.options['orientation'];
// calculate range and step (step such that we have space for 7 characters per label)
var start = this.yRange.start;
var end = this.yRange.end;
var minimumStep = (this.props.minorCharHeight || 10); //in pixels
var minimumStep = (this.props.majorCharHeight || 10); //in pixels
var step = new DataStep(start, end, minimumStep, this.dom.frame.offsetHeight);
this.step = step;
step.first();
// 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 _redrawLabels, left over elements will be cleaned up
var dom = this.dom;
dom.redundant.lines = dom.lines;
dom.redundant.labels = dom.labels;
dom.lines = [];
dom.labels = [];
// get the distance in pixels for a step
var stepPixels = this.dom.frame.offsetHeight / ((step.marginRange / step.step) + 1);
this.stepPixels = stepPixels;
@ -3212,7 +3264,6 @@ DataAxis.prototype._redrawLabels = function () {
amountOfSteps = this.height / stepPixels;
}
var xFirstMajorLabel = undefined;
this.valueAtZero = step.marginEnd;
var marginStartPos = 0;
@ -3227,21 +3278,18 @@ DataAxis.prototype._redrawLabels = function () {
marginStartPos = max * stepPixels;
var isMajor = step.isMajor();
if (this.options['showMinorLabels'] && isMajor == false) {
if (this.options['showMinorLabels'] && isMajor == false || this.master == false) {
this._redrawLabel(y - 2, step.current, orientation, 'yAxis minor', this.props.minorCharHeight);
}
if (isMajor && this.options['showMajorLabels']) {
if (isMajor && this.options['showMajorLabels'] && this.master == true) {
if (y >= 0) {
if (xFirstMajorLabel == undefined) {
xFirstMajorLabel = y;
}
this._redrawLabel(y - 2, step.current, orientation, 'yAxis major', this.props.majorCharHeight);
}
this._redrawMajorLine(y, orientation);
this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth);
}
else {
this._redrawMinorLine(y, orientation);
this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth);
}
step.next();
@ -3260,55 +3308,23 @@ DataAxis.prototype._redrawLabels = function () {
this.conversionFactor = marginStartPos/((amountOfSteps-1) * step.step);
// Cleanup leftover DOM elements from the redundant list
util.forEach(this.dom.redundant, function (arr) {
while (arr.length) {
var elem = arr.pop();
if (elem && elem.parentNode) {
elem.parentNode.removeChild(elem);
}
}
});
DOMutil.cleanupElements(this.DOMelements);
};
DataAxis.prototype.convertValues = function(data) {
for (var i = 0; i < data.length; i++) {
data[i].y = this.convertValue(data[i].y);
}
return data;
}
DataAxis.prototype.convertValue = function(value) {
var invertedValue = this.valueAtZero - value;
var convertedValue = invertedValue * this.conversionFactor;
return convertedValue; // the -2 is to compensate for the borders
}
/**
* Create a label for the axis at position x
* @param {Number} x
* @param {String} text
* @param {String} orientation "top" or "bottom" (default)
* @private
* @param y
* @param text
* @param orientation
* @param className
* @param characterHeight
*/
DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) {
// reuse redundant label
var label = this.dom.redundant.labels.shift();
if (!label) {
// create label
var content = document.createTextNode(text);
label = document.createElement('div');
label.className = className;
label.appendChild(content);
this.dom.frame.appendChild(label);
}
this.dom.labels.push(label);
label.childNodes[0].nodeValue = text;
//label.title = title; // TODO: this is a heavy operation
var label = DOMutil.getDOMElement('div',this.DOMelements, this.dom.frame); //this.dom.redundant.labels.shift();
label.className = className;
label.innerHTML = text;
if (orientation == 'left') {
label.style.left = '-' + this.options.labelOffsetX + 'px';
@ -3327,70 +3343,40 @@ DataAxis.prototype._redrawLabel = function (y, text, orientation, className, cha
if (this.maxLabelSize < text.length * largestWidth) {
this.maxLabelSize = text.length * largestWidth;
}
};
/**
* Create a minor line for the axis at position y
* @param {Number} y
* @param {String} orientation "top" or "bottom" (default)
* @private
* @param y
* @param orientation
* @param className
* @param offset
* @param width
*/
DataAxis.prototype._redrawMinorLine = function (y, orientation) {
DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) {
if (this.master == true) {
// reuse redundant line
var line = this.dom.redundant.lines.shift();
if (!line) {
// create vertical line
line = document.createElement('div');
line.className = 'grid horizontal minor';
this.dom.lineContainer.appendChild(line);
}
this.dom.lines.push(line);
var line = DOMutil.getDOMElement('div',this.DOMelements, this.dom.lineContainer);//this.dom.redundant.lines.shift();
line.className = className;
line.innerHTML = '';
var props = this.props;
if (orientation == 'left') {
line.style.left = (this.width - this.options.minorLinesOffset) + 'px';
line.style.left = (this.width - offset) + 'px';
}
else {
line.style.left = -1*(this.width - this.options.minorLinesOffset) + 'px';
line.style.left = -1*(this.width - offset) + 'px';
}
line.style.width = props.minorLineWidth + 'px';
line.style.width = width + 'px';
line.style.top = y + 'px';
}
};
/**
* Create a Major line for the axis at position x
* @param {Number} x
* @param {String} orientation "top" or "bottom" (default)
* @private
*/
DataAxis.prototype._redrawMajorLine = function (y, orientation) {
if (this.master == true) {
// reuse redundant line
var line = this.dom.redundant.lines.shift();
if (!line) {
// create vertical line
line = document.createElement('div');
line.className = 'grid horizontal major';
this.dom.lineContainer.appendChild(line);
}
this.dom.lines.push(line);
if (orientation == 'left') {
line.style.left = (this.width - this.options.majorLinesOffset) + 'px';
}
else {
line.style.left = -1*(this.width - this.options.majorLinesOffset) + 'px';
}
line.style.top = y + 'px';
line.style.width = this.props.majorLineWidth + 'px';
}
DataAxis.prototype.convertValue = function (value) {
var invertedValue = this.valueAtZero - value;
var convertedValue = invertedValue * this.conversionFactor;
return convertedValue; // the -2 is to compensate for the borders
};
@ -3538,7 +3524,6 @@ function Linegraph(body, options) {
this.setOptions(options);
this.groupsUsingDefaultStyles = [0];
var me = this;
this.body.emitter.on("rangechange",function() {
if (me.lastStart != 0) {
var offset = me.body.range.start - me.lastStart;
@ -3756,27 +3741,7 @@ Linegraph.prototype._onRemove = Linegraph.prototype._onUpdate;
Linegraph.prototype._onUpdateGroups = function (groupIds) {
for (var i = 0; i < groupIds.length; i++) {
var group = this.groupsData.get(groupIds[i]);
if (!this.groups.hasOwnProperty(groupIds[i])) {
this.groups[groupIds[i]] = new GraphGroup(group, this.options, this.groupsUsingDefaultStyles);
this.legend.addGroup(groupIds[i],this.groups[groupIds[i]]);
if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') {
this.yAxisRight.addGroup(groupIds[i], this.groups[groupIds[i]]);
}
else {
this.yAxisLeft.addGroup(groupIds[i], this.groups[groupIds[i]]);
}
}
else {
this.groups[groupIds[i]].update(group);
this.legend.updateGroup(groupIds[i],this.groups[groupIds[i]]);
if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') {
this.yAxisRight.updateGroup(groupIds[i], this.groups[groupIds[i]]);
}
else {
this.yAxisLeft.updateGroup(groupIds[i], this.groups[groupIds[i]]);
}
}
this._updateGroup(group, groupIds[i]);
}
this._updateUngrouped();
this._updateGraph();
@ -3793,34 +3758,40 @@ Linegraph.prototype._onRemoveGroups = function (groupIds) {
this.redraw();
};
/**
* Create or delete the group holding all ungrouped items. This group is used when
* there are no groups specified. This anonymous group is called 'graph'.
* @protected
*/
Linegraph.prototype._updateUngrouped = function() {
var group = {id: UNGROUPED, content: "graph"};
if (!this.groups.hasOwnProperty(UNGROUPED)) {
this.groups[UNGROUPED] = new GraphGroup(group, this.options, this.groupsUsingDefaultStyles);
this.legend.addGroup(UNGROUPED,this.groups[UNGROUPED]);
if (this.groups[UNGROUPED].options.yAxisOrientation == 'right') {
this.yAxisRight.addGroup(UNGROUPED, this.groups[UNGROUPED]);
Linegraph.prototype._updateGroup = function (group, groupId) {
if (!this.groups.hasOwnProperty(groupId)) {
this.groups[groupId] = new GraphGroup(group, this.options, this.groupsUsingDefaultStyles);
this.legend.addGroup(groupId, this.groups[groupId]);
if (this.groups[groupId].options.yAxisOrientation == 'right') {
this.yAxisRight.addGroup(groupId, this.groups[groupId]);
}
else {
this.yAxisLeft.addGroup(UNGROUPED, this.groups[UNGROUPED]);
this.yAxisLeft.addGroup(groupId, this.groups[groupId]);
}
}
else {
this.groups[UNGROUPED].update(group);
this.legend.updateGroup(UNGROUPED,this.groups[UNGROUPED]);
if (this.groups[UNGROUPED].options.yAxisOrientation == 'right') {
this.yAxisRight.updateGroup(UNGROUPED, this.groups[UNGROUPED]);
this.groups[groupId].update(group);
this.legend.updateGroup(groupId, this.groups[groupId]);
if (this.groups[groupId].options.yAxisOrientation == 'right') {
this.yAxisRight.updateGroup(groupId, this.groups[groupId]);
}
else {
this.yAxisLeft.updateGroup(UNGROUPED, this.groups[UNGROUPED]);
this.yAxisLeft.updateGroup(groupId, this.groups[groupId]);
}
}
};
/**
* Create or delete the group holding all ungrouped items. This group is used when
* there are no groups specified. This anonymous group is called 'graph'.
* @protected
*/
Linegraph.prototype._updateUngrouped = function() {
var group = {id: UNGROUPED, content: "graph"};
this._updateGroup(group, UNGROUPED);
if (this.itemsData != null) {
var datapoints = this.itemsData.get({
@ -3885,7 +3856,7 @@ Linegraph.prototype.redraw = function() {
*/
Linegraph.prototype._updateGraph = function () {
// reset the svg elements
SVGutil._prepareSVGElements(this.svgElements);
DOMutil.prepareElements(this.svgElements);
if (this.width != 0 && this.itemsData != null) {
// look at different lines
@ -3898,10 +3869,8 @@ Linegraph.prototype._updateGraph = function () {
}
}
// this.legend.redraw();
// cleanup unused svg elements
SVGutil._cleanupSVGElements(this.svgElements);
DOMutil.cleanupElements(this.svgElements);
};
/**
@ -3909,7 +3878,7 @@ Linegraph.prototype._updateGraph = function () {
* @param {array} groupIds
* @private
*/
Linegraph.prototype._updateYAxis = function(groupIds) {
Linegraph.prototype._updateYAxis = function (groupIds) {
var yAxisLeftUsed = false;
var yAxisRightUsed = false;
var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal;
@ -3925,28 +3894,38 @@ Linegraph.prototype._updateYAxis = function(groupIds) {
orientation = 'right';
}
var view = new vis.DataSet(this.itemsData.get({filter: function (item) {return item.group == groupIds[i];}}));
var view = new vis.DataSet(this.itemsData.get({filter: function (item) {
return item.group == groupIds[i];
}}));
minVal = view.min("y").y;
maxVal = view.max("y").y;
if (orientation == 'left') {
yAxisLeftUsed = true;
if (minLeft > minVal) {minLeft = minVal;}
if (maxLeft < maxVal) {maxLeft = maxVal;}
if (minLeft > minVal) {
minLeft = minVal;
}
if (maxLeft < maxVal) {
maxLeft = maxVal;
}
}
else {
yAxisRightUsed = true;
if (minRight > minVal) {minRight = minVal;}
if (maxRight < maxVal) {maxRight = maxVal;}
if (minRight > minVal) {
minRight = minVal;
}
if (maxRight < maxVal) {
maxRight = maxVal;
}
}
delete view;
}
if (yAxisLeftUsed == true) {
this.yAxisLeft.setRange({start: minLeft, end: maxLeft});
if (yAxisLeftUsed == true) {
this.yAxisLeft.setRange(minLeft, maxLeft);
}
if (yAxisRightUsed == true) {
this.yAxisRight.setRange({start: minRight, end: maxRight});
this.yAxisRight.setRange(minRight, maxRight);
}
}
@ -3957,11 +3936,11 @@ Linegraph.prototype._updateYAxis = function(groupIds) {
}
if (yAxisRightUsed == true && yAxisLeftUsed == true) {
this.yAxisLeft.drawIcons = true;
this.yAxisLeft.drawIcons = true;
this.yAxisRight.drawIcons = true;
}
else {
this.yAxisLeft.drawIcons = false;
this.yAxisLeft.drawIcons = false;
this.yAxisRight.drawIcons = false;
}
@ -3978,17 +3957,17 @@ Linegraph.prototype._updateYAxis = function(groupIds) {
else {
this.yAxisRight.redraw();
}
}
};
/**
* This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function
*
* @param {boolean} axisUsed
* @param {DataAxis object} axis
* @returns {boolean}
* @private
* @param axis
*/
Linegraph.prototype._toggleAxisVisiblity = function(axisUsed, axis) {
Linegraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) {
var changed = false;
if (axisUsed == false) {
if (axis.dom.frame.parentNode) {
@ -4003,7 +3982,7 @@ Linegraph.prototype._toggleAxisVisiblity = function(axisUsed, axis) {
}
}
return changed;
}
};
/**
@ -4023,24 +4002,30 @@ Linegraph.prototype._drawGraph = function (groupId, groupIndex, amountOfGraphs)
this._drawLineGraph(datapoints, group);
}
else {
this._drawBarGraph(datapoints, group, amountOfGraphs);
this._drawBarGraph(datapoints, group);
}
};
/**
* draw a bar graph
* @param datapoints
* @param options
* @param amountOfGraphs
* @param group
*/
Linegraph.prototype._drawBarGraph = function (datapoints, group, amountOfGraphs) {
Linegraph.prototype._drawBarGraph = function (datapoints, group) {
if (datapoints != null) {
if (datapoints.length > 0) {
var dataset = this._prepareData(datapoints, group.options);
var bar;
console.log(group.options);
var width, coreDistance;
var minWidth = 0.1 * group.options.barChart.width;
for (var i = 0; i < dataset.length; i++) {
this._drawBar(dataset[i].x, dataset[i].y, group.options.barChart.width, this.zeroPosition - dataset[i].y, group.className + ' bar');
width = group.options.barChart.width;
// dynammically downscale the width so there is no overlap up to 1/10th the original width
if (i+1 < dataset.length) {coreDistance = Math.abs(dataset[i+1].x - dataset[i].x);}
if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(dataset[i-1].x - dataset[i].x));}
if (coreDistance < width) {width = coreDistance < minWidth ? minWidth : coreDistance;}
DOMutil.drawBar(dataset[i].x, dataset[i].y, width, this.zeroPosition - dataset[i].y, group.className + ' bar', this.svgElements, this.svg);
}
// draw points
@ -4051,29 +4036,12 @@ Linegraph.prototype._drawBarGraph = function (datapoints, group, amountOfGraphs)
}
};
/**
* draw a bar SVG element
*
* @param x
* @param y
* @param className
*/
Linegraph.prototype._drawBar = function (x, y, width, height, className) {
rect = SVGutil._getSVGElement('rect',this.svgElements, this.svg);
rect.setAttributeNS(null, "x", x - 0.5 * width);
rect.setAttributeNS(null, "y", y);
rect.setAttributeNS(null, "width", width);
rect.setAttributeNS(null, "height", height);
rect.setAttributeNS(null, "class", className);
};
/**
* draw a line graph
*
*
* @param datapoints
* @param options
* @param group
*/
Linegraph.prototype._drawLineGraph = function (datapoints, group) {
if (datapoints != null) {
@ -4081,7 +4049,7 @@ Linegraph.prototype._drawLineGraph = function (datapoints, group) {
var dataset = this._prepareData(datapoints, group.options);
var path, d;
path = SVGutil._getSVGElement('path', this.svgElements, this.svg);
path = DOMutil.getSVGElement('path', this.svgElements, this.svg);
path.setAttributeNS(null, "class", group.className);
@ -4095,12 +4063,13 @@ Linegraph.prototype._drawLineGraph = function (datapoints, group) {
// append with points for fill and finalize the path
if (group.options.shaded.enabled == true) {
var fillPath = SVGutil._getSVGElement('path',this.svgElements, this.svg);
var fillPath = DOMutil.getSVGElement('path',this.svgElements, this.svg);
var dFill;
if (group.options.shaded.orientation == 'top') {
var dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0;
dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0;
}
else {
var dFill = "M" + dataset[0].x + "," + this.svg.offsetHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + this.svg.offsetHeight;
dFill = "M" + dataset[0].x + "," + this.svg.offsetHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + this.svg.offsetHeight;
}
fillPath.setAttributeNS(null, "class", group.className + " fill");
fillPath.setAttributeNS(null, "d", dFill);
@ -4118,15 +4087,15 @@ Linegraph.prototype._drawLineGraph = function (datapoints, group) {
/**
* draw the data points
*
*
* @param dataset
* @param options
* @param JSONcontainer
* @param svg
* @param group
*/
Linegraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg) {
for (var i = 0; i < dataset.length; i++) {
SVGutil.drawPoint(dataset[i].x, dataset[i].y, group, JSONcontainer, svg);
DOMutil.drawPoint(dataset[i].x, dataset[i].y, group, JSONcontainer, svg);
}
};
@ -4152,7 +4121,7 @@ Linegraph.prototype._prepareData = function (datapoints, options) {
axis = this.yAxisRight;
}
for (var i = 0; i < datapoints.length; i++) {
xValue = toScreen(datapoints[i].x) + this.width;
xValue = toScreen(datapoints[i].x) + this.width - 1;
yValue = axis.convertValue(datapoints[i].y);
extractedData.push({x: xValue, y: yValue});
}
@ -4508,28 +4477,6 @@ DataStep.prototype.isMajor = function() {
return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0);
};
/**
* Returns formatted text for the minor axislabel, depending on the current
* date and the scale. For example when scale is MINUTE, the current time is
* formatted as "hh:mm".
* @param {Date} [date] custom date. if not provided, current date is taken
*/
DataStep.prototype.getLabelMinor = function() {
return this.current;
};
/**
* Returns formatted text for the major axis label, depending on the current
* date and the scale. For example when scale is MINUTE, the major scale is
* hours, and the hour will be formatted as "hh".
* @param {Date} [date] custom date. if not provided, current date is taken
*/
DataStep.prototype.getLabelMajor = function() {
return this.current;
};
/**
* Utility functions for ordering and stacking of items
*/

src/svgUtil.js → src/DOMutil.js View File

@ -2,13 +2,13 @@
* Created by Alex on 6/20/14.
*/
var SVGutil = {}
var DOMutil = {}
/**
* this prepares the JSON container for allocating SVG elements
* @param JSONcontainer
* @private
*/
SVGutil._prepareSVGElements = function(JSONcontainer) {
DOMutil.prepareElements = function(JSONcontainer) {
// cleanup the redundant svgElements;
for (var elementType in JSONcontainer) {
if (JSONcontainer.hasOwnProperty(elementType)) {
@ -25,7 +25,7 @@ SVGutil._prepareSVGElements = function(JSONcontainer) {
* @param JSONcontainer
* @private
*/
SVGutil._cleanupSVGElements = function(JSONcontainer) {
DOMutil.cleanupElements = function(JSONcontainer) {
// cleanup the redundant svgElements;
for (var elementType in JSONcontainer) {
if (JSONcontainer.hasOwnProperty(elementType)) {
@ -47,7 +47,7 @@ SVGutil._cleanupSVGElements = function(JSONcontainer) {
* @returns {*}
* @private
*/
SVGutil._getSVGElement = function (elementType, JSONcontainer, svgContainer) {
DOMutil.getSVGElement = function (elementType, JSONcontainer, svgContainer) {
var element;
// allocate SVG element, if it doesnt yet exist, create one.
if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before
@ -73,6 +73,43 @@ SVGutil._getSVGElement = function (elementType, JSONcontainer, svgContainer) {
};
/**
* Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
* the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
*
* @param elementType
* @param JSONcontainer
* @param svgContainer
* @returns {*}
* @private
*/
DOMutil.getDOMElement = function (elementType, JSONcontainer, DOMContainer) {
var element;
// allocate SVG element, if it doesnt yet exist, create one.
if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before
// check if there is an redundant element
if (JSONcontainer[elementType].redundant.length > 0) {
element = JSONcontainer[elementType].redundant[0];
JSONcontainer[elementType].redundant.shift()
}
else {
// create a new element and add it to the SVG
element = document.createElement(elementType);
DOMContainer.appendChild(element);
}
}
else {
// create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
element = document.createElement(elementType);
JSONcontainer[elementType] = {used: [], redundant: []};
DOMContainer.appendChild(element);
}
JSONcontainer[elementType].used.push(element);
return element;
};
/**
* draw a point object. this is a seperate function because it can also be called by the legend.
@ -86,17 +123,17 @@ SVGutil._getSVGElement = function (elementType, JSONcontainer, svgContainer) {
* @param svgContainer
* @returns {*}
*/
SVGutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
DOMutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
var point;
if (group.options.drawPoints.style == 'circle') {
point = SVGutil._getSVGElement('circle',JSONcontainer,svgContainer);
point = DOMutil.getSVGElement('circle',JSONcontainer,svgContainer);
point.setAttributeNS(null, "cx", x);
point.setAttributeNS(null, "cy", y);
point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size);
point.setAttributeNS(null, "class", group.className + " point");
}
else {
point = SVGutil._getSVGElement('rect',JSONcontainer,svgContainer);
point = DOMutil.getSVGElement('rect',JSONcontainer,svgContainer);
point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size);
point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size);
point.setAttributeNS(null, "width", group.options.drawPoints.size);
@ -104,4 +141,20 @@ SVGutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
point.setAttributeNS(null, "class", group.className + " point");
}
return point;
}
};
/**
* draw a bar SVG element
*
* @param x
* @param y
* @param className
*/
DOMutil.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) {
rect = DOMutil.getSVGElement('rect',JSONcontainer, svgContainer);
rect.setAttributeNS(null, "x", Math.round(x - 0.5 * width));
rect.setAttributeNS(null, "y", Math.round(y));
rect.setAttributeNS(null, "width", width);
rect.setAttributeNS(null, "height", height);
rect.setAttributeNS(null, "class", className);
};

+ 0
- 22
src/timeline/DataStep.js View File

@ -192,25 +192,3 @@ DataStep.prototype.snap = function(date) {
DataStep.prototype.isMajor = function() {
return (this.current % (this.scale * this.majorSteps[this.stepIndex]) == 0);
};
/**
* Returns formatted text for the minor axislabel, depending on the current
* date and the scale. For example when scale is MINUTE, the current time is
* formatted as "hh:mm".
* @param {Date} [date] custom date. if not provided, current date is taken
*/
DataStep.prototype.getLabelMinor = function() {
return this.current;
};
/**
* Returns formatted text for the major axis label, depending on the current
* date and the scale. For example when scale is MINUTE, the major scale is
* hours, and the hour will be formatted as "hh".
* @param {Date} [date] custom date. if not provided, current date is taken
*/
DataStep.prototype.getLabelMajor = function() {
return this.current;
};

+ 53
- 127
src/timeline/component/DataAxis.js View File

@ -4,6 +4,7 @@
* options.
* @constructor DataAxis
* @extends Component
* @param body
*/
function DataAxis (body, options) {
this.id = util.randomUUID();
@ -24,15 +25,13 @@ function DataAxis (body, options) {
};
this.props = {};
this.dom = {
lines: [],
labels: [],
redundant: {
lines: [],
labels: []
}
this.DOMelements = { // dynamic elements
lines: {},
labels: {}
};
this.dom = {};
this.yRange = {start:0, end:0};
this.options = util.extend({}, this.defaultOptions);
@ -76,7 +75,7 @@ DataAxis.prototype.deleteGroup = function(label) {
};
DataAxis.prototype.setOptions = function(options) {
DataAxis.prototype.setOptions = function (options) {
if (options) {
var redraw = false;
if (this.options.orientation != options.orientation && options.orientation !== undefined) {
@ -100,7 +99,7 @@ DataAxis.prototype.setOptions = function(options) {
this.show();
}
}
}
};
/**
@ -125,14 +124,15 @@ DataAxis.prototype._create = function() {
this.dom.frame.appendChild(this.svg);
};
DataAxis.prototype._redrawGroupIcons = function() {
SVGutil._prepareSVGElements(this.svgContainer);
DataAxis.prototype._redrawGroupIcons = function () {
DOMutil.prepareElements(this.svgElements);
var x;
var iconWidth = this.options.iconWidth;
var iconHeight = 15;
var iconOffset = 4;
var y = iconOffset + 0.5 * iconHeight;
if (this.options.orientation == 'left') {
x = iconOffset;
}
@ -147,8 +147,8 @@ DataAxis.prototype._redrawGroupIcons = function() {
}
}
SVGutil._cleanupSVGElements(this.svgContainer);
}
DOMutil.cleanupElements(this.svgElements);
};
/**
* Create the HTML DOM for the DataAxis
@ -183,14 +183,13 @@ DataAxis.prototype.hide = function() {
/**
* Set a range (start and end)
* @param {Range | Object} range A Range or an object containing start and end.
* @param end
* @param start
* @param 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;
DataAxis.prototype.setRange = function (start, end) {
this.yRange.start = start;
this.yRange.end = end;
};
/**
@ -217,7 +216,7 @@ DataAxis.prototype.redraw = function () {
props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2*this.options.minorLinesOffset;
props.minorLineHeight = 1;
props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2*this.options.majorLinesOffset;;
props.majorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth - this.lineOffset - this.width + 2 * this.options.majorLinesOffset;
props.majorLineHeight = 1;
// take frame offline while updating (is almost twice as fast)
@ -247,26 +246,19 @@ DataAxis.prototype.redraw = function () {
* @private
*/
DataAxis.prototype._redrawLabels = function () {
DOMutil.prepareElements(this.DOMelements);
var orientation = this.options['orientation'];
// calculate range and step (step such that we have space for 7 characters per label)
var start = this.yRange.start;
var end = this.yRange.end;
var minimumStep = (this.props.minorCharHeight || 10); //in pixels
var minimumStep = (this.props.majorCharHeight || 10); //in pixels
var step = new DataStep(start, end, minimumStep, this.dom.frame.offsetHeight);
this.step = step;
step.first();
// 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 _redrawLabels, left over elements will be cleaned up
var dom = this.dom;
dom.redundant.lines = dom.lines;
dom.redundant.labels = dom.labels;
dom.lines = [];
dom.labels = [];
// get the distance in pixels for a step
var stepPixels = this.dom.frame.offsetHeight / ((step.marginRange / step.step) + 1);
this.stepPixels = stepPixels;
@ -282,7 +274,6 @@ DataAxis.prototype._redrawLabels = function () {
amountOfSteps = this.height / stepPixels;
}
var xFirstMajorLabel = undefined;
this.valueAtZero = step.marginEnd;
var marginStartPos = 0;
@ -297,21 +288,18 @@ DataAxis.prototype._redrawLabels = function () {
marginStartPos = max * stepPixels;
var isMajor = step.isMajor();
if (this.options['showMinorLabels'] && isMajor == false) {
if (this.options['showMinorLabels'] && isMajor == false || this.master == false) {
this._redrawLabel(y - 2, step.current, orientation, 'yAxis minor', this.props.minorCharHeight);
}
if (isMajor && this.options['showMajorLabels']) {
if (isMajor && this.options['showMajorLabels'] && this.master == true) {
if (y >= 0) {
if (xFirstMajorLabel == undefined) {
xFirstMajorLabel = y;
}
this._redrawLabel(y - 2, step.current, orientation, 'yAxis major', this.props.majorCharHeight);
}
this._redrawMajorLine(y, orientation);
this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth);
}
else {
this._redrawMinorLine(y, orientation);
this._redrawLine(y, orientation, 'grid horizontal minor', this.options.minorLinesOffset, this.props.minorLineWidth);
}
step.next();
@ -330,55 +318,23 @@ DataAxis.prototype._redrawLabels = function () {
this.conversionFactor = marginStartPos/((amountOfSteps-1) * step.step);
// Cleanup leftover DOM elements from the redundant list
util.forEach(this.dom.redundant, function (arr) {
while (arr.length) {
var elem = arr.pop();
if (elem && elem.parentNode) {
elem.parentNode.removeChild(elem);
}
}
});
DOMutil.cleanupElements(this.DOMelements);
};
DataAxis.prototype.convertValues = function(data) {
for (var i = 0; i < data.length; i++) {
data[i].y = this.convertValue(data[i].y);
}
return data;
}
DataAxis.prototype.convertValue = function(value) {
var invertedValue = this.valueAtZero - value;
var convertedValue = invertedValue * this.conversionFactor;
return convertedValue; // the -2 is to compensate for the borders
}
/**
* Create a label for the axis at position x
* @param {Number} x
* @param {String} text
* @param {String} orientation "top" or "bottom" (default)
* @private
* @param y
* @param text
* @param orientation
* @param className
* @param characterHeight
*/
DataAxis.prototype._redrawLabel = function (y, text, orientation, className, characterHeight) {
// reuse redundant label
var label = this.dom.redundant.labels.shift();
if (!label) {
// create label
var content = document.createTextNode(text);
label = document.createElement('div');
label.className = className;
label.appendChild(content);
this.dom.frame.appendChild(label);
}
this.dom.labels.push(label);
label.childNodes[0].nodeValue = text;
//label.title = title; // TODO: this is a heavy operation
var label = DOMutil.getDOMElement('div',this.DOMelements, this.dom.frame); //this.dom.redundant.labels.shift();
label.className = className;
label.innerHTML = text;
if (orientation == 'left') {
label.style.left = '-' + this.options.labelOffsetX + 'px';
@ -397,70 +353,40 @@ DataAxis.prototype._redrawLabel = function (y, text, orientation, className, cha
if (this.maxLabelSize < text.length * largestWidth) {
this.maxLabelSize = text.length * largestWidth;
}
};
/**
* Create a minor line for the axis at position y
* @param {Number} y
* @param {String} orientation "top" or "bottom" (default)
* @private
* @param y
* @param orientation
* @param className
* @param offset
* @param width
*/
DataAxis.prototype._redrawMinorLine = function (y, orientation) {
DataAxis.prototype._redrawLine = function (y, orientation, className, offset, width) {
if (this.master == true) {
// reuse redundant line
var line = this.dom.redundant.lines.shift();
if (!line) {
// create vertical line
line = document.createElement('div');
line.className = 'grid horizontal minor';
this.dom.lineContainer.appendChild(line);
}
this.dom.lines.push(line);
var line = DOMutil.getDOMElement('div',this.DOMelements, this.dom.lineContainer);//this.dom.redundant.lines.shift();
line.className = className;
line.innerHTML = '';
var props = this.props;
if (orientation == 'left') {
line.style.left = (this.width - this.options.minorLinesOffset) + 'px';
line.style.left = (this.width - offset) + 'px';
}
else {
line.style.left = -1*(this.width - this.options.minorLinesOffset) + 'px';
line.style.left = -1*(this.width - offset) + 'px';
}
line.style.width = props.minorLineWidth + 'px';
line.style.width = width + 'px';
line.style.top = y + 'px';
}
};
/**
* Create a Major line for the axis at position x
* @param {Number} x
* @param {String} orientation "top" or "bottom" (default)
* @private
*/
DataAxis.prototype._redrawMajorLine = function (y, orientation) {
if (this.master == true) {
// reuse redundant line
var line = this.dom.redundant.lines.shift();
if (!line) {
// create vertical line
line = document.createElement('div');
line.className = 'grid horizontal major';
this.dom.lineContainer.appendChild(line);
}
this.dom.lines.push(line);
if (orientation == 'left') {
line.style.left = (this.width - this.options.majorLinesOffset) + 'px';
}
else {
line.style.left = -1*(this.width - this.options.majorLinesOffset) + 'px';
}
line.style.top = y + 'px';
line.style.width = this.props.majorLineWidth + 'px';
}
DataAxis.prototype.convertValue = function (value) {
var invertedValue = this.valueAtZero - value;
var convertedValue = invertedValue * this.conversionFactor;
return convertedValue; // the -2 is to compensate for the borders
};

+ 20
- 13
src/timeline/component/GraphGroup.js View File

@ -39,20 +39,21 @@ GraphGroup.prototype.update = function(group) {
GraphGroup.prototype.drawIcon = function(x,y,JSONcontainer, SVGcontainer, iconWidth, iconHeight) {
var fillHeight = iconHeight * 0.5;
var path, fillPath, outline;
if (this.options.style == 'line') {
outline = SVGutil._getSVGElement("rect", JSONcontainer, SVGcontainer);
outline.setAttributeNS(null, "x", x);
outline.setAttributeNS(null, "y", y - fillHeight);
outline.setAttributeNS(null, "width", iconWidth);
outline.setAttributeNS(null, "height", 2*fillHeight);
outline.setAttributeNS(null, "class", "outline");
var path, fillPath;
var outline = DOMutil.getSVGElement("rect", JSONcontainer, SVGcontainer);
outline.setAttributeNS(null, "x", x);
outline.setAttributeNS(null, "y", y - fillHeight);
outline.setAttributeNS(null, "width", iconWidth);
outline.setAttributeNS(null, "height", 2*fillHeight);
outline.setAttributeNS(null, "class", "outline");
path = SVGutil._getSVGElement("path", JSONcontainer, SVGcontainer);
if (this.options.style == 'line') {
path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer);
path.setAttributeNS(null, "class", this.className);
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+"");
if (this.options.shaded.enabled == true) {
fillPath = SVGutil._getSVGElement("path", JSONcontainer, SVGcontainer);
fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer);
if (this.options.shaded.orientation == 'top') {
fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) +
"L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight));
@ -67,11 +68,17 @@ GraphGroup.prototype.drawIcon = function(x,y,JSONcontainer, SVGcontainer, iconWi
}
if (this.options.drawPoints.enabled == true) {
SVGutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer);
DOMutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer);
}
}
else {
console.log("bar")
//TODO: bars
var barWidth = Math.round(0.3 * iconWidth);
var bar1Height = Math.round(0.4 * iconHeight);
var bar2Height = Math.round(0.75 * iconHeight);
var offset = Math.round((iconWidth - (2 * barWidth))/3);
DOMutil.drawBar(x + 0.5*barWidth + offset , y + fillHeight - bar1Height - 1, barWidth, bar1Height, this.className + ' bar', JSONcontainer, SVGcontainer);
DOMutil.drawBar(x + 1.5*barWidth + offset + 2, y + fillHeight - bar2Height - 1, barWidth, bar2Height, this.className + ' bar', JSONcontainer, SVGcontainer);
}
}

+ 6
- 6
src/timeline/component/Legend.js View File

@ -94,7 +94,7 @@ Legend.prototype.redraw = function() {
}
Legend.prototype.drawLegend = function() {
this.linegraph._prepareSVGElements.call(this,this.svgElements);
this.linegraph.prepareElements.call(this,this.svgElements);
var x = 0;
var y = 0;
var lineLength = 30;
@ -104,7 +104,7 @@ Legend.prototype.drawLegend = function() {
var legendWidth = 298;
var padding = 5;
var border = this._getSVGElement("rect", this.svgLegendElements, this.svgLegend);
var border = this.getSVGElement("rect", this.svgLegendElements, this.svgLegend);
border.setAttributeNS(null, "x", x);
border.setAttributeNS(null, "y", y);
border.setAttributeNS(null, "width", legendWidth);
@ -115,18 +115,18 @@ Legend.prototype.drawLegend = function() {
if (classes.length > 0) {
for (var i = 0; i < classes.length; i++) {
outline = this._getSVGElement("rect", this.svgLegendElements, this.svgLegend);
outline = this.getSVGElement("rect", this.svgLegendElements, this.svgLegend);
outline.setAttributeNS(null, "x", x);
outline.setAttributeNS(null, "y", y - fillHeight);
outline.setAttributeNS(null, "width", lineLength);
outline.setAttributeNS(null, "height", 2*fillHeight);
outline.setAttributeNS(null, "class", "outline");
path = this._getSVGElement("path", this.svgLegendElements, this.svgLegend);
path = this.getSVGElement("path", this.svgLegendElements, this.svgLegend);
path.setAttributeNS(null, "class", classes[i]);
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + lineLength) + ","+y+"");
if (this.options.shaded.enabled == true) {
fillPath = this._getSVGElement("path", this.svgLegendElements, this.svgLegend);
fillPath = this.getSVGElement("path", this.svgLegendElements, this.svgLegend);
if (this.options.shaded.orientation == 'top') {
fillPath.setAttributeNS(null, "d", "M"+x+", " + (y - fillHeight) +
"L"+x+","+y+" L"+ (x + lineLength) + ","+y+" L"+ (x + lineLength) + "," + (y - fillHeight));
@ -152,5 +152,5 @@ Legend.prototype.drawLegend = function() {
this._cleanupSVGElements(this.svgLegendElements);
this.cleanupElements(this.svgLegendElements);
}

+ 76
- 93
src/timeline/component/Linegraph.js View File

@ -97,7 +97,6 @@ function Linegraph(body, options) {
this.setOptions(options);
this.groupsUsingDefaultStyles = [0];
var me = this;
this.body.emitter.on("rangechange",function() {
if (me.lastStart != 0) {
var offset = me.body.range.start - me.lastStart;
@ -315,27 +314,7 @@ Linegraph.prototype._onRemove = Linegraph.prototype._onUpdate;
Linegraph.prototype._onUpdateGroups = function (groupIds) {
for (var i = 0; i < groupIds.length; i++) {
var group = this.groupsData.get(groupIds[i]);
if (!this.groups.hasOwnProperty(groupIds[i])) {
this.groups[groupIds[i]] = new GraphGroup(group, this.options, this.groupsUsingDefaultStyles);
this.legend.addGroup(groupIds[i],this.groups[groupIds[i]]);
if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') {
this.yAxisRight.addGroup(groupIds[i], this.groups[groupIds[i]]);
}
else {
this.yAxisLeft.addGroup(groupIds[i], this.groups[groupIds[i]]);
}
}
else {
this.groups[groupIds[i]].update(group);
this.legend.updateGroup(groupIds[i],this.groups[groupIds[i]]);
if (this.groups[groupIds[i]].options.yAxisOrientation == 'right') {
this.yAxisRight.updateGroup(groupIds[i], this.groups[groupIds[i]]);
}
else {
this.yAxisLeft.updateGroup(groupIds[i], this.groups[groupIds[i]]);
}
}
this._updateGroup(group, groupIds[i]);
}
this._updateUngrouped();
this._updateGraph();
@ -352,34 +331,40 @@ Linegraph.prototype._onRemoveGroups = function (groupIds) {
this.redraw();
};
/**
* Create or delete the group holding all ungrouped items. This group is used when
* there are no groups specified. This anonymous group is called 'graph'.
* @protected
*/
Linegraph.prototype._updateUngrouped = function() {
var group = {id: UNGROUPED, content: "graph"};
if (!this.groups.hasOwnProperty(UNGROUPED)) {
this.groups[UNGROUPED] = new GraphGroup(group, this.options, this.groupsUsingDefaultStyles);
this.legend.addGroup(UNGROUPED,this.groups[UNGROUPED]);
if (this.groups[UNGROUPED].options.yAxisOrientation == 'right') {
this.yAxisRight.addGroup(UNGROUPED, this.groups[UNGROUPED]);
Linegraph.prototype._updateGroup = function (group, groupId) {
if (!this.groups.hasOwnProperty(groupId)) {
this.groups[groupId] = new GraphGroup(group, this.options, this.groupsUsingDefaultStyles);
this.legend.addGroup(groupId, this.groups[groupId]);
if (this.groups[groupId].options.yAxisOrientation == 'right') {
this.yAxisRight.addGroup(groupId, this.groups[groupId]);
}
else {
this.yAxisLeft.addGroup(UNGROUPED, this.groups[UNGROUPED]);
this.yAxisLeft.addGroup(groupId, this.groups[groupId]);
}
}
else {
this.groups[UNGROUPED].update(group);
this.legend.updateGroup(UNGROUPED,this.groups[UNGROUPED]);
if (this.groups[UNGROUPED].options.yAxisOrientation == 'right') {
this.yAxisRight.updateGroup(UNGROUPED, this.groups[UNGROUPED]);
this.groups[groupId].update(group);
this.legend.updateGroup(groupId, this.groups[groupId]);
if (this.groups[groupId].options.yAxisOrientation == 'right') {
this.yAxisRight.updateGroup(groupId, this.groups[groupId]);
}
else {
this.yAxisLeft.updateGroup(UNGROUPED, this.groups[UNGROUPED]);
this.yAxisLeft.updateGroup(groupId, this.groups[groupId]);
}
}
};
/**
* Create or delete the group holding all ungrouped items. This group is used when
* there are no groups specified. This anonymous group is called 'graph'.
* @protected
*/
Linegraph.prototype._updateUngrouped = function() {
var group = {id: UNGROUPED, content: "graph"};
this._updateGroup(group, UNGROUPED);
if (this.itemsData != null) {
var datapoints = this.itemsData.get({
@ -444,7 +429,7 @@ Linegraph.prototype.redraw = function() {
*/
Linegraph.prototype._updateGraph = function () {
// reset the svg elements
SVGutil._prepareSVGElements(this.svgElements);
DOMutil.prepareElements(this.svgElements);
if (this.width != 0 && this.itemsData != null) {
// look at different lines
@ -457,10 +442,8 @@ Linegraph.prototype._updateGraph = function () {
}
}
// this.legend.redraw();
// cleanup unused svg elements
SVGutil._cleanupSVGElements(this.svgElements);
DOMutil.cleanupElements(this.svgElements);
};
/**
@ -468,7 +451,7 @@ Linegraph.prototype._updateGraph = function () {
* @param {array} groupIds
* @private
*/
Linegraph.prototype._updateYAxis = function(groupIds) {
Linegraph.prototype._updateYAxis = function (groupIds) {
var yAxisLeftUsed = false;
var yAxisRightUsed = false;
var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal;
@ -484,28 +467,38 @@ Linegraph.prototype._updateYAxis = function(groupIds) {
orientation = 'right';
}
var view = new vis.DataSet(this.itemsData.get({filter: function (item) {return item.group == groupIds[i];}}));
var view = new vis.DataSet(this.itemsData.get({filter: function (item) {
return item.group == groupIds[i];
}}));
minVal = view.min("y").y;
maxVal = view.max("y").y;
if (orientation == 'left') {
yAxisLeftUsed = true;
if (minLeft > minVal) {minLeft = minVal;}
if (maxLeft < maxVal) {maxLeft = maxVal;}
if (minLeft > minVal) {
minLeft = minVal;
}
if (maxLeft < maxVal) {
maxLeft = maxVal;
}
}
else {
yAxisRightUsed = true;
if (minRight > minVal) {minRight = minVal;}
if (maxRight < maxVal) {maxRight = maxVal;}
if (minRight > minVal) {
minRight = minVal;
}
if (maxRight < maxVal) {
maxRight = maxVal;
}
}
delete view;
}
if (yAxisLeftUsed == true) {
this.yAxisLeft.setRange({start: minLeft, end: maxLeft});
if (yAxisLeftUsed == true) {
this.yAxisLeft.setRange(minLeft, maxLeft);
}
if (yAxisRightUsed == true) {
this.yAxisRight.setRange({start: minRight, end: maxRight});
this.yAxisRight.setRange(minRight, maxRight);
}
}
@ -516,11 +509,11 @@ Linegraph.prototype._updateYAxis = function(groupIds) {
}
if (yAxisRightUsed == true && yAxisLeftUsed == true) {
this.yAxisLeft.drawIcons = true;
this.yAxisLeft.drawIcons = true;
this.yAxisRight.drawIcons = true;
}
else {
this.yAxisLeft.drawIcons = false;
this.yAxisLeft.drawIcons = false;
this.yAxisRight.drawIcons = false;
}
@ -537,17 +530,17 @@ Linegraph.prototype._updateYAxis = function(groupIds) {
else {
this.yAxisRight.redraw();
}
}
};
/**
* This shows or hides the Y axis if needed. If there is a change, the changed event is emitted by the updateYAxis function
*
* @param {boolean} axisUsed
* @param {DataAxis object} axis
* @returns {boolean}
* @private
* @param axis
*/
Linegraph.prototype._toggleAxisVisiblity = function(axisUsed, axis) {
Linegraph.prototype._toggleAxisVisiblity = function (axisUsed, axis) {
var changed = false;
if (axisUsed == false) {
if (axis.dom.frame.parentNode) {
@ -562,7 +555,7 @@ Linegraph.prototype._toggleAxisVisiblity = function(axisUsed, axis) {
}
}
return changed;
}
};
/**
@ -582,24 +575,30 @@ Linegraph.prototype._drawGraph = function (groupId, groupIndex, amountOfGraphs)
this._drawLineGraph(datapoints, group);
}
else {
this._drawBarGraph(datapoints, group, amountOfGraphs);
this._drawBarGraph(datapoints, group);
}
};
/**
* draw a bar graph
* @param datapoints
* @param options
* @param amountOfGraphs
* @param group
*/
Linegraph.prototype._drawBarGraph = function (datapoints, group, amountOfGraphs) {
Linegraph.prototype._drawBarGraph = function (datapoints, group) {
if (datapoints != null) {
if (datapoints.length > 0) {
var dataset = this._prepareData(datapoints, group.options);
var bar;
console.log(group.options);
var width, coreDistance;
var minWidth = 0.1 * group.options.barChart.width;
for (var i = 0; i < dataset.length; i++) {
this._drawBar(dataset[i].x, dataset[i].y, group.options.barChart.width, this.zeroPosition - dataset[i].y, group.className + ' bar');
width = group.options.barChart.width;
// dynammically downscale the width so there is no overlap up to 1/10th the original width
if (i+1 < dataset.length) {coreDistance = Math.abs(dataset[i+1].x - dataset[i].x);}
if (i > 0) {coreDistance = Math.min(coreDistance,Math.abs(dataset[i-1].x - dataset[i].x));}
if (coreDistance < width) {width = coreDistance < minWidth ? minWidth : coreDistance;}
DOMutil.drawBar(dataset[i].x, dataset[i].y, width, this.zeroPosition - dataset[i].y, group.className + ' bar', this.svgElements, this.svg);
}
// draw points
@ -610,29 +609,12 @@ Linegraph.prototype._drawBarGraph = function (datapoints, group, amountOfGraphs)
}
};
/**
* draw a bar SVG element
*
* @param x
* @param y
* @param className
*/
Linegraph.prototype._drawBar = function (x, y, width, height, className) {
rect = SVGutil._getSVGElement('rect',this.svgElements, this.svg);
rect.setAttributeNS(null, "x", x - 0.5 * width);
rect.setAttributeNS(null, "y", y);
rect.setAttributeNS(null, "width", width);
rect.setAttributeNS(null, "height", height);
rect.setAttributeNS(null, "class", className);
};
/**
* draw a line graph
*
*
* @param datapoints
* @param options
* @param group
*/
Linegraph.prototype._drawLineGraph = function (datapoints, group) {
if (datapoints != null) {
@ -640,7 +622,7 @@ Linegraph.prototype._drawLineGraph = function (datapoints, group) {
var dataset = this._prepareData(datapoints, group.options);
var path, d;
path = SVGutil._getSVGElement('path', this.svgElements, this.svg);
path = DOMutil.getSVGElement('path', this.svgElements, this.svg);
path.setAttributeNS(null, "class", group.className);
@ -654,12 +636,13 @@ Linegraph.prototype._drawLineGraph = function (datapoints, group) {
// append with points for fill and finalize the path
if (group.options.shaded.enabled == true) {
var fillPath = SVGutil._getSVGElement('path',this.svgElements, this.svg);
var fillPath = DOMutil.getSVGElement('path',this.svgElements, this.svg);
var dFill;
if (group.options.shaded.orientation == 'top') {
var dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0;
dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0;
}
else {
var dFill = "M" + dataset[0].x + "," + this.svg.offsetHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + this.svg.offsetHeight;
dFill = "M" + dataset[0].x + "," + this.svg.offsetHeight + " " + d + "L" + dataset[dataset.length - 1].x + "," + this.svg.offsetHeight;
}
fillPath.setAttributeNS(null, "class", group.className + " fill");
fillPath.setAttributeNS(null, "d", dFill);
@ -677,15 +660,15 @@ Linegraph.prototype._drawLineGraph = function (datapoints, group) {
/**
* draw the data points
*
*
* @param dataset
* @param options
* @param JSONcontainer
* @param svg
* @param group
*/
Linegraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg) {
for (var i = 0; i < dataset.length; i++) {
SVGutil.drawPoint(dataset[i].x, dataset[i].y, group, JSONcontainer, svg);
DOMutil.drawPoint(dataset[i].x, dataset[i].y, group, JSONcontainer, svg);
}
};
@ -711,7 +694,7 @@ Linegraph.prototype._prepareData = function (datapoints, options) {
axis = this.yAxisRight;
}
for (var i = 0; i < datapoints.length; i++) {
xValue = toScreen(datapoints[i].x) + this.width;
xValue = toScreen(datapoints[i].x) + this.width - 1;
yValue = axis.convertValue(datapoints[i].y);
extractedData.push({x: xValue, y: yValue});
}

+ 1
- 0
src/timeline/component/css/pathStyles.css View File

@ -76,6 +76,7 @@
.vis.timeline .bar {
fill-opacity:0.7;
stroke-width:1px;
}
.vis.timeline .point {

Loading…
Cancel
Save