// 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}; RGraph.SVG = RGraph.SVG || {}; // Module pattern (function (win, doc, undefined) { var RG = RGraph, ua = navigator.userAgent, ma = Math, win = window, doc = document; RG.SVG.Pie = 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') { var ret = RG.SVG.commonSetter({ object: this, name: i, value: arguments[0][i] }); name = ret.name; value = ret.value; this.set(name, value); } } } else { var ret = RG.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 = RG.SVG.arrayClone(value); this.colorsParsed = false; } } return this; }; this.id = conf.id; this.uid = RG.SVG.createUID(); this.container = document.getElementById(this.id); this.layers = {}; // MUST be before the SVG tag is created! this.svg = RG.SVG.createSVG({object: this,container: this.container}); this.isRGraph = true; this.width = Number(this.svg.getAttribute('width')); this.height = Number(this.svg.getAttribute('height')); this.data = conf.data; this.type = 'pie'; this.angles = []; this.colorsParsed = false; this.originalColors = {}; this.gradientCounter = 1; this.nodes = []; this.shadowNodes = []; // Add this object to the ObjectRegistry RG.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, gutterLeft: 35, gutterRight: 35, gutterTop: 35, gutterBottom: 35, colors: [ '#f66', '#6f6', '#66f', '#ff6', '#6ff', '#ccc', 'pink', 'orange', 'cyan', 'maroon', 'olive', 'teal' ], strokestyle: 'rgba(0,0,0,0)', margin: 3, textColor: 'black', textFont: 'sans-serif', textSize: 12, textBold: false, textItalic: false, labels: [], labelsSticks: true, labelsSticksHlength: 50, linewidth: 1, tooltips: null, tooltipsOverride: null, tooltipsEffect: 'fade', tooltipsCssClass: 'RGraph_tooltip', tooltipsEvent: 'click', highlightStroke: 'rgba(0,0,0,0)', highlightFill: 'rgba(255,255,255,0.7)', highlightLinewidth: 1, highlightStyle: 'normal', highlightStyleOutlineWidth: 7, title: '', titleSize: 16, titleX: null, titleY: null, titleHalign: 'center', titleValign: null, titleColor: 'black', titleFont: null, titleBold: false, titleItalic: false, titleSubtitle: null, titleSubtitleSize: 10, titleSubtitleX: null, titleSubtitleY: null, titleSubtitleHalign: 'center', titleSubtitleValign: null, titleSubtitleColor: '#aaa', titleSubtitleFont: null, titleSubtitleBold: false, titleSubtitleItalic: false, shadow: false, shadowOffsetx: 2, shadowOffsety: 2, shadowBlur: 2, shadowOpacity: 0.25, exploded: 0, roundRobinMultiplier: 1, donut: false, donutWidth: 75, key: null, keyColors: null, keyOffsetx: 0, keyOffsety: 0, keyTextOffsetx: 0, keyTextOffsety: -1, keyTextSize: null, keyTextBold: null, keyTextItalic: null }; // // Copy the global object properties to this instance // RG.SVG.getGlobals(this); /** * "Decorate" the object with the generic effects if the effects library has been included */ if (RG.SVG.FX && typeof RG.SVG.FX.decorate === 'function') { RG.SVG.FX.decorate(this); } var prop = this.properties; // // The draw method draws the Bar chart // this.draw = function () { // Fire the beforedraw event RG.SVG.fireCustomEvent(this, 'onbeforedraw'); //(Re)set this so it doesn't grow endlessly this.angles = []; // Should the first thing that's done inthe.draw() function // except for the onbeforedraw event this.width = Number(this.svg.getAttribute('width')); this.height = Number(this.svg.getAttribute('height')); // Create the defs tag if necessary RG.SVG.createDefs(this); this.graphWidth = this.width - prop.gutterLeft - prop.gutterRight; this.graphHeight = this.height - prop.gutterTop - prop.gutterBottom; // Work out the center point this.centerx = (this.graphWidth / 2) + prop.gutterLeft; this.centery = (this.graphHeight / 2) + prop.gutterTop; this.radius = ma.min(this.graphWidth, this.graphHeight) / 2; // Allow the user to override the calculated centerx/y/radius this.centerx = typeof prop.centerx === 'number' ? prop.centerx : this.centerx; this.centery = typeof prop.centery === 'number' ? prop.centery : this.centery; this.radius = typeof prop.radius === 'number' ? prop.radius : this.radius; // // Allow the centerx/centery/radius to be a plus/minus // if (typeof prop.radius === 'string' && prop.radius.match(/^\+|-\d+$/) ) this.radius += parseFloat(prop.radius); if (typeof prop.centerx === 'string' && prop.centerx.match(/^\+|-\d+$/) ) this.centerx += parseFloat(prop.centerx); if (typeof prop.centery === 'string' && prop.centery.match(/^\+|-\d+$/) ) this.centery += parseFloat(prop.centery); // Parse the colors for gradients // Must be after the cx/cy/r calculations RG.SVG.resetColorsToOriginalValues({object:this}); this.parseColors(); // Go through the data and work out the maximum value this.max = RG.SVG.arrayMax(this.data); this.total = RG.SVG.arraySum(this.data); // Set the explosion to be an array if it's a number if (typeof prop.exploded === 'number' && prop.exploded > 0) { var val = prop.exploded; prop.exploded = []; for (var i=0; i 0 && angles[i].halfway < RG.SVG.TRIG.HALFPI) { halign = 'left'; valign = 'bottom'; } else if (angles[i].halfway > RG.SVG.TRIG.HALFPI && angles[i].halfway < RG.SVG.TRIG.PI) { halign = 'left'; valign = 'top'; } else if (angles[i].halfway > RG.SVG.TRIG.PI && angles[i].halfway < (RG.SVG.TRIG.HALFPI + RG.SVG.TRIG.PI)) { halign = 'right'; valign = 'top'; } else if (angles[i].halfway > (RG.SVG.TRIG.HALFPI + RG.SVG.TRIG.PI) && angles[i].halfway < RG.SVG.TRIG.TWOPI) { halign = 'right'; valign = 'top'; } RG.SVG.text({ object: this, parent: this.svg.all, tag: 'labels', text: typeof labels[i] === 'string' ? labels[i] : '', font: prop.textFont, size: prop.textSize, x: x, y: y, valign: valign, halign: halign, bold: prop.textBold, italic: prop.textItalic, color: prop.textColor }); } }; // // Draws the ingraph labels // this.drawIngraphLabels = function () { if (prop.labelsIngraph) { for (var i=0; i RG.SVG.TRIG.HALFPI) { var index = labels_left.length; labels_left[index] = []; labels_left[index].text = prop.labels[i]; labels_left[index].halign = 'right'; labels = labels_left; labels_coords[i].halign = 'right'; } else { var index = labels_right.length; labels_right[index] = []; labels_right[index].text = prop.labels[i]; labels_right[index].halign = 'right'; labels = labels_right; labels_coords[i].halign = 'left'; } endpoint_inner[0] += (explosion[1] || 0); endpoint_inner[1] += (explosion[2] || 0); endpoint_outer[0] += (explosion[1] || 0); endpoint_outer[1] += (explosion[2] || 0); var x,y; if (labels[index].text) { var stick = RG.SVG.create({ svg: this.svg, parent: this.svg.all, type: 'path', attr: { d: 'M {1} {2} L {3} {4}'.format( this.centerx + endpoint_inner[0], this.centery + endpoint_inner[1], this.centerx + endpoint_outer[0], this.centery + endpoint_outer[1] ), stroke: '#999', fill: 'rgba(0,0,0,0)' } }); } // The path is altered later so this needs saving if (stick) { labels[index].stick = stick; } x = (this.centerx + endpoint_outer[0] + (angle > 1.57 ? -50 : 50)); y = (this.centery + endpoint_outer[1]); labels_coords[i].x = x ; labels_coords[i].y = y; labels_coords[i].text = prop.labels[i]; } // Calculate the spacing for each side var vspace_right = (this.height - prop.gutterTop - prop.gutterBottom) / labels_right.length; var vspace_left = (this.height - prop.gutterTop - prop.gutterBottom) / labels_left.length; // Reset these x = y = 0; // Loop through the RHS labels for (var i=0; i