|
|
@ -4,8 +4,8 @@ |
|
|
|
* |
|
|
|
* A dynamic, browser-based visualization library. |
|
|
|
* |
|
|
|
* @version 1.1.0 |
|
|
|
* @date 2014-06-20 |
|
|
|
* @version @@version |
|
|
|
* @date @@date |
|
|
|
* |
|
|
|
* @license |
|
|
|
* Copyright (C) 2011-2014 Almende B.V, http://almende.com
|
|
|
@ -1341,6 +1341,138 @@ util.copyObject = function(objectFrom, objectTo) { |
|
|
|
return objectTo; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* this is used to set the options of subobjects in the options object. A requirement of these subobjects |
|
|
|
* is that they have an 'enabled' element which is optional for the user but mandatory for the program. |
|
|
|
* |
|
|
|
* @param [object] mergeTarget | this is either this.options or the options used for the groups. |
|
|
|
* @param [object] options | options |
|
|
|
* @param [String] option | this is the option key in the options argument |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
util._mergeOptions = function (mergeTarget, options, option) { |
|
|
|
if (options[option]) { |
|
|
|
if (typeof options[option] == 'boolean') { |
|
|
|
mergeTarget[option].enabled = options[option]; |
|
|
|
} |
|
|
|
else { |
|
|
|
mergeTarget[option].enabled = true; |
|
|
|
for (prop in options[option]) { |
|
|
|
if (options[option].hasOwnProperty(prop)) { |
|
|
|
mergeTarget[option][prop] = options[option][prop]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
* Created by Alex on 6/20/14. |
|
|
|
*/ |
|
|
|
|
|
|
|
var SVGutil = {} |
|
|
|
/** |
|
|
|
* this prepares the JSON container for allocating SVG elements |
|
|
|
* @param JSONcontainer |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
SVGutil._prepareSVGElements = function(JSONcontainer) { |
|
|
|
// cleanup the redundant svgElements;
|
|
|
|
for (var elementType in JSONcontainer) { |
|
|
|
if (JSONcontainer.hasOwnProperty(elementType)) { |
|
|
|
JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; |
|
|
|
JSONcontainer[elementType].used = []; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from |
|
|
|
* which to remove the redundant elements. |
|
|
|
* |
|
|
|
* @param JSONcontainer |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
SVGutil._cleanupSVGElements = function(JSONcontainer) { |
|
|
|
// cleanup the redundant svgElements;
|
|
|
|
for (var elementType in JSONcontainer) { |
|
|
|
if (JSONcontainer.hasOwnProperty(elementType)) { |
|
|
|
for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { |
|
|
|
JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); |
|
|
|
} |
|
|
|
JSONcontainer[elementType].redundant = []; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* 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 |
|
|
|
*/ |
|
|
|
SVGutil._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
|
|
|
|
// 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.createElementNS('http://www.w3.org/2000/svg', elementType); |
|
|
|
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); |
|
|
|
JSONcontainer[elementType] = {used: [], redundant: []}; |
|
|
|
svgContainer.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. |
|
|
|
* The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions |
|
|
|
* as well. |
|
|
|
* |
|
|
|
* @param x |
|
|
|
* @param y |
|
|
|
* @param group |
|
|
|
* @param JSONcontainer |
|
|
|
* @param svgContainer |
|
|
|
* @returns {*} |
|
|
|
*/ |
|
|
|
SVGutil.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { |
|
|
|
var point; |
|
|
|
if (group.options.drawPoints.style == 'circle') { |
|
|
|
point = SVGutil._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.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); |
|
|
|
point.setAttributeNS(null, "height", group.options.drawPoints.size); |
|
|
|
point.setAttributeNS(null, "class", group.className + " point"); |
|
|
|
} |
|
|
|
return point; |
|
|
|
} |
|
|
|
/** |
|
|
|
* DataSet |
|
|
|
* |
|
|
@ -2568,14 +2700,14 @@ DataView.prototype.unsubscribe = DataView.prototype.off; |
|
|
|
* @param {Object} data |
|
|
|
* @param {ItemSet} itemSet |
|
|
|
*/ |
|
|
|
function GraphGroup (group, options, linegraph) { |
|
|
|
var fields = ['yAxisOrientation','barGraph','drawPoints','catmullRom'] |
|
|
|
function GraphGroup (group, options, groupsUsingDefaultStyles) { |
|
|
|
var fields = ['style','yAxisOrientation','barChart','drawPoints','shaded','catmullRom'] |
|
|
|
this.options = util.selectiveDeepExtend(fields,{},options); |
|
|
|
this.linegraph = linegraph; |
|
|
|
this.usingDefaultStyle = group.className === undefined; |
|
|
|
this.groupsUsingDefaultStyles = groupsUsingDefaultStyles; |
|
|
|
this.update(group); |
|
|
|
if (this.usingDefaultStyle == true) { |
|
|
|
this.linegraph.groupsUsingDefaultStyles += 1; |
|
|
|
this.groupsUsingDefaultStyles[0] += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -2585,55 +2717,57 @@ GraphGroup.prototype.setClass = function (className) { |
|
|
|
|
|
|
|
GraphGroup.prototype.setOptions = function(options) { |
|
|
|
if (options !== undefined) { |
|
|
|
var fields = ['yAxisOrientation']; |
|
|
|
util.selectiveExtend(fields, this.options, options); |
|
|
|
this.linegraph._mergeOptions(this.options, options,'catmullRom'); |
|
|
|
this.linegraph._mergeOptions(this.options, options,'drawPoints'); |
|
|
|
this.linegraph._mergeOptions(this.options, options,'shaded'); |
|
|
|
var fields = ['yAxisOrientation','style','barChart']; |
|
|
|
util.selectiveDeepExtend(fields, this.options, options); |
|
|
|
|
|
|
|
util._mergeOptions(this.options, options,'catmullRom'); |
|
|
|
util._mergeOptions(this.options, options,'drawPoints'); |
|
|
|
util._mergeOptions(this.options, options,'shaded'); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
GraphGroup.prototype.update = function(group) { |
|
|
|
this.group = group; |
|
|
|
this.content = group.content || 'graph'; |
|
|
|
this.className = group.className || this.className || "graphGroup" + this.linegraph.groupsUsingDefaultStyles; |
|
|
|
this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0]; |
|
|
|
this.setOptions(group.options); |
|
|
|
}; |
|
|
|
|
|
|
|
GraphGroup.prototype.drawIcon = function(x,y,JSONcontainer, SVGcontainer, lineLength, iconHeight) { |
|
|
|
GraphGroup.prototype.drawIcon = function(x,y,JSONcontainer, SVGcontainer, iconWidth, iconHeight) { |
|
|
|
var fillHeight = iconHeight * 0.5; |
|
|
|
var path, fillPath, outline; |
|
|
|
if (this.options.barGraph.enabled == false) { |
|
|
|
outline = this.linegraph._getSVGElement("rect", JSONcontainer, SVGcontainer); |
|
|
|
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", lineLength); |
|
|
|
outline.setAttributeNS(null, "width", iconWidth); |
|
|
|
outline.setAttributeNS(null, "height", 2*fillHeight); |
|
|
|
outline.setAttributeNS(null, "class", "outline"); |
|
|
|
|
|
|
|
path = this.linegraph._getSVGElement("path", JSONcontainer, SVGcontainer); |
|
|
|
path = SVGutil._getSVGElement("path", JSONcontainer, SVGcontainer); |
|
|
|
path.setAttributeNS(null, "class", this.className); |
|
|
|
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + lineLength) + ","+y+""); |
|
|
|
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+""); |
|
|
|
if (this.options.shaded.enabled == true) { |
|
|
|
fillPath = this.linegraph._getSVGElement("path", JSONcontainer, SVGcontainer); |
|
|
|
fillPath = SVGutil._getSVGElement("path", JSONcontainer, SVGcontainer); |
|
|
|
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)); |
|
|
|
"L"+x+","+y+" L"+ (x + iconWidth) + ","+y+" L"+ (x + iconWidth) + "," + (y - fillHeight)); |
|
|
|
} |
|
|
|
else { |
|
|
|
fillPath.setAttributeNS(null, "d", "M"+x+","+y+" " + |
|
|
|
"L"+x+"," + (y + fillHeight) + " " + |
|
|
|
"L"+ (x + lineLength) + "," + (y + fillHeight) + |
|
|
|
"L"+ (x + lineLength) + ","+y); |
|
|
|
"L"+ (x + iconWidth) + "," + (y + fillHeight) + |
|
|
|
"L"+ (x + iconWidth) + ","+y); |
|
|
|
} |
|
|
|
fillPath.setAttributeNS(null, "class", this.className + " iconFill"); |
|
|
|
} |
|
|
|
|
|
|
|
if (this.options.drawPoints.enabled == true) { |
|
|
|
this.linegraph.drawPoint(x + 0.5 * lineLength,y, this, JSONcontainer, SVGcontainer); |
|
|
|
SVGutil.drawPoint(x + 0.5 * iconWidth,y, this, JSONcontainer, SVGcontainer); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
console.log("bar") |
|
|
|
//TODO: bars
|
|
|
|
} |
|
|
|
} |
|
|
@ -2780,7 +2914,7 @@ Legend.prototype.drawLegend = function() { |
|
|
|
fillPath.setAttributeNS(null, "class", classes[i] + " fill"); |
|
|
|
} |
|
|
|
|
|
|
|
if (this.options.drawPoints.enabled == true) { |
|
|
|
if (this.options._drawPoints.enabled == true) { |
|
|
|
this.drawPoint(x + 0.5 * lineLength,y,classes[i], this.svgLegendElements, this.svgLegend); |
|
|
|
} |
|
|
|
y += spacing; |
|
|
@ -2922,6 +3056,8 @@ DataAxis.prototype._create = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
DataAxis.prototype._redrawGroupIcons = function() { |
|
|
|
SVGutil._prepareSVGElements(this.svgContainer); |
|
|
|
|
|
|
|
var x; |
|
|
|
var iconWidth = this.options.iconWidth; |
|
|
|
var iconHeight = 15; |
|
|
@ -2940,6 +3076,8 @@ DataAxis.prototype._redrawGroupIcons = function() { |
|
|
|
y += iconHeight + iconOffset; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
SVGutil._cleanupSVGElements(this.svgContainer); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@ -3185,7 +3323,7 @@ DataAxis.prototype._redrawLabel = function (y, text, orientation, className, cha |
|
|
|
|
|
|
|
text += ''; |
|
|
|
|
|
|
|
var largestWidth = this.props.majorCharWidth > this.props.minorCharWidth ? this.props.majorCharWidth : this.props.minorCharWidth; |
|
|
|
var largestWidth = Math.max(this.props.majorCharWidth,this.props.minorCharWidth); |
|
|
|
if (this.maxLabelSize < text.length * largestWidth) { |
|
|
|
this.maxLabelSize = text.length * largestWidth; |
|
|
|
} |
|
|
@ -3301,8 +3439,6 @@ DataAxis.prototype.snap = function(date) { |
|
|
|
return this.step.snap(date); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
|
|
|
|
|
|
|
|
function Linegraph(body, options) { |
|
|
@ -3315,9 +3451,9 @@ function Linegraph(body, options) { |
|
|
|
enabled: true, |
|
|
|
orientation: 'top' // top, bottom
|
|
|
|
}, |
|
|
|
barGraph: { |
|
|
|
enabled: false, |
|
|
|
binSize: 'auto' |
|
|
|
style: 'line', // line, bar
|
|
|
|
barChart: { |
|
|
|
width: 50 |
|
|
|
}, |
|
|
|
drawPoints: { |
|
|
|
enabled: true, |
|
|
@ -3341,9 +3477,18 @@ function Linegraph(body, options) { |
|
|
|
visible:true |
|
|
|
}, |
|
|
|
legend: { |
|
|
|
orientation: 'left', // left, right
|
|
|
|
position: 'left', // left, center, right
|
|
|
|
visible: true |
|
|
|
enabled: true, |
|
|
|
axisIcons: true, |
|
|
|
left: { |
|
|
|
visible: true, |
|
|
|
position: 'top-left', // top/bottom - left,center,right
|
|
|
|
textAlign: 'left' |
|
|
|
}, |
|
|
|
right: { |
|
|
|
visible: true, |
|
|
|
position: 'top-left', // top/bottom - left,center,right
|
|
|
|
textAlign: 'right' |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -3391,8 +3536,7 @@ function Linegraph(body, options) { |
|
|
|
|
|
|
|
this.svgElements = {}; |
|
|
|
this.setOptions(options); |
|
|
|
this.groupsUsingDefaultStyles = 0; |
|
|
|
|
|
|
|
this.groupsUsingDefaultStyles = [0]; |
|
|
|
|
|
|
|
var me = this; |
|
|
|
this.body.emitter.on("rangechange",function() { |
|
|
@ -3409,7 +3553,7 @@ function Linegraph(body, options) { |
|
|
|
this.body.emitter.on("rangechanged", function() { |
|
|
|
me.lastStart = me.body.range.start; |
|
|
|
me.svg.style.left = util.option.asSize(-me.width); |
|
|
|
me.updateGraph.apply(me); |
|
|
|
me._updateGraph.apply(me); |
|
|
|
}); |
|
|
|
|
|
|
|
// create the HTML DOM
|
|
|
@ -3458,7 +3602,7 @@ Linegraph.prototype._create = function(){ |
|
|
|
*/ |
|
|
|
Linegraph.prototype.setOptions = function(options) { |
|
|
|
if (options) { |
|
|
|
var fields = ['yAxisOrientation','dataAxis','legend']; |
|
|
|
var fields = ['yAxisOrientation','style','barChart','dataAxis','legend']; |
|
|
|
util.selectiveDeepExtend(fields, this.options, options); |
|
|
|
|
|
|
|
if (options.catmullRom) { |
|
|
@ -3478,38 +3622,12 @@ Linegraph.prototype.setOptions = function(options) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
console.log("OPTIONS:",this.options , options); |
|
|
|
this._mergeOptions(this.options, options,'catmullRom'); |
|
|
|
this._mergeOptions(this.options, options,'drawPoints'); |
|
|
|
this._mergeOptions(this.options, options,'shaded'); |
|
|
|
util._mergeOptions(this.options, options,'catmullRom'); |
|
|
|
util._mergeOptions(this.options, options,'drawPoints'); |
|
|
|
util._mergeOptions(this.options, options,'shaded'); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* this is used to set the options of subobjects in the options object. A requirement of these subobjects |
|
|
|
* is that they have an 'enabled' element which is optional for the user but mandatory for the program. |
|
|
|
* |
|
|
|
* @param [object] mergeTarget | this is either this.options or the options used for the groups. |
|
|
|
* @param [object] options | options |
|
|
|
* @param [String] option | this is the option key in the options argument |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
Linegraph.prototype._mergeOptions = function (mergeTarget, options, option) { |
|
|
|
if (options[option]) { |
|
|
|
if (typeof options[option] == 'boolean') { |
|
|
|
mergeTarget[option].enabled = options[option]; |
|
|
|
} |
|
|
|
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 |
|
|
|
*/ |
|
|
@ -3575,7 +3693,7 @@ Linegraph.prototype.setItems = function(items) { |
|
|
|
this._onAdd(ids); |
|
|
|
} |
|
|
|
this._updateUngrouped(); |
|
|
|
this.updateGraph(); |
|
|
|
this._updateGraph(); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
|
|
|
@ -3622,7 +3740,7 @@ Linegraph.prototype.setGroups = function(groups) { |
|
|
|
this._onAddGroups(ids); |
|
|
|
} |
|
|
|
this._updateUngrouped(); |
|
|
|
this.updateGraph(); |
|
|
|
this._updateGraph(); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
|
|
|
@ -3630,7 +3748,7 @@ Linegraph.prototype.setGroups = function(groups) { |
|
|
|
|
|
|
|
Linegraph.prototype._onUpdate = function(ids) { |
|
|
|
this._updateUngrouped(); |
|
|
|
this.updateGraph(); |
|
|
|
this._updateGraph(); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
Linegraph.prototype._onAdd = Linegraph.prototype._onUpdate; |
|
|
@ -3639,8 +3757,9 @@ 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); |
|
|
|
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]]); |
|
|
|
} |
|
|
@ -3659,7 +3778,8 @@ Linegraph.prototype._onUpdateGroups = function (groupIds) { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
this.updateGraph(); |
|
|
|
this._updateUngrouped(); |
|
|
|
this._updateGraph(); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
Linegraph.prototype._onAddGroups = Linegraph.prototype._onUpdateGroups; |
|
|
@ -3668,7 +3788,8 @@ Linegraph.prototype._onRemoveGroups = function (groupIds) { |
|
|
|
for (var i = 0; i < groupIds.length; i++) { |
|
|
|
this.legend.removeGroup(groupIds[i]); |
|
|
|
} |
|
|
|
this.updateGraph(); |
|
|
|
this._updateUngrouped(); |
|
|
|
this._updateGraph(); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
|
|
|
@ -3678,10 +3799,11 @@ Linegraph.prototype._onRemoveGroups = function (groupIds) { |
|
|
|
* @protected |
|
|
|
*/ |
|
|
|
Linegraph.prototype._updateUngrouped = function() { |
|
|
|
var group = {content: "graph"}; |
|
|
|
var group = {id: UNGROUPED, content: "graph"}; |
|
|
|
if (!this.groups.hasOwnProperty(UNGROUPED)) { |
|
|
|
this.groups[UNGROUPED] = new GraphGroup(group, this.options, this); |
|
|
|
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]); |
|
|
|
} |
|
|
@ -3752,7 +3874,7 @@ Linegraph.prototype.redraw = function() { |
|
|
|
this.svg.style.left = util.option.asSize(-this.width); |
|
|
|
} |
|
|
|
if (zoomed == true) { |
|
|
|
this.updateGraph(); |
|
|
|
this._updateGraph(); |
|
|
|
} |
|
|
|
return resized; |
|
|
|
}; |
|
|
@ -3761,9 +3883,9 @@ Linegraph.prototype.redraw = function() { |
|
|
|
* Update and redraw the graph. |
|
|
|
* |
|
|
|
*/ |
|
|
|
Linegraph.prototype.updateGraph = function () { |
|
|
|
Linegraph.prototype._updateGraph = function () { |
|
|
|
// reset the svg elements
|
|
|
|
this._prepareSVGElements(this.svgElements); |
|
|
|
SVGutil._prepareSVGElements(this.svgElements); |
|
|
|
|
|
|
|
if (this.width != 0 && this.itemsData != null) { |
|
|
|
// look at different lines
|
|
|
@ -3771,15 +3893,15 @@ Linegraph.prototype.updateGraph = function () { |
|
|
|
if (groupIds.length > 0) { |
|
|
|
this._updateYAxis(groupIds); |
|
|
|
for (var i = 0; i < groupIds.length; i++) { |
|
|
|
this.drawGraph(groupIds[i], i, groupIds.length); |
|
|
|
this._drawGraph(groupIds[i], i, groupIds.length); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// this.legend.redraw();
|
|
|
|
// this.legend.redraw();
|
|
|
|
|
|
|
|
// cleanup unused svg elements
|
|
|
|
this._cleanupSVGElements(this.svgElements); |
|
|
|
SVGutil._cleanupSVGElements(this.svgElements); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -3891,17 +4013,17 @@ Linegraph.prototype._toggleAxisVisiblity = function(axisUsed, axis) { |
|
|
|
* @param groupIndex |
|
|
|
* @param amountOfGraphs |
|
|
|
*/ |
|
|
|
Linegraph.prototype.drawGraph = function (groupId, groupIndex, amountOfGraphs) { |
|
|
|
Linegraph.prototype._drawGraph = function (groupId, groupIndex, amountOfGraphs) { |
|
|
|
var datapoints = this.itemsData.get({filter: function (item) {return item.group == groupId;}, type: {x:"Date"}}); |
|
|
|
|
|
|
|
// can be optimized, only has to be done once.
|
|
|
|
var group = this.groups[groupId]; |
|
|
|
|
|
|
|
if (this.options.barGraph.enabled == 'true') { |
|
|
|
this.drawBarGraph(datapoints, group, amountOfGraphs); |
|
|
|
if (group.options.style == 'line') { |
|
|
|
this._drawLineGraph(datapoints, group); |
|
|
|
} |
|
|
|
else { |
|
|
|
this.drawLineGraph(datapoints, group); |
|
|
|
this._drawBarGraph(datapoints, group, amountOfGraphs); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -3911,13 +4033,19 @@ Linegraph.prototype.drawGraph = function (groupId, groupIndex, amountOfGraphs) { |
|
|
|
* @param options |
|
|
|
* @param amountOfGraphs |
|
|
|
*/ |
|
|
|
Linegraph.prototype.drawBarGraph = function (datapoints, group, amountOfGraphs) { |
|
|
|
Linegraph.prototype._drawBarGraph = function (datapoints, group, amountOfGraphs) { |
|
|
|
if (datapoints != null) { |
|
|
|
if (datapoints.length > 0) { |
|
|
|
var dataset = this._prepareData(datapoints); |
|
|
|
// draw points
|
|
|
|
var dataset = this._prepareData(datapoints, group.options); |
|
|
|
var bar; |
|
|
|
console.log(group.options); |
|
|
|
for (var i = 0; i < dataset.length; i++) { |
|
|
|
this.drawBar(dataset[i].x, dataset[i].y, className); |
|
|
|
this._drawBar(dataset[i].x, dataset[i].y, group.options.barChart.width, this.zeroPosition - dataset[i].y, group.className + ' bar'); |
|
|
|
} |
|
|
|
|
|
|
|
// draw points
|
|
|
|
if (group.options.drawPoints.enabled == true) { |
|
|
|
this._drawPoints(dataset, group, this.svgElements, this.svg); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -3930,15 +4058,14 @@ Linegraph.prototype.drawBarGraph = function (datapoints, group, amountOfGraphs) |
|
|
|
* @param y |
|
|
|
* @param className |
|
|
|
*/ |
|
|
|
Linegraph.prototype.drawBar = function (x, y, className) { |
|
|
|
var width = 10; |
|
|
|
rect = this._getSVGElement('rect',this.svgElements, this.svg); |
|
|
|
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", this.svg.offsetHeight - y); |
|
|
|
rect.setAttributeNS(null, "class", className + " point"); |
|
|
|
rect.setAttributeNS(null, "height", height); |
|
|
|
rect.setAttributeNS(null, "class", className); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -3948,13 +4075,13 @@ Linegraph.prototype.drawBar = function (x, y, className) { |
|
|
|
* @param datapoints |
|
|
|
* @param options |
|
|
|
*/ |
|
|
|
Linegraph.prototype.drawLineGraph = function (datapoints, group) { |
|
|
|
Linegraph.prototype._drawLineGraph = function (datapoints, group) { |
|
|
|
if (datapoints != null) { |
|
|
|
if (datapoints.length > 0) { |
|
|
|
var dataset = this._prepareData(datapoints, group.options); |
|
|
|
var path, d; |
|
|
|
|
|
|
|
path = this._getSVGElement('path', this.svgElements, this.svg); |
|
|
|
path = SVGutil._getSVGElement('path', this.svgElements, this.svg); |
|
|
|
path.setAttributeNS(null, "class", group.className); |
|
|
|
|
|
|
|
|
|
|
@ -3968,7 +4095,7 @@ Linegraph.prototype.drawLineGraph = function (datapoints, group) { |
|
|
|
|
|
|
|
// append with points for fill and finalize the path
|
|
|
|
if (group.options.shaded.enabled == true) { |
|
|
|
var fillPath = this._getSVGElement('path',this.svgElements, this.svg); |
|
|
|
var fillPath = SVGutil._getSVGElement('path',this.svgElements, this.svg); |
|
|
|
if (group.options.shaded.orientation == 'top') { |
|
|
|
var dFill = "M" + dataset[0].x + "," + 0 + " " + d + "L" + dataset[dataset.length - 1].x + "," + 0; |
|
|
|
} |
|
|
@ -3983,7 +4110,7 @@ Linegraph.prototype.drawLineGraph = function (datapoints, group) { |
|
|
|
|
|
|
|
// draw points
|
|
|
|
if (group.options.drawPoints.enabled == true) { |
|
|
|
this.drawPoints(dataset, group, this.svgElements, this.svg); |
|
|
|
this._drawPoints(dataset, group, this.svgElements, this.svg); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -3997,112 +4124,13 @@ Linegraph.prototype.drawLineGraph = function (datapoints, group) { |
|
|
|
* @param JSONcontainer |
|
|
|
* @param svg |
|
|
|
*/ |
|
|
|
Linegraph.prototype.drawPoints = function (dataset, group, JSONcontainer, svg) { |
|
|
|
Linegraph.prototype._drawPoints = function (dataset, group, JSONcontainer, svg) { |
|
|
|
for (var i = 0; i < dataset.length; i++) { |
|
|
|
this.drawPoint(dataset[i].x, dataset[i].y, group, JSONcontainer, svg); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* draw a point object. this is a seperate function because it can also be called by the legend. |
|
|
|
* The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions |
|
|
|
* as well. |
|
|
|
* |
|
|
|
* @param x |
|
|
|
* @param y |
|
|
|
* @param group |
|
|
|
* @param JSONcontainer |
|
|
|
* @param svgContainer |
|
|
|
* @returns {*} |
|
|
|
*/ |
|
|
|
Linegraph.prototype.drawPoint = function(x, y, group, JSONcontainer, svgContainer) { |
|
|
|
var point; |
|
|
|
if (group.options.drawPoints.style == 'circle') { |
|
|
|
point = this._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 = this._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); |
|
|
|
point.setAttributeNS(null, "height", group.options.drawPoints.size); |
|
|
|
point.setAttributeNS(null, "class", group.className + " point"); |
|
|
|
SVGutil.drawPoint(dataset[i].x, dataset[i].y, group, JSONcontainer, svg); |
|
|
|
} |
|
|
|
return point; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 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 |
|
|
|
*/ |
|
|
|
Linegraph.prototype._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
|
|
|
|
// 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.createElementNS('http://www.w3.org/2000/svg', elementType); |
|
|
|
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); |
|
|
|
JSONcontainer[elementType] = {used: [], redundant: []}; |
|
|
|
svgContainer.appendChild(element); |
|
|
|
} |
|
|
|
JSONcontainer[elementType].used.push(element); |
|
|
|
return element; |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from |
|
|
|
* which to remove the redundant elements. |
|
|
|
* |
|
|
|
* @param JSONcontainer |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
Linegraph.prototype._cleanupSVGElements = function(JSONcontainer) { |
|
|
|
// cleanup the redundant svgElements;
|
|
|
|
for (var elementType in JSONcontainer) { |
|
|
|
if (JSONcontainer.hasOwnProperty(elementType)) { |
|
|
|
for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) { |
|
|
|
JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]); |
|
|
|
} |
|
|
|
JSONcontainer[elementType].redundant = []; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* this prepares the JSON container for allocating SVG elements |
|
|
|
* @param JSONcontainer |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
Linegraph.prototype._prepareSVGElements = function(JSONcontainer) { |
|
|
|
// cleanup the redundant svgElements;
|
|
|
|
for (var elementType in JSONcontainer) { |
|
|
|
if (JSONcontainer.hasOwnProperty(elementType)) { |
|
|
|
JSONcontainer[elementType].redundant = JSONcontainer[elementType].used; |
|
|
|
JSONcontainer[elementType].used = []; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* This uses the DataAxis object to generate the correct Y coordinate on the SVG window. It uses the |
|
|
@ -4118,6 +4146,7 @@ Linegraph.prototype._prepareData = function (datapoints, options) { |
|
|
|
var xValue, yValue; |
|
|
|
var axis = this.yAxisLeft; |
|
|
|
var toScreen = this.body.util.toScreen; |
|
|
|
this.zeroPosition = 0; |
|
|
|
|
|
|
|
if (options.yAxisOrientation == 'right') { |
|
|
|
axis = this.yAxisRight; |
|
|
@ -4128,6 +4157,8 @@ Linegraph.prototype._prepareData = function (datapoints, options) { |
|
|
|
extractedData.push({x: xValue, y: yValue}); |
|
|
|
} |
|
|
|
|
|
|
|
this.zeroPosition = Math.min(this.svg.offsetHeight, axis.convertValue(0)); |
|
|
|
|
|
|
|
// extractedData.sort(function (a,b) {return a.x - b.x;});
|
|
|
|
return extractedData; |
|
|
|
}; |
|
|
|