// 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 pie chart constructor // RGraph.Pie = function (conf) { var id = conf.id, canvas = document.getElementById(id), data = conf.data; // Get the canvas and context objects 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.total = 0; this.subTotal = 0; this.angles = []; this.data = data; this.properties = []; this.type = 'pie'; this.isRGraph = true; this.isrgraph = true; this.rgraph = true; this.coords = []; this.coords.key = []; this.coordsSticks = []; this.coordsText = []; 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 this.exploding = null; this.stopAnimationRequested = false;// Used to control the animations // // Convert strings to numbers // this.data = RGraph.stringsToNumbers(this.data); this.properties = { centerxAdjust: 0, centeryAdjust: 0, colors: ['red', '#ccc', '#cfc', 'blue', 'pink', 'yellow', 'black', 'orange', 'cyan', 'purple', '#78CAEA', '#E284E9', 'white', 'blue', '#9E7BF6'], colorsStroke: 'white', linewidth: 3, labels: [], labelsFormattedDecimals: 0, labelsFormattedPoint: '.', labelsFormattedThousand: ',', labelsFormattedUnitsPre: '', labelsFormattedUnitsPost: '', labelsFont: null, labelsSize: null, labelsColor: null, labelsBold: null, labelsItalic: null, labelsRadiusOffset: 0, labelsSticks: false, labelsSticksLength: 7, labelsSticksColors: null, labelsSticksLinewidth: 1, labelsSticksHlength: 5, labelsList: true, labelsListLeftOffsetx: 0, labelsListLeftOffsety: 0, labelsListRightOffsetx: 0, labelsListRightOffsety: 0, labelsIngraph: null, labelsIngraphColor: null, labelsIngraphFont: null, labelsIngraphSize: null, labelsIngraphBold: null, labelsIngraphItalic: null, labelsIngraphBounding: true, labelsIngraphBoundingFill: 'rgba(255,255,255,0.85)', labelsIngraphBoundingStroke: 'rgba(0,0,0,0)', labelsIngraphSpecific: null, labelsIngraphSpecificFormattedDecimals: 0, labelsIngraphSpecificFormattedPoint: '.', labelsIngraphSpecificFormattedThousand: ',', labelsIngraphSpecificFormattedUnitsPre: '', labelsIngraphSpecificFormattedUnitsPost: '', labelsIngraphUnitsPre: '', labelsIngraphUnitsPost: '', labelsIngraphPoint: '.', labelsIngraphThousand: ',', labelsIngraphDecimals: 0, labelsIngraphRadius: null, labelsIngraphRadiusOffset: 0, labelsIngraphUndrawn: null, labelsIngraphUndrawnAsLabels: null, labelsIngraphUndrawnAlwaysShow: false, labelsCenter: null, labelsCenterSize: 26, labelsCenterFont: null, labelsCenterColor: null, labelsCenterItalic: null, labelsCenterBold: null, labelsCenterOffsetx: 0, labelsCenterOffsety: 0, labelsInside: null, labelsInsideColor: null, labelsInsideSize: null, labelsInsideFont: null, labelsInsideBold: null, labelsInsideItalic: null, labelsInsideDecimals: 0, labelsInsidePoint: '.', labelsInsideThousand: ',', labelsInsideUnitsPre: '', labelsInsideUnitsPost: '', labelsInsideOffsetr: 0, labelsInsideHalign: 'auto', labelsInsideBounding: false, labelsInsideBoundingFill: 'rgba(255,255,255,0.75)', labelsInsideBoundingStroke: 'transparent', labelsInsideSpecific: null, labelsInsideSpecificFormattedDecimals: 0, labelsInsideSpecificFormattedPoint: '.', labelsInsideSpecificFormattedThousand: ',', labelsInsideSpecificFormattedUnitsPre: '', labelsInsideSpecificFormattedUnitsPost: '', marginLeft: 35, marginRight: 35, marginTop: 35, marginBottom: 35, title: '', titleBold: null, titleFont: null, titleSize: null, titleColor: null, titleItalic: 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, shadow: true, shadowColor: '#aaa', shadowOffsetx: 0, shadowOffsety: 0, shadowBlur: 15, textBold: false, textItalic: false, textSize: 12, textColor: 'black', textFont: 'Arial, Verdana, sans-serif', textAccessible: false, textAccessibleOverflow: 'visible', textAccessiblePointerevents: false, text: null, contextmenu: null, tooltips: [], tooltipsEvent: 'onclick', tooltipsEffect: 'slide', tooltipsCssClass: 'RGraph_tooltip', tooltipsCss: null, tooltipsHighlight: true, tooltipsFormattedThousand: ',', tooltipsFormattedPoint: '.', tooltipsFormattedDecimals: 0, tooltipsFormattedUnitsPre: '', tooltipsFormattedUnitsPost: '', tooltipsFormattedKeyColors: null, tooltipsFormattedKeyColorsShape: 'square', tooltipsFormattedKeyLabels: [], tooltipsFormattedListType: 'ul', tooltipsFormattedListItems: null, tooltipsFormattedTableHeaders: null, tooltipsFormattedTableData: null, tooltipsPointer: true, tooltipsPointerOffsetx: 0, tooltipsPointerOffsety: 0, tooltipsPositionStatic: true, tooltipsHotspotIgnore: null, highlightStyle: '2d', highlightStyleTwodFill: 'rgba(255,255,255,0.7)', highlightStyleTwodStroke: 'transparent', highlightStyleTwodLinewidth: 2, highlightStyleOutlineWidth: null, centerx: null, centery: null, radius: null, border: false, borderColor: 'rgba(255,255,255,0.5)', key: null, keyBackground: 'white', keyPosition: 'graph', keyHalign: 'right', keyValign: null, keyShadow: false, keyShadowColor: '#666', keyShadowBlur: 3, keyShadowOffsetx: 2, keyShadowOffsety: 2, keyPositionGutterBoxed: false, keyPositionX: null, keyPositionY: null, keyColorShape: 'square', keyRounded: true, keyLinewidth: 1, keyColors: null, keyInteractive: false, keyInteractiveHighlightChartLinewidth: 2, keyInteractiveHighlightChartStroke: 'black', keyInteractiveHighlightChartFill: 'rgba(255,255,255,0.7)', keyInteractiveHighlightLabel: 'rgba(255,0,0,0.2)', keyLabelsColor: null, keyLabelsFont: null, keyLabelsSize: null, keyLabelsBold: null, keyLabelsItalic: null, keyLabelsOffsetx: 0, keyLabelsOffsety: 0, keyFormattedDecimals: 0, keyFormattedPoint: '.', keyFormattedThousand: ',', keyFormattedUnitsPre: '', keyFormattedUnitsPost: '', keyFormattedValueSpecific: null, keyFormattedItemsCount: null, annotatable: false, annotatableColor: 'black', variant: 'pie', variantDonutWidth: null, variantThreedDepth: 20, exploded: [], effectRoundrobinMultiplier: 1, centerpin: null, centerpinFill: 'gray', centerpinStroke: 'white', origin: 0 - (Math.PI / 2), clearto: 'rgba(0,0,0,0)', events: true, clip: null } // // 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; } } // // Calculate the total // for (var i=0,len=data.length; i 0) { return this.draw3d(); } // // Draw the title // this.drawTitle(); // // The total of the array of values // this.total = RGraph.arraySum(this.data); var tot = this.total; var data = this.data; for (var i=0,len=this.data.length; i 0) { this.drawBorders(); } // // Now draw the segments again with shadow turned off. This is always performed, // not just if the shadow is on. // var len = this.angles.length; var r = this.radius; for (var action=0; action<2; action+=1) { for (var i=0; i 0) || typeof properties.exploded == 'number') { var explosion = typeof properties.exploded == 'number' ? properties.exploded : properties.exploded[index]; var x = 0; var y = 0; var h = explosion; var t = subTotal + (radians / 2); var x = (Math.cos(t) * explosion); var y = (Math.sin(t) * explosion); var r = this.radius; this.context.moveTo(this.centerx + x, this.centery + y); } else { var x = 0; var y = 0; var r = this.radius; } // // Calculate the angles // var startAngle = subTotal; var endAngle = ((subTotal + radians)); this.context.arc(this.centerx + x, this.centery + y, r, startAngle, endAngle, 0); if (properties.variant == 'donut') { this.context.arc( this.centerx + x, this.centery + y, typeof properties.variantDonutWidth == 'number' ? r - properties.variantDonutWidth : r / 2, endAngle, startAngle, true ); } else { this.context.lineTo(this.centerx + x, this.centery + y); } this.context.closePath(); // Keep hold of the angles this.angles.push([ subTotal, subTotal + radians, this.centerx + x, this.centery + y ]); //this.context.stroke(); this.context.fill(); // // Calculate the segment angle // this.subTotal += radians; }; // // Draws the graphs labels // this.drawLabels = function () { if (properties.labels && properties.labels.length) { // // If the xaxisLabels option is a string then turn it // into an array. // if (typeof properties.labels === 'string') { properties.labels = RGraph.arrayPad({ array: [], length: this.data.length, value: properties.labels }); } for (var i=0; i (RGraph.TWOPI + RGraph.HALFPI) ? 2 : -2) : 0), y = cy + explosion_offsety + (((r + 10 + properties.labelsRadiusOffset) * Math.sin(a))); // // If sticks are enabled use the endpoints that have been saved // if (this.coordsSticks && this.coordsSticks[i]) { var x = this.coordsSticks[i][4][0] + (x < cx ? -5 : 5), y = this.coordsSticks[i][4][1]; } // // Alignment // //vAlignment = y < cy ? 'center' : 'center'; vAlignment = 'center'; hAlignment = x < cx ? 'right' : 'left'; this.context.fillStyle = properties.textColor; //if ( typeof properties.labelsColors === 'object' && properties.labelsColors && properties.labelsColors[i]) { // this.context.fillStyle = properties.labelsColors[i]; //} RGraph.text({ object: this, font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic, x: x, y: y, text: labels[i], valign: vAlignment, halign: hAlignment, tag: 'labels', cssClass: RGraph.getLabelsCSSClassName({ object: this, name: 'labelsClass', index: i }) }); } this.context.fill(); } }; // // A newer way of spacing out labels // this.drawLabelsList = function () { var segment = this.angles[i], labels = properties.labels, labels_right = [], labels_left = [], left = [], right = [], centerx = this.centerx, centery = this.centery, radius = this.radius, offset = 50 // This may not be used - see the endpoint_outer var below // // Draw the right hand side labels // for (var i=0; i (-1 * RGraph.HALFPI) && angle < RGraph.HALFPI) { labels_right.push([ i, angle, labels[i] ? labels[i] : '', endpoint_inner, endpoint_outer, textConf.color, RGraph.arrayClone(explosion) ]); } else { labels_left.push([ i, angle, labels[i] ? labels[i] : '', endpoint_inner, endpoint_outer, textConf.color, RGraph.arrayClone(explosion) ]); } } // // Draw the right hand side labels first // // Calculate how much space there is for each label var vspace_right = (this.canvas.height - properties.marginTop - properties.marginBottom) / labels_right.length; for (var i=0,y=(properties.marginTop + (vspace_right / 2)); i=0; y+=vspace_left,i--) { if (labels_left[i][2]) { var x = this.centerx - this.radius - offset, idx = labels_left[i][0], explosionX = labels_left[i][6][0] ? labels_left[i][6][1] : 0, explosionY = labels_left[i][6][0] ? labels_left[i][6][2] : 0 var ret = RGraph.text({ object: this, font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic, x: x + explosionX + properties.labelsListLeftOffsetx, y: y + explosionY + properties.labelsListLeftOffsety, text: labels_left[i][2], valign: 'center', halign: 'right', tag: 'labels', cssClass: RGraph.getLabelsCSSClassName({ object: this, name: 'labelsClass', index: i }) }); if (ret && ret.node) { ret.node.__index__ = labels_left[i][0]; } this.path( 'lw % b m % % qc % % % % s %', properties.labelsSticksLinewidth, labels_left[i][3][0] + explosionX,labels_left[i][3][1] + explosionY, labels_left[i][4][0] + explosionX,Math.round(labels_left[i][4][1] + explosionY),ret.x + 5 + ret.width,ret.y + (ret.height / 2), labels_left[i][5] ); // Draw a circle at the end of the stick this.path( 'b a % % 2 0 6.2830 false, f %', ret.x + 5 + ret.width,ret.y + (ret.height / 2), labels_left[i][5] ); } } }; // // Draw the labelsInside labels if they've been // specified // this.drawLabelsInside = function () { // Get the text configuration var textConf = RGraph.getTextConf({ object: this, prefix: 'labelsInside' }); // If the labelsInsideSpecific is a string (or a number) // then convert it to an array of that string repeated // a number of times (the same number as there are // items in the data) if (RGraph.isTextual(properties.labelsInsideSpecific)) { properties.labelsInsideSpecific = RGraph.arrayFill( [], this.data.length, properties.labelsInsideSpecific ); } for (let i=0; i= this.centerx) { var halign = 'right'; } else { var halign = 'left'; } // If the labelsInsideSpecific property is set // then use that if (RGraph.isArray(properties.labelsInsideSpecific) && RGraph.isTextual(properties.labelsInsideSpecific[i])) { var label = String(properties.labelsInsideSpecific[i]); // // Allow for label substitution // label = RGraph.labelSubstitution({ object: this, text: label, index: i, value: this.data[i], decimals: properties.labelsInsideSpecificFormattedDecimals || 0, unitsPre: properties.labelsInsideSpecificFormattedUnitsPre || '', unitsPost: properties.labelsInsideSpecificFormattedUnitsPost || '', thousand: properties.labelsInsideSpecificFormattedThousand || ',', point: properties.labelsInsideSpecificFormattedPoint || '.' }); } else { var label = RGraph.numberFormat({ object: this, number: this.data[i].toFixed(properties.labelsInsideDecimals), unitspre: properties.labelsInsideUnitsPre, unitspost: properties.labelsInsideUnitsPost, point: properties.labelsInsidePoint, thousand: properties.labelsInsideThousand }); } RGraph.text({ object: this, x: p[0], y: p[1], text: label, size: textConf.size, font: textConf.font, color: textConf.color, bold: textConf.bold, italic: textConf.italic, halign: properties.labelsInsideHalign === 'center' ? 'center' : halign, valign: 'center', bounding: properties.labelsInsideBounding, boundingFill: properties.labelsInsideBoundingFill, boundingStroke: properties.labelsInsideBoundingStroke }); } } // // This function draws the pie chart sticks (for the labels) // this.drawSticks = function () { var offset = properties.linewidth / 2, exploded = properties.exploded, sticks = properties.labelsSticks, colors = properties.colors, cx = this.centerx, cy = this.centery, radius = this.radius, points = [], linewidth = properties.labelsSticksLinewidth for (var i=0,len=this.angles.length; i cx ? 5 : -5); points[4] = [ points[2][0] + (points[2][0] > cx ? 5 + properties.labelsSticksHlength : -5 - properties.labelsSticksHlength), points[2][1] ]; this.context.moveTo(points[0][0], points[0][1]); this.context.quadraticCurveTo( points[2][0], points[2][1], points[4][0], points[4][1] ); this.context.stroke(); // // Save the stick end coords // this.coordsSticks[i] = [ points[0], points[1], points[2], points[3], points[4] ]; } }; // // The (now Pie chart specific) getSegment function // // @param object e The event object // this.getShape = function (e) { // The optional arg provides a way of allowing some accuracy (pixels) var accuracy = arguments[1] ? arguments[1] : 0; var canvas = this.canvas; var context = this.context; var mouseCoords = RGraph.getMouseXY(e); var mouseX = mouseCoords[0]; var mouseY = mouseCoords[1]; var r = this.radius; var angles = this.angles; for (var i=0,len=angles.length; i RGraph.TWOPI) angles[i][1] -= RGraph.TWOPI; // // Get the tooltip for the returned shape // if (RGraph.parseTooltipText && properties.tooltips) { var tooltip = RGraph.parseTooltipText(properties.tooltips, i); } return { object: this, x: angles[i][2], y: angles[i][3], radius: this.radius, angleStart: angles[i][0], angleEnd: angles[i][1], index: i, dataset: 0, sequentialIndex: i, label: properties.labels && typeof properties.labels[i] === 'string' ? properties.labels[i] : null, tooltip: typeof tooltip === 'string' ? tooltip : null }; } } return null; }; // // Draw the borders for the Pie chart // this.drawBorders = function () { if (properties.linewidth > 0) { this.context.lineWidth = properties.linewidth; this.context.strokeStyle = properties.colorsStroke; var r = this.radius; for (var i=0,len=this.angles.length; i -1) { // this.context.arc(shape.x, shape.y, shape.radius, shape.angleStart, shape.angleEnd, false); // this.context.arc(shape.x, shape.y, typeof properties.variantDonutWidth == 'number' ? this.radius - properties.variantDonutWidth : shape.radius / 2, shape.angleEnd, shape.angleStart, true); // } else { // this.context.arc(shape.x, shape.y, shape.radius + 1, shape.angleStart, shape.angleEnd, false); // this.context.lineTo(shape.x, shape.y); // } //this.context.closePath(); //this.context.stroke(); //this.context.fill(); // // 3D style of highlighting // } else if (properties.highlightStyle == '3d') { this.context.lineWidth = 1; // This is the extent of the 2D effect. Bigger values will give the appearance of a larger "protusion" var extent = 2; // Draw a white-out where the segment is this.context.beginPath(); RGraph.noShadow(this); this.context.fillStyle = 'rgba(0,0,0,0)'; this.context.arc(shape.x, shape.y, shape.radius, shape.angleStart, shape.angleEnd, false); if (properties.variant == 'donut') { this.context.arc(shape.x, shape.y, shape.radius / 5, shape.angleEnd, shape.angleStart, true); } else { this.context.lineTo(shape.x, shape.y); } this.context.closePath(); this.context.fill(); // Draw the new segment this.context.beginPath(); this.context.shadowColor = '#666'; this.context.shadowBlur = 3; this.context.shadowOffsetX = 3; this.context.shadowOffsetY = 3; this.context.fillStyle = properties.colors[shape.index]; this.context.strokeStyle = properties.colorsStroke; this.context.arc(shape.x - extent, shape.y - extent, shape.radius, shape.angleStart, shape.angleEnd, false); if (properties.variant == 'donut') { this.context.arc(shape.x - extent, shape.y - extent, shape.radius / 2, shape.angleEnd, shape.angleStart, true) } else { this.context.lineTo(shape.x - extent, shape.y - extent); } this.context.closePath(); this.context.stroke(); this.context.fill(); // Turn off the shadow RGraph.noShadow(this); // // If a border is defined, redraw that // if (properties.border) { this.context.beginPath(); this.context.strokeStyle = properties.borderColor; this.context.lineWidth = 5; this.context.arc(shape.x - extent, shape.y - extent, shape.radius - 2, shape.angleStart, shape.angleEnd, false); this.context.stroke(); } // Outline style of highlighting } else if (properties.highlightStyle === 'outline') { var index = shape.index, coords = this.angles[index], color = this.get('colors')[index] width = this.radius / 12.5; // Allow custom setting of outline if (typeof properties.highlightStyleOutlineWidth === 'number') { width = properties.highlightStyleOutlineWidth; } this.path( 'ga 0.25 b a % % % % % false a % % % % % true c f % ga 1', coords[2], coords[3], this.radius + 2 + width, coords[0], coords[1], coords[2], coords[3], this.radius + 2, coords[1], coords[0], color ); // Default 2D style of highlighting } else { this.path( 'b ss % fs % lw %', properties.highlightStyleTwodStroke, properties.highlightStyleTwodFill, properties.highlightStyleTwodLinewidth ); if (properties.variant.indexOf('donut') > -1) { this.context.arc(shape.x, shape.y, shape.radius, shape.angleStart, shape.angleEnd, false); this.context.arc(shape.x, shape.y, typeof properties.variantDonutWidth == 'number' ? this.radius - properties.variantDonutWidth : shape.radius / 2, shape.angleEnd, shape.angleStart, true); } else { this.context.arc(shape.x, shape.y, shape.radius + 1, shape.angleStart, shape.angleEnd, false); this.context.lineTo(shape.x, shape.y); } this.context.closePath(); this.context.stroke(); this.context.fill(); } } }; // // The getObjectByXY() worker method. The Pie chart is able to use the // getShape() method - so it does. // this.getObjectByXY = function (e) { if (this.getShape(e)) { return this; } }; // // Draws the centerpin if requested // this.drawCenterpin = function () { if (typeof properties.centerpin === 'number' && properties.centerpin > 0) { var cx = this.centerx; var cy = this.centery; this.context.beginPath(); this.context.strokeStyle = properties.centerpinStroke ? properties.centerpinStroke : properties.colorsStroke; this.context.fillStyle = properties.centerpinFill ? properties.centerpinFill : properties.colorsStroke; this.context.moveTo(cx, cy); this.context.arc(cx, cy, properties.centerpin, 0, RGraph.TWOPI, false); this.context.stroke(); this.context.fill(); } }; // // This draws Ingraph labels // this.drawLabelsIngraph = this.drawInGraphLabels = function () { var context = this.context; var cx = this.centerx; var cy = this.centery; var radius = properties.labelsIngraphRadius; // If the labelsIngraphSpecific property is a string // make it an array and populate it if (typeof properties.labelsIngraphSpecific === 'string') { properties.labelsIngraphSpecific = RGraph.arrayFill( [], this.data.length, properties.labelsIngraphSpecific ); } // Reset this to an empty array this.set('labelsIngraphUndrawn', []); // Account for offsetting if (RGraph.isNumber(properties.labelsIngraphRadiusOffset)) { var radiusOffset = properties.labelsIngraphRadiusOffset; } else { var radiusOffset = 0; } // // Is the radius less than 2? If so then it's a // factor and not an exact point // if (radius <= 2 && radius > 0) { radiusFactor = radius; } else { radiusFactor = 0.5; } if (properties.variant === 'donut') { var r = this.radius * (0.5 + (radiusFactor * 0.5)); if (typeof properties.variantDonutWidth == 'number') { var r = (this.radius - properties.variantDonutWidth) + (properties.variantDonutWidth / 2); } } else { var r = this.radius * radiusFactor; } if (radius > 2) { r = radius; } for (var i=0,len=this.angles.length; i= 0) { this.path( 'a % % % % % true', this.centerx, this.centery, this.radius - (this.properties.variantDonutWidth || this.radius / 2), this.angles[i][1], this.angles[i][0] ); } let [textX, textY, textW, textH] = [ret.x, ret.y, ret.width, ret.height]; if ( this.properties.labelsIngraphUndrawnAlwaysShow === false && ( !this.context.isPointInPath(textX, textY) || !this.context.isPointInPath(textX + textW, textY) || !this.context.isPointInPath(textX, textY + textH) || !this.context.isPointInPath(textX + textW, textY + textH) ) ) { // This clears any existing path this.context.beginPath(); var undrawn = { index: i, text: text }; this.get('labelsIngraphUndrawn').push(undrawn); // If undrawn ingraph labels are wanted to // be set as labels instead - add this text // to the Pie chart labels property if (properties.labelsIngraphUndrawn && properties.labelsIngraphUndrawnAsLabels) { if (!RGraph.isArray(this.properties.labels)) { this.properties.labels = []; } this.properties.labels[undrawn.index] = undrawn.text; if (!RGraph.Registry.get('pie-chart-ingraphlabels-redraw-function-added')) { var id = RGraph.addCustomEventListener(this, 'draw', function (obj) { // Now that we're in the // function that runs to // reenable the labelsingraph // labels - remove it RGraph.removeCustomEventListener( obj, RGraph.Registry.get('pie-chart-ingraphlabels-redraw-function-added') ); // Redraw the canvas - but only // once RGraph.runOnce('pie-chart-ingraphlabels-redraw-function', function () { RGraph.redraw(); }); }); RGraph.Registry.set('pie-chart-ingraphlabels-redraw-function-added', id); } } continue; } // This clears any existing path this.context.beginPath(); ////////////////////////////////////////////////////////////////// var ret = RGraph.text({ object: this, font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic, x: x, y: y, text: text, valign: 'center', halign: 'center', bounding: properties.labelsIngraphBounding, boundingFill: properties.labelsIngraphBoundingFill, boundingStroke: properties.labelsIngraphBoundingStroke, tag: 'labels.ingraph' }); this.context.stroke(); } } }; // // Draws the center label if required // this.drawCenterLabel = function (label) { var textConf = RGraph.getTextConf({ object: this, prefix: 'labelsCenter' }); RGraph.text({ object: this, font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic, x: this.centerx + properties.labelsCenterOffsetx, y: this.centery + properties.labelsCenterOffsety, halign: 'center', valign: 'center', text: label, bounding: true, boundingFill: 'rgba(255,255,255,0.7)', boundingStroke: 'rgba(0,0,0,0)', tag: 'labels.center' }); }; // // This returns the angle for a value based around the maximum number // // @param number value The value to get the angle for // this.getAngle = function (value) { if (value > this.total) { return null; } var angle = (value / this.total) * RGraph.TWOPI; // Handle the origin (it can br -HALFPI or 0) angle += properties.origin; 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.colors = RGraph.arrayClone(properties.colors); this.original_colors.keyColors = RGraph.arrayClone(properties.keyColors); this.original_colors.colorsStroke = RGraph.arrayClone(properties.colorsStroke); this.original_colors.highlightStroke = RGraph.arrayClone(properties.highlightStroke); this.original_colors.highlightStyleTwodFill = RGraph.arrayClone(properties.highlightStyleTwodFill); this.original_colors.highlightStyleTwodStroke = RGraph.arrayClone(properties.highlightStyleTwodStroke); this.original_colors.labelsIngraphBoundingFill = RGraph.arrayClone(properties.labelsIngraphBoundingFill); this.original_colors.labelsIngraphColor = RGraph.arrayClone(properties.labelsIngraphColor); } for (var i=0; i0; i-=1) { this.set({ centeryAdjust: i }); if (i === parseInt(depth / 2) ) { this.set({ labels: prop_labels, labelsSticks: prop_labelsSticks }); } if (i === 0) { this.set({ shadow: prop_shadow }); } if (i === 1) { this.set({ title: prop_title }); } this.draw(); // Turn off the shadow after the bottom pie/donut has // been drawn this.set({ shadow: false, title: '' }); // // If on the middle pie/donut turn the labels and sticks off // if (i <= parseInt(depth / 2) ) { this.set({ labels: [], labelsSticks: false }); } // // Make what we're drawng darker by going over // it in a semi-transparent dark color // if (i > 1) { if (properties.variant.indexOf('donut') !== -1) { for (var j=0; j= 0) { // Determine the radius if (RGraph.isNull(properties.variantDonutWidth)) { var radius = (this.radius / 2) + (this.radius / 4); } else { var radius = (this.radius - properties.variantDonutWidth) + (properties.variantDonutWidth / 2); } } else { var radius = this.radius * multiplier; } var explosion = typeof properties.exploded == 'number' ? properties.exploded : properties.exploded[index]; var endpoint = RGraph.getRadiusEndPoint( this.centerx, this.centery, angle, radius + (explosion || 0) ); // Allow for the 3D stretching of the canvas if (properties.variant.indexOf('3d') > 0) { var width = properties.variantDonutWidth === 'number' ? properties.variantDonutWidth : this.radius / 2; endpoint[0] = (endpoint[0] - this.centerx) * 1.5 + this.centerx; } // Position the tooltip in the X direction args.tooltip.style.left = ( canvasXY[0] // The X coordinate of the canvas + endpoint[0] // The X coordinate of the shape on the chart - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width + obj.properties.tooltipsOffsetx // Add any user defined offset ) + 'px'; args.tooltip.style.top = ( canvasXY[1] // The Y coordinate of the canvas + endpoint[1] // The Y coordinate of the shape on the chart - tooltip.offsetHeight // The height of the tooltip + obj.properties.tooltipsOffsety // Add any user defined offset - 10 // Account for the pointer ) + 'px'; }; // // This returns the relevant value for the formatted key // macro %{value}. THIS VALUE SHOULD NOT BE FORMATTED. // // @param number index The index in the dataset to get // the value for // this.getKeyValue = function (index) { if ( RGraph.isArray(this.properties.keyFormattedValueSpecific) && RGraph.isNumber(this.properties.keyFormattedValueSpecific[index])) { return this.properties.keyFormattedValueSpecific[index]; } else { return this.data[index]; } }; // // Returns how many data-points there should be when a string // based key property has been specified. For example, this: // // key: '%{property:_labels[%{index}]} %{value_formatted}' // // ...depending on how many bits of data ther is might get // turned into this: // // key: [ // '%{property:_labels[%{index}]} %{value_formatted}', // '%{property:_labels[%{index}]} %{value_formatted}', // '%{property:_labels[%{index}]} %{value_formatted}', // '%{property:_labels[%{index}]} %{value_formatted}', // '%{property:_labels[%{index}]} %{value_formatted}', // ] // // ... ie in that case there would be 4 data-points so the // template is repeated 4 times. // this.getKeyNumDatapoints = function () { return this.data.length; }; // // Now need to register all chart types. MUST be after the setters/getters are defined // RGraph.register(this); // // This is the 'end' of the constructor so if the first argument // contains configuration data - handle that. // RGraph.parseObjectStyleConfig(this, conf.options); };