|
|
@ -1298,6 +1298,7 @@ util.copyObject = function(objectFrom, objectTo) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return objectTo; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -2557,6 +2558,10 @@ DataView.prototype._trigger = DataSet.prototype._trigger; |
|
|
|
DataView.prototype.subscribe = DataView.prototype.on; |
|
|
|
DataView.prototype.unsubscribe = DataView.prototype.off; |
|
|
|
|
|
|
|
/** |
|
|
|
* Created by Alex on 6/17/14. |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* A horizontal time axis |
|
|
|
* @param {Object} [options] See DataAxis.setOptions for the available |
|
|
@ -2572,6 +2577,8 @@ function DataAxis (body, options) { |
|
|
|
orientation: 'left', // supported: 'left'
|
|
|
|
showMinorLabels: true, |
|
|
|
showMajorLabels: true, |
|
|
|
majorLinesOffset: 25, |
|
|
|
minorLinesOffset: 25, |
|
|
|
width: '50px', |
|
|
|
height: '300px' |
|
|
|
}; |
|
|
@ -2606,15 +2613,32 @@ function DataAxis (body, options) { |
|
|
|
this.width = Number(this.options.width.replace("px","")); |
|
|
|
this.height = Number(this.options.height.replace("px","")); |
|
|
|
|
|
|
|
this.stepPixels = 25; |
|
|
|
this.stepPixelsForced = 25; |
|
|
|
this.lineOffset = 0; |
|
|
|
this.master = true; |
|
|
|
|
|
|
|
// create the HTML DOM
|
|
|
|
this._create(); |
|
|
|
} |
|
|
|
|
|
|
|
DataAxis.prototype = new Component(); |
|
|
|
|
|
|
|
//DataAxis.prototype.setOptions = function(options) {
|
|
|
|
//
|
|
|
|
//}
|
|
|
|
DataAxis.prototype.setOptions = function(options) { |
|
|
|
if (options) { |
|
|
|
var redraw = false; |
|
|
|
if (this.options.orientation != options.orientation && options.orientation !== undefined) { |
|
|
|
redraw = true; |
|
|
|
} |
|
|
|
var fields = ['orientation','showMinorLabels','showMajorLabels','width','height']; |
|
|
|
util.selectiveExtend(fields, this.options, options); |
|
|
|
|
|
|
|
if (redraw == true && this.dom.frame) { |
|
|
|
this.hide(); |
|
|
|
this.show(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
@ -2628,8 +2652,6 @@ DataAxis.prototype._create = function() { |
|
|
|
this.dom.lineContainer = document.createElement('div'); |
|
|
|
this.dom.lineContainer.style.width = '100%'; |
|
|
|
this.dom.lineContainer.style.height = this.options.height; |
|
|
|
|
|
|
|
this.show(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -2641,7 +2663,7 @@ DataAxis.prototype.show = function() { |
|
|
|
if (this.options.orientation == 'left') { |
|
|
|
this.body.dom.left.appendChild(this.dom.frame); |
|
|
|
} |
|
|
|
if (this.options.orientation == 'right') { |
|
|
|
else { |
|
|
|
this.body.dom.right.appendChild(this.dom.frame); |
|
|
|
} |
|
|
|
} |
|
|
@ -2698,9 +2720,9 @@ DataAxis.prototype.redraw = function () { |
|
|
|
props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0; |
|
|
|
props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0; |
|
|
|
|
|
|
|
props.minorLineWidth = this.body.dom.backgroundHorizontal.offsetWidth; |
|
|
|
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; |
|
|
|
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)
|
|
|
@ -2751,13 +2773,27 @@ DataAxis.prototype._redrawLabels = function () { |
|
|
|
|
|
|
|
step.first(); |
|
|
|
var stepPixels = this.dom.frame.offsetHeight / ((step.marginRange / step.step) + 1); |
|
|
|
var xFirstMajorLabel = undefined; |
|
|
|
this.stepPixels = stepPixels; |
|
|
|
|
|
|
|
var amountOfSteps = this.height / stepPixels; |
|
|
|
var stepDifference = 0; |
|
|
|
|
|
|
|
if (this.master == false) { |
|
|
|
stepPixels = this.stepPixelsForced; |
|
|
|
stepDifference = Math.round((this.height / stepPixels) - amountOfSteps); |
|
|
|
for (var i = 0; i < 0.5 * stepDifference; i++) { |
|
|
|
step.previous(); |
|
|
|
} |
|
|
|
amountOfSteps = this.height / stepPixels; |
|
|
|
} |
|
|
|
|
|
|
|
var xFirstMajorLabel = undefined; |
|
|
|
this.valueAtZero = step.marginEnd; |
|
|
|
var marginStartPos = 0; |
|
|
|
var max = 0; |
|
|
|
while (step.hasNext() && max < 1000) { |
|
|
|
var y = Math.round(max * stepPixels); |
|
|
|
var y = 0; |
|
|
|
while (max < amountOfSteps) { |
|
|
|
y = Math.round(max * stepPixels); |
|
|
|
marginStartPos = max * stepPixels; |
|
|
|
var isMajor = step.isMajor(); |
|
|
|
|
|
|
@ -2766,7 +2802,7 @@ DataAxis.prototype._redrawLabels = function () { |
|
|
|
} |
|
|
|
|
|
|
|
if (isMajor && this.options['showMajorLabels']) { |
|
|
|
if (y > 0) { |
|
|
|
if (y >= 0) { |
|
|
|
if (xFirstMajorLabel == undefined) { |
|
|
|
xFirstMajorLabel = y; |
|
|
|
} |
|
|
@ -2782,7 +2818,7 @@ DataAxis.prototype._redrawLabels = function () { |
|
|
|
max++; |
|
|
|
} |
|
|
|
|
|
|
|
this.conversionFactor = marginStartPos/step.marginRange; |
|
|
|
this.conversionFactor = marginStartPos/((amountOfSteps-1) * step.step); |
|
|
|
|
|
|
|
// create a major label on the left when needed
|
|
|
|
if (this.options['showMajorLabels']) { |
|
|
@ -2816,7 +2852,7 @@ DataAxis.prototype.convertValues = function(data) { |
|
|
|
DataAxis.prototype.convertValue = function(value) { |
|
|
|
var invertedValue = this.valueAtZero - value; |
|
|
|
var convertedValue = invertedValue * this.conversionFactor; |
|
|
|
return convertedValue - 2; // the -2 is to compensate for the borders
|
|
|
|
return convertedValue; // the -2 is to compensate for the borders
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@ -2884,7 +2920,7 @@ DataAxis.prototype._redrawMajorText = function (y, text, orientation) { |
|
|
|
label.style.textAlign = "right"; |
|
|
|
} |
|
|
|
else { |
|
|
|
label.style.left = '2'; |
|
|
|
label.style.left = '2px'; |
|
|
|
label.style.textAlign = "left"; |
|
|
|
} |
|
|
|
|
|
|
@ -2898,27 +2934,29 @@ DataAxis.prototype._redrawMajorText = function (y, text, orientation) { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
DataAxis.prototype._redrawMinorLine = function (y, orientation) { |
|
|
|
// reuse redundant line
|
|
|
|
var line = this.dom.redundant.minorLines.shift(); |
|
|
|
if (this.master == true) { |
|
|
|
// reuse redundant line
|
|
|
|
var line = this.dom.redundant.minorLines.shift(); |
|
|
|
|
|
|
|
if (!line) { |
|
|
|
// create vertical line
|
|
|
|
line = document.createElement('div'); |
|
|
|
line.className = 'grid horizontal minor'; |
|
|
|
this.dom.lineContainer.appendChild(line); |
|
|
|
} |
|
|
|
this.dom.minorLines.push(line); |
|
|
|
if (!line) { |
|
|
|
// create vertical line
|
|
|
|
line = document.createElement('div'); |
|
|
|
line.className = 'grid horizontal minor'; |
|
|
|
this.dom.lineContainer.appendChild(line); |
|
|
|
} |
|
|
|
this.dom.minorLines.push(line); |
|
|
|
|
|
|
|
var props = this.props; |
|
|
|
if (orientation == 'left') { |
|
|
|
line.style.left = (this.width - 15) + 'px'; |
|
|
|
} |
|
|
|
else { |
|
|
|
line.style.left = -1*(this.width - 15) + 'px'; |
|
|
|
} |
|
|
|
var props = this.props; |
|
|
|
if (orientation == 'left') { |
|
|
|
line.style.left = (this.width - this.options.minorLinesOffset) + 'px'; |
|
|
|
} |
|
|
|
else { |
|
|
|
line.style.left = -1*(this.width - this.options.minorLinesOffset) + 'px'; |
|
|
|
} |
|
|
|
|
|
|
|
line.style.width = props.minorLineWidth + 'px'; |
|
|
|
line.style.top = y + 'px'; |
|
|
|
line.style.width = props.minorLineWidth + 'px'; |
|
|
|
line.style.top = y + 'px'; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -2928,25 +2966,27 @@ DataAxis.prototype._redrawMinorLine = function (y, orientation) { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
DataAxis.prototype._redrawMajorLine = function (y, orientation) { |
|
|
|
// reuse redundant line
|
|
|
|
var line = this.dom.redundant.majorLines.shift(); |
|
|
|
if (this.master == true) { |
|
|
|
// reuse redundant line
|
|
|
|
var line = this.dom.redundant.majorLines.shift(); |
|
|
|
|
|
|
|
if (!line) { |
|
|
|
// create vertical line
|
|
|
|
line = document.createElement('div'); |
|
|
|
line.className = 'grid horizontal major'; |
|
|
|
this.dom.lineContainer.appendChild(line); |
|
|
|
} |
|
|
|
this.dom.majorLines.push(line); |
|
|
|
if (!line) { |
|
|
|
// create vertical line
|
|
|
|
line = document.createElement('div'); |
|
|
|
line.className = 'grid horizontal major'; |
|
|
|
this.dom.lineContainer.appendChild(line); |
|
|
|
} |
|
|
|
this.dom.majorLines.push(line); |
|
|
|
|
|
|
|
if (orientation == 'left') { |
|
|
|
line.style.left = (this.width - 25) + 'px'; |
|
|
|
} |
|
|
|
else { |
|
|
|
line.style.left = -1*(this.width - 25) + 'px'; |
|
|
|
if (orientation == 'left') { |
|
|
|
line.style.left = (this.width - this.options.majorLinesOffset) + 'px'; |
|
|
|
} |
|
|
|
else { |
|
|
|
line.style.left = -1*(this.width - this.options.mayorLinesOffset) + 'px'; |
|
|
|
} |
|
|
|
line.style.top = y + 'px'; |
|
|
|
line.style.width = this.props.majorLineWidth + 'px'; |
|
|
|
} |
|
|
|
line.style.top = y + 'px'; |
|
|
|
line.style.width = this.props.majorLineWidth + 'px'; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -3001,6 +3041,7 @@ function Linegraph(body, options) { |
|
|
|
this.body = body; |
|
|
|
|
|
|
|
this.defaultOptions = { |
|
|
|
yAxisOrientation: 'left', |
|
|
|
shaded: { |
|
|
|
enabled: true, |
|
|
|
orientation: 'top' // top, bottom
|
|
|
@ -3044,6 +3085,19 @@ function Linegraph(body, options) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// listeners for the DataSet of the groups
|
|
|
|
this.groupListeners = { |
|
|
|
'add': function (event, params, senderId) { |
|
|
|
me._onAddGroups(params.items); |
|
|
|
}, |
|
|
|
'update': function (event, params, senderId) { |
|
|
|
me._onUpdateGroups(params.items); |
|
|
|
}, |
|
|
|
'remove': function (event, params, senderId) { |
|
|
|
me._onRemoveGroups(params.items); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
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; |
|
|
@ -3051,11 +3105,10 @@ function Linegraph(body, options) { |
|
|
|
|
|
|
|
this.svgElements = {}; |
|
|
|
this.svgLegendElements = {}; |
|
|
|
// create the HTML DOM
|
|
|
|
this.setOptions(options); |
|
|
|
|
|
|
|
this._create(); |
|
|
|
var me = this; |
|
|
|
|
|
|
|
var me = this; |
|
|
|
this.body.emitter.on("rangechange",function() { |
|
|
|
if (me.lastStart != 0) { |
|
|
|
var offset = me.body.range.start - me.lastStart; |
|
|
@ -3073,7 +3126,8 @@ function Linegraph(body, options) { |
|
|
|
me.updateGraph.apply(me); |
|
|
|
}); |
|
|
|
|
|
|
|
this.setOptions(options); |
|
|
|
// create the HTML DOM
|
|
|
|
this._create(); |
|
|
|
this.body.emitter.emit("change"); |
|
|
|
} |
|
|
|
|
|
|
@ -3105,10 +3159,22 @@ Linegraph.prototype._create = function(){ |
|
|
|
frame.appendChild(this.svgLegend); |
|
|
|
|
|
|
|
// panel with time axis
|
|
|
|
this.yAxis = new DataAxis(this.body, { |
|
|
|
this.yAxisLeft = new DataAxis(this.body, { |
|
|
|
orientation: 'left', |
|
|
|
showMinorLabels: true, |
|
|
|
showMajorLabels: true, |
|
|
|
majorLinesOffset: 25, |
|
|
|
minorLinesOffset: 25, |
|
|
|
width: '50px', |
|
|
|
height: this.svg.style.height |
|
|
|
}); |
|
|
|
|
|
|
|
this.yAxisRight = new DataAxis(this.body, { |
|
|
|
orientation: 'right', |
|
|
|
showMinorLabels: true, |
|
|
|
showMajorLabels: true, |
|
|
|
majorLinesOffset: 25, |
|
|
|
minorLinesOffset: 25, |
|
|
|
width: '50px', |
|
|
|
height: this.svg.style.height |
|
|
|
}); |
|
|
@ -3116,22 +3182,13 @@ Linegraph.prototype._create = function(){ |
|
|
|
this.show(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Linegraph.prototype.setOptions = function(options) { |
|
|
|
if (options) { |
|
|
|
var fields = ['barGraph','catmullRom','shaded','drawPoints']; |
|
|
|
var fields = ['yAxisOrientation']; |
|
|
|
util.selectiveExtend(fields, this.options, options); |
|
|
|
|
|
|
|
if (options.catmullRom) { |
|
|
|
if (typeof options.catmullRom == 'boolean') { |
|
|
|
this.options.catmullRom.enabled = options.catmullRom; |
|
|
|
} |
|
|
|
else { |
|
|
|
this.options.catmullRom.enabled = true; |
|
|
|
for (var prop in options.catmullRom) { |
|
|
|
if (options.catmullRom.hasOwnProperty(prop)) { |
|
|
|
this.options.catmullRom[prop] = options.catmullRom[prop]; |
|
|
|
} |
|
|
|
} |
|
|
|
if (typeof options.catmullRom == 'object') { |
|
|
|
if (options.catmullRom.parametrization) { |
|
|
|
if (options.catmullRom.parametrization == 'uniform') { |
|
|
|
this.options.catmullRom.alpha = 0; |
|
|
@ -3146,36 +3203,27 @@ Linegraph.prototype.setOptions = function(options) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
this._mergeOptions(this.options, options,'catmullRom'); |
|
|
|
this._mergeOptions(this.options, options,'drawPoints'); |
|
|
|
this._mergeOptions(this.options, options,'shaded'); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
if (options.drawPoints) { |
|
|
|
if (typeof options.catmullRom == 'boolean') { |
|
|
|
this.options.drawPoints.enabled = options.drawPoints; |
|
|
|
} |
|
|
|
else { |
|
|
|
this.options.drawPoints.enabled = true; |
|
|
|
for (prop in options.drawPoints) { |
|
|
|
if (options.drawPoints.hasOwnProperty(prop)) { |
|
|
|
this.options.drawPoints[prop] = options.drawPoints[prop]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
Linegraph.prototype._mergeOptions = function (mergeTarget, options,option) { |
|
|
|
if (options[option]) { |
|
|
|
if (typeof options[option] == 'boolean') { |
|
|
|
mergeTarget[option].enabled = options[option]; |
|
|
|
} |
|
|
|
|
|
|
|
if (options.shaded) { |
|
|
|
if (typeof options.shaded == 'boolean') { |
|
|
|
this.options.shaded.enabled = options.shaded; |
|
|
|
} |
|
|
|
else { |
|
|
|
this.options.shaded.enabled = true; |
|
|
|
for (prop in options.shaded) { |
|
|
|
if (options.shaded.hasOwnProperty(prop)) { |
|
|
|
this.options.shaded[prop] = options.shaded.drawPoints[prop]; |
|
|
|
} |
|
|
|
else { |
|
|
|
mergeTarget[option].enabled = true; |
|
|
|
for (prop in options[option]) { |
|
|
|
if (options[option].hasOwnProperty(prop)) { |
|
|
|
mergeTarget[option][prop] = options[option][prop]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Hide the component from the DOM |
|
|
@ -3199,8 +3247,6 @@ Linegraph.prototype.show = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Set items |
|
|
|
* @param {vis.DataSet | null} items |
|
|
@ -3243,31 +3289,91 @@ Linegraph.prototype.setItems = function(items) { |
|
|
|
ids = this.itemsData.getIds(); |
|
|
|
this._onAdd(ids); |
|
|
|
} |
|
|
|
this._updateUngrouped(); |
|
|
|
this.updateGraph(); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Handle added items |
|
|
|
* @param {Number[]} ids |
|
|
|
* @protected |
|
|
|
* Set groups |
|
|
|
* @param {vis.DataSet} groups |
|
|
|
*/ |
|
|
|
Linegraph.prototype.setGroups = function(groups) { |
|
|
|
var me = this, |
|
|
|
ids; |
|
|
|
|
|
|
|
// unsubscribe from current dataset
|
|
|
|
if (this.groupsData) { |
|
|
|
util.forEach(this.groupListeners, function (callback, event) { |
|
|
|
me.groupsData.unsubscribe(event, callback); |
|
|
|
}); |
|
|
|
|
|
|
|
// remove all drawn groups
|
|
|
|
ids = this.groupsData.getIds(); |
|
|
|
this.groupsData = null; |
|
|
|
this._onRemoveGroups(ids); // note: this will cause a redraw
|
|
|
|
} |
|
|
|
|
|
|
|
// replace the dataset
|
|
|
|
if (!groups) { |
|
|
|
this.groupsData = null; |
|
|
|
} |
|
|
|
else if (groups instanceof DataSet || groups instanceof DataView) { |
|
|
|
this.groupsData = groups; |
|
|
|
} |
|
|
|
else { |
|
|
|
throw new TypeError('Data must be an instance of DataSet or DataView'); |
|
|
|
} |
|
|
|
|
|
|
|
if (this.groupsData) { |
|
|
|
// subscribe to new dataset
|
|
|
|
var id = this.id; |
|
|
|
util.forEach(this.groupListeners, function (callback, event) { |
|
|
|
me.groupsData.on(event, callback, id); |
|
|
|
}); |
|
|
|
|
|
|
|
// draw all ms
|
|
|
|
ids = this.groupsData.getIds(); |
|
|
|
this._onAddGroups(ids); |
|
|
|
} |
|
|
|
this._updateUngrouped(); |
|
|
|
this.updateGraph(); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Handle updated items |
|
|
|
* @param {Number[]} ids |
|
|
|
* @protected |
|
|
|
*/ |
|
|
|
Linegraph.prototype._onUpdate = function(ids) { |
|
|
|
this.updateGraph(); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
Linegraph.prototype._onAdd = Linegraph.prototype._onUpdate; |
|
|
|
Linegraph.prototype._onRemove = function(ids) {}; |
|
|
|
Linegraph.prototype._onAdd = Linegraph.prototype._onUpdate; |
|
|
|
Linegraph.prototype._onRemove = Linegraph.prototype._onUpdate; |
|
|
|
Linegraph.prototype._onUpdateGroups = Linegraph.prototype._onUpdate; |
|
|
|
Linegraph.prototype._onAddGroups = Linegraph.prototype._onUpdate; |
|
|
|
Linegraph.prototype._onRemoveGroups = Linegraph.prototype._onUpdate; |
|
|
|
|
|
|
|
/** |
|
|
|
* Create or delete the group holding all ungrouped items. This group is used when |
|
|
|
* there are no groups specified. |
|
|
|
* @protected |
|
|
|
*/ |
|
|
|
Linegraph.prototype._updateUngrouped = function() { |
|
|
|
if (this.itemsData != null) { |
|
|
|
var datapoints = this.itemsData.get({ |
|
|
|
filter: function (item) {return item.group === undefined;}, |
|
|
|
showInternalIds:true |
|
|
|
}); |
|
|
|
|
|
|
|
var updateQuery = []; |
|
|
|
for (var i = 0; i < datapoints.length; i++) { |
|
|
|
updateQuery.push({id:datapoints[i].id, group: 'graph'}); |
|
|
|
} |
|
|
|
|
|
|
|
this.itemsData.update(updateQuery); |
|
|
|
|
|
|
|
} |
|
|
|
}; |
|
|
|
/** |
|
|
|
* Repaint the component |
|
|
|
* @return {boolean} Returns true if the component is resized |
|
|
@ -3275,7 +3381,7 @@ Linegraph.prototype._onRemove = function(ids) {}; |
|
|
|
Linegraph.prototype.redraw = function() { |
|
|
|
var resized = false; |
|
|
|
|
|
|
|
if (this.lastWidth === undefined && this.width) { |
|
|
|
if (this.lastWidth === undefined && this.width || this.lastWidth != this.width) { |
|
|
|
resized = true; |
|
|
|
} |
|
|
|
// check whether zoomed (in that case we need to re-stack everything)
|
|
|
@ -3287,6 +3393,7 @@ Linegraph.prototype.redraw = function() { |
|
|
|
// calculate actual size and position
|
|
|
|
this.width = this.dom.frame.offsetWidth; |
|
|
|
this.svgLegend.style.left = (this.width - this.svgLegend.offsetWidth - 10) + "px"; |
|
|
|
|
|
|
|
// check if this component is resized
|
|
|
|
resized = this._isResized() || resized; |
|
|
|
if (resized) { |
|
|
@ -3304,42 +3411,126 @@ Linegraph.prototype.updateGraph = function () { |
|
|
|
this._prepareSVGElements(this.svgElements); |
|
|
|
|
|
|
|
if (this.width != 0 && this.itemsData != null) { |
|
|
|
// get the range for the y Axis and draw it
|
|
|
|
var yRange = {start: this.itemsData.min('y').y, end: this.itemsData.max('y').y}; |
|
|
|
this.yAxis.setRange(yRange); |
|
|
|
this.yAxis.redraw(); |
|
|
|
|
|
|
|
// look at different lines
|
|
|
|
var classes = this.itemsData.distinct('className'); |
|
|
|
if (classes.length > 0) { |
|
|
|
for (var i = 0; i < classes.length; i++) { |
|
|
|
this.drawGraph(classes[i], classes.length); |
|
|
|
var groupIds = this.itemsData.distinct('group'); |
|
|
|
if (groupIds.length > 0) { |
|
|
|
this._setYRanges(groupIds); |
|
|
|
for (var i = 0; i < groupIds.length; i++) { |
|
|
|
this.drawGraph(groupIds[i], i, groupIds.length); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
this.drawGraph('group0', 1); |
|
|
|
this._setYRanges(groupIds); |
|
|
|
this.drawGraph('graph', 0, 1); |
|
|
|
} |
|
|
|
this.drawLegend(classes); |
|
|
|
} |
|
|
|
|
|
|
|
// cleanup unused svg elements
|
|
|
|
this._cleanupSVGElements(this.svgElements); |
|
|
|
}; |
|
|
|
|
|
|
|
Linegraph.prototype.drawGraph = function (className, amountOfGraphs) { |
|
|
|
var datapoints = this.itemsData.get({filter: function (item) { |
|
|
|
return item.className == className || !item.className; |
|
|
|
}}); |
|
|
|
Linegraph.prototype._setYRanges = function(groupIds) { |
|
|
|
var yAxisLeftUsed = false; |
|
|
|
var yAxisRightUsed = false; |
|
|
|
var minLeft = 1e9, minRight = 1e9, maxLeft = -1e9, maxRight = -1e9, minVal, maxVal; |
|
|
|
var orientation = 'left'; |
|
|
|
|
|
|
|
if (groupIds.length > 0) { |
|
|
|
for (var i = 0; i < groupIds.length; i++) { |
|
|
|
orientation = 'left'; |
|
|
|
var group = this.groupsData.get(groupIds[i]); |
|
|
|
if (group != null) { |
|
|
|
if (group.options) { |
|
|
|
if (group.options.yAxisOrientation == 'right') { |
|
|
|
orientation = 'right'; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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;} |
|
|
|
} |
|
|
|
else { |
|
|
|
yAxisRightUsed = true; |
|
|
|
if (minRight > minVal) {minRight = minVal;} |
|
|
|
if (maxRight < maxVal) {maxRight = maxVal;} |
|
|
|
} |
|
|
|
|
|
|
|
delete view; |
|
|
|
} |
|
|
|
if (yAxisLeftUsed == true) { |
|
|
|
this.yAxisLeft.setRange({start: minLeft, end: maxLeft}); |
|
|
|
} |
|
|
|
if (yAxisRightUsed == true) { |
|
|
|
this.yAxisRight.setRange({start: minRight, end: maxRight}); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
var yRange = {start: this.itemsData.min('y').y, end: this.itemsData.max('y').y}; |
|
|
|
if (this.options.yAxisOrientation == 'left') { |
|
|
|
this.yAxisLeft.setRange(yRange); |
|
|
|
yAxisLeftUsed = true; |
|
|
|
} |
|
|
|
else { |
|
|
|
this.yAxisRight.setRange(yRange); |
|
|
|
yAxisRightUsed = true; |
|
|
|
} |
|
|
|
} |
|
|
|
var changed = this._toggleAxisVisiblity(yAxisLeftUsed, this.yAxisLeft); |
|
|
|
changed = this._toggleAxisVisiblity(yAxisRightUsed, this.yAxisRight) || changed; |
|
|
|
if (changed) { |
|
|
|
this.body.emitter.emit('change'); |
|
|
|
} |
|
|
|
|
|
|
|
this.yAxisRight.master = !yAxisLeftUsed; |
|
|
|
|
|
|
|
if (this.yAxisRight.master == false) { |
|
|
|
if (yAxisRightUsed == true) { |
|
|
|
this.yAxisLeft.lineOffset = this.yAxisRight.width; |
|
|
|
} |
|
|
|
this.yAxisLeft.redraw(); |
|
|
|
this.yAxisRight.stepPixelsForced = this.yAxisLeft.stepPixels; |
|
|
|
this.yAxisRight.redraw(); |
|
|
|
} |
|
|
|
else { |
|
|
|
this.yAxisRight.redraw(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Linegraph.prototype._toggleAxisVisiblity = function(axisUsed, axis) { |
|
|
|
var changed = false; |
|
|
|
if (axisUsed == false) { |
|
|
|
if (axis.dom.frame.parentNode) { |
|
|
|
axis.hide(); |
|
|
|
changed = true; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
if (!axis.dom.frame.parentNode) { |
|
|
|
axis.show(); |
|
|
|
changed = true; |
|
|
|
} |
|
|
|
} |
|
|
|
return changed; |
|
|
|
} |
|
|
|
|
|
|
|
Linegraph.prototype.drawGraph = function (groupId, groupIndex, amountOfGraphs) { |
|
|
|
var datapoints = this.itemsData.get({filter: function (item) {return item.group == groupId;}}); |
|
|
|
var options = this._getGroupOptions(groupId, groupIndex); |
|
|
|
if (this.options.style == 'bar') { |
|
|
|
this.drawBarGraph(datapoints, className, amountOfGraphs); |
|
|
|
this.drawBarGraph(datapoints, options, amountOfGraphs); |
|
|
|
} |
|
|
|
else { |
|
|
|
this.drawLineGraph(datapoints, className); |
|
|
|
this.drawLineGraph(datapoints, options); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Linegraph.prototype.drawBarGraph = function (datapoints, className, amountOfGraphs) { |
|
|
|
Linegraph.prototype.drawBarGraph = function (datapoints, options, amountOfGraphs) { |
|
|
|
if (datapoints != null) { |
|
|
|
if (datapoints.length > 0) { |
|
|
|
var dataset = this._prepareData(datapoints); |
|
|
@ -3351,7 +3542,6 @@ Linegraph.prototype.drawBarGraph = function (datapoints, className, amountOfGrap |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Linegraph.prototype.drawBar = function (x, y, className) { |
|
|
|
var width = 10; |
|
|
|
rect = this._getSVGElement('rect',this.svgElements, this.svg); |
|
|
@ -3363,14 +3553,14 @@ Linegraph.prototype.drawBar = function (x, y, className) { |
|
|
|
rect.setAttributeNS(null, "class", className + " point"); |
|
|
|
}; |
|
|
|
|
|
|
|
Linegraph.prototype.drawLineGraph = function (datapoints, className) { |
|
|
|
Linegraph.prototype.drawLineGraph = function (datapoints, options) { |
|
|
|
if (datapoints != null) { |
|
|
|
if (datapoints.length > 0) { |
|
|
|
var dataset = this._prepareData(datapoints); |
|
|
|
var dataset = this._prepareData(datapoints, options); |
|
|
|
var path, d; |
|
|
|
|
|
|
|
path = this._getSVGElement('path', this.svgElements, this.svg); |
|
|
|
path.setAttributeNS(null, "class", className); |
|
|
|
path.setAttributeNS(null, "class", options.className); |
|
|
|
|
|
|
|
|
|
|
|
// construct path from dataset
|
|
|
@ -3382,43 +3572,40 @@ Linegraph.prototype.drawLineGraph = function (datapoints, className) { |
|
|
|
} |
|
|
|
|
|
|
|
// append with points for fill and finalize the path
|
|
|
|
if (this.options.shaded.enabled == true) { |
|
|
|
if (options.shaded.enabled == true) { |
|
|
|
var fillPath = this._getSVGElement('path',this.svgElements, this.svg); |
|
|
|
if (this.options.shaded.orientation == 'top') { |
|
|
|
if (options.shaded.orientation == 'top') { |
|
|
|
var 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; |
|
|
|
} |
|
|
|
fillPath.setAttributeNS(null, "class", className + " fill"); |
|
|
|
fillPath.setAttributeNS(null, "class", options.className + " fill"); |
|
|
|
fillPath.setAttributeNS(null, "d", dFill); |
|
|
|
} |
|
|
|
// copy properties to path for drawing.
|
|
|
|
path.setAttributeNS(null, "d", "M" + d); |
|
|
|
|
|
|
|
// draw points
|
|
|
|
if (this.options.drawPoints.enabled == true) { |
|
|
|
this.drawPoints(dataset, className, this.svgElements, this.svg); |
|
|
|
if (options.drawPoints.enabled == true) { |
|
|
|
this.drawPoints(dataset, options, this.svgElements, this.svg); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Linegraph.prototype.drawPoints = function (dataset, className, container, svg) { |
|
|
|
Linegraph.prototype.drawPoints = function (dataset, options, container, svg) { |
|
|
|
for (var i = 0; i < dataset.length; i++) { |
|
|
|
this.drawPoint(dataset[i].x, dataset[i].y, className, container, svg); |
|
|
|
this.drawPoint(dataset[i].x, dataset[i].y, options, container, svg); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Linegraph.prototype.drawPoint = function(x, y, className, container, svg) { |
|
|
|
Linegraph.prototype.drawPoint = function(x, y, options, container, svg) { |
|
|
|
var point; |
|
|
|
if (this.options.drawPoints.style == 'circle') { |
|
|
|
if (options.drawPoints.style == 'circle') { |
|
|
|
point = this._getSVGElement('circle',container,svg); |
|
|
|
point.setAttributeNS(null, "cx", x); |
|
|
|
point.setAttributeNS(null, "cy", y); |
|
|
|
point.setAttributeNS(null, "r", 0.5 * this.options.drawPoints.size); |
|
|
|
point.setAttributeNS(null, "class", className + " point"); |
|
|
|
point.setAttributeNS(null, "r", 0.5 * options.drawPoints.size); |
|
|
|
point.setAttributeNS(null, "class", options.className + " point"); |
|
|
|
} |
|
|
|
else { |
|
|
|
point = this._getSVGElement('rect',container,svg); |
|
|
@ -3426,37 +3613,35 @@ Linegraph.prototype.drawPoint = function(x, y, className, container, svg) { |
|
|
|
point.setAttributeNS(null, "y", y - 0.5*this.options.drawPoints.size); |
|
|
|
point.setAttributeNS(null, "width", this.options.drawPoints.size); |
|
|
|
point.setAttributeNS(null, "height", this.options.drawPoints.size); |
|
|
|
point.setAttributeNS(null, "class", className + " point"); |
|
|
|
point.setAttributeNS(null, "class", options.className + " point"); |
|
|
|
} |
|
|
|
return point; |
|
|
|
} |
|
|
|
|
|
|
|
Linegraph.prototype._getSVGElement = function (elementType, container, svg) { |
|
|
|
|
|
|
|
Linegraph.prototype._getSVGElement = function (elementType, JSONcontainer, svgContainer) { |
|
|
|
var element; |
|
|
|
// allocate SVG element, if it doesnt yet exist, create one.
|
|
|
|
if (container.hasOwnProperty(elementType)) { // this element has been created before
|
|
|
|
if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before
|
|
|
|
// check if there is an redundant element
|
|
|
|
if (container[elementType].redundant.length > 0) { |
|
|
|
element = container[elementType].redundant[0]; |
|
|
|
container[elementType].redundant.shift() |
|
|
|
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.createElementNS('http://www.w3.org/2000/svg', elementType); |
|
|
|
svg.appendChild(element); |
|
|
|
svgContainer.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.createElementNS('http://www.w3.org/2000/svg', elementType); |
|
|
|
container[elementType] = {used: [], redundant: []}; |
|
|
|
svg.appendChild(element); |
|
|
|
JSONcontainer[elementType] = {used: [], redundant: []}; |
|
|
|
svgContainer.appendChild(element); |
|
|
|
} |
|
|
|
container[elementType].used.push(element); |
|
|
|
JSONcontainer[elementType].used.push(element); |
|
|
|
return element; |
|
|
|
}; |
|
|
|
|
|
|
|
Linegraph.prototype._cleanupSVGElements = function(container) { |
|
|
|
// cleanup the redundant svgElements;
|
|
|
|
for (var elementType in container) { |
|
|
@ -3468,7 +3653,6 @@ Linegraph.prototype._cleanupSVGElements = function(container) { |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Linegraph.prototype._prepareSVGElements = function(container) { |
|
|
|
// cleanup the redundant svgElements;
|
|
|
|
for (var elementType in container) { |
|
|
@ -3478,21 +3662,44 @@ Linegraph.prototype._prepareSVGElements = function(container) { |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Linegraph.prototype._prepareData = function (dataset) { |
|
|
|
Linegraph.prototype._prepareData = function (dataset, options) { |
|
|
|
var extractedData = []; |
|
|
|
var xValue, yValue; |
|
|
|
var axis = this.yAxisLeft; |
|
|
|
var toScreen = this.body.util.toScreen; |
|
|
|
|
|
|
|
if (options.yAxisOrientation == 'right') { |
|
|
|
axis = this.yAxisRight; |
|
|
|
} |
|
|
|
for (var i = 0; i < dataset.length; i++) { |
|
|
|
xValue = this.body.util.toScreen(new Date(dataset[i].x)) + this.width; |
|
|
|
yValue = this.yAxis.convertValue(dataset[i].y); |
|
|
|
xValue = toScreen(new Date(dataset[i].x)) + this.width; |
|
|
|
yValue = axis.convertValue(dataset[i].y); |
|
|
|
extractedData.push({x: xValue, y: yValue}); |
|
|
|
} |
|
|
|
|
|
|
|
// extractedData.sort(function (a,b) {return a.x - b.x;});
|
|
|
|
return extractedData; |
|
|
|
}; |
|
|
|
Linegraph.prototype._getGroupOptions = function(groupId, groupIndex) { |
|
|
|
var group = this.groupsData.get(groupId); |
|
|
|
var options = util.copyObject(this.options, {}); |
|
|
|
|
|
|
|
options.className = 'graphGroup' + groupIndex%10; |
|
|
|
if (group != null) { |
|
|
|
if (group.className) { |
|
|
|
options.className = group.className; |
|
|
|
} |
|
|
|
if (group.options) { |
|
|
|
var fields = ['yAxisOrientation']; |
|
|
|
util.selectiveExtend(fields, options, group.options); |
|
|
|
|
|
|
|
this._mergeOptions(options, group.options,'catmullRom'); |
|
|
|
this._mergeOptions(options, group.options,'drawPoints'); |
|
|
|
this._mergeOptions(options, group.options,'shaded'); |
|
|
|
} |
|
|
|
} |
|
|
|
return options; |
|
|
|
} |
|
|
|
Linegraph.prototype._catmullRomUniform = function(data) { |
|
|
|
// catmull rom
|
|
|
|
var p0, p1, p2, p3, bp1, bp2; |
|
|
@ -3613,7 +3820,6 @@ Linegraph.prototype._linear = function(data) { |
|
|
|
return d; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Linegraph.prototype.drawLegend = function(classes) { |
|
|
|
this._prepareSVGElements(this.svgLegendElements); |
|
|
|
var x = 0; |
|
|
@ -3677,6 +3883,8 @@ Linegraph.prototype.drawLegend = function(classes) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* @constructor DataStep |
|
|
|
* The class DataStep is an iterator for data for the lineGraph. You provide a start data point and an |
|
|
@ -3703,7 +3911,7 @@ Linegraph.prototype.drawLegend = function(classes) { |
|
|
|
* @param {Date} [end] The end date |
|
|
|
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds |
|
|
|
*/ |
|
|
|
function DataStep(start, end, minimumStep, containerHeight) { |
|
|
|
function DataStep(start, end, minimumStep, containerHeight, forcedStepSize) { |
|
|
|
// variables
|
|
|
|
this.current = 0; |
|
|
|
|
|
|
@ -3718,7 +3926,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, forcedStepSize); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -3733,12 +3941,12 @@ function DataStep(start, end, minimumStep, containerHeight) { |
|
|
|
* @param {Number} [end] The end date and time. |
|
|
|
* @param {Number} [minimumStep] Optional. Minimum step size in milliseconds |
|
|
|
*/ |
|
|
|
DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight) { |
|
|
|
DataStep.prototype.setRange = function(start, end, minimumStep, containerHeight, forcedStepSize) { |
|
|
|
this._start = start; |
|
|
|
this._end = end; |
|
|
|
this.setFirst(); |
|
|
|
if (this.autoScale) { |
|
|
|
this.setMinimumStep(minimumStep, containerHeight); |
|
|
|
this.setMinimumStep(minimumStep, containerHeight, forcedStepSize); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -3832,6 +4040,16 @@ DataStep.prototype.next = function() { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Do the next step |
|
|
|
*/ |
|
|
|
DataStep.prototype.previous = function() { |
|
|
|
this.current += this.step; |
|
|
|
this.marginEnd += this.step; |
|
|
|
this.marginRange = this.marginEnd - this.marginStart; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Get the current datetime |
|
|
@ -9351,6 +9569,28 @@ Graph2d.prototype.setItems = function(items) { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Set groups |
|
|
|
* @param {vis.DataSet | Array | google.visualization.DataTable} groups |
|
|
|
*/ |
|
|
|
Graph2d.prototype.setGroups = function(groups) { |
|
|
|
// convert to type DataSet when needed
|
|
|
|
var newDataSet; |
|
|
|
if (!groups) { |
|
|
|
newDataSet = null; |
|
|
|
} |
|
|
|
else if (groups instanceof DataSet || groups instanceof DataView) { |
|
|
|
newDataSet = groups; |
|
|
|
} |
|
|
|
else { |
|
|
|
// turn an array into a dataset
|
|
|
|
newDataSet = new DataSet(groups); |
|
|
|
} |
|
|
|
|
|
|
|
this.groupsData = newDataSet; |
|
|
|
this.linegraph.setGroups(newDataSet); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Clear the Graph2d. By Default, items, groups and options are cleared. |
|
|
|