// o---------------------------------------------------------------------------------o // | This file is part of the RGraph package - you can learn more at: | // | | // | https://www.rgraph.net/license.html | // | | // | RGraph is dual-licensed under the Open Source GPL license. That means that it's | // | free to use and there are no restrictions on what you can use RGraph for! | // | If the GPL license does not suit you however, then there's an inexpensive | // | commercial license option available. See the URL above for more details. | // o---------------------------------------------------------------------------------o RGraph = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true}; // // The odometer constructor. Pass it the ID of the canvas tag, the start value of the odo, // the end value, and the value that the pointer should point to. // RGraph.Odometer = function (conf) { var id = conf.id, canvas = document.getElementById(id), min = conf.min, max = conf.max, value = conf.value; this.id = id; this.canvas = canvas; this.context = this.canvas.getContext ? this.canvas.getContext("2d", {alpha: (typeof id === 'object' && id.alpha === false) ? false : true}) : null; this.canvas.__object__ = this; this.type = 'odo'; this.isRGraph = true; this.isrgraph = true; this.rgraph = true; this.min = RGraph.stringsToNumbers(min); this.max = RGraph.stringsToNumbers(max); this.value = RGraph.stringsToNumbers(value); this.currentValue = null; this.uid = RGraph.createUID(); this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.createUID(); this.colorsParsed = false; this.coordsText = []; this.original_colors = []; this.firstDraw = true; // After the first draw this will be false this.stopAnimationRequested = false;// Used to control the animations this.properties = { backgroundBorder: 'black', backgroundColor: '#eee', backgroundLinesColor: '#ddd', centerx: null, centery: null, radius: null, labelsMargin: 35, labelsFont: null, labelsSize: null, labelsColor: null, labelsBold: null, labelsItalic: null, labelsValue: false, labelsValueDecimals: 0, labelsValuePoint: '.', labelsValueThousand: ',', labelsValueUnitsPre: '', labelsValueUnitsPost: '', labelsValueFont: null, labelsValueSize: null, labelsValueColor: null, labelsValueBold: null, labelsValueItalic: null, labelsValueOffsetx: 0, labelsValueOffsety: 0, needleColor: 'black', needleLength: null, needleWidth: 2, needleHead: true, needleTail: true, needleType: 'pointer', needleTriangleBorder: '#aaa', textSize: 12, textColor: 'black', textFont: 'Arial, Verdana, sans-serif', textBold: false, textItalic: false, textAccessible: false, textAccessibleOverflow: 'visible', textAccessiblePointerevents: false, text: null, colorsGreenMax: max * 0.75, colorsGreenColor: 'Gradient(white:#0c0)', colorsYellowColor: 'Gradient(white:#ff0)', colorsRedMin: max * 0.9, colorsRedColor: 'Gradient(white:#f00)', marginLeft: 35, marginRight: 35, marginTop: 35, marginBottom: 35, title: '', titleFont: null, titleBold: null, titleItalic: null, titleSize: null, titleColor: null, titleX: null, titleY: null, titleHalign: null, titleValign: null, titleOffsetx: 0, titleOffsety: 0, titleSubtitle: '', titleSubtitleSize: null, titleSubtitleColor: '#aaa', titleSubtitleFont: null, titleSubtitleBold: null, titleSubtitleItalic: null, titleSubtitleOffsetx: 0, titleSubtitleOffsety: 0, contextmenu: null, linewidth: 1, shadowInner: false, shadowInnerColor: 'black', shadowInnerOffsetx: 3, shadowInnerOffsety: 3, shadowInnerBlur: 6, shadowOuter: false, shadowOuterColor: 'black', shadowOuterOffsetx: 3, shadowOuterOffsety: 3, shadowOuterBlur: 6, annotatable: false, annotatableColor: 'black', scaleDecimals: 0, scalePoint: '.', scaleThousand: ',', scaleUnitsPre: '', scaleUnitsPost: '', scaleZerostart: false, resizable: false, resizableHandleAdjust: [0,0], resizableHandleBackground: null, border: true, borderColor1: '#BEBCB0', borderColor2: '#F0EFEA', borderColor3: '#BEBCB0', tickmarks: false, tickmarksHighlighted: true, tickmarksLargeColor: '#999', labels: null, key: null, keyBackground: 'white', keyPosition: 'graph', keyShadow: false, keyShadowColor: '#666', keyShadowBlur: 3, keyShadowOffsetx: 2, keyShadowOffsety: 2, keyPositionMarginBoxed: false, keyPositionMarginHSpace: 0, keyPositionX: null, keyPositionY: null, keyHalign: 'right', keyColorShape: 'square', keyRounded: true, keyColors: null, keyLabelsSize: null, keyLabelsFont: null, keyLabelsColor: null, keyLabelsBold: null, keyLabelsItalic: null, keyLabelsOffsetx: 0, keyLabelsOffsety: 0, keyFormattedDecimals: 0, keyFormattedPoint: '.', keyFormattedThousand: ',', keyFormattedUnitsPre: '', keyFormattedUnitsPost: '', keyFormattedValueSpecific: null, keyFormattedItemsCount: null, adjustable: false, clearto: 'rgba(0,0,0,0)' } // // Add the reverse look-up table for property names // so that property names can be specified in any case. // this.properties_lowercase_map = []; for (var i in this.properties) { if (typeof i === 'string') { this.properties_lowercase_map[i.toLowerCase()] = i; } } // Easy access to properties and the path function var properties = this.properties; this.path = RGraph.pathObjectFunction; // // "Decorate" the object with the generic effects if the effects library has been included // if (RGraph.Effects && typeof RGraph.Effects.decorate === 'function') { RGraph.Effects.decorate(this); } // Add the responsive method. This method resides in the common file. this.responsive = RGraph.responsive; // // A peudo setter // this.set = function (name) { var value = typeof arguments[1] === 'undefined' ? null : arguments[1]; // Go through all of the properties and make sure // that they're using the correct capitalisation if (typeof name === 'string') { name = this.properties_lowercase_map[name.toLowerCase()] || name; } // Set the colorsParsed flag to false if the colors // property is being set if ( name === 'colorsRedColor' || name === 'colorsYellowColor' || name === 'colorsGreenColor' ) { this.colorsParsed = false; } // the number of arguments is only one and it's an // object - parse it for configuration data and return. if (arguments.length === 1 && typeof arguments[0] === 'object') { for (i in arguments[0]) { if (typeof i === 'string') { this.set(i, arguments[0][i]); } } return this; } properties[name] = value; return this; }; // // A getter // // @param name string The name of the property to get // this.get = function (name) { // Go through all of the properties and make sure // that they're using the correct capitalisation name = this.properties_lowercase_map[name.toLowerCase()] || name; return properties[name]; }; // // Draws the odometer // this.draw = function () { // // Fire the onbeforedraw event // RGraph.fireCustomEvent(this, 'onbeforedraw'); // Translate half a pixel for antialiasing purposes - but only if it hasn't been // done already // // MUST be the first thing done! // if (!this.canvas.__rgraph_aa_translated__) { this.context.translate(0.5,0.5); this.canvas.__rgraph_aa_translated__ = true; } // // Set the current value // this.currentValue = this.value; // // No longer allow values outside the range of the Odo // if (this.value > this.max) { this.value = this.max; } if (this.value < this.min) { this.value = this.min; } // // Make the margins easy ro access // this.marginLeft = properties.marginLeft; this.marginRight = properties.marginRight; this.marginTop = properties.marginTop; this.marginBottom = properties.marginBottom; // Work out a few things this.radius = Math.min( (this.canvas.width - this.marginLeft - this.marginRight) / 2, (this.canvas.height - this.marginTop - this.marginBottom) / 2 ) - (properties.border ? 25 : 0); this.diameter = 2 * this.radius; this.centerx = ((this.canvas.width - this.marginLeft- this.marginRight) / 2) + this.marginLeft; this.centery = ((this.canvas.height - this.marginTop - this.marginBottom) / 2) + this.marginTop; this.range = this.max - this.min; this.coordsText = []; // // Move the centerx if the key is defined // if (properties.key && properties.key.length > 0 && this.canvas.width > this.canvas.height) this.centerx = 5 + this.radius; if (typeof properties.centerx === 'number') this.centerx = properties.centerx; if (typeof properties.centery === 'number') this.centery = properties.centery; // // Allow the centerx/centery/radius to be a plus/minus // if (typeof properties.radius === 'string' && properties.radius.match(/^\+|-\d+$/) ) this.radius += parseFloat(properties.radius); if (typeof properties.centerx === 'string' && properties.centerx.match(/^\+|-\d+$/) ) this.centerx += parseFloat(properties.centerx); if (typeof properties.centery === 'string' && properties.centery.match(/^\+|-\d+$/) ) this.centery += parseFloat(properties.centery); // // Allow custom setting of the radius // if (typeof properties.radius === 'number') { this.radius = properties.radius; if (properties.border) { this.radius -= 25; } } // // Parse the colors for gradients. Its down here so that the center X/Y can be used // if (!this.colorsParsed) { this.parseColors(); // Don't want to do this again this.colorsParsed = true; } // // Install clipping // if (!RGraph.isNull(this.properties.clip)) { RGraph.clipTo.start(this, this.properties.clip); } this.context.lineWidth = properties.linewidth; // Draw the background this.drawBackground(); // And lastly, draw the labels this.drawLabels(); // Draw the needle if (RGraph.isArray(this.value)) { for (let i=0; i 0) { // Build a colors array out of the needle colors var colors = RGraph.isArray(properties.needleColor) ? properties.needleColor : [properties.needleColor]; RGraph.drawKey(this, properties.key, colors ); } // // Setup the context menu if required // if (properties.contextmenu) { RGraph.showContext(this); } // // Add custom text thats specified // RGraph.addCustomText(this); // // This installs the event listeners // RGraph.installEventListeners(this); // // End clipping // if (!RGraph.isNull(this.properties.clip)) { RGraph.clipTo.end(); } // // Fire the onfirstdraw event // if (this.firstDraw) { this.firstDraw = false; RGraph.fireCustomEvent(this, 'onfirstdraw'); this.firstDrawFunc(); } // // Fire the RGraph draw event // RGraph.fireCustomEvent(this, 'ondraw'); // // Install any inline responsive configuration. This // should be last in the draw function - even after // the draw events. // RGraph.installInlineResponsive(this); return this; }; // // Used in chaining. Runs a function there and then - not waiting for // the events to fire (eg the onbeforedraw event) // // @param function func The function to execute // this.exec = function (func) { func(this); return this; }; // // Draws the background // this.drawBackground = function () { this.context.beginPath(); // // Turn on the shadow if need be // if (properties.shadowOuter) { RGraph.setShadow( this, properties.shadowOuterColor, properties.shadowOuterOffsetx, properties.shadowOuterOffsety, properties.shadowOuterBlur ); } var backgroundColor = properties.backgroundColor; // Draw the grey border this.context.fillStyle = backgroundColor; this.context.arc( this.centerx, this.centery, this.radius + (properties.border ? 20 : 0), 0.0001, RGraph.TWOPI, false ); this.context.fill(); // // Turn off the shadow // RGraph.noShadow(this); // Draw a circle this.context.strokeStyle = '#666'; this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.TWOPI, false); // Now draw a big white circle to make the lines appear as tick marks // This is solely for Chrome this.context.fillStyle = backgroundColor; this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.TWOPI, false); this.context.fill(); // // Draw more tickmarks // if (properties.tickmarks) { this.context.beginPath(); this.context.strokeStyle = '#bbb'; for (var i=0; i<=360; i+=3) { this.context.arc(this.centerx, this.centery, this.radius, 0, i / 57.3, false); this.context.lineTo(this.centerx, this.centery); } this.context.stroke(); } this.context.beginPath(); this.context.lineWidth = 1; this.context.strokeStyle = 'black'; // Now draw a big white circle to make the lines appear as tick marks this.context.fillStyle = backgroundColor; this.context.strokeStyle = backgroundColor; this.context.arc(this.centerx, this.centery, this.radius - 5, 0, RGraph.TWOPI, false); this.context.fill(); this.context.stroke(); // Gray lines at 18 degree intervals this.context.beginPath(); this.context.strokeStyle = properties.backgroundLinesColor; for (var i=0; i<360; i+=18) { this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.toRadians(i), false); this.context.lineTo(this.centerx, this.centery); } this.context.stroke(); // Redraw the outer circle this.context.beginPath(); this.context.strokeStyle = properties.backgroundBorder; this.context.arc(this.centerx, this.centery, this.radius, 0, RGraph.TWOPI, false); this.context.stroke(); // // Now draw the center bits shadow if need be // if (properties.shadowInner) { this.context.beginPath(); RGraph.setShadow( this, properties.shadowInnerColor, properties.shadowInnerOffsetx, properties.shadowInnerOffsety, properties.shadowInnerBlur ); this.context.arc(this.centerx, this.centery, this.radius - properties.labelsMargin, 0, RGraph.TWOPI, 0); this.context.fill(); this.context.stroke(); // // Turn off the shadow // RGraph.noShadow(this); } // // Draw the green area // var greengrad = properties.colorsGreenColor; // Draw the "tick highlight" if (properties.tickmarksHighlighted) { this.context.beginPath(); this.context.lineWidth = 5; this.context.strokeStyle = greengrad; this.context.arc(this.centerx, this.centery, this.radius - 2.5, -1 * RGraph.HALFPI, (((properties.colorsGreenMax - this.min)/ (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI, 0); this.context.stroke(); this.context.lineWidth = 1; } this.context.beginPath(); this.context.fillStyle = greengrad; this.context.arc( this.centerx, this.centery, this.radius - properties.labelsMargin, 0 - RGraph.HALFPI, (((properties.colorsGreenMax - this.min)/ (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI, false ); this.context.lineTo(this.centerx, this.centery); this.context.closePath(); this.context.fill(); // // Draw the yellow area // var yellowgrad = properties.colorsYellowColor; // Draw the "tick highlight" if (properties.tickmarksHighlighted) { this.context.beginPath(); this.context.lineWidth = 5; this.context.strokeStyle = yellowgrad; this.context.arc(this.centerx, this.centery, this.radius - 2.5, ( ((properties.colorsGreenMax - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI, (((properties.colorsRedMin - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI, 0); this.context.stroke(); this.context.lineWidth = 1; } this.context.beginPath(); this.context.fillStyle = yellowgrad; this.context.arc( this.centerx, this.centery, this.radius - properties.labelsMargin, ( ((properties.colorsGreenMax - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI, ( ((properties.colorsRedMin - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI, false ); this.context.lineTo(this.centerx, this.centery); this.context.closePath(); this.context.fill(); // // Draw the red area // var redgrad = properties.colorsRedColor; // Draw the "tick highlight" if (properties.tickmarksHighlighted) { this.context.beginPath(); this.context.lineWidth = 5; this.context.strokeStyle = redgrad; this.context.arc(this.centerx, this.centery, this.radius - 2.5,(((properties.colorsRedMin - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI,RGraph.TWOPI - RGraph.HALFPI,0); this.context.stroke(); this.context.lineWidth = 1; } this.context.beginPath(); this.context.fillStyle = redgrad; this.context.strokeStyle = redgrad; this.context.arc( this.centerx, this.centery, this.radius - properties.labelsMargin, (((properties.colorsRedMin - this.min) / (this.max - this.min)) * RGraph.TWOPI) - RGraph.HALFPI, RGraph.TWOPI - RGraph.HALFPI, false ); this.context.lineTo(this.centerx, this.centery); this.context.closePath(); this.context.fill(); // // Draw the thick border // if (properties.border) { var grad = this.context.createRadialGradient(this.centerx, this.centery, this.radius, this.centerx, this.centery, this.radius + 20); grad.addColorStop(0, properties.borderColor1); grad.addColorStop(0.5, properties.borderColor2); grad.addColorStop(1, properties.borderColor3); this.context.beginPath(); this.context.fillStyle = grad; this.context.strokeStyle = 'rgba(0,0,0,0)' this.context.lineWidth = 0.001; this.context.arc(this.centerx, this.centery, this.radius + 20, 0, RGraph.TWOPI, 0); this.context.arc(this.centerx, this.centery, this.radius - 2, RGraph.TWOPI, 0, 1); this.context.fill(); } // Put the linewidth back to what it was this.context.lineWidth = properties.linewidth; // // Draw the title if specified // if (properties.title) { RGraph.drawTitle(this); } // Draw the big tick marks if (!properties.tickmarksHighlighted) { for (var i=18; i<=360; i+=36) { this.context.beginPath(); this.context.strokeStyle = properties.tickmarksLargeColor; this.context.lineWidth = 2; this.context.arc(this.centerx, this.centery, this.radius - 1, RGraph.toRadians(i), RGraph.toRadians(i+0.01), false); this.context.arc(this.centerx, this.centery, this.radius - 7, RGraph.toRadians(i), RGraph.toRadians(i+0.01), false); this.context.stroke(); } } }; // // Draws the needle of the odometer // // @param object opt An object map of the following // properties: // o value // o head // o width // o length // o type // o color // o triangleborder // this.drawNeedle = function (opt) { // The optional length of the needle opt.length = opt.length ? opt.length : this.radius - properties.labelsMargin - 10; // First draw a grey background circle at the center of // the Odo this.context.fillStyle = '#999'; this.context.beginPath(); this.context.moveTo(this.centerx, this.centery); this.context.arc( this.centerx, this.centery, 10, 0, RGraph.TWOPI, false ); this.context.closePath(); this.context.fill(); this.context.fill(); this.context.fillStyle = opt.color this.context.strokeStyle = '#666'; // Draw the centre bit this.context.beginPath(); this.context.moveTo(this.centerx, this.centery); this.context.arc(this.centerx, this.centery, 8, 0, RGraph.TWOPI, false); this.context.fill(); this.context.closePath(); this.context.stroke(); this.context.fill(); if (opt.type === 'pointer') { this.context.strokeStyle = opt.color; this.context.lineWidth = opt.linewidth; this.context.lineCap = 'round'; this.context.lineJoin = 'round'; // Draw the needle this.context.beginPath(); // The trailing bit on the opposite side of the dial this.context.beginPath(); this.context.moveTo( this.centerx, this.centery ); if (opt.tail) { this.context.arc( this.centerx, this.centery, 20, (opt.value / this.range) * RGraph.TWOPI + RGraph.HALFPI, (opt.value / this.range) * RGraph.TWOPI + RGraph.HALFPI, false ); } // Draw the long bit on the opposite side this.context.arc( this.centerx, this.centery, opt.length - 20, (((opt.value / this.range) * 360) - 90) / (180 / RGraph.PI), (((opt.value / this.range) * 360) - 90 + 0.1 ) / (180 / RGraph.PI), false ); this.context.closePath(); //this.context.stroke(); //this.context.fill(); } else if (opt.type === 'triangle') { this.context.lineWidth = 0.01; this.context.lineEnd = 'square'; this.context.lineJoin = 'miter'; // // This draws the version of the pointer that becomes the border // this.context.beginPath(); this.context.fillStyle = opt.triangleBorder; this.context.arc(this.centerx, this.centery, 11, (((opt.value / this.range) * 360)) / 57.3, ((((opt.value / this.range) * 360)) + 0.01) / 57.3, 0); this.context.arc(this.centerx, this.centery, 11, (((opt.value / this.range) * 360) + 180) / 57.3, ((((opt.value / this.range) * 360) + 180) + 0.01)/ 57.3, 0); this.context.arc(this.centerx, this.centery, opt.length - 5, (((opt.value / this.range) * 360) - 90) / 57.3, ((((opt.value / this.range) * 360) - 90) / 57.3) + 0.01, 0); this.context.closePath(); this.context.fill(); this.context.beginPath(); this.context.arc( this.centerx, this.centery, 15, 0, RGraph.TWOPI, false ); this.context.closePath(); this.context.fill(); // This draws the pointer this.path( 'b a % % % % % false a % % % % % false', this.centerx, this.centery, 7, (((opt.value / this.range) * 360)) / 57.3, ((((opt.value / this.range) * 360)) + 0.01) / 57.3, this.centerx, this.centery, 7, (((opt.value / this.range) * 360) + 180) / 57.3, ((((opt.value / this.range) * 360) + 180) + 0.01)/ 57.3 ); this.context.arc(this.centerx, this.centery, opt.length - 13, (((opt.value / this.range) * 360) - 90) / 57.3, ((((opt.value / this.range) * 360) - 90) / 57.3) + 0.01, 0); this.context.closePath(); this.context.strokeStyle = 'black'; this.context.fillStyle = opt.color; this.context.stroke(); this.context.fill(); } this.context.stroke(); this.context.fill(); // Draw the mini center circle this.context.beginPath(); this.context.fillStyle = opt.color; this.context.arc( this.centerx, this.centery, opt.type === 'pointer' ? 7 : 12, 0.01, RGraph.TWOPI, false ); this.context.fill(); // This draws the arrow at the end of the line if (opt.head && opt.type === 'pointer') { this.context.lineWidth = 1; this.context.fillStyle = opt.color; // round, bevel, miter this.context.lineJoin = 'miter'; this.context.lineCap = 'butt'; this.context.beginPath(); this.context.arc( this.centerx, this.centery, opt.length - 5, RGraph.toRadians(((opt.value / this.range) * 360) - 90), RGraph.toRadians(((opt.value / this.range) * 360) - 90 + 0.1), false ); this.context.arc( this.centerx, this.centery, opt.length - 20, RGraph.toRadians( ((opt.value / this.range) * 360) - (opt.length < 60 ? 80 : 85) ), RGraph.toRadians( ((opt.value / this.range) * 360) - (opt.length < 60 ? 100 : 95) ), true ); this.context.closePath(); this.context.fill(); //this.context.stroke(); } // // Draw a white circle at the centre // this.context.beginPath(); this.context.fillStyle = 'gray'; this.context.moveTo(this.centerx, this.centery); this.context.arc( this.centerx, this.centery, 2, 0, 6.2795, false ); this.context.closePath(); this.context.fill(); }; // // Draws the labels for the Odo // this.drawLabels = function () { var centerx = this.centerx, centery = this.centery, r = this.radius - (properties.labelsMargin / 2) - 5, start = this.min, end = this.max, decimals = properties.scaleDecimals, point = properties.scalePoint, thousand = properties.scaleThousand, labels = properties.labels, units_pre = properties.scaleUnitsPre, units_post = properties.scaleUnitsPost; this.context.beginPath(); this.context.fillStyle = properties.textColor; var textConf = RGraph.getTextConf({ object: this, prefix: 'labels' }); // // If labels are specified, use those // if (labels) { for (var i=0; i= this.centerx && mouseXY[1] <= this.centery) { angle -= RGraph.TWOPI; } var value = ((angle / RGraph.TWOPI) * (this.max - this.min)) + this.min; return value; }; // // The getObjectByXY() worker method. Don't call this call: // // RGraph.ObjectRegistry.getObjectByXY(e) // // @param object e The event object // this.getObjectByXY = function (e) { var mouseXY = RGraph.getMouseXY(e); var radius = RGraph.getHypLength(this.centerx, this.centery, mouseXY[0], mouseXY[1]); if ( mouseXY[0] > (this.centerx - this.radius) && mouseXY[0] < (this.centerx + this.radius) && mouseXY[1] > (this.centery - this.radius) && mouseXY[1] < (this.centery + this.radius) && radius <= this.radius ) { return this; } }; // // This method handles the adjusting calculation for when the mouse is moved // // @param object e The event object // this.adjusting_mousemove = function (e) { // // Handle adjusting for the Bar // if (properties.adjustable && RGraph.Registry.get('adjusting') && RGraph.Registry.get('adjusting').uid == this.uid) { this.value = this.getValue(e); RGraph.clear(this.canvas); RGraph.redrawCanvas(this.canvas); RGraph.fireCustomEvent(this, 'onadjust'); } }; // // This method returns the appropriate angle for a value // // @param number value The value // this.getAngle = function (value) { // Higher than max or lower than min if (value > this.max || value < this.min) { return null; } var angle = (((value - this.min) / (this.max - this.min)) * RGraph.TWOPI); angle -= RGraph.HALFPI; return angle; }; // // This allows for easy specification of gradients // this.parseColors = function () { // Save the original colors so that they can be restored when the canvas is reset if (this.original_colors.length === 0) { this.original_colors.colorsGreenColor = RGraph.arrayClone(properties.colorsGreenColor); this.original_colors.colorsYellowColor = RGraph.arrayClone(properties.colorsYellowColor); this.original_colors.colorsRedColor = RGraph.arrayClone(properties.colorsRedColor); } // Parse the basic colors properties.colorsGreenColor = this.parseSingleColorForGradient(properties.colorsGreenColor); properties.colorsYellowColor = this.parseSingleColorForGradient(properties.colorsYellowColor); properties.colorsRedColor = this.parseSingleColorForGradient(properties.colorsRedColor); }; // // Use this function to reset the object to the post-constructor state. Eg reset colors if // need be etc // this.reset = function () { }; // // This parses a single color value // this.parseSingleColorForGradient = function (color) { if (!color || typeof color != 'string') { return color; } if (color.match(/^gradient\((.*)\)$/i)) { // Allow for JSON gradients if (color.match(/^gradient\(({.*})\)$/i)) { return RGraph.parseJSONGradient({object: this, def: RegExp.$1}); } var parts = RegExp.$1.split(':'); // Create the gradient var grad = this.context.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius); var diff = 1 / (parts.length - 1); grad.addColorStop(0, RGraph.trim(parts[0])); for (var j=1; j