// version: 2017-11-25 /** * o--------------------------------------------------------------------------------o * | This file is part of the RGraph package - you can learn more at: | * | | * | http://www.rgraph.net | * | | * | RGraph is licensed under the Open Source MIT license. That means that it's | * | totally free to use and there are no restrictions on what you can do with it! | * o--------------------------------------------------------------------------------o */ RGraph = window.RGraph || {isRGraph: 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. * * @param string id The ID of the canvas tag * @param int start The start value of the Odo * @param int end The end value of the odo * @param int value The indicated value (what the needle points to) */ RGraph.Odometer = function (conf) { /** * Allow for object config style */ if ( typeof conf === 'object' && typeof conf.value !== 'undefined' && typeof conf.id === 'string') { var id = conf.id var canvas = document.getElementById(id); var min = conf.min; var max = conf.max; var value = conf.value; 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 min = arguments[1]; var max = arguments[2]; var value = arguments[3]; } 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.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 /** * Compatibility with older browsers */ //RGraph.OldBrowserCompat(this.context); this.properties = { 'chart.background.border': 'black', 'chart.background.color': '#eee', 'chart.background.lines.color': '#ddd', 'chart.centerx': null, 'chart.centery': null, 'chart.radius': null, 'chart.value.text': false, 'chart.value.text.decimals': 0, 'chart.needle.color': 'black', 'chart.needle.width': 2, 'chart.needle.head': true, 'chart.needle.tail': true, 'chart.needle.type': 'pointer', 'chart.needle.extra': [], 'chart.needle.triangle.border': '#aaa', 'chart.text.size': 12, 'chart.text.color': 'black', 'chart.text.font': 'Segoe UI, Arial, Verdana, sans-serif', 'chart.text.accessible': false, 'chart.text.accessible.overflow': 'visible', 'chart.text.accessible.pointerevents': true, 'chart.green.max': max * 0.75, 'chart.red.min': max * 0.9, 'chart.green.color': 'Gradient(white:#0c0)', 'chart.yellow.color': 'Gradient(white:#ff0)', 'chart.red.color': 'Gradient(white:#f00)', 'chart.label.area': 35, 'chart.gutter.left': 25, 'chart.gutter.right': 25, 'chart.gutter.top': 25, 'chart.gutter.bottom': 25, 'chart.title': '', 'chart.title.background': null, 'chart.title.hpos': null, 'chart.title.vpos': null, 'chart.title.font': null, 'chart.title.bold': true, 'chart.title.x': null, 'chart.title.y': null, 'chart.title.halign': null, 'chart.title.valign': null, 'chart.contextmenu': null, 'chart.linewidth': 1, 'chart.shadow.inner': false, 'chart.shadow.inner.color': 'black', 'chart.shadow.inner.offsetx': 3, 'chart.shadow.inner.offsety': 3, 'chart.shadow.inner.blur': 6, 'chart.shadow.outer': false, 'chart.shadow.outer.color': 'black', 'chart.shadow.outer.offsetx': 3, 'chart.shadow.outer.offsety': 3, 'chart.shadow.outer.blur': 6, 'chart.annotatable': false, 'chart.annotate.color': 'black', 'chart.scale.decimals': 0, 'chart.scale.point': '.', 'chart.scale.thousand': ',', '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.resizable': false, 'chart.resize.handle.adjust': [0,0], 'chart.resize.handle.background': null, 'chart.units.pre': '', 'chart.units.post': '', 'chart.border': false, 'chart.border.color1': '#BEBCB0', 'chart.border.color2': '#F0EFEA', 'chart.border.color3': '#BEBCB0', 'chart.tickmarks': true, 'chart.tickmarks.highlighted': false, 'chart.tickmarks.big.color': '#999', 'chart.zerostart': false, 'chart.labels': null, 'chart.units.pre': '', 'chart.units.post': '', 'chart.value.units.pre': '', 'chart.value.units.post': '', 'chart.key': null, 'chart.key.background': 'white', 'chart.key.position': 'graph', '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.halign': 'right', 'chart.key.color.shape': 'square', 'chart.key.rounded': true, 'chart.key.text.size': 10, 'chart.key.colors': null, 'chart.key.text.color': 'black', 'chart.adjustable': false, 'chart.clearto': 'rgba(0,0,0,0)' } /* * 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, ca = this.canvas, co = ca.getContext('2d'), prop = this.properties, pa2 = RG.path2, win = window, doc = document, 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); } /** * A peudo setter * * @param name string The name of the property to set * @param value mixed The value of the property */ this.set = this.Set = function (name, value) { var value = arguments[1] || null; /** * 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; } /** * This should be done first - prepend the property name with "chart." if necessary */ if (name.substr(0,6) != 'chart.') { name = 'chart.' + name; } // Convert uppercase letters to dot+lower case letter while(name.match(/([A-Z])/)) { name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase()); } if (name == 'chart.needle.style') { alert('[RGRAPH] The RGraph property chart.needle.style has changed to chart.needle.color'); } if (name == 'chart.needle.thickness') { name = 'chart.needle.width'; } if (name == 'chart.value') { this.value = value; return; } prop[name] = value; return this; }; /** * A getter * * @param name string The name of the property to get */ 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; } // Convert uppercase letters to dot+lower case letter while(name.match(/([A-Z])/)) { name = name.replace(/([A-Z])/, '.' + RegExp.$1.toLowerCase()); } if (name == 'chart.value') { return this.value; } return prop[name.toLowerCase()]; }; /** * Draws the odometer */ this.draw = this.Draw = function () { /** * Fire the onbeforedraw event */ RG.FireCustomEvent(this, 'onbeforedraw'); /** * 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; } /** * 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']; // Work out a few things this.radius = Math.min( (ca.width - this.gutterLeft - this.gutterRight) / 2, (ca.height - this.gutterTop - this.gutterBottom) / 2 ) - (prop['chart.border'] ? 25 : 0); this.diameter = 2 * this.radius; this.centerx = ((ca.width - this.gutterLeft- this.gutterRight) / 2) + this.gutterLeft; this.centery = ((ca.height - this.gutterTop - this.gutterBottom) / 2) + this.gutterTop; this.range = this.max - this.min; this.coordsText = []; /** * Move the centerx if the key is defined */ if (prop['chart.key'] && prop['chart.key'].length > 0 && ca.width > ca.height) this.centerx = 5 + this.radius; if (typeof(prop['chart.centerx']) == 'number') this.centerx = prop['chart.centerx']; if (typeof(prop['chart.centery']) == 'number') this.centery = prop['chart.centery']; /** * Allow custom setting of the radius */ if (typeof(prop['chart.radius']) == 'number') { this.radius = prop['chart.radius']; if (prop['chart.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; } co.lineWidth = prop['chart.linewidth']; // Draw the background this.DrawBackground(); // And lastly, draw the labels this.DrawLabels(); // Draw the needle this.DrawNeedle(this.value, prop['chart.needle.color']); /** * Draw any extra needles */ if (prop['chart.needle.extra'].length > 0) { for (var i=0; i 0) { // Build a colors array out of the needle colors var colors = [prop['chart.needle.color']]; if (prop['chart.needle.extra'].length > 0) { for (var i=0; i= this.centerx && mouseXY[1] <= this.centery) { angle -= RG.TWOPI; } var value = ((angle / RG.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 = RG.getMouseXY(e); var radius = RG.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 = this.Adjusting_mousemove = function (e) { /** * Handle adjusting for the Bar */ if (prop['chart.adjustable'] && RG.Registry.Get('chart.adjusting') && RG.Registry.Get('chart.adjusting').uid == this.uid) { this.value = this.getValue(e); RG.clear(ca); RG.redrawCanvas(ca); RG.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)) * RG.TWOPI); angle -= RG.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['chart.green.color'] = RG.array_clone(prop['chart.green.color']); this.original_colors['chart.yellow.color'] = RG.array_clone(prop['chart.yellow.color']); this.original_colors['chart.red.color'] = RG.array_clone(prop['chart.red.color']); } // Parse the basic colors prop['chart.green.color'] = this.parseSingleColorForGradient(prop['chart.green.color']); prop['chart.yellow.color'] = this.parseSingleColorForGradient(prop['chart.yellow.color']); prop['chart.red.color'] = this.parseSingleColorForGradient(prop['chart.red.color']); }; /** * 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)) { var parts = RegExp.$1.split(':'); // Create the gradient var grad = co.createRadialGradient(this.centerx, this.centery, 0, this.centerx, this.centery, this.radius); var diff = 1 / (parts.length - 1); grad.addColorStop(0, RG.trim(parts[0])); for (var j=1; j