UNPKG

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
// 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