// 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.Activity = 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') { 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; var ret = RGraph.SVG.commonSetter({ object: this, name: name, value: value }); name = ret.name; value = ret.value; 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 this.properties[name]; }; this.type = 'activity'; this.min = RGraph.SVG.stringsToNumbers(conf.min); this.max = RGraph.SVG.stringsToNumbers(conf.max); this.value = RGraph.SVG.stringsToNumbers(conf.value); this.currentValue = null; // Used by animations 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.colorsParsed = false; this.originalColors = {}; this.gradientCounter = 1; this.adjusting_index = null; this.nodes = {}; this.firstDraw = true; // After the first draw this will be false // 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 = { radius: null, centerx: null, centery: null, width: null, ends: 'round', marginLeft: 15, marginRight: 15, marginTop: 15, marginBottom: 15, marginInner: 1, backgroundColor: 'black', backgroundGrid: false, backgroundGridColor: '#ddd', backgroundGridRadials: true, backgroundGridRadialsCount: 8, backgroundRings: true, backgroundRingsColors: null, backgroundRingsAlpha: 0.5, colors: ['#F45B5B','#90EE7E','#2B908F','red','green','blue','yellow','pink'], icons: null, iconsWidth: null, iconsHeight: null, iconsOffsetx: 0, iconsOffsety: 0, textFont: 'Arial, Verdana, sans-serif', textSize: 12, textColor: '#aaa', textBold: false, textItalic: false, text: null, labelsCenter: false, labelsCenterIndex: 0, labelsCenterFont: null, labelsCenterSize: 40, labelsCenterColor: null, labelsCenterBold: null, labelsCenterItalic: null, labelsCenterUnitsPre: '', labelsCenterUnitsPost: '', labelsCenterDecimals: 0, labelsCenterPoint: '.', labelsCenterThousand: ',', labelsCenterSpecific: null, labelsCenterHalign: 'center', labelsCenterValign: 'center', labelsCenterOffsetx: 0, labelsCenterOffsety: 0, labels: [], labelsColor: null, labelsFont: null, labelsSize: null, labelsBold: null, labelsItalic: null, labelsBackgroundFill: 'transparent', labelsBackgroundStroke: 'transparent', labelsHalign: 'right', labelsValign: 'center', labelsOffsetx: 0, labelsOffsety: 0, labelsFormattedDecimals: 0, labelsFormattedPoint: '.', labelsFormattedThousand: ',', labelsFormattedUnitsPre: '', labelsFormattedUnitsPost: '', adjustable: false, 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, tooltipsPositionStatic: true, highlightStroke: 'rgba(0,0,0,0)', highlightFill: 'rgba(255,255,255,0.7)', highlightLinewidth: 1, 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'); // // Convert the value to an array if its a number // if (typeof this.value === 'number') { this.value = [this.value]; } // Bounds checking for (var i=0; i<this.value.length; ++i) { if (this.value[i] > this.max) this.value[i] = this.max; if (this.value[i] < this.min) this.value[i] = this.min; } // Reset this to prevent it from growing this.nodes = {}; // Should the first 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 this.defs = 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.graphHeight / 2) + properties.marginTop; this.radius = Math.min(this.graphWidth / 2, this.graphHeight / 2); // Allow the user to override the calculated centerx/y/radius this.centerx = typeof properties.centerx === 'number' ? properties.centerx : this.centerx; this.centery = typeof properties.centery === 'number' ? properties.centery : this.centery; this.radius = typeof properties.radius === 'number' ? properties.radius : this.radius; // // 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); // Parse the colors for gradients RGraph.SVG.resetColorsToOriginalValues({object:this}); this.parseColors(); // Calculate the width if (!properties.width) { properties.width = (this.radius * 0.75) / this.value.length; properties.width -= (2 * properties.marginInner); } // Install clipping if requested if (this.properties.clip && !this.clipid) { 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'); } // Draw the background this.drawBackground(); // Draw the meter this.drawMeter(); // Draw the labels this.drawLabels(); // Draw the icons this.drawIcons(); // // Ajusting // if (properties.adjustable && !this.adjusting_event_listeners_installed) { this.adjusting_mousedown = false; 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; } // Get the radius of the click var radius = obj.getRadius(e); if (radius > obj.radius) { return; } if (typeof obj.adjusting_index !== 'number') { var index = obj.getIndexByRadius({radius: radius}); obj.adjusting_index = index; } else { var index = obj.adjusting_index; } var value = obj.getValue(e); obj.value[index] = value; obj.currentValue = RGraph.SVG.arrayClone(value); //RGraph.SVG.clear(obj.svg); //obj.draw(); RGraph.SVG.redraw(obj.svg); }; this.container.addEventListener('mousedown', function (e) { obj.adjusting_mousedown = true; func(e); // Fire the beforedraw event RGraph.SVG.fireCustomEvent(obj, 'onadjustbegin'); }, false); this.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; obj.adjusting_index = null; // Fire the beforedraw event RGraph.SVG.fireCustomEvent(obj, 'onadjustend'); }, false); this.adjusting_event_listeners_installed = true; } // Add the event listener that clears the highlight var obj = this; document.body.addEventListener('mouseup', function (e) { obj.removeHighlight(); }, false); // // 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); } // // 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 //eg: // this.create('rect,x:0,y:0,width:100,height:100'[,parent]); // // @param mixed definition This can either be an object // which holds details of the object // that you want to make or a string // that holds the same information. // @param mixed This can be either a string that // holds style information to be // applied to the new node or it // can be the parent node that the // new node is to be added to. // @param string If used, this can be a string // that holds the style information // that is to be applied to the new // node. // 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); }; // // Go through the bars looking for the correct one for the given radius // this.getIndexByRadius = function (opt) { var radius = opt.radius; for (var i=0; i<this.nodes.bars.length; ++i) { var radiusInner = parseFloat(this.nodes.bars[i].getAttribute('data-radius-inner')); var radiusOuter = parseFloat(this.nodes.bars[i].getAttribute('data-radius-outer')); if (radius >= radiusInner && radius <= radiusOuter) { return i; } } return null; }; // // Draw the background"grid" // this.drawBackground = function () { // First thing to do is clear the canvas to the backgroundColor if ( properties.backgroundColor) { RGraph.SVG.create({ svg: this.svg, type: 'rect', parent: this.svgAllGroup, attr: { fill: properties.backgroundColor, x: 0, y: 0, width: this.width, height: this.height } }); } // // Draw the grid? // if (properties.backgroundGrid) { // Determine how many background circles // should be shown on the data points var count = this.value.length + 1; for (var i=0; i<count; i++) { var radius = this.radius - (i * (properties.width + (2 * properties.marginInner))); RGraph.SVG.create({ svg: this.svg, type: 'circle', parent: this.svgAllGroup, attr: { fill: 'transparent', stroke: properties.backgroundGridColor, cx: this.centerx, cy: this.centery, r: radius } }); } // Rename the variable so that it makes a little more sense // when used further down var minRadius = radius // // Draw the background lines that go from the center outwards // if (properties.backgroundGridRadials) { var angle = (RGraph.SVG.TRIG.TWOPI / properties.backgroundGridRadialsCount); for (var i=0; i<=properties.backgroundGridRadialsCount; ++i) { var path1 = RGraph.SVG.TRIG.getArcPath3({ svg: this.svg, cx: this.centerx, cy: this.centery, radius: this.radius, start: i * angle, end: i * angle, lineto: false }); var path2 = RGraph.SVG.TRIG.getArcPath3({ svg: this.svg, cx: this.centerx, cy: this.centery, radius: minRadius, start: i * angle, end: i * angle, lineto: true }); RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.svgAllGroup, attr: { fill: 'transparent', stroke: properties.backgroundGridColor, d: path1 + ' ' + path2 } }); } } } }; // // Draws the meter // this.drawMeter = function () { this.nodes.bars = []; // // Loop through the values and draw the background rings // for (var i=0; i<this.value.length; ++i) { // The radiuses of the ring var inner = this.radius - properties.marginInner - (i * properties.marginInner * 2) - (i * properties.width) - properties.width, outer = this.radius - properties.marginInner - (i * properties.marginInner * 2) - (i * properties.width); // Determine the color if (RGraph.SVG.isArray(properties.backgroundRingsColors) && typeof properties.backgroundRingsColors[i] === 'string') { var color = properties.backgroundRingsColors[i]; } else { var color = properties.colors[i]; } // Draw the background rings if enabled if (properties.backgroundRings) { // Draw the background ring var path = RGraph.SVG.donut({ svg: this.svgAllGroup, cx: this.centerx, cy: this.centery, innerRadius: inner, outerRadius: outer, fill: color, opacity: properties.backgroundRingsAlpha }); path.setAttribute('data-radiusInner', inner); path.setAttribute('data-radiusOuter', outer); path.setAttribute('data-centerx', this.centerx); path.setAttribute('data-centery', this.centery); } // Create the group the main path and the ends path objects will be children of. // This does not include the background of the ring // // *** IMPORTANT *** // // This group must be created and added to the SVG document AFTER the // background ring or the background rings will sit on top of the clickable // bit and prevent tooltips from working // var group = RGraph.SVG.create({ svg: this.svg, type: 'g', parent: this.svgAllGroup, attr: { 'data-index': i } }); // First calculate the angle that the indicator bar extends to var angle = ((this.value[i] - this.min) / (this.max - this.min)) * RGraph.SVG.TRIG.TWOPI; // // Draw the foreground to the ring // var arcPath = this.pathBar({ radiusInner: inner, radiusOuter: outer, angle: angle }); var path = RGraph.SVG.create({ svg: this.svg, type: 'path', parent: group, attr: { fill: properties.colors[i], d: arcPath, 'data-index': i, 'data-centerx': this.centerx, 'data-centery': this.centery, 'data-angles-info': 'These angles are designed to be used with the RGraph.SVG.TRIG.getArcPath3() function', 'data-start-angle': 0, 'data-end-angle': angle, 'data-radius-inner': inner, 'data-radius-outer': outer } }); // Set the angle and radiuses on the group tag too group.setAttribute('data-centerx', this.centerx); group.setAttribute('data-centery', this.centery); group.setAttribute('data-start-angle', 0); group.setAttribute('data-end-angle', angle); group.setAttribute('data-radius-inner', inner); group.setAttribute('data-radius-outer', outer); this.nodes.bars[i] = group; // // Draw the circles at each end of the bar if necessary // if (properties.ends === 'round' && 0) { var endcircle1 = RGraph.SVG.create({ svg: this.svg, type: 'circle', parent: group, attr: { fill: color, cx: this.centerx, cy: this.centery - outer + (properties.width / 2), r: properties.width / 2 } }); // Calculate the endpoint for the second circle var endpoint = RGraph.SVG.TRIG.getRadiusEndPoint({ cx: this.centerx, cy: this.centery, radius: outer - (properties.width / 2), angle: angle - RGraph.SVG.TRIG.HALFPI }); var endcircle2 = RGraph.SVG.create({ svg: this.svg, type: 'circle', parent: group, attr: { fill: color, cx: this.centerx + endpoint[0], cy: this.centery + endpoint[1], r: properties.width / 2 } }); } // // Add the tooltip if necessary // if ( !RGraph.SVG.isNullish(properties.tooltips) && (!RGraph.SVG.isNullish(properties.tooltips[i]) || typeof properties.tooltips === 'string') ) { var obj = this; // // Add tooltip event listeners // (function (index) { group.addEventListener(properties.tooltipsEvent.replace(/^on/, ''), function (e) { obj.removeHighlight(); // Show the tooltip RGraph.SVG.tooltip({ object: obj, index: index, group: null, sequentialIndex: index, text: typeof properties.tooltips === 'string' ? properties.tooltips : properties.tooltips[index], event: e }); // Highlight the group that has been clicked on obj.highlight(e.target); }, false); group.addEventListener('mousemove', function (e) { e.target.style.cursor = 'pointer'; }, false); })(i); } } // Store the new value as the currentValue this.currentValue = RGraph.SVG.arrayClone(this.value); }; // // This function creates the path for the indicator bar. It only creates the // path - it does not create an element or add anything to the DOM // this.pathBar = function (opt) { var inner = opt.radiusInner, outer = opt.radiusOuter, angle = opt.angle; var arcPath1 = RGraph.SVG.TRIG.getArcPath3({ svg: this.svg, cx: this.centerx, cy: this.centery, radius: outer, start: 0, end: angle, lineto: false }); // Draw a round end if (properties.ends === 'round') { var endpoint1 = RGraph.SVG.TRIG.getRadiusEndPoint({ cx: this.centerx, cy: this.centery, radius: ((outer - inner) / 2) + inner, angle: opt.angle - RGraph.SVG.TRIG.HALFPI }); var endPath1 = RGraph.SVG.TRIG.getArcPath3({ svg: this.svg, cx: this.centerx + endpoint1[0], cy: this.centery + endpoint1[1], radius: (outer - inner) / 2, start: angle, end: angle + RGraph.SVG.TRIG.PI, lineto: false, moveto: false }); } else { endPath1 = ''; } var arcPath2 = RGraph.SVG.TRIG.getArcPath3({ svg: this.svg, cx: this.centerx, cy: this.centery, radius: inner, start: angle, end: 0, anticlockwise: true }); // Draw a round end if (properties.ends === 'round') { var endPath2 = RGraph.SVG.TRIG.getArcPath3({ svg: this.svg, cx: this.centerx, cy: this.centery - inner - ((outer - inner) / 2), radius: (outer - inner) / 2, start: RGraph.SVG.TRIG.PI, end: RGraph.SVG.TRIG.TWOPI, lineto: true }); } else { endPath2 = ''; } return arcPath1 + ' ' + endPath1 + ' ' + arcPath2 + ' ' + endPath2 + ' z'; }; // // Highlights a bar when it has been clicked // // @param el object The element that has been clicked on // this.highlight = function (el) { var group = el.parentNode, index = group.getAttribute('data-index'), radiusInner = parseFloat(group.getAttribute('data-radius-inner')), radiusOuter = parseFloat(group.getAttribute('data-radius-outer')), angle = parseFloat(group.getAttribute('data-end-angle')); // Create a group for the highlight var highlightGroup = RGraph.SVG.create({ svg: this.svg, type: 'g', parent: this.svgAllGroup, attr: { fill: properties.highlightFill, stroke: properties.highlightStroke, }, style: { pointerEvents: 'none' } }); // Get the path for the highlight node var arcPath = this.pathBar({ radiusInner: radiusInner, radiusOuter: radiusOuter, angle: angle }); // Create the highlight node var path = RGraph.SVG.create({ svg: this.svg, type: 'path', parent: highlightGroup, attr: { d: arcPath, 'stroke-width': properties.highlightLinewidth, 'data-highlight': 'true', 'data-index': index, 'data-centerx': this.centerx, 'data-centery': this.centery, 'data-angles-info': 'These angles are designed to be used with the RGraph.SVG.TRIG.getArcPath3() function', 'data-start-angle': 0, 'data-end-angle': angle, 'data-radius-inner': radiusInner, 'data-radius-outer': radiusOuter } }); this.nodes.highlight = highlightGroup; RGraph.SVG.REG.set('highlight', highlightGroup); }; // // Removes any highlight from the chart // this.removeHighlight = function () { //var highlight = RGraph.SVG.REG.get('highlight'); //if (highlight && highlight.parentNode) { // highlight.parentNode.removeChild(highlight); //} //RGraph.SVG.REG.set('highlight', null); RGraph.SVG.removeHighlight(); }; // // Draw the labels // this.drawLabels = function () { // Draw the center label if (properties.labelsCenter) { var label = RGraph.SVG.numberFormat({ object: this, num: 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 : label, x: this.centerx + properties.labelsCenterOffsetx, y: this.centery + properties.labelsCenterOffsety, valign: properties.labelsCenterValign, halign: properties.labelsCenterHalign, 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; } if (properties.labels.length) { var textConf = RGraph.SVG.getTextConf({ object: this, prefix: 'labels' }); if (typeof properties.labels === 'string') { properties.labels = RGraph.SVG.arrayPad({ array: [], length: this.value.length, value: properties.labels }); } // Loop thru the labels for (var i=0; i<properties.labels.length; ++i) { if ( typeof properties.labels === 'object' && properties.labels.length && typeof properties.labels[i] === 'string' ) { var text = RGraph.SVG.labelSubstitution({ object: this, text: properties.labels[i], index: i, value: this.value[i], decimals: properties.labelsFormattedDecimals, point: properties.labelsFormattedPoint, thousand: properties.labelsFormattedThousand, unitsPre: properties.labelsFormattedUnitsPre, unitsPost: properties.labelsFormattedUnitsPost, }); } RGraph.SVG.text({ object: this, parent: this.svgAllGroup, color: textConf.color, font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, text: text, x: this.centerx - 5 + properties.labelsOffsetx - (properties.ends ? (properties.width / 2) : 0), y: this.centery - this.radius + properties.marginInner + (i * properties.width) + (properties.width / 2) + (i * 2 * properties.marginInner) + properties.labelsOffsety, valign: properties.labelsValign, halign: properties.labelsHalign, background: properties.labelsBackground, padding: 2 }); } } }; // // Draw icons if they're wanted // this.drawIcons = function () { if (RGraph.SVG.isArray(properties.icons)) { for (var i=0,images=[]; i<this.value.length; ++i) { if (typeof properties.icons[i] === 'string' && properties.icons[i].length) { // Use this to store the SVG image tags that are created in var svg_images = []; // A reference to this chart object var obj = this; images[i] = new Image(); images[i].src = properties.icons[i]; images[i].index = i; svg_images[i] = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'image', attr: { href: properties.icons[i], x: this.centerx + properties.iconsOffsetx - (properties.ends ? (properties.width / 2) : 0) + 5, y: this.centery - this.radius + properties.marginInner + (i * properties.width) + (properties.width / 2) + (i * 2 * properties.marginInner) + properties.iconsOffsety, index: i } }); // Set the width/height on the image if requested if (typeof properties.iconsWidth === 'number') svg_images[i].setAttribute('width', properties.iconsWidth); if (typeof properties.iconsHeight === 'number') svg_images[i].setAttribute('height', properties.iconsHeight); // Now move the SVG image as required svg_images[i].onload = function () { var index = this.getAttribute('index'), width = properties.iconsWidth || images[index].width, height = properties.iconsHeight || images[index].height; // Reposition the image //this.setAttribute('x', parseInt(this.getAttribute('x')) + (width / 2)); this.setAttribute('y', parseInt(this.getAttribute('y')) - (height / 2)); } } } } }; // // 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), backgroundColor: RGraph.SVG.arrayClone(properties.backgroundColor) } } // 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' }); } } // Background color properties.backgroundColor = RGraph.SVG.parseColorLinear({ object: this, color: properties.backgroundColor, start: properties.marginLeft, end: this.width - properties.marginRight, direction: 'horizontal' }); }; // // 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; }; // // This function returns the pertinent angle for a particular click (or other mouse event) // // @param obj e The event object // OR // int e An integer value for // which to the relevant // angle // this.getAngle = function (e) { if (typeof e === 'number') { var angle = ((e - this.min) / (this.max - this.min)) * RGraph.SVG.TRIG.TWOPI; angle -= RGraph.SVG.TRIG.HALFPI; } else { var mouseX = e.offsetX, mouseY = e.offsetY; var angle = RGraph.SVG.TRIG.getAngleByXY({ cx: this.centerx, cy: this.centery, x: mouseX, y: mouseY }); angle -= RGraph.SVG.TRIG.HALFPI; } return angle; }; // // This function returns the pertinent value for a particular click (or other mouse event) // // @param obj e The event object // // this.getValue = function (e) { // Treat the argument as an angle (given in radians) if (typeof e === 'number') { var angle = e; // Treat the argument as an event object } else { // Get the angle of the click var angle = this.getAngle(e); } // Calculate the value based on the angle and min/max values var value = (((angle + RGraph.SVG.TRIG.HALFPI) / RGraph.SVG.TRIG.TWOPI) * (this.max - this.min)) + this.min; // Ensure that the value is in range value = Math.max(value, this.min); value = Math.min(value, this.max); return value; }; // // This function returns the pertinent radius (from the centerx/y) of a given event object // (eg from a click or mouse event). // // @param obj e The event object // this.getRadius = function (e) { var x = e.offsetX, y = e.offsetY; var radius = RGraph.SVG.TRIG.getHypLength({ x1: x, y1: y, x2: this.centerx, y2: this.centery }); return radius; }; // // SVG Activity meter grow() // // This effect gradually increases the represented value // // @param An object of options - eg: {frames: 60} // @param function An optional callback function // this.grow = function () { var obj = this; // // Convert the value to an array if its a number // if (typeof this.value === 'number') { this.value = [this.value]; } //this.currentValue = this.currentValue || obj.min; if (RGraph.SVG.isNullish(this.currentValue)) { this.currentValue = []; for (var i=0; i<this.value.length; ++i) { this.currentValue[i] = this.min; } } var opt = arguments[0] || {}, frames = opt.frames || 30, frame = 0, diff = [], step = [], callback = arguments[1] || function () {}, initial = []; // Set a few properties too by looping through the data for (var i=0; i<this.value.length; ++i) { diff[i] = this.value[i] - this.currentValue[i]; step[i] = diff[i] / frames; initial[i] = this.currentValue[i]; } function iterator () { for (var i=0; i<obj.value.length; ++i) { obj.value[i] = initial[i] + (frame * step[i]); } // Increment the frame frame++; RGraph.SVG.redraw(obj.svg); if (frame <= frames) { RGraph.SVG.FX.update(iterator); } else { callback(obj); } } iterator(); return this; }; // // A worker function that handles Bar chart specific tooltip substitutions // this.tooltipSubstitutions = function (opt) { var indexes = [0, opt.index]; return { index: indexes[1], dataset: indexes[0], sequentialIndex: indexes[1], value: this.value[indexes[1]], values: [this.value[indexes[1]]] }; }; // // 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) { return { label: (RGraph.SVG.isArray(properties.tooltipsFormattedKeyLabels) && typeof properties.tooltipsFormattedKeyLabels[specific.index] === 'string') ? properties.tooltipsFormattedKeyLabels[specific.index] : properties.labels[specific.index], color: (RGraph.SVG.isArray(properties.tooltipsFormattedKeyColors) && properties.tooltipsFormattedKeyColors[specific.index]) ? properties.tooltipsFormattedKeyColors[specific.index] : properties.colors[specific.index], value: this.value[specific.index] }; }; // // 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), radiusOuter = parseFloat(this.nodes.bars[index].firstChild.getAttribute('data-radius-outer')), radiusInner = parseFloat(this.nodes.bars[index].firstChild.getAttribute('data-radius-inner')), start = parseFloat(this.nodes.bars[index].firstChild.getAttribute('data-start-angle')) - RGraph.SVG.TRIG.HALFPI; end = parseFloat(this.nodes.bars[index].firstChild.getAttribute('data-end-angle')) - RGraph.SVG.TRIG.HALFPI; var endpoint = RGraph.SVG.TRIG.getRadiusEndPoint({ cx: this.centerx, cy: this.centery, radius: radiusInner + ((radiusOuter - radiusInner) / 2) , angle: ((end - start) / 2) + start }); // Position the tooltip in the X direction args.tooltip.style.left = ( svgXY[0] // The X coordinate of the canvas + (this.centerx + endpoint[0]) - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width ) + 'px'; // Position the tooltip in the Y direction args.tooltip.style.top = ( svgXY[1] // The Y coordinate of the canvas + (this.centery + endpoint[1]) // The Y coordinate of the position - 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. // // @param string clip The clip string as supplied by the // user in the chart configuration // this.clipToScaleWorker = function (clipPath) { // The Regular expression is actually done by the // calling RGraph.clipTo.start() function in the core // library 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 = this.getAngle(this.min); } // Change the radius if the number is "max" if (RegExp.$2 === 'max') { a2 = this.getAngle(this.max); } var arcPath = RGraph.SVG.TRIG.getArcPath3({ cx: this.centerx, cy: this.centery, r: Math.max(this.width, this.height), start: a1 + RGraph.SVG.TRIG.HALFPI, end: a2 + RGraph.SVG.TRIG.HALFPI, anticlockwise: false, lineto: true }); RGraph.SVG.create({ svg: this.svg, type: 'path', parent: clipPath, attr: { d: 'M {1} {2} {3} '.format( this.centerx, this.centery, arcPath ) } }); // Now set the clip-path attribute on the first // Line charts 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);