// 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}; RGraph.SVG = RGraph.SVG || {}; // Module pattern (function (win, doc, undefined) { RGraph.SVG.SemiCircularProgress = function (conf) { // // A setter that the constructor uses (at the end) // to set all of the properties // // @param string name The name of the property to set // @param string value The value to set the property to // this.set = function (name, value) { if (arguments.length === 1 && typeof name === 'object') { for (i in arguments[0]) { if (typeof i === 'string') { if (i === 'backgroundColor') {i = 'backgroundFill';} if (i === 'backgroundColorOpacity') {i = 'backgroundFillOpacity';} this.set(i, arguments[0][i]); } } } else { // Go through all of the properties and make sure // that they're using the correct capitalisation name = this.properties_lowercase_map[name.toLowerCase()] || name; if (name === 'backgroundColor') {name = 'backgroundFill';} if (name === 'backgroundColorOpacity') {name = 'backgroundFillOpacity';} var ret = RGraph.SVG.commonSetter({ object: this, name: name, value: value }); name = ret.name; value = ret.value; this.properties[name] = value; // If setting the colors, update the originalColors // property too if (name === 'colors') { this.originalColors = RGraph.SVG.arrayClone(value, true); this.colorsParsed = false; } } 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 this.properties[name]; }; this.type = 'semicircularprogress'; this.min = RGraph.SVG.stringsToNumbers(conf.min); this.max = RGraph.SVG.stringsToNumbers(conf.max); this.value = RGraph.SVG.stringsToNumbers(conf.value); this.id = conf.id; this.uid = RGraph.SVG.createUID(); this.container = document.getElementById(this.id); this.layers = {}; // MUST be before the SVG tag is created! this.svg = RGraph.SVG.createSVG({object: this,container: this.container}); this.svgAllGroup = RGraph.SVG.createAllGroup(this); this.clipid = null; // Used to clip the canvas this.isRGraph = true; this.isrgraph = true; this.rgraph = true; this.width = Number(this.svg.getAttribute('width')); this.height = Number(this.svg.getAttribute('height')); this.data = conf.data; // Is this used? Don't think so. this.colorsParsed = false; this.originalColors = {}; this.gradientCounter = 1; this.nodes = {}; this.coords = []; this.shadowNodes = []; this.currentValue = null; this.firstDraw = true; // After the first draw this will be false // Used for adjusting this.adjusting_mousedown = false; // Bounds checking if (this.value > this.max) this.value = this.max; if (this.value < this.min) this.value = this.min; // Add this object to the ObjectRegistry RGraph.SVG.OR.add(this); // Set the DIV container to be inline-block this.container.style.display = 'inline-block'; this.properties = { centerx: null, centery: null, radius: null, width: 60, marginLeft: 35, marginRight: 35, marginTop: 35, marginBottom: 35, backgroundStrokeLinewidth: 0.25, backgroundStroke: 'gray', backgroundFill: null, backgroundFillOpacity: 0.15, backgroundGrid: false, backgroundGridMargin: 20, backgroundGridColor: '#ddd', backgroundGridLinewidth: 1, backgroundGridCircles: true, backgroundGridRadials: true, backgroundGridRadialsCount: 10, colors: ['#6d6','#FFA5A5','#A0A2F8','yellow','gray','pink','orange','cyan','green'], colorsStroke: 'transparent', textColor: 'black', textFont: 'Arial, Verdana, sans-serif', textSize: 12, textBold: false, textItalic: false, text: null, scale: false, scaleMin: null, // Defaults to the charts min value scaleMax: null, // Defaults to the charts max value scaleDecimals: 0, scalePoint: '.', scaleThousand: ',', scaleFormatter: null, scaleUnitsPre: '', scaleUnitsPost: '', scaleLabelsCount: 10, scaleLabelsFont: null, scaleLabelsSize: null, scaleLabelsColor: null, scaleLabelsBold: null, scaleLabelsItalic: null, scaleLabelsOffsetr: 0, scaleLabelsOffsetx: 0, scaleLabelsOffsety: 0, labelsMin: true, labelsMinSpecific: null, labelsMinPoint: null, labelsMinThousand: null, labelsMinDecimals: null, labelsMinFormatter: null, labelsMinFont: null, labelsMinSize: null, labelsMinBold: null, labelsMinItalic: null, labelsMinColor: null, labelsMinUnitsPre: null, labelsMinUnitsPost: null, labelsMax: true, labelsMaxSpecific: null, labelsMaxPoint: null, labelsMaxThousand: null, labelsMaxFormatter: null, labelsMaxFont: null, labelsMaxSize: null, labelsMaxBold: null, labelsMaxItalic: null, labelsMaxColor: null, labelsMaxDecimals: null, labelsMaxUnitsPre: null, labelsMaxUnitsPost: null, labelsCenter: true, labelsCenterIndex: 0, labelsCenterSpecific: null, labelsCenterPoint: null, labelsCenterThousand: null, labelsCenterFormatter: null, labelsCenterFont: null, labelsCenterSize: 40, labelsCenterBold: true, labelsCenterItalic: null, labelsCenterColor: null, labelsCenterDecimals: null, labelsCenterUnitsPre: null, labelsCenterUnitsPost: null, linewidth: 1, tooltips: null, tooltipsOverride: null, tooltipsEffect: 'fade', tooltipsCssClass: 'RGraph_tooltip', tooltipsCss: null, tooltipsEvent: 'click', tooltipsPersistent: false, tooltipsFormattedThousand: ',', tooltipsFormattedPoint: '.', tooltipsFormattedDecimals: 0, tooltipsFormattedUnitsPre: '', tooltipsFormattedUnitsPost: '', tooltipsFormattedKeyColors: null, tooltipsFormattedKeyColorsShape: 'square', tooltipsFormattedKeyLabels: [], tooltipsFormattedTableHeaders: null, tooltipsFormattedTableData: null, tooltipsPointer: true, tooltipsPointerOffsetx: 0, tooltipsPointerOffsety: 0, tooltipsPositionStatic: true, adjustable: false, highlightStroke: 'rgba(0,0,0,0)', highlightFill: 'rgba(255,255,255,0.7)', highlightLinewidth: 1, title: '', titleX: null, titleY: null, titleHalign: 'center', titleValign: null, titleFont: null, titleSize: null, titleColor: null, titleBold: null, titleItalic: null, titleSubtitle: null, titleSubtitleSize: null, titleSubtitleColor: '#aaa', titleSubtitleFont: null, titleSubtitleBold: null, titleSubtitleItalic: null, clip: null, zoom: false }; // // 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; } } // // Copy the global object properties to this instance // RGraph.SVG.getGlobals(this); // // "Decorate" the object with the generic effects if the effects library has been included // if (RGraph.SVG.FX && typeof RGraph.SVG.FX.decorate === 'function') { RGraph.SVG.FX.decorate(this); } // Add the responsive function to the object this.responsive = RGraph.SVG.responsive; var properties = this.properties; // // The draw method draws the Bar chart // this.draw = function () { // Fire the beforedraw event RGraph.SVG.fireCustomEvent(this, 'onbeforedraw'); // Reset this to prevent it from growing this.nodes = {}; // Should be the first(ish) thing that's done in the // .draw() function except for the onbeforedraw event // and the installation of clipping. this.width = Number(this.svg.getAttribute('width')); this.height = Number(this.svg.getAttribute('height')); // Create the defs tag if necessary RGraph.SVG.createDefs(this); // Add these this.graphWidth = this.width - properties.marginLeft - properties.marginRight; this.graphHeight = this.height - properties.marginTop - properties.marginBottom; // Work out the center point this.centerx = (this.graphWidth / 2) + properties.marginLeft; this.centery = this.height - properties.marginBottom; this.radius = Math.min(this.graphWidth / 2, this.graphHeight); // Allow the user to override the calculated centerx/y/radius this.radius = typeof properties.radius === 'number' ? properties.radius : this.radius; this.centerx = typeof properties.centerx === 'number' ? properties.centerx : this.centerx; this.centery = typeof properties.centery === 'number' ? properties.centery : this.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); // Set the width of the meter this.progressWidth = properties.width || (this.radius / 3); // Parse the colors for gradients RGraph.SVG.resetColorsToOriginalValues({object:this}); this.parseColors(); // // Set the current value // this.currentValue = this.value; // Install clipping if requested if (this.properties.clip) { this.clipid = RGraph.SVG.installClipping(this); // Add the clip ID to the all group this.svgAllGroup.setAttribute( 'clip-path', 'url(#{1})'.format(this.clipid) ); } else { // No clipping - so ensure that there's no clip-path // attribute this.clipid = null; this.svgAllGroup.removeAttribute('clip-path'); } this.drawBackground(); // Draw the background "grid" this.drawMeter(); // Draw the segments RGraph.SVG.drawTitle(this); // Draw the title and subtitle this.drawLabels(); // Draw the labels this.drawScale(); // Draw the scale var obj = this; // Add the tooltip event listener if (!RGraph.SVG.isNullish(properties.tooltips) && (properties.tooltips.length || RGraph.SVG.isString(properties.tooltips)) ) { for (var i=0; i<this.coords.length; ++i) { (function (index) { if (RGraph.SVG.isString(properties.tooltips) || (RGraph.SVG.isArray(properties.tooltips) && properties.tooltips[index])) { obj.coords[index].element.addEventListener(properties.tooltipsEvent.replace(/^on/, ''), function (e) { obj.removeHighlight(); // Show the tooltip RGraph.SVG.tooltip({ object: obj, index: index, group: null, sequentialIndex: index, text: RGraph.SVG.isString(properties.tooltips) ? properties.tooltips : properties.tooltips[index], event: e }); // Highlight the rect that has been clicked on obj.highlight(e.target); }, false); // Add the mousemove listener that changes the // mouse pointer obj.coords[index].element.addEventListener('mousemove', function (e) { e.target.style.cursor = 'pointer'; }, false); } })(i); } } // Add the event listener that clears the highlight if // there is any. Must be MOUSEDOWN (ie before the click // event) if (!this.added_remove_highlight_listener) { document.body.addEventListener('mousedown', function (e) { obj.removeHighlight(); }, false); this.added_remove_highlight_listener = true; } // // Allow the addition of custom text via the // text: property. // RGraph.SVG.addCustomText(this); // Lastly - install the zoom event listeners if // requested if (this.properties.zoom) { RGraph.SVG.addZoom(this); } // // Ajusting // if (properties.adjustable) { var obj = this; var func = function (e) { var div = e.currentTarget, mouseX = e.offsetX, mouseY = e.offsetY; if (RGraph.SVG.ISFF) { mouseX = e.pageX - e.currentTarget.offsetLeft; mouseY = e.pageY - e.currentTarget.offsetTop; } //var radius = RGraph.SVG.TRIG.getHypLength({ // x1: mouseX, // y1: mouseY, // x2: obj.centerx, // y2: obj.centery, // object: obj //}); //if (radius > obj.radius) { // return; //} var value = obj.getValue(e); obj.value = value; RGraph.SVG.redraw(obj.svg); }; // // Only add the adjusting event listeners once // var obj = this; RGraph.SVG.runOnce('rgraph-svg-scp-adjusting-event-listeners-' + obj.uid, function () { // // Create a reference so that code thats inside // the event listeners can easily access the // object obj.svg.addEventListener('mousedown', function (e) { var mouseX = e.offsetX, mouseY = e.offsetY; if (RGraph.SVG.ISFF) { mouseX = e.pageX - e.currentTarget.offsetLeft; mouseY = e.pageY - e.currentTarget.offsetTop; } var radius = RGraph.SVG.TRIG.getHypLength({ x1: mouseX, y1: mouseY, x2: obj.centerx, y2: obj.centery, object: obj }); // The first click that starts adjusting // must be within the progress meter area // // Allow the pointer to be inside the meter // /* || radius < (obj.radius - obj.progressWidth) */ if (radius > obj.radius) { return; } obj.adjusting_mousedown = true; func(e); // Fire the beforedraw event RGraph.SVG.fireCustomEvent(obj, 'onadjustbegin'); }, false); obj.container.addEventListener('mousemove', function (e) { if (obj.adjusting_mousedown) { func(e); // Fire the beforedraw event RGraph.SVG.fireCustomEvent(obj, 'onadjust'); } }, false); window.addEventListener('mouseup', function (e) { obj.adjusting_mousedown = false; // Fire the beforedraw event RGraph.SVG.fireCustomEvent(obj, 'onadjustend'); }, false); }); } // // Fire the onfirstdraw event // if (this.firstDraw) { this.firstDraw = false; RGraph.SVG.fireCustomEvent(this, 'onfirstdraw'); } // Fire the draw event RGraph.SVG.fireCustomEvent(this, 'ondraw'); // // Install any inline responsive configuration. This // should be last in the draw function - even after // the draw events. // RGraph.SVG.installInlineResponsive(this); return this; }; // // New create() shortcut function // For example: // this.create('rect,x:0,y:0,width:100,height:100'[,parent]); // // @param str string The tag definition to parse and create // @param object The (optional) parent element // @return object The new tag // this.create = function (str) { var def = RGraph.SVG.create.parseStr(this, str); def.svg = this.svg; // By default the parent is the SVG tag - but if // requested then change it to the tag that has // been given if (arguments[1]) { def.parent = arguments[1]; } return RGraph.SVG.create(def); }; // // This function returns the value that the mouse is positioned at, regardless of // the actual indicated value. // // @param object e The event object // this.getValue = function (e) { var mouseX = e.offsetX, mouseY = e.offsetY; if (RGraph.SVG.ISFF) { mouseX = e.pageX - e.currentTarget.offsetLeft; mouseY = e.pageY - e.currentTarget.offsetTop; } var angle = RGraph.SVG.TRIG.getAngleByXY({ cx: this.centerx, cy: this.centery, x: mouseX, y: mouseY }); var value = ((angle - this.angleStart) / (this.angleEnd - this.angleStart)); // Adjust the angle aso that it goes from 0 - 3.14 if (mouseX < this.centerx) { angle = angle - RGraph.SVG.TRIG.PI - RGraph.SVG.TRIG.HALFPI; } else if (mouseX > this.centerx) { angle += RGraph.SVG.TRIG.HALFPI; } else if (mouseX === this.centerx) { angle = RGraph.SVG.TRIG.HALFPI; } value = (this.max - this.min) * (angle / RGraph.SVG.TRIG.PI); if (value < this.min) value = this.min; if (value > this.max) value = this.max; return value; }; // // This function returns the relevant angle for the given // value. // // @param number value The value that you want to get an // angle for // this.getAngle = function (value, outofbounds = false) { if (!outofbounds && (value > this.max || value < this.min) ) { return null; } return (((value - this.min) / (this.max - this.min)) * RGraph.SVG.TRIG.PI) - RGraph.SVG.TRIG.HALFPI; }; // // Draw the background "grid" // this.drawBackground = function () { if (properties.backgroundGrid) { var margin = properties.backgroundGridMargin; var outerRadius = this.radius + margin; var innerRadius = this.radius - properties.width - margin; // Draw the background grid "circles" if (properties.backgroundGridCircles) { // Create the path for the outer line of the grid var arcPath1 = RGraph.SVG.TRIG.getArcPath3({ cx: this.centerx, cy: this.centery, r: outerRadius, start: -RGraph.SVG.TRIG.HALFPI, end: RGraph.SVG.TRIG.HALFPI, anticlockwise: false, lineto: false, moveto: true }); // Create the path for the inner line of the grid var arcPath2 = RGraph.SVG.TRIG.getArcPath3({ cx: this.centerx, cy: this.centery, r: innerRadius, start: RGraph.SVG.TRIG.HALFPI, end: -RGraph.SVG.TRIG.HALFPI, anticlockwise: true, lineto: true }); RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.svgAllGroup, attr: { d: arcPath1 + ' ' + arcPath2 + ' z', stroke: properties.backgroundGridColor, fill: 'transparent' } }); } // // Draw the background grid radials // if (properties.backgroundGridRadials) { // Calculate the radius increment var increment = (RGraph.SVG.TRIG.HALFPI - (0 - RGraph.SVG.TRIG.HALFPI) ) / properties.backgroundGridRadialsCount; var angle = -RGraph.SVG.TRIG.HALFPI; for (var i=0,path=''; i<properties.backgroundGridRadialsCount; ++i) { path += RGraph.SVG.TRIG.getArcPath3({ cx: this.centerx, cy: this.centery, r: outerRadius, start: -RGraph.SVG.TRIG.HALFPI + (i * increment), end: -RGraph.SVG.TRIG.HALFPI + (i * increment), anticlockwise: true, lineto: false, moveto: true }); path += RGraph.SVG.TRIG.getArcPath3({ cx: this.centerx, cy: this.centery, r: innerRadius, start: -RGraph.SVG.TRIG.HALFPI + (i * increment), end: -RGraph.SVG.TRIG.HALFPI + (i * increment), anticlockwise: true, lineto: true }); path += ' '; angle += increment; } RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.svgAllGroup, attr: { d: path + ' z', stroke: properties.backgroundGridColor, fill: 'transparent', 'stroke-width': properties.backgroundGridLinewidth } }); } } }; // // Draws the meter // this.drawMeter = function () { // Reset this this.coords = []; var path = RGraph.SVG.TRIG.getArcPath({ cx: this.centerx, cy: this.centery, r: this.radius, start: RGraph.SVG.TRIG.PI + RGraph.SVG.TRIG.HALFPI, end: RGraph.SVG.TRIG.HALFPI, anticlockwise: false }); var path2 = RGraph.SVG.TRIG.getArcPath({ cx: this.centerx, cy: this.centery, r: this.radius - this.progressWidth, end: RGraph.SVG.TRIG.PI + RGraph.SVG.TRIG.HALFPI, start: RGraph.SVG.TRIG.HALFPI, anticlockwise: true, moveto: false }); // This element is repeated AFTER the green bar that indicates // the value so that the stroke appears AFTER the indicator bar var background = RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.svgAllGroup, attr: { d: path + " L " + (this.centerx + this.radius - this.progressWidth) + " " + this.centery + path2 + " L " + (this.centerx - this.radius) + " " + this.centery, fill: properties.backgroundFill || properties.colors[0], 'stroke-width': 0, 'fill-opacity': properties.backgroundFillOpacity } }); // Store a reference to the background this.nodes.background = background; // // This draws the bar that indicates the value // // A single number if (typeof this.value === 'number') { var angle = ((this.value - this.min) / (this.max - this.min)) * RGraph.SVG.TRIG.PI; // Because the Meter is always a semi-circle // Take off half a pi because our origin is the north axis angle -= RGraph.SVG.TRIG.HALFPI; // Store the angle for later use this.angle = angle; // Now get the path of the inner indicator bar var path = RGraph.SVG.TRIG.getArcPath({ cx: this.centerx, cy: this.centery, r: this.radius, start: RGraph.SVG.TRIG.PI + RGraph.SVG.TRIG.HALFPI, end: angle, anticlockwise: false }); var path2 = RGraph.SVG.TRIG.getArcPath({ cx: this.centerx, cy: this.centery, r: this.radius - this.progressWidth, start: angle, end: angle, anticlockwise: false, array: true }); var path3 = RGraph.SVG.TRIG.getArcPath({ cx: this.centerx, cy: this.centery, r: this.radius - this.progressWidth, start: angle, end: RGraph.SVG.TRIG.PI + RGraph.SVG.TRIG.HALFPI, anticlockwise: true, moveto: false }); // Create a group for the indicator bar. At a later point any // highlight can be also appended to this group var group = RGraph.SVG.create({ svg: this.svg, type: 'g', parent: this.svgAllGroup, attr: { id: 'indicator-bar-group' } }); // Now draw the path var path = RGraph.SVG.create({ svg: this.svg, type: 'path', parent: group, attr: { d: path + " L{1} {2} ".format( path2[1], path2[2] ) + path3 + ' z', fill: properties.colors[0], stroke: properties.colorsStroke, 'stroke-width': properties.linewidth } }); // Store a reference to the bar in the nodes array and the // group as well. If necessary any highlight thats later // added can be appended to this group this.nodes.barGroup = group; this.nodes.bar = path; this.coords.push({ cx: this.centerx, cy: this.centery, radiusOuter: this.radius, radiusInner: this.radius - this.progressWidth, start: -RGraph.SVG.TRIG.HALFPI, end: angle, element: path }); // // Multiple values // } else if (RGraph.SVG.isArray(this.value)) { // Create a group for the indicator bars. At a later point any // highlight can be also appended to this group var group = RGraph.SVG.create({ svg: this.svg, type:'g', parent: this.svgAllGroup, attr: { id: 'indicator-bar-group' } }); for (var i=0,start=-RGraph.SVG.TRIG.HALFPI; i<this.value.length; ++i) { var angle = ((this.value[i] - this.min) / (this.max - this.min)) * RGraph.SVG.TRIG.PI; // Because the Meter is always a semi-circle var path1 = RGraph.SVG.TRIG.getArcPath({ cx: this.centerx, cy: this.centery, r: this.radius, // - this.progressWidth, start: start, end: start + angle, anticlockwise: false, lineto: false, moveto: true }); var path2 = RGraph.SVG.TRIG.getArcPath3({ cx: this.centerx, cy: this.centery, r: this.radius - this.progressWidth, start: start + angle, end: start, anticlockwise: true }); var el = RGraph.SVG.create({ svg: this.svg, type: 'path', parent: group, attr: { d: '{1} {2} z'.format(path1, path2), stroke: properties.colorsStroke, 'stroke-width': properties.linewidth, fill: properties.colors[i] } }); // Store the angle for later use this.coords.push({ cx: this.centerx, cy: this.centery, radiusOuter: this.radius, radiusInner: this.radius - this.progressWidth, start: start, end: start + angle, element: el }); // Increment the start angle // // DO THIS LAST start += angle; } } }; // // Draw the labels // this.drawLabels = function () { // Draw the min label if (properties.labelsMin) { var min = RGraph.SVG.numberFormat({ object: this, num: this.min.toFixed(properties.labelsMinDecimals), prepend: properties.labelsMinUnitsPre, append: properties.labelsMinUnitsPost, point: properties.labelsMinPoint, thousand: properties.labelsMinThousand, formatter: properties.labelsMinFormatter }); // Get the text configuration var textConf = RGraph.SVG.getTextConf({ object: this, prefix: 'labelsMin' }); var text = RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.min', text: typeof properties.labelsMinSpecific === 'string' ? properties.labelsMinSpecific : min, x: this.centerx - this.radius + (this.progressWidth / 2), y: this.centery + 5 + properties.backgroundStrokeLinewidth, valign: 'top', halign: 'center', font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color }); this.nodes.labelsMin = text; } // Draw the max label if (properties.labelsMax) { var max = RGraph.SVG.numberFormat({ object: this, num: this.max.toFixed(properties.labelsMaxDecimals), prepend: properties.labelsMaxUnitsPre, append: properties.labelsMaxUnitsPost, point: properties.labelsMaxPoint, thousand: properties.labelsMaxThousand, formatter: properties.labelsMaxFormatter }); // Get the text configuration var textConf = RGraph.SVG.getTextConf({ object: this, prefix: 'labelsMax' }); var text = RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.max', text: typeof properties.labelsMaxSpecific === 'string' ? properties.labelsMaxSpecific : max, x: this.centerx + this.radius - (this.progressWidth / 2), y: this.centery + 5 + properties.backgroundStrokeLinewidth, valign: 'top', halign: 'center', font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color }); // Store a reference to the text node this.nodes.labelsMax = text; } // Draw the center label if (properties.labelsCenter) { var center = RGraph.SVG.numberFormat({ object: this, num: (typeof this.value === 'number' ? this.value : this.value[properties.labelsCenterIndex]).toFixed(properties.labelsCenterDecimals), prepend: properties.labelsCenterUnitsPre, append: properties.labelsCenterUnitsPost, point: properties.labelsCenterPoint, thousand: properties.labelsCenterThousand, formatter: properties.labelsCenterFormatter }); // Get the text configuration var textConf = RGraph.SVG.getTextConf({ object: this, prefix: 'labelsCenter' }); var text = RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.center', text: typeof properties.labelsCenterSpecific === 'string' ? properties.labelsCenterSpecific : center, x: this.centerx, y: this.centery, valign: 'bottom', halign: 'center', font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color }); // Store a reference to the center label this.nodes.labelsCenter = text; } }; // // Draw the scale if necessary // this.drawScale = function () { if (properties.scale) { // // Generate an appropiate scale // this.scale = RGraph.SVG.getScale({ object: this, numlabels: properties.scaleLabelsCount, unitsPre: properties.scaleUnitsPre, unitsPost: properties.scaleUnitsPost, max: typeof properties.scaleMax === 'number' ? properties.scaleMax : this.max, min: typeof properties.scaleMin === 'number' ? properties.scaleMin : this.min, point: properties.scalePoint, round: false, thousand: properties.scaleThousand, decimals: properties.scaleDecimals, strict: true, formatter: properties.scaleFormatter }); // // Loop thru the number of labels // var textConf = RGraph.SVG.getTextConf({ object: this, prefix: 'scaleLabels' }); for (var i=0; i<this.scale.labels.length; ++i) { var xy = RGraph.SVG.TRIG.getRadiusEndPoint({ angle: (-RGraph.SVG.TRIG.PI) + (((i+1) / this.scale.labels.length) * (RGraph.SVG.TRIG.HALFPI - (-RGraph.SVG.TRIG.HALFPI)) ), r: this.radius + (properties.backgroundGrid ? properties.backgroundGridMargin : 0) + textConf.size + properties.scaleLabelsOffsetr + 5 }); // Draw the label RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'scale', font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic, x: xy[0] + this.centerx + (properties.scaleLabelsOffsetx || 0), y: xy[1] + this.centery + (properties.scaleLabelsOffsety || 0), valign: 'center', halign: ((i+1) / this.scale.labels.length === 0.5) ? 'center' : (((i+1) > (this.scale.labels.length / 2)) ? 'left' : 'right'), text: this.scale.labels[i] }); } // Draw the zero label // // Draw the zero label // var xy = RGraph.SVG.TRIG.getRadiusEndPoint({ angle: -RGraph.SVG.TRIG.PI, r: this.radius + (properties.backgroundGrid ? properties.backgroundGridMargin : 0) + textConf.size + properties.scaleLabelsOffsetr + 5 }); RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'scale', font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic, x: xy[0] + this.centerx + (properties.scaleLabelsOffsetx || 0), y: xy[1] + this.centery + (properties.scaleLabelsOffsety || 0), valign: 'center', halign: 'right', text: (typeof properties.scaleFormatter === 'function') ? properties.scaleFormatter(this, this.min) : properties.scaleUnitsPre + (typeof properties.scaleMin === 'number' ? properties.scaleMin : this.min).toFixed(properties.scaleDecimals).replace(/\./, properties.scalePoint) + properties.scaleUnitsPost, }); } }; // // This function can be used to highlight a segment on the chart // // @param object segment The segment to highlight // this.highlight = function (segment) { // Remove any highlight that's already been // installed this.removeHighlight(); var highlight = RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.nodes.barGroup, attr: { d: segment.getAttribute('d'), fill: properties.highlightFill, stroke: properties.highlightStroke, 'stroke-width': properties.highlightLinewidth }, style: { pointerEvents: 'none' } }); // Store the highlight node in the registry RGraph.SVG.REG.set('highlight', highlight); // Add the event listener that clears the highlight path if // there is any. Must be MOUSEDOWN (ie before the click event) var obj = this; document.body.addEventListener('mousedown', function (e) { obj.removeHighlight(); }, false); }; // // This function can be used to remove the highlight that is added // by tooltips // this.removeHighlight = function () { RGraph.SVG.removeHighlight(); }; // // 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 (!Object.keys(this.originalColors).length) { this.originalColors = { colors: RGraph.SVG.arrayClone(properties.colors, true), highlightFill: RGraph.SVG.arrayClone(properties.highlightFill, true), backgroundFill: RGraph.SVG.arrayClone(properties.backgroundFill, true) } } // colors var colors = properties.colors; if (colors) { for (var i=0; i<colors.length; ++i) { colors[i] = RGraph.SVG.parseColorLinear({ object: this, color: colors[i], start: this.centerx - this.radius, end: this.centerx + this.radius, direction: 'horizontal' }); } } // Highlight fill properties.highlightFill = RGraph.SVG.parseColorLinear({ object: this, color: properties.highlightFill, start: properties.marginLeft, end: this.width - properties.marginRight, direction: 'horizontal' }); // Background color // Background color properties.backgroundFill = RGraph.SVG.parseColorLinear({ object: this, color: properties.backgroundFill, start: properties.marginLeft, end: this.width - properties.marginRight, direction: 'horizontal' }); }; // // The SCP chart grow effect // this.grow = function () { var opt = arguments[0] || {}, frames = opt.frames || 30, frame = 0, obj = this; // // Copy the data // var value = RGraph.SVG.arrayClone(this.value, true); if (RGraph.SVG.isNumber(this.value)) { if (RGraph.SVG.isNullish(this.currentValue)) { this.currentValue = this.min; } var initial_value = this.currentValue, diff = this.value - Number(this.currentValue), increment = diff / frames; } else { var initial_value = [], diff = [], increment = []; for (var i=0; i<this.value.length; ++i) { initial_value[i] = RGraph.SVG.isNullish(this.currentValue) ? 0 : this.currentValue[i]; diff[i] = this.value[i] - Number(RGraph.SVG.isNullish(this.currentValue) ? 0 : this.currentValue[i]); increment[i] = diff[i] / frames; } } var iterate = function () { var multiplier = frame / frames; if (typeof obj.value === 'object') { for (var i=0; i<obj.value.length; ++i) { obj.value[i] = initial_value[i] + (increment[i] * frame); } } else { obj.value = initial_value + (increment * frame); } RGraph.SVG.redraw(obj.svg); if (frame++ < frames) { RGraph.SVG.FX.update(iterate); } else if (opt.callback) { obj.value = value; RGraph.SVG.redraw(); (opt.callback)(obj); } }; iterate(); return this; }; // // Using a function to add events makes it easier to facilitate method // chaining // // @param string type The type of even to add // @param function func // this.on = function (type, func) { if (type.substr(0,2) !== 'on') { type = 'on' + type; } RGraph.SVG.addCustomEventListener(this, type, func); 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; }; // // Remove highlight from the chart (tooltips) // this.removeHighlight = function () { RGraph.SVG.removeHighlight(); }; // // A worker function that handles Bar chart specific tooltip substitutions // this.tooltipSubstitutions = function (opt) { var value = RGraph.SVG.isArray(this.value) ? this.value[opt.index] : this.value; var values = RGraph.SVG.isArray(this.value) ? this.value : [this.value]; return { index: opt.index, dataset: 0, sequentialIndex: opt.index, value: value, values: values }; }; // // A worker function that returns the correct color/label/value // // @param object specific The indexes that are applicable // @param number index The appropriate index // this.tooltipsFormattedCustom = function (specific, index, colors) { var color = colors[index]; var label = ( (typeof properties.tooltipsFormattedKeyLabels === 'object' && typeof properties.tooltipsFormattedKeyLabels[0] === 'string') ? properties.tooltipsFormattedKeyLabels[index] : ''); return { label: label, color: color }; }; // // This allows for static tooltip positioning // this.positionTooltipStatic = function (args) { var obj = args.object, e = args.event, tooltip = args.tooltip, index = args.index, svgXY = RGraph.SVG.getSVGXY(obj.svg); // Calculate the coordinates for the tooltip var coords = RGraph.SVG.TRIG.toCartesian({ cx: this.centerx, cy: this.centery, //angle: this.angle - RGraph.SVG.TRIG.HALFPI - ((this.angle + RGraph.SVG.TRIG.HALFPI) / 2), angle: this.coords[index].start - RGraph.SVG.TRIG.HALFPI + ((this.coords[index].end - this.coords[index].start) / 2), r: this.radius - (this.progressWidth / 2) }); // Position the tooltip in the X direction args.tooltip.style.left = ( svgXY[0] // The X coordinate of the canvas + coords.x - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width ) + 'px'; args.tooltip.style.top = ( svgXY[1] // The Y coordinate of the canvas + coords.y - tooltip.offsetHeight // The height of the tooltip - 10 // An arbitrary amount ) + 'px'; }; // // This function handles clipping to scale values. Because // each chart handles scales differently, a worker function // is needed instead of it all being done centrally in the // main function. // // @param object clipPath The parent cipPath element // this.clipToScaleWorker = function (clipPath) { if (RegExp.$1 === 'min') from = this.min; else from = Number(RegExp.$1); if (RegExp.$2 === 'max') to = this.max; else to = Number(RegExp.$2); var a1 = this.getAngle(from), a2 = this.getAngle(to); // Change the radius if the number is "min" if (RegExp.$1 === 'min') { a1 = -1 * RGraph.SVG.TRIG.PI; } // Change the radius if the number is "max" if (RegExp.$2 === 'max') { a2 = RGraph.SVG.TRIG.PI } var path = RGraph.SVG.TRIG.getArcPath3({ cx: this.centerx, cy: this.centery, radius: Math.max(this.width, this.height), start: a1, end: a2, anticlockwise: false }); RGraph.SVG.create({ svg: this.svg, parent: clipPath, type: 'path', attr: { d: 'M {1} {2} {3} z'.format( this.centerx, this.centery, path ) } }); // Now set the clip-path attribute on the // all-elements group this.svgAllGroup.setAttribute( 'clip-path', 'url(#' + clipPath.id + ')' ); }; // // Set the options that the user has provided // for (i in conf.options) { if (typeof i === 'string') { this.set(i, conf.options[i]); } } return this; }; // End module pattern })(window, document);