This field is optional. A style can be used to give groups
+ an individual css style, and any style tags specified in style will
+ override the definition in the className style defined in css.
+
+
options
JSON object
@@ -330,6 +339,12 @@ The options colored in green can also be used as options for the groups. All opt
'bottom'
This determines if the shaded area is at the bottom or at the top of the curve. The options are 'bottom' or 'top'.
+
+
shaded.style
+
String
+
undefined
+
Set the style for the shading. This is a css string and it will override the attributes set in the class.
+
style
String
@@ -455,13 +470,53 @@ The options colored in green can also be used as options for the groups. All opt
true
Show or hide the data axis.
+
+
dataAxis.title.left.text
+
String
+
undefined
+
Set the title for the left axis.
+
+
+
dataAxis.title.left.style
+
String
+
undefined
+
Set the title style for the left axis. This is a css string and it will override the attributes set in the class.
+
+
+
dataAxis.title.right.text
+
String
+
undefined
+
Set the title for the right axis.
+
+
+
dataAxis.title.right.style
+
String
+
undefined
+
Set the title style for the right axis. This is a css string and it will override the attributes set in the class.
+
+
+
dataAxis.format.left.decimals
+
Number
+
undefined
+
Set the number of decimal points used on the the left axis. If set, this will fix the number of decimal places
+ displayed after the decimal point.
+ If undefined, trailing zeros will be removed.
+
+
+
dataAxis.format.right.decimals
+
Number
+
undefined
+
Set the number of decimal points used on the the right axis. If set, this will fix the number of decimal places
+ displayed after the decimal point.
+ If undefined, trailing zeros will be removed.
+
groups.visibility
Object
You can use this to toggle the visibility of groups per graph2D instance. This is different from setting the visibility flag of the groups since
this is not communicated across instances of graph2d. Take a look at Example 14
- for more explaination.
+ for more explanation.
+ This example shows setting a title for the left and right axes. Optionally the example allows the user
+ to show icons and labels on the left and right axis.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Left decimals
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/graph2d/17_dynamicStyling.html b/examples/graph2d/17_dynamicStyling.html
new file mode 100644
index 00000000..fed71c38
--- /dev/null
+++ b/examples/graph2d/17_dynamicStyling.html
@@ -0,0 +1,256 @@
+
+
+
+
+
+
+ Graph2d | Dynamic Styling
+
+
+
+
+
+
+
+
Graph2d | Dynamic Styling Example
+
+
+ This example shows how to programmatically change the styling of a group. While this can also
+ be done in CSS, this must be statically defined, and the programmatic interface allows the
+ user to define the look of the graph at runtime.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Line Color
+
+
+
+
+
+
Line Style
+
+
+
+
+
+
Line thickness
+
+
+
+
+
+
Fill Position
+
+
+
+
+
+
Fill Color
+
+
+
+
+
+
Fill Opacity
+
+
+
+
+
+
Points Shape
+
+
+
+
+
+
Points Size
+
+
+
+
+
+
Points Color
+
+
+
+
+
+
Point Line Thickness
+
+
+
+
+
+
Points Fill Color
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/DOMutil.js b/lib/DOMutil.js
index 38b35c7e..5cc01f6f 100644
--- a/lib/DOMutil.js
+++ b/lib/DOMutil.js
@@ -139,7 +139,6 @@ exports.drawPoint = function(x, y, group, 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 = exports.getSVGElement('rect',JSONcontainer,svgContainer);
@@ -147,8 +146,12 @@ exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
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");
}
+
+ if(group.options.drawPoints.styles !== undefined) {
+ point.setAttributeNS(null, "style", group.group.options.drawPoints.styles);
+ }
+ point.setAttributeNS(null, "class", group.className + " point");
return point;
};
diff --git a/lib/timeline/DataStep.js b/lib/timeline/DataStep.js
index e5923aa4..14dc51f1 100644
--- a/lib/timeline/DataStep.js
+++ b/lib/timeline/DataStep.js
@@ -178,19 +178,59 @@ DataStep.prototype.previous = function() {
* Get the current datetime
* @return {String} current The current date
*/
-DataStep.prototype.getCurrent = function() {
+DataStep.prototype.getCurrent = function(decimals) {
var toPrecision = '' + Number(this.current).toPrecision(5);
- if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) {
- for (var i = toPrecision.length-1; i > 0; i--) {
- if (toPrecision[i] == "0") {
- toPrecision = toPrecision.slice(0,i);
+ // If decimals is specified, then limit or extend the string as required
+ if(decimals !== undefined && !isNaN(Number(decimals))) {
+ // If string includes exponent, then we need to add it to the end
+ var exp = "";
+ var index = toPrecision.indexOf("e");
+ if(index != -1) {
+ // Get the exponent
+ exp = toPrecision.slice(index);
+ // Remove the exponent in case we need to zero-extend
+ toPrecision = toPrecision.slice(0, index);
+ }
+ index = Math.max(toPrecision.indexOf(","), toPrecision.indexOf("."));
+ if(index === -1) {
+ // No decimal found - if we want decimals, then we need to add it
+ if(decimals !== 0) {
+ toPrecision += '.';
}
- else if (toPrecision[i] == "." || toPrecision[i] == ",") {
- toPrecision = toPrecision.slice(0,i);
- break;
+ // Calculate how long the string should be
+ index = toPrecision.length + decimals;
+ }
+ else if(decimals !== 0) {
+ // Calculate how long the string should be - accounting for the decimal place
+ index += decimals + 1;
+ }
+ if(index > toPrecision.length) {
+ // We need to add zeros!
+ for(var cnt = index - toPrecision.length; cnt > 0; cnt--) {
+ toPrecision += '0';
}
- else{
- break;
+ }
+ else {
+ // we need to remove characters
+ toPrecision = toPrecision.slice(0, index);
+ }
+ // Add the exponent if there is one
+ toPrecision += exp;
+ }
+ else {
+ if (toPrecision.indexOf(",") != -1 || toPrecision.indexOf(".") != -1) {
+ // If no decimal is specified, and there are decimal places, remove trailing zeros
+ for (var i = toPrecision.length - 1; i > 0; i--) {
+ if (toPrecision[i] == "0") {
+ toPrecision = toPrecision.slice(0, i);
+ }
+ else if (toPrecision[i] == "." || toPrecision[i] == ",") {
+ toPrecision = toPrecision.slice(0, i);
+ break;
+ }
+ else {
+ break;
+ }
}
}
}
diff --git a/lib/timeline/component/DataAxis.js b/lib/timeline/component/DataAxis.js
index 8e23d411..7945e209 100644
--- a/lib/timeline/component/DataAxis.js
+++ b/lib/timeline/component/DataAxis.js
@@ -30,6 +30,14 @@ function DataAxis (body, options, svg, linegraphOptions) {
customRange: {
left: {min:undefined, max:undefined},
right: {min:undefined, max:undefined}
+ },
+ title: {
+ left: {text:undefined},
+ right: {text:undefined}
+ },
+ format: {
+ left: {decimals: undefined},
+ right: {decimals: undefined}
}
};
@@ -38,7 +46,8 @@ function DataAxis (body, options, svg, linegraphOptions) {
this.props = {};
this.DOMelements = { // dynamic elements
lines: {},
- labels: {}
+ labels: {},
+ title: {}
};
this.dom = {};
@@ -113,7 +122,9 @@ DataAxis.prototype.setOptions = function (options) {
'iconWidth',
'width',
'visible',
- 'customRange'
+ 'customRange',
+ 'title',
+ 'format'
];
util.selectiveExtend(fields, this.options, options);
@@ -227,7 +238,8 @@ DataAxis.prototype.setRange = function (start, end) {
DataAxis.prototype.redraw = function () {
var changeCalled = false;
var activeGroups = 0;
-
+
+ // Make sure the line container adheres to the vertical scrolling.
this.dom.lineContainer.style.top = this.body.domProps.scrollTop + 'px';
for (var groupId in this.groups) {
@@ -261,7 +273,7 @@ DataAxis.prototype.redraw = function () {
var showMinorLabels = this.options.showMinorLabels;
var showMajorLabels = this.options.showMajorLabels;
- // determine the width and height of the elemens for the axis
+ // determine the width and height of the elements for the axis
props.minorLabelHeight = showMinorLabels ? props.minorCharHeight : 0;
props.majorLabelHeight = showMajorLabels ? props.majorCharHeight : 0;
@@ -289,6 +301,8 @@ DataAxis.prototype.redraw = function () {
if (this.options.icons == true) {
this._redrawGroupIcons();
}
+
+ this._redrawTitle(orientation);
}
return changeCalled;
};
@@ -335,23 +349,28 @@ DataAxis.prototype._redrawLabels = function () {
// do not draw the first label
var max = 1;
+ // Get the number of decimal places
+ var decimals;
+ if(this.options.format[orientation] !== undefined) {
+ decimals = this.options.format[orientation].decimals;
+ }
+
this.maxLabelSize = 0;
var y = 0;
while (max < Math.round(amountOfSteps)) {
-
step.next();
y = Math.round(max * stepPixels);
marginStartPos = max * stepPixels;
var isMajor = step.isMajor();
if (this.options['showMinorLabels'] && isMajor == false || this.master == false && this.options['showMinorLabels'] == true) {
- this._redrawLabel(y - 2, step.getCurrent(), orientation, 'yAxis minor', this.props.minorCharHeight);
+ this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis minor', this.props.minorCharHeight);
}
if (isMajor && this.options['showMajorLabels'] && this.master == true ||
this.options['showMinorLabels'] == false && this.master == false && isMajor == true) {
if (y >= 0) {
- this._redrawLabel(y - 2, step.getCurrent(), orientation, 'yAxis major', this.props.majorCharHeight);
+ this._redrawLabel(y - 2, step.getCurrent(decimals), orientation, 'yAxis major', this.props.majorCharHeight);
}
this._redrawLine(y, orientation, 'grid horizontal major', this.options.majorLinesOffset, this.props.majorLineWidth);
}
@@ -369,9 +388,14 @@ DataAxis.prototype._redrawLabels = function () {
this.conversionFactor = this.dom.frame.offsetHeight / step.marginRange;
}
- var offset = this.options.icons == true ? this.options.iconWidth + this.options.labelOffsetX + 15 : this.options.labelOffsetX + 15;
+ // Note that title is rotated, so we're using the height, not width!
+ var titleWidth = 0;
+ if(this.options.title[orientation] !== undefined) {
+ titleWidth = this.props.titleCharHeight;
+ }
+ var offset = this.options.icons == true ? Math.max(this.options.iconWidth, titleWidth) + this.options.labelOffsetX + 15 : titleWidth + this.options.labelOffsetX + 15;
- // this will resize the yAxis to accomodate the labels.
+ // this will resize the yAxis to accommodate the labels.
if (this.maxLabelSize > (this.width - offset) && this.options.visible == true) {
this.width = this.maxLabelSize + offset;
this.options.width = this.width + "px";
@@ -461,6 +485,36 @@ DataAxis.prototype._redrawLine = function (y, orientation, className, offset, wi
}
};
+/**
+ * Create a title for the axis
+ * @private
+ * @param orientation
+ */
+DataAxis.prototype._redrawTitle = function (orientation) {
+ DOMutil.prepareElements(this.DOMelements.title);
+
+ // Check if the title is defined for this axes
+ if(this.options.title[orientation] == undefined || this.options.title[orientation].text === undefined) {
+ return;
+ }
+ var title = DOMutil.getDOMElement('div',this.DOMelements.title, this.dom.frame);
+ title.className = 'yAxis title ' + orientation;
+ title.innerHTML = this.options.title[orientation].text;
+
+ // Add style - if provided
+ if(this.options.title[orientation].style !== undefined) {
+ util.addCssText(title, this.options.title[orientation].style);
+ }
+
+ if (orientation == 'left') {
+ title.style.left = this.props.titleCharHeight + 'px';
+ }
+ else {
+ title.style.right = this.props.titleCharHeight + 'px';
+ }
+
+ title.style.width = this.height + 'px';
+};
@@ -497,6 +551,19 @@ DataAxis.prototype._calculateCharSize = function () {
this.dom.frame.removeChild(measureCharMajor);
}
+
+ if (!('titleCharHeight' in this.props)) {
+ var textTitle = document.createTextNode('0');
+ var measureCharTitle = document.createElement('DIV');
+ measureCharTitle.className = 'yAxis title measure';
+ measureCharTitle.appendChild(textTitle);
+ this.dom.frame.appendChild(measureCharTitle);
+
+ this.props.titleCharHeight = measureCharTitle.clientHeight;
+ this.props.titleCharWidth = measureCharTitle.clientWidth;
+
+ this.dom.frame.removeChild(measureCharTitle);
+ }
};
/**
diff --git a/lib/timeline/component/GraphGroup.js b/lib/timeline/component/GraphGroup.js
index 5917c79b..f70cd3dd 100644
--- a/lib/timeline/component/GraphGroup.js
+++ b/lib/timeline/component/GraphGroup.js
@@ -71,6 +71,7 @@ GraphGroup.prototype.update = function(group) {
this.content = group.content || 'graph';
this.className = group.className || this.className || "graphGroup" + this.groupsUsingDefaultStyles[0] % 10;
this.visible = group.visible === undefined ? true : group.visible;
+ this.style = group.style;
this.setOptions(group.options);
};
@@ -88,6 +89,10 @@ GraphGroup.prototype.drawIcon = function(x, y, JSONcontainer, SVGcontainer, icon
if (this.options.style == 'line') {
path = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer);
path.setAttributeNS(null, "class", this.className);
+ if(this.style !== undefined) {
+ path.setAttributeNS(null, "style", this.style);
+ }
+
path.setAttributeNS(null, "d", "M" + x + ","+y+" L" + (x + iconWidth) + ","+y+"");
if (this.options.shaded.enabled == true) {
fillPath = DOMutil.getSVGElement("path", JSONcontainer, SVGcontainer);
diff --git a/lib/timeline/component/LineGraph.js b/lib/timeline/component/LineGraph.js
index 3dd3e1e5..2b0acf80 100644
--- a/lib/timeline/component/LineGraph.js
+++ b/lib/timeline/component/LineGraph.js
@@ -74,7 +74,6 @@ function LineGraph(body, options) {
}
};
-
// options is shared by this ItemSet and all its items
this.options = util.extend({}, this.defaultOptions);
this.dom = {};
@@ -1067,7 +1066,10 @@ LineGraph.prototype._drawLineGraph = function (dataset, group) {
var path, d;
var svgHeight = Number(this.svg.style.height.replace('px',''));
path = DOMutil.getSVGElement('path', this.svgElements, this.svg);
- path.setAttributeNS(null, 'class', group.className);
+ path.setAttributeNS(null, "class", group.className);
+ if(group.style !== undefined) {
+ path.setAttributeNS(null, "style", group.style);
+ }
// construct path from dataset
if (group.options.catmullRom.enabled == true) {
@@ -1087,8 +1089,11 @@ LineGraph.prototype._drawLineGraph = function (dataset, group) {
else {
dFill = 'M' + dataset[0].x + ',' + svgHeight + ' ' + d + 'L' + dataset[dataset.length - 1].x + ',' + svgHeight;
}
- fillPath.setAttributeNS(null, 'class', group.className + ' fill');
- fillPath.setAttributeNS(null, 'd', dFill);
+ fillPath.setAttributeNS(null, "class", group.className + " fill");
+ if(group.options.shaded.style !== undefined) {
+ fillPath.setAttributeNS(null, "style", group.options.shaded.style);
+ }
+ fillPath.setAttributeNS(null, "d", dFill);
}
// copy properties to path for drawing.
path.setAttributeNS(null, 'd', 'M' + d);
diff --git a/lib/timeline/component/css/dataaxis.css b/lib/timeline/component/css/dataaxis.css
index 1fe4d71d..279899d5 100644
--- a/lib/timeline/component/css/dataaxis.css
+++ b/lib/timeline/component/css/dataaxis.css
@@ -44,6 +44,48 @@
width: auto;
}
+.vis.timeline .dataaxis .yAxis.title{
+ position: absolute;
+ color: #4d4d4d;
+ white-space: nowrap;
+ bottom: 20px;
+ text-align: center;
+}
+
+.vis.timeline .dataaxis .yAxis.title.measure{
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 0px 0px;
+ visibility: hidden;
+ width: auto;
+}
+
+.vis.timeline .dataaxis .yAxis.title.left {
+ bottom: 0px;
+ -webkit-transform-origin: left top;
+ -moz-transform-origin: left top;
+ -ms-transform-origin: left top;
+ -o-transform-origin: left top;
+ transform-origin: left bottom;
+ -webkit-transform: rotate(-90deg);
+ -moz-transform: rotate(-90deg);
+ -ms-transform: rotate(-90deg);
+ -o-transform: rotate(-90deg);
+ transform: rotate(-90deg);
+}
+
+.vis.timeline .dataaxis .yAxis.title.right {
+ bottom: 0px;
+ -webkit-transform-origin: right bottom;
+ -moz-transform-origin: right bottom;
+ -ms-transform-origin: right bottom;
+ -o-transform-origin: right bottom;
+ transform-origin: right bottom;
+ -webkit-transform: rotate(90deg);
+ -moz-transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ -o-transform: rotate(90deg);
+ transform: rotate(90deg);
+}
.vis.timeline .legend {
background-color: rgba(247, 252, 255, 0.65);