|
|
@ -66,6 +66,7 @@ function LineGraph(body, options) { |
|
|
|
this.abortedGraphUpdate = false; |
|
|
|
this.updateSVGheight = false; |
|
|
|
this.updateSVGheightOnResize = false; |
|
|
|
this.forceGraphUpdate = true; |
|
|
|
|
|
|
|
var me = this; |
|
|
|
this.itemsData = null; // DataSet
|
|
|
@ -105,18 +106,18 @@ function LineGraph(body, options) { |
|
|
|
this.svgElements = {}; |
|
|
|
this.setOptions(options); |
|
|
|
this.groupsUsingDefaultStyles = [0]; |
|
|
|
this.COUNTER = 0; |
|
|
|
this.body.emitter.on('rangechanged', function () { |
|
|
|
me.lastStart = me.body.range.start; |
|
|
|
me.svg.style.left = util.option.asSize(-me.props.width); |
|
|
|
me.redraw.call(me, true); |
|
|
|
|
|
|
|
me.forceGraphUpdate = true; |
|
|
|
//Is this local redraw necessary? (Core also does a change event!)
|
|
|
|
me.redraw.call(me); |
|
|
|
}); |
|
|
|
|
|
|
|
// create the HTML DOM
|
|
|
|
this._create(); |
|
|
|
this.framework = {svg: this.svg, svgElements: this.svgElements, options: this.options, groups: this.groups}; |
|
|
|
this.body.emitter.emit('change'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
LineGraph.prototype = new Component(); |
|
|
@ -158,7 +159,7 @@ LineGraph.prototype._create = function () { |
|
|
|
LineGraph.prototype.setOptions = function (options) { |
|
|
|
if (options) { |
|
|
|
var fields = ['sampling', 'defaultGroup', 'stack', 'height', 'graphHeight', 'yAxisOrientation', 'style', 'barChart', 'dataAxis', 'sort', 'groups']; |
|
|
|
if (options.graphHeight === undefined && options.height !== undefined && this.body.domProps.centerContainer.height !== undefined) { |
|
|
|
if (options.graphHeight === undefined && options.height !== undefined) { |
|
|
|
this.updateSVGheight = true; |
|
|
|
this.updateSVGheightOnResize = true; |
|
|
|
} |
|
|
@ -210,8 +211,9 @@ LineGraph.prototype.setOptions = function (options) { |
|
|
|
} |
|
|
|
|
|
|
|
// this is used to redraw the graph if the visibility of the groups is changed.
|
|
|
|
if (this.dom.frame) { |
|
|
|
this.redraw(true); |
|
|
|
if (this.dom.frame) { //not on initial run?
|
|
|
|
this.forceGraphUpdate=true; |
|
|
|
this.body.emitter.emit("change"); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -331,7 +333,6 @@ LineGraph.prototype.setGroups = function (groups) { |
|
|
|
|
|
|
|
LineGraph.prototype._onUpdate = function (ids) { |
|
|
|
this._updateAllGroupData(); |
|
|
|
this.redraw(true); |
|
|
|
}; |
|
|
|
LineGraph.prototype._onAdd = function (ids) { |
|
|
|
this._onUpdate(ids); |
|
|
@ -341,7 +342,6 @@ LineGraph.prototype._onRemove = function (ids) { |
|
|
|
}; |
|
|
|
LineGraph.prototype._onUpdateGroups = function (groupIds) { |
|
|
|
this._updateAllGroupData(); |
|
|
|
this.redraw(true); |
|
|
|
}; |
|
|
|
LineGraph.prototype._onAddGroups = function (groupIds) { |
|
|
|
this._onUpdateGroups(groupIds); |
|
|
@ -356,7 +356,8 @@ LineGraph.prototype._onRemoveGroups = function (groupIds) { |
|
|
|
for (var i = 0; i < groupIds.length; i++) { |
|
|
|
this._removeGroup(groupIds[i]); |
|
|
|
} |
|
|
|
this.redraw(true); |
|
|
|
this.forceGraphUpdate = true; |
|
|
|
this.body.emitter.emit("change"); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -404,10 +405,16 @@ LineGraph.prototype._updateGroup = function (group, groupId) { |
|
|
|
if (this.groups[groupId].options.yAxisOrientation == 'right') { |
|
|
|
this.yAxisRight.updateGroup(groupId, this.groups[groupId]); |
|
|
|
this.legendRight.updateGroup(groupId, this.groups[groupId]); |
|
|
|
//If yAxisOrientation changed, clean out the group from the other axis.
|
|
|
|
this.yAxisLeft.removeGroup(groupId); |
|
|
|
this.legendLeft.removeGroup(groupId); |
|
|
|
} |
|
|
|
else { |
|
|
|
this.yAxisLeft.updateGroup(groupId, this.groups[groupId]); |
|
|
|
this.legendLeft.updateGroup(groupId, this.groups[groupId]); |
|
|
|
//If yAxisOrientation changed, clean out the group from the other axis.
|
|
|
|
this.yAxisRight.removeGroup(groupId); |
|
|
|
this.legendRight.removeGroup(groupId); |
|
|
|
} |
|
|
|
} |
|
|
|
this.legendLeft.redraw(); |
|
|
@ -484,6 +491,8 @@ LineGraph.prototype._updateAllGroupData = function () { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
this.forceGraphUpdate = true; |
|
|
|
this.body.emitter.emit("change"); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -491,7 +500,7 @@ LineGraph.prototype._updateAllGroupData = function () { |
|
|
|
* Redraw the component, mandatory function |
|
|
|
* @return {boolean} Returns true if the component is resized |
|
|
|
*/ |
|
|
|
LineGraph.prototype.redraw = function (forceGraphUpdate) { |
|
|
|
LineGraph.prototype.redraw = function () { |
|
|
|
var resized = false; |
|
|
|
|
|
|
|
// calculate actual size and position
|
|
|
@ -500,9 +509,9 @@ LineGraph.prototype.redraw = function (forceGraphUpdate) { |
|
|
|
- this.body.domProps.border.top |
|
|
|
- this.body.domProps.border.bottom; |
|
|
|
|
|
|
|
// update the graph if there is no lastWidth or with, used for the initial draw
|
|
|
|
// update the graph if there is no lastWidth or width, used for the initial draw
|
|
|
|
if (this.lastWidth === undefined && this.props.width) { |
|
|
|
forceGraphUpdate = true; |
|
|
|
this.forceGraphUpdate = true; |
|
|
|
} |
|
|
|
|
|
|
|
// check if this component is resized
|
|
|
@ -539,8 +548,9 @@ LineGraph.prototype.redraw = function (forceGraphUpdate) { |
|
|
|
} |
|
|
|
|
|
|
|
// zoomed is here to ensure that animations are shown correctly.
|
|
|
|
if (resized == true || zoomed == true || this.abortedGraphUpdate == true || forceGraphUpdate == true) { |
|
|
|
if (resized == true || zoomed == true || this.abortedGraphUpdate == true || this.forceGraphUpdate == true) { |
|
|
|
resized = this._updateGraph() || resized; |
|
|
|
this.forceGraphUpdate = false; |
|
|
|
} |
|
|
|
else { |
|
|
|
// move the whole svg while dragging
|
|
|
@ -554,7 +564,6 @@ LineGraph.prototype.redraw = function (forceGraphUpdate) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.legendLeft.redraw(); |
|
|
|
this.legendRight.redraw(); |
|
|
|
return resized; |
|
|
@ -621,101 +630,97 @@ LineGraph.prototype._updateGraph = function () { |
|
|
|
this._getYRanges(groupIds, groupsData, groupRanges); |
|
|
|
|
|
|
|
// update the Y axis first, we use this data to draw at the correct Y points
|
|
|
|
// changeCalled is required to clean the SVG on a change emit.
|
|
|
|
changeCalled = this._updateYAxis(groupIds, groupRanges); |
|
|
|
var MAX_CYCLES = 5; |
|
|
|
if (changeCalled == true && this.COUNTER < MAX_CYCLES) { |
|
|
|
|
|
|
|
// at changeCalled, abort this update cycle as the graph needs another update with new Width input from the Redraw container.
|
|
|
|
// Cleanup SVG elements on abort.
|
|
|
|
if (changeCalled == true) { |
|
|
|
DOMutil.cleanupElements(this.svgElements); |
|
|
|
this.abortedGraphUpdate = true; |
|
|
|
this.COUNTER++; |
|
|
|
this.body.emitter.emit('change'); |
|
|
|
return true; |
|
|
|
} |
|
|
|
else { |
|
|
|
if (this.COUNTER > MAX_CYCLES) { |
|
|
|
console.log("WARNING: there may be an infinite loop in the _updateGraph emitter cycle."); |
|
|
|
} |
|
|
|
this.COUNTER = 0; |
|
|
|
this.abortedGraphUpdate = false; |
|
|
|
|
|
|
|
// With the yAxis scaled correctly, use this to get the Y values of the points.
|
|
|
|
var below = undefined; |
|
|
|
for (i = 0; i < groupIds.length; i++) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (this.options.stack === true && this.options.style === 'line') { |
|
|
|
if (group.options.excludeFromStacking == undefined || !group.options.excludeFromStacking) { |
|
|
|
if (below != undefined) { |
|
|
|
this._stack(groupsData[group.id], groupsData[below.id]); |
|
|
|
if (group.options.shaded.enabled == true && group.options.shaded.orientation !== "group"){ |
|
|
|
if (group.options.shaded.orientation == "top" && below.options.shaded.orientation !== "group"){ |
|
|
|
below.options.shaded.orientation="group"; |
|
|
|
below.options.shaded.groupId=group.id; |
|
|
|
} else { |
|
|
|
group.options.shaded.orientation="group"; |
|
|
|
group.options.shaded.groupId=below.id; |
|
|
|
} |
|
|
|
this.abortedGraphUpdate = false; |
|
|
|
|
|
|
|
// With the yAxis scaled correctly, use this to get the Y values of the points.
|
|
|
|
var below = undefined; |
|
|
|
for (i = 0; i < groupIds.length; i++) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (this.options.stack === true && this.options.style === 'line') { |
|
|
|
if (group.options.excludeFromStacking == undefined || !group.options.excludeFromStacking) { |
|
|
|
if (below != undefined) { |
|
|
|
this._stack(groupsData[group.id], groupsData[below.id]); |
|
|
|
if (group.options.shaded.enabled == true && group.options.shaded.orientation !== "group"){ |
|
|
|
if (group.options.shaded.orientation == "top" && below.options.shaded.orientation !== "group"){ |
|
|
|
below.options.shaded.orientation="group"; |
|
|
|
below.options.shaded.groupId=group.id; |
|
|
|
} else { |
|
|
|
group.options.shaded.orientation="group"; |
|
|
|
group.options.shaded.groupId=below.id; |
|
|
|
} |
|
|
|
} |
|
|
|
below = group; |
|
|
|
} |
|
|
|
below = group; |
|
|
|
} |
|
|
|
this._convertYcoordinates(groupsData[groupIds[i]], group); |
|
|
|
} |
|
|
|
this._convertYcoordinates(groupsData[groupIds[i]], group); |
|
|
|
} |
|
|
|
|
|
|
|
//Precalculate paths and draw shading if appropriate. This will make sure the shading is always behind any lines.
|
|
|
|
var paths = {}; |
|
|
|
for (i = 0; i < groupIds.length; i++) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (group.options.style === 'line' && group.options.shaded.enabled == true) { |
|
|
|
var dataset = groupsData[groupIds[i]]; |
|
|
|
if (!paths.hasOwnProperty(groupIds[i])) { |
|
|
|
paths[groupIds[i]] = Lines.calcPath(dataset, group); |
|
|
|
} |
|
|
|
if (group.options.shaded.orientation === "group") { |
|
|
|
var subGroupId = group.options.shaded.groupId; |
|
|
|
if (groupIds.indexOf(subGroupId) === -1) { |
|
|
|
console.log(group.id + ": Unknown shading group target given:" + subGroupId); |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (!paths.hasOwnProperty(subGroupId)) { |
|
|
|
paths[subGroupId] = Lines.calcPath(groupsData[subGroupId], this.groups[subGroupId]); |
|
|
|
} |
|
|
|
Lines.drawShading(paths[groupIds[i]], group, paths[subGroupId], this.framework); |
|
|
|
//Precalculate paths and draw shading if appropriate. This will make sure the shading is always behind any lines.
|
|
|
|
var paths = {}; |
|
|
|
for (i = 0; i < groupIds.length; i++) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (group.options.style === 'line' && group.options.shaded.enabled == true) { |
|
|
|
var dataset = groupsData[groupIds[i]]; |
|
|
|
if (dataset == null || dataset.length == 0) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (!paths.hasOwnProperty(groupIds[i])) { |
|
|
|
paths[groupIds[i]] = Lines.calcPath(dataset, group); |
|
|
|
} |
|
|
|
if (group.options.shaded.orientation === "group") { |
|
|
|
var subGroupId = group.options.shaded.groupId; |
|
|
|
if (groupIds.indexOf(subGroupId) === -1) { |
|
|
|
console.log(group.id + ": Unknown shading group target given:" + subGroupId); |
|
|
|
continue; |
|
|
|
} |
|
|
|
else { |
|
|
|
Lines.drawShading(paths[groupIds[i]], group, undefined, this.framework); |
|
|
|
if (!paths.hasOwnProperty(subGroupId)) { |
|
|
|
paths[subGroupId] = Lines.calcPath(groupsData[subGroupId], this.groups[subGroupId]); |
|
|
|
} |
|
|
|
Lines.drawShading(paths[groupIds[i]], group, paths[subGroupId], this.framework); |
|
|
|
} |
|
|
|
else { |
|
|
|
Lines.drawShading(paths[groupIds[i]], group, undefined, this.framework); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// draw the groups, calculating paths if still necessary.
|
|
|
|
Bars.draw(groupIds, groupsData, this.framework); |
|
|
|
for (i = 0; i < groupIds.length; i++) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (groupsData[groupIds[i]].length > 0) { |
|
|
|
switch (group.options.style) { |
|
|
|
case "line": |
|
|
|
if (!paths.hasOwnProperty(groupIds[i])) { |
|
|
|
paths[groupIds[i]] = Lines.calcPath(groupsData[groupIds[i]], group); |
|
|
|
} |
|
|
|
Lines.draw(paths[groupIds[i]], group, this.framework); |
|
|
|
//explicit no break;
|
|
|
|
case "point": |
|
|
|
//explicit no break;
|
|
|
|
case "points": |
|
|
|
if (group.options.style == "point" || group.options.style == "points" || group.options.drawPoints.enabled == true) { |
|
|
|
Points.draw(groupsData[groupIds[i]], group, this.framework); |
|
|
|
} |
|
|
|
break; |
|
|
|
case "bar": |
|
|
|
// bar needs to be drawn enmasse
|
|
|
|
//explicit no break
|
|
|
|
default: |
|
|
|
//do nothing...
|
|
|
|
} |
|
|
|
// draw the groups, calculating paths if still necessary.
|
|
|
|
Bars.draw(groupIds, groupsData, this.framework); |
|
|
|
for (i = 0; i < groupIds.length; i++) { |
|
|
|
group = this.groups[groupIds[i]]; |
|
|
|
if (groupsData[groupIds[i]].length > 0) { |
|
|
|
switch (group.options.style) { |
|
|
|
case "line": |
|
|
|
if (!paths.hasOwnProperty(groupIds[i])) { |
|
|
|
paths[groupIds[i]] = Lines.calcPath(groupsData[groupIds[i]], group); |
|
|
|
} |
|
|
|
Lines.draw(paths[groupIds[i]], group, this.framework); |
|
|
|
//explicit no break;
|
|
|
|
case "point": |
|
|
|
//explicit no break;
|
|
|
|
case "points": |
|
|
|
if (group.options.style == "point" || group.options.style == "points" || group.options.drawPoints.enabled == true) { |
|
|
|
Points.draw(groupsData[groupIds[i]], group, this.framework); |
|
|
|
} |
|
|
|
break; |
|
|
|
case "bar": |
|
|
|
// bar needs to be drawn enmasse
|
|
|
|
//explicit no break
|
|
|
|
default: |
|
|
|
//do nothing...
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|