// 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.Bipolar = 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 (name === 'xaxisLabelsPositionEdgeTickmarksCount') { name = 'xaxisTickmarksCount'; } 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; // 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 = 'bipolar'; 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.data = [conf.left, conf.right]; this.left = conf.left; this.right = conf.right; this.coords = []; this.coordsLeft = []; this.coordsRight = []; this.coords2 = []; this.coords2Left = []; this.coords2Right = []; this.stackedBackfacesLeft = []; this.stackedBackfacesRight = []; this.originalColors = {}; this.gradientCounter = 1; this.sequentialIndex = 0; // Used for tooltips this.firstDraw = true; // After the first draw this will be false // // Convert strings to numbers // this.data[0] = RGraph.SVG.stringsToNumbers(this.data[0]); this.data[1] = RGraph.SVG.stringsToNumbers(this.data[1]); this.left = RGraph.SVG.stringsToNumbers(this.left); this.right = RGraph.SVG.stringsToNumbers(this.right); // // Make the .data_arr variable (All data in a linear array) // this.data_arr = []; for (var i=0; i<this.left.length; ++i) this.data_arr.push(this.left[i]); for (var i=0; i<this.right.length; ++i) this.data_arr.push(this.right[i]); // Add this object to the ObjectRegistry RGraph.SVG.OR.add(this); this.container.style.display = 'inline-block'; this.properties = { marginLeft: 35, marginRight: 35, marginTop: 35, marginBottom: 35, marginCenter: null, marginInner: 3, marginInnerGrouped: 2, backgroundColor: null, backgroundGrid: true, backgroundGridColor: '#ddd', backgroundGridLinewidth: 1, backgroundGridHlines: true, backgroundGridHlinesCount: null, backgroundGridVlines: true, backgroundGridVlinesCount: null, backgroundGridBorder: true, backgroundGridDashed: false, backgroundGridDotted: false, backgroundGridDashArray: null, xaxis: true, xaxisLinewidth: 1, xaxisTickmarks: true, xaxisTickmarksLength: 3, xaxisTickmarksCount: 5, xaxisLabelsCount: 5, xaxisColor: 'black', xaxisLabelsOffsetx: 0, xaxisLabelsOffsety: 0, xaxisLabelsFont: null, xaxisLabelsSize: null, xaxisLabelsBold: null, xaxisLabelsItalic: null, xaxisLabelsColor: null, xaxisScaleUnitsPre: '', xaxisScaleUnitsPost: '', xaxisScaleStrict: false, xaxisScaleDecimals: 0, xaxisScalePoint: '.', xaxisScaleThousand: ',', xaxisScaleRound: false, xaxisScaleMax: null, xaxisScaleMin: 0, xaxisScaleFormatter: null, yaxis: true, yaxisTickmarks: true, yaxisTickmarksLength: 3, yaxisTickmarksCount: null, yaxisColor: 'black', yaxisScale: false, yaxisLabels: null, yaxisLabelsOffsetx: 0, yaxisLabelsOffsety: 0, yaxisLabelsFont: null, yaxisLabelsSize: null, yaxisLabelsBold: null, yaxisLabelsItalic: null, yaxisLabelsColor: null, yaxisLabelsFormattedDecimals: 0, yaxisLabelsFormattedPoint: '.', yaxisLabelsFormattedThousand: ',', yaxisLabelsFormattedUnitsPre: '', yaxisLabelsFormattedUnitsPost: '', // 20 colors. If you need more you need to set the colors property colors: [ 'red', '#0f0', '#00f', '#ff0', '#0ff', '#0f0','pink','orange','gray','black', 'red', '#0f0', '#00f', '#ff0', '#0ff', '#0f0','pink','orange','gray','black' ], colorsSequential: false, colorsStroke: 'rgba(0,0,0,0)', colorsLeft: null, colorsRight: null, labelsAbove: false, labelsAboveFont: null, labelsAboveSize: null, labelsAboveBold: null, labelsAboveItalic: null, labelsAboveColor: null, labelsAboveBackground: null, labelsAboveBackgroundPadding: 0, labelsAboveUnitsPre: null, labelsAboveUnitsPost: null, labelsAbovePoint: null, labelsAboveThousand: null, labelsAboveFormatter: null, labelsAboveDecimals: null, labelsAboveOffsetx: 0, labelsAboveOffsety: 0, labelsAboveSpecific: null, textColor: 'black', textFont: 'Arial, Verdana, sans-serif', textSize: 12, textBold: false, textItalic: false, text: null, linewidth: 1, grouping: 'grouped', 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, highlightStroke: 'rgba(0,0,0,0)', highlightFill: 'rgba(255,255,255,0.7)', highlightLinewidth: 1, title: '', titleX: null, titleY: null, titleHalign: 'center', titleValign: null, titleSize: null, titleColor: null, titleFont: null, titleBold: null, titleItalic: null, titleSubtitle: null, titleSubtitleColor: '#aaa', titleSubtitleSize: null, titleSubtitleFont: null, titleSubtitleBold: null, titleSubtitleItalic: null, shadow: false, shadowOffsetx: 2, shadowOffsety: 2, shadowBlur: 2, shadowColor: 'rgb(0,0,0,0.25)', key: null, keyColors: null, keyOffsetx: 0, keyOffsety: 0, keyLabelsOffsetx: 0, keyLabelsOffsety: -1, keyLabelsSize: null, keyLabelsBold: null, keyLabelsItalic: null, keyLabelsFont: null, keyLabelsColor: 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'); if (properties.yaxisLabels && properties.yaxisLabels.length) { // // If the xaxisLabels option is a string then turn it // into an array. // if (typeof properties.yaxisLabels === 'string') { properties.yaxisLabels = RGraph.SVG.arrayPad({ array: [], length: this.left.length, value: properties.yaxisLabels }); } // // Label substitution // for (var i=0; i<properties.yaxisLabels.length; ++i) { properties.yaxisLabels[i] = RGraph.SVG.labelSubstitution({ object: this, text: properties.yaxisLabels[i], index: i, value: this.left[i], decimals: properties.yaxisLabelsFormattedDecimals || 0, unitsPre: properties.yaxisLabelsFormattedUnitsPre || '', unitsPost: properties.yaxisLabelsFormattedUnitsPost || '', thousand: properties.yaxisLabelsFormattedThousand || ',', point: properties.yaxisLabelsFormattedPoint || '.' }); } } // 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 RGraph.SVG.createDefs(this); // // Autosize the center gutter to allow for big labels // 16th September 2019 - took out the IF() condition so that the centermargin i // ALWAYS calculated properties.marginCenter = this.getMarginCenter(); //} // Reset the coords arrays this.coords = []; this.coordsLeft = []; this.coordsRight = []; this.coords2 = []; this.coords2Left = []; this.coords2Right = []; // (Re)set this to zero this.sequentialIndex = 0; this.graphWidth = (this.width - properties.marginLeft - properties.marginRight - properties.marginCenter) / 2; this.graphHeight = this.height - properties.marginTop - properties.marginBottom; // // Parse the colors. This allows for simple gradient syntax // // Parse the colors for gradients RGraph.SVG.resetColorsToOriginalValues({object:this}); this.parseColors(); // Go through the data and work out the maximum value var values = []; for (var i=0; i<2; ++i) { for (var j=0,max=0; j<this.data[i].length; ++j) { if (typeof this.data[i][j] === 'number') { values.push(this.data[i][j]); } else if (RGraph.SVG.isArray(this.data[i][j]) && properties.grouping === 'grouped') { values.push(RGraph.SVG.arrayMax(this.data[i][j])); } else if (RGraph.SVG.isArray(this.data[i][j]) && properties.grouping === 'stacked') { values.push(RGraph.SVG.arraySum(this.data[i][j])); } } } var max = RGraph.SVG.arrayMax(values); // A custom, user-specified maximum value if (typeof properties.xaxisScaleMax === 'number') { max = properties.xaxisScaleMax; } // // Generate an appropiate scale // this.scale = RGraph.SVG.getScale({ object: this, numlabels: properties.xaxisLabelsCount, unitsPre: properties.xaxisScaleUnitsPre, unitsPost: properties.xaxisScaleUnitsPost, max: max, min: properties.xaxisScaleMin, point: properties.xaxisScalePoint, round: properties.xaxisScaleRound, thousand: properties.xaxisScaleThousand, decimals: properties.xaxisScaleDecimals, strict: typeof properties.xaxisScaleMax === 'number', formatter: properties.xaxisScaleFormatter }); // Now the scale has been generated adopt its max value this.max = this.scale.max; this.min = this.scale.min; properties.yaxisScaleMax = this.scale.max; properties.yaxisScaleMin = this.scale.min; // 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'); } // Draw the background first this.drawBackground(this); // Draw the title this.drawTitle(); // Draw the bars this.drawBars(); // Draw the axes over the bars this.drawAxes(); // Draw the labels for both of the axes this.drawLabels() // Draw the labelsAbove labels this.drawLabelsAbove(); // Draw the key if (typeof properties.key !== null && RGraph.SVG.drawKey) { RGraph.SVG.drawKey(this); } else if (!RGraph.SVG.isNullish(properties.key)) { alert('The drawKey() function does not exist - have you forgotten to include the key library?'); } // // 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 // 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); }; // // Draws the background // this.drawBackground = function () { // For some reason this appears to be necessary var properties = this.properties; // Save the original margin properties var originalMarginRight = properties.marginRight, originalMarginLeft = properties.marginLeft; // Draw the LEFT background properties.marginRight = this.width - (properties.marginLeft + this.graphWidth); if (RGraph.SVG.isNullish(properties.backgroundGridHlinesCount)) { var resetToNull = true; properties.backgroundGridHlinesCount = this.left.length; } // Set the LEFT background image properties var props = ['','Aspect','Opacity','Stretch','X','Y','W','H',]; for (i in props) { if (typeof props[i] === 'string') { properties['backgroundImage' + String(props[i])] = properties['backgroundImageLeft' + props[i]]; } } RGraph.SVG.drawBackground(this); if (resetToNull) { properties.backgroundGridHlinesCount = null; } // Draw the RIGHT background properties.marginRight = originalMarginRight; properties.marginLeft = this.width - (properties.marginRight + this.graphWidth); if (RGraph.SVG.isNullish(properties.backgroundGridHlinesCount)) { properties.backgroundGridHlinesCount = this.right.length; } // Set the RIGHT background image properties var props = ['','Aspect','Opacity','Stretch','X','Y','W','H',]; for (i in props) { if (typeof props[i] === 'string') { properties['backgroundImage' + props[i]] = properties['backgroundImageRight' + props[i]]; } } // Draw the background RGraph.SVG.drawBackground(this); // Reset the margin properties to the original values properties.marginLeft = originalMarginLeft; properties.marginRight = originalMarginRight; }; // // Draws the axes // this.drawAxes = function (opt = {}) { // Draw the LEFT X axes if (properties.xaxis && opt.xaxis !== false) { RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.svgAllGroup, attr: { d: 'M {1} {2} L {3} {4}'.format( properties.marginLeft, this.height - properties.marginBottom, properties.marginLeft + this.graphWidth, this.height - properties.marginBottom ), 'stroke-width': properties.xaxisLinewidth, stroke: properties.xaxisColor, fill: 'rgba(0,0,0,0)', 'shape-rendering': 'crispEdges', 'stroke-linecap': 'square' } }); // Draw the right X axis var foo2 = RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.svgAllGroup, attr: { d: 'M {1} {2} L {3} {4}'.format( this.width - properties.marginRight, this.height - properties.marginBottom, this.width - properties.marginRight - this.graphWidth, this.height - properties.marginBottom ), 'stroke-width': properties.xaxisLinewidth, stroke: properties.xaxisColor, fill: 'rgba(0,0,0,0)', 'shape-rendering': 'crispEdges', 'stroke-linecap': 'square' } }); // // Draw tickmarks if necessary // if (properties.xaxisTickmarks && opt.tickmarks !== false) { var startY = this.height - properties.marginBottom, endY = this.height - properties.marginBottom + properties.xaxisTickmarksLength; // Draw the LEFT sides tickmarks for (var i=0; i<properties.xaxisTickmarksCount; ++i) { var x = properties.marginLeft + (i * (this.graphWidth / properties.xaxisTickmarksCount)); RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'path', attr: { d: 'M{1} {2} L{3} {4}'.format( x + 0.001, startY, x, endY ), stroke: properties.xaxisColor, 'stroke-width': properties.xaxisLinewidth, 'shape-rendering': "crispEdges" } }); } // Draw an extra LEFT tick if no Y axis is being shown if (!properties.yaxis) { var x = properties.marginLeft + this.graphWidth; RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'path', attr: { d: 'M{1} {2} L{3} {4}'.format( x + 0.001, startY, x, endY ), stroke: properties.xaxisColor, 'stroke-width': properties.xaxisLinewidth, 'shape-rendering': "crispEdges" } }); } // Draw the RIGHT sides tickmarks for (var i=0; i<properties.xaxisTickmarksCount; ++i) { var x = properties.marginLeft + properties.marginCenter + this.graphWidth + ((i+1) * (this.graphWidth / properties.xaxisTickmarksCount)); RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'path', attr: { d: 'M{1} {2} L{3} {4}'.format( x + 0.001, startY, x, endY ), stroke: properties.xaxisColor, 'stroke-width': properties.xaxisLinewidth, 'shape-rendering': "crispEdges" } }); } // Draw an extra RIGHT tick if no Y axis is being shown if (!properties.yaxis) { var x = properties.marginLeft + this.graphWidth + properties.marginCenter; RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'path', attr: { d: 'M{1} {2} L{3} {4}'.format( x + 0.001, startY, x, endY ), stroke: properties.xaxisColor, 'stroke-width': properties.xaxisLinewidth, 'shape-rendering': "crispEdges" } }); } } } // Draw the LEFT vertical axes if (properties.yaxis && opt.yaxis !== false) { RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.svgAllGroup, attr: { d: 'M {1} {2} L {3} {4}'.format( properties.marginLeft + this.graphWidth, this.height - properties.marginBottom, properties.marginLeft + this.graphWidth, properties.marginTop ), 'stroke-width': properties.yaxisLinewidth, stroke: properties.yaxisColor, fill: 'rgba(0,0,0,0)', 'shape-rendering': 'crispEdges', 'stroke-linecap': 'square' } }); // Draw the RIGHT vertical axis RGraph.SVG.create({ svg: this.svg, type: 'path', parent: this.svgAllGroup, attr: { d: 'M {1} {2} L {3} {4}'.format( properties.marginLeft + this.graphWidth + properties.marginCenter, this.height - properties.marginBottom, properties.marginLeft + this.graphWidth + properties.marginCenter, properties.marginTop ), 'stroke-width': properties.yaxisLinewidth, stroke: properties.yaxisColor, fill: 'rgba(0,0,0,0)', 'shape-rendering': 'crispEdges', 'stroke-linecap': 'square' } }); // // Draw Y axis tickmarks if necessary // if (properties.yaxisTickmarks) { var startX = properties.marginLeft + this.graphWidth, endX = properties.marginLeft + this.graphWidth + properties.yaxisTickmarksLength, numticks = RGraph.SVG.isNumber(properties.yaxisTickmarksCount) ? properties.yaxisTickmarksCount : this.left.length; // Draw the left sides tickmarks for (var i=0; i<numticks; ++i) { var y = properties.marginTop + (i * (this.graphHeight / numticks)); RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'path', attr: { d: 'M{1} {2} L{3} {4}'.format( startX + 0.001, y, endX, y ), stroke: properties.yaxisColor, 'stroke-width': properties.yaxisLinewidth, 'shape-rendering': "crispEdges" } }); } // Draw an extra LEFT tickmark if the X axis is not being shown if (!properties.xaxis) { var y = properties.marginTop + this.graphHeight; RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'path', attr: { d: 'M{1} {2} L{3} {4}'.format( startX + 0.001, y, endX, y ), stroke: properties.yaxisColor, 'stroke-width': properties.yaxisLinewidth, 'shape-rendering': "crispEdges" } }); } var startX = properties.marginLeft + this.graphWidth + properties.marginCenter, endX = properties.marginLeft + this.graphWidth + properties.marginCenter - properties.yaxisTickmarksLength, numticks = RGraph.SVG.isNumber(properties.yaxisTickmarksCount) ? properties.yaxisTickmarksCount : this.right.length; for (var i=0; i<numticks; ++i) { var y = properties.marginTop + (i * (this.graphHeight / numticks)); RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'path', attr: { d: 'M{1} {2} L{3} {4}'.format( startX + 0.001, y, endX, y ), stroke: properties.yaxisColor, 'stroke-width': properties.yaxisLinewidth, 'shape-rendering': "crispEdges" } }); } // Draw an extra RIGHT tickmark if the X axis is not being shown if (!properties.xaxis) { var y = properties.marginTop + this.graphHeight; RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'path', attr: { d: 'M{1} {2} L{3} {4}'.format( startX + 0.001, y, endX, y ), stroke: properties.yaxisColor, 'stroke-width': properties.yaxisLinewidth, 'shape-rendering': "crispEdges" } }); } } } }; // // Draws the labels // this.drawLabels = function () { // // Draw the Y axis labels // var numlabels = properties.yaxisLabels ? properties.yaxisLabels.length : 5 // Get the Y axis labels configuration var textConf = RGraph.SVG.getTextConf({ object: this, prefix: 'yaxisLabels' }); for (var i=0; i<numlabels; ++i) { var segment = this.graphHeight / numlabels, y = properties.marginTop + (segment * i) + (segment / 2) + properties.yaxisLabelsOffsety, x = properties.marginLeft + this.graphWidth + (properties.marginCenter / 2) + properties.yaxisLabelsOffsetx; var text = RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.yaxis', text: properties.yaxisLabels && properties.yaxisLabels[i] ? properties.yaxisLabels[i] : '', x: x, y: y, halign: 'center', valign: 'center', font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color }); } // // Add the minimum label for the LEFT side // var y = this.height - properties.marginBottom + 3 + properties.xaxisTickmarksLength, str = (typeof properties.xaxisScaleFormatter === 'function') ? properties.xaxisScaleFormatter(this, properties.xaxisScaleMin) : (properties.xaxisScaleUnitsPre + properties.xaxisScaleMin.toFixed(properties.xaxisScaleDecimals).replace(/\./, properties.xaxisScalePoint) + properties.xaxisScaleUnitsPost); // Get the Y axis labels configuration var textConf = RGraph.SVG.getTextConf({ object: this, prefix: 'xaxisLabels' }); var text = RGraph.SVG.text({ object: this, parent: this.svgAllGroup, text: str, x: properties.marginLeft + this.graphWidth + properties.xaxisLabelsOffsetx, y: y + properties.xaxisLabelsOffsety, halign: 'center', valign: 'top', tag: 'labels.xaxis', font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color }); // // Draw the X axis scale for the LEFT side // var segment = this.graphWidth / properties.xaxisLabelsCount; for (var i=0; i<this.scale.labels.length; ++i) { RGraph.SVG.text({ object: this, parent: this.svgAllGroup, text: this.scale.labels[i], x: properties.marginLeft + this.graphWidth - (segment * (i+1)) + properties.xaxisLabelsOffsetx, y: this.height - properties.marginBottom + 3 + properties.xaxisTickmarksLength + properties.xaxisLabelsOffsety, halign: 'center', valign: 'top', tag: 'labels.xaxis', font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color }); } // // Add the minimum label for the RIGHT side // var text = RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.xaxis', text: (typeof properties.xaxisScaleFormatter == 'function') ? properties.xaxisScaleFormatter(this, properties.xaxisScaleMin) : properties.xaxisScaleUnitsPre + properties.xaxisScaleMin.toFixed(properties.xaxisScaleDecimals).replace(/\./, properties.xaxisScalePoint) + properties.xaxisScaleUnitsPost, x: properties.marginLeft + this.graphWidth + properties.marginCenter + properties.xaxisLabelsOffsetx, y: this.height - properties.marginBottom + 3 + properties.xaxisTickmarksLength + properties.xaxisLabelsOffsety, halign: 'center', valign: 'top', font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color }); // // Draw the X axis scale for the RIGHT side // for (var i=0; i<this.scale.labels.length; ++i) { RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.xaxis', text: this.scale.labels[i], x: properties.marginLeft + this.graphWidth + properties.marginCenter + (segment * (i + 1)) + properties.xaxisLabelsOffsetx, y: this.height - properties.marginBottom + 3 + properties.xaxisTickmarksLength + properties.xaxisLabelsOffsety, halign: 'center', valign: 'top', font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color }); } }; // // Draws the bars // this.drawBars = function () { if (properties.shadow) { RGraph.SVG.setShadow({ object: this, offsetx: properties.shadowOffsetx, offsety: properties.shadowOffsety, blur: properties.shadowBlur, color: properties.shadowColor, id: 'dropShadow' }); } // Go thru the LEFT data and draw the bars for (var i=0; i<this.left.length; ++i) { // LEFT REGULAR NUMBER if (typeof this.left[i] === 'number') { var color = properties.colors[this.sequentialIndex], tooltip = RGraph.SVG.isNullish(properties.tooltips) ? null : properties.tooltips[this.sequentialIndex], y = properties.marginTop + ((this.graphHeight / this.left.length) * i) + properties.marginInner, width = this.getWidth(this.left[i]), x = properties.marginLeft + this.graphWidth - width, height = (this.graphHeight / this.left.length) - properties.marginInner - properties.marginInner; // // If the colorsLeft option is set then change // the colors option to that. // if (!RGraph.SVG.isNullish(properties.colorsLeft)) { // Save the initial colors value properties.colorsInitial = properties.colors; // Set the new value properties.colors = properties.colorsLeft; } var rect = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { x: x, y: y, width: width, height: height, fill: properties.colorsSequential ? properties.colors[this.sequentialIndex] : properties.colors[0], stroke: properties.colorsStroke, 'stroke-width': properties.linewidth, 'shape-rendering': 'crispEdges', 'data-original-x': x, 'data-original-y': y, 'data-original-width': width, 'data-original-height': height, 'data-tooltop': (tooltip || ''), 'data-index': i, 'data-sequential-index': this.sequentialIndex, 'data-value': this.left[i], filter: properties.shadow ? 'url(#dropShadow)' : '' } }); this.coords.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coordsLeft.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.installTooltipsEventListeners({ rect: rect, index: i, sequentialIndex: this.sequentialIndex }); this.sequentialIndex++; // // If the colorsLeft option is set then change // the colors option back to what it was. // if (!RGraph.SVG.isNullish(properties.colorsLeft)) { properties.colors = properties.colorsInitial; } // LEFT STACKED } else if (RGraph.SVG.isArray(this.left[i]) && properties.grouping === 'stacked') { var accWidth = 0; for (var j=0; j<this.left[i].length; ++j) { var color = properties.colors[this.sequentialIndex], tooltip = RGraph.SVG.isNullish(properties.tooltips) ? null : properties.tooltips[this.sequentialIndex], y = properties.marginTop + ((this.graphHeight / this.left.length) * i) + properties.marginInner, width = this.getWidth(this.left[i][j]), accWidth = accWidth + width, x = properties.marginLeft + this.graphWidth - accWidth, height = (this.graphHeight / this.left.length) - properties.marginInner - properties.marginInner; // // If the colorsLeft option is set then change // the colors option to that. // if (!RGraph.SVG.isNullish(properties.colorsLeft)) { // Save the initial colors value properties.colorsInitial = properties.colors; // Set the new value properties.colors = properties.colorsLeft; } // If this is the first iteration of the loop and a shadow // is requested draw a rect here to create it. if (j === 0 && properties.shadow) { var shadowBackfaceX = properties.marginLeft + this.graphWidth - (this.getWidth(RGraph.SVG.arraySum(this.left[i]))), shadowBackfaceWidth = this.getWidth(RGraph.SVG.arraySum(this.left[i])); var rect = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { fill: '#eee', x: shadowBackfaceX, y: y, width: shadowBackfaceWidth, height: height, 'stroke-width': 0, 'data-index': i, filter: 'url(#dropShadow)' } }); this.stackedBackfacesLeft[i] = rect; } var rect = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { x: x, y: y, width: width, height: height, fill: properties.colorsSequential ? properties.colors[this.sequentialIndex] : properties.colors[j], stroke: properties.colorsStroke, 'stroke-width': properties.linewidth, 'shape-rendering': 'crispEdges', 'data-original-x': x, 'data-original-y': y, 'data-original-width': width, 'data-original-height': height, 'data-tooltop': (tooltip || ''), 'data-index': i, 'data-subindex': j, 'data-sequential-index': this.sequentialIndex, 'data-value': this.left[i][j] } }); this.coords.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coordsLeft.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); if (!this.coords2[i]) { this.coords2[i] = []; } if (!this.coords2Left[i]) { this.coords2Left[i] = []; } this.coords2[i].push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coords2Left[i].push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.installTooltipsEventListeners({ rect: rect, index: i, sequentialIndex: this.sequentialIndex }); // // If the colorsLeft option is set then change // the colors option back to what it was. // if (!RGraph.SVG.isNullish(properties.colorsLeft)) { properties.colors = properties.colorsInitial; } this.sequentialIndex++; } // LEFT GROUPED } else if (RGraph.SVG.isArray(this.left[i]) && properties.grouping === 'grouped') { for (var j=0; j<this.left[i].length; ++j) { var color = properties.colors[this.sequentialIndex], tooltip = RGraph.SVG.isNullish(properties.tooltips) ? null : properties.tooltips[this.sequentialIndex], height = ((this.graphHeight / this.left.length) - properties.marginInner - properties.marginInner - (properties.marginInnerGrouped * (this.left[i].length - 1))) / this.left[i].length, y = properties.marginTop + ((this.graphHeight / this.left.length) * i) + properties.marginInner + (height * j) + (j * properties.marginInnerGrouped), width = this.getWidth(this.left[i][j]), x = properties.marginLeft + this.graphWidth - width; // // If the colorsLeft option is set then change // the colors option to that. // if (!RGraph.SVG.isNullish(properties.colorsLeft)) { // Save the initial colors value properties.colorsInitial = properties.colors; // Set the new value properties.colors = properties.colorsLeft; } var rect = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { x: x, y: y, width: width, height: height, fill: properties.colorsSequential ? properties.colors[this.sequentialIndex] : properties.colors[j], stroke: properties.colorsStroke, 'stroke-width': properties.linewidth, 'shape-rendering': 'crispEdges', 'data-original-x': x, 'data-original-y': y, 'data-original-width': width, 'data-original-height': height, 'data-tooltop': (tooltip || ''), 'data-index': i, 'data-subindex': j, 'data-sequential-index': this.sequentialIndex, 'data-value': this.left[i][j], filter: properties.shadow ? 'url(#dropShadow)' : '' } }); this.coords.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coordsLeft.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); if (!this.coords2[i]) { this.coords2[i] = []; } if (!this.coords2Left[i]) { this.coords2Left[i] = []; } this.coords2[i].push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coords2Left[i].push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.installTooltipsEventListeners({ rect: rect, index: i, sequentialIndex: this.sequentialIndex }); // // If the colorsLeft option is set then change // the colors option back to what it was. // if (!RGraph.SVG.isNullish(properties.colorsLeft)) { properties.colors = properties.colorsInitial; } this.sequentialIndex++; } } } // Go thru the RIGHT data and draw the bars for (var i=0; i<this.right.length; ++i) { // RIGHT REGULAR if (typeof this.right[i] === 'number') { var color = properties.colors[this.sequentialIndex], tooltip = RGraph.SVG.isNullish(properties.tooltips) ? null : properties.tooltips[this.sequentialIndex], y = properties.marginTop + ((this.graphHeight / this.right.length) * i) + properties.marginInner, width = this.getWidth(this.right[i]), x = properties.marginLeft + this.graphWidth + properties.marginCenter, height = (this.graphHeight / this.right.length) - properties.marginInner - properties.marginInner; // // If the colorsRight option is set then change // the colors option to that. // if (!RGraph.SVG.isNullish(properties.colorsRight)) { // Save the initial colors value properties.colorsInitial = properties.colors; // Set the new value properties.colors = properties.colorsRight; } var rect = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { x: x, y: y, width: width, height: height, fill: properties.colorsSequential ? properties.colors[this.sequentialIndex] : properties.colors[0], stroke: properties.colorsStroke, 'stroke-width': properties.linewidth, 'shape-rendering': 'crispEdges', 'data-original-x': x, 'data-original-y': y, 'data-original-width': width, 'data-original-height': height, 'data-tooltop': (tooltip || ''), 'data-index': i, 'data-sequential-index': this.sequentialIndex, 'data-value': this.right[i], filter: properties.shadow ? 'url(#dropShadow)' : '' } }); this.coords.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coordsRight.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.installTooltipsEventListeners({ rect: rect, index: i, sequentialIndex: this.sequentialIndex }); // // If the colorsRight option is set then change // the colors option back to what it was. // if (!RGraph.SVG.isNullish(properties.colorsRight)) { properties.colors = properties.colorsInitial; } this.sequentialIndex++; // RIGHT STACKED } else if (RGraph.SVG.isArray(this.right[i]) && properties.grouping === 'stacked') { var accWidth = 0; for (var j=0; j<this.right[i].length; ++j) { var color = properties.colors[this.sequentialIndex], tooltip = RGraph.SVG.isNullish(properties.tooltips) ? null : properties.tooltips[this.sequentialIndex], y = properties.marginTop + ((this.graphHeight / this.right.length) * i) + properties.marginInner, width = this.getWidth(this.right[i][j]), x = properties.marginLeft + this.graphWidth + properties.marginCenter + accWidth, accWidth = accWidth + width, height = (this.graphHeight / this.left.length) - properties.marginInner - properties.marginInner; // // If the colorsRight option is set then change // the colors option to that. // if (!RGraph.SVG.isNullish(properties.colorsRight)) { // Save the initial colors value properties.colorsInitial = properties.colors; // Set the new value properties.colors = properties.colorsRight; } // If this is the first iteration of the loop and a shadow // is requested draw a rect here to create it. if (j === 0 && properties.shadow) { var shadowBackfaceX = properties.marginLeft + this.graphWidth + properties.marginCenter, shadowBackfaceWidth = this.getWidth(RGraph.SVG.arraySum(this.right[i])); var rect = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { fill: '#eee', x: shadowBackfaceX, y: y, width: shadowBackfaceWidth, height: height, 'stroke-width': 0, 'data-index': i, filter: 'url(#dropShadow)' } }); this.stackedBackfacesRight[i] = rect; } var rect = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { x: x, y: y, width: width, height: height, fill: properties.colorsSequential ? properties.colors[this.sequentialIndex] : properties.colors[j], stroke: properties.colorsStroke, 'stroke-width': properties.linewidth, 'shape-rendering': 'crispEdges', 'data-original-x': x, 'data-original-y': y, 'data-original-width': width, 'data-original-height': height, 'data-tooltop': (tooltip || ''), 'data-index': i, 'data-subindex': j, 'data-sequential-index': this.sequentialIndex, 'data-value': this.right[i][j] } }); this.coords.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coordsRight.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); if (!this.coords2[i + this.left.length]) { this.coords2[i + this.left.length] = []; } if (!this.coords2Right[i]) { this.coords2Right[i] = []; } this.coords2[i + this.left.length].push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coords2Right[i].push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.installTooltipsEventListeners({ rect: rect, index: i, sequentialIndex: this.sequentialIndex }); // // If the colorsRight option is set then change // the colors option back to what it was. // if (!RGraph.SVG.isNullish(properties.colorsRight)) { properties.colors = properties.colorsInitial; } this.sequentialIndex++; } // RIGHT GROUPED } else if (RGraph.SVG.isArray(this.right[i]) && properties.grouping === 'grouped') { for (var j=0; j<this.right[i].length; ++j) { var color = properties.colors[this.sequentialIndex], tooltip = RGraph.SVG.isNullish(properties.tooltips) ? null : properties.tooltips[this.sequentialIndex], height = ((this.graphHeight / this.right.length) - properties.marginInner - properties.marginInner - (properties.marginInnerGrouped * (this.right[i].length - 1))) / this.right[i].length, y = properties.marginTop + ((this.graphHeight / this.right.length) * i) + properties.marginInner + (height * j) + (j * properties.marginInnerGrouped), width = this.getWidth(this.right[i][j]), x = properties.marginLeft + this.graphWidth + properties.marginCenter; // // If the colorsRight option is set then change // the colors option to that. // if (!RGraph.SVG.isNullish(properties.colorsRight)) { // Save the initial colors value properties.colorsInitial = properties.colors; // Set the new value properties.colors = properties.colorsRight; } var rect = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { x: x, y: y, width: width, height: height, fill: properties.colorsSequential ? properties.colors[this.sequentialIndex] : properties.colors[j], stroke: properties.colorsStroke, 'stroke-width': properties.linewidth, 'shape-rendering': 'crispEdges', 'data-original-x': x, 'data-original-y': y, 'data-original-width': width, 'data-original-height': height, 'data-tooltop': (tooltip || ''), 'data-index': i, 'data-subindex': j, 'data-sequential-index': this.sequentialIndex, 'data-value': this.right[i][j], filter: properties.shadow ? 'url(#dropShadow)' : '' } }); this.coords.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coordsRight.push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); if (!this.coords2[i + this.left.length]) { this.coords2[i + this.left.length] = []; } if (!this.coords2Right[i]) { this.coords2Right[i] = []; } this.coords2[i + this.left.length].push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.coords2Right[i].push({ object: this, element: rect, x: parseFloat(rect.getAttribute('x')), y: parseFloat(rect.getAttribute('y')), width: parseFloat(rect.getAttribute('width')), height: parseFloat(rect.getAttribute('height')) }); this.installTooltipsEventListeners({ rect: rect, index: i, sequentialIndex: this.sequentialIndex }); // // If the colorsRight option is set then change // the colors option back to what it was. // if (!RGraph.SVG.isNullish(properties.colorsRight)) { properties.colors = properties.colorsInitial; } this.sequentialIndex++; } } } }; // // Installs the tooltips event lissteners. This is called from the // above function. // // @param object opt The various arguments to the function // this.installTooltipsEventListeners = function (opt) { var obj = this; // Add the tooltip events if (!RGraph.SVG.isNullish(properties.tooltips) && (properties.tooltips[this.sequentialIndex] || typeof properties.tooltips === 'string') ) { // // Add tooltip event listeners // (function (idx, seq) { opt.rect.addEventListener(properties.tooltipsEvent.replace(/^on/, ''), function (e) { obj.removeHighlight(); // Show the tooltip RGraph.SVG.tooltip({ object: obj, index: idx, group: null, sequentialIndex: seq, text: typeof properties.tooltips === 'string' ? properties.tooltips : properties.tooltips[seq], event: e }); // Highlight the rect that has been clicked on obj.highlight(e.target); }, false); opt.rect.addEventListener('mousemove', function (e) { e.target.style.cursor = 'pointer' }, false); })(opt.index, opt.sequentialIndex); } }; // // // // @param int value The value to get the width for. // this.getWidth = function (value) { var x1 = this.getLeftXCoord(0), x2 = this.getLeftXCoord(value); if (RGraph.SVG.isNullish(x1) || RGraph.SVG.isNullish(x2)) { return null; } return x1 - x2; }; // // This function is similar to the above but instead // of a width it gets a relevant coord for a value // on the LEFT side // // @param int value The value to get the coordinate for. // this.getLeftXCoord = function (value) { var width; if (value > this.scale.max) { return null; } if (value < this.scale.min) { return null; } width = ((value - this.scale.min) / (this.scale.max - this.scale.min)); width *= this.graphWidth; // Calculate the X coord var x = properties.marginLeft + this.graphWidth - width; return x; }; // // This function gets an X coordinate for the RIGHT // side. // // @param int value The value to get the coordinate for. // this.getRightXCoord = function (value) { var width; if (value > this.scale.max) { return null; } if (value < this.scale.min) { return null; } width = ((value - this.scale.min) / (this.scale.max - this.scale.min)); width *= this.graphWidth; // Calculate the X coord var x = properties.marginLeft + this.graphWidth + properties.marginCenter + width; return x; }; // // This function can be used to highlight a bar on the chart // // @param object rect The rectangle to highlight // this.highlight = function (rect) { var x = parseFloat(rect.getAttribute('x')) - 0.5, y = parseFloat(rect.getAttribute('y')) - 0.5, width = parseFloat(rect.getAttribute('width')) + 1, height = parseFloat(rect.getAttribute('height')) + 1; var highlight = RGraph.SVG.create({ svg: this.svg, parent: this.svgAllGroup, type: 'rect', attr: { 'stroke-width': properties.highlightLinewidth, stroke: properties.highlightStroke, fill: properties.highlightFill, x: x, y: y, width: width, height: height }, style: { pointerEvents: 'none' } }); // Redraw the Y axis so the highlight doesn't go // over the axis this.drawAxes({ xaxis: false, tickmarks: false, yaxis: true }); // Store the highlight rect in the rebistry so // it can be cleared later RGraph.SVG.REG.set('highlight', highlight); }; // // This allows for easy specification of gradients // this.parseColors = function () { // Save the original colors so that they can be restored when // the canvas is cleared if (!Object.keys(this.originalColors).length) { this.originalColors = { colors: RGraph.SVG.arrayClone(properties.colors, true), backgroundGridColor: RGraph.SVG.arrayClone(properties.backgroundGridColor, true), highlightFill: RGraph.SVG.arrayClone(properties.highlightFill, true), backgroundColor: RGraph.SVG.arrayClone(properties.backgroundColor, 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], direction: 'horizontal' }); } } properties.backgroundGridColor = RGraph.SVG.parseColorLinear({object: this, color: properties.backgroundGridColor,direction:'horizontal'}); properties.highlightFill = RGraph.SVG.parseColorLinear({object: this, color: properties.highlightFill,direction:'horizontal'}); properties.backgroundColor = RGraph.SVG.parseColorLinear({object: this, color: properties.backgroundColor,direction:'horizontal'}); }; // // Draws the labelsAbove // this.drawLabelsAbove = function () { // Go through the above labels if (properties.labelsAbove) { //var data_seq = RGraph.SVG.arrayLinearize(this.data), // seq = 0, // stacked_total = 0; // Get the text configuration var textConf = RGraph.SVG.getTextConf({ object: this, prefix: 'labelsAbove' }); for (var dataset=0,seq=0; dataset<this.data.length; ++dataset,++seq) { for (var i=0; i<this.data[dataset].length; ++i,++seq) { var value = this.data[dataset][i], halign = dataset === 0 ? 'right' : 'left', valign = 'center', hoffset = dataset === 0 ? -10 : 10; // REGULAR CHART if (typeof value === 'number') { if (this.coords[seq]) { var x = parseInt(this.coords[seq].element.getAttribute('x')) + hoffset + properties.labelsAboveOffsetx; var height = parseInt(this.coords[seq].element.getAttribute('height')); var y = parseInt(this.coords[seq].element.getAttribute('y')) + (height / 2) + properties.labelsAboveOffsety; var width = parseInt(this.coords[seq].element.getAttribute('width')); // If the dataset is the RHS (which would equal ) // then set the alignment appropriately if (dataset === 1) { x += width; } var str = RGraph.SVG.numberFormat({ object: this, num: value.toFixed(properties.labelsAboveDecimals), prepend: typeof properties.labelsAboveUnitsPre === 'string' ? properties.labelsAboveUnitsPre : null, append: typeof properties.labelsAboveUnitsPost === 'string' ? properties.labelsAboveUnitsPost : null, point: typeof properties.labelsAbovePoint === 'string' ? properties.labelsAbovePoint : null, thousand: typeof properties.labelsAboveThousand === 'string' ? properties.labelsAboveThousand : null, formatter: typeof properties.labelsAboveFormatter === 'function' ? properties.labelsAboveFormatter : null }); // Facilitate labelsAboveSpecific if (properties.labelsAboveSpecific && properties.labelsAboveSpecific.length && (typeof properties.labelsAboveSpecific[seq] === 'string' || typeof properties.labelsAboveSpecific[seq] === 'number') ) { str = String(properties.labelsAboveSpecific[seq]); } else if ( properties.labelsAboveSpecific && properties.labelsAboveSpecific.length && typeof properties.labelsAboveSpecific[seq] !== 'string' && typeof properties.labelsAboveSpecific[seq] !== 'number') { continue; } // Add the text to the SVG RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.above', text: str, x: x, y: y, halign: halign, valign: valign, font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color, background: properties.labelsAboveBackground, padding: properties.labelsAboveBackgroundPadding }); } // STACKED CHART } else if (!RGraph.SVG.isNullish(value) && typeof value === 'object' && properties.grouping === 'stacked') { for (var k=0,sum=0,width=0; k<this.coords2[seq].length; ++k) { sum += parseFloat(this.coords2[seq][k].element.getAttribute('data-value')); } var len =this.coords2[seq].length; if (dataset === 0) { var x = parseFloat(this.coords2[seq][len - 1].x) + hoffset, height = parseFloat(this.coords2[seq][len - 1].height), y = parseFloat(this.coords2[seq][0].y) + (height / 2); } else { var x = parseFloat(this.coords2[this.data[0].length + i][0].x) + hoffset + properties.labelsAboveOffsetx, height = parseFloat(this.coords2[seq][len - 1].height), y = parseFloat(this.coords2[seq][0].y) + (height / 2) + properties.labelsAboveOffsety; // Work out the total width by summing all the individual widths for (var j=0; j<this.coords2Right[i].length; ++j) { x += this.coords2Right[i][j].width; } } var str = RGraph.SVG.numberFormat({ object: this, num: sum.toFixed(properties.labelsAboveDecimals), prepend: typeof properties.labelsAboveUnitsPre === 'string' ? properties.labelsAboveUnitsPre : null, append: typeof properties.labelsAboveUnitsPost === 'string' ? properties.labelsAboveUnitsPost : null, point: typeof properties.labelsAbovePoint === 'string' ? properties.labelsAbovePoint : null, thousand: typeof properties.labelsAboveThousand === 'string' ? properties.labelsAboveThousand : null, formatter: typeof properties.labelsAboveFormatter === 'function' ? properties.labelsAboveFormatter : null }); // Facilitate labelsAboveSpecific if (properties.labelsAboveSpecific && properties.labelsAboveSpecific.length && (typeof properties.labelsAboveSpecific[seq] === 'string' || typeof properties.labelsAboveSpecific[seq] === 'number') ) { str = String(properties.labelsAboveSpecific[seq]); } else if ( properties.labelsAboveSpecific && properties.labelsAboveSpecific.length && typeof properties.labelsAboveSpecific[seq] !== 'string' && typeof properties.labelsAboveSpecific[seq] !== 'number') { continue; } // Add the text to the SVG RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.above', text: str, x: x, y: y, halign: halign, valign: valign, font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color, background: properties.labelsAboveBackground, padding: properties.labelsAboveBackgroundPadding }); // GROUPED CHART } else if (!RGraph.SVG.isNullish(value) && typeof value === 'object' && properties.grouping === 'grouped') { for (var k=0; k<value.length; ++k) { val = value[k]; if (typeof val !== 'number') { val = ''; } var x = parseInt(this.coords[seq].element.getAttribute('x')) + hoffset + properties.labelsAboveOffsetx, height = parseInt(this.coords[seq].element.getAttribute('height')), y = parseInt(this.coords[seq].element.getAttribute('y')) + (height / 2) + properties.labelsAboveOffsety, width = parseInt(this.coords[seq].element.getAttribute('width')); // If the dataset is the RHS (which would equal ) // then set the alignment appropriately if (dataset === 1) { x += width; } var str = RGraph.SVG.numberFormat({ object: this, num: parseFloat(val).toFixed(properties.labelsAboveDecimals), prepend: typeof properties.labelsAboveUnitsPre === 'string' ? properties.labelsAboveUnitsPre : null, append: typeof properties.labelsAboveUnitsPost === 'string' ? properties.labelsAboveUnitsPost : null, point: typeof properties.labelsAbovePoint === 'string' ? properties.labelsAbovePoint : null, thousand: typeof properties.labelsAboveThousand === 'string' ? properties.labelsAboveThousand : null, formatter: typeof properties.labelsAboveFormatter === 'function' ? properties.labelsAboveFormatter : null }); if (val === 0 || RGraph.SVG.isNullish(val) || val === '') { str = ''; } // Facilitate labelsAboveSpecific if (properties.labelsAboveSpecific && properties.labelsAboveSpecific.length && (typeof properties.labelsAboveSpecific[seq] === 'string' || typeof properties.labelsAboveSpecific[seq] === 'number') ) { str = String(properties.labelsAboveSpecific[seq]); } else if ( properties.labelsAboveSpecific && properties.labelsAboveSpecific.length && typeof properties.labelsAboveSpecific[seq] !== 'string' && typeof properties.labelsAboveSpecific[seq] !== 'number') { continue; } // Add the text to the SVG RGraph.SVG.text({ object: this, parent: this.svgAllGroup, tag: 'labels.above', text: str, x: x, y: y, halign: halign, valign: valign, font: textConf.font, size: textConf.size, bold: textConf.bold, italic: textConf.italic, color: textConf.color, background: properties.labelsAboveBackground, padding: properties.labelsAboveBackgroundPadding }); seq++; } seq--; } else if (RGraph.SVG.isNullish(value)) { --seq; } } --seq; } } }; // // 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 () { //var highlight = RGraph.SVG.REG.get('highlight'); //if (highlight && highlight.parentNode) { // highlight.parentNode.removeChild(highlight); //} //RGraph.SVG.REG.set('highlight', null); RGraph.SVG.removeHighlight(); }; // // Calulate the center gutter size // this.getMarginCenter = this.getGutterCenter = function () { var bold = typeof properties.yaxisLabelsBold === 'boolean' ? properties.yaxisLabelsBold : properties.textBold, font = typeof properties.yaxisLabelsFont === 'string' ? properties.yaxisLabelsFont : properties.textFont, size = typeof properties.yaxisLabelsSize === 'number' ? properties.yaxisLabelsSize : properties.textSize, width = 0; // Loop through the labels measuring them if (properties.yaxisLabels) { for (var i=0,len=properties.yaxisLabels.length; i<len; ++i) { width = Math.max(width, RGraph.SVG.measureText({ text: properties.yaxisLabels[i], bold: bold, font: font, size: size })[0]); } } else { var width = 50; } return width + 15; }; // // Draw the title // this.drawTitle = function () { // Taken out on 16th Sep 2019 to accommodate responsive()ness. Setting the titleX // property was causing the smaller charts to rtain the larger charts titleX //position //if (RGraph.SVG.isNullish(properties.titleX)) { // properties.titleX = ((this.width - properties.marginLeft - properties.marginRight) / 2) + properties.marginLeft; //} RGraph.SVG.drawTitle(this); }; // // The Bar chart grow effect // this.grow = function () { var opt = arguments[0] || {}, frames = opt.frames || 30, frame = 0, obj = this, left = RGraph.SVG.arrayClone(this.left, true), right = RGraph.SVG.arrayClone(this.right, true), seq = 0; this.draw(); var iterate = function () { // LOOP THROUGH THE LEFT DATA for (var i=0,seq=0,len=obj.coordsLeft.length; i<len; ++i, ++seq) { var multiplier = (frame / frames); // The main loop through the data // LEFT REGULAR if (typeof left[i] === 'number') { var width = Math.abs(obj.getLeftXCoord(left[i]) - obj.getLeftXCoord(0)); left[i] = obj.left[i] * multiplier; // Set the new height on the rect obj.coords[seq].element.setAttribute( 'width', width ); // Set the correct Y coord on the object obj.coords[seq].element.setAttribute( 'x', obj.getLeftXCoord(0) - width ); // LEFT STACKED } else if (typeof left[i] === 'object' && properties.grouping === 'stacked') { var accumulativeWidth = 0; for (var j=0,len2=left[i].length; j<len2; ++j, ++seq) { var width = Math.abs(obj.getLeftXCoord(left[i][j]) - obj.getLeftXCoord(0)); left[i][j] = obj.left[i][j] * multiplier; obj.coords[seq].element.setAttribute( 'width', width ); obj.coords[seq].element.setAttribute( 'x', obj.getLeftXCoord(0) - width - accumulativeWidth ); accumulativeWidth += (properties.grouping === 'stacked' ? width : 0); } // // Set the width and X coord of the backfaces // if (obj.stackedBackfacesLeft[i]) { obj.stackedBackfacesLeft[i].setAttribute( 'width', accumulativeWidth ); obj.stackedBackfacesLeft[i].setAttribute( 'x', obj.getLeftXCoord(0) - accumulativeWidth ); } // Decrease seq by one so that it's not incremented twice --seq; // LEFT GROUPED } else if (typeof left[i] === 'object' && properties.grouping === 'grouped') { // Loop thru the group for (var j=0,len2=left[i].length; j<len2; ++j, ++seq) { var width = obj.getLeftXCoord(0) - obj.getLeftXCoord(left[i][j]); left[i][j] = obj.left[i][j] * multiplier; obj.coords[seq].element.setAttribute( 'width', width ); obj.coords[seq].element.setAttribute( 'x', obj.getLeftXCoord(0) - width ); } // Decrease seq by one so that it's not incremented twice --seq; } } // LOOP THROUGH THE RIGHT DATA for (var i=0,seq=0,len=obj.coordsRight.length; i<len; ++i, ++seq) { var multiplier = (frame / frames) // RGraph.SVG.FX.getEasingMultiplier(frames, frame) // RGraph.SVG.FX.getEasingMultiplier(frames, frame); // The main loop through the data // RIGHT REGULAR if (typeof right[i] === 'number') { width = Math.abs(obj.getRightXCoord(right[i]) - obj.getRightXCoord(0)); right[i] = obj.right[i] * multiplier; // Set the new height on the rect obj.coordsRight[i].element.setAttribute( 'width', width ); // Set the correct Y coord on the object obj.coordsRight[seq].element.setAttribute( 'x', obj.getRightXCoord(0) ); // RIGHT STACKED } else if (typeof right[i] === 'object' && properties.grouping === 'stacked') { var accumulativeWidth = 0; for (var j=0,len2=right[i].length; j<len2; ++j, ++seq) { width = Math.abs(obj.getRightXCoord(right[i][j]) - obj.getRightXCoord(0)); right[i][j] = obj.right[i][j] * multiplier; obj.coordsRight[seq].element.setAttribute( 'width', width ); obj.coordsRight[seq].element.setAttribute( 'x', obj.getRightXCoord(0) + accumulativeWidth ); accumulativeWidth += width; } // // Set the width and X coord of the backfaces // if (obj.stackedBackfacesRight[i]) { obj.stackedBackfacesRight[i].setAttribute( 'width', accumulativeWidth ); obj.stackedBackfacesRight[i].setAttribute( 'x', obj.getRightXCoord(0) ); } // Decrease seq by one so that it's not incremented twice --seq; // RIGHT GROUPED } else if (typeof right[i] === 'object' && properties.grouping === 'grouped') { // Loop thru the group for (var j=0,len2=right[i].length; j<len2; ++j, ++seq) { width = Math.abs(obj.getRightXCoord(right[i][j]) - obj.getRightXCoord(0)); right[i][j] = obj.right[i][j] * multiplier; obj.coordsRight[seq].element.setAttribute( 'width', width ); obj.coordsRight[seq].element.setAttribute( 'x', obj.getRightXCoord(0) ); } // Decrease seq by one so that it's not incremented twice --seq; } } if (frame++ <= frames) { RGraph.SVG.FX.update(iterate); } else if (opt.callback) { (opt.callback)(obj); } }; iterate(); return this; }; // // Bipolar chart Wave effect. // // @param object OPTIONAL An object map of options. You specify 'frames' // here to give the number of frames in the effect // and also callback to specify a callback function // thats called at the end of the effect // this.wave = function () { var obj = this, opt = arguments[0] || {}, frames = opt.frames || 120, startFrames_left = [], startFrames_right = [], counters_left = [], counters_right = []; var framesperbar = frames / 3, frame_left = -1, frame_right = -1, callback = arguments[1] || function () {}, original_left = RGraph.SVG.arrayClone(this.left, true), original_right = RGraph.SVG.arrayClone(this.right, true); for (var i=0,len=this.left.length,seq=0; i<len; i+=1,++seq) { startFrames_left[seq] = ((frames / 3) / (RGraph.SVG.arrayLinearize(this.left).length - 1)) * i; counters_left[seq] = 0; if (RGraph.SVG.isArray(this.left[i])) { for (var j=0; j<this.left[i].length; ++j,seq++) { startFrames_left[seq] = ((frames / 3) / (RGraph.SVG.arrayLinearize(this.left).length - 1)) * seq; counters_left[seq] = 0; } --seq; } } for (var i=0,len=this.right.length,seq=0; i<len; i+=1,++seq) { startFrames_right[seq] = ((frames / 3) / (RGraph.SVG.arrayLinearize(this.right).length - 1)) * i; counters_right[seq] = 0; if (RGraph.SVG.isArray(this.right[i])) { for (var j=0; j<this.right[i].length; ++j,seq++) { startFrames_right[seq] = ((frames / 3) / (RGraph.SVG.arrayLinearize(this.right).length - 1)) * seq; counters_right[seq] = 0; } --seq; } } // This stops the chart from jumping this.draw(); // Zero all of the data for the left side for (var i=0,len=this.left.length; i<len; i+=1) { if (typeof this.left[i] === 'number') { this.left[i] = 0; } else if (typeof this.left[i] === 'object' && !RGraph.SVG.isNullish(this.left[i])) { for (var j=0; j<this.left[i].length; ++j) { this.left[i][j] = 0; } } } // Zero all of the bar-lengths for the left side for (var i=0; i<this.coordsLeft.length; i+=1) { this.coordsLeft[i].element.setAttribute('width', 0); if (this.stackedBackfacesLeft[i]) { this.stackedBackfacesLeft[i].setAttribute('width', 0); } } // Zero all of the data for the right side for (var i=0; i<this.right.length; i+=1) { if (RGraph.SVG.isNumber(this.right[i])) { this.right[i] = 0; } else if (RGraph.SVG.isArray(this.right[i])) { for (var j=0; j<this.right[i].length; ++j) { this.right[i][j] = 0; } } } // Zero all of the bar-lengths for the right side for (var i=0; i<this.coordsRight.length; i+=1) { this.coordsRight[i].element.setAttribute('width', 0); if (this.stackedBackfacesRight[i]) { this.stackedBackfacesRight[i].setAttribute('width', 0); } } // // Iterate over the left side // function iteratorLeft () { ++frame_left; for (var i=0,len=obj.left.length,seq=0; i<len; i+=1,seq+=1) { if (frame_left >= startFrames_left[seq]) { var isNull = RGraph.SVG.isNullish(obj.left[i]); // Regular bars if (typeof obj.left[i] === 'number') { obj.left[i] = Math.min( Math.abs(original_left[i]), Math.abs(original_left[i] * ( (counters_left[i]++) / framesperbar)) ); var rect_left = obj.coords[seq].element; rect_left.setAttribute( 'width', parseFloat(rect_left.getAttribute('data-original-width')) * (obj.left[i] / rect_left.getAttribute('data-value')) ); rect_left.setAttribute( 'x', obj.properties.marginLeft + obj.graphWidth - (parseFloat(rect_left.getAttribute('data-original-width')) * (obj.left[i] / rect_left.getAttribute('data-value'))) ); // Stacked or grouped bars } else if (RGraph.SVG.isArray(obj.left[i])) { for (var j=0,accWidth=0; j<obj.left[i].length; ++j,++seq) { obj.left[i][j] = Math.min( Math.abs(original_left[i][j]), Math.abs(original_left[i][j] * ( (counters_left[seq]++) / framesperbar)) ); var rect_left = obj.coords[seq].element; rect_left.setAttribute( 'width', parseFloat(rect_left.getAttribute('data-original-width')) * (obj.left[i][j] / rect_left.getAttribute('data-value')) ); rect_left.setAttribute( 'x', obj.properties.marginLeft + obj.graphWidth - (parseFloat(rect_left.getAttribute('data-original-width')) * (obj.left[i][j] / rect_left.getAttribute('data-value'))) - accWidth ); // Only update this for stacked charts if (obj.properties.grouping === 'stacked') { accWidth += parseFloat(rect_left.getAttribute('width')); obj.stackedBackfacesLeft[i].setAttribute('x', obj.properties.marginLeft + obj.graphWidth - accWidth); obj.stackedBackfacesLeft[i].setAttribute('width', accWidth); } } seq--; } if (isNull) { obj.left[i] = null; } } else { obj.left[i] = typeof obj.left[i] === 'object' && obj.left[i] ? RGraph.SVG.arrayPad({array: [], length: obj.left[i].length, value: 0}) : (RGraph.SVG.isNullish(obj.left[i]) ? null : 0); } } // No callback here - only called by the right function if (frame_left <= frames) { RGraph.SVG.FX.update(iteratorLeft); } } // // Iterate over the right side // function iteratorRight () { ++frame_right; for (var i=0,len=obj.right.length,seq=0; i<len; i+=1,seq+=1) { if (frame_right >= startFrames_right[seq]) { var isNull = RGraph.SVG.isNullish(obj.right[i]); // Regular bars if (typeof obj.right[i] === 'number') { obj.right[i] = Math.min( Math.abs(original_right[i]), Math.abs(original_right[i] * ( (counters_right[i]++) / framesperbar)) ); var rect_right = obj.coordsRight[seq].element; rect_right.setAttribute( 'width', parseFloat(rect_right.getAttribute('data-original-width')) * (obj.right[i] / rect_right.getAttribute('data-value')) ); rect_right.setAttribute( 'x', properties.marginLeft + obj.graphWidth + properties.marginCenter ); // Stacked or grouped bars } else if (RGraph.SVG.isArray(obj.right[i])) { for (var j=0,accWidth=0; j<obj.right[i].length; ++j,++seq) { obj.right[i][j] = Math.min( Math.abs(original_right[i][j]), Math.abs(original_right[i][j] * ( (counters_right[seq]++) / framesperbar)) ); var rect_right = obj.coordsRight[seq].element; rect_right.setAttribute( 'width', parseFloat(rect_right.getAttribute('data-original-width')) * (obj.right[i][j] / rect_right.getAttribute('data-value')) ); rect_right.setAttribute( 'x', obj.properties.marginLeft + obj.graphWidth + obj.properties.marginCenter + accWidth ); // Only update this for stacked charts if (obj.properties.grouping === 'stacked') { accWidth += parseFloat(rect_right.getAttribute('width')); obj.stackedBackfacesRight[i].setAttribute('x', obj.properties.marginLeft + obj.graphWidth + obj.properties.marginCenter); obj.stackedBackfacesRight[i].setAttribute('width', accWidth); } } seq--; } if (isNull) { obj.right[i] = null; } } else { obj.right[i] = typeof obj.right[i] === 'object' && obj.right[i] ? RGraph.SVG.arrayPad({array: [], length: obj.right[i].length, value: 0}) : (RGraph.SVG.isNullish(obj.right[i]) ? null : 0); } } // No callback here - only called by the right function if (frame_right <= frames) { RGraph.SVG.FX.update(iteratorRight); } } iteratorLeft(); iteratorRight(); return this; }; // // A worker function that handles Bipolar chart specific tooltip substitutions // this.tooltipSubstitutions = function (opt) { var indexes = RGraph.SVG.sequentialIndexToGrouped(opt.index, this.data); var linear_indexes = RGraph.SVG.sequentialIndexToGrouped(opt.index, this.data_arr); return { index: linear_indexes[1], dataset: linear_indexes[0], dataset2: linear_indexes[0] >= this.left.length ? linear_indexes[0] - this.left.length : linear_indexes[0], sequentialIndex: opt.index, value: typeof this.data_arr[linear_indexes[0]] === 'number' ? this.data_arr[linear_indexes[0]] : this.data_arr[linear_indexes[0]][linear_indexes[1]], values: typeof this.data_arr[linear_indexes[0]] === 'number' ? [this.data_arr[linear_indexes[0]]] : this.data_arr[linear_indexes[0]] }; }; // // 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) { var side = ((specific.dataset + 1) > this.left.length) ? 'right' : 'left'; var color = (!RGraph.SVG.isNullish(properties.tooltipsFormattedKeyColors) && typeof properties.tooltipsFormattedKeyColors === 'object' && properties.tooltipsFormattedKeyColors[index]) ? properties.tooltipsFormattedKeyColors[index] : properties.colors[index]; if (typeof this[side][specific.dataset2] === 'object') { var label = (!RGraph.SVG.isNullish(properties.tooltipsFormattedKeyLabels) && typeof properties.tooltipsFormattedKeyLabels === 'object' && properties.tooltipsFormattedKeyLabels[index]) ? properties.tooltipsFormattedKeyLabels[index] : ''; } else { var label = (!RGraph.SVG.isNullish(properties.tooltipsFormattedKeyLabels) && typeof properties.tooltipsFormattedKeyLabels === 'object' && properties.tooltipsFormattedKeyLabels[specific.dataset2]) ? properties.tooltipsFormattedKeyLabels[specific.dataset2] : ''; } 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), coords = this.coords[args.index]; // Position the tooltip in the X direction args.tooltip.style.left = ( svgXY[0] // The X coordinate of the canvas + coords.x // The X coordinate of the bar on the chart - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width + (coords.width / 2) // Add half of the bar width ) + 'px'; args.tooltip.style.top = ( svgXY[1] // The Y coordinate of the canvas + coords.y // The Y coordinate of the bar on the chart - 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 // RGraph.clipTo.start() function. // // @param string clip The clip string as supplied by the // user in the chart configuration // this.clipToScaleWorker = function (clipPath) { var match1 = RegExp.$1; var match2 = RegExp.$2; // The Regular expression is actually done by the // calling RGraph.clipTo.start() function in the core // library if (match1 === 'min') from = 0; else from = Number(match1); if (match2 === 'max') to = this.scale.max; else to = Number(match2); var x1 = this.getLeftXCoord(to); var x2 = this.getLeftXCoord(from); var x3 = this.getRightXCoord(from); var x4 = this.getRightXCoord(to); for (var i=0; i<2; ++i) { // LEFT-HAND-SIDE if (i === 0) { // Change the X if the number is "min" if (match1 === 'min') { x2 += (this.properties.marginCenter / 2); } // Change the width if the number is "max" if (match2 === 'max') { x1 = 0; } RGraph.SVG.create({ svg: this.svg, type: 'rect', parent: clipPath, attr: { x: x1, y: 0, width: Math.abs(x1 - x2), height: this.height } }); // Now set the clip-path attribute on the first // Line charts all-elements group this.svgAllGroup.setAttribute( 'clip-path', 'url(#' + clipPath.id + ')' ); // RIGHT-HAND-SIDE } else { // Change the X if the number is "min" if (match1 === 'min') { x3 -= (this.properties.marginCenter / 2); } // Change the width if the number is "max" if (match2 === 'max') { x4 = this.width; } RGraph.SVG.create({ svg: this.svg, type: 'rect', parent: clipPath, attr: { x: x3, y: 0, width: Math.abs(x4 - x3), height: this.height } }); // 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);