// o--------------------------------------------------------------------------------o // | This file is part of the RGraph package - you can learn more at: | // | | // | https://www.rgraph.net | // | | // | RGraph is licensed under the Open Source MIT license. That means that it's | // | totally free to use and there are no restrictions on what you can do with it! | // o--------------------------------------------------------------------------------o RGraph = window.RGraph || {isrgraph:true,isRGraph:true,rgraph:true}; // // The horizontal bar chart constructor. The horizontal bar is a minor variant // on the bar chart. If you have big labels, this may be useful as there is usually // more space available for them. // RGraph.HBar = function (conf) { // // Allow for object config style // var id = conf.id var canvas = document.getElementById(id); var data = conf.data; this.id = id; this.canvas = canvas; this.context = this.canvas.getContext ? this.canvas.getContext("2d", {alpha: (typeof id === 'object' && id.alpha === false) ? false : true}) : null; this.canvas.__object__ = this; this.data = data; this.type = 'hbar'; this.isRGraph = true; this.isrgraph = true; this.rgraph = true; this.uid = RGraph.createUID(); this.canvas.uid = this.canvas.uid ? this.canvas.uid : RGraph.createUID(); this.colorsParsed = false; this.coords = []; this.coords2 = []; this.coordsText = []; this.original_colors = []; this.firstDraw = true; // After the first draw this will be false this.yaxisLabelsSize = 0; // Used later when the margin is auto calculated this.yaxisTitleSize = 0; // Used later when the margin is auto calculated this.max = 0; this.stackedOrGrouped = false; // Default properties this.properties = { marginLeft: 75, marginLeftAuto: true, marginRight: 35, marginTop: 35, marginBottom: 35, marginInner: 2, marginInnerGrouped: 2, backgroundBarsCount: null, backgroundBarsColor1: 'rgba(0,0,0,0)', backgroundBarsColor2: 'rgba(0,0,0,0)', backgroundGrid: true, backgroundGridColor: '#ddd', backgroundGridLinewidth: 1, backgroundGridHsize: 25, backgroundGridVsize: 25, backgroundGridHlines: true, backgroundGridVlines: true, backgroundGridBorder: true, backgroundGridAutofit: true, backgroundGridAutofitAlign:true, backgroundGridHlinesCount: null, backgroundGridVlinesCount: 5, backgroundGridDashed: false, backgroundGridDotted: false, backgroundColor: null, linewidth: 1, title: '', titleBackground: null, titleHpos: null, titleVpos: null, titleBold: null, titleItalic: null, titleFont: null, titleSize: null, titleColor: null, titleX: null, titleY: null, titleHalign: null, titleValign: null, titleOffsetx: 0, titleOffsety: 0, textSize: 12, textColor: 'black', textFont: 'Arial, Verdana, sans-serif', textBold: false, textItalic: false, textAngle: 0, textAccessible: true, textAccessibleOverflow: 'visible', textAccessiblePointerevents: false, text: null, colors: ['red', 'blue', 'green', 'pink', 'yellow', 'cyan', 'navy', 'gray', 'black'], colorsSequential: false, colorsStroke: 'rgba(0,0,0,0)', xaxis: true, xaxisLinewidth: 1, xaxisColor: 'black', xaxisPosition: 'bottom', xaxisTickmarks: true, xaxisTickmarksLength: 3, xaxisTickmarksLastLeft: null, xaxisTickmarksLastRight: null, xaxisTickmarksCount: null, xaxisLabels: true, xaxisLabelsCount: 5, xaxisLabelsBold: null, xaxisLabelsItalic: null, xaxisLabelsFont: null, xaxisLabelsSize: null, xaxisLabelsColor: null, xaxisLabelsSpecific: null, xaxisLabelsAngle: 0, xaxisLabelsOffsetx: 0, xaxisLabelsOffsety: 0, xaxisLabelsHalign: null, xaxisLabelsValign: null, xaxisLabelsPosition: 'edge', xaxisLabelsSpecificAlign:'left', xaxisScale: true, xaxisScaleUnitsPre: '', xaxisScaleUnitsPost: '', xaxisScaleMin: 0, xaxisScaleMax: 0, xaxisScalePoint: '.', xaxisScaleThousand: ',', xaxisScaleDecimals: null, xaxisScaleZerostart: true, xaxisTitle: '', xaxisTitleBold: null, xaxisTitleItalic: null, xaxisTitleSize: null, xaxisTitleFont: null, xaxisTitleColor: null, xaxisTitleX: null, xaxisTitleY: null, xaxisTitleOffsetx: null, xaxisTitleOffsety: null, xaxisTitlePos: null, xaxisTitleHalign: null, xaxisTitleValign: null, yaxis: true, yaxisLinewidth: 1, yaxisColor: 'black', yaxisTickmarks: true, yaxisTickmarksCount: null, yaxisTickmarksLastTop: null, yaxisTickmarksLastBottom: null, yaxisTickmarksLength: 3, yaxisScale: false, yaxisLabels: null, yaxisLabelsCount: null, // Not used by the HBar yaxisLabelsOffsetx: 0, yaxisLabelsOffsety: 0, yaxisLabelsHalign: null, yaxisLabelsValign: null, yaxisLabelsFont: null, yaxisLabelsSize: null, yaxisLabelsColor: null, yaxisLabelsBold: null, yaxisLabelsItalic: null, yaxisLabelsPosition: 'section', yaxisLabelsFormattedDecimals: 0, yaxisLabelsFormattedPoint: '.', yaxisLabelsFormattedThousand: ',', yaxisLabelsFormattedUnitsPre: '', yaxisLabelsFormattedUnitsPost: '', yaxisPosition: 'left', yaxisTitle: null, yaxisTitleBold: null, yaxisTitleSize: null, yaxisTitleFont: null, yaxisTitleColor: null, yaxisTitleItalic: null, yaxisTitlePos: null, yaxisTitleX: null, yaxisTitleY: null, yaxisTitleOffsetx: 0, yaxisTitleOffsety: 0, yaxisTitleHalign: null, yaxisTitleValign: null, yaxisTitleAccessible: null, labelsAbove: false, labelsAboveDecimals: 0, labelsAboveSpecific: null, labelsAboveUnitsPre: '', labelsAboveUnitsPost: '', labelsAboveColor: null, labelsAboveFont: null, labelsAboveSize: null, labelsAboveBold: null, labelsAboveItalic: null, labelsAboveOffsetx: 0, labelsAboveOffsety: 0, labelsInbar: false, labelsInbarHalign: 'center', labelsInbarValign: 'center', labelsInbarFont: null, labelsInbarSize: null, labelsInbarBold: null, labelsInbarItalic: null, labelsInbarColor: null, labelsInbarBackground: null, labelsInbarBackgroundPadding: 0, labelsInbarUnitsPre: null, labelsInbarUnitsPost: null, labelsInbarPoint: null, labelsInbarThousand: null, labelsInbarFormatter: null, labelsInbarDecimals: null, labelsInbarOffsetx: 0, labelsInbarOffsety: 0, labelsInbarSpecific: null, labelsInbarFormatter: null, contextmenu: null, key: null, keyBackground: 'white', keyPosition: 'graph', keyHalign: 'right', keyShadow: false, keyShadowColor: '#666', keyShadowBlur: 3, keyShadowOffsetx: 2, keyShadowOffsety: 2, keyPositionMarginBoxed: false, keyPositionX: null, keyPositionY: null, keyColorShape: 'square', keyRounded: true, keyLinewidth: 1, keyColors: null, keyInteractive: false, keyInteractiveHighlightChartStroke: 'black', keyInteractiveHighlightChartFill:'rgba(255,255,255,0.7)', keyInteractiveHighlightLabel:'rgba(255,0,0,0.2)', keyLabelsColor: null, keyLabelsFont: null, keyLabelsSize: null, keyLabelsBold: null, keyLabelsItalic: null, keyLabelsOffsetx: 0, keyLabelsOffsety: 0, unitsIngraph: false, shadow: false, shadowColor: '#666', shadowBlur: 3, shadowOffsetx: 3, shadowOffsety: 3, grouping: 'grouped', tooltips: null, tooltipsEvent: 'onclick', tooltipsEffect: 'slide', tooltipsCssClass: 'RGraph_tooltip', tooltipsCss: null, tooltipsHighlight: true, tooltipsFormattedThousand: ',', tooltipsFormattedPoint: '.', tooltipsFormattedDecimals: 0, tooltipsFormattedUnitsPre: '', tooltipsFormattedUnitsPost: '', tooltipsFormattedKeyColors: null, tooltipsFormattedKeyColorsShape: 'square', tooltipsFormattedKeyLabels: [], tooltipsFormattedListType: 'ul', tooltipsFormattedListItems: null, tooltipsFormattedTableHeaders: null, tooltipsFormattedTableData: null, tooltipsPointer: true, tooltipsPositionStatic: true, tooltipsHotspotYonly: false, highlightFill: 'rgba(255,255,255,0.7)', highlightStroke: 'rgba(0,0,0,0)', highlightStyle: null, annotatable: false, annotatableColor: 'black', annotatableLinewidth: 1, resizable: false, resizableHandleAdjust: [0,0], resizableHandleBackground: null, redraw: true, variant: 'hbar', variantThreedAngle: 0.1, variantThreedOffsetx: 10, variantThreedOffsety: 5, variantThreedXaxis: true, variantThreedYaxis: true, adjustable: false, adjustableOnly: null, corners: 'square', cornersRoundRadius: 10, clearto: 'rgba(0,0,0,0)' } // Check for support if (!this.canvas) { alert('[HBAR] No canvas support'); return; } // // Allow the data to be given as a string // this.data = RGraph.stringsToNumbers(this.data); // This loop is used to check for stacked or grouped charts and now // also to convert strings to numbers. And now also undefined values // (29/07/2016 for (i=0,len=this.data.length; i 0 && properties.grouping === 'stacked') { alert('[HBAR] Using xaxisScaleMin is not supported with stacked charts, resetting xaxisScaleMin to zero'); this.set('xaxisScaleMin', 0); } // // Work out a few things. They need to be here because they depend on things you can change before you // call Draw() but after you instantiate the object // this.graphwidth = this.canvas.width - this.marginLeft - this.marginRight; this.graphheight = this.canvas.height - this.marginTop - this.marginBottom; this.halfgrapharea = this.grapharea / 2; this.halfTextHeight = properties.textSize / 2; this.halfway = Math.round((this.graphwidth / 2) + this.marginLeft); // Progressively Draw the chart RGraph.Background.draw(this); this.drawbars(); this.drawAxes(); this.drawLabels(); // Draw the labelsInbar this.drawLabelsInbar(); // Draw the key if necessary if (properties.key && properties.key.length) { RGraph.drawKey(this, properties.key, properties.colors); } // // Setup the context menu if required // if (properties.contextmenu) { RGraph.showContext(this); } // // Draw "in graph" labels // RGraph.drawInGraphLabels(this); // // Add custom text thats specified // RGraph.addCustomText(this); // // This installs the event listeners // RGraph.installEventListeners(this); // // Fire the onfirstdraw event // if (this.firstDraw) { this.firstDraw = false; RGraph.fireCustomEvent(this, 'onfirstdraw'); this.firstDrawFunc(); } // // Fire the RGraph draw event // RGraph.fireCustomEvent(this, 'ondraw'); 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 draws the axes // this.drawAxes = function () { // Draw the X axis RGraph.drawXAxis(this); // Draw the Y axis RGraph.drawYAxis(this); }; // // Do the label substitution. This is called from the top of the // draw function // this.yaxisLabelSubstitution = function () { if (properties.yaxisLabels && properties.yaxisLabels.length) { // // If the yaxisLabels option is a string then turn it // into an array. // if (typeof properties.yaxisLabels === 'string') { properties.yaxisLabels = RGraph.arrayPad({ array: [], length: this.data.length, value: properties.yaxisLabels }); } // // Label substitution // for (var i=0; i=0; --i) { // Work out the width and height var width = Math.abs((this.data[i] / this.max) * graphwidth); var height = this.graphheight / this.data.length; var orig_height = height; var x = this.getXCoord(0); var y = this.marginTop + (i * height); var vmargin = properties.marginInner; // // Edge case: When X axis min is greater than 0 // eg min=1 and max=2.5 // if (properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) { x = this.getXCoord(properties.xaxisScaleMin); } // Account for the Y axis being on the right hand side if ( properties.yaxisPosition === 'right') { x = this.canvas.width - this.marginRight - Math.abs(width); } // Account for negative lengths - Some browsers (eg Chrome) don't like a negative value if (width < 0) { x -= width; width = Math.abs(width); } // // Turn on the shadow if need be // if (properties.shadow) { this.context.shadowColor = properties.shadowColor; this.context.shadowBlur = properties.shadowBlur; this.context.shadowOffsetX = properties.shadowOffsetx; this.context.shadowOffsetY = properties.shadowOffsety; } // // Draw the bar // this.context.beginPath(); // Standard (non-grouped and non-stacked) bars here if (typeof this.data[i] === 'number' || RGraph.isNull(this.data[i])) { var barHeight = height - (2 * vmargin), barWidth = ((this.data[i] - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin)) * this.graphwidth, barX = x; // Accommodate an offset Y axis if (this.scale2.min < 0 && this.scale2.max > 0 && properties.yaxisPosition === 'left') { barWidth = (this.data[i] / (this.max - properties.xaxisScaleMin)) * this.graphwidth; } // Account for Y axis pos if ( properties.yaxisPosition == 'center') { barWidth /= 2; barX += halfwidth; if (this.data[i] < 0) { barWidth = (Math.abs(this.data[i]) - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin); barWidth = barWidth * (this.graphwidth / 2); barX = ((this.graphwidth / 2) + this.marginLeft) - barWidth; } else if (this.data[i] > 0) { barX = (this.graphwidth / 2) + this.marginLeft; } } else if ( properties.yaxisPosition == 'right') { barWidth = Math.abs(barWidth); barX = this.canvas.width - this.marginRight - barWidth; } // Set the fill color this.context.strokeStyle = properties.colorsStroke; this.context.fillStyle = properties.colors[0]; // Sequential colors ++colorIdx; if (properties.colorsSequential && typeof colorIdx === 'number') { if (properties.colors[this.numbars - colorIdx]) { this.context.fillStyle = properties.colors[this.numbars - colorIdx]; } else { this.context.fillStyle = properties.colors[properties.colors.length - 1]; } } if (properties.corners === 'round') { this.context.rectOld = this.context.rect; this.context.rect = this.roundedCornersRect; } this.context.beginPath(); this.context.lineJoin = 'miter'; this.context.lineCap = 'square'; this.context.rect(barX, this.marginTop + (i * height) + properties.marginInner, barWidth, barHeight); this.context.stroke(); this.context.fill(); // Put the rect function back to what it was if (properties.corners === 'round' ) { this.context.rect = this.context.rectOld; this.context.rectOld = null; } this.coords.push([ barX, y + vmargin, barWidth, height - (2 * vmargin), this.context.fillStyle, this.data[i], true ]); // Draw the 3D effect using the coords that have just been stored if (properties.variant === '3d' && typeof this.data[i] == 'number') { var prevStrokeStyle = this.context.strokeStyle, prevFillStyle = this.context.fillStyle; // // Turn off the shadow for the 3D bits // RGraph.noShadow(this); // DRAW THE 3D BITS HERE var barX = barX, barY = y + vmargin, barW = barWidth, barH = height - (2 * vmargin), offsetX = properties.variantThreedOffsetx, offsetY = properties.variantThreedOffsety, value = this.data[i]; this.path( 'b m % % l % % l % % l % % c s % f % f rgba(255,255,255,0.6)', barX, barY, barX + offsetX - ( properties.yaxisPosition == 'left' && value < 0 ? offsetX : 0), barY - offsetY, barX + barW + offsetX - ( properties.yaxisPosition == 'center' && value < 0 ? offsetX : 0), barY - offsetY, barX + barW, barY, this.context.strokeStyle,this.context.fillStyle ); if ( properties.yaxisPosition !== 'right' && !( properties.yaxisPosition === 'center' && value < 0) && value >= 0 && !RGraph.isNull(value) ) { this.path( 'b fs % m % % l % % l % % l % % c s % f % f rgba(0,0,0,0.25)', prevFillStyle, barX + barW, barY, barX + barW + offsetX, barY - offsetY, barX + barW + offsetX, barY - offsetY + barH, barX + barW, barY + barH, this.context.strokeStyle,prevFillStyle ); } } // // Stacked bar chart // } else if (typeof this.data[i] == 'object' && properties.grouping === 'stacked') { if ( properties.yaxisPosition == 'center') { alert('[HBAR] You can\'t have a stacked chart with the Y axis in the center, change it to grouped'); } else if ( properties.yaxisPosition == 'right') { var x = this.canvas.width - this.marginRight } var barHeight = height - (2 * vmargin); if (typeof this.coords2[i] == 'undefined') { this.coords2[i] = []; } for (j=0; j=0; --j) { // // Turn on the shadow if need be // if (properties.shadow) { RGraph.setShadow( this, properties.shadowColor, properties.shadowOffsetx, properties.shadowOffsety, properties.shadowBlur ); } // Set the fill/stroke colors this.context.strokeStyle = properties.colorsStroke; // Sequential colors ++colorIdx; if (properties.colorsSequential && typeof colorIdx === 'number') { if (properties.colors[this.numbars - colorIdx]) { this.context.fillStyle = properties.colors[this.numbars - colorIdx]; } else { this.context.fillStyle = properties.colors[properties.colors.length - 1]; } } else if (properties.colors[j]) { this.context.fillStyle = properties.colors[j]; } var startY = this.marginTop + (height * i) + (individualBarHeight * j) + vmargin + (vmarginGrouped * j); if (properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) { var width = ((this.data[i][j] - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin)) * (this.canvas.width - this.marginLeft - this.marginRight ); var startX = this.getXCoord((properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) ? properties.xaxisScaleMin : 0);//this.marginLeft; } else { var width = (this.data[i][j] / (this.max - properties.xaxisScaleMin)) * (this.canvas.width - this.marginLeft - this.marginRight); var startX = this.getXCoord(0); } // Account for the Y axis being in the middle if ( properties.yaxisPosition == 'center') { width /= 2; // Account for the Y axis being on the right } else if ( properties.yaxisPosition == 'right') { width = Math.abs(width); startX = this.canvas.width - this.marginRight - Math.abs(width); } if (width < 0) { startX += width; width *= -1; } if (properties.corners === 'round') { this.context.rectOld = this.context.rect; this.context.rect = this.roundedCornersRect; } this.context.beginPath(); this.context.lineJoin = 'miter'; this.context.lineCap = 'square'; this.context.rect(startX, startY, width, individualBarHeight); this.context.stroke(); this.context.fill(); // Put the rect function back to what it was if (properties.corners === 'round') { this.context.rect = this.context.rectOld; this.context.rectOld = null; } this.coords.push([ startX, startY, width, individualBarHeight, this.context.fillStyle, this.data[i][j], true ]); this.coords2[i].push([ startX, startY, width, individualBarHeight, this.context.fillStyle, this.data[i][j], true ]); // 3D effect if (properties.variant === '3d') { // // Turn off the shadow for the 3D bits // RGraph.noShadow(this); var prevStrokeStyle = this.context.strokeStyle, prevFillStyle = this.context.fillStyle; // DRAW THE 3D BITS HERE var barX = startX, barY = startY, barW = width, barH = individualBarHeight, offsetX = properties.variantThreedOffsetx, offsetY = properties.variantThreedOffsety, value = this.data[i][j]; this.path( 'b m % % l % % l % % l % % c s % f % f rgba(255,255,255,0.6)', barX, barY, barX + offsetX, barY - offsetY, barX + barW + offsetX - (value < 0 ? offsetX : 0), barY - offsetY, barX + barW, barY, this.context.strokeStyle,this.context.fillStyle ); if ( properties.yaxisPosition !== 'right' && !( properties.yaxisPosition === 'center' && value < 0) && value >= 0 && !RGraph.isNull(value) ) { this.path( 'fs % b m % % l % % l % % l % % c s % f % f rgba(0,0,0,0.25)', prevFillStyle, barX + barW, barY, barX + barW + offsetX, barY - offsetY, barX + barW + offsetX, barY - offsetY + barH, barX + barW, barY + barH, this.context.strokeStyle,prevFillStyle ); } this.context.beginPath(); this.context.strokeStyle = prevStrokeStyle; this.context.fillStyle = prevFillStyle; } } startY += vmargin; } this.context.closePath(); } this.context.stroke(); this.context.fill(); // Under certain circumstances we can cover the shadow // overspill with a white rectangle if ( properties.yaxisPosition === 'right') { this.path( 'cr % % % %', this.canvas.width - this.marginRight + properties.variantThreedOffsetx,'0',this.marginRight,this.canvas.height ); } // Draw the 3d axes AGAIN if the Y axis is on the right if ( properties.yaxisPosition === 'right' && properties.variant === '3d' ) { RGraph.draw3DYAxis(this); } // // Now the bars are stroke()ed, turn off the shadow // RGraph.noShadow(this); // // Reverse the coords arrays as the bars are drawn from the borrom up now // this.coords = RGraph.arrayReverse(this.coords); if (properties.grouping === 'grouped') { for (var i=0; i this.canvas.width ? true : false, text = RGraph.numberFormat({ object: this, number: (this.coords[i][5]).toFixed(properties.labelsAboveDecimals), unitspre: properties.labelsAboveUnitsPre, unitspost: properties.labelsAboveUnitsPost, point: properties.labelsAbovePoint, thousand: properties.labelsAboveThousand }); RGraph.noShadow(this); // Check for specific labels if (typeof properties.labelsAboveSpecific === 'object' && properties.labelsAboveSpecific && properties.labelsAboveSpecific[i]) { text = properties.labelsAboveSpecific[i]; } var x = coords[i][0] + coords[i][2] + 5; var y = coords[i][1] + (coords[i][3] / 2); if ( properties.yaxisPosition === 'right') { x = coords[i][0] - 5; halign = 'right'; } else if ( properties.yaxisPosition === 'center' && this.data_arr[i] < 0) { x = coords[i][0] - 5; halign = 'right'; } var textConf = RGraph.getTextConf({ object: this, prefix: 'labelsAbove' }); RGraph.text({ object: this, font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic, x: x + properties.labelsAboveOffsetx, y: y + properties.labelsAboveOffsety, text: text, valign: 'center', halign: halign, tag: 'labels.above' }); } } }; // // This function can be used to get the appropriate bar information (if any) // // @param e Event object // @return Appriate bar information (if any) // this.getShape = function (e) { var mouseXY = RGraph.getMouseXY(e); // // Loop through the bars determining if the mouse is over a bar // for (var i=0,len=this.coords.length; i (this.canvas.height - this.marginBottom) || mouseX < this.marginLeft || mouseX > (this.canvas.width - this.marginRight) ) { return null; } if ( properties.yaxisPosition == 'center') { var value = ((mouseX - this.marginLeft) / (this.graphwidth / 2)) * (this.max - properties.xaxisScaleMin); value = value - this.max // Special case if xmin is defined if ( properties.xaxisScaleMin > 0) { value = ((mouseX - this.marginLeft - (this.graphwidth / 2)) / (this.graphwidth / 2)) * (this.max - properties.xaxisScaleMin); value += properties.xaxisScaleMin; if (mouseX < (this.marginLeft + (this.graphwidth / 2))) { value -= (2 * properties.xaxisScaleMin); } } // TODO This needs fixing } else if ( properties.yaxisPosition == 'right') { var value = ((mouseX - this.marginLeft) / this.graphwidth) * (this.scale2.max - properties.xaxisScaleMin); value = this.scale2.max - value; } else { var value = ((mouseX - this.marginLeft) / this.graphwidth) * (this.scale2.max - properties.xaxisScaleMin); value += properties.xaxisScaleMin; } return value; }; // // Each object type has its own Highlight() function which highlights the appropriate shape // // @param object shape The shape to highlight // this.highlight = function (shape) { // highlightStyle is a function - user defined highlighting if (typeof properties.highlightStyle === 'function') { (properties.highlightStyle)(shape); // Highlight all of the rects except this one - essentially an inverted highlight } else if (typeof properties.highlightStyle === 'string' && properties.highlightStyle === 'invert') { for (var i=0; i= this.marginLeft && mouseXY[0] <= (this.canvas.width - this.marginRight) && mouseXY[1] >= this.marginTop && mouseXY[1] <= (this.canvas.height - this.marginBottom) ) { return this; } }; // // Returns the appropriate X coord for the given value // // @param number value The value to get the coord for // this.getXCoord = function (value) { if ( properties.yaxisPosition == 'center') { // Range checking if (value > this.max || value < (-1 * this.max)) { return null; } var width = (this.canvas.width - properties.marginLeft - properties.marginRight) / 2; var coord = (((value - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin)) * width) + width; coord = properties.marginLeft + coord; } else { // Range checking if (value > this.max || value < 0) { return null; } var width = this.canvas.width - properties.marginLeft - properties.marginRight; var coord = ((value - properties.xaxisScaleMin) / (this.max - properties.xaxisScaleMin)) * width; coord = properties.marginLeft + coord; } return coord; }; // // // this.parseColors = function () { // Save the original colors so that they can be restored when the canvas is reset if (this.original_colors.length === 0) { //this.original_colors['chart.'] = RGraph.arrayClone(properties.); this.original_colors.colors = RGraph.arrayClone(properties.colors); this.original_colors.backgroundGridColor = RGraph.arrayClone(properties.backgroundGridColor); this.original_colors.backgroundColor = RGraph.arrayClone(properties.backgroundColor); this.original_colors.backgroundBarsColor1 = RGraph.arrayClone(properties.backgroundBarsColor1); this.original_colors.backgroundBarsColor2 = RGraph.arrayClone(properties.backgroundBarsColor2); this.original_colors.textColor = RGraph.arrayClone(properties.textColor); this.original_colors.yaxisLabelsColor = RGraph.arrayClone(properties.yaxisLabelsColor); this.original_colors.colorsStroke = RGraph.arrayClone(properties.colorsStroke); this.original_colors.axesColor = RGraph.arrayClone(properties.axesColor); this.original_colors.highlightFill = RGraph.arrayClone(properties.highlightFill); this.original_colors.highlightStroke = RGraph.arrayClone(properties.highlightStroke); this.original_colors.annotatableColor = RGraph.arrayClone(properties.annotatableColor); } var colors = properties.colors; for (var i=0; i= top && mouseY <= (top + height)) { var indexes = RGraph.sequentialIndexToGrouped(i, this.data); var group = indexes[0]; var index = indexes[1]; if (properties.tooltips) { var tooltip = RGraph.parseTooltipText ? RGraph.parseTooltipText(properties.tooltips, i) : properties.tooltips[i]; } return { object: obj, x: left, y: top, width: width, height: height, dataset: group, index: index, sequentialIndex: i, tooltip: tooltip || null }; } } return null; }; // // This method handles the adjusting calculation for when the mouse is moved // // @param object e The event object // this.adjusting_mousemove = function (e) { // // Handle adjusting for the Bar // if (properties.adjustable && RGraph.Registry.get('adjusting') && RGraph.Registry.get('adjusting').uid == this.uid) { // Rounding the value to the given number of decimals make the chart step var value = Number(this.getValue(e)), shape = RGraph.Registry.get('adjusting.shape'); if (shape) { RGraph.Registry.set('adjusting.shape', shape); if (this.stackedOrGrouped && properties.grouping == 'grouped') { var indexes = RGraph.sequentialIndexToGrouped(shape.sequentialIndex, this.data); if (typeof this.data[indexes[0]] == 'number') { this.data[indexes[0]] = Number(value); } else if (!RGraph.isNull(this.data[indexes[0]])) { this.data[indexes[0]][indexes[1]] = Number(value); } } else if (typeof this.data[shape.dataset] == 'number') { this.data[shape.dataset] = Number(value); } RGraph.redrawCanvas(e.target); RGraph.fireCustomEvent(this, 'onadjust'); } } }; // // Draws the labelsInbar // this.drawLabelsInbar = function () { // Go through the above labels if (properties.labelsInbar) { // Default alignment var valign = properties.labelsInbarValign, halign = properties.labelsInbarHalign; // Get the text configuration for the labels var textConf = RGraph.getTextConf({ object: this, prefix: 'labelsInbar' }); // Linearize the data using a custom function because the coords are // stored in the wrong order var linearize = function (data) { var ret = []; for (var i=0; i=0; j--) { ret.push(data[i][j]); } } } return ret; }; // Linearize the data using the custom linearize function if stacked, // if not stacked use the API function if (properties.grouping === 'stacked') { data = linearize(this.data); } else { data = RGraph.arrayLinearize(this.data); } for (var i=0; i 0) { var value = data[i].toFixed(properties.labelsInbarDecimals); var indexes = RGraph.sequentialIndexToGrouped(i, this.data); var str = RGraph.numberFormat({ object: this, number: Number(value).toFixed(properties.labelsInbarDecimals), unitspre: properties.labelsInbarUnitsPre, unitspost: properties.labelsInbarUnitsPost, point: properties.labelsInbarPoint, thousand: properties.labelsInbarThousand, formatter: properties.labelsInbarFormatter }); var dimensions = RGraph.measureText({ text: str, bold: textConf.bold, font: textConf.font, size: textConf.size }); var x = this.coords[i][0] + (this.coords[i][2] / 2) + properties.labelsInbarOffsetx, y = this.coords[i][1] + (this.coords[i][3] / 2) + properties.labelsInbarOffsety, width = dimensions[0], height = dimensions[1]; // // Horizontal alignment // if (properties.labelsInbarHalign === 'left') { halign = 'left'; x = this.coords[i][0] + 5 + properties.labelsInbarOffsetx; } else if (properties.labelsInbarHalign === 'right') { halign = 'right'; x = this.coords[i][0] + this.coords[i][2] - 5 + properties.labelsInbarOffsetx; } // // Vertical alignment // if (properties.labelsInbarValign === 'bottom') { valign = 'bottom'; y = this.coords[i][1] - 5 + this.coords[i][3] + properties.labelsInbarOffsety; } else if (properties.labelsInbarValign === 'top') { valign = 'top'; y = this.coords[i][1] + 5 + properties.labelsInbarOffsety; } // Specific label given if (RGraph.isArray(properties.labelsInbarSpecific) && (RGraph.isString(properties.labelsInbarSpecific[i]) || RGraph.isNumber(properties.labelsInbarSpecific[i]))) { str = properties.labelsInbarSpecific[i]; } RGraph.text({ object: this, font: textConf.font, size: textConf.size, color: textConf.color, bold: textConf.bold, italic: textConf.italic, x: x, y: y, text: str, valign: valign, halign: halign, tag: 'labels.above', bounding: RGraph.isString(properties.labelsInbarBackground), boundingFill: properties.labelsInbarBackground, boundingStroke: 'transparent' }); } } } }; // // Grow // // The HBar chart Grow effect gradually increases the values of the bars // // @param object OPTIONAL Options for the effect. You can pass frames here // @param function OPTIONAL A callback function // //this.grow = function () //{ // var obj = this, // opt = arguments[0] || {}, // frames = opt.frames || 30, // frame = 0, // callback = arguments[1] || function () {}, // labelsAbove = properties.labelsAbove; // // this.set('labelsAbove', false); // // Save the data // obj.original_data = RGraph.arrayClone(obj.data); // // Stop the scale from changing by setting xaxisScaleMax (if it's not already set) // if ( properties.xaxisScaleMax == 0) { // var xmax = 0; // for (var i=0; i 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) { obj.data[j][k] = (easingMultiplier * (obj.original_data[j][k] - properties.xaxisScaleMin)) + properties.xaxisScaleMin; } else { obj.data[j][k] = easingMultiplier * obj.original_data[j][k]; } } else if (opt.data && opt.data.length === obj.original_data.length) { var diff = opt.data[j][k] - obj.original_data[j][k]; obj.data[j][k] = (easingMultiplier * diff) + obj.original_data[j][k]; } } } else { if (obj.firstDraw || !opt.data) { if (properties.xaxisScaleMin > 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) { obj.data[j] = (easingMultiplier * (obj.original_data[j] - properties.xaxisScaleMin)) + properties.xaxisScaleMin; } else { obj.data[j] = easingMultiplier * obj.original_data[j]; } } else if (opt.data && opt.data.length === obj.original_data.length) { var diff = opt.data[j] - obj.original_data[j]; obj.data[j] = (easingMultiplier * diff) + obj.original_data[j]; } } } //RGraph.clear(obj.canvas); RGraph.redrawCanvas(obj.canvas); if (frame < frames) { frame += 1; RGraph.Effects.updateCanvas(iterator); // Call the callback function } else { // Do some housekeeping if new data was specified thats done in // the constructor - but needs to be redone because new data // has been specified if (RGraph.isArray(opt.data)) { var linear_data = RGraph.arrayLinearize(data); for (var i=0; i opt.startFrames[i]) { if (typeof obj.data[i] === 'number') { obj.data[i] = Math.min(Math.abs(original[i]),Math.abs(original[i] * (opt.counters[i] / framesperbar))); opt.counters[i] += 1; // Make the number negative if the original was if (original[i] < 0) { obj.data[i] *= -1; } } else if (!RGraph.isNull(obj.data[i])) { for (var j=0,len2=obj.data[i].length; j 0 && properties.xaxisScaleMax > properties.xaxisScaleMin) { obj.data[i][j] = Math.min( Math.abs(original[i][j]), Math.abs(((original[i][j] - properties.xaxisScaleMin) * ( opt.counters[i][j] / framesperbar)) + properties.xaxisScaleMin) ); opt.counters[i][j] += 1; } else { obj.data[i][j] = Math.min( Math.abs(original[i][j]), Math.abs(original[i][j] * (opt.counters[i][j] / framesperbar)) ); // Having this here seems to skirt a // minification bug opt.counters[i][j] += 1; } // Make the number negative if the original was if (original[i][j] < 0) { obj.data[i][j] *= -1; } } } } else { obj.data[i] = typeof obj.data[i] === 'object' && obj.data[i] ? RGraph.arrayPad([], obj.data[i].length, (properties.xaxisScaleMin > 0 ? properties.xaxisScaleMin : 0)) : (RGraph.isNull(obj.data[i]) ? null : (properties.xaxisScaleMin > 0 ? properties.xaxisScaleMin : 0)); } } if (frame >= opt.frames) { if (labelsAbove) { obj.set('labelsAbove', true); RGraph.redrawCanvas(obj.canvas); } callback(obj); } else { RGraph.redrawCanvas(obj.canvas); RGraph.Effects.updateCanvas(iterator); } } iterator(); return this; }; // // Determines whether the given shape is adjustable or not // // @param object The shape that pertains to the relevant bar // this.isAdjustable = function (shape) { if (RGraph.isNull(properties.adjustableOnly)) { return true; } if (RGraph.isArray(properties.adjustableOnly) && properties.adjustableOnly[shape.sequentialIndex]) { return true; } return false; }; // // This adds a roundedRect(x, y, width, height, radius) function to the drawing context. // The radius argument dictates by how much the corners are rounded. // // @param number x The X coordinate // @param number y The Y coordinate // @param number width The width of the rectangle // @param number height The height of the rectangle // @param number radius The radius of the corners. Bigger values mean more rounded corners // this.roundedCornersRect = function (x, y, width, height) { var radius = properties.cornersRoundRadius; radius = Math.min(width / 2, height / 2, radius); // Because the function is added to the context prototype // the 'this' variable is actually the context // Save the existing state of the canvas so that it can be restored later this.save(); // Translate to the given X/Y coordinates this.translate(x, y); // Move to the center of the top horizontal line this.moveTo(width / 2,0); // Draw the rounded corners. The connecting lines in between them are drawn automatically this.arcTo(width,0,width,height, Math.min(height / 2, radius)); this.arcTo(width, height, 0, height, radius); this.arcTo(0, height, 0, 0, 0); this.arcTo(0, 0, radius, 0, Math.min(height / 2, 0)); // Draw a line back to the start coordinates this.lineTo(width / 2,0); // Restore the state of the canvas to as it was before the save() this.restore(); }; // // A worker function that handles Bar chart specific tooltip substitutions // this.tooltipSubstitutions = function (opt) { var indexes = RGraph.sequentialIndexToGrouped(opt.index, this.data); if (typeof this.data[indexes[0]] === 'object') { var values = this.data[indexes[0]]; } else { var values = [this.data[indexes[0]]]; } var value = this.data_arr[opt.index]; var index = indexes[1]; var seq = opt.index; // Skirt an indexes bug if (typeof this.data[indexes[0]] === 'object' && properties.grouping === 'stacked') { value = this.data[indexes[0]][this.data[indexes[0]].length - 1 - indexes[1]]; } // // Return the values to the user // return { index: index, dataset: indexes[0], sequentialIndex: seq, value: value, values: values }; }; // // A worker function that returns the correct color/label/value // // @param object specific The indexes that are applicable // @param number index The appropriate index // this.tooltipsFormattedCustom = function (specific, index) { if (this.stackedOrGrouped) { var label = (!RGraph.isNull(properties.tooltipsFormattedKeyLabels) && typeof properties.tooltipsFormattedKeyLabels === 'object' && properties.tooltipsFormattedKeyLabels[index]) ? properties.tooltipsFormattedKeyLabels[index] : ''; } else { var label = ( !RGraph.isNull(properties.tooltipsFormattedKeyLabels) && typeof properties.tooltipsFormattedKeyLabels === 'object' && properties.tooltipsFormattedKeyLabels[specific.index] ) ? properties.tooltipsFormattedKeyLabels[specific.index] : ''; } return { label: label }; }; // // This allows for static tooltip positioning // this.positionTooltipStatic = function (args) { var obj = args.object, e = args.event, tooltip = args.tooltip, index = args.index, canvasXY = RGraph.getCanvasXY(obj.canvas) coords = this.coords[args.index]; // Position the tooltip in the X direction args.tooltip.style.left = ( canvasXY[0] // The X coordinate of the canvas + coords[0] // The X coordinate of the point on the chart + (coords[2] / 2) // Add half of the width of the bar - (tooltip.offsetWidth / 2) // Subtract half of the tooltip width + obj.properties.tooltipsOffsetx // Add any user defined offset ) + 'px'; args.tooltip.style.top = ( canvasXY[1] // The Y coordinate of the canvas + coords[1] // The Y coordinate of the bar on the chart - tooltip.offsetHeight // The height of the tooltip - 10 // An arbitrary amount + obj.properties.tooltipsOffsety // Add any user defined offset ) + 'px'; // If the chart is a 3D version the tooltip Y position needs this // adjustment if (properties.variant === '3d') { var left = parseInt(args.tooltip.style.left); var top = coords[1]; var angle = properties.variantThreedAngle; var adjustment = Math.tan(angle) * left; args.tooltip.style.top = parseInt(args.tooltip.style.top) + adjustment + 'px'; } }; // // Charts are now always registered // RGraph.register(this); // // This is the 'end' of the constructor so if the first argument // contains configuration data - handle that. // RGraph.parseObjectStyleConfig(this, conf.options); };