node-red-contrib-smartnode-hook
Version:
this project is dependenced by the node-red-contrib-smartnode for some IoT display function
1,239 lines (989 loc) • 150 kB
JavaScript
// version: 2014-11-15
/**
* o--------------------------------------------------------------------------------o
* | This file is part of the RGraph package - you can learn more at: |
* | |
* | http://www.rgraph.net |
* | |
* | This package is licensed under the Creative Commons BY-NC license. That means |
* | that for non-commercial purposes it's free to use and for business use there's |
* | a 99 GBP per-company fee to pay. You can read the full license here: |
* | |
* | http://www.rgraph.net/license |
* o--------------------------------------------------------------------------------o
*/
RGraph = window.RGraph || {isRGraph: true};
/**
* The line chart constructor
*
* @param object canvas The cxanvas object
* @param array ... The lines to plot
*/
RGraph.Line = function (conf)
{
/**
* Allow for object config style
*/
if ( typeof conf === 'object'
&& typeof conf.data === 'object'
&& typeof conf.id === 'string') {
var id = conf.id;
var canvas = document.getElementById(id);
var data = conf.data;
var parseConfObjectForOptions = true; // Set this so the config is parsed (at the end of the constructor)
} else {
var id = conf;
var canvas = document.getElementById(id);
var data = arguments[1];
}
this.id = id;
this.canvas = canvas;
this.context = this.canvas.getContext('2d');
this.canvas.__object__ = this;
this.type = 'line';
this.max = 0;
this.coords = [];
this.coords2 = [];
this.coords.key = [];
this.coordsText = [];
this.coordsSpline = [];
this.hasnegativevalues = false;
this.isRGraph = true;
this.uid = RGraph.CreateUID();
this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.CreateUID();
this.colorsParsed = false;
this.original_colors = [];
this.firstDraw = true; // After the first draw this will be false
/**
* Compatibility with older browsers
*/
//RGraph.OldBrowserCompat(this.context);
// Various config type stuff
this.properties =
{
'chart.background.barcolor1': 'rgba(0,0,0,0)',
'chart.background.barcolor2': 'rgba(0,0,0,0)',
'chart.background.grid': 1,
'chart.background.grid.width': 1,
'chart.background.grid.hsize': 25,
'chart.background.grid.vsize': 25,
'chart.background.grid.color': '#ddd',
'chart.background.grid.vlines': true,
'chart.background.grid.hlines': true,
'chart.background.grid.border': true,
'chart.background.grid.autofit': true,
'chart.background.grid.autofit.align': false,
'chart.background.grid.autofit.numhlines': 5,
'chart.background.grid.autofit.numvlines': 20,
'chart.background.grid.dashed': false,
'chart.background.grid.dotted': false,
'chart.background.hbars': null,
'chart.background.image': null,
'chart.background.image.stretch': true,
'chart.background.image.x': null,
'chart.background.image.y': null,
'chart.background.image.w': null,
'chart.background.image.h': null,
'chart.background.image.align': null,
'chart.background.color': null,
'chart.labels': null,
'chart.labels.ingraph': null,
'chart.labels.above': false,
'chart.labels.above.size': 8,
'chart.labels.above.decimals': null,
'chart.xtickgap': 20,
'chart.smallxticks': 3,
'chart.largexticks': 5,
'chart.ytickgap': 20,
'chart.smallyticks': 3,
'chart.largeyticks': 5,
'chart.numyticks': 10,
'chart.linewidth': 2.01,
'chart.colors': ['red', '#0f0', '#00f', '#f0f', '#ff0', '#0ff','green','pink','blue','black'],
'chart.hmargin': 0,
'chart.tickmarks.dot.color': 'white',
'chart.tickmarks': 'endcircle',
'chart.tickmarks.linewidth': null,
'chart.tickmarks.image': null,
'chart.tickmarks.image.halign': 'center',
'chart.tickmarks.image.valign': 'center',
'chart.tickmarks.image.offsetx':0,
'chart.tickmarks.image.offsety':0,
'chart.ticksize': 3,
'chart.gutter.left': 25,
'chart.gutter.right': 25,
'chart.gutter.top': 25,
'chart.gutter.bottom': 25,
'chart.tickdirection': -1,
'chart.yaxispoints': 5,
'chart.fillstyle': null,
'chart.xaxispos': 'bottom',
'chart.yaxispos': 'left',
'chart.xticks': null,
'chart.text.size': 10,
'chart.text.angle': 0,
'chart.text.color': 'black',
'chart.text.font': 'Arial',
'chart.ymin': 0,
'chart.ymax': null,
'chart.title': '',
'chart.title.background': null,
'chart.title.hpos': null,
'chart.title.vpos': null,
'chart.title.bold': true,
'chart.title.font': null,
'chart.title.xaxis': '',
'chart.title.xaxis.bold': true,
'chart.title.xaxis.size': null,
'chart.title.xaxis.font': null,
'chart.title.yaxis': '',
'chart.title.yaxis.bold': true,
'chart.title.yaxis.size': null,
'chart.title.yaxis.font': null,
'chart.title.yaxis.color': null,
'chart.title.xaxis.pos': null,
'chart.title.yaxis.pos': null,
'chart.title.yaxis.x': null,
'chart.title.yaxis.y': null,
'chart.title.xaxis.x': null,
'chart.title.xaxis.y': null,
'chart.title.x': null,
'chart.title.y': null,
'chart.title.halign': null,
'chart.title.valign': null,
'chart.shadow': true,
'chart.shadow.offsetx': 2,
'chart.shadow.offsety': 2,
'chart.shadow.blur': 3,
'chart.shadow.color': 'rgba(128,128,128,0.5)',
'chart.tooltips': null,
'chart.tooltips.hotspot.xonly': false,
'chart.tooltips.hotspot.size': 5,
'chart.tooltips.effect': 'fade',
'chart.tooltips.css.class': 'RGraph_tooltip',
'chart.tooltips.event': 'onmousemove',
'chart.tooltips.highlight': true,
'chart.tooltips.coords.page': false,
'chart.highlight.stroke': 'gray',
'chart.highlight.fill': 'white',
'chart.stepped': false,
'chart.key': null,
'chart.key.background': 'white',
'chart.key.position': 'graph',
'chart.key.halign': null,
'chart.key.shadow': false,
'chart.key.shadow.color': '#666',
'chart.key.shadow.blur': 3,
'chart.key.shadow.offsetx': 2,
'chart.key.shadow.offsety': 2,
'chart.key.position.gutter.boxed': false,
'chart.key.position.x': null,
'chart.key.position.y': null,
'chart.key.color.shape': 'square',
'chart.key.rounded': true,
'chart.key.linewidth': 1,
'chart.key.colors': null,
'chart.key.interactive': false,
'chart.key.interactive.highlight.chart.stroke': 'rgba(255,0,0,0.3)',
'chart.key.interactive.highlight.label': 'rgba(255,0,0,0.2)',
'chart.key.text.color': 'black',
'chart.contextmenu': null,
'chart.ylabels': true,
'chart.ylabels.count': 5,
'chart.ylabels.inside': false,
'chart.scale.invert': false,
'chart.xlabels.inside': false,
'chart.xlabels.inside.color': 'rgba(255,255,255,0.5)',
'chart.noaxes': false,
'chart.noyaxis': false,
'chart.noxaxis': false,
'chart.noendxtick': false,
'chart.noendytick': false,
'chart.units.post': '',
'chart.units.pre': '',
'chart.scale.zerostart': false,
'chart.scale.decimals': null,
'chart.scale.point': '.',
'chart.scale.thousand': ',',
'chart.crosshairs': false,
'chart.crosshairs.color': '#333',
'chart.crosshairs.hline': true,
'chart.crosshairs.vline': true,
'chart.annotatable': false,
'chart.annotate.color': 'black',
'chart.axesontop': false,
'chart.filled': false,
'chart.filled.range': false,
'chart.filled.range.threshold': null,
'chart.filled.range.threshold.colors': ['red', 'green'],
'chart.filled.accumulative': true,
'chart.variant': null,
'chart.axis.color': 'black',
'chart.axis.linewidth': 1,
'chart.numxticks': (data && typeof(data[0]) == 'number' ? data.length - 1: 20),
'chart.numyticks': 10,
'chart.zoom.factor': 1.5,
'chart.zoom.fade.in': true,
'chart.zoom.fade.out': true,
'chart.zoom.hdir': 'right',
'chart.zoom.vdir': 'down',
'chart.zoom.frames': 25,
'chart.zoom.delay': 16.666,
'chart.zoom.shadow': true,
'chart.zoom.background': true,
'chart.zoom.action': 'zoom',
'chart.backdrop': false,
'chart.backdrop.size': 30,
'chart.backdrop.alpha': 0.2,
'chart.resizable': false,
'chart.resize.handle.adjust': [0,0],
'chart.resize.handle.background': null,
'chart.adjustable': false,
'chart.noredraw': false,
'chart.outofbounds': false,
'chart.chromefix': true,
'chart.animation.factor': 1,
'chart.animation.unfold.x': false,
'chart.animation.unfold.y': true,
'chart.animation.unfold.initial': 2,
'chart.animation.trace.clip': 1,
'chart.curvy': false,
'chart.line.visible': true,
'chart.events.click': null,
'chart.events.mousemove': null
}
/**
* Change null arguments to empty arrays
*/
for (var i=1; i<arguments.length; ++i) {
if (typeof(arguments[i]) == 'null' || !arguments[i]) {
arguments[i] = [];
}
}
/**
* Store the original data. This also allows for giving arguments as one big array.
*/
this.original_data = [];
// This allows for the new object based configuration style
if (typeof conf === 'object' && conf.data) {
if (typeof conf.data[0] === 'number' || RGraph.isNull(conf.data[0])) {
this.original_data[0] = RGraph.arrayClone(conf.data);
//} else if (typeof conf.data[0] === 'object' && !RGraph.isNull(conf.data[0])) {
} else {
for (var i=0; i<conf.data.length; ++i) {
this.original_data[i] = RGraph.arrayClone(conf.data[i]);
}
}
// Allow for the older configuration style
} else {
for (var i=1; i<arguments.length; ++i) {
if ( arguments[1]
&& typeof(arguments[1]) == 'object'
&& arguments[1][0]
&& typeof(arguments[1][0]) == 'object'
&& arguments[1][0].length) {
var tmp = [];
for (var i=0; i<arguments[1].length; ++i) {
tmp[i] = RGraph.array_clone(arguments[1][i]);
}
for (var j=0; j<tmp.length; ++j) {
this.original_data[j] = RGraph.array_clone(tmp[j]);
}
} else {
this.original_data[i - 1] = RGraph.array_clone(arguments[i]);
}
}
}
// Check for support
if (!this.canvas) {
alert('[LINE] Fatal error: no canvas support');
return;
}
/**
* Store the data here as one big array
*/
this.data_arr = RGraph.array_linearize(this.original_data);
for (var i=0; i<this.data_arr.length; ++i) {
this['$' + i] = {};
}
/**
* Translate half a pixel for antialiasing purposes - but only if it hasn't beeen
* done already
*/
if (!this.canvas.__rgraph_aa_translated__) {
this.context.translate(0.5,0.5);
this.canvas.__rgraph_aa_translated__ = true;
}
// Short variable names
var RG = RGraph;
var ca = this.canvas;
var co = ca.getContext('2d');
var prop = this.properties;
var jq = jQuery;
var pa = RG.Path;
var win = window;
var doc = document;
var ma = Math;
/**
* "Decorate" the object with the generic effects if the effects library has been included
*/
if (RG.Effects && typeof RG.Effects.decorate === 'function') {
RG.Effects.decorate(this);
}
/**
* An all encompassing accessor
*
* @param string name The name of the property
* @param mixed value The value of the property
*/
this.set =
this.Set = function (name)
{
var value = typeof arguments[1] === 'undefined' ? null : arguments[1];
/**
* the number of arguments is only one and it's an
* object - parse it for configuration data and return.
*/
if (arguments.length === 1 && typeof name === 'object') {
RG.parseObjectStyleConfig(this, name);
return this;
}
name = name.toLowerCase();
/**
* This should be done first - prepend the propertyy name with "chart." if necessary
*/
if (name.substr(0,6) != 'chart.') {
name = 'chart.' + name;
}
// Consolidate the tooltips
if (name == 'chart.tooltips' && typeof value == 'object' && value) {
var tooltips = [];
for (var i=1; i<arguments.length; i++) {
if (typeof(arguments[i]) == 'object' && arguments[i][0]) {
for (var j=0; j<arguments[i].length; j++) {
tooltips.push(arguments[i][j]);
}
} else if (typeof(arguments[i]) == 'function') {
tooltips = arguments[i];
} else {
tooltips.push(arguments[i]);
}
}
// Because "value" is used further down at the end of this function, set it to the expanded array os tooltips
value = tooltips;
}
/**
* If (buggy) Chrome and the linewidth is 1, change it to 1.01
*/
if (name == 'chart.linewidth' && navigator.userAgent.match(/Chrome/)) {
if (value == 1) {
value = 1.01;
} else if (RGraph.is_array(value)) {
for (var i=0; i<value.length; ++i) {
if (typeof(value[i]) == 'number' && value[i] == 1) {
value[i] = 1.01;
}
}
}
}
/**
* Check for xaxispos
*/
if (name == 'chart.xaxispos' ) {
if (value != 'bottom' && value != 'center' && value != 'top') {
alert('[LINE] (' + this.id + ') chart.xaxispos should be top, center or bottom. Tried to set it to: ' + value + ' Changing it to center');
value = 'center';
}
}
/**
* chart.xticks is now called chart.numxticks
*/
if (name == 'chart.xticks') {
name = 'chart.numxticks';
}
/**
* Change the new chart.spline option to chart.curvy
*/
if (name == 'chart.spline') {
name = 'chart.curvy';
}
/**
* Chnge chart.ylabels.invert to chart.scale.invert
*/
if (name == 'chart.ylabels.invert') {
name = 'chart.scale.invert';
}
this.properties[name] = value;
return this;
};
/**
* An all encompassing accessor
*
* @param string name The name of the property
*/
this.get =
this.Get = function (name)
{
/**
* This should be done first - prepend the property name with "chart." if necessary
*/
if (name.substr(0,6) != 'chart.') {
name = 'chart.' + name;
}
/**
* If requested property is chart.spline - change it to chart.curvy
*/
if (name == 'chart.spline') {
name = 'chart.curvy';
}
return prop[name];
};
/**
* The function you call to draw the line chart
*
* @param bool An optional bool used internally to ditinguish whether the
* line chart is being called by the bar chart
*
* Draw()
* |
* +--Draw()
* | |
* | +-DrawLine()
* |
* +-RedrawLine()
* |
* +-DrawCurvyLine()
* |
* +-DrawSpline()
*/
this.draw =
this.Draw = function ()
{
// MUST be the first thing done!
if (typeof(prop['chart.background.image']) == 'string') {
RG.DrawBackgroundImage(this);
}
/**
* Fire the onbeforedraw event
*/
RG.FireCustomEvent(this, 'onbeforedraw');
/**
* Parse the colors. This allows for simple gradient syntax
*/
if (!this.colorsParsed) {
this.parseColors();
// Don't want to do this again
this.colorsParsed = true;
}
/**
* This is new in May 2011 and facilitates indiviual gutter settings,
* eg chart.gutter.left
*/
this.gutterLeft = prop['chart.gutter.left'];
this.gutterRight = prop['chart.gutter.right'];
this.gutterTop = prop['chart.gutter.top'];
this.gutterBottom = prop['chart.gutter.bottom'];
/**
* Check for Chrome 6 and shadow
*
* TODO Remove once it's been fixed (for a while)
* 07/03/2014 - Removed
* 29/10/2011 - Looks like it's been fixed as long the linewidth is at least 1.01
* SEARCH TAGS: CHROME FIX SHADOW BUG
*/
//if ( prop['chart.shadow']
// && RG.ISCHROME
// && prop['chart.linewidth'] <= 1
// && prop['chart.chromefix']
// && prop['chart.shadow.blur'] > 0) {
// alert('[RGRAPH WARNING] Chrome has a shadow bug, meaning you should increase the linewidth to at least 1.01');
//}
// Reset the data back to that which was initially supplied
this.data = RG.array_clone(this.original_data);
// Reset the max value
this.max = 0;
/**
* Reverse the datasets so that the data and the labels tally
* COMMENTED OUT 15TH AUGUST 2011
*/
//this.data = RG.array_reverse(this.data);
if (prop['chart.filled'] && !prop['chart.filled.range'] && this.data.length > 1 && prop['chart.filled.accumulative']) {
var accumulation = [];
for (var set=0; set<this.data.length; ++set) {
for (var point=0; point<this.data[set].length; ++point) {
this.data[set][point] = Number(accumulation[point] ? accumulation[point] : 0) + this.data[set][point];
accumulation[point] = this.data[set][point];
}
}
}
/**
* Get the maximum Y scale value
*/
if (prop['chart.ymax']) {
this.max = prop['chart.ymax'];
this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
this.scale2 = RG.getScale2(this, {
'max':this.max,
'min':prop['chart.ymin'],
'strict':true,
'scale.thousand':prop['chart.scale.thousand'],
'scale.point':prop['chart.scale.point'],
'scale.decimals':prop['chart.scale.decimals'],
'ylabels.count':prop['chart.ylabels.count'],
'scale.round':prop['chart.scale.round'],
'units.pre': prop['chart.units.pre'],
'units.post': prop['chart.units.post']
});
this.max = this.scale2.max ? this.scale2.max : 0;
// Check for negative values
if (!prop['chart.outofbounds']) {
for (dataset=0; dataset<this.data.length; ++dataset) {
if (RGraph.isArray(this.data[dataset])) {
for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) {
// Check for negative values
this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues;
}
}
}
}
} else {
this.min = prop['chart.ymin'] ? prop['chart.ymin'] : 0;
// Work out the max Y value
for (dataset=0; dataset<this.data.length; ++dataset) {
for (var datapoint=0; datapoint<this.data[dataset].length; datapoint++) {
this.max = Math.max(this.max, this.data[dataset][datapoint] ? Math.abs(parseFloat(this.data[dataset][datapoint])) : 0);
// Check for negative values
if (!prop['chart.outofbounds']) {
this.hasnegativevalues = (this.data[dataset][datapoint] < 0) || this.hasnegativevalues;
}
}
}
this.scale2 = RG.getScale2(this, {
'max':this.max,
'min':prop['chart.ymin'],
'scale.thousand':prop['chart.scale.thousand'],
'scale.point':prop['chart.scale.point'],
'scale.decimals':prop['chart.scale.decimals'],
'ylabels.count':prop['chart.ylabels.count'],
'scale.round':prop['chart.scale.round'],
'units.pre': prop['chart.units.pre'],
'units.post': prop['chart.units.post']
});
this.max = this.scale2.max ? this.scale2.max : 0;
}
/**
* Setup the context menu if required
*/
if (prop['chart.contextmenu']) {
RG.ShowContext(this);
}
/**
* Reset the coords arrays otherwise it will keep growing
*/
this.coords = [];
this.coordsText = [];
/**
* Work out a few things. They need to be here because they depend on things you can change before you
* call Draw() but after you instantiate the object
*/
this.grapharea = ca.height - this.gutterTop - this.gutterBottom;
this.halfgrapharea = this.grapharea / 2;
this.halfTextHeight = prop['chart.text.size'] / 2;
// Check the combination of the X axis position and if there any negative values
//
// 19th Dec 2010 - removed for Opera since it can be reported incorrectly whn there
// are multiple graphs on the page
if (prop['chart.xaxispos'] == 'bottom' && this.hasnegativevalues && !RG.ISOPERA) {
alert('[LINE] You have negative values and the X axis is at the bottom. This is not good...');
}
if (prop['chart.variant'] == '3d') {
RG.Draw3DAxes(this);
}
// Progressively Draw the chart
RG.background.Draw(this);
/**
* Draw any horizontal bars that have been defined
*/
if (prop['chart.background.hbars'] && prop['chart.background.hbars'].length > 0) {
RG.DrawBars(this);
}
if (prop['chart.axesontop'] == false) {
this.DrawAxes();
}
//if (typeof(shadowColor) == 'object') {
// shadowColor = RG.array_reverse(RG.array_clone(prop['chart.shadow.color']]);
//}
/**
* This facilitates the new Trace2 effect
*/
co.save()
co.beginPath();
co.rect(0, 0, ca.width * prop['chart.animation.trace.clip'], ca.height);
co.clip();
for (var i=0, j=0, len=this.data.length; i<len; i++, j++) {
co.beginPath();
/**
* Turn on the shadow if required
*/
if (!prop['chart.filled']) {
this.SetShadow(i);
}
/**
* Draw the line
*/
if (prop['chart.fillstyle']) {
if (typeof(prop['chart.fillstyle']) == 'object' && prop['chart.fillstyle'][j]) {
var fill = prop['chart.fillstyle'][j];
} else if (typeof(prop['chart.fillstyle']) == 'object' && prop['chart.fillstyle'].toString().indexOf('Gradient') > 0) {
var fill = prop['chart.fillstyle'];
} else if (typeof(prop['chart.fillstyle']) == 'string') {
var fill = prop['chart.fillstyle'];
}
} else if (prop['chart.filled']) {
var fill = prop['chart.colors'][j];
} else {
var fill = null;
}
/**
* Figure out the tickmark to use
*/
if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'object') {
var tickmarks = prop['chart.tickmarks'][i];
} else if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'string') {
var tickmarks = prop['chart.tickmarks'];
} else if (prop['chart.tickmarks'] && typeof(prop['chart.tickmarks']) == 'function') {
var tickmarks = prop['chart.tickmarks'];
} else {
var tickmarks = null;
}
this.DrawLine(this.data[i],
prop['chart.colors'][j],
fill,
this.GetLineWidth(j),
tickmarks,
i);
co.stroke();
}
/**
* If the line is filled re-stroke the lines
*/
if (prop['chart.filled'] && prop['chart.filled.accumulative'] && !prop['chart.curvy']) {
for (var i=0; i<this.coords2.length; ++i) {
co.beginPath();
co.lineWidth = this.GetLineWidth(i);
co.strokeStyle = prop['chart.colors'][i];
for (var j=0,len=this.coords2[i].length; j<len; ++j) {
if (j == 0 || this.coords2[i][j][1] == null || (this.coords2[i][j - 1] && this.coords2[i][j - 1][1] == null)) {
co.moveTo(this.coords2[i][j][0], this.coords2[i][j][1]);
} else {
if (prop['chart.stepped']) {
co.lineTo(this.coords2[i][j][0], this.coords2[i][j - 1][1]);
}
co.lineTo(this.coords2[i][j][0], this.coords2[i][j][1]);
}
}
co.stroke();
// No fill!
}
//Redraw the tickmarks
if (prop['chart.tickmarks']) {
co.beginPath();
co.fillStyle = 'white';
for (var i=0,len=this.coords2.length; i<len; ++i) {
co.beginPath();
co.strokeStyle = prop['chart.colors'][i];
for (var j=0; j<this.coords2[i].length; ++j) {
if (typeof(this.coords2[i][j]) == 'object' && typeof(this.coords2[i][j][0]) == 'number' && typeof(this.coords2[i][j][1]) == 'number') {
var tickmarks = typeof(prop['chart.tickmarks']) == 'object' ? prop['chart.tickmarks'][i] : prop['chart.tickmarks'];
this.DrawTick( this.coords2[i],
this.coords2[i][j][0],
this.coords2[i][j][1],
co.strokeStyle,
false,
j == 0 ? 0 : this.coords2[i][j - 1][0],
j == 0 ? 0 : this.coords2[i][j - 1][1],
tickmarks,
j);
}
}
}
co.stroke();
co.fill();
}
} else if (prop['chart.filled'] && prop['chart.filled.accumulative'] && prop['chart.curvy']) {
// Restroke the curvy filled accumulative lines
for (var i=0; i<this.coordsSpline.length; i+=1) {
co.beginPath();
co.strokeStyle = prop['chart.colors'][i];
co.lineWidth = this.GetLineWidth(i);
for (var j=0,len=this.coordsSpline[i].length; j<len; j+=1) {
var point = this.coordsSpline[i][j];
j == 0 ? co.moveTo(point[0], point[1]) : co.lineTo(point[0], point[1]);
}
co.stroke();
}
for (var i=0,len=this.coords2.length; i<len; i+=1) {
for (var j=0,len2=this.coords2[i].length; j<len2; ++j) {
if (typeof(this.coords2[i][j]) == 'object' && typeof(this.coords2[i][j][0]) == 'number' && typeof(this.coords2[i][j][1]) == 'number') {
var tickmarks = typeof prop['chart.tickmarks'] == 'object' && !RGraph.is_null(prop['chart.tickmarks']) ? prop['chart.tickmarks'][i] : prop['chart.tickmarks'];
co.strokeStyle = prop['chart.colors'][i];
this.DrawTick( this.coords2[i],
this.coords2[i][j][0],
this.coords2[i][j][1],
prop['chart.colors'][i],
false,
j == 0 ? 0 : this.coords2[i][j - 1][0],
j == 0 ? 0 : this.coords2[i][j - 1][1],
tickmarks,
j);
}
}
}
}
co.restore();
// ???
co.beginPath();
/**
* If the axes have been requested to be on top, do that
*/
if (prop['chart.axesontop']) {
this.DrawAxes();
}
/**
* Draw the labels
*/
this.DrawLabels();
/**
* Draw the range if necessary
*/
this.DrawRange();
// Draw a key if necessary
if (prop['chart.key'] && prop['chart.key'].length && RG.DrawKey) {
RG.DrawKey(this, prop['chart.key'], prop['chart.colors']);
}
/**
* Draw " above" labels if enabled
*/
if (prop['chart.labels.above']) {
this.DrawAboveLabels();
}
/**
* Draw the "in graph" labels
*/
RG.DrawInGraphLabels(this);
/**
* Redraw the lines if a filled range is on the cards
*/
if (prop['chart.filled'] && prop['chart.filled.range'] && this.data.length == 2) {
co.beginPath();
var len = this.coords.length / 2;
co.lineWidth = prop['chart.linewidth'];
co.strokeStyle = prop['chart.colors'][0];
for (var i=0; i<len; ++i) {
if (!RG.is_null(this.coords[i][1])) {
if (i == 0) {
co.moveTo(this.coords[i][0], this.coords[i][1]);
} else {
co.lineTo(this.coords[i][0], this.coords[i][1]);
}
}
}
co.stroke();
co.beginPath();
if (prop['chart.colors'][1]) {
co.strokeStyle = prop['chart.colors'][1];
}
for (var i=this.coords.length - 1; i>=len; --i) {
if (!RG.is_null(this.coords[i][1])) {
if (i == (this.coords.length - 1)) {
co.moveTo(this.coords[i][0], this.coords[i][1]);
} else {
co.lineTo(this.coords[i][0], this.coords[i][1]);
}
}
}
co.stroke();
} else if (prop['chart.filled'] && prop['chart.filled.range']) {
alert('[LINE] You must have only two sets of data for a filled range chart');
}
/**
* This function enables resizing
*/
if (prop['chart.resizable']) {
RG.AllowResizing(this);
}
/**
* This installs the event listeners
*/
RG.InstallEventListeners(this);
/**
* Fire the onfirstdraw event
*/
if (this.firstDraw) {
RG.fireCustomEvent(this, 'onfirstdraw');
this.firstDraw = false;
this.firstDrawFunc();
}
/**
* Fire the RGraph ondraw event
*/
RG.FireCustomEvent(this, 'ondraw');
return this;
};
/**
* Draws the axes
*/
this.drawAxes =
this.DrawAxes = function ()
{
//var RG = RGraph;
//var ca = this.canvas;
//var co = this.context;
//var prop = this.properties;
// Don't draw the axes?
if (prop['chart.noaxes']) {
return;
}
// Turn any shadow off
RG.NoShadow(this);
co.lineWidth = prop['chart.axis.linewidth'] + 0.001;
co.lineCap = 'butt';
co.strokeStyle = prop['chart.axis.color'];
co.beginPath();
// Draw the X axis
if (prop['chart.noxaxis'] == false) {
if (prop['chart.xaxispos'] == 'center') {
co.moveTo(this.gutterLeft, Math.round((this.grapharea / 2) + this.gutterTop));
co.lineTo(ca.width - this.gutterRight, Math.round((this.grapharea / 2) + this.gutterTop));
} else if (prop['chart.xaxispos'] === 'top') {
co.moveTo(this.gutterLeft, this.gutterTop);
co.lineTo(ca.width - this.gutterRight, this.gutterTop);
} else {
co.moveTo(this.gutterLeft, ca.height - this.gutterBottom);
co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
}
}
// Draw the Y axis
if (prop['chart.noyaxis'] == false) {
if (prop['chart.yaxispos'] == 'left') {
co.moveTo(this.gutterLeft, this.gutterTop);
co.lineTo(this.gutterLeft, ca.height - this.gutterBottom);
} else {
co.moveTo(ca.width - this.gutterRight, this.gutterTop);
co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
}
}
/**
* Draw the X tickmarks
*/
if (prop['chart.noxaxis'] == false && prop['chart.numxticks'] > 0) {
var xTickInterval = (ca.width - this.gutterLeft - this.gutterRight) / prop['chart.numxticks'];
if (!xTickInterval || xTickInterval <= 0) {
xTickInterval = (ca.width - this.gutterLeft - this.gutterRight) / (prop['chart.labels'] && prop['chart.labels'].length ? prop['chart.labels'].length - 1 : 10);
}
for (x=this.gutterLeft + (prop['chart.yaxispos'] == 'left' ? xTickInterval : 0); x<=(ca.width - this.gutterRight + 1 ); x+=xTickInterval) {
if (prop['chart.yaxispos'] == 'right' && x >= (ca.width - this.gutterRight - 1) ) {
break;
}
// If the last tick is not desired...
if (prop['chart.noendxtick']) {
if (prop['chart.yaxispos'] == 'left' && x >= (ca.width - this.gutterRight - 1)) {
break;
} else if (prop['chart.yaxispos'] == 'right' && x == this.gutterLeft) {
continue;
}
}
var yStart = prop['chart.xaxispos'] == 'center' ? (this.gutterTop + (this.grapharea / 2)) - 3 : ca.height - this.gutterBottom;
var yEnd = prop['chart.xaxispos'] == 'center' ? yStart + 6 : ca.height - this.gutterBottom - (x % 60 == 0 ? prop['chart.largexticks'] * prop['chart.tickdirection'] : prop['chart.smallxticks'] * prop['chart.tickdirection']);
if (prop['chart.xaxispos'] == 'center') {
var yStart = Math.round((this.gutterTop + (this.grapharea / 2))) - 3;
var yEnd = yStart + 6;
} else if (prop['chart.xaxispos'] == 'bottom') {
var yStart = ca.height - this.gutterBottom;
var yEnd = ca.height - this.gutterBottom - (x % 60 == 0 ? prop['chart.largexticks'] * prop['chart.tickdirection'] : prop['chart.smallxticks'] * prop['chart.tickdirection']);
yEnd += 0;
} else if (prop['chart.xaxispos'] == 'top') {
yStart = this.gutterTop - 3;
yEnd = this.gutterTop;
}
co.moveTo(Math.round(x), yStart);
co.lineTo(Math.round(x), yEnd);
}
// Draw an extra tickmark if there is no X axis, but there IS a Y axis
} else if (prop['chart.noyaxis'] == false && prop['chart.numyticks'] > 0) {
if (!prop['chart.noendytick']) {
if (prop['chart.yaxispos'] == 'left') {
co.moveTo(this.gutterLeft, Math.round(ca.height - this.gutterBottom));
co.lineTo(this.gutterLeft - prop['chart.smallyticks'], Math.round(ca.height - this.gutterBottom));
} else {
co.moveTo(ca.width - this.gutterRight, Math.round(ca.height - this.gutterBottom));
co.lineTo(ca.width - this.gutterRight + prop['chart.smallyticks'], Math.round(ca.height - this.gutterBottom));
}
}
}
/**
* Draw the Y tickmarks
*/
var numyticks = prop['chart.numyticks'];
if (prop['chart.noyaxis'] == false && numyticks > 0) {
var counter = 0;
var adjustment = 0;
if (prop['chart.yaxispos'] == 'right') {
adjustment = (ca.width - this.gutterLeft - this.gutterRight);
}
// X axis at the center
if (prop['chart.xaxispos'] == 'center') {
var interval = (this.grapharea / numyticks);
var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft : ca.width - this.gutterRight + prop['chart.smallyticks']);
// Draw the upper halves Y tick marks
for (y=this.gutterTop; y<(this.grapharea / 2) + this.gutterTop; y+=interval) {
if (y < (this.grapharea / 2) + this.gutterTop) {
co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
co.lineTo(lineto, Math.round(y));
}
}
// Draw the lower halves Y tick marks
for (y=this.gutterTop + (this.halfgrapharea) + interval; y <= this.grapharea + this.gutterTop; y+=interval) {
co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
co.lineTo(lineto, Math.round(y));
}
// X axis at the top
} else if (prop['chart.xaxispos'] == 'top') {
var interval = (this.grapharea / numyticks);
var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft : ca.width - this.gutterRight + prop['chart.smallyticks']);
// Draw the Y tick marks
for (y=this.gutterTop + interval; y <=this.grapharea + this.gutterTop; y+=interval) {
co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), Math.round(y));
co.lineTo(lineto, Math.round(y));
}
// If there's no X axis draw an extra tick
if (prop['chart.noxaxis'] && prop['chart.noendytick'] == false) {
co.moveTo((prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight), this.gutterTop);
co.lineTo(lineto, this.gutterTop);
}
// X axis at the bottom
} else {
var lineto = (prop['chart.yaxispos'] == 'left' ? this.gutterLeft - prop['chart.smallyticks'] : ca.width - this.gutterRight + prop['chart.smallyticks']);
for (y=this.gutterTop; y<(ca.height - this.gutterBottom) && counter < numyticks; y+=( (ca.height - this.gutterTop - this.gutterBottom) / numyticks) ) {
co.moveTo(this.gutterLeft + adjustment, Math.round(y));
co.lineTo(lineto, Math.round(y));
var counter = counter + 1;
}
}
// Draw an extra X tickmark
} else if (prop['chart.noxaxis'] == false && prop['chart.numxticks'] > 0) {
if (prop['chart.yaxispos'] == 'left') {
co.moveTo(this.gutterLeft, prop['chart.xaxispos'] == 'top' ? this.gutterTop : ca.height - this.gutterBottom);
co.lineTo(this.gutterLeft, prop['chart.xaxispos'] == 'top' ? this.gutterTop - prop['chart.smallxticks'] : ca.height - this.gutterBottom + prop['chart.smallxticks']);
} else {
co.moveTo(ca.width - this.gutterRight, ca.height - this.gutterBottom);
co.lineTo(ca.width - this.gutterRight, ca.height - this.gutterBottom + prop['chart.smallxticks']);
}
}
co.stroke();
};
/**
* Draw the text labels for the axes
*/
this.drawLabels =
this.DrawLabels